11

Solution des exercices

 

1. Les fenêtres

 

Exercice 1 :  (Création de fenêtres)

Créer une fenêtre de fond noir avec XCreateSimpleWindow et une deuxième, fille de la première, avec un bord blanc d'une certaine épaisseur. On utilisera pour créer la fenêtre fille la fonction XCreateWindow. Afficher ensuite les deux fenêtres à l'écran.  Le programme déplacera ensuite la fenêtre fille horizontalement d'une distance perceptible (150 points, par exemple). Après lecture d'un caractère (avec getchar), on quittera le programme. Pour être sûr que la librairie Xlib a bien envoyé les requêtes au serveur, on utilisera la requête

 

      XFlush (dpy).

 

(En effet, la Xlib empile les requêtes de création et tant que l'on n'a pas encore cherché à récupérer les événements, les requêtes ne sont pas envoyées au serveur.)

 

 

#include <stdio.h>

#include <X11/Xlib.h>

#include <X11/Xutil.h>

 

#define LARGEUR   300      

#define HAUTEUR   400      

 

char*                        nom_de_station = NULL;

unsigned long       pixel_blanc, pixel_noir;

Display*                 dpy;

Window                  root;

int                       screen;

 

/*

 * Une fonction pour imprimer une erreur et quitter le programme

 */

 

void erreur (message, a1, a2, a3) char* message;

{

       printf (message, a1, a2, a3);

       exit (1);

}

 

main (argc, argv) char **argv;

{

       Window                                Fenetre,

                                                         sous_fenetre;

       int                                     i, j;

       XSetWindowAttributes  xsw;

       char                                         lire;      /* caractere a lire pour quitter*/

                                  

       /*

        * Regarder si on a passe un argument au programme,

        *  si oui modifier l'argument nom_de_station

        */

       if (argc == 3 && !strcmp (argv [1], "-d"))

                     nom_de_station = argv [2];

 

       /*

        * Connexion au serveur nom_de_station et initialisation des variables globales

        * screen, root, pixel_blanc et pixel_noir

        */

       dpy = XOpenDisplay (nom_de_station);

      

       if (dpy == NULL)

                     erreur ("%s: Impossible d'ouvrir%s\n", argv[0],

                                                                                                   XDisplayName(nom_de_station));

       screen               =     DefaultScreen (dpy);

       root                    =     RootWindow (dpy, screen);

       pixel_blanc     =     WhitePixel (dpy, screen);

       pixel_noir       =     BlackPixel (dpy, screen);

 

       /*

        * Creation de la fenetre principale avec XCreateSimpleWindow

        */ 

       Fenetre = XCreateSimpleWindow (dpy, root, 100, 100, LARGEUR, HAUTEUR, 2,

                                                                                      pixel_blanc, pixel_noir);

       /*

        * Creer la sous-fenetre avec XCreateWindow et donner une valeur pour les

        * attributs : border_pixel, background_pixel et win_gravity

        */

 

       xsw.border_pixel               =    pixel_blanc;

       xsw.background_pixel    =    pixel_noir;

       xsw.win_gravity         =    SouthEastGravity;

 

       sous_fenetre = XCreateWindow (dpy, Fenetre, 10, 20, LARGEUR/4, HAUTEUR/4,

                                                                              2, CopyFromParent, InputOutput,

                                                                              CopyFromParent, CWBackPixel |

                                                                              CWBorderPixel | CWWinGravity, &xsw);

       /*

        * Faire afficher les fenetres

        */

 

       XMapSubwindows (dpy, Fenetre);

       XMapWindow (dpy, Fenetre);

       /*

       * Envoyer les requetes au serveur

       * et attendre leur execution

       */

 

       XFlush (dpy);               /* pour envoyer les requetes au serveur */

       sleep(3);                         /* pour augmenter le suspens */

       lire = getchar();           /* pour controler l'attente */

 

       /*

        * Deplacer la sous-fenetre

        */

 

       XMoveWindow(dpy, sous_fenetre, 50, 50);

       XMoveWindow(dpy, sous_fenetre, 100, 200);

       XFlush (dpy);

 

       /*

        * On attend de voir bouger la fenetre et on entre un caractere pour quitter

        */

 

       lire = getchar();

}

 

Exercice 2 :  (Fond de fenêtres)

Créer une fenêtre dont le fond est constitué par un Pixmap créé interactivement à l'aide de l'utilitaire bitmap. On pourra faire deux versions. Une première version dans laquelle le fichier généré par l'utilitaire bitmap est inclu directement dans le programme et une deuxième dans laquelle le fichier est chargé en cours d'exécution. Quels sont les avantages et inconvénients des deux méthodes  ?

 

                  L'avantage de la première méthode est que les données sont compilées et font partie intégrante du programme. On utilisera cette formule pour les fonds qui ne dépendent pas du choix de l'utilisateur. En particulier, les fonds prévus par défaut pourront être initialisés de cette manière car cette méthode présente l'avantage de ne pas dépendre de la configuration locale.

 

                  La deuxième méthode est intéressante pour faire des essais puisque les fichiers lus peuvent être modifiés sans que l'on ait besoin de recompiler le programme. Cette méthode est aussi celle utilisée pour lire les fichiers passé en argument d'appel du programme.

 

/*

 * Première version utilisant XCreateBitmapFromData

 */

 

#include <stdio.h>

#include <X11/Xlib.h>

#include <X11/Xutil.h>

 

#define LARGEUR   300       /* largeur de la fenetre fen */

#define HAUTEUR   400       /* hauteur de la fenetre fen */

 

/*

* contenu litteral du fichier essai.bm cree interactivement avec la commande bitmap

*/

 

#define essai_width 16

#define essai_height 16

static char essai_bits[] = {

       0x00, 0x00, 0x00, 0x00, 0x40, 0x00, 0xb0, 0x01,

       0x08, 0x02, 0x08, 0x02, 0x04, 0x04, 0x08, 0x02,

       0x08, 0x02, 0xb0, 0x1d, 0x40, 0x1c, 0x00, 0x1c,

       0x00, 0x00, 0xfe, 0x7f, 0x00, 0x00, 0x00, 0x00};

 

/*

 * Les variables globales habituelles

 */

 

char*                        nom_de_station = NULL;

unsigned long       pixel_blanc, pixel_noir;

Display*                 dpy;

Window                  root;

int                       screen;

 

extern void erreur (/* char* message, a1, a2, a3 */);   /* cf. exercice 1 */

 

main (argc, argv) char **argv;

{

       Window                                       fen;

       int                                            i, j;

       Pixmap                                         pm_essai;

       XSetWindowAttributes xsw;

      

       /*

        * Connexion au serveur et initialisation des variables globales

        */

 

       dpy = XOpenDisplay (nom_de_station);

       if (dpy == NULL)

                     erreur ("%s:impossible d'ouvrir la connexion\n", argv[0]);

       screen               =     DefaultScreen (dpy);

       root                    =     RootWindow (dpy, screen);

       pixel_blanc     =     WhitePixel (dpy, screen);

       pixel_noir       =     BlackPixel (dpy, screen);

 

       /*

        * Creation de la fenetre principale

        */

 

       fen = XCreateSimpleWindow (dpy, root, 100, 100, LARGEUR, HAUTEUR, 2,

                                                                              pixel_blanc, pixel_noir);

 

       /*

        * Creation d'un fond a partir des donnees incluses en debut de programme

        */

 

       pm_essai = XCreateBitmapFromData (dpy, root, essai_bits, essai_width,

                                                                                            essai_height);

       XSetWindowBackgroundPixmap (dpy, fen, pm_essai);

 

       /*

        * Faire afficher la fenetre et attendre les evenements

        */

 

       XMapWindow (dpy, fen);

 

       for (;;) {

                            /* La boucle etant infinie on quittera le programme en tapant ^C

                             * depuis la fenetre d'ou le programme aura ete appele

                             */

              XEvent ev;

              XNextEvent (dpy, &ev);

       }

}

 

 

/*********************************************************************

 * exercice 2: deuxième version utilisant XReadBitmapFile

 *********************************************************************/

/*

 * On suppose cette fois que le fichier bitmap essai.bm (cree interactivement avec la

 * commande bitmap ) se trouve dans le repertoire /usr/include/X11/bitmaps

 */

 

char*   nom_de_bitmap = "/usr/include/X11/bitmaps/essai.bm"

 

/*

 * autres declarations globales identiques a celles du corrige precedent

 */

 

main (argc, argv) char **argv;

{

 

       Window                                       fen;

       int                                            i, j, nil;

       unsigned int                                u_nil;                 /* un entier bidon */

       Pixmap                                         pm_essai;         /* un Pixmap d'essai */

       XSetWindowAttributes xsw;

      

       /*

        * meme debut de programme

        */

       dpy = XOpenDisplay (nom_de_station);

 

       if (dpy == NULL)

                     erreur ("%s:connexion a %s impossible\n", argv[0],

                                                                                                   XDisplayName(nom_de_station));

 

       screen               =     DefaultScreen (dpy);

       root                    =     RootWindow (dpy, screen);

       pixel_blanc     =     WhitePixel (dpy, screen);

       pixel_noir       =     BlackPixel (dpy, screen);

 

       fen = XCreateSimpleWindow (dpy, root, 100, 100, LARGEUR, HAUTEUR, 2,

                                                                       pixel_blanc, pixel_noir);

 

       /*

        * Creation d'un pixmap a partir du fichier dont le chemin

        *  d'acces est nom_de_bitmap

        */

 

       if (BitmapSuccess != XReadBitmapFile (dpy, root, nom_de_bitmap, &unil, &unil,

                                                                                            &pm_essai, &nil, &nil))

                                           erreur ("%s: fichier %s incorrect\n", argv[0], nom_de_bitmap);

      

       /*

        * Mettre ce fond a la fenetre principale

        */

 

       XSetWindowBackgroundPixmap (dpy, fen, pm_essai);

       /*

        * Faire afficher la fenetre et attendre les evenements

        */

 

       XMapWindow (dpy, fen);

 

       for (;;) {

              XEvent ev;

              XNextEvent (dpy, &ev);

       }

}

 


 

