11
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);
}
}
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 ();
}
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;
}
}
}
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();
}
: (lire les entrées tapées au clavier)
Exercice 15Faire 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();
}
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;
}
}
}