next up previous contents Next:  Bibliothèques de widgets et Up: Le système X-Window. Previous: Le système X-Window.   Table des matières  

44.1  Le protocole X.

X est un protocole de commandes échangées entre une application et un terminal graphique spécial appelé serveur X (désormais, nous l'appelerons le serveur). [En l'occurence, le terme ``serveur'' est ambigü, parce qu'il y a de nombreux serveurs X sur chaque machine cliente, et que l'utilisateur se trouve du côté serveur.] Classiquement, c'est l'inverse.] Concernant la manière avec laquelle le serveur effectue des tracés graphiques sur une unité matérielle,les développeurs d'applications graphiques ne doivent pas se préoccuper de la manière dont le serveur X interprète les graphiques. l'application doit juste savoir que, si elle envoye une séquence particulière d'octets par TCP/IP, le serveur interprétera ces octets de sorte qu'une ligne, un cercle, une boîte, une police de caractères ou toute autre objet graphique soit affiché à l'écran. En retour, l'application doit savoir qu'aux séquences particulières d'octets correspond l'enfoncement d'une touche d'un clavier ou le déplacement d'une souris. Cette communication TCP est appelée protocole X.

Quand vous utilisez X, nous ne réalisez pas forcément que l'intéraction décrite ci-dessus a lieu. Le serveur et l'application peuvent se trouver sur la même machine. La puissance réelle d'X ressort lorsque l'application et le serveur ne fonctionnent pas sur la même machine. Supposons, par exemple, que 20 utilisateurs soient connectés à un même ordinateur et qu'ils employent des programmes différents dont l'affichage est réalisé sur 20 serveurs X. Tout se passe comme si une machine unique permettait l'usage d'écrans et de claviers multiples. C'est pour cette raison qu'X est défini comme un système de fenêtrage transparent en mode réseau.

Les développeurs d'applications graphiques sont donc dispensés de connaître les propriétés graphiques du matériel (comparons cela avec les applications DOS où chacun doit construire le support de nombreuses cartes graphiques); ils ne doivent pas connaître le type de machines sur lesquelles l'application graphique sera affichée.

