1. Tutorial¶
La façon la plus simple de travailler est de programmer directement dans le répertoire de EZ-Draw en partant d’un exemple, et de compléter le Makefile fourni, voir section Compilation.
1.1. Premier programme avec une fenêtre¶
Écrivons un premier programme qui ouvre une fenêtre, appelé demo-01.c
.
Il faut commencer par inclure ez-draw.h (ligne 1, ci-dessous) :
ce fichier décrit les types et prototypes du module principal de EZ-Draw,
et il inclut aussi les fichiers .h
standards tels que <stdio.h>
,
<stdlib.h>
, <string.h>
; vous n’avez donc pas à vous en occuper.
Dans le main
, on initialise le module et le mode graphique en appelant
ez_init()
; si l’initialisation du mode graphique échoue, la fonction
affiche un message d’erreur dans le terminal, puis renvoie -1.
Dans ce cas, il faut sortir du programme en faisant exit(1)
.
Ensuite on crée une (ou plusieurs) fenêtre(s) avec la fonction
ez_window_create()
. Ces fenêtres sont affichées quand le programme atteint
ez_main_loop()
: c’est cette fonction qui fait « vivre » les fenêtres.
Elle s’arrête lorsqu’on appelle ez_quit()
, ou lorsque toutes les fenêtres
sont détruites.
Voici le fichier demo-01.c :
1
2#include "ez-draw.h"
3
4
5int main ()
6{
7 if (ez_init() < 0) exit(1);
8
9 ez_window_create (400, 300, "Demo 01: Hello World", NULL);
10
11 ez_main_loop ();
12 exit(0);
13}
14
On obtient cette fenêtre :
![demo-01](https://pageperso.lis-lab.fr/~edouard.thiel/snap-EZ-Draw-1.2/snap-demo-01.png)
1.2. Compilation¶
Pour compiler demo-01.c
dans un terminal sous Unix, taper
gcc -Wall demo-01.c ez-draw.c -o demo-01 -lX11 -lXext
ou sous Windows, taper
gcc -Wall demo-01.c ez-draw.c -o demo-01.exe -lgdi32
Pour exécuter le programme sous Unix, taper
./demo-01
ou sous Windows, taper
demo-01
À l’usage, il est beaucoup plus pratique d’utiliser la commande
make
au lieu de retaper chaque fois la ligne de commande de gcc
.
La commande make
utilise le même fichier Makefile sous Unix et Windows.
Pour tout compiler la première fois, ou pour recompiler les fichiers C modifiés, il suffit de taper dans le terminal, quelque soit votre système :
make all
En cas d’erreur, voir le chapitre Installation.
Si tout est à jour, make
affiche :
make: Rien à faire pour « all ».
Vous pouvez toujours forcer une recompilation générale en tapant :
make clean all
Enfin si vous souhaitez faire le ménage, par exemple avant de faire une sauvegarde, tapez :
make distclean
Cela effacera tous les exécutables et fichiers temporaires, et ne conservera que les fichiers sources.
- Exercice :
Éditez le fichier
demo-01.c
, et modifiez le titre par"Mon premier programme"
. Compilez en utilisantmake
puis exécutez le programme.
Voyons maintenant comment rajouter un nouveau programme. Il suffit
d’éditer le fichier Makefile, et de rajouter section C le nom de l’exécutable
dans la liste
EXECS
, EXECS_M
ou EXECS_IM
, puis d’enregistrer.
On peut alors taper make all
pour compiler le nouveau programme.
Il y a plusieurs listes pour distinguer les cas :
EXECS
: pour compiler uniquement avec ez-draw.c ;EXECS_M
: pour compiler avec ez-draw.c et-lm
(la librairie math) ;EXECS_IM
: pour compiler avec ez-draw.c,-lm
et ez-image.c (pour afficher des images).
Voici les listes actuelles dans le Makefile :
EXECS = demo-01 demo-02 demo-03 demo-04 demo-05 demo-06 demo-07 demo-08 \
demo-09 demo-11 jeu-sudoku jeu-nim jeu-vie jeu-taquin jeu-2048 \
jeu-tetris
EXECS_M = demo-10 jeu-laby jeu-ezen jeu-heziom jeu-tangram
EXECS_IM = demo-12 demo-13 demo-14 demo-15 demo-16 demo-17 \
Comme vous pouvez le constater, lorsqu’une liste est sur plusieurs lignes,
les lignes intermédiaires sont terminées par un \
.
- Exercice :
Recopiez le fichier
demo-01.c
enessai1.c
, et rajoutezessai1
dans le Makefile. Compilez en utilisantmake
puis exécutez le programme.
Vous pouvez utiliser le Makefile fourni pour des projets plus
élaborés découpés en plusieurs modules.
Supposons que votre exécutable s’appelle
monappli
,
que votre projet utilise les modules ez-draw.c
, ez-image.c
, monprog.c
et divers.c
, et que vous avez besoin des librairies -lm
et -lgomp
.
Il vous suffit de renseigner les champs EXECS_PRO
, OBJS_PRO
et
LIBS_PRO
dans le Makefile section D comme ceci :
EXECS_PRO = monappli
OBJS_PRO = ez-draw.o ez-image.o monprog.o divers.o
LIBS_PRO = -lm -lgomp
Enfin, si vous voulez créer votre projet dans un
nouveau répertoire, voici les fichiers qu’il faudra recopier dedans :
ez-image.h et ez-image.c (si vous comptez afficher des images),
Il suffira de vider les listes EXECS
dans le Makefile et de
compléter les champs nécessaires.
1.3. Gestion des évènements¶
Nous allons maintenant nous intéresser à la gestion des évènements dans les fenêtres.
Les paramètres de la fonction ez_window_create()
responsable de la
création d’une fenêtre sont :
Ez_window ez_window_create (int w, int h, const char *name, Ez_func on_event);
w
est la largeur (width) de l’intérieur de la fenêtre en pixels,
h
est la hauteur (height),
name
est le titre de la fenêtre ;
on_event
est la fonction d’évènement de la fenêtre, voir ci-dessous.
Le résultat de ez_window_create()
est de type Ez_window
;
il sert à identifier la fenêtre, et on peut l’afficher dans le terminal
sous une forme hexadécimale :
Ez_window win1;
win1 = ez_window_create (400, 300, "Demo 0: Hello World", NULL);
printf ("win1 = 0x%x\n", ez_window_get_id(win1));
Pour être en mesure de réagir aux actions de l’utilisateur (touches clavier,
mouvement de souris, clics de souris, etc) il faut réaliser ce qu’on nomme une
gestion d’évènements ; c’est pourquoi on donne la fonction on_event
en 4e argument de ez_window_create()
.
La fonction d’évènement on_event
, encore appelée callback, est une fonction
de votre programme (ou NULL
comme dans l’exemple demo-01
).
Cette fonction sera automatiquement appelée par ez_main_loop()
lors de
chaque évènement concernant la fenêtre.
La fonction on_event
doit obligatoirement avoir le prototype suivant :
void on_event (Ez_event *ev);
La variable ev
pointe sur un struct dont les champs décrivent
l’évènement ; en particulier, ev->win
indique la fenêtre concernée
par l’évènement. On détaille les autres champs de ev
dans la section
Tracer les évènements.
Découvrons deux évènements dans l’exemple suivant demo-02.c :
Le gestionnaire de fenêtre indique à votre programme si vos fenêtres doivent être redessinées (la première fois qu’elles apparaissent, si une autre fenêtre est passée devant, etc). Lorsque cela arrive, l’évènement
Expose
est déclenché. Vous devez alors redessiner l’ensemble du contenu de la fenêtreev->win
.
Lorsque l’utilisateur enfonce une touche, l’évènement
KeyPress
est déclenché. Le code de la touche est disponible dansev->key_sym
(pour key symbol). Chaque code de touche correspond à une constante préfixée parXK_
, par exemple iciXK_q
pour la touche « q ».
1
2#include "ez-draw.h"
3
4
5void win1_on_event (Ez_event *ev) /* Called by ez_main_loop() */
6{ /* for each event on win1 */
7 switch (ev->type) {
8
9 case Expose : /* We must redraw everything */
10 ez_set_color (ez_red);
11 ez_draw_text (ev->win, EZ_MC, 200, 150,
12 "To quit, press the key 'q', or click\n"
13 "on the Close button of the window");
14 break;
15
16 case KeyPress : /* A key was pressed */
17 switch (ev->key_sym) {
18 case XK_q : ez_quit (); break;
19 }
20 break;
21 }
22}
23
24
25int main ()
26{
27 if (ez_init() < 0) exit(1);
28
29 ez_window_create (400, 300, "Demo 02: Window and events", win1_on_event);
30
31 ez_main_loop ();
32 exit(0);
33}
34
On obtient cette fenêtre :
![demo-02](https://pageperso.lis-lab.fr/~edouard.thiel/snap-EZ-Draw-1.2/snap-demo-02.png)
Cet exemple demo-02.c est une première façon de gérer les événements, avec
un gros switch
dans win1_on_event
.
L’inconvénient de cette méthode est que le switch
peut rapidement grossir
lorsqu’on augmente le programme, au risque de rendre le programme illisible.
C’est pourquoi il vaut mieux éclater win1_on_event
en fonctions
spécialisées par événement (toujours avec un switch
),
ce que l’on fait dans la section suivante.
1.4. Les dessins et les couleurs¶
Comme expliqué dans la section précédente,
l’évènement Expose
signifie qu’il faut redessiner le contenu
de la fenêtre, ce qu’on fait dans l’exemple suivant en appelant
win1_on_expose
. À noter : pour chaque Expose
, EZ-Draw
vide entièrement la fenêtre (avec un fond blanc) avant de passer
l’évènement à votre programme.
La liste des dessins est donné dans la section Dessins.
Les coordonnées sont relatives à l’origine, qui est le coin en haut à gauche
de l’intérieur de la fenêtre, avec x
vers la droite et y
vers le bas.
Les dessins sont automatiquements coupés par le bord de la fenêtre, il n’y a donc pas à se préoccuper de savoir si un dessin risque de dépasser ou pas.
Les dessins sont faits dans l’épaisseur courante (par défaut 1 pixel).
On peut changer l’épaisseur courante avec ez_set_thick()
,
voir Dessins.
Les dessins sont faits dans la couleur courante (par défaut en noir).
Pour changer la couleur courante, on appelle ez_set_color()
en lui
donnant un numéro de couleur. Quelques couleurs sont prédéfinies :
ez_black
, ez_white
, ez_grey
, ez_red
, ez_green
, ez_blue
,
ez_yellow
, ez_cyan
, ez_magenta
.
On peut créer d’autres couleurs, voir Couleurs.
Voici le fichier demo-03.c :
1
2#include "ez-draw.h"
3
4
5void win1_on_expose (Ez_event *ev)
6{
7 ez_set_color (ez_magenta);
8 ez_draw_text (ev->win, EZ_BL, 10, 20, "Thickness 1");
9 ez_set_thick (1);
10 ez_draw_point (ev->win, 30, 50);
11 ez_draw_line (ev->win, 60, 35, 130, 70);
12 ez_draw_rectangle (ev->win, 160, 30, 220, 70);
13 ez_draw_circle (ev->win, 240, 30, 300, 70);
14 ez_draw_triangle (ev->win, 320, 30, 380, 40, 350, 70);
15
16 ez_set_color (ez_black);
17 ez_draw_text (ev->win, EZ_BL, 10, 100, "Thickness 2");
18 ez_set_color (ez_cyan);
19 ez_set_thick (2);
20 ez_draw_point (ev->win, 30, 130);
21 ez_draw_line (ev->win, 60, 115, 130, 150);
22 ez_draw_rectangle (ev->win, 160, 110, 220, 150);
23 ez_draw_circle (ev->win, 240, 110, 300, 150);
24 ez_draw_triangle (ev->win, 320, 110, 380, 120, 350, 150);
25
26 ez_set_color (ez_blue);
27 ez_draw_text (ev->win, EZ_BL, 10, 180, "Thickness 9");
28 ez_set_color (ez_green);
29 ez_set_thick (9);
30 ez_draw_point (ev->win, 30, 210);
31 ez_draw_line (ev->win, 60, 195, 130, 230);
32 ez_draw_rectangle (ev->win, 160, 190, 220, 230);
33 ez_draw_circle (ev->win, 240, 190, 300, 230);
34 ez_draw_triangle (ev->win, 320, 190, 380, 200, 350, 230);
35
36 ez_set_color (ez_red);
37 ez_draw_text (ev->win, EZ_BL, 10, 260, "Fill");
38 ez_set_color (ez_yellow);
39 ez_fill_rectangle (ev->win, 160, 270, 220, 310);
40 ez_fill_circle (ev->win, 240, 270, 300, 310);
41 ez_fill_triangle (ev->win, 320, 270, 380, 280, 350, 310);
42}
43
44
45void win1_on_key_press (Ez_event *ev)
46{
47 switch (ev->key_sym) {
48 case XK_q : ez_quit (); break;
49 }
50
51}
52
53
54void win1_on_event (Ez_event *ev) /* Called by ez_main_loop() */
55{ /* for each event on win1 */
56 switch (ev->type) {
57 case Expose : win1_on_expose (ev); break;
58 case KeyPress : win1_on_key_press (ev); break;
59 }
60}
61
62
63int main ()
64{
65 if (ez_init() < 0) exit(1);
66
67 ez_window_create (400, 320, "Demo 03: All drawings", win1_on_event);
68
69 ez_main_loop ();
70 exit(0);
71}
72
On obtient cette fenêtre :
![demo-03](https://pageperso.lis-lab.fr/~edouard.thiel/snap-EZ-Draw-1.2/snap-demo-03.png)
1.5. Affichage de texte¶
On peut afficher du texte n’importe où dans la fenêtre à l’aide de
la fonction ez_draw_text()
. Elle prend en argument la fenêtre,
le type d’alignement align
,
puis des coordonnées x1,y1
, enfin une chaîne de caractères
à imprimer, ou comme dans printf
, un format et des paramètres.
Tout est détaillé dans Texte et fontes.
La chaîne de caractères peut comporter des \n
, ceci provoquera
des saut de lignes dans l’affichage.
L’affichage de texte se fait dans la couleur courante, modifiable par
ez_set_color()
.
Dans l’exemple suivant demo-04.c on illustre
l’affichage de texte, ainsi que l’usage de ez_window_get_size()
pour
faire un dessin qui s’adapte aux changements de taille de la fenêtre.
1
2#include "ez-draw.h"
3
4
5void win1_on_expose (Ez_event *ev)
6{
7 int i, w, h;
8
9 ez_window_get_size (ev->win, &w, &h);
10
11 ez_set_color (ez_black);
12 for (i = 0; i <= 3; i++) {
13 ez_set_nfont (i);
14 ez_draw_text (ev->win, EZ_TC, w/2, h/2 + 25*(i-2),
15 "Font number %d", i); /* like a printf */
16 }
17
18 ez_set_nfont (0);
19 ez_set_color (ez_red);
20
21 ez_draw_text (ev->win, EZ_TL, 2, 1, "Top\nLeft");
22 ez_draw_text (ev->win, EZ_TC, w/2, 1, "Top\nCenter");
23 ez_draw_text (ev->win, EZ_TR, w-2, 1, "Top\nRight");
24 ez_draw_text (ev->win, EZ_ML, 2, h/2, "Middle\nLeft");
25 ez_draw_text (ev->win, EZ_MR, w-2, h/2, "Middle\nRight");
26 ez_draw_text (ev->win, EZ_BL, 2, h-2, "Bottom\nLeft");
27 ez_draw_text (ev->win, EZ_BC, w/2, h-2, "Bottom\nCenter");
28 ez_draw_text (ev->win, EZ_BR, w-2, h-2, "Bottom\nRight");
29}
30
31
32void win1_on_key_press (Ez_event *ev)
33{
34 switch (ev->key_sym) {
35 case XK_q : ez_quit (); break;
36 }
37
38}
39
40
41void win1_on_event (Ez_event *ev)
42{
43 switch (ev->type) {
44 case Expose : win1_on_expose (ev); break;
45 case KeyPress : win1_on_key_press (ev); break;
46 }
47}
48
49
50int main ()
51{
52 if (ez_init() < 0) exit(1);
53
54 ez_window_create (400, 300, "Demo 04: Displaying text", win1_on_event);
55
56 ez_main_loop ();
57 exit(0);
58}
59
On obtient cette fenêtre :
![demo-04](https://pageperso.lis-lab.fr/~edouard.thiel/snap-EZ-Draw-1.2/snap-demo-04.png)
1.6. Tracer les évènements¶
Dans l’exemple suivant on recense tous les évènements possibles
et on affiche dans le terminal les champs utilisables de la variable ev
(les autres champs sont réinitialisés à 0).
Voir aussi la section Évènements.
Par défaut, le bouton « Fermer » dans la barre de titre d’une des fenêtres de
l’application provoque la fin du programme. On peut changer ce réglage
d’origine en faisant ez_auto_quit(0)
:
le bouton « Fermer » provoquera l’évènement WindowClose
, comme dans l’exemple
suivant :
Voici le fichier demo-05.c :
1
2#include "ez-draw.h"
3
4
5void win1_on_expose (Ez_event *ev) /* We must redraw everything */
6{
7 ez_draw_text (ev->win, EZ_MC, 200, 150,
8 "Events are traced\nin the terminal.\n\n"
9 "Type 'q' to quit.");
10 printf ("Expose win = 0x%x\n", ez_window_get_id(ev->win));
11}
12
13
14void win1_on_button_press (Ez_event *ev) /* Mouse button pressed */
15{
16 printf ("ButtonPress win = 0x%x mx = %d my = %d mb = %d\n",
17 ez_window_get_id(ev->win), ev->mx, ev->my, ev->mb);
18}
19
20
21void win1_on_button_release (Ez_event *ev) /* Mouse button released */
22{
23 printf ("ButtonRelease win = 0x%x mx = %d my = %d mb = %d\n",
24 ez_window_get_id(ev->win), ev->mx, ev->my, ev->mb);
25}
26
27
28void win1_on_motion_notify (Ez_event *ev) /* Mouse moved */
29{
30 printf ("MotionNotify win = 0x%x mx = %d my = %d mb = %d\n",
31 ez_window_get_id(ev->win), ev->mx, ev->my, ev->mb);
32}
33
34
35void win1_on_key_press (Ez_event *ev) /* Key pressed */
36{
37 printf ("KeyPress win = 0x%x mx = %d my = %d "
38 "key_sym = 0x%x key_name = %s key_count = %d key_string = \"%s\"\n",
39 ez_window_get_id(ev->win), ev->mx, ev->my,
40 (int) ev->key_sym, ev->key_name, ev->key_count,
41 ev->key_sym == XK_Return || ev->key_sym == XK_KP_Enter ? "" : ev->key_string);
42}
43
44
45void win1_on_key_release (Ez_event *ev) /* Key released */
46{
47 printf ("KeyRelease win = 0x%x mx = %d my = %d "
48 "key_sym = 0x%x key_name = %s key_count = %d key_string = \"%s\"\n",
49 ez_window_get_id(ev->win), ev->mx, ev->my,
50 (int) ev->key_sym, ev->key_name, ev->key_count,
51 ev->key_sym == XK_Return || ev->key_sym == XK_KP_Enter ? "" : ev->key_string);
52 switch (ev->key_sym) {
53 case XK_q : ez_quit (); break;
54 }
55}
56
57
58void win1_on_configure_notify (Ez_event *ev) /* Window size changed */
59{
60 printf ("ConfigureNotify win = 0x%x width = %d height = %d\n",
61 ez_window_get_id(ev->win), ev->width, ev->height);
62}
63
64
65void win1_on_window_close (Ez_event *ev) /* Close button pressed */
66{
67 printf ("WindowClose win = 0x%x\n", ez_window_get_id(ev->win));
68}
69
70
71void win1_on_event (Ez_event *ev) /* Called by ez_main_loop() */
72{ /* for each event on win1 */
73 switch (ev->type) {
74 case Expose : win1_on_expose (ev); break;
75 case ButtonPress : win1_on_button_press (ev); break;
76 case ButtonRelease : win1_on_button_release (ev); break;
77 case MotionNotify : win1_on_motion_notify (ev); break;
78 case KeyPress : win1_on_key_press (ev); break;
79 case KeyRelease : win1_on_key_release (ev); break;
80 case ConfigureNotify : win1_on_configure_notify (ev); break;
81 case WindowClose : win1_on_window_close (ev); break;
82 default :
83 printf ("Unknown event: %d\n", ev->type);
84 }
85}
86
87
88int main ()
89{
90 if (ez_init() < 0) exit(1);
91
92 ez_window_create (400, 300, "Demo 05: Tracing events", win1_on_event);
93
94 ez_auto_quit (0); /* to get WindowClose event */
95
96 ez_main_loop ();
97 exit(0);
98}
99
On obtient cette fenêtre :
![demo-05](https://pageperso.lis-lab.fr/~edouard.thiel/snap-EZ-Draw-1.2/snap-demo-05.png)
Remarque : les évènements TimerNotify
ne sont pas traités ici,
voir Timers.
1.7. Dessiner à la souris¶
L’exemple présenté dans cette section permet de dessiner une ligne polygonale à la souris. Les coordonnées des sommets sont mémorisées dans des variables globales (on pourrait aussi éviter d’avoir des variables globales, voir Client-data). Chaque fois que le bouton de la souris est cliqué, un sommet est inséré ; à chaque déplacement de la souris avec bouton enfoncé, le dernier sommet est déplacé.
Par principe (et pour des raisons techniques), les dessins ne peuvent
être faits que lors de l’évènement Expose
. Si l’on veut mettre à jour
le dessin dans la fenêtre lors d’un autre évènement, il suffit d’envoyer
l’évènement Expose
avec la fonction ez_send_expose()
.
C’est ce que l’on fait ici pour les évènements
ButtonPress
, MotionNotify
et KeyPress
.
Voici le fichier demo-06.c :
1
2#include "ez-draw.h"
3
4#define VER_MAX 100
5int ver_nb = 0, ver_x[VER_MAX], ver_y[VER_MAX];
6
7
8void vertex_clear ()
9{
10 ver_nb = 0;
11}
12
13
14void vertex_insert (int x, int y)
15{
16 if (ver_nb >= VER_MAX) return;
17 ver_x[ver_nb] = x;
18 ver_y[ver_nb] = y;
19 ver_nb++;
20}
21
22
23void vertex_move (int x, int y)
24{
25 if (ver_nb <= 0 || ver_nb >= VER_MAX) return;
26 ver_x[ver_nb-1] = x;
27 ver_y[ver_nb-1] = y;
28}
29
30
31void draw_vertices (Ez_window win)
32{
33 int i;
34
35 ez_set_color (ez_blue);
36 for (i = 0; i < ver_nb; i++)
37 ez_draw_rectangle (win, ver_x[i]-2, ver_y[i]-2, ver_x[i]+2, ver_y[i]+2);
38}
39
40void draw_segments (Ez_window win)
41{
42 int i;
43
44 ez_set_color (ez_grey);
45 for (i = 1; i < ver_nb; i++)
46 ez_draw_line (win, ver_x[i-1], ver_y[i-1], ver_x[i], ver_y[i]);
47}
48
49
50void win1_on_expose (Ez_event *ev)
51{
52 ez_set_color (ez_black);
53 ez_draw_text (ev->win, EZ_TL, 10, 10,
54 "Click and drag the mouse in the window to draw.\n"
55 "Type Space to clear the window, 'q' to quit.");
56 draw_segments (ev->win);
57 draw_vertices (ev->win);
58}
59
60
61void win1_on_button_press (Ez_event *ev)
62{
63 vertex_insert (ev->mx, ev->my);
64 ez_send_expose (ev->win);
65}
66
67
68void win1_on_motion_notify (Ez_event *ev)
69{
70 if (ev->mb == 0) return; /* No button pressed */
71 vertex_move (ev->mx, ev->my);
72 ez_send_expose (ev->win);
73}
74
75
76void win1_on_key_press (Ez_event *ev)
77{
78 switch (ev->key_sym) {
79 case XK_q :
80 ez_quit ();
81 break;
82 case XK_space :
83 vertex_clear ();
84 ez_send_expose (ev->win);
85 break;
86 }
87}
88
89
90void win1_on_event (Ez_event *ev)
91{
92 switch (ev->type) {
93 case Expose : win1_on_expose (ev); break;
94 case ButtonPress : win1_on_button_press (ev); break;
95 case MotionNotify : win1_on_motion_notify (ev); break;
96 case KeyPress : win1_on_key_press (ev); break;
97 }
98}
99
100
101int main ()
102{
103 Ez_window win1;
104
105 if (ez_init() < 0) exit(1);
106
107 win1 = ez_window_create (400, 300, "Demo 06: Drawing wih the mouse", win1_on_event);
108
109 /* Enable double buffer to prevent window flashes */
110 ez_window_dbuf (win1, 1);
111
112 ez_main_loop ();
113 exit(0);
114}
115
On obtient cette fenêtre :
![demo-06](https://pageperso.lis-lab.fr/~edouard.thiel/snap-EZ-Draw-1.2/snap-demo-06.png)
1.8. Gérer plusieurs fenêtres¶
On peut créer autant de fenêtre que l’on veut, par des appels
à ez_window_create()
. Chaque fenêtre créée est immédiatement affichée
par dessus les autres fenêtres. Pour faire détruire une fenêtre win
(et donc
la faire disparaître de l’écran), utiliser
ez_window_destroy(win)
.
On peut cacher une fenêtre win
(c’est-à-dire la rendre invisible) avec
ez_window_show(win, 0)
puis la rendre visible (par
dessus les autres fenêtres) avec ez_window_show(win, 1)
.
Dans l’exemple qui suit, on paramètre la boucle principale en faisant
ez_auto_quit(0)
: le bouton « Fermer » de la barre de
titre d’une fenêtre ne provoquera plus la fin du programme, mais provoquera
l’évènement WindowClose
. Selon la fenêtre incriminée, on cache la fenêtre,
détruit la fenêtre ou quitte le programme.
Voici le fichier demo-07.c :
1
2#include "ez-draw.h"
3
4/* Global variables */
5Ez_window win1, win2, win3 = None; int show2 = 0;
6
7
8void win3_on_expose (Ez_event *ev)
9{
10 ez_draw_text (ev->win, EZ_TL, 10, 10,
11 "If you close this window, it will be destroyed.");
12}
13
14
15/* The user has clicked on the Close button of the window */
16
17void win3_on_window_close (Ez_event *ev)
18{
19 (void) ev; /* Tell the compiler that ev is unused */
20 ez_window_destroy (win3); win3 = None;
21}
22
23
24void win3_on_event (Ez_event *ev)
25{
26 switch (ev->type) {
27 case Expose : win3_on_expose (ev); break;
28 case WindowClose : win3_on_window_close (ev); break;
29 }
30}
31
32
33void win2_on_expose (Ez_event *ev)
34{
35 ez_draw_text (ev->win, EZ_TL, 10, 10,
36 "If you close this window, it will be hidden.");
37}
38
39
40void win2_on_window_close (Ez_event *ev)
41{
42 (void) ev;
43 ez_window_show (win2, 0); show2 = 0;
44}
45
46
47void win2_on_event (Ez_event *ev)
48{
49 switch (ev->type) {
50 case Expose : win2_on_expose (ev); break;
51 case WindowClose : win2_on_window_close (ev); break;
52 }
53}
54
55
56void win1_on_expose (Ez_event *ev)
57{
58 ez_draw_text (ev->win, EZ_TL, 10, 10,
59 "Click in this window (to get the keyboard focus),\n"
60 "then type :\n"
61 " - on 'm' to show or hide window 2;\n"
62 " - on 'c' to create or destroy window 3;\n"
63 " - on 'q' to quit.\n"
64 "\n"
65 "If you close this window, the program will end.");
66}
67
68
69void win1_on_key_press (Ez_event *ev)
70{
71 switch (ev->key_sym) {
72 case XK_q : ez_quit (); break;
73
74 case XK_m :
75 show2 = !show2; /* show or hide the window */
76 ez_window_show (win2, show2);
77 break;
78
79 case XK_c :
80 if (win3 == None) /* if the window doesn't exist, create it */
81 win3 = ez_window_create (380, 220, "Window 3", win3_on_event);
82 else { ez_window_destroy (win3); win3 = None; }
83 break;
84 }
85}
86
87
88void win1_on_window_close (Ez_event *ev)
89{
90 (void) ev;
91 ez_quit ();
92}
93
94
95void win1_on_event (Ez_event *ev)
96{
97 switch (ev->type) {
98 case Expose : win1_on_expose (ev); break;
99 case KeyPress : win1_on_key_press (ev); break;
100 case WindowClose : win1_on_window_close (ev); break;
101 }
102}
103
104
105int main ()
106{
107 if (ez_init() < 0) exit(1);
108
109 win1 = ez_window_create (400, 300, "Demo 07: Several windows", win1_on_event);
110 win2 = ez_window_create (400, 200, "Window 2", win2_on_event);
111 ez_window_show (win2, show2);
112
113 /* By default, closing any window will cause the end of the program.
114 We change this behaviour: for now on, closing any window will send
115 a WindowClose event for this window. */
116 ez_auto_quit (0);
117
118 ez_main_loop ();
119 exit(0);
120}
121
On obtient cette fenêtre :
![demo-07](https://pageperso.lis-lab.fr/~edouard.thiel/snap-EZ-Draw-1.2/snap-demo-07.png)
1.9. Saisie de texte¶
L’exemple suivant demo-08.c montre comment lire au clavier une chaîne de caractères, supprimer des caractères avec la touche Backspace, et détecter la frappe de la touche Entrée pour déclencher une action.
1
2#include "ez-draw.h"
3
4#define BUF_MAX 80
5char buf1[BUF_MAX] = "", buf2[BUF_MAX] = "";
6
7
8/* Return 1 if text must be displayed again, 2 if text is validated, else 0 */
9
10int text_input (Ez_event *ev, char *s)
11{
12 int i;
13
14 switch (ev->key_sym) {
15
16 case XK_BackSpace : /* Backspace key */
17 i = strlen (s);
18 if (i == 0) break;
19 s[i-1] = 0;
20 return 1;
21
22 case XK_Return : /* Enter key */
23 return 2;
24
25 default : /* Insert a character */
26 if (ev->key_count != 1) break;
27 i = strlen (s);
28 if (i >= BUF_MAX-1) break;
29 s[i] = ev->key_string[0]; s[i+1] = 0;
30 return 1;
31 }
32 return 0;
33}
34
35
36void text_display (Ez_window win, int x, int y, char *s1, char *s2)
37{
38 ez_set_color (ez_black);
39 ez_draw_text (win, EZ_TL, x, y, "Text: %s_", s1);
40
41 if (strcmp (buf2, "") != 0) {
42 ez_set_color (ez_blue);
43 ez_draw_text (win, EZ_TC, 200, 70,
44 "You have validated this text:\n%s", s2);
45 }
46}
47
48
49void win1_on_expose (Ez_event *ev)
50{
51 text_display (ev->win, 10, 10, buf1, buf2);
52}
53
54
55void win1_on_key_press (Ez_event *ev)
56{
57 int k = text_input (ev, buf1);
58 if (k == 2) strncpy (buf2, buf1, BUF_MAX);
59 if (k > 0) ez_send_expose (ev->win);
60}
61
62
63void win1_on_event (Ez_event *ev)
64{
65 switch (ev->type) {
66 case Expose : win1_on_expose (ev); break;
67 case KeyPress : win1_on_key_press (ev); break;
68 }
69}
70
71
72int main ()
73{
74 if (ez_init() < 0) exit(1);
75
76 ez_window_create (400, 200, "Demo 08: Text input", win1_on_event);
77
78 ez_main_loop ();
79 exit(0);
80}
81
On obtient cette fenêtre :
![demo-08](https://pageperso.lis-lab.fr/~edouard.thiel/snap-EZ-Draw-1.2/snap-demo-08.png)
1.10. Animations¶
Pour réaliser une animation il faut deux ingrédients supplémentaires : un timer (voir Timers) pour entretenir la séquence temporelle, et un double buffer d’affichage (voir Double-buffer d’affichage) pour éviter que l’affichage ne clignote.
- Attention :
il ne faut surtout pas employer
sleep
ouusleep
car ces fonction empêchent les callbacks de « rendre la main » àez_main_loop()
, et donc elles figent l’affichage et l’interface (ou les perturbent fortement).
Le principe d’une animation est le suivant :
on démarre un timer dans
main
, qui provoque un évènementTimerNotify
quelques millisecondes plus tard ;
à réception de cet évènement
TimerNotify
dans la callback de la fenêtre, on fait trois choses :on incrémente un compteur (ou des coordonnées) permettant de modifier la position de l’objet à animer ;
on émet un évènement
Expose
pour que l’affichage soit refait ;enfin, on réarme le timer pour qu’il y ait un prochain évènement
TimerNotify
(on obtient une espèce de « boucle » deTimerNotify
temporisée) ;
chaque fois qu’un évènement
Expose
est reçu, on redessine la fenêtre en tenant compte du compteur (ou des coordonnées) pour dessiner l’objet animé dans sa position courante. Il ne faut pas dessiner pour un autre évènement, car cela perturberait le double-buffer d’affichage. De plus, EZ-Draw optimise l’affichage en éliminant les évènementsExpose
inutiles.
Voici un premier exemple simple avec le fichier demo-09.c affiché ci-dessous. L’animation consiste à faire grossir un cercle au milieu de la fenêtre ; elle s’adapte également à la taille de la fenêtre.
1
2#include "ez-draw.h"
3
4#define MAX_CPT1 100
5
6/* We can avoid global variables by using ez_set_data(), see demo-10.c
7 and the next examples */
8int count1 = 0, win1_w = 300, win1_h = 200, delay1 = 30;
9
10
11void win1_on_expose (Ez_event *ev)
12{
13 /* We draw based on count1 */
14 int xc = win1_w/2, rx = xc * count1 / MAX_CPT1,
15 yc = win1_h/2, ry = yc * count1 / MAX_CPT1;
16
17 ez_set_color (ez_magenta);
18 ez_set_thick (3);
19 ez_draw_circle (ev->win, xc-rx, yc-ry, xc+rx, yc+ry);
20
21 ez_set_color (ez_black); ez_set_nfont (0);
22 ez_draw_text (ev->win, EZ_BL, 8, win1_h-8, "q: quit");
23}
24
25
26void win1_on_key_press (Ez_event *ev)
27{
28 switch (ev->key_sym) {
29 case XK_q : ez_quit (); break;
30 }
31}
32
33
34void win1_on_configure_notify (Ez_event *ev)
35{
36 win1_w = ev->width; win1_h = ev->height;
37}
38
39
40void win1_on_timer_notify (Ez_event *ev) /* The timer has expired */
41{
42 /* We increment the counter count1 so as to modify the position of the
43 object to animate */
44 count1 = (count1 + 1) % MAX_CPT1;
45 /* We send an Expose event so that the window will be displayed again */
46 ez_send_expose (ev->win);
47 /* We restart the timer to maintain a "loop" or TimerNotify events,
48 which is iterated each delay1 milliseconds */
49 ez_start_timer (ev->win, delay1);
50}
51
52
53void win1_on_event (Ez_event *ev)
54{
55 switch (ev->type) {
56 case Expose : win1_on_expose (ev); break;
57 case KeyPress : win1_on_key_press (ev); break;
58 case ConfigureNotify : win1_on_configure_notify (ev); break;
59 case TimerNotify : win1_on_timer_notify (ev); break;
60 }
61}
62
63
64int main ()
65{
66 Ez_window win1;
67
68 if (ez_init() < 0) exit(1);
69
70 win1 = ez_window_create (win1_w, win1_h, "Demo 09: Hypnosis", win1_on_event);
71
72 /* Enable double buffer to prevent window flashes */
73 ez_window_dbuf (win1, 1);
74
75 /* Start a timer to get a TimerNotify event in delay1 milliseconds:
76 this is the starting point of the "loop" of TimerNotify events. */
77 ez_start_timer (win1, delay1);
78
79 ez_main_loop ();
80 exit(0);
81}
82
On obtient cette fenêtre :
![demo-09](https://pageperso.lis-lab.fr/~edouard.thiel/snap-EZ-Draw-1.2/snap-demo-09.png)
L’exemple suivant demo-10.c illustre les animations multiples : on fait tourner les aiguilles d’une montre dans une fenêtre (on peut faire pause avec la touche espace), tandis qu’une balle rebondit sur une raquette dans une seconde fenêtre (que l’on peut agrandir).
On obtient ces fenêtres :
![demo-10-1](https://pageperso.lis-lab.fr/~edouard.thiel/snap-EZ-Draw-1.2/snap-demo-10-1.png)
![demo-10-2](https://pageperso.lis-lab.fr/~edouard.thiel/snap-EZ-Draw-1.2/snap-demo-10-2.png)
Cet exemple demo-10.c illustre également comment attacher une information (par
exemple un struct
) à une fenêtre, pour éviter les variables globales.
Pour plus d’explications voir Client-data.
On se sert beaucoup de cette possibilité dans la partie suivante.
1.11. Images¶
Dans les sections précédentes on a vu tout ce que l’on pouvait faire avec le module de base.
EZ-Draw contient un second module, ez-image.c, qui permet de charger et d’afficher des images en couleur au format PNG, JPEG, GIF ou BMP, ou encore de créer une image en mémoire et de dessiner dedans. Tout est détaillé dans les sections Le type image et suivantes.
Pour utiliser ce module il faut inclure ez-image.h. Voici l’exemple demo-13.c dans lequel on récupère un nom de fichier image en argument de la ligne de commande, puis on charge l’image et enfin on l’affiche :
1
2#include "ez-draw.h"
3#include "ez-image.h"
4
5
6typedef struct {
7 Ez_image *image1;
8 Ez_window win1;
9} App_data;
10
11
12void app_data_init (App_data *a, char *filename)
13{
14 a->image1 = ez_image_load (filename); /* Load an image */
15 if (a->image1 == NULL) exit (1);
16}
17
18
19void app_data_destroy (App_data *a)
20{
21 ez_image_destroy (a->image1); /* Destroy image */
22}
23
24
25void win1_on_expose (Ez_event *ev)
26{
27 App_data *a = ez_get_data (ev->win);
28
29 ez_image_paint (a->win1, a->image1, 0, 0); /* Display image */
30}
31
32
33void win1_on_key_press (Ez_event *ev)
34{
35 switch (ev->key_sym) {
36 case XK_q : ez_quit (); break;
37 }
38}
39
40
41void win1_on_event (Ez_event *ev)
42{
43 switch (ev->type) {
44 case Expose : win1_on_expose (ev); break;
45 case KeyPress : win1_on_key_press (ev); break;
46 }
47}
48
49
50int main (int argc, char *argv[])
51{
52 char *filename = "images/tux2.gif";
53 App_data a;
54
55 if (argc-1 != 1)
56 fprintf (stderr, "Usage: %s image\n", argv[0]);
57 else filename = argv[1];
58
59 if (ez_init() < 0) exit (1);
60 app_data_init (&a, filename);
61
62 a.win1 = ez_window_create ( /* Resize window for image */
63 a.image1->width, a.image1->height,
64 filename, win1_on_event);
65 ez_set_data (a.win1, &a);
66 ez_window_dbuf(a.win1, 1);
67
68 ez_main_loop ();
69
70 app_data_destroy (&a);
71 exit(0);
72}
73
On obtient par exemple cette fenêtre :
![demo-13](https://pageperso.lis-lab.fr/~edouard.thiel/snap-EZ-Draw-1.2/snap-demo-13.png)
Pour compiler cet exemple demo-13.c
sous Unix, taper :
gcc -Wall demo-13.c ez-draw.c ez-image.c -o demo-13 -lX11 -lXext -lm
ou sous Windows, taper :
gcc -Wall demo-13.c ez-draw.c ez-image.c -o demo-13.exe -lgdi32 -lmsimg32 -lm
On peut également rajouter le nom de l’exécutable à la fin de EXECS_IM =
dans le Makefile, puis taper make all
pour compiler.
Les formats PNG, GIF et BMP permettent de mémoriser le degré de transparence, dans ce qu’on appelle le canal alpha. Les formats GIF et BMP codent le canal alpha sur 1 bit ; les pixels sont soit transparents (0), soit opaques (255). Le format PNG code le canal alpha sur 8 bits (de 0 pour transparent à 255 pour opaque).
Le module ez-image
est capable d’afficher une image en tenant compte
de la transparence, en utilisant un seuil d’opacité sur le canal alpha :
les pixels sont soit opaques (affichés), soit transparents (non affichés).
L’exemple suivant demo-14.c récupère deux noms de
fichiers en argument de la ligne de commande, puis superpose les deux images.
On peut ensuite déplacer la
seconde image avec les flèches, ou modifier le seuil d’opacité avec les touches
+
et -
.
1
2#include "ez-draw.h"
3#include "ez-image.h"
4
5
6typedef struct {
7 int i2_x, i2_y;
8 Ez_image *image1, *image2;
9 Ez_window win1;
10} App_data;
11
12
13void app_data_init (App_data *a, char *filename1, char *filename2)
14{
15 a->image1 = ez_image_load (filename1);
16 if (a->image1 == NULL) exit (1);
17
18 a->image2 = ez_image_load (filename2);
19 if (a->image2 == NULL) exit (1);
20
21 /* Initial position is centered */
22 a->i2_x = (a->image1->width - a->image2->width ) / 2;
23 a->i2_y = (a->image1->height - a->image2->height) / 2;
24}
25
26
27void app_data_destroy (App_data *a)
28{
29 ez_image_destroy (a->image1);
30 ez_image_destroy (a->image2);
31}
32
33
34void win1_on_expose (Ez_event *ev)
35{
36 App_data *a = ez_get_data (ev->win);
37
38 ez_image_paint (a->win1, a->image1, 0, 0);
39 ez_image_paint (a->win1, a->image2, a->i2_x, a->i2_y);
40 ez_draw_text (a->win1, EZ_BLF, 10, a->image1->height+15,
41 "[Arrows] to move");
42 ez_draw_text (a->win1, EZ_BRF, a->image1->width-10, a->image1->height+15,
43 "Opacity [+-] : %d", a->image2->opacity);
44}
45
46
47void win1_on_key_press (Ez_event *ev)
48{
49 App_data *a = ez_get_data (ev->win);
50
51 switch (ev->key_sym) {
52 case XK_q : ez_quit (); break;
53 case XK_Left :
54 case XK_KP_Left : a->i2_x-- ; break;
55 case XK_Right :
56 case XK_KP_Right : a->i2_x++ ; break;
57 case XK_Up :
58 case XK_KP_Up : a->i2_y-- ; break;
59 case XK_Down :
60 case XK_KP_Down : a->i2_y++ ; break;
61 case XK_minus :
62 case XK_KP_Subtract : a->image2->opacity--; break;
63 case XK_plus :
64 case XK_KP_Add : a->image2->opacity++; break;
65 default : return;
66 }
67 ez_send_expose (a->win1);
68}
69
70
71void win1_on_event (Ez_event *ev)
72{
73 switch (ev->type) {
74 case Expose : win1_on_expose (ev); break;
75 case KeyPress : win1_on_key_press (ev); break;
76 }
77}
78
79
80int main (int argc, char *argv[])
81{
82 char *file1 = "images/paper1.jpg", *file2 = "images/tux1.png";
83 App_data a;
84
85 if (argc-1 != 2)
86 fprintf (stderr, "Usage: %s image1 image2\n", argv[0]);
87 else { file1 = argv[1]; file2 = argv[2]; }
88
89 if (ez_init() < 0) exit(1);
90 app_data_init (&a, file1, file2);
91
92 a.win1 = ez_window_create (a.image1->width, a.image1->height+15,
93 "Demo 14: Images with transparency", win1_on_event);
94 ez_set_data (a.win1, &a);
95 ez_window_dbuf(a.win1, 1);
96
97 ez_main_loop ();
98
99 app_data_destroy (&a);
100 exit(0);
101}
102
On obtient par exemple cette fenêtre :
![demo-14](https://pageperso.lis-lab.fr/~edouard.thiel/snap-EZ-Draw-1.2/snap-demo-14.png)
Il est également possible de créer une image en mémoire, puis d’affecter les couleurs des pixels. L’exemple demo-12.c affiche la palette de couleurs HSV à l’aide d’une image calculée en mémoire.
On obtient cette fenêtre :
![demo-12](https://pageperso.lis-lab.fr/~edouard.thiel/snap-EZ-Draw-1.2/snap-demo-12.png)
Vous trouverez plus d’informations sur les images dans le Manuel de référence.