Exercice 3 : (Nom d'une fenêtre et attributs de gravité des sous-fenêtres)

Créer une fenêtre possédant une bannière dont le titre, inscrit par le window manager, est suggéré dans le programme avec la fonction XStoreName (dpy,win,string). Cette fenêtre comportera un certain nombre de sous-fenêtres, rangées en ligne, et possédant un fond différent de la fenêtre principale. Faire varier les valeurs de l'attribut win_gravity et observer les modifications obtenues lors d'un changement de taille de la fenêtre parent.

 

#include <stdio.h>

#include <X11/Xlib.h>

#include <X11/Xutil.h>

#include <X11/bitmaps/star>

#include <X11/bitmaps/gray3>

 

/*

 * nom_de_bitmap est le nom du fichier bitmap qui sert a tapisser le fond de la

 * fenetre principale. On pourra le modifier en le passant en argument

 */

char*   nom_de_bitmap =  "/usr/include/X11/bitmaps/wide_weave";

 

#define LARGEUR   300       /* la largeur de la fenetre */

#define HAUTEUR   400       /* la hauteur de la fenetre */

#define COTE             40  /* la largeur d'une sous fenetre */

#define SUIV               50  /* la distance entre les sous-fenetres */

 

char*                                display_name = NULL;

unsigned long               white_pixel, black_pixel;

Display*                         dpy;

Window                          root;

int                              screen;

 

extern void erreur (/* char* message, a1, a2, a3 */);   /* cf. exercice 1 */

 

main (argc, argv) char **argv;

{

       Window                  fen;

       Window                  sousfen [5];

       int                       i, j,

                                           nil;                     /* un entier bidon */

       unsigned int           u_nil;                /* un bidon non signe */

 

       Pixmap                    gray3, pm_de_fond;

       XSetWindowAttributes xsw;

 

       /*

        * Regarder si on a passe un argument au programme,

        * si oui modifier nom_de_bitmap

        */

 

       if (argc == 3 && !strcmp (argv [1], "-b"))

                     nom_de_bitmap = argv [2];

 

       /*

        * Connexion au serveur et initialisation des variables globales

        */

       dpy = XOpenDisplay (display_name);

       if (dpy == NULL)

                     erreur ("%s: Cannot open display\n", argv[0]);

       screen               =     DefaultScreen (dpy);

       root                    =     RootWindow (dpy, screen);

       white_pixel     =     WhitePixel (dpy, screen);

       black_pixel     =     BlackPixel (dpy, screen);

 

       /*

        * Creation de la fenetre principale

        */

       fen = XCreateSimpleWindow (dpy, root, 100, 100, LARGEUR, HAUTEUR, 2,

                                                                      white_pixel, black_pixel);

 

       /*

        * Creation des pixmap a partir des donnees incluses en debut de fichier

        */

       gray3 = XCreateBitmapFromData (dpy, root, gray3_bits, gray3_width,

                                                                              gray3_height);

       /*

        * Creation d'un pixmap a partir d’un fichier

        */

       if (BitmapSuccess != XReadBitmapFile (dpy, root, nom_de_bitmap, &unil, &unil,

                                                                                            &pm_de_fond, &nil, &nil))

                            erreur ("%s: fichier %s non charge\n", argv[0], nom_de_bitmap);

      

       /*

        * Creation des sous-fenetres. Donner une valeur pour les attributs

        *   background_pixmap, border_pixel et win_gravity

        */

       xsw.background_pixmap      =           gray3;

       xsw.border_pixel                      =           white_pixel;

       xsw.win_gravity                =           SouthEastGravity;

       for (i = 0; i < 4; i++)

              /*

               * Creer les sous-fenetres sur une meme ligne ; la taille d'une sous fenetre est

               * COTE x COTE, la position de la premiere est (SUIV, SUIV). Seule l'abscisse

               * varie avec i : (SUIV + i*SUIV, SUIV)

               */

            sousfen [i] = XCreateWindow (dpy, fen, SUIV+i*SUIV, SUIV, COTE,

                                                                                      COTE, 0, CopyFromParent, InputOutput,

                                                                                      CopyFromParent, CWBackPixmap | 

                                                                                      CWBorderPixel |CWWinGravity, &xsw);

       /*

        * Mettre un fond a la fenetre principale

        */

 

       XSetWindowBackgroundPixmap (dpy, fen, pm_de_fond);

       /*

        * lui donner un nom pour le window manager avec la fonction XStoreName

        */

        

       XStoreName(dpy, fen, "  Quel joli nom ! ");

       /*

        * Afficher les sous-fenetres avant la mere

        */

       XMapSubwindows (dpy, fen);

       XMapWindow (dpy, fen);

 

       /*

        * Ici la boucle d'evenement ne sert a rien car on n'a pas selectionne d'evenement.

        * Cependant, l'appel a XNextEvent d'envoyer les requêtes au serveur et d'attendre

        * sans quitter le programme. On evite ainsi les appels a XFlush, sleep ou getchar

        */

 

for (;;) {

              XEvent ev;

              XNextEvent (dpy, &ev);

       }

}

 

2. Les événements

 

Exercice 4 : (Impression du type des événements)

Ecrire un programme permettant d'imprimer, pour chaque événement reçu, le type de l'événement reçu. Créer une fenêtre, l'afficher et lui faire subir un certain nombre de modifications à l'aide du window manager pour observer les événements reçus.

 

 

 

#include <stdio.h>

#include <X11/Xlib.h>

 

#define                    LARGEUR     300

#define                    HAUTEUR     200

 

/*

 * variables globales habituelles

 */

 

char*                        nom_de_station = NULL;

unsigned long       pixel_blanc, pixel_noir;

Display*                 dpy;

Window                  root;

int                       screen;

Window                  fenetre;

 

/*

 * Le tableau de chaines de caracteres qui permettra d'imprimer le type des evenements

 * L'ordre de la declaration est important car il faut que la chaine de rang i dans le tableau

 * corresponde bien au symbole de type (entier) i defini par la Xlib

 */

 

static char * nom_du_type[] = {

       "",

       "",

       "KeyPress",

       "KeyRelease",

       "ButtonPress",

       "ButtonRelease",

       "MotionNotify",

       "EnterNotify",

       "LeaveNotify",

       "FocusIn",

       "FocusOut",

       "KeymapNotify",

       "Expose",

       "GraphicsExpose",

       "NoExpose",

       "VisibilityNotify",

       "CreateNotify",

       "DestroyNotify",

       "UnmapNotify",

       "MapNotify",

       "MapRequest",

       "ReparentNotify",

       "ConfigureNotify",

       "ConfigureRequest",

       "GravityNotify",

       "ResizeRequest",

       "CirculateNotify",

       "CirculateRequest",

       "PropertyNotify",

       "SelectionClear",

       "SelectionRequest",

       "SelectionNotify",

       "ColormapNotify",

       "ClientMessage",

       "MappingNotify"

} ;

 

void imprime_type(ev) XEvent  ev;

{

       printf("evenement de type %s\n", nom_du_type[ev.type]);

}

 

extern void erreur (/* char *message, a1, a2, a3 */);   /* cf. exercice 1 */

 

main (argc, argv) char **argv;

{

       /*

        * On regarde s'il y a un argument d'appel, si oui, on modifie nom_de_station

        */

 

       if (argc == 3 && !strcmp (argv [1], "-d"))

                     nom_de_station = argv [2];

 

       dpy = XOpenDisplay (nom_de_station);

       if (dpy == NULL)

                     erreur ("%s: Cannot open display%s\n", argv[0],

                                                                                                           XDisplayName(nom_de_station));

       screen               =     DefaultScreen (dpy);

       root                    =     RootWindow (dpy, screen);

       pixel_blanc     =     WhitePixel (dpy, screen);

       pixel_noir       =     BlackPixel (dpy, screen);

 

       /*

        * Creation de la fenetre principale

        */

       fenetre = XCreateSimpleWindow (dpy, root, 100, 100, LARGEUR,HAUTEUR, 2,

                                                                              pixel_blanc, pixel_noir);

 

       /*

        * Selection des principaux evenements

        */

       XSelectInput(dpy,  fenetre,  KeyPressMask | KeyReleaseMask | 

                                                                ButtonPressMask | ButtonReleaseMask |

                                                                EnterWindowMask | LeaveWindowMask |

                                                                PointerMotionMask | 

                                                                FocusChangeMask |

                                                                ExposureMask |

                                                                VisibilityChangeMask |

                                                                StructureNotifyMask |

                                                                PropertyChangeMask);

       /*

        * Requete d'affichage

        */

      

       XMapWindow (dpy, fenetre);

 

       /*

        * boucle principale : reception et traitement des evenements

        */

       events();

}

 

events () {

       XEvent ev;

 

       /*

        * c'est une boucle infinie. Pour quitter le programme, envoyer le signal ^C

        */

       for (;;) {

              XNextEvent (dpy, &ev);

              imprime_type(ev);

       }

}

 

 

 

 

Exercice 5 : (Variante avec les événements souris)

Etude de ButtonPress, ButtonRelease et MotionNotify. On fait une variante à partir de l’exercice précédent : on sélectionne uniquement les trois événements ci-dessus et on imprime pour chaque événement reçu, l’identificateur de la fenêtre ayant reçu l’événement et le numéro du bouton enfoncé. Pour l'événement MotionNotify, on fera varier le masque de sélection pour ne recevoir que les mouvements se produisant le bouton2 étant enfoncé.

 

 

main (argc, argv) char **argv;

{

       /* meme debut */

 

       fenetre = XCreateSimpleWindow (dpy, root, 100, 100, LARGEUR,HAUTEUR, 2,

                                                                              pixel_blanc, pixel_noir);

       XSelectInput(dpy, fenetre, ButtonPressMask | ButtonReleaseMask

                                                                | PointerMotionMask);

       /*

        * Pour ne recevoir que les mouvements se produisant avec le bouton 2 enfoncé,

        *  il faut utiliser Button2MotionMask au lieu de PointerMotionMask

        */

 

       XMapWindow (dpy, fenetre);

 

       events();

}

 

events () {

       XEvent ev;

 

       for (;;) {

 

              XNextEvent (dpy, &ev);

              switch (ev.type) {

                     case ButtonPress:

                            /*

                             * ButtonPress indique qu'on a enfonce un bouton. On affiche

                             * l’identificateur de la fenetre et le numero du bouton. Pour un affichage

                             * plus agreable, on masque les bits de poids forts

                             */

                                   printf ("Fenetre #%d: Bouton n°%d enfonce\n",

                                                         ev.xbutton.window & 0xff, ev.xbutton.button);

                            break;

                     case ButtonRelease:

                            /*

                             * un bouton est relache

                             */

                                   printf ("Fenetre #%d: Bouton n°%d \ relache\n",

                                                         ev.xbutton.window & 0xff,ev.xbutton.button);

                            break;

                     case MotionNotify:

                            /*

                             * La souris a bouge

                             */

                                   printf ("Fenetre #%d: mouvement \ souris\n",

                                                         ev.xbutton.window & 0xff);

                                   break;

              }  

       }

}

 

 

 

Exercice 6 :  (Propagation des événements souris)

Le but de cet exercice est de recevoir les événements de type ButtonPress ButtonRelease et MotionNotify et d’observer la propagation de ces événements. Pour cela, on va créer une fenêtre principale de fond noir contenant une rangée de sous-fenêtres de fond blanc. On rendra  sensible la fenêtre principale et deux des cinq sous-fenêtres aux événements de type ButtonPress et aux mouvements de souris survenant avec un bouton enfoncé. On empêchera par contre la propagation de ces événements dans les sous-fenêtres qui n’auront pas sélectionné ces événements. On réalisera ensuite une boucle d'événement qui affiche pour chaque événement reçu l’identificateur de la fenêtre source et le numéro du bouton enfoncé, ainsi que la position du pointeur au moment de l’événement, relativement à la fenêtre source et relativement à la racine.

 

#include <stdio.h>

#include <X11/Xlib.h>

 

#define                    LARGEUR     300

#define                    HAUTEUR     400

 

char*                        nom_de_station = NULL;

unsigned long       pixel_blanc, pixel_noir;

Display*                 dpy;

Window                  root;

int                       screen;

 

void erreur (message, a1, a2, a3) char* message;

{

       printf (message, a1, a2, a3);

       exit (1);

}

 

 

events () {

       XEvent      ev;

 

       for (;;) {

 

              XNextEvent (dpy, &ev);

 

              switch (ev.type) {

 

                     case ButtonPress:

                            printf ("Fenetre #%d : Bouton %d enfonce\n",

                                           ev.xbutton.window & 0xff, ev.xbutton.button);    

                            printf("\treperage fenetre : (%d,%d)\n\treperage racine: (%d,%d)\n",

                                          ev.xbutton.x, ev.xbutton.y,ev.xbutton.x_root, ev.xbutton.y_root);

                            break;

 

                     case ButtonRelease:

                            printf ("Fenetre #%d : Bouton %d relache\n", ev.xbutton.window & 0xff,

                                           ev.xbutton.button);

                            printf("\treperage fenetre : (%d,%d)\n\treperage racine: (%d,%d)\n",

                                           ev.xbutton.x, ev.xbutton.y,ev.xbutton.x_root, ev.xbutton.y_root);

                            break;

 

                     case MotionNotify:

                            printf ("Fenetre #%d : Mouvement\n", ev.xbutton.window & 0xff);

                            printf("\treperage fenetre : (%d,%d)\n \treperage racine: (%d,%d)\n",

                                           ev.xbutton.x, ev.xbutton.y,ev.xbutton.x_root, ev.xbutton.y_root);

                            break;

              }

       }

}

 

 

main (argc, argv) char **argv;

{

       Window                                       fen;

       Window                                       sousfen [5];

       int                                            i, j;

       long                                                mask;

       XSetWindowAttributes xsw;

      

      

       dpy = XOpenDisplay (nom_de_station);

       if (dpy == NULL)

                     erreur ("%s: Cannot open display\n", argv[0]);

 

       screen               =     DefaultScreen (dpy);

       root                    =     RootWindow (dpy, screen);

       pixel_blanc     =     WhitePixel (dpy, screen);

       pixel_noir       =     BlackPixel (dpy, screen);

 

       /*

        * On cree une fenetre de fond noir et bord blanc

        */

       fen = XCreateSimpleWindow (dpy, root, 100, 100, LARGEUR, HAUTEUR, 2,

                                                                      pixel_blanc, pixel_noir);

 

       /*

        * Les sous-fenetres ont un fond blanc

        */

       xsw.background_pixel = pixel_blanc;

 

       /*

        * On interdit la propagation de ButtonPress, ButtonRelease et MotionNotify

        * Ainsi lorsque l'on appuiera sur un bouton dans une sous-fenetre, la fenetre

        * principale n'en sera pas avertie

        */

 

       mask = ButtonPressMask | ButtonReleaseMask | ButtonMotionMask;

       xsw.do_not_propagate_mask = mask;

       /*

        * Les sous-fenetres sont crees horizontalement dans la fenetre principale

        */

 

       for (i = 0; i < 4; i++)

              sousfen [i] = XCreateWindow (dpy, fen, 50+i*50, 50, 40, 40, 0,

                                                                              CopyFromParent, InputOutput,

                                                                              CopyFromParent,

                                                                              CWBackPixel | CWDontPropagate, &xsw);

       /*

        * On rend sensible aux clics la fenetre principale et deux sous-fenetres

        */

       XSelectInput (dpy, fen, mask);

       XSelectInput (dpy, sousfen [0], mask);

       XSelectInput (dpy, sousfen [1], mask);

 

       /*

        * On affiche les fenetres et on traite les evenements

        */

       XMapSubwindows (dpy, fen);

       XMapWindow (dpy, fen);

       events ();

}

 

 

 

Exercice 7 : (Un pop-up menu de pattern de fond d'écran)

Faire un pop-up menu de fonds d'écran disposés trois par trois en pavés sur chaque ligne du menu. Pour cela, on créera une fenêtre principale dans laquelle ce menu apparaîtra sur enfoncement du bouton gauche de la souris. Une fois affiché, on maintient la souris enfoncée et on passe de fond en fond. Chaque entrée dans une des petites fenêtres constituant un fond d’écran provoque l’impression d’une chaîne de caractère (par printf) donnant le nom du fichier bitmap servant de fond. On doit pouvoir relâcher le bouton sur un fond d’écran et voir alors s’afficher le nom du fond sélectionné.

 

indication : on pourra utiliser un XContext pour associer la chaîne de caractères à imprimer à chaque sous-fenêtre.

 

 

 

       Pour empêcher la souris d'être saisie sur enfoncement d'un bouton, on ajoutera le masque OwnerGrabButtonMask lors de la sélection de l'événement ButtonPress. Pour que le menu puisse pendre en dehors du cadre de la fenêtre, il faut qu'il soit fille de la racine et non de la fenêtre principale. Mais l'inconvénient est alors que sa création peut être interceptée par le window manager. Pour empêcher le window manager d'intercepter les requêtes d'affichage, il faudra positionner l'attribut override_redirect à True. Sinon, le menu aurait les décorations du window manager, ce qui n'est pas souhaitable.

 

       Pour que le menu soit toujours visible, même près d'un bord de l'écran, il faudrait tester la position relativement à la racine afin d'empêcher qu'il ne soit affiché en dehors.

 

 

 

 

 

 

#include <stdio.h>

#include <X11/Xlib.h>

#include <X11/Xutil.h>

 

#include <X11/bitmaps/boxes>

#include <X11/bitmaps/cross_weave>

#include <X11/bitmaps/gray3>

#include <X11/bitmaps/icon>

#include <X11/bitmaps/opendot>

#include <X11/bitmaps/root_weave>

#include <X11/bitmaps/scales>

#include <X11/bitmaps/star>

#include <X11/bitmaps/stipple>

#include <X11/bitmaps/target>

#include <X11/bitmaps/wide_weave>

#include <X11/bitmaps/xlogo11>

 

#define      LARGEUR            300

#define      HAUTEUR            400

#define      LARGMENU 150

#define      HAUTMENU       120

 

char*                        display_name = NULL;

unsigned long       white_pixel, black_pixel;

Display*                 dpy;

Window                  root;

int                       screen;

 

Window                  win, menu;

 

XContext         context;

 

char*                        Message [] = {

                                                  "gray3",

                                                  "target",

                                                  "scales",

                                                  "star",

                                                  "opendot",

                                                  "boxes",

                                                  "cross_weave",

                                                  "icon",

                                                  "root_weave",

                                                  "stipple",

                                                  "wide_weave",

                                                  "xlogo11",

                                   };

 

extern void erreur (/* char *message, a1, a2, a3 */);   /* cf. exercice 1 */

 

events ()  {

 

       XEvent      ev;

       Window    selection = NULL;

       char*          message;

      

       for (;;) {

 

              XNextEvent (dpy, &ev);

 

              switch (ev.type) {

                     case EnterNotify:

                     /*

                      * On entre dans un item, on affiche le nom du pattern selectionne

                      *  et on memorise quelle fenetre est selectionnee

                      */

                            selection = ev.xcrossing.window;

                            XFindContext (dpy, selection, context, &message);

                            printf ("pattern courant: %s\n",message);

                            break;

 

                     case LeaveNotify:

                     /*         

                      * On vient de quitter un item, on memorise

                      * qu'on n'a finalement rien selectionne

                      */

                            selection = NULL;

                            break;

 

                     case ButtonPress:

                     /*

                      * Le bouton 3 permet de quitter le programme

                      */

                            if (ev.xbutton.button == Button3)

                                           exit(1);

                     /*

                      * Sinon amener le menu sous le curseur et l'afficher au premier-plan

                      */

                            XMoveWindow (dpy, menu, ev.xbutton.x_root, ev.xbutton.y_root);

                            XMapRaised (dpy, menu);

                            break;

 

                     case ButtonRelease:

                     /*

                      * Enlever le menu. Si on a relache le bouton de la souris sur un item,

                      * imprimer le nom du pattern se trouvant finalement selectionne

                      */

                            XUnmapWindow (dpy, menu);

                            if (selection) {

                                   XFindContext (dpy, selection, context,  &message);

                                   printf ("Votre selection: %s\n", message);

                                   selection = NULL;

                            }

                            break;

                     default: break;

              }

       }

}

 

 

main (argc, argv) char **argv;

{

       int                                            i, j;

       Pixmap                                         pixmap[12];

       Window                                        w;

       XSetWindowAttributes xsw;

      

       dpy = XOpenDisplay (display_name);

       if (dpy == NULL)

                     error ("%s: Cannot open display\n", argv[0]);

 

       screen               =     DefaultScreen (dpy);

       root                    =     RootWindow (dpy, screen);

       white_pixel    =     WhitePixel (dpy, screen);

       black_pixel     =     BlackPixel (dpy, screen);

       /*

        * win : la fenetre principale dans laquelle apparaitra le menu

        */

 

       win = XCreateSimpleWindow (dpy, root, 100, 100, LARGEUR, HAUTEUR, 2,

                                                                      white_pixel, black_pixel);

       XSelectInput (dpy, win, ButtonPressMask | ButtonReleaseMask

                                                         | OwnerGrabButtonMask);

      

       /*

        * menu : la fenetre qui constitue le fond graphique du menu

        */

 

       xsw.border_pixel               = white_pixel;

       xsw.override_redirect      = True;

       menu = XCreateWindow (dpy, root, 0, 0, LARGMENU, HAUTMENU, 1,

                                                                CopyFromParent, InputOutput, CopyFromParent,

                                                                CWBorderPixel | CWOverrideRedirect, &xsw);

 

       /*

        * Creer les douze bitmaps pour le fond des douze items

        */

 

       pixmap[0] = XCreateBitmapFromData (dpy, root, gray3_bits, gray3_width,

                                                                                            gray3_height);

       pixmap[1] = XCreateBitmapFromData (dpy, root, target_bits, target_width,

                                                                                            target_height);

       pixmap[2] = XCreateBitmapFromData (dpy, root, scales_bits, scales_width,

                                                                                            scales_height);

       pixmap[3] = XCreateBitmapFromData (dpy, root, star_bits, star_width,

                                                                                            star_height);

       pixmap[4] = XCreateBitmapFromData (dpy, root, opendot_bits, opendot_width,

                                                                                            opendot_height);

        ... etc ...

 

       pixmap[11] = XCreateBitmapFromData (dpy, root, xlogo11_bits, xlogo11_width,

                                                                                            xlogo11_height);

             

       /*

        * Creer les sous-fenetres (items) du menu et memoriser les messages a emettre

        * lors de la selection d'un item

        */

 

       context = XUniqueContext ();

 

       for (i = 0; i < 3; i++)       

              for (j=0;j<4;j++)  {    

                            w = XCreateSimpleWindow (dpy, menu, i*LARGMENU/3,

                                                  j*HAUTMENU/4, LARGMENU/3, HAUTMENU/4, 0, 0, 0);

                            XSetWindowBackgroundPixmap (dpy, w, pixmap[i + 3*j]);

                            XSelectInput (dpy, w, EnterWindowMask | LeaveWindowMask);

                            XSaveContext (dpy, w, context, Message[i + 3*j]);

              }

 

       /*

        * Afficher la fenetre principale, les sous-fenetres du menu  mais pas le menu

        */

       XMapSubwindows (dpy, menu);

       XMapWindow (dpy, win);

 

       events ();

}

 

3. Le clavier et la souris

 

Exercice 8 : (Réception des caractères lus dans une fenêtre X)

Imprimer (sur la console de lancement du programme), les caractères frappés au clavier dans une fenêtre X,  par exemple avec la commande fprintf (stderr, ...).

 

 

#include <stdio.h>

#include <ctype.h>

#include <X11/Xlib.h>

 

extern void erreur (/* message, a1, a2, a3 */);  /* cf. exercice 1 */

 

#define LARGEUR   300

#define HAUTEUR   400

 

char*                        nom_du_terminal = NULL;

unsigned long       blanc, noir;

Display*                 dpy;

Window                  root;

int                       screen_num;

 

events () {

       XEvent      ev;

       KeySym    ks;

       int         nb;

       char             c;

      

       for (;;) {

              XNextEvent (dpy, &ev);

 

              switch (ev.type) {

                     case KeyRelease:

                            /*

                             * KeyRelease indique qu'on a relache une touche du clavier.

                             * Dans cet exemple on ne s'y attarde pas, on reagit seulement

                             * a KeyPress.

                             */

                            printf ("Key released.\n");

                            break;

                     case KeyPress:

                            /*

                             * KeyPress indique que l'on a appuye sur une touche du

                             * clavier, la fonction XLookupString retourne le code

                             * ascii de la touche frappee.

                             */

                            if (0 != XLookupString (&ev, &c, 1, &ks, 0)) {

                                   printf ("Code ascii: %x, Keysym: %d", c, ks);

                                   if (isalnum(c))

                                           printf (", caractere: \"%c\"\n", c);

                                   else /* c n'est pas alpha numerique */

                                           printf("\n");

                            }

                            else

                                   printf ("Keysym: %d\n", ks);

                            break;

              }

       }

}

 

 

main (argc, argv) char **argv;

{

       Window    win;

       Window    subwins [5];

       int         i;

             

       dpy = XOpenDisplay (nom_du_terminal);

       if (dpy == NULL)

              erreur ("%s: Cannot open display\n", argv[0]);

 

       screen_num    =     DefaultScreen (dpy);

       root                    =     RootWindow (dpy, screen_num);

       blanc          =    WhitePixel (dpy, screen_num);

       noir                    =     BlackPixel (dpy, screen_num);

 

 

       win = XCreateSimpleWindow (dpy, root, 100, 100, LARGEUR, HAUTEUR, 2,

                                                                      blanc, noir);

       XSelectInput (dpy, win, KeyPressMask | KeyReleaseMask);

 

       XMapWindow (dpy, win);

       events ();

}

 

 

 

Exercice 9 : (Etude des événements EnterNotify et LeaveNotify)

Créer deux fenêtres ayant chacune une sous-fenêtre et les placer à l'aide du window manager selon la disposition de la figure 5.5. Faire varier les masques de sélection des événements boutons pour avoir (ou non) saisie de la souris ; afficher les champs mode et détail des événements d'entrée/sortie des fenêtres.

 

#include <stdio.h>

#include <X11/Xlib.h>

 

extern void imprime_type(ev) XEvent  ev;

extern void erreur (/* message, a1, a2, a3 */);

      

#define                    LARGEUR     300

#define                    HAUTEUR     200

 

char*                        nom_de_station = NULL;

unsigned long       pixel_blanc, pixel_noir;

Display*                 dpy;

Window                  root;

int                       screen;

Window                  fenetre[2],

                                   sous_fenetre[2] ;

 

static char * nom_du_type[] = {

                     /*

                      * meme definition que dans l'exercice 4

                      */

} ;

 

main (argc, argv) char **argv;

{

       int i;

 

       if (argc == 3 && !strcmp (argv [1], "-d"))

              nom_de_station = argv [2];

       dpy = XOpenDisplay (nom_de_station);

       if (dpy == NULL)

              erreur ("%s: Cannot open display\n", argv[0]);

 

       screen               =     DefaultScreen (dpy);

       root                    =     RootWindow (dpy, screen);

       pixel_blanc     =     WhitePixel (dpy, screen);

       pixel_noir       =     BlackPixel (dpy, screen);

 

       for ( i = 0 ; i<2 ; i++) {

 

              fenetre[i] = XCreateSimpleWindow (dpy, root, 100, 100, LARGEUR,                                                                                                                                      HAUTEUR, 2, pixel_blanc, pixel_noir);

              XSelectInput(dpy, fenetre[i], ButtonPressMask | EnterWindowMask |

                                                                      LeaveWindowMask);

              sous_fenetre[i] = XCreateSimpleWindow (dpy, fenetre[i], 0, 10, LARGEUR/2,

                                                                                                   HAUTEUR/2, 2, pixel_blanc,

                                                                                                   pixel_noir);

              XSelectInput(dpy, sous_fenetre[i], ButtonPressMask | EnterWindowMask |     

                                                                                      LeaveWindowMask);

              /*         

               * variante : on pourrait rajouter le masque OwnerGrabButtonMask

               */

              XMapSubwindows (dpy, fenetre[i]);

              XMapWindow (dpy, fenetre[i]);

              printf("Affichage de mere %d et fille %d\n", fenetre[i] & 0xff,

                                   sous_fenetre[i] & 0xff);

       }

       events();

}

 

 

events () {

       XEvent ev;

 

       for (;;) {

 

              XNextEvent (dpy, &ev);

              imprime_type(ev);

              printf("\twindow %d\n", ev.xany.window & 0xff);

 

              switch(ev.type) {

 

                     case ButtonPress: /* le cas suivant est execute car il n'y a pas de break */

 

                     case ButtonRelease:

                                                  printf("\tsubwindow %d\n\tx %d\n\ty %d\n",

                                                  ev.xkey.subwindow & 0xff,

                                                  ev.xkey.x, ev.xkey.y); break;

 

                     case EnterNotify: /* le cas suivant est execute en cascade */

                     case LeaveNotify:

                                                  printf("\tsubwindow %d\n\tx %d\n\ty %d\n",

                                                                              ev.xkey.subwindow & 0xff,

                                                                              ev.xkey.x, ev.xcrossing.y);

                                                 printf("\tfocus %d\n", ev.xcrossing.focus);

                                                  printf("\tmode ");

                                                  switch(ev.xcrossing.mode) {

                                                         case NotifyNormal:

                                                                                      printf("NotifyNormal\n"); break;

                                                         case NotifyGrab: printf("NotifyGrab\n"); break;

                                                         case NotifyUngrab:

                                                                                      printf("NotifyUngrab\n"); break;

                                                         case NotifyWhileGrabbed:

                                                                                      printf("NotifyWhileGrabbed\n");

                                                                                      break;

                                                         default: printf("inconnu\n");

                                                  };

                                                  printf("\tdetail ");

 

                                                  switch(ev.xcrossing.detail){

                                                         case NotifyAncestor:

                                                                                      printf("NotifyAncestor\n"); break;

                                                         case NotifyVirtual:

                                                                                      printf("NotifyVirtual\n"); break;

                                                         case NotifyInferior:

                                                                                      printf("NotifyInferior\n"); break;

                                                         case NotifyNonlinear:

                                                                                      printf("NotifyNonlinear\n"); break;

                                                         case NotifyNonlinearVirtual:

                                                                                      printf("NotifyNonlinearVirtual\n"); break;

                                                         case NotifyPointer:

                                                                                      printf("NotifyPointer\n"); break;

                                                         case NotifyPointerRoot:

                                                                                      printf("NotifyPointerRoot\n"); break;

                                                         case NotifyDetailNone:

                                                                                      printf("NotifyDetailNone\n"); break;

                                                         default: printf("inconnu: %d\n", ev.xfocus.detail);

                                                  };

                                                  break;

              }

 

       }

}

 

4. Les contextes graphiques et les dessins

Exercice 10 :  (événements, contexte graphique)

Faire une copie de tout l'écran dans une structure Pixmap. En afficher une partie dans une fenêtre et en faire défiler le contenu sur déplacement de la souris avec le bouton 2 enfoncé.

 

 

#include <stdio.h>

#include <X11/Xlib.h>

#include <X11/Xutil.h>

 

#define LARGEUR          300

#define HAUTEUR          400

 

extern void erreur (/* message, a1, a2, a3 */);

 

char*                        nom_de_station = NULL;

unsigned long       pixel_blanc, pixel_noir;

Display*                 dpy;

Window                  root;

int                       screen;

GC                            gc;

 

Window                  win;

Pixmap                    fond;                                              

int                       pos_x,  /* position et taille de la fenetre win sur le pixmap de fond */

                                   pos_y,

                                   width=LARGEUR, height=HAUTEUR;

int                       dispWidth, dispHeight;                                       /* taille de l'ecran */

 

 

events ()  {

       XEvent      ev;

       int         i, prec_x, prec_y,

                            prec_pos_x,prec_pos_y;

      

       for ( ; ; ) {

              XNextEvent (dpy, &ev);

              switch (ev.type) {

                     case ConfigureNotify:

                     /*

                      * on met a jour la largeur et hauteur de la fenetre

                      */

                            width = ev.xconfigure.width;

                            height = ev.xconfigure.height;

                            break;

 

                     case ButtonPress:

                            switch (ev.xbutton.button) {

                                                         case Button2:

                                                         /* on sauve la position relat. a win */

                                                                                      prec_x = ev.xbutton.x;

                                                                                      prec_y = ev.xbutton.y; break;

                                                         case Button3:  /* on s'en va */

                                                                                      erreur("bye bye\n"); break;

                                                         case Button1:  /* ne rien faire */

                                                         default:              break;

                            }

                            break;

 

                     case Expose:

                     /*

                      * on reaffiche le contenu de la fenetre

                      */

                            XCopyArea(dpy,fond,win,gc,pos_x + ev.xexpose.x,

                                                         pos_y + ev.xexpose.y, ev.xexpose.width,

                                                         ev.xexpose.height, ev.xexpose.x,ev.xexpose.y);

                            break;

                     case MotionNotify:

                     /*

                      * on recupere l'ancienne position (prec_x) dans prec_pos_x

                      */

                            prec_pos_x = prec_x;

                            prec_pos_y = prec_y;

                     /*

                      * et la nouvelle position (actuelle) de la souris (avec XQueryPointer)

                      * dans prec_x et prec_y pour plus tard

                      */

                            {     Window wbidon;

                                   int bidon;

                                   XQueryPointer(dpy, win, &wbidon, &wbidon, &bidon, &bidon,

                                                                      &prec_x, &prec_y, &bidon);

                            }

                     /*

                      * On deplace le fond de la difference entre prec_x et la nouvelle position de

                      * souris, a ceci pres qu'on empeche la fenetre d'etre deplacee a l'exterieur

                      */

                            if ((pos_x += prec_pos_x - prec_x) < 0)

                                           pos_x = 0;

                            else if ( pos_x > 2*dispWidth -width)

                                           pos_x = 2*dispWidth - width;

                            if ((pos_y += prec_pos_y - prec_y) < 0)

                                           pos_y = 0;

                            else if (pos_y > 2*dispHeight - height)

                                           pos_y = 2*dispHeight -height;

                     /*

                      * maintenant les positions pos_x et pos_y sont correctes

                      */

                            XCopyArea(dpy, fond, win, gc, pos_x, pos_y, width, height, 0, 0);

                            break;

              }    

       }

}

 

 

main (argc, argv) char **argv;

{

 

       int                                            i;

       Window                                        w;

       XSetWindowAttributes xsw;

       XGCValues                                xgc;

 

       /*

        * Les initialisations habituelles

        */

 

       dpy = XOpenDisplay (nom_de_station);

       if (dpy == NULL)

              erreur ("%s: Cannot open display\n", argv[0]);

 

       screen               =     DefaultScreen (dpy);

       root                    =     RootWindow (dpy, screen);

       dispWidth       =     DisplayWidth(dpy,screen);

       dispHeight      =     DisplayHeight(dpy,screen);

       pixel_blanc     =     WhitePixel (dpy, screen);

       pixel_noir       =     BlackPixel (dpy, screen);

 

       /*

        * Creer le pixmap de fond a bouger plus grand que l'ecran

        */

 

       xgc.function                         =    GXcopy;

       xgc.subwindow_mode     =    IncludeInferiors;

       xgc.fill_style                 =    FillTiled;

 

       gc = XCreateGC(dpy,root, GCFillStyle | GCFunction | GCSubwindowMode,

                                           &xgc);

 

       fond  = XCreatePixmap(dpy, root, 2*dispWidth, 2*dispHeight,

                                                         DefaultDepth(dpy, screen));

 

       /*

        * y recopier quatre fois le contenu de la racine pour que le fond soit bien rempli

        */

 

       XCopyArea(dpy, root, fond, gc, 0, 0, dispWidth, dispHeight,0,0);

       XCopyArea(dpy, root, fond, gc, 0, 0, dispWidth, dispHeight, dispWidth,

                                   dispHeight);

       XCopyArea(dpy, root, fond, gc, 0, 0, dispWidth, dispHeight, dispWidth, 0);

       XCopyArea(dpy, root, fond, gc, 0, 0, dispWidth, dispHeight, 0, dispHeight);

       width = LARGEUR;

       height = HAUTEUR;

       win = XCreateSimpleWindow (dpy, root, 50, 100, width, height, 2, pixel_blanc,

                                                                      pixel_noir);

 

       XSelectInput (dpy, win, ButtonPressMask | Button2MotionMask |

                                                         PointerMotionHintMask | StructureNotifyMask |

                                                         ExposureMask);

 

       xsw.background_pixmap = fond;

       xsw.do_not_propagate_mask = ButtonPressMask | ButtonReleaseMask;

       XChangeWindowAttributes(dpy,win,CWBackPixmap | CWDontPropagate,&xsw);

       /*

        * afficher la fenetre et attendre les evenements

        */

       XMapWindow (dpy, win);

       events ();

}

 

 

 

 

Exercice 11 :  (contexte graphique)

Petite farce :  copier toute la racine de l'écran d'un collègue dans une fenêtre (de même taille que la racine) et l'afficher ensuite sur son écran. Prévoir tout de même un cas de sortie. On peut aussi adapter l'exercice précédent de sorte que toute la racine de l'écran se déplace quand on tire la souris en maintenant le bouton 2 enfoncé.

 

 

/*

 * debut identique a l'exercice precedent

 */

 

main (argc, argv) char **argv;

{

       /*

        * debut identique au main de l'exercice precedent

        */

 

       /*

        * Creer la fenetre principale aux memes dimensions que l'ecran

        */

       width   =    dispWidth;

       height =    dispHeight;

       win              =    XCreateSimpleWindow (dpy, root, 0, 0, dispWidth, dispHeight, 0,

                                                                                            pixel_blanc, pixel_noir);

       XSelectInput (dpy, win, ButtonPressMask | Button2MotionMask |

                                                         PointerMotionHintMask | StructureNotifyMask |

                                                         ExposureMask);

 

       /* mettre l'attribut override_redirect à True

        * pour empecher la redirection des requetes d'affichage au window manager

        */

 

       xsw.override_redirect = True;

 

       /*

        * empecher la propagation des evenements de type boutons pour eviter des

        * interferences avec les autres clients

        */

 

       xsw.do_not_propagate_mask = ButtonPressMask | ButtonReleaseMask;

       XChangeWindowAttributes(dpy, win, CWOverrideRedirect | CWDontPropagate,

                                                                &xsw);

       XMapWindow (dpy, win);

       events ();

}

 

 

 

Exercice 12 :  (Etude des événements de type Expose)

Créer une fenêtre comportant un certain nombre de sous-fenêtres. On sélectionnera sur toutes les fenêtres les événements de type Expose et on imprimera sur réception d'un événement, les composantes de la zone exposée et le champ count de l'événement. (Pour créer des événements de type Expose, créer des recouvrements suivis de mises en avant-plan à l'aide du window manager.)

 

#include <stdio.h>

#include <X11/Xlib.h>

 

#define LARGEUR   300

#define HAUTEUR   400

 

char*                        nom_du_terminal = NULL;

unsigned long       blanc, noir;

Display*                 dpy;

Window                  root;

int                       screen_num;

 

extern void erreur (/* char *message, a1, a2, a3 */);

{

       printf (message, a1, a2, a3);

       exit (1);

}

 

/*

 * La boucle d'evenements: on ne s'interesse qu'a l'evenement Expose

 */

 

events () {

       XEvent ev;

 

       for (;;) {

 

          XNextEvent (dpy, &ev);

 

          switch (ev.type) {

 

              case ButtonPress: exit(1);

              case Expose:

                           printf ("count: %d  zone: x = %d, y = %d, w = %d, h = %d\n",

                                                  ev.xexpose.count,

                                                  ev.xexpose.x,

                                                  ev.xexpose.y,

                                                  ev.xexpose.width,

                                                  ev.xexpose.height);

                            break;

          }

       }

}

 

 

main (argc, argv) char **argv;

{

       Window           win;

       Window           sousfenetres [5];

       int                i;

      

       dpy = XOpenDisplay (nom_du_terminal);

       if (dpy == NULL)

              erreur ("%s: Cannot open display\n", argv[0]);

       screen_num    =     DefaultScreen (dpy);

       root                    =     RootWindow (dpy, screen_num);

       blanc          =    WhitePixel (dpy, screen_num);

       noir                    =     BlackPixel (dpy, screen_num);

 

       /*

        * win : la fenetre principale

        */

 

       win = XCreateSimpleWindow (dpy, root, 100, 100, LARGEUR, HAUTEUR, 2,

                                                                      blanc, noir);

       XSelectInput (dpy, win, ExposureMask | ButtonPressMask);

 

       /*

        * Creation des sous-fenetres

        */

 

       for (i = 0; i < 4; i++)

              sousfenetres [i] = XCreateSimpleWindow (dpy, win, 50 + i*50, 50, 40, 40, 0,

                                                                                                   noir, blanc);

 

       /*

        * Faire afficher les fenetres

        */

 

       XMapSubwindows (dpy, win);

       XMapWindow (dpy, win);

 

       events ();

}

 

 

 

Exercice 13 : (Etude des événements de type GraphicsExpose)

Créer deux fenêtres dont l'une suffisamment grande. Quand on clique dans une fenêtre, le contenu de la racine située en (0,0) de largeur 200 et hauteur 200 est copié en (150, 200) dans la grande fenêtre avec la fonction XCopyArea. Tester ainsi les événements de type GraphicsExpose envoyés selon la configuration des deux fenêtres (disponibilité de la source, de la destination, des deux).

 

 

include <stdio.h>

#include <X11/Xlib.h>

#include <X11/Xutil.h>

#include <X11/Xproto.h>

 

#define             LARGEUR     400

#define             HAUTEUR     500

 

extern void erreur (/* message, a1, a2, a3 */);

 

char*                        nom_de_station = NULL;

unsigned long       pixel_blanc, pixel_noir;

Display*                 dpy;

Window                  root;

int                       screen;

Window                  fenetre[2];

 

static char * nom_du_type[] = {

       /*

        * meme declaration que dans l'exercice 4

        */

} ;

 

 

main (argc, argv) char **argv;

{

       int i;

 

       if (argc == 3 && !strcmp (argv [1], "-d"))

              nom_de_station = argv [2];

 

       dpy = XOpenDisplay (nom_de_station);

       if (dpy == NULL)

              error ("%s: Impossible d'ouvrir%s\n", argv[0], XDisplayName(nom_de_station));

       screen               =     DefaultScreen (dpy);

       root                    =     RootWindow (dpy, screen);

       pixel_blanc     =     WhitePixel (dpy, screen);

       pixel_noir       =     BlackPixel (dpy, screen);

 

       fenetre[0] = XCreateSimpleWindow (dpy, root, 100, 100, LARGEUR, HAUTEUR,

                                                                                      2, pixel_blanc, pixel_noir);

       XSelectInput(dpy, fenetre[0], ButtonPressMask);

       XMapWindow (dpy, fenetre[0]);

 

       fenetre[1] = XCreateSimpleWindow (dpy, root, 600, 600, 200, 200, 2, pixel_blanc,

                                                                                      pixel_noir);

       XSelectInput(dpy, fenetre[1], ButtonPressMask);

       XMapWindow (dpy, fenetre[1]);

       events();

}

 

events () {

 

       XEvent ev;

       GC gc ;

       gc = DefaultGC(dpy, screen);

       XSetGraphicsExposures (dpy, gc, True);

       for (;;) {

 

              XNextEvent (dpy, &ev);

              printf("evenement de type %s\n", nom_du_type[ev.type]);

              printf("\twindow %d\n", ev.xany.window & 0xff );

              switch(ev.type) {

                    case ButtonPress :

                                          XCopyArea(dpy, root, fenetre[0], gc, 0, 0, 200, 200, 100, 150);

                                           break;

                    case GraphicsExpose:

                                          printf("\tdrawable %d\n\tx %d\n\ty %d\n\twidth %d\n\theight \ %d\n\tmajor_code %s\n",

                                                        ev.xgraphicsexpose.drawable & 0xff,

                                                         ev.xgraphicsexpose.x, ev.xgraphicsexpose.y,

                                                         ev.xgraphicsexpose.width, ev.xgraphicsexpose.height,

                                                         ((ev.xgraphicsexpose.major_code == X_CopyArea) ?

                                                                                                   " XCopyArea\n" : " XCopyPlane\n"));

                                           break ;

              }

       }

}

 

 

 

Exercice 14 : (Un rectangle qui suit la souris)

Dans une fenêtre, quand on clique avec les boutons 1 ou 2 et que l'on maintient le bouton enfoncé, on trace le rectangle dont le premier point correspond au point où le bouton a été enfoncé et dont le sommet opposé suit la souris. Quand on relâche, le rectangle reste affiché jusqu'au prochain enfoncement de bouton. On quitte sur enfoncement du bouton 3.

 

 

#include <X11/Xlib.h>

#include <X11/Xutil.h>

#include <ctype.h>

#include <stdio.h>

 

extern void erreur (/* message, a1, a2, a3 */);

 

#define LARGEUR 600

#define HAUTEUR 600

 

char*                        nom_de_station = NULL;

unsigned long       pixel_blanc, pixel_noir;

Display*                 dpy;

Window                  root;

int                       screen;

GC                            gc;

Window                  zone ;                              /* la fenetre principale */

 

events ()  {

       XEvent      ev;

       int         i;

       int         x1, y1,                                   /* ancien cadre */

                            hauteur1, largeur1,                   

                            x, y, hauteur, largeur,               /* nouveau cadre */

                   x_dep, y_dep;                            /* position de depart */

 

       for ( ; ; ) {

              XNextEvent (dpy, &ev);

              switch (ev.type) {

                     case ButtonPress:

                                 /*

                                                          * on efface l'ancien cadre et on note le point de depart

                                                          */

                                           XDrawRectangle (dpy, zone, gc, x1 , y1 , largeur1 , hauteur1);  

                                           x_dep = ev.xbutton.x;

                                           y_dep = ev.xbutton.y;

                                                         /*

                                                          * sauvegarde du cadre actuel

                                                          */

                                           x = x_dep;

                                           y = y_dep;

                                           largeur = 0;

                                           hauteur = 0;

                                           break;

 

                     case ButtonRelease :

                                                         /*

                                                          * on note les dimensions au point relache

                                                          */

                                           x1 = x;

                                           y1 = y;

                                           largeur1 = largeur;

                                           hauteur1 = hauteur;

                                           break;

 

                     case MotionNotify:

                                                         /*

                                                         /* on doit effacer le cadre actuel et dessiner le nouveau

                                                          */

                                           XDrawRectangle (dpy, zone, gc, x , y , largeur , hauteur);

 

                                           largeur = abs(ev.xmotion.x - x_dep);

                                           hauteur = abs(ev.xmotion.y - y_dep);  

                                           if (ev.xmotion.x < x_dep)

                                                         x = ev.xmotion.x;

                                           else

                                                         x = x_dep;

                                           if (ev.xmotion.y < y_dep)

                                                         y = ev.xmotion.y;

                                           else

                                                         y = y_dep;  

      

                                           XDrawRectangle (dpy, zone, gc, x , y , largeur , hauteur);

                                           break;

 

                     default: break;

              }

       }

}     /* fin de la procedure events */

 

 

main (argc, argv) char** argv ;

{

       XGCValues    xgc;

 

       dpy = XOpenDisplay (nom_de_station);

       if (dpy == NULL)

              erreur ("%s: Cannot open nom_de_station\n");

 

       screen               =     DefaultScreen (dpy);

       root                    =     RootWindow (dpy, screen);

       pixel_blanc     =     WhitePixel (dpy, screen);

       pixel_noir       =     BlackPixel (dpy, screen);

 

       /*

        * Creation de la fenetre principale avec fond blanc

        */

      

       zone = XCreateSimpleWindow (dpy, root, 100, 100, LARGEUR , HAUTEUR, 2 ,

                                                                              pixel_noir, pixel_blanc);

 

       /*

        * On s'interesse ici aux evenements boutons pour definir un rectangle de selection

        */

      XSelectInput(dpy, zone, ButtonPressMask | ButtonReleaseMask |

                                                                ButtonMotionMask);                     

      

       /*

        *  initialisation du contexte graphique

        */

 

       gc = DefaultGC(dpy,screen);

       XSetFunction (dpy, gc, GXinvert);

       XSetForeground (dpy, gc, pixel_noir);

       XSetBackground (dpy, gc, pixel_blanc);

      

       /*

        * Afficher la fenetre et attendre les evenements

        */

 

       XMapWindow (dpy, zone);

       events();

}

 

 

5. Les textes, les fontes et les dessins

 textes et les fontes;Exercice 15 : (lire les entrées tapées au clavier)

Faire une fenêtre dans laquelle on affichera plusieurs items de texte sur une même ligne avec XDrawText. Au centre, une petite fenêtre permettra d'entrer des touches lues sur le clavier et affichera les caractères lus sur une ligne. Les caractères BackSpace et la touche Del permettront de reculer. Le retour chariot effacera la ligne courante.

 

#include <X11/Xlib.h>

#include <X11/cursorfont.h>

#include <X11/keysym.h>

#include <ctype.h>

#define      ESPACE          3

#define      LARGEUR     500

#define      HAUTEUR     400

#define error(mes) { printf(mes); exit(1); }

 

char*                        display = 0;

unsigned long       white_pixel, black_pixel;

Display                    *dpy;

Window                  root;

int                       screen;

GC                            gc;

 

Window                  win, wsaisie;

 

char*                        fontname = "10x20";

Font                          font1;

 

char*                        SaisieFont = "8x13";

XFontStruct* xfs;                     /* pour la partie saisie */

int                       sf_height;         /* hauteur de la fonte de saisie */

 

XTextItem      LigneDeTexte [] = {

       { "Dans cette ",0, 0, None },

       { "fenetre,", 0, 0, None },

       { "c'est XDrawText ", 0, 0, None},

       { "qui affiche", 0, 11, None },

       };

 

#define NITEMS (sizeof(LigneDeTexte)/sizeof(XTextItem))

 

#define NBCHAR     40

static char        Tampon[NBCHAR];

 

static int    Colonne = 0;

static int    PoxY;

 

main (argc, argv) char **argv;

{

       int i;

       Cursor cursor1, cursor2;

      

       /*

        * Initialisations habituelles et test des arguments pour recuperer le nom de la fonte

        */

 

       if (argc == 3 && !strcmp (argv [1], "-f"))

                     fontname = argv [2];

 

       dpy = XOpenDisplay (display);

       if (!dpy)

                     error ("Cannot open display.\n");

 

       screen               =     DefaultScreen (dpy);

       root                    =     RootWindow (dpy, screen);

       white_pixel    =     WhitePixel (dpy, screen);

       black_pixel     =     BlackPixel (dpy, screen);

       gc                =    DefaultGC (dpy, screen);

 

       /*

        * XSetState permet de positionner les attributs foreground, background, function

        * et plane_mask du contexte graphique (attributs frequemment utilises)

        */

       XSetState (dpy, gc, black_pixel, white_pixel, GXcopy, AllPlanes);

 

       /*

        * Charger les deux fontes

        */

 

       font1 = XLoadFont (dpy, fontname);

       xfs = XLoadQueryFont (dpy, SaisieFont);

       if (!font1 || !xfs)

                     error ("La fonte ne peut pas etre chargee\n");

       sf_height = xfs->ascent + xfs->descent;

       PoxY = ESPACE + xfs->ascent;

 

       /*

        * Creer la fenetre principale de fond blanc

        */

       win = XCreateSimpleWindow (dpy, root, 100, 100, LARGEUR, HAUTEUR, 1,

                                                                      black_pixel, white_pixel);

       /*

        * Creer la fenetre de saisie

        */

       wsaisie = XCreateSimpleWindow (dpy, win, 10, HAUTEUR/2, LARGEUR-20,

                                                                              sf_height + 2*ESPACE, 1, black_pixel,

                                                                              white_pixel);

       XSelectInput (dpy, win, ExposureMask);

       XSelectInput (dpy, wsaisie, ExposureMask | KeyPressMask);

 

       /*

        * Initialiser deux curseurs differents et les associer aux fenetres

        */

       cursor1 = XCreateFontCursor (dpy, XC_umbrella);

       cursor2 = XCreateFontCursor (dpy, XC_spider);

       XDefineCursor (dpy, win, cursor1);

       XDefineCursor (dpy, wsaisie, cursor2);

 

       /*

        * Faire afficher les fenetres

        */

       XMapWindow (dpy, win);

       XMapWindow (dpy, wsaisie);

 

       /*

        * On initialise le tableau d'items de texte (pour ecrire avec XDrawText)

        */

       for (i = 0; i < NITEMS; i++) {

              LigneDeTexte[i] . nchars = strlen (LigneDeTexte[i].chars);

              LigneDeTexte[i] . font = ( i % 2 == 0   ? font1  : xfs -> fid);

       }

 

       /*

        * et la variable Tampon qui contiendra la chaine des caracteres saisis

        */

       Tampon[0]='\0';

 

       /*

        * Attendre les evenements avant d'ecrire dans les fenetres

        */

       for (;;)

              events ();

}

 

 

void events ()

{

       XEvent             ev;

       char                   c;

       KeySym    ks; /* le code symbolique du caractere lu */

       int len;

      

       XNextEvent (dpy, &ev);

       switch (ev . type) {

              case Expose:

                            /*

                             * pour optimiser, ne reafficher que sur le dernier evenement Expose

                             */

                                   if (ev.xexpose.count != 0)

                                   break;

                            /*

                             * Sinon, (re)afficher la ligne de texte de la fenetre win

                             */

                                   XDrawText (dpy, win, gc, 20, 30, LigneDeTexte, NITEMS);

 

                            /*

                             * Reafficher dans la fenetre de saisie la chaine de caracteres

                             * sauvegardee dans le tampon

                             */

                                   XSetFont (dpy, gc, xfs->fid);

                                   XDrawString (dpy, wsaisie, gc, 5, PoxY, Tampon, strlen(Tampon));

                                   break;

 

              case KeyPress:

                            /*

                             * Recuperer les caracteres : la fin de ligne efface la ligne courante

                             * Backspace permet de reculer d'un caractere

                             */

 

                                   len = XLookupString (&ev, &c, 1, &ks, 0);

                                   switch (ks) {

                                           case XK_Clear:

                                           case XK_Return: NewLine ();

                                                                                      break;

                                           case XK_Delete:

                                           case XK_BackSpace:

                                                                      if (Colonne!=0) {

                                                                              XClearWindow(dpy, wsaisie);

                                                                              Tampon[--Colonne] = '\0';

                                                                              XDrawString (dpy, wsaisie, gc, 5, PoxY,

                                                                                                                 Tampon, Colonne);

                                                                      };

                                                                      break;

 

                                           default:            if (len == 1) {

                                                                                      if (Colonne >= NBCHAR-1)

                                                                                                   NewLine();

                                                                                      Tampon[Colonne++] = c;

                                                                                      Tampon[Colonne] = '\0';

                                                                                      XDrawString (dpy, wsaisie, gc, 5, PoxY,

                                                                                                                 Tampon, Colonne);

                                                                      } else

                                                                                     printf("on a lu une touche codee par plus \

d'un caractere\n"); break;                   

                                   }     /* end switch (ks) */

              break;

       }

}

NewLine ()

{

       Tampon[0] = '\0';

       Colonne = 0;

       XClearWindow(dpy, wsaisie);

}

 

 

Exercice 16 : (Un petit logiciel de dessin)

Faire une fenêtre comportant quatre boutons placés en bas à gauche sur une horizontale (comme des boutons de télévision); au-dessus, une grande fenêtre (appelée zone) servira de cadre aux dessins. Les quatre boutons seront étiquetés par :

                 • <QUIT> pour quitter le programme

                 • <INVERT> pour afficher en inverse un rectangle sélectionné

                 • <CLEAR> pour effacer le contenu du rectangle sélectionné

                 • <NEW> pour effacer tout le contenu de la zone de dessin

Pour sélectionner un rectangle, on cliquera sur un point de la zone d'affichage; on maintiendra ensuite le bouton de souris enfoncé, puis on relâchera le bouton sur le point diamétralement opposé du rectangle. Pendant toute cette opération, un tracé du rectangle suivra le mouvement de la souris.

 

Variante : on change l'organisation du programme précédent pour mettre des handlers d'événements.

 

 

#include <X11/Xlib.h>

#include <X11/Xutil.h>

#include <ctype.h>

#include <stdio.h>

 

extern void erreur (/* char *message, a1, a2, a3 */);

 

char*                        nom_de_station = NULL;

unsigned long       pixel_blanc, pixel_noir;

Display*                 dpy;

Window                  root;

int                       screen;

GC                            gc, gc_invert;

 

/*

 * les parametres lies aux dimensions

 *

 * la zone d'affichage laisse un bord de DECAL/2 a gauche et a droite. Les boutons sont

 * places a partir de la gauche, sous la zone d'affichage. Le premier est situe a DECAL/2

 * du bord. Deux boutons voisins sont espaces de DECAL.

 */

 

#define      LARGEUR     600

#define      HAUTEUR     600

#define      DECAL            24

 

/*

* les objets de l'interface

*/

 

Window                  win,                                                       /* fenetre principale */

                                   zone ;                                             /* la zone de dessin */

Pixmap                    pixmap;                                                /* pour sauver les dessins */

 

int                       HauteurBouton, LargeurBMax, /* dimensions des boutons */

                                   LargeurZone, HauteurZone, /* dimensions de la fenetre zone */

                                   LargeurWin, HauteurWin,           /* dimensions de la fenetre win */

                                   PosFontX, PosFontY;                    /* position du texte dans un bouton */

 

XContext         context; /* pour associer des donnees aux fenetres boutons  */

 

/*

* les types de boutons

*/

 

#define      CLEAR            0

#define      QUIT          1

#define      INVERT   2

#define      NEW          3

 

typedef struct {

       int type;                          /* le type sera code par les #define precedents */

       char * caract;         /* chaine de caractères apparaissant sur le bouton */

} Donnees;

 

Donnees Table[] =            /* une table contenant les informations precedentes */

       {{QUIT," <QUIT> "},

       {CLEAR," <CLEAR> "},

       {INVERT," <INVERT> "},

       {NEW, " <NEW> "}};

 

/*

 * une astuce bien connue en C pour parametrer la taille d'un tableau

 */

 

#define NBBOUTONS sizeof(Table)/sizeof(Donnees)

 

/*

* pour recuperer les informations sur la fonte

*/

 

char*                        FonteBouton = "10x20";

XFontStruct* xfs;

int                       fheight, fwidth;

 

/*

 * la boucle de traitement des evenements

 */

 events ()  {

 

       XEvent             ev;

       Window           w ;

       Donnees *       pdata;                                             /* pour recuperer les donnees */

       Bool                   selection_existe = False ; /* pour coder l'etat de l'interface */

       int                x,y,hauteur,largeur,

                                   x_sel, y_sel,                                /* pour sauvegarder la selection */         

                                   haut_sel, larg_sel;     

       int                LargeurBouton ;

       XPoint              depart;                                     /* point de depart de selection */

       XRectangle     rect;                                                /* pour optimiser la zone de clipping */

                                  

       for (;;) {

              XNextEvent (dpy, &ev);

 

              w = ev.xany.window;

       /*

        * on discute ici selon la fenetre ayant recu l'evenement pour eviter de tester la

        * fenetre dans chaque cas du switch sur le type de l'evenement. Une meilleure

        * solution est d'utiliser des contextes de type d'evenements. Cf. exercice suivant.

        */

              if (w == zone) {

                     switch (ev.type) {

                            case Expose:

                                   /*

                                    * pour optimiser, on affecte la zone de clipping

                                    */

                                           rect.x = ev.xexpose.x;                   

                                           rect.y = ev.xexpose.y;                   

                                           rect.width = ev.xexpose.width;               

                                           rect.height = ev.xexpose.height;

                                           XSetClipRectangles(dpy, gc, 0, 0,&rect, 1, Unsorted);

                                   /*

                                    * on recopie la sauvegarde dans la zone exposee

                                    */

                                           XCopyArea(dpy, pixmap, zone, gc, 0, 0, LargeurZone,

                                                                       HauteurZone, 0, 0);

                                           XSetClipMask(dpy, gc, None); break;

                                  

                            case ButtonPress :

                                           if (selection_existe) {

                                                         /*

                                                          * on efface l'ancienne selection

                                                          */

                                                  XDrawRectangle (dpy, zone, gc_invert, x_sel, y_sel, larg_sel,

                                                                                            haut_sel); 

                                                  XDrawRectangle (dpy, pixmap, gc_invert, x_sel, y_sel,

                                                                                            larg_sel, haut_sel);

                                           };

                                           selection_existe = True ;

 

                                   /*

                                    * on sauvegarde le premier point et le rectangle d'origine

                                    */

                                           depart.x = ev.xbutton.x;

                                           depart.y = ev.xbutton.y;

                                           x = depart.x;

                                           y = depart.y;

                                           largeur = 0;

                                           hauteur = 0; break;                      

 

                            case MotionNotify :

                                           XDrawRectangle (dpy, w, gc_invert, x, y, largeur, hauteur);       

                                           XDrawRectangle (dpy, pixmap, gc_invert, x, y,  largeur, hauteur);

 

                                           largeur = abs(ev.xmotion.x - depart.x);

                                           hauteur = abs(ev.xmotion.y - depart.y);      

                                           if (ev.xmotion.x < depart.x)

                                                                x = ev.xmotion.x;

                                           else x = depart.x;

                                           if (ev.xmotion.y < depart.y)

                                                                y = ev.xmotion.y;

                                           else  y = depart.y; 

 

                                           XDrawRectangle (dpy, w, gc_invert, x, y, largeur, hauteur);

                                           XDrawRectangle (dpy, pixmap, gc_invert, x, y, largeur, hauteur);

                                           break;

                            case ButtonRelease :

                                           x_sel = x;

                                           y_sel = y;

                                           larg_sel = largeur;

                                           haut_sel = hauteur;     break;

                                          

                            default : break;

                     }

              }

              else if ((w == win) &&  (ev.type == ConfigureNotify) )  {

                                   /*

                                    * On doit changer la taille de la fenetre d'affichage

                                    */

                            LargeurWin = ev.xconfigure.width;

                            HauteurWin = ev.xconfigure.height;

                            LargeurZone = LargeurWin - DECAL;

                            HauteurZone = HauteurWin - ((3 *  DECAL) / 2) - HauteurBouton;

                            XResizeWindow(dpy, zone, LargeurZone, HauteurZone);    

              }

 

              else {  /*

                                    * un bouton a recu l'evenement, on recupere les donnees associees

                                    */

                     XFindContext(dpy, w, context, &pdata);

                     switch (ev.type) {

 

                            case Expose:

                                   /*

                                    * on reaffiche la chaine du bouton dans la zone exposee

                                    */

                                           rect.x = ev.xexpose.x;                   

                                           rect.y = ev.xexpose.y;                   

                                           rect.width = ev.xexpose.width;               

                                           rect.height = ev.xexpose.height;

                                           XSetClipRectangles(dpy, gc_invert, 0, 0, &rect,1,Unsorted);

                                           XDrawString(dpy, w, gc_invert, PosFontX, PosFontY,

                                                                       pdata->caract, strlen(pdata->caract));

                                           XSetClipMask(dpy, gc_invert, None);

                                           break;

 

                            case ButtonPress:

                                   /*

                                    * l'afficher en inverse

                                    */

                                   LargeurBouton = fwidth * (1 + strlen(pdata->caract));                               

                                   XFillRectangle (dpy,w,gc_invert,0, 0, LargeurBouton,

                                                                      HauteurBouton);

                                   break;

 

                            case ButtonRelease:

                          /*

                                    * on reaffiche encore en inverse puis

                                    * on declenche l'operation appropriee

                                    */

                                   LargeurBouton = fwidth * (1 + strlen(pdata->caract));

                                   XFillRectangle (dpy, w, gc_invert, 0, 0, LargeurBouton,

                                                                      HauteurBouton);

 

                                   switch (pdata-> type){

 

                                           case QUIT :

                                                         XCloseDisplay(dpy);       exit(0);

 

                                           case CLEAR : if (selection_existe) {

                                                         XDrawRectangle (dpy, zone, gc_invert, x_sel, y_sel,

                                                                                                   larg_sel, haut_sel);    

                                                         XDrawRectangle (dpy, pixmap, gc_invert, x_sel, y_sel,

                                                                                                   larg_sel,haut_sel);              

                                                         XSetForeground(dpy , gc , pixel_blanc);

                                                         XFillRectangle (dpy, zone, gc, x_sel, y_sel, larg_sel,

                                                                                                   haut_sel);

                                                         XFillRectangle (dpy, pixmap, gc, x_sel, y_sel, larg_sel,

                                                                                                   haut_sel);

                                                         }

                                                         break;

 

                                           case INVERT : if (selection_existe) {                              

                                                         XFillRectangle (dpy, zone, gc_invert, x_sel, y_sel,

                                                                                            larg_sel, haut_sel);

                                                         XFillRectangle (dpy, pixmap, gc_invert, x_sel, y_sel,

                                                                                            larg_sel, haut_sel);

                                                         }

                                                         break;

 

                                           case NEW :

                                                         XClearWindow(dpy, zone);

                                                         XSetForeground(dpy , gc , pixel_blanc);

                                                         XFillRectangle(dpy, pixmap, gc, 0, 0, LargeurZone,

                                                                                            HauteurZone);

                                                         break;

 

                                           default : break;

                                   }

                                   selection_existe = False ;

                                   break;

 

                            default: break;

                     }

              }

       }

}

                    

 

/*

 * Procedure de creation des boutons

 */

 

InitBoutons(){

 

       int                                            i, j, LargeurBouton, 

                                                                PosX, PosY;          /* position des fenetres boutons */

       XSetWindowAttributes xsw;

       Window                                        w ;

 

       /*

        * initialisation du contexte et des positions. Le premier bouton est situe a

        * DECAL/2  du bord.

        */

       context      =    XUniqueContext();

       PosX   =    DECAL / 2;

       PosY   =    HauteurWin - HauteurBouton - DECAL / 2;

 

       xsw.event_mask  =    ExposureMask | ButtonPressMask | ButtonReleaseMask;

 

       /*

        * Les boutons sont attaches au pole Sud-Ouest

        */

       xsw.win_gravity         =    SouthWestGravity ;

       xsw.background_pixel    =    pixel_blanc ;

       xsw.border_pixel               =    pixel_noir ;

       for (i = 0; i < NBBOUTONS ; i++) {

                     LargeurBouton = fwidth * (strlen(Table[i].caract) +  1);

                     w = XCreateWindow (dpy, win, PosX, PosY,  LargeurBouton,

                                                                      HauteurBouton , 2, CopyFromParent, InputOutput,

                                                                      CopyFromParent, CWBackPixel | CWBorderPixel |

                                                                                                          CWEventMask | CWWinGravity,

                                                                      &xsw);

                     /*

                      * on sauve les donnees associees a cette fenetre dans le contexte

                      */

                     XSaveContext (dpy, w, context, &Table[i]);

 

                     /*

                      * et on calcule la position du bouton suivant

                      */

                     PosX = PosX + LargeurBouton + DECAL;

       }

}

 

main (argc, argv) char **argv;

{

       XGCValues    xgc;

       XSetWindowAttributes   xswa;

 

       dpy = XOpenDisplay (nom_de_station);

       if (dpy == NULL)

                     erreur ("%s: Cannot open nom_de_station\n");

 

       screen               =     DefaultScreen (dpy);

       root                    =     RootWindow (dpy, screen);

       pixel_blanc     =     WhitePixel (dpy, screen);

       pixel_noir       =     BlackPixel (dpy, screen);

      

       /*

        * Creation de la fenetre principale (de fond blanc)

        */

 

       LargeurWin = LARGEUR;

       HauteurWin = HAUTEUR;

       win = XCreateSimpleWindow (dpy, root, 100, 100, LargeurWin, HauteurWin, 0,

                                                                              pixel_noir, pixel_blanc);

       /*

        * On ne s'interesse qu'aux changements de taille

        */

 

       XSelectInput (dpy, win, StructureNotifyMask);   

 

       /*

        *  initialisation d'un contexte graphique permettant de dessiner en inverse

        *  et des parametres concernant les fontes

        */

 

       xfs                             =    XLoadQueryFont(dpy , FonteBouton);

       fheight                     =    xfs->ascent + xfs->descent; 

       fwidth                      =    xfs->max_bounds.width;

 

       xgc.font                   =    xfs->fid;

       xgc.function   =     GXinvert;

       gc_invert = XCreateGC(dpy, root, GCFunction | GCFont, &xgc);

      

       /*

        * Initialisation des parametres lies a l'affichage des boutons

        */

       HauteurBouton = 2 * fheight;

       PosFontY = (HauteurBouton + fheight) /2;

       PosFontX = fwidth / 2;

        

       /*

        * Creation des fenetres boutons

        */

       InitBoutons();

      

       /*

        * Creer la zone d'affichage du dessin dans l'espace restant

        */

 

       LargeurZone = LargeurWin - DECAL;

       HauteurZone = HauteurWin - ((3 * DECAL) / 2) - HauteurBouton;

       zone = XCreateSimpleWindow (dpy, win, DECAL / 2, DECAL / 2, LargeurZone ,

                                                                              HauteurZone, 2 , pixel_noir, pixel_blanc);

       /*

       * Si on change la taille on laissera le contenu attache au pole Nord-Ouest

       */

 

       xswa.bit_gravity = NorthWestGravity;

       XChangeWindowAttributes(dpy,zone,CWBitGravity,&xswa);

       /*

        * On s'interesse ici aux evenements souris pour definir le rectangle de selection

        * et aux evenements Expose pour redessiner

        */

 

       XSelectInput(dpy, zone, ExposureMask | ButtonPressMask | ButtonReleaseMask |

                                                         ButtonMotionMask);

       /*

        * Creation d'un contexte graphique pour reporter le Pixmap en blanc

        */

       gc = DefaultGC(dpy, screen);

       /* On sait que par defaut function == GXcopy et fill_style == FillSolid */

 

       XSetForeground (dpy, gc, pixel_blanc);     /* couleur des dessins: blanc */

 

       /*

        * Creation d'un pixmap permettant de sauvegarder le contenu de la zone de dessin

        */

       pixmap = XCreatePixmap(dpy, zone, XDisplayWidth(dpy, screen),

                                                                XDisplayHeight(dpy, screen),

                                                                DefaultDepth(dpy, screen));

       XFillRectangle(dpy, pixmap, gc, 0, 0, XDisplayWidth(dpy, screen),

                                           XDisplayHeight(dpy, screen));

      

       /*

        * initialisation effective du contexte gc pour dessiner avec GXcopy en noir

        */

       XSetForeground (dpy, gc, pixel_noir);  /* couleur des dessins : noir */

       XSetBackground (dpy, gc, pixel_blanc);

      

       /*

        * Afficher les fenetres et attendre les evenements

        */

       XMapSubwindows(dpy,win);

       XMapWindow (dpy, win);

 

       events();

}

 

 

 

Variante (exercice 16) : on change l'organisation du programme précédent pour mettre des handlers d'événements associés aux fenêtres dans des contextes selon le type de l'événement reçu.

 

#include <X11/Xlib.h>

#include <X11/Xutil.h>

#include <ctype.h>

#include <stdio.h>

 

extern void erreur (/* message, a1, a2, a3 */);  /* cf. exercice 1 */

 

#define      LARGEUR     600

#define      HAUTEUR     600

#define      DECAL            24

 

#define      CLEAR            0

#define      QUIT          1

#define      INVERT   2

#define      NEW          3

 

unsigned long       pixel_blanc, pixel_noir;

Display*                 dpy;

GC                            gc, gc_invert;

int                       fheight, fwidth;

 

/*

* les objets de l'interface

*/

 

Window                  win,                    /* fenetre principale */

                                   zone ;         /* la zone de dessin */

Pixmap                    pixmap;            /* pour sauver les dessins */

 

int                       HauteurBouton,

                                   LargeurBMax;

int                       LargeurZone, HauteurZone,

                                   LargeurWin, HauteurWin;

int                       PosFontX, PosFontY;

 

/*

 * Variables  pour traiter la selection

 */

 

static Bool              selection_existe = False;

static int           x, y, hauteur, largeur,

                                   x_sel, y_sel,

                                   haut_sel, larg_sel;     

 

static XPoint  depart;        /* le point de depart du rectangle de selection */

 

/*

 * le contexte d'association pour stocker les donnees 

 * associees a l'affichage des fenetres boutons

 */

XContext  boutonContext;

 

typedef struct {

       int type;                   /* coder par des symboles definis plus haut avec #define */

       char * caract; /* chaine apparaissant dans le bouton */

} Donnees;

 

Donnees Table[] =

       {{QUIT," <QUIT> "},

       {CLEAR," <CLEAR> "},

       {INVERT," <INVERT> "},

       {NEW, " <NEW> "}};

 

#define NBBOUTONS sizeof(Table)/sizeof(Donnees)

 

/*

 * Definition du type fonction qui recoit un pointeur d'evenement et le traite

 */

 

typedef void (*FoncTraitEv) ( /* &XEvent */ ) ;

 

/*

 * Tableau de contextes pour stocker le traitement de chaque type d'evenement

 */

 

XContext ContextesTraitementEv [LASTEvent] ;

 

/*

 * La fonction permettant d'enregistrer une fonction de traitement pour une fenetre

 * et un type d'evenement donne s'appelle EnregistreFonction

 */

void EnregistreFonction (w, ev_type, fonction)

Window                  w;

int                       ev_type ;

FoncTraitEv          fonction ;

{

       FoncTraitEv   f ;

 

       /*

        * Si le contexte n'existe pas encore pour ce type d'evenement, le creer

        */

       XContext context = ContextesTraitementEv [ev_type] ;

       if (context == 0)

                     context = ContextesTraitementEv [ev_type] = XUniqueContext () ;

 

       /*

        * Si une fonction est deja associee a cette fenetre, supprimer cette association

        */

       if (XFindContext (dpy, w, context, &f) == 0)

                     XDeleteContext (dpy, w, context) ;

       /*

        * Associer la fonction a la fenetre dans ce contexte

        */

       XSaveContext (dpy, w, context, fonction) ;     

}

 

/*

 * La fonction de traitement d'evenements pour la fenetre win

 */

 

void ConfigureWin (ev)

XEvent* ev;          /* ev.type == ConfigureNotify */

{     /*

        * il suffit d'adapter la taille de la fenetre zone

        * les attributs win_gravity des autres sous-fenetres

        * se chargeront de les deplacer

        */

       LargeurWin = ev->xconfigure.width;

       HauteurWin = ev->xconfigure.height;

       LargeurZone = LargeurWin - DECAL;

       HauteurZone = HauteurWin - ((3 * DECAL) / 2) - HauteurBouton;

       XResizeWindow(dpy, zone, LargeurZone, HauteurZone);     

}

 

/*

 * Les fonctions de traitement d'evenements pour la fenetre zone

 */

 

void ExposeZone        (ev)

XEvent* ev;          /* ev.type == Expose */

{

       XRectangle     rect[1];       /* pour optimiser la zone de reaffichage */

 

       rect[0].x                  = ev->xexpose.x;              

       rect[0].y                  = ev->xexpose.y;              

       rect[0].width = ev->xexpose.width;                    

       rect[0].height        = ev->xexpose.height;

       XSetClipRectangles(dpy, gc, 0, 0, rect, 1, Unsorted);

       XCopyArea(dpy, pixmap, zone,gc, 0, 0, LargeurZone, HauteurZone, 0, 0);

       XSetClipMask(dpy, gc, None);

}

 

void MotionZone(ev)

XEvent* ev;          /* le type de ev est MotionNotify */                                  

{

       XDrawRectangle (dpy, ev->xmotion.window, gc_invert, x, y, largeur, hauteur);   

       XDrawRectangle (dpy, pixmap, gc_invert, x, y, largeur, hauteur);   

 

       largeur = abs(ev->xmotion.x - depart.x);

       hauteur = abs(ev->xmotion.y - depart.y);   

       if (ev->xmotion.x < depart.x)

                     x = ev->xmotion.x;

       else x = depart.x;

       if (ev->xmotion.y < depart.y)

                     y = ev->xmotion.y;

       else  y = depart.y;  

 

       XDrawRectangle (dpy, ev->xmotion.window, gc_invert, x, y, largeur, hauteur);

       XDrawRectangle (dpy, pixmap, gc_invert, x, y, largeur, hauteur);

}

 

void ButtonPressZone (ev)

XEvent* ev;          /* ev.type == ButtonPress */

{

       if (selection_existe) {

 

       /*

        * on efface l'ancien rectangle selectionne

        */

              XDrawRectangle (dpy, zone, gc_invert, x_sel, y_sel,larg_sel,haut_sel);     

              XDrawRectangle (dpy, pixmap, gc_invert, x_sel, y_sel,larg_sel,haut_sel);

       };

       selection_existe = True ;

 

       /*

        * on sauve le premier point et le rectangle d'origine

        */

       depart.x = ev->xbutton.x;

       depart.y = ev->xbutton.y;

       x = depart.x;

       y = depart.y;

       largeur = 0;

       hauteur = 0;

}

 

void ButtonReleaseZone (ev)

XEvent* ev;          /* ev.type == ButtonRelease */

{     /*

        * on note la position du nouveau rectangle de selection

        */

       x_sel   =    x;

       y_sel   =    y;

       larg_sel     =    largeur;

       haut_sel    =    hauteur;

}

 

/*

 * Les fonctions de traitement d'evenement des boutons

 */

 

void ExposeBouton (ev)

XEvent* ev;          /* ev.type == Expose */

{

       Donnees *       pdata;  

       XRectangle     rect[1];

      

       XFindContext(dpy, ev->xexpose.window, boutonContext, &pdata);

 

       /*

       * pour optimiser, on affecte la zone de clipping

       */

       rect[0].x                  =    ev->xexpose.x;                  

       rect[0].y                  =    ev->xexpose.y;                  

       rect[0].width =     ev->xexpose.width;                

       rect[0].height        =    ev->xexpose.height;

       XSetClipRectangles(dpy, gc_invert, 0, 0, &rect,1,Unsorted);

 

       /*

        * on inverse le fond pour ecrire. Si le bouton etait selectionne,

        * ca ne marche pas. (On pourrait maintenir une donne au niveau

        * du contexte pour savoir si le bouton est selectionne ou non)

        */

 

       XDrawString(dpy, ev->xexpose.window, gc_invert, PosFontX, PosFontY,

                                           pdata->caract, strlen(pdata->caract));

       XSetClipMask(dpy, gc_invert, None);

}

 

void PressBouton (ev)

XEvent* ev;          /* ev.type == ButtonPress */

{    

       Donnees *       pdata;  

       int LargeurBouton;

      

       XFindContext(dpy, ev->xexpose.window, boutonContext, &pdata);

       LargeurBouton = fwidth *  (1 + strlen(pdata->caract));

       XFillRectangle (dpy, ev->xbutton.window, gc_invert, 0, 0, LargeurBouton,

                                           HauteurBouton);

}

 

void ReleaseBouton (ev)

XEvent* ev;          /* ev.type == ButtonRelease */

{

       Donnees *       pdata;  

       int                LargeurBouton;

             

       XFindContext(dpy, ev->xexpose.window, boutonContext, &pdata);

       LargeurBouton = fwidth * (1 + strlen(pdata->caract));

 

       /* on dessine encore en inverse */

       XFillRectangle (dpy, ev->xbutton.window, gc_invert, 0, 0, LargeurBouton,

                                           HauteurBouton);

 

       switch (pdata-> type){

 

              case QUIT :

                     XCloseDisplay(dpy);

                     exit(0);                           

 

              case CLEAR :

                     if (selection_existe) {

                            XDrawRectangle (dpy, zone, gc_invert, x_sel, y_sel, larg_sel, haut_sel);                                            XDrawRectangle (dpy, pixmap, gc_invert, x_sel, y_sel, larg_sel,

                                                                      haut_sel);           

                            XSetForeground(dpy , gc , pixel_blanc);

                            XFillRectangle (dpy, zone, gc, x_sel, y_sel, larg_sel, haut_sel);

                            XFillRectangle (dpy, pixmap, gc, x_sel, y_sel, larg_sel, haut_sel);

                     }

                     break;

 

              case INVERT :

                     if (selection_existe) {                                  

                            XFillRectangle (dpy, zone, gc_invert, x_sel, y_sel, larg_sel, haut_sel);

                            XFillRectangle (dpy, pixmap, gc_invert, x_sel, y_sel, larg_sel, haut_sel);

                     }

                     break;

 

              case NEW :

                     XClearWindow(dpy, zone);

                     XSetForeground(dpy , gc , pixel_blanc);

                     XFillRectangle(dpy, pixmap, gc, 0, 0, LargeurZone, HauteurZone);

                     break;

 

              default : break;

       }

       selection_existe = False ;

}

 

 

/*

 * la boucle de traitement des evenements du programme principal

 */

 

void MainEventLoop ()

{

 

       while (True) {

              XEvent             ev ;

              XContext  context ;

              FoncTraitEv   fonction ;

 

              XNextEvent (dpy, &ev) ;

      

              /*

               * Recuperer le contexte associe a ce type d'evenement

               */

              context = ContextesTraitementEv [ev.type] ;

 

              /*

               * Recuperer et invoquer la fonction associee a la fenetre qui a recu l'evenement

               */

              if (context != 0 &&

                     XFindContext (dpy, ev.xany.window, context, &fonction) == 0)

                     (*fonction) (&ev) ;

              else

                     fprintf(stderr, "evenement mais non traite par l'application\n") ;

       }

}

void InitBoutons () {

int                                            i, j, LargeurBouton, 

                                                         PosX, PosY;                 /* position des fenetres boutons */

XSetWindowAttributes xsw;

Window                                        w;

 

       /*

        * initialisation du contexte de sauvegarde et des positions

        */

 

       boutonContext = XUniqueContext();

       PosX = DECAL / 2;

       PosY = HauteurWin - HauteurBouton - DECAL / 2;

 

       /*

        * creation des boutons

        */

 

       xsw.event_mask = ExposureMask | ButtonPressMask | ButtonReleaseMask;

       xsw.win_gravity = SouthWestGravity ;

       xsw.background_pixel = pixel_blanc ;

       xsw.border_pixel = pixel_noir ;

 

       for (i = 0 ; i < NBBOUTONS ; i++) {

              LargeurBouton = fwidth * (strlen(Table[i].caract) + 1);

              w = XCreateWindow (dpy, win, PosX, PosY, LargeurBouton, HauteurBouton ,

                                                                2, CopyFromParent, InputOutput, CopyFromParent,

                                                                CWBackPixel | CWBorderPixel | CWEventMask |

                                                                CWWinGravity, &xsw);

              XSaveContext (dpy, w, boutonContext, &Table[i]);

              EnregistreFonction (w, Expose, ExposeBouton);

              EnregistreFonction (w, ButtonPress, PressBouton);

              EnregistreFonction (w, ButtonRelease, ReleaseBouton);

                    

              PosX = PosX + LargeurBouton + DECAL;

       };   /* end for */

}

 

 

main (argc, argv) char **argv;

{

       char*                 nom_de_station = NULL;

       Window           root;

       int                screen;

 

       /*

        * les informations sur la fonte

        */

 

       char*                         FonteBouton = "10x20";

       XFontStruct* xfs;

 

       XGCValues           xgc;

       XSetWindowAttributes   xswa;

 

       dpy = XOpenDisplay (nom_de_station);

       if (dpy == NULL)

              erreur ("%s: Cannot open nom_de_station\n");

 

       screen               =     DefaultScreen (dpy);

       root                    =     RootWindow (dpy, screen);

       pixel_blanc     =     WhitePixel (dpy, screen);

       pixel_noir =    BlackPixel (dpy, screen);

      

       /*

        * Creation de la fenetre principale avec fond blanc

        */

 

       LargeurWin = LARGEUR;

       HauteurWin = HAUTEUR;

       win = XCreateSimpleWindow (dpy, root, 100, 100, LargeurWin, HauteurWin, 0,

                                                                      pixel_noir, pixel_blanc);

 

       /*

        * On ne s'interesse qu'aux changements de taille

        */

       XSelectInput (dpy, win, StructureNotifyMask);

       EnregistreFonction (win, ConfigureNotify, ConfigureWin);

      

       /*

        *  initialisation d'un contexte graphique permettant de dessiner

        *  en inverse video et des parametres concernant les fontes

        */

 

       xfs               =    XLoadQueryFont(dpy , FonteBouton);

       fheight       =    xfs->ascent + xfs->descent; 

       fwidth        =    xfs->max_bounds.width;

       xgc.font                   =    xfs->fid;

       xgc.function   =     GXinvert; /* inverse la source */

       gc_invert = XCreateGC (dpy, root, GCFunction | GCFont,&xgc);

      

       /*

        * Initialisations des parametres lies a l'affichage des boutons

        */

 

       HauteurBouton = 2 * fheight;

       PosFontY = (HauteurBouton + fheight) /2;

       PosFontX = fwidth / 2;

        

       /*

        * Creation des boutons

        */

 

       InitBoutons();

      

       /*

        * Creer la zone de dessin dans l'espace restant

        */

       LargeurZone = LargeurWin - DECAL;

       HauteurZone = HauteurWin - ((3 * DECAL) / 2)  - HauteurBouton;

       zone = XCreateSimpleWindow (dpy, win, DECAL / 2, DECAL / 2, LargeurZone ,

                                                                              HauteurZone, 2 , pixel_noir, pixel_blanc);

       /*

       * pour les changements de taille on replacera le contenu par rapport

       * au point nord-ouest

       */

       xswa.bit_gravity = NorthWestGravity;

       XChangeWindowAttributes(dpy, zone, CWBitGravity, &xswa);

       /*

        * On s'interesse ici aux evenements boutons pour definir le rectangle de selection 

        * et aux evenements Expose pour redessiner

        */

      XSelectInput(dpy, zone, ExposureMask | ButtonPressMask | ButtonReleaseMask

                                                                | ButtonMotionMask);                          

       EnregistreFonction (zone, Expose, ExposeZone);

       EnregistreFonction (zone, MotionNotify, MotionZone);

       EnregistreFonction (zone, ButtonPress, ButtonPressZone);

       EnregistreFonction (zone, ButtonRelease, ButtonReleaseZone);

 

       /*

        * Creation d'un pixmap permettant de sauvegarder le contenu

        * de la zone de dessin

        */

       gc = DefaultGC(dpy, screen);

       /* la fonction est par defaut GXcopy et FillStyle est FillSolid */

       XSetForeground (dpy, gc, pixel_blanc);

       pixmap = XCreatePixmap(dpy, zone, XDisplayWidth(dpy, screen),

                                                                XDisplayHeight(dpy, screen),

                                                                DefaultDepth(dpy, screen));

       XFillRectangle(dpy, pixmap, gc, 0, 0, XDisplayWidth(dpy, screen),

                                           XDisplayHeight(dpy, screen));

       /*

        * initialisation effective du contexte gc pour dessiner en noir avec GXcopy

        */

       XSetForeground (dpy, gc, pixel_noir);

       XSetBackground (dpy, gc, pixel_blanc);

      

       /*

        * Afficher les fenetres et attendre les evenements

        */

       XMapSubwindows(dpy,win);

       XMapWindow (dpy, win);

       events();

}

 

 

 

Exercice 17 :  (fontes, événements et contexte d'association)

Modifier l'exercice sur le pop-up menu de pattern pour en faire un menu d'item de texte. La fonte utilisée pour les items pourra être passée en argument au programme. Pour capter les déplacements de la souris sur le menu, utiliser des fenêtres transparentes, et afficher les textes des items dans une fenêtre de fond du menu. Un contexte d'association pourra permettre de sauvegarder l’endroit où l'on doit afficher la chaîne, ainsi que la taille du fond d’item. Cette fois, un item sur lequel on passe s’affichera en inverse video. (On ne sélectionne cependant l’item que si l’utilisateur relâche le bouton dessus.) Imprimer la chaîne de caractères associée à l'item sélectionné.

 

 

#include <stdio.h>

#include <X11/Xlib.h>

#include <X11/Xutil.h>

 

#define LARGEUR   300

#define HAUTEUR   400

char*          terminal = NULL;

unsigned long       blanc, noir;

Display*   dpy;

Window           root;

int         scr_num;

GC              gc;

 

/*

 * La structure contenant les informations d'un item de menu

 */

 

typedef struct {

       char*          titre;            /* titre de l'item */

       char*          message;   /* message a afficher lors de la selection. */

       int         y;          /* ordonnee de l'item dans le menu */

       /*

        * Informations diverses: couleurs, fonctions a appeler, etc.

        */

} ItemMenu;

 

Window                  fenetre, menu;

XContext         context;

char*                        nom_fonte = "9x15";

int                       LargeurMenu;

int                       MargeMenu;

XFontStruct* FonteInfo;

int                       HauteurFonte;

 

/*

 * une table contenant les informations sur les differents items

 */

 

ItemMenu       MenuInfo [] = {

       { "Load",  "Charger", 0 },

       { "Save",   "Sauver", 0 },

       { "Rename",   "Renommer", 0 },

       { "Delete",      "Detruire", 0 },

       { "Print",   "Imprimer", 0 },

       { "Recover",   "Retrouver", 0 },

       { "Compress",       "Comprimer", 0 },

       { "Quit",   "Quitter le programme", 0  }

};

 

#define NBITEM (sizeof (MenuInfo) / sizeof (ItemMenu))

 

extern void erreur ( /* char*  message, a1, a2, a3 */ );      /* cf. exercice 1 */

CreerMenu  ()

{

       int         i;

       XSetWindowAttributes xsw;

       Window    w;

      

       /*

        * Charger la fonte.

        */

       FonteInfo = XLoadQueryFont (dpy, nom_fonte);

       if (!FonteInfo)

                     erreur ("Cannot open font %s\n", nom_fonte);

       XSetFont (dpy, gc, FonteInfo->fid);

 

       /*

        * Memoriser la hauteur de la fonte et la marge gauche du menu.

        */

       HauteurFonte = FonteInfo->ascent + FonteInfo->descent;

       MargeMenu = XTextWidth (FonteInfo, "X", 1) / 2;

 

       /*

        * Calculer la largeur du menu.

        */

       LargeurMenu = 0;

       for (i = 0; i < NBITEM; i++) {

              int largeur = XTextWidth (FonteInfo, MenuInfo[i].titre,

                                                                      strlen(MenuInfo[i].titre));

              if (largeur > LargeurMenu)

                            LargeurMenu = largeur;

       }

       LargeurMenu += 2 * MargeMenu;

             

       /*

        * Creer la fenetre de fond du menu

        */

 

       xsw.background_pixel    =    noir;

       xsw.border_pixel               =    blanc;

       xsw.override_redirect      =    True;

       xsw.event_mask         =    ExposureMask;

 

       menu = XCreateWindow (dpy, root, 0, 0, LargeurMenu, NBITEM*HauteurFonte, 1,

                                                               CopyFromParent, InputOutput, CopyFromParent,                                                                                                 CWBorderPixel | CWOverrideRedirect | CWEventMask |

                                                                CWBackPixel, &xsw);

       /*

        * Creer les fenetres d'items de classe InputOnly et leur associer a chacune un item

        * (dans le context) pour pouvoir ensuite recuperer informations associees

        */

 

       context = XUniqueContext ();

       xsw.event_mask = EnterWindowMask | LeaveWindowMask;

 

       for (i = 0; i < NBITEM; i++) {

              MenuInfo[i] . y = i * HauteurFonte;

              w = XCreateWindow (dpy, menu, 0, MenuInfo[i].y, LargeurMenu,

                                                                HauteurFonte, 0, CopyFromParent, InputOnly,

                                                                CopyFromParent, CWEventMask, &xsw);

 

              XSaveContext (dpy, w, context, &MenuInfo [i]);

       }

 

       /*

        * Afficher seulement les sous-fenetres de menu par XMapSubwindows,

        * de sorte que toutes les fenetres soient ensuite affichees par un appel a

        * XMapWindow (dpy, menu) dans la boucle d'evenements.

        */

 

       XMapSubwindows (dpy, menu);

}

 

 

InvertItem (item) ItemMenu* item;

{

       /*

        * Peindre l'item en inverse video

        */

       XFillRectangle (dpy, menu, gc, 0, item->y, LargeurMenu, HauteurFonte);

}

 

 

DessinerUnItem (selection) ItemMenu* selection;

{

       /*

        * Ecrire le texte des items

        */

 

       int i;

       int y = FonteInfo->ascent;

       for (i = 0; i < NBITEM; i++, y += HauteurFonte)

                     XDrawString (dpy, menu, gc, MargeMenu, y, MenuInfo[i].titre,

                                                         strlen (MenuInfo[i].titre));

       if (selection != NULL)

                     InvertItem (selection);

}

 

 

events ()  {

 

       XEvent             ev;

       ItemMenu*     selection = NULL;

      

       for (;;) {

 

              XNextEvent (dpy, &ev);

 

              switch (ev.type) {

                     case Expose:

                     /*

                      * Il faut reafficher le texte de l'item

                      */

                            if (ev.xexpose.count == 0)

                                           DessinerUnItem (selection);

                            break;

 

                     case EnterNotify:

                     /*

                      * On vient de rentrer dans un item. On retrouve la structure ItemMenu

                      * associee et on repeint l'item en inverse video.

                      */

                            XFindContext (dpy, ev.xcrossing.window, context, &selection);

                            InvertItem (selection);

                            break;

 

                     case LeaveNotify:

                     /*

                      * On sort d'un item. S'il etait selectionne on le repeint dans sa couleur

                      * d'origine. On annule la selection.

                      */

                            if (selection)

                                           InvertItem (selection);

                            selection = NULL;

                            break;

 

                     case ButtonPress:

                     /*

                      * On a appuye sur un bouton.

                      * On deplace le menu sous la souris et on l'affiche.

                      */

                            XMoveWindow (dpy, menu, ev.xbutton.x_root, ev.xbutton.y_root);

                            XMapRaised (dpy, menu);

                            break;

 

                     case ButtonRelease:

                     /*

                      * On a lache un bouton. On efface le menu, et si on avait selectionne

                      * quelque chose, on le dit.

                      */

                            XUnmapWindow (dpy, menu);

                            if (selection) {

                                   printf ("%s\n", selection->message);

      

                            if (!strcmp(selection->titre, "Quit"))

                                                  exit (0);

                                   selection = NULL;

                            }

                            break;

              }

       }

}

 

 

main (argc, argv) char **argv;

{

 

       if (argc == 3 && !strcmp (argv [1], "-f"))

              nom_fonte = argv [2];

 

       dpy = XOpenDisplay (terminal);

       if (dpy == NULL)

              erreur ("%s: Cannot open display %s\n", argv[0],

                            XDisplayName(terminal));

 

       scr_num    =    DefaultScreen (dpy);

       root             =    RootWindow (dpy, scr_num);

       blanc   =    WhitePixel (dpy, scr_num);

       noir             =    BlackPixel (dpy, scr_num);

       gc         =    DefaultGC (dpy, scr_num);

 

       XSetFunction (dpy, gc, GXinvert);

 

       /*

        * Creer la fenetre principale

        */

       fenetre = XCreateSimpleWindow (dpy, root, 100, 100, LARGEUR, HAUTEUR, 2,

                                                                              blanc, noir);

       XSelectInput (dpy, fenetre, ButtonPressMask | ButtonReleaseMask |

                                                                OwnerGrabButtonMask);

      

       /*

        * Creer le menu

        */

       CreerMenu ();

 

       /*

        * Faire afficher la fenetre et attendre les evenements.

        */

       XMapWindow (dpy, fenetre);

       events ();

}

 

 

 

Exercice 18 :  (fontes, événements, contextes)

Faire un jeu de pousse-pousse de 16 cases, dont 15 cases numérotées (belle fonte, assez grande et bien centrée) et une case vide noircie. Comme pour le menu de l'exercice précédent, on se servira d'un contexte d’association pour sauvegarder l’endroit où afficher le texte dans la fenêtre. (On pourra aussi organiser le contrôle des événements avec des contextes pour chaque type d'événements et des handlers d'événements.) Quand on clique sur une case, elle est sélectionnée (de façon interne) et si l'on relâche le bouton sur la case vide, le programme vérifie s'il peut ou non procéder à l'échange de la case vide avec la case sélectionnée. Quand l'échange est autorisé (même ligne ou même colonne et cases voisines), il est effectué à l'écran.

 

#include <stdio.h>

#include <ctype.h>

#include <X11/Xlib.h>

#include <X11/Xutil.h>

#include <X11/cursorfont.h>

 

#define COTE 400  /* la taille du cadre du jeu de pousse-pousse */

typedef struct {

       char*   numero;            /* chaine numerique */

       int         x;                  /* position de la chaine */

       int         y;

} Case;      /* les informations pour reafficher la chaine au centre de la case */

 

Case tableau[] = {

       /*

        * pour stocker les informations necessaires au reaffichage des cases

        * (remarque: les champs x,y ne sont pas encore initialises)

        */

 

       {     "1",      0,   0},

       {     "10",    0,   0},

       {     "3",      0,   0},

       {     "7",      0,   0},

       {     "14",    0,   0},

       {     "9",      0,   0},

       {     "5",      0,   0},

       {     "12",    0,   0},

       {     "4",      0,   0},

       {     "13",    0,   0},

       {     "16",    0,   0},

       {     "8",      0,   0},

       {     "15",    0,   0},

       {     "11",    0,   0},

       {     "6",      0,   0},

       {     "2",      0,   0}

};

 

char*                        nom_de_station = NULL;

unsigned long       pixel_blanc, pixel_noir;

Display*                 dpy;

Window                  root;

int                       screen;

GC                            gc;

 

/*

 * un contexte global pour stocker les informations sur le reaffichage des cases

 */

 

XContext         contexte;

Window                  cadre , cases[16];

 

 

char*   font =  "-adobe-times-bold-r-normal--34-240-100-100-p-177-iso8859-1";

 

XFontStruct     *xfs;

int                       taille_fonte;            

 

Window                  fen_sel=NULL;                                /* la case selectionnee */

int                       fen_sel_x, fen_sel_y,                      /* position de la case selectionnee */

                                   vide_x, vide_y;                                 /* position de la case vide */

 

extern void erreur (/* format, a1, a2, a3 */);

 

typedef void (*FoncTraitEv) ( /* &XEvent */ ) ;

 

/*

 * Tableau de contextes pour stocker le traitement de chaque type d'evenement

 */

 

XContext ContextesTraitementEv [LASTEvent] ;

 

/*

 * La fonction permettant d'enregistrer une fonction de traitement pour une fenetre

 * et un type d'evenement donne

 */

 

void  EnregistreFonction (w, ev_type, fonction)

Window                  w;

int                       ev_type ;

FoncTraitEv          fonction ;

{

       FoncTraitEv   f ;

 

       /*

        * Si le contexte n'existe pas encore pour ce type d'evenement, le creer

        */

 

       XContext context = ContextesTraitementEv [ev_type] ;

       if (context == 0)

              context = ContextesTraitementEv [ev_type] = XUniqueContext () ;

 

       /*

        * Si une fonction est deja associee, la supprimer

        */

 

       if (XFindContext (dpy, w, context, &f) == 0)

              XDeleteContext (dpy, w, context) ;

       /*

        * Associer la fonction a la fenetre dans ce contexte

        */

 

       XSaveContext (dpy, w, context, fonction) ;

}

 

/*

 * les "handlers" d'evenements des cases numerotees

 */

 

void ExposeCaseNum (ev)

XEvent* ev;

{

       XRectangle    xrect;

       Case *        infos; /* les informations sur la case courante */

      

       XFindContext(dpy,ev->xany.window,contexte,&infos);

       XDrawString(dpy,ev->xexpose.window,gc,

                     infos->x,infos->y,infos->numero,

                     strlen(infos->numero));

}

 

void PressCaseNum (ev)

XEvent* ev;

{

       Window           w_nil;

       unsigned int    nil;

 

              /* cette fenetre est selectionnee */

       fen_sel = ev->xbutton.window;

              /*

               * on sauvegarde ses composantes

               */

       XGetGeometry(dpy, fen_sel, &w_nil,&fen_sel_x, &fen_sel_y, &nil, &nil, &nil,

                                           &nil);

}

 

/*

 * la fonction de traitement pour la case vide

 */

 

void PressCaseVide (ev)

XEvent* ev;

{

       Window           w_nil;

       unsigned int    nil;

 

       if (fen_sel!=NULL) {

 

              /*

               * on recupere la position de la case vide

               */

              XGetGeometry(dpy, ev->xbutton.window, &w_nil, &vide_x, &vide_y, &nil,

                                                  &nil, &nil, &nil);

              /*

               * on effectue des tests pour savoir si on peut proceder a l'echange

               */

              if ( (   (vide_x == fen_sel_x)

                      && (abs(fen_sel_y - vide_y)==COTE/4))

                   ||

                   (   (vide_y == fen_sel_y)

                      && (abs(vide_x - fen_sel_x)==COTE/4))) 

              {                                

                           XUnmapWindow(dpy, fen_sel);

                           XMoveWindow(dpy, fen_sel, vide_x, vide_y);

                           XMapWindow (dpy, fen_sel);

                                  XMoveWindow(dpy,ev->xbutton.window,

                                                                      fen_sel_x,fen_sel_y);

                                   /* et on annule la selection */

                                   fen_sel = NULL;

              }

       }

}

 

 

void MainEventLoop ()

{

       while (True) {

              XEvent      ev ;

              XContext  context ;

              FoncTraitEv   fonction ;

 

              XNextEvent (dpy, &ev) ;

 

              /*

               * Recuperer le contexte associe a ce type d'evenement

               */

              context = ContextesTraitementEv [ev.type] ;

 

              /*

               * Recuperer et invoquer la fonction associee a la fenetre qui a recu l'evenement

               */

              if ( context != 0 &&

                   XFindContext (dpy, ev.xany.window, context, &fonction) == 0)

                           (*fonction) (&ev) ;

              else

                            fprintf(stderr, "evenement recu mais non traite par l'application\n") ;

       }

}

 

void  InitCases() {

 

       XSetWindowAttributes   xsw;

       int                                     i, j, rang, taille_num;

 

       for (i=0;i<4;i++)

          for(j=0;j<4;j++) {

              rang = 4*i + j;

              if (strcmp(tableau[rang].numero,"16")) {

                     /*

                      * c'est une case numerotee normale

                      */

                     xsw.event_mask         =    ButtonPressMask | OwnerGrabButtonMask |

                                                                              ExposureMask ;

                     xsw.background_pixel    =     pixel_blanc ;

                     xsw.border_pixel              =     pixel_noir;

             cases[rang] = XCreateWindow(dpy, cadre, i*COTE/4, j*COTE/4, COTE/4-1,

                                                                                      COTE/4-1, 1, CopyFromParent,

                                                                                      InputOutput, CopyFromParent,

                                                                                      CWEventMask | CWBackPixel |

                                                                                      CWBorderPixel, &xsw) ;

                     EnregistreFonction(cases[rang], ButtonPress, PressCaseNum);

                     EnregistreFonction(cases[rang], Expose, ExposeCaseNum);

              }

              else {

                     /*

                      * c'est la case vide

                      */

                     xsw.event_mask = ButtonPressMask | ButtonReleaseMask ;

             cases[rang] = XCreateWindow(dpy, cadre, i*COTE/4, j*COTE/4,

                                                                                            COTE/4, COTE/4, 0, CopyFromParent,

                                                                                            InputOnly, CopyFromParent,

                                                                                            CWEventMask, &xsw) ;

                     EnregistreFonction(cases[rang], ButtonPress, PressCaseVide);

                     EnregistreFonction(cases[rang], ButtonRelease, PressCaseVide);

              }

 

              /* on initialise en outre les informations de position d'affichage de la chaine

               * relativement a la fonte

               */

             

              taille_num = XTextWidth(xfs,tableau[rang].numero,

                                                                      strlen(tableau[rang].numero));

              tableau[rang].x = (COTE/4-taille_num)/2;

              tableau[rang].y = (COTE/4-taille_fonte)/2+xfs->ascent;

 

              /* on associe a chaque fenetre cree dans le contexte l'adresse de la case du

               * tableau  qui la decrit. On pourra ensuite, connaissant la fenetre, retrouver le

               * numero a imprimer et la position ou l'imprimer dans la fenetre

               */

 

                    XSaveContext (dpy,cases[rang],contexte,&tableau[rang]) ;

       }

}

 

 

main (argc, argv) 

char *argv[] ;

{

       XGCValues           gcval;

       Cursor                      cursor;

      

       dpy = XOpenDisplay (nom_de_station) ;

       if (dpy == NULL)

              erreur ("%s: Cannot open display\n", argv[0]);

             

       screen               =     DefaultScreen (dpy) ;

      root                    =     XDefaultRootWindow (dpy) ;

      pixel_blanc     =     WhitePixel (dpy,screen) ;

      pixel_noir       =     BlackPixel (dpy,screen) ;

 

       xfs               =    XLoadQueryFont(dpy,font);

      taille_fonte     =     xfs->ascent + xfs->descent;

 

       gcval.function              =    GXcopy;

       gcval.font                      =    xfs->fid;

       gcval.background       =    pixel_blanc;

       gcval.foreground        =    pixel_noir;

       gc = XCreateGC(dpy, root, GCFunction | GCBackground | GCForeground |

                                                                GCFont, &gcval);

       cadre = XCreateSimpleWindow (dpy, root, 0, 0, COTE, COTE, 0,

                                                              pixel_blanc, pixel_noir) ;

       XStoreName(dpy, cadre, "Pousse-Pousse");

 

       cursor = XCreateFontCursor(dpy, XC_iron_cross);

       XDefineCursor(dpy, cadre, cursor);

 

       contexte = XUniqueContext();

      

       InitCases();

 

       XMapSubwindows(dpy, cadre);

       XMapWindow (dpy, cadre);

 

       MainEventLoop(); 

}

 

 

6. La couleur

 

Exercice 19 : (Allocation et écriture de cellules)

Le but de cet exercice est de créer et d'afficher une palette de couleurs. On passe en argument au programme le nombre de niveaux souhaités dans chaque couleur fondamentale. Soit n, m et p les trois entrées, il s'agit alors de créer n*m*p couleurs et de les afficher dans des bandes régulièrement réparties sur une fenêtre (plutôt plus large que haute). Par exemple, si le programme a pris en argument 3, 4 et 2, on formera une palette comportant trois niveaux de rouge (0, max/2 et max), quatre niveaux de vert (0, max/3, 2*max/3 et max) et 2 niveaux de bleu (0 et max). Quand on cliquera avec la souris dans une des bandes de couleurs, le programme affichera les niveaux d'intensités RGB correspondants.

 

#include <X11/Xlib.h>

#include <X11/Xutil.h>

 

typedef unsigned long     PIXEL;

 

Display*                        dpy;

int                                     scr_num;

Window                                root;

GC                                           gc;

int                                     depth;

Visual*                                  visual;

Colormap                       cmap;

/*

 * Fonction qui cherche a reserver ncell cellules et renvoie un tableau

 * contenant leur indice dans la table de couleurs

 */

 

PIXEL* AllouerLesPixels (ncell)

{

       /*

        * Allouer le tableau pixels des indices de couleurs

        */

       PIXEL* pixels = (PIXEL*) malloc (ncell * sizeof(PIXEL));

 

       /*

        * On essaye d'allouer dans la colormap par defaut

        */

       if (!XAllocColorCells (dpy, cmap, False, 0, 0, pixels, ncell)) {

              /*

               * Si ca ne marche pas on essaye de creer une colormap

               */

              cmap = XCreateColormap (dpy, root, visual, AllocNone);

              if (!XAllocColorCells (dpy, cmap, False, 0, 0, pixels, ncell)) {

                     /*

                      * Ca ne marche vraiment pas du tout (trop de couleurs demandees)

                      * ou cas d'un ecran noir et blanc

                      */

                     printf ("Impossible d'allouer %d cellules de couleurs\n", ncell);

                     exit (1);

              }

       }

       return pixels;

}

 

/*

 * la fonction chargee de remplir les cellules de couleurs regulierement

 */

void RemplirCellules (pixels, nred, ngreen, nblue)

PIXEL* pixels;

{

       XColor xc;

       int r, g, b;

      

       xc.flags = DoRed | DoGreen | DoBlue;

 

       /* on va normaliser les valeurs des differents niveaux pour avoir une repartition

        * reguliere entre 0 et 65 535

        */

      

       for (r = 0; r < nred; r++) {

              xc.red = (r * 65535) / (nred - 1);

              for (g = 0; g < ngreen; g++) {

                            xc.green = (g * 65535) / (ngreen - 1);

                            for (b = 0; b < nblue; b++) {

                                           xc.blue = (b * 65535) / (nblue - 1);

                                           xc.pixel = *pixels++;

                                           XStoreColor (dpy, cmap, &xc);

                            }

              }

       }

}

 

 

main (argc, argv) char **argv;

{

       int         nred, ngreen, nblue, ncell, i;

       int         LargeurBande, hauteur;

       PIXEL*    pixels;        /* tableau personnel des indices alloues dans cmap */

       Window    w;

       XEvent      ev;

       XColor      xc;

      

       /*

        * Test des arguments et initialisations d'usage

        */

 

       if (argc != 4) {

              printf ("Usage : %s nred ngreen nblue\n", argv[0]);

              exit (1);

       }

       if (!(dpy = XOpenDisplay (0))) {

              printf ("Connexion impossible\n");

              exit (1);

       }

 

       scr_num    =    DefaultScreen (dpy);

       root             =    RootWindow (dpy, scr_num);

       gc         =    DefaultGC (dpy, scr_num);

       depth   =    DefaultDepth (dpy, scr_num);

       visual =    DefaultVisual (dpy, scr_num);

       cmap   =    DefaultColormap (dpy, scr_num);

 

       nred            =    atoi (argv[1]);

       ngreen       =    atoi (argv[2]);

       nblue   =    atoi (argv[3]);

 

       ncell = nred * ngreen * nblue;

 

       /*

        * Allouer le nombre de pixels demandes

        */

 

       pixels = AllouerLesPixels (ncell);

 

       /*

        * Remplir les cellules des pixels alloues

        */

 

       RemplirCellules (pixels, nred, ngreen, nblue);

 

       /*

        * Determiner la largeur d'une bande de couleur

        */

 

       LargeurBande = (DisplayWidth(dpy, scr_num) - 20) / ncell;

       If (LargeurBande < 2) {

              printf ("Trop de couleurs pour l'affichage\n");

              exit (1);            

       }

             

       /*

        * Creer la fenetre principale en lui associant la Colormap definie

        */

 

       hauteur = 200;

       w = XCreateSimpleWindow (dpy, root, 10, 100, ncell * LargeurBande, hauteur,

                                                                      1, 0, 1);

 

       XSetWindowColormap (dpy, w, cmap);

       XSelectInput (dpy, w, ExposureMask | ButtonPressMask);

       XMapWindow (dpy, w);

       /*

        * Attendre et traiter les evenements

        */

 

       while (True) {

 

              XNextEvent (dpy, &ev);

 

              switch (ev.type) {

 

                     case ButtonPress :

                                   /*

                                    * Si on presse le bouton 3 on sort du programme,

                                    * sinon on affiche les valeurs r g b de la case de couleur choisie

                                    */

 

                                   if (ev.xbutton.button == Button3)

                                           exit (0);

                                   xc . pixel = pixels[ev.xbutton.x / LargeurBande];

                                   XQueryColor (dpy, cmap, &xc);

                                   printf ("rgb : %d %d %d\n", xc.red, xc.green, xc.blue);

 

                                   break;

 

                     case Expose :

 

                                   /*

                                    * Il faut reafficher

                                    */

 

                                   for (i = 0; i < ncell; i++) {

 

                                           XSetState (dpy, gc, pixels[i], 0, GXcopy, AllPlanes);

                                           XFillRectangle (dpy, w, gc, i * LargeurBande, 0, LargeurBande,

                                                                                      hauteur);

                                   }

 

                                   break;

              }

       }

}