Le programme qui réalise cette opération miraculeuse se trouve dans /usr/X11/bin/X. La séquence typique des événements qui permettent à un programme graphique d'être exécuté est donnée ci-dessous. Il s'agit d'une description simplifiée car, en pratique, de nombreux utilitaires sont mis à contribution.

  1. Le programme /usr/X11R6/bin/X est démarré et exécuté en tâche de fond. En lisant les fichiers de configuration (/etc/XF86Config ou /etc/X11/XF86Config sur LINUX), X détecte le matériel graphique disponible (par exemple, le type de carte graphique). Il initialise ce matériel en mode graphique.
  2. X ouvre une connexion socket pour écouter les appels entrants sur un port spécifique (généralement, le port TCP 6000) et s'apprête à interpréter toute connexion comme un flux de commandes graphiques.
  3. Une application est lancée sur la machine locale ou sur une machine distante. Tous les programmes X possèdent une option de configuration via laquelle vous pouvez indiquer (grâce à une adresse IP ou un nom d'hôte) où vous souhaitez que le programme se connecte, c'est-à-dire le serveur où vous aimeriez que le résultat soit affiché.
  4. L'application ouvre une connexion socket à un serveur donné sur le réseau. C'est lors de cette étape que la plupart des erreurs ont lieu. Il peut y avoir échec si le serveur n'est pas en cours d'exécution, s'il a été indiqué incorrectement ou si le serveur refuse une connexion à un hôte qui n'est pas de confiance.
  5. L'application commence à envoyer des requêtes de protocole X, en attendant qu'elles soient traitées. Après réception, les réponses du protocole X sont traitées à leur tour. Aux yeux de l'utilisateur, l'application semble fonctionner sur l'écran du serveur.
La communication entre l'application et le serveur est tout de même plus compliquée que le simple tracé de lignes, de rectangle et que la traduction des déplacements de la souris ou de l'enfoncement des touches du clavier. Le serveur doit être capable de traiter de multiples applications qui se sont connectées à partir de nombreuses machines. Ces applications peuvent intéragir (songez aux opérations dites de ``copier-coller'' entre des applications qui fonctionnent sur différentes machines). Voici quelques exemples de requêtes du protocole X de base qu'une application peut entreprendre:

Créer une fenêtre:
ouvre une fenêtre (rectangle logique sur l'écran, propriété d'une application) dans laquelle des tracés graphiques peuvent être réalisés.

Afficher des polices:
affiche une liste des polices de caractères mises à disposition d'une application.

Allouer de la couleur:
permet de définir une couleur par un nom ou une valeur RGB (Red Green Blue - Rouge Vert Bleu).

Créer un contexte graphique:
un contexte graphique est une définition de la manière dont les graphiques doivent être tracés à l'intérieur d'une fenêtre. Par exemple, la couleur de fond par défaut, le style de ligne, les coupures, les polices de caractères.

Trouver le propriétaire d'une sélection:
permet de trouver la fenêtre associée à une sélection (par exemple, une copie de texte). La fenêtre peut appartenir à une autre application.
En retour, le serveur répond en renvoyant des événements à l'application. L'application doit constamment écouter le serveur pour capter ces événements. A côté des événements associés au déplacement de la souris ou des entrées au clavier, il y en a d'autres comme, par exemple, l'indication qu'une fenêtre est passée en premier plan (c'est-à-dire par dessus une autre fenêtre. L'application devrait alors envoyer les commandes idoines pour retracer les graphes dans la fenêtre en premier plan). Un autre exemple concerne l'avis de requête d'un collage depuis une autre application lors d'un ``copier-coller''. Le fichier /usr/include/X11/Xproto.h contient les listes complètes des requêtes du protocole X et des événements.

Les programmeurs d'applications X ne sont pas forcément concernés par ces requêtes. Une bibliothèque de haut-niveau traitent les tâches relatives à l'interaction avec le serveur. Cette bibliothèque est appelée la bibilothèque X, /usr/X11R6/lib/libX11.so.6.

Une des limitations du protocole X tient en ce que les développeurs ne peuvent utiliser que le jeu de commandes qui a été définis. X contourne ce problème en rendant le protocole ``extensible'' depuis le début, c'est-à-dire en permettant que soient ajoutées des extensions ou des améliorations sans compliquer ou affecter la compatibilité. A l'heure actuelle, il existe des extensions à X pour permettre l'affichage 3D sur le serveur, l'interprétation des commandes PostScript, et bien d'autres possibilités qui améliorent l'esthétique et les performances. A chaque extension sont associés des requêtes et des événements du protocole X ainsi que des interfaces de bibliothèques de programmation.

Voici un exemple de programme X réel. Il s'agit d'un programme très simple qui affiche un fichier image XPM dans une fenêtre et attend qu'une touche soit enfoncée ou qu'un clic de souris ait lieu pour se terminer. Vous pouvez le compiler avec gcc -o splash splash.c -lX11 -L/usr/X11R6/lib. Vous verrez de suite pourquoi il y a peu d'applications écrites directement avec X. Notez que toutes les fonctions des bibliothèques X sont préfixées avec un X.

/* splash.c - display an image */ 
 
#include <stdlib.h> 
#include <stdio.h> 
#include <string.h> 
 
#include <X11/Xlib.h> 
 
/* XPM */ 
static char *graham_splash[] = { 
``28 32 16 1'' 
``  c #34262e'', ``. c #4c3236'', ``X c #673a39'', ``o c #543b44'', 
``O c #724e4e'', ``+ c #6a5459'', ``@ c #6c463c'', ``# c #92706c'', 
``$ c #92685f'', ``% c #987e84'', ``& c #aa857b'', ``n c #b2938f'', 
``= c #bca39b'', ``- c #a89391'', ``; c #c4a49e'', ``: c #c4a8a4'', 
/* pixels */ 
``-%#%%nnnn#-nnnnnn=====;;=;:'', ``----n-nnnnnn=n==;==;=:;:'', 
``--n-n-n-n-n-nn===:::::::'', ``---&---nn-n=n====::::::'', 
``--------n===;=::::::'', ``--%&-%-%##%--n===:::::::'', 
``---%#%+++o+++--=:::::::'', ``-#-%%#+++oo. oo+#-=:::::::'', 
``-%%%%++++o..    .++&-==:::::'', ``--%#+#+++o.      oo+&n=::::'', 
``-%###+$+++Oo.      o+#-:=::'', ``-&%########++Oo       @$-==:'', 
``####$$$+###$++OX      .O+&=='', ``&##$O+OXo+++$#+Oo.    ..O&&-'', 
``&##+OX..... .oOO@@...  o@+&SPMamp;'', ``&###$Oo.o++     ..oX@oo@O$&-'', 
``n###$$$$O$o ...X.. .XXX@$$$SPMamp;'', ``nnn##$$#$00. .XX+@ .XXX@$$#SPMamp;'', 
``nnn&&####$OX.X$$@.   XX$$$$SPMamp;'', ``nnnnn&&###$$$OX$$X..XXX@O$&n'', 
``nnnnnn&&%###$$$$@XXXXX@O$&&n'', ``;n=;nnnn&&&#$$$$$@@@@@@O$&n;'', 
``;n;=nn;nnnn#&$$$@X@O$@@$$&n;'', ``=n=;;;n;;nn&&&$$$$OO$$$$$&;;'', 
``n;=n;;=nn&n&&&&&&$$$$$##&&n;'', ``n;=;;;;;;;;&&&n&&&&&&&&#&n=;'', 
``;n;n;;=n;&;&;&n&&&&&&&#nn;;;'', ``n;=;;;;;;;;n;&&n&&&n&n&nnnn;'', 
``n=;;:;;=;;nn;&n;&n&nnnnnnn=;'', ``nn;;;;;;;;;;;;;;n&nnnnnn===;'', 
``=nn;;:;n;;;;&&&&n&&nnnnnn;=;'', ``n====;;;;&&&&&&&nnnnnnnnnn;;''  
}; 
 
int main (int argc, char **argv) 
{ 
   int i, j, x, y, width, height, n_colors; 
   XSetWindowAttributes xswa; 
   XGCValues gcv; 
   Display *display; 
   char *display_name = 0; 
   int depth = 0; 
   Visual *visual; 
   Window window; 
   Pixmap pixmap; 
   XImage *image; 
   Colormap colormap; 
   GC gc; 
   int bytes_per_pixel; 
   unsigned long colors[256];  
   unsigned char **p, *q; 
   for (i=1; i < argc -1; i++) 
      if (argv[i]) 
         if (!strcmp (argv[i], ``-display'')) 
            display_name = argv[i+1]; 
   display = XOpenDisplay (display_name); 
   if (!display) { 
     printf (``splash: cannot open display\n''); 
     exit(1); 
   } 
   depth = DefaultDepth (display, DefaultScreen (display)); 
   visual = DefaultVisual (display, DefaultScreen (display)); 
   p = (unsigned char **) graham_splash; 
   q = p[0]; 
   width = atoi ((const char *) q); 
   q = (unsigned char *) strchr (q, ' '); 
   height = atoi ((const char *) ++q); 
   q = (unsigned char *) strchr (q, ' '); 
   n_colors = atoi ((const char *) ++q); 
 
   colormap = DefaultColormap (display, DefaultScreen (display)); 
   pixmap = XCreatePixmap (display, DefaultRootWindow (display), width, height, depth); 
   gc = XCreateGC (display, pixmap, 0, &gcv); 
   image = XCreateImage (display, visual, depth, ZPixmap, 0, 0, width, height, 8, 0); 
   image->data = (char *) malloc (image->bytes_per_line * height + 16); 
 
/* create color palette */ 
   for (p = p + 1, i = 0; i < n_colors; p++, i++) { 
      XColor c, c1; 
      unsigned char *x; 
      x = *p + 4; 
      if (*x == '#') { 
         unsigned char *h = (unsigned char *) ``0123456789abcdef''; 
         x++; 
         c.red =  
                ((unsigned long) strchr (h, *x++) - 
                 (unsigned long) h) « 12; 
         c.red |=  
                ((unsigned long) strchr (h, *x++) - 
                 ((unsigned long) h) «8; 
         c.green =  
                ((unsigned long) strchr (h, *x++) - 
                 (unsigned long) h) « 12; 
         c.green |= 
                ((unsigned long) strchr (h, *x++) - 
                 ((unsigned long) h) « 8; 
         c.blue = 
                ((unsigned long) strchr (h, *x++) - 
                 (unsigned long) h) « 12; 
         c.blue |= 
                ((unsigned long) strchr (h, *x++) - 
                 ((unsigned long) h) « 8; 
         if (!XAllocColor (display, colormap, &c)) 
            printf (``splash: could not allocate color cell\n''); 
      } else { 
         if (!XAllocNamedColor (display, colormap, (char *) x, &c, &c1)) 
            printf (``splash: could not allocate color cell\n'') 
      } 
      colors[(*p)[0]] = c.pixel; 
} 
 
   bytes_per_pixel = image->bytes_per_line / width; 
 
/* cope with servers having different byte ordering and depths */ 
   for (j = 0; j < height; j++, p++) { 
      unsigned char *r; 
      unsigned long c; 
      q = image->data + image->bytes_per_line *j; 
      r = *p; 
      if (image->byte_order == MSBFirst) { 
         switch (bytes_per_pixel) { 
         case 4: 
            for (i = 0; i < width; i++) { 
               c = colors[*r++]; 
               *q++ = c » 24; 
               *q++ = c » 16; 
               *q++ = c » 8; 
               *q++ = c; 
            } 
            break; 
         case 3: 
            for (i = 0; i < width; i++) { 
               c = colors[*r++]; 
               *q++ = c » 16; 
               *q++ = c » 8; 
               *q++ = c; 
            } 
            break; 
         case 2: 
            for (i = 0; i < width; i++) { 
               c = colors[*r++]; 
               *q++ = c » 8; 
               *q++ = c; 
            } 
            break; 
         case 1: 
            for (i = 0; i < width; i++) 
               *q++ = colors[*r++]; 
            break;  
         } 
      } else { 
         switch (bytes_per_pixel) { 
         case 4: 
            for (i = 0; i < width; i++) { 
               c = colors[*r++]; 
               *q++ = c; 
               *q++ = c » 8; 
               *q++ = c » 16; 
               *q++ = c » 24; 
            } 
            break; 
         case 3: 
            for (i = 0; i < width; i++) { 
               c = colors[*r++]; 
               *q++ = c ; 
               *q++ = c » 8; 
               *q++ = c » 16; 
            } 
            break; 
         case 2: 
            for (i = 0; i < width; i++) { 
               c = colors[*r++]; 
               *q++ = c; 
               *q++ = c » 8; 
            } 
            break; 
         case 1: 
            for (i = 0; i < width; i++) 
               *q++ = colors[*r++]; 
            break;  
         } 
        } 
   } 
 
   XPutImage (display, pixmap, gc, image, 0, 0, 0, 0, width, height); 
 
   x = (DisplayWidth (display, DefaultScreen (display)) - width) / 2; 
   y = (DisplayHeight (display, DefaultScreen (display)) - heigth) / 2; 
 
   xswa.colormap = colormap; 
   xswa.background_pixmap = pixmap; 
   window = 
      XCreateWindow (display, DefaultRootWindow (display), x, y, width, 
                     height, 0, depth, InputOutput, visual, 
                     CWColormap | CWBackPixmap, &xswa); 
   XSelectInput (display, window, KeyPressMask | ButtonPressMask); 
 
   XMapRaised (display, window); 
 
   while (1) { 
      XEvent event; 
      XNextEvent (display, &event); 
      if (event.xany.type == KeyPress || event.xany.type == ButtonPressMask) 
         break; 
   } 
   XUnmapWindow (display, window), 
   XCloseDisplay (display); 
   return 0; 
} 


Il est possible d'apprendre à programmer avec X en utilisant la documentation du Système X Window . Voyez ci-dessous. On qualifie le programme que nous venons de voir de ``programme écrit directement avec X-lib'' parce qu'il est seulement lié à la bibilothèque X de plus bas-niveau, libX11.so. Les avantages d'un développement selon cette méthode viennent du fait que votre programme fonctionne sur les différentes variantes d'UNIX sans aucune adaptation. Notez que le programme n'a rien à voir avec la résolution (largeur $\times$ hauteur ou, nombre de pixels par cm \ensuremath{²}), la couleur ou l'architecture matérielle.


next up previous contents Next:  Bibliothèques de widgets et Up: Le système X-Window. Previous: Le système X-Window.   Table des matières  
1-01-2006