1. Tutorial

La façon la plus simple de travailler est de récupérer dans son répertoire les fichiers ez-draw.h et ez-draw.c, puis de créer vos programmes dans ce même répertoire.

Vous pouvez aussi programmer directement dans le répertoire de EZ-Draw et rajouter les noms des exécutables dans le fichier Makefile (sous Unix) ou Makefile.win (sous Windows), 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 : ce fichier décrit les types et prototypes du module, et il inclut aussi les fichiers .h standards tels que <stdio.h>, <stdlib.h>, <string.h>.

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 la console, puis renvoie -1. Dans ce cas, il faut sortir du programme en faisant exit(1) .

Ensuite on crée une (ou plusieurs) fenêtres 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
 3
 4
 5
 6
 7
 8
 9
10
11
12
#include "ez-draw.h"


int main ()
{
    if (ez_init() < 0) exit(1);

    ez_window_create (400, 300, "Demo 01 : Hello World", NULL);

    ez_main_loop ();
    exit(0);
}

On obtient cette fenêtre :

demo-01

1.2. Compilation

Pour compiler le précédent exemple demo-01.c sous Unix, taper

gcc -Wall demo-01.c ez-draw.c -o demo-01 -lX11 -lXext -L/usr/X11R6/lib

ou sous Windows, taper

gcc -Wall demo-01.c ez-draw.c -o demo-01.exe -lgdi32

On peut aussi rajouter le nom de l’exécutable à la fin de EXECS = dans le Makefile sous Unix (ou Makefile.win pour Windows), puis taper make all pour tout compiler.

Pour exécuter le programme dans un terminal sous Unix, taper

./demo-01

ou sous Windows, taper

demo-01
Remarque :
Par défaut, le Makefile compile les fichiers en C ANSI (option -ansi dans la variable CFLAGS). Vous pouvez sans problème compiler en C99 si vous souhaitez bénéficier des nouveautés du langage (à commencer par les commentaires //), il suffit de remplacer -ansi par -std=c99.

1.3. Fonction traitant les évènements

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. On met en place ce mécanisme au moment de la création d’une fenêtre avec la fonction ez_window_create() :

Ez_window ez_window_create (int w, int h, const char *name, Ez_func func);

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 ; func est la fonction d’évènement de la fenêtre, voir ci-dessous.

Le résultat de ez_window_create() est le numéro qui identifie la fenêtre, de type Ez_window ; on peut afficher ce numéro dans la console :

Ez_window win1;
win1 = ez_window_create (400, 300, "Demo 0 : Hello World", NULL);
printf ("win1 = 0x%x\n", (int) win1);

La fonction d’évènement func, 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 func doit obligatoirement avoir le prototype suivant :

void func (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 Tous 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être ev->win.
  • Lorsque l’utilisateur enfonce une touche, l’évènement KeyPress est déclenché. Le code de la touche est disponible dans ev->key_sym (pour key symbol). Chaque code de touche correspond à une constante préfixée par XK_, par exemple ici XK_q pour la touche “q”.
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
#include "ez-draw.h"


void win1_event (Ez_event *ev)        /* Appele'e a chaque evenement sur win1 */
{                                     /* par ez_main_loop()                   */
    switch (ev->type) {

        case Expose :                              /* Il faut tout redessiner */
            ez_set_color (ez_red);
            ez_draw_text (ev->win, EZ_MC, 200, 150, 
                "Pour quitter, tapez sur la touche 'q', ou\n"
                "cliquez sur l'icone fermeture de la fenetre");
            break;

        case KeyPress :                           /* Une touche a ete pressee */
            switch (ev->key_sym) {
                case XK_q : ez_quit (); break;
            }
            break;
    }
}


int main ()
{
    if (ez_init() < 0) exit(1);

    ez_window_create (400, 300, "Demo 02 : fenetre et evenements", win1_event);

    ez_main_loop ();
    exit(0);
}

On obtient cette fenêtre :

demo-02

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_redessiner. À 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
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
#include "ez-draw.h"


void win1_on_expose (Ez_event *ev)
{
    ez_set_color (ez_magenta);
    ez_draw_text      (ev->win, EZ_BL, 10, 20, "draw epaisseur 1 :");
    ez_set_thick (1);
    ez_draw_point     (ev->win,  30,  50);
    ez_draw_line      (ev->win,  60,  35, 130,  70);
    ez_draw_rectangle (ev->win, 160,  30, 220,  70);
    ez_draw_circle    (ev->win, 240,  30, 300,  70);
    ez_draw_triangle  (ev->win, 320,  30, 380,  40, 350,  70);

    ez_set_color (ez_black);
    ez_draw_text      (ev->win, EZ_BL, 10, 100, "draw epaisseur 2 :");
    ez_set_color (ez_cyan);
    ez_set_thick (2);
    ez_draw_point     (ev->win,  30, 130);
    ez_draw_line      (ev->win,  60, 115, 130, 150);
    ez_draw_rectangle (ev->win, 160, 110, 220, 150);
    ez_draw_circle    (ev->win, 240, 110, 300, 150);
    ez_draw_triangle  (ev->win, 320, 110, 380, 120, 350, 150);

    ez_set_color (ez_blue);
    ez_draw_text      (ev->win, EZ_BL, 10, 180, "draw epaisseur 9 :");
    ez_set_color (ez_green);
    ez_set_thick (9);
    ez_draw_point     (ev->win,  30, 210);
    ez_draw_line      (ev->win,  60, 195, 130, 230);
    ez_draw_rectangle (ev->win, 160, 190, 220, 230);
    ez_draw_circle    (ev->win, 240, 190, 300, 230);
    ez_draw_triangle  (ev->win, 320, 190, 380, 200, 350, 230);

    ez_set_color (ez_red);
    ez_draw_text      (ev->win, EZ_BL, 10, 260, "fill :");
    ez_set_color (ez_yellow);
    ez_fill_rectangle (ev->win, 160, 270, 220, 310);
    ez_fill_circle    (ev->win, 240, 270, 300, 310);
    ez_fill_triangle  (ev->win, 320, 270, 380, 280, 350, 310);
}


void win1_on_key_press (Ez_event *ev)
{
    switch (ev->key_sym) {
        case XK_q : ez_quit (); break;
    }

}


void win1_event (Ez_event *ev)
{
    switch (ev->type) {
        case Expose   : win1_on_expose    (ev); break;
        case KeyPress : win1_on_key_press (ev); break;
    }
}


int main ()
{
    if (ez_init() < 0) exit(1);

    ez_window_create (400, 320, "Demo 03 : tous les dessins", win1_event);

    ez_main_loop ();
    exit(0);
}

On obtient cette fenêtre :

demo-03

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 paramètre 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 arguments. Tout est détaillé dans Texte et fontes.

Le résultat peut comporter des \n, provoquant 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
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
#include "ez-draw.h"


void win1_on_expose (Ez_event *ev)
{
    int i, w, h;

    ez_window_get_size (ev->win, &w, &h);

    ez_set_color (ez_black);
    for (i = 0; i <= 3; i++) {
        ez_set_nfont (i);
        ez_draw_text (ev->win, EZ_TC, w/2, h/2 + 25*(i-2), 
            "Fonte numero %d", i);                         /* comme un printf */
    }

    ez_set_nfont (0);
    ez_set_color (ez_red);

    ez_draw_text (ev->win, EZ_TL,   2,   1, "Top\nLeft");
    ez_draw_text (ev->win, EZ_TC, w/2,   1, "Top\nCenter");
    ez_draw_text (ev->win, EZ_TR, w-2,   1, "Top\nRight");
    ez_draw_text (ev->win, EZ_ML,   2, h/2, "Middle\nLeft");
    ez_draw_text (ev->win, EZ_MR, w-2, h/2, "Middle\nRight");
    ez_draw_text (ev->win, EZ_BL,   2, h-2, "Bottom\nLeft");
    ez_draw_text (ev->win, EZ_BC, w/2, h-2, "Bottom\nCenter");
    ez_draw_text (ev->win, EZ_BR, w-2, h-2, "Bottom\nRight");
}


void win1_on_key_press (Ez_event *ev)
{
    switch (ev->key_sym) {
        case XK_q : ez_quit (); break;
    }

}


void win1_event (Ez_event *ev)
{
    switch (ev->type) {
        case Expose   : win1_on_expose    (ev); break;
        case KeyPress : win1_on_key_press (ev); break;
    }
}


int main ()
{
    if (ez_init() < 0) exit(1);

    ez_window_create (400, 300, "Demo 04 : affichage de texte", win1_event);

    ez_main_loop ();
    exit(0);
}

On obtient cette fenêtre :

demo-04

1.6. Tous les évènements

Dans l’exemple suivant on recense tous les évènements possibles et on affiche dans la console les champs utilisables de la variable ev (les autres champs sont réinitialisés à 0).

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
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
#include "ez-draw.h"


void win1_on_expose (Ez_event *ev)                /* Il faut tout redessiner */
{
    ez_draw_text (ev->win, EZ_MC, 200, 150, 
        "L'affichage des evenements\nest fait dans la console.\n\n"
        "Tapez 'q' pour quitter.");
    printf ("Expose           win = 0x%x\n", (int) ev->win);
}


void win1_on_button_press (Ez_event *ev)           /* Bouton souris enfonce' */
{
    printf ("ButtonPress      win = 0x%x  mx = %d  my = %d  mb = %d\n",
        (int) ev->win, ev->mx, ev->my, ev->mb);
}


void win1_on_button_release (Ez_event *ev)         /* Bouton souris relache' */
{
    printf ("ButtonRelease    win = 0x%x  mx = %d  my = %d  mb = %d\n",
        (int) ev->win, ev->mx, ev->my, ev->mb);
}


void win1_on_motion_notify (Ez_event *ev)                /* Souris deplace'e */
{
    printf ("MotionNotify     win = 0x%x  mx = %d  my = %d  mb = %d\n",
        (int) ev->win, ev->mx, ev->my, ev->mb);
}


void win1_on_key_press (Ez_event *ev)            /* Touche clavier enfonce'e */
{
    printf ("KeyPress         win = 0x%x  mx = %d  my = %d  "
            "key_sym = 0x%x  key_name = %s  key_count = %d  key_string = \"%s\"\n",
        (int) ev->win, ev->mx, ev->my,
        (int) ev->key_sym, ev->key_name, ev->key_count,
        ev->key_sym == XK_Return || ev->key_sym == XK_KP_Enter ? "" : ev->key_string);
}


void win1_on_key_release (Ez_event *ev)          /* Touche clavier relache'e */
{
    printf ("KeyRelease       win = 0x%x  mx = %d  my = %d  "
            "key_sym = 0x%x  key_name = %s  key_count = %d  key_string = \"%s\"\n",
        (int) ev->win, ev->mx, ev->my,
        (int) ev->key_sym, ev->key_name, ev->key_count,
        ev->key_sym == XK_Return || ev->key_sym == XK_KP_Enter ? "" : ev->key_string);
     switch (ev->key_sym) {
        case XK_q : ez_quit (); break;
    }
}


void win1_on_configure_notify (Ez_event *ev)     /* Taille fenetre modifie'e */
{
    printf ("ConfigureNotify  win = 0x%x  width = %d  height = %d\n",
        (int) ev->win, ev->width, ev->height);
}


void win1_on_window_close (Ez_event *ev)   /* Fermeture fenetre intercepte'e */
{
    printf ("WindowClose      win = 0x%x\n", (int) ev->win);
}


void win1_event (Ez_event *ev)       /* Appele'e a chaque evenement sur win1 */
{
    switch (ev->type) {
        case Expose          : win1_on_expose           (ev); break;
        case ButtonPress     : win1_on_button_press     (ev); break;
        case ButtonRelease   : win1_on_button_release   (ev); break;
        case MotionNotify    : win1_on_motion_notify    (ev); break;
        case KeyPress        : win1_on_key_press        (ev); break;
        case KeyRelease      : win1_on_key_release      (ev); break;
        case ConfigureNotify : win1_on_configure_notify (ev); break;
        case WindowClose     : win1_on_window_close     (ev); break;
        default :
             printf ("Evenement inconnu : %d\n", ev->type);
   }
}


int main ()
{
    if (ez_init() < 0) exit(1);

    ez_window_create (400, 300, "Demo 05 : trace les evenements", win1_event);

    ez_auto_quit (0);  /* pour capter l'evenement WindowClose */

    ez_main_loop ();
    exit(0);
}

On obtient cette fenêtre :

demo-05

Seul 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. 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
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
#include "ez-draw.h"

#define SOM_MAX 100
int som_nb = 0, som_x[SOM_MAX], som_y[SOM_MAX];


void sommet_vider ()
{
    som_nb = 0;
}


void sommet_ajouter (int x, int y)
{
    if (som_nb >= SOM_MAX) return;
    som_x[som_nb] = x;
    som_y[som_nb] = y;
    som_nb++;
}


void sommet_deplacer (int x, int y)
{
    if (som_nb <= 0 || som_nb >= SOM_MAX) return;
    som_x[som_nb-1] = x;
    som_y[som_nb-1] = y;
}


void sommet_dessiner (Ez_window win)
{
    int i;

    /* On dessine les sommets */
    ez_set_color (ez_blue);
    for (i = 0; i < som_nb; i++)
        ez_draw_rectangle (win, som_x[i]-2, som_y[i]-2, som_x[i]+2, som_y[i]+2);

    /* On relie les sommets par des segments */
    ez_set_color (ez_grey);
    for (i = 1; i < som_nb; i++)
        ez_draw_line (win, som_x[i-1], som_y[i-1], som_x[i], som_y[i]);
}


void win1_on_expose (Ez_event *ev)
{
    ez_set_color (ez_black);
    ez_draw_text (ev->win, EZ_TL, 10, 10,
        "Cliquez et tirez la souris dans la fenetre pour dessiner.\n"
        "Tapez sur espace pour vider la fenetre, 'q' pour quitter.");
    sommet_dessiner (ev->win);
}


void win1_on_button_press (Ez_event *ev)
{
    sommet_ajouter (ev->mx, ev->my);
    ez_send_expose (ev->win);
}


void win1_on_motion_notify (Ez_event *ev)
{
    if (ev->mb == 0) return;                       /* pas de bouton enfonce' */
    sommet_deplacer (ev->mx, ev->my);
    ez_send_expose (ev->win);
}


void win1_on_key_press (Ez_event *ev)
{
    switch (ev->key_sym) {
        case XK_q : 
            ez_quit (); 
            break;
        case XK_space : 
            sommet_vider ();
            ez_send_expose (ev->win); 
            break;
    }
}


void win1_event (Ez_event *ev)
{
    switch (ev->type) {
        case Expose       : win1_on_expose        (ev); break;
        case ButtonPress  : win1_on_button_press  (ev); break;
        case MotionNotify : win1_on_motion_notify (ev); break;
        case KeyPress     : win1_on_key_press     (ev); break;
    }
}


int main ()
{
    Ez_window win1;

    if (ez_init() < 0) exit(1);

    win1 = ez_window_create (400, 300, "Demo 06 : dessin a la souris", win1_event);

    /* On associe un double-buffer d'affichage pour eviter tout clignotement */
    ez_window_dbuf (win1, 1);

    ez_main_loop ();
    exit(0);
}

On obtient cette fenêtre :

demo-06

1.8. Ouvrir 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 cet exemple, 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
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
#include "ez-draw.h"

/* En global */
Ez_window win1, win2, win3 = None; int show2 = 0;


void win3_on_expose (Ez_event *ev)
{
    ez_draw_text (ev->win, EZ_TL, 10, 10,
        "Si vous fermez cette fenetre, elle sera detruite.");
}


/* L'utilisateur a clique' sur l'icone fermeture de la fenetre */

void win3_on_window_close (Ez_event *ev)
{
    (void) ev;  /* Parametre inutilise' */
    ez_window_destroy (win3); win3 = None;
}


void win3_event (Ez_event *ev)
{
    switch (ev->type) {
        case Expose      : win3_on_expose       (ev); break;
        case WindowClose : win3_on_window_close (ev); break;
    }
}


void win2_on_expose (Ez_event *ev)
{
    ez_draw_text (ev->win, EZ_TL, 10, 10,
        "Si vous fermez cette fenetre, elle sera simplement cachee.");
}


void win2_on_window_close (Ez_event *ev)
{
    (void) ev;
    ez_window_show (win2, 0); show2 = 0;
}


void win2_event (Ez_event *ev)
{
    switch (ev->type) {
        case Expose      : win2_on_expose       (ev); break;
        case WindowClose : win2_on_window_close (ev); break;
    }
}


void win1_on_expose (Ez_event *ev)
{
    ez_draw_text (ev->win, EZ_TL, 10, 10,
        "Cliquez dans cette fenetre (pour donner le focus clavier),\n"
        "puis tapez :\n"
        "    - sur 'm' pour montrer ou cacher la fenetre 2 ;\n"
        "    - sur 'c' pour creer ou detruire la fenetre 3 ;\n"
        "    - sur 'q' pour quitter.\n"
        "\n"
        "Si vous fermez cette fenetre, le programme se terminera.");
}


void win1_on_key_press (Ez_event *ev)
{
    switch (ev->key_sym) {
        case XK_q : ez_quit (); break;

        case XK_m :
            show2 = !show2;         /* on affiche ou on cache la fenetre */
            ez_window_show (win2, show2);
        break;

        case XK_c :
            if (win3 == None)  /* si la fenetre n'existe pas, on la cree */
                win3 = ez_window_create (380, 220, "Fenetre 3", win3_event);
            else { ez_window_destroy (win3); win3 = None; }
        break;
    }
}


void win1_on_window_close (Ez_event *ev)
{
    (void) ev;
    ez_quit ();
}


void win1_event (Ez_event *ev)
{
    switch (ev->type) {
        case Expose      : win1_on_expose       (ev); break;
        case KeyPress    : win1_on_key_press    (ev); break;
        case WindowClose : win1_on_window_close (ev); break;
    }
}


int main ()
{
    if (ez_init() < 0) exit(1);

    win1 = ez_window_create (400, 300, "Demo 07 : plusieurs fenetres", win1_event);
    win2 = ez_window_create (400, 200, "Fenetre 2", win2_event);
    ez_window_show (win2, show2);

    /* Par defaut, fermer n'importe quelle fenetre provoque la fin du programme.
       On desactive cette fin automatique ; fermer une fenetre provoquera alors
       l'evenement WindowClose pour ce window. */
    ez_auto_quit (0);

    ez_main_loop ();
    exit(0);
}

On obtient cette fenêtre :

demo-07

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
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
#include "ez-draw.h"

#define BUF_MAX 80
char buf1[BUF_MAX] = "", buf2[BUF_MAX] = "";


/* Renvoie 1 si l'affichage doit etre refait, 2 si texte valide', 0 sinon */

int texte_saisir (Ez_event *ev, char *s)
{
    int i;

    switch (ev->key_sym) {

        case XK_BackSpace :                               /* Touche backspace */
            i = strlen (s);
            if (i == 0) break;
            s[i-1] = 0;
            return 1;

        case XK_Return :                                     /* Touche Entree */
            return 2;

        default :                                 /* Insertion d'un caractere */
            if (ev->key_count != 1) break;
            i = strlen (s);
            if (i >= BUF_MAX-1) break;
            s[i] = ev->key_string[0]; s[i+1] = 0;
            return 1;
    }
    return 0;
}


void texte_afficher (Ez_window win, int x, int y, char *s1, char *s2)
{
    ez_set_color (ez_black);
    ez_draw_text (win, EZ_TL, x, y, "Texte : %s_", s1);

    if (strcmp (buf2, "") != 0) {
        ez_set_color (ez_red);
        ez_draw_text (win, EZ_TC, 200, 70,
            "Vous avez valide le texte :\n%s", s2);
    }
}


void win1_on_expose (Ez_event *ev)
{
    texte_afficher (ev->win, 10, 10, buf1, buf2);
}


void win1_on_key_press (Ez_event *ev)
{
    int k = texte_saisir (ev, buf1);
    if (k == 2) strncpy (buf2, buf1, BUF_MAX);
    if (k > 0) ez_send_expose (ev->win);
}


void win1_event (Ez_event *ev)
{
    switch (ev->type) {
        case Expose   : win1_on_expose    (ev); break;
        case KeyPress : win1_on_key_press (ev); break;
    }
}


int main ()
{
    if (ez_init() < 0) exit(1);

    ez_window_create (400, 200, "Demo 08 : saisie de texte", win1_event);

    ez_main_loop ();
    exit(0);
}

On obtient cette fenêtre :

demo-08

1.10. Animations

Pour réaliser une animation il faut deux ingrédients supplémentaires : un timer (voir Double-buffer d’affichage) pour entretenir la séquence temporelle, et un double buffer d’affichage (voir Timers) pour éviter que l’affichage ne clignote.

Attention, il ne faut surtout pas employer sleep ou usleep 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ènement TimerNotify 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 fait ;
    • enfin, on réarme le timer pour qu’il y ait un prochain évènement TimerNotify (on obtient une espèce de “boucle” de TimerNotify 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ènements Expose 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
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
#include "ez-draw.h"

#define MAX_CPT1 100

/* On peut eviter ces variables globales avec ez_set_data(), voir demo-10.c
   et suivantes. */
int cpt1 = 0, win1_w = 300, win1_h = 200, delay1 = 30;


void win1_on_expose (Ez_event *ev)
{
    /* On fait le dessin en fonction de cpt1 */
    int xc = win1_w/2, rx = xc * cpt1 / MAX_CPT1,
        yc = win1_h/2, ry = yc * cpt1 / MAX_CPT1;

    ez_set_color (ez_magenta); 
    ez_set_thick (3);
    ez_draw_circle (ev->win, xc-rx, yc-ry, xc+rx, yc+ry);

    ez_set_color (ez_black); ez_set_nfont (0);
    ez_draw_text (ev->win, EZ_BL, 8, win1_h-8, "q : quitter");
}


void win1_on_key_press (Ez_event *ev)
{
    switch (ev->key_sym) {
        case XK_q : ez_quit (); break;
    }
}


void win1_on_configure_notify (Ez_event *ev)
{
    win1_w = ev->width; win1_h = ev->height;
}


void win1_on_timer_notify (Ez_event *ev)   /* Le timer est arrive' a echance */
{
    /* On incremente le compteur cpt1 pour modifier la position de l'objet
       que l'on veut animer */
    cpt1 = (cpt1 + 1) % MAX_CPT1;
    /* On envoie l'evenement Expose pour que la fenetre soit redessinee */
    ez_send_expose (ev->win);
    /* On re'arme le timer, pour entretenir une "boucle" de TimerNotify 
       qui est iteree tous les delay1 millisecondes */
    ez_start_timer (ev->win, delay1);
}


void win1_event (Ez_event *ev)
{
    switch (ev->type) {
        case Expose          : win1_on_expose           (ev); break;
        case KeyPress        : win1_on_key_press        (ev); break;
        case ConfigureNotify : win1_on_configure_notify (ev); break;
        case TimerNotify     : win1_on_timer_notify     (ev); break;
    }
}


int main ()
{
    Ez_window win1;

    if (ez_init() < 0) exit(1);

    win1 = ez_window_create (win1_w, win1_h, "Demo 09 : hypnose", win1_event);

    /* On associe un double-buffer d'affichage pour eviter tout clignotement */
    ez_window_dbuf (win1, 1);

    /* Provoque l'envoie d'un evenement TimerNotify dans delay1 millisecondes :
       c'est le point de depart de la "boucle" de TimerNotify */
    ez_start_timer (win1, delay1);

    ez_main_loop ();
    exit(0);
}

On obtient cette fenêtre :

demo-09

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 demo-10-2

1.11. Images

Dans les chapitres précédents 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
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
#include "ez-draw.h"
#include "ez-image.h"


typedef struct {
    Ez_image *image1;
    Ez_window win1;
} App_data;


void app_data_init (App_data *a, char *filename)
{
    a->image1 = ez_image_load (filename);                /* Charge une image */
    if (a->image1 == NULL) exit (1);
}


void app_data_destroy (App_data *a)
{
    ez_image_destroy (a->image1);                         /* Detruit l'image */
}


void win1_on_expose (Ez_event *ev)
{
    App_data *a = ez_get_data (ev->win);

    ez_image_paint (a->win1, a->image1, 0, 0);            /* Affiche l'image */
}


void win1_on_key_press (Ez_event *ev)
{
    switch (ev->key_sym) {
        case XK_q : ez_quit (); break;
    }
}


void win1_event (Ez_event *ev)
{
    switch (ev->type) {
        case Expose   : win1_on_expose    (ev); break;
        case KeyPress : win1_on_key_press (ev); break;
    }
}


int main (int argc, char *argv[])
{
    char *filename = "images/tux2.gif";
    App_data a;

    if (argc-1 != 1)
         fprintf (stderr, "Usage: %s image\n", argv[0]);
    else filename = argv[1];

    if (ez_init() < 0) exit (1);
    app_data_init (&a, filename);

    a.win1 = ez_window_create (            /* Taille la fenetre pour l'image */
        a.image1->width, a.image1->height, 
        filename, win1_event);
    ez_set_data (a.win1, &a);
    ez_window_dbuf(a.win1, 1);

    ez_main_loop ();

    app_data_destroy (&a);
    exit(0);
}

On obtient par exemple cette fenêtre :

demo-13

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 -L/usr/X11R6/lib -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 sous Unix (ou Makefile.win pour Windows), 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
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
#include "ez-draw.h"
#include "ez-image.h"


typedef struct {
    int i2_x, i2_y;
    Ez_image *image1, *image2;
    Ez_window win1;
} App_data;


void app_data_init (App_data *a, char *filename1, char *filename2)
{
    a->image1 = ez_image_load (filename1);
    if (a->image1 == NULL) exit (1);

    a->image2 = ez_image_load (filename2);
    if (a->image2 == NULL) exit (1);

    /* Position initiale centre'e */
    a->i2_x = (a->image1->width  - a->image2->width ) / 2;
    a->i2_y = (a->image1->height - a->image2->height) / 2;
}


void app_data_destroy (App_data *a)
{
    ez_image_destroy (a->image1);
    ez_image_destroy (a->image2);
}


void win1_on_expose (Ez_event *ev)
{
    App_data *a = ez_get_data (ev->win);

    ez_image_paint (a->win1, a->image1, 0, 0);
    ez_image_paint (a->win1, a->image2, a->i2_x, a->i2_y); 
    ez_draw_text (a->win1, EZ_BLF, 10, a->image1->height+15, 
        "[Fleches] pour deplacer");
    ez_draw_text (a->win1, EZ_BRF, a->image1->width-10, a->image1->height+15, 
        "Opacite [+-] : %d", a->image2->opacity);
}


void win1_on_key_press (Ez_event *ev)
{
    App_data *a = ez_get_data (ev->win);

    switch (ev->key_sym) {
        case XK_q : ez_quit (); break;
        case XK_Left        : 
        case XK_KP_Left     : a->i2_x-- ; break;
        case XK_Right       :
        case XK_KP_Right    : a->i2_x++ ; break;
        case XK_Up          : 
        case XK_KP_Up       : a->i2_y-- ; break;
        case XK_Down        : 
        case XK_KP_Down     : a->i2_y++ ; break;
        case XK_minus       :
        case XK_KP_Subtract : a->image2->opacity--; break;
        case XK_plus        :
        case XK_KP_Add      : a->image2->opacity++; break;
        default             : return;
    }
    ez_send_expose (a->win1);
}


void win1_event (Ez_event *ev)
{
    switch (ev->type) {
        case Expose   : win1_on_expose    (ev); break;
        case KeyPress : win1_on_key_press (ev); break;
    }
}


int main (int argc, char *argv[])
{
    char *file1 = "images/paper1.jpg", *file2 = "images/tux1.png";
    App_data a;

    if (argc-1 != 2)
        fprintf (stderr, "Usage: %s image1 image2\n", argv[0]);
    else { file1 = argv[1]; file2 = argv[2]; }

    if (ez_init() < 0) exit(1);
    app_data_init (&a, file1, file2);

    a.win1 = ez_window_create (a.image1->width, a.image1->height+15, 
        "Demo 14 : images avec transparence", win1_event);
    ez_set_data (a.win1, &a);
    ez_window_dbuf(a.win1, 1);

    ez_main_loop ();

    app_data_destroy (&a);
    exit(0);
}

On obtient par exemple cette fenêtre :

demo-14

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 image2calculée en mémoire.

On obtient cette fenêtre :

demo-12