1. Tutorial

The easiest way to proceed is to copy the files ez-draw.h and ez-draw.c in your directory, then create your programs there.

You may also write you programs directly in the EZ-Draw directory, and simply add the executable file names in the Makefile (on Unix) or Makefile.win (on Windows), see section Compilation.

1.1. First program with a window

Let us write our first program that opens a window, and name it demo-01.c. We first include ez-draw.h (line 1, see below): this file defines the types and prototypes of the main EZ-Draw module, and it also includes the standard headers such as <stdio.h>, <stdlib.h>, <string.h>, so we don’t need to worry about.

In main, we initialize the module and the graphic mode by calling ez_init(); if the initialization fails, the function print an error message in the terminal, then return -1. In this case, we must terminate the program by exit(1) .

Next we create one (or several) window(s) using function ez_window_create(). These windows are displayed when the program reaches ez_main_loop(): that is this function which “gives life” to the windows. It stops when one calls ez_quit(), or when all windows are destroyed.

Here is the file 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);
}

This window is obtained:

demo-01

1.2. Compilation

To compile the previous example demo-01.c on Unix, type

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

or on Windows, type

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

You can also add the executable file name at the end of EXECS = in the Makefile on Unix or Makefile.win on Windows; then type make all to compile.

To run the program in a terminal on Unix, type

./demo-01

or on Windows, type

demo-01
Note:
by default, the Makefile compiles the files in C ANSI (option -ansi in the variable CFLAGS). You are free to compile in C99 if you want to benefit from the new language features (such as \\ comments), simply replace -ansi by -std=c99.

1.3. Event handler

The parameters of the function ez_window_create() which is responsible of the windows creation are:

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

w is the width of the interior of the window in pixels, h is the height, name is the window title; func is the event handler of the window, see below.

The result of ez_window_create() is a number which identifies the window, of type Ez_window; it can be printed in the terminal:

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

To be able to interact with the user (key press and release, mouse moves and click, etc) we have to realize a so-called event handling; that is why we give the function func as a fourth parameter of ez_window_create().

The event handler func (also named callback), is a function of your program (or NULL as in example demo-01). This function will be automatically called by ez_main_loop() for each event that involves the window.

The function func has the following prototype:

void func (Ez_event *ev);

The parameter ev is the address of a struct whose fields describe the event; in particular, ev->win tells which window is involved. The other fields are detailed in section Tracing events.

We study two events in the following example demo-02.c :

  • The window manager tells to your program if the windows must be redrawn (the first time they appear, when another window is passing ahead, etc). When this happens, an Expose event is generated and the event handler is called. At this time you have to redraw the whole content of the window ev->win.
  • When the user press a key, the KeyPress event is generated. The key code is available in ev->key_sym (for key symbol). Each key code is expressed as a constant prefixed by XK_, for instance XK_q stands for the key “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);
}

This window is obtained:

demo-02

1.4. Drawings and colors

As explained in the previous section, the Expose event means that the whole content of the windows must be redrawn. In the next example we redraw by calling win1_on_expose. Note: for each Expose event, EZ-Draw empties the window (with a white background) before passing the event to your program.

The list of drawings is given in section Drawings. The coordinates are relative to the Origin, which is the top left corner inside the window; x goes to the right and y goes down.

The drawings are automatically cut by the window border, so that there is no need to worry if a drawing fits or not.

The drawings are performed using the current thickness (1 pixel by default). you can change the thickness with ez_set_thick(), see Drawings.

The drawings are done in the current color (black by default). To change color, call ez_set_color() by giving it the color number. Some colors are predefined: ez_black, ez_white, ez_grey, ez_red, ez_green, ez_blue, ez_yellow, ez_cyan, ez_magenta. It is possible to create other colors, see Colors.

Here is the file 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);
}

This window is obtained:

demo-03

1.5. Displaying text

It is possible to draw text at any place in the window, thanks to the function ez_draw_text(). It takes as arguments: the window, the kind of alignment align, the coordinates x1,y1, finally a string to draw, or as in printf, a format and parameters. Everything is detailed in section Text and fonts.

The string can contain some \n, causing line breaks in the display. The text drawing is performed in the current color, modifiable by ez_set_color().

In the next example demo-04.c we illustrate this, as well as the use of ez_window_get_size() to kindly adapt the drawing to the window size changes.

 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);
}

This window is obtained:

demo-04

1.6. Tracing events

In the next example we list all possible events, and we trace in the terminal the ev variable fields that are usable (the other ones are set to 0). See also section Events.

By default, the “Close” button in the title bar of one of the application windows causes the termination of the program. We can change this behavior by calling ez_auto_quit(0) : from now on, the “Close” button will cause a WindowClose event, as in the following example.

Here is the file 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);
}

This window is obtained:

demo-05

Note: the TimerNotify events are not treated here, see Timers.

1.7. Drawing with the mouse

The example shown in this section allows to draw a polygonal line with the mouse. The coordinates of the vertices are stored in global variables. Each time the mouse button is clicked, a new vertex is inserted; for each mouse move with a pressed button (drag), the last vertex is moved.

By principle (and for technical reasons), the drawings must only be done for the Expose event. If you want to update the drawings of a window for another event, it is sufficient to send an Expose event using the function ez_send_expose(). We do this here, for ButtonPress, MotionNotify and KeyPress events.

Here is the file 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);
}

This window is obtained:

demo-06

1.8. Managing several windows

You can create as much windows that you want, by calling ez_window_create(). Each window that is created is immediately displayed on top of the existing windows. To destroy a window win (and thus hide it from screen), use ez_window_destroy(win).

It is possible to hide a window win (it still exists but is not displayed) by calling ez_window_show(win, 0), then show it again (on top of the other ones) by calling ez_window_show(win, 1).

In the next example, the main loop is configured by ez_auto_quit(0), which means that pressing the “Close” button of the window title bar will not end the program, but send in place a WindowClose event. According to the window involved, we hide the window, destroy the window or exit the program.

Here is the file 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);
}

This window is obtained:

demo-07

1.9. Text input

The next example demo-08.c demonstrates how to read a string on keyboard, suppress some characters using the Backspace, and detect the Enter key to trigger an 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);
}

This window is obtained:

demo-08

1.10. Animations

To perform animations, two additional ingredients are needed: a timer (see Timers) to maintain the temporal sequence, and double buffering (see Double buffering) to prevent display flashing.

Warning:
Never ever employ sleep or usleep, because these functions prevent the callbacks to restore the control to ez_main_loop(), and thus they freeze the display and the interface (or at least severely disrupt them).

The principle of an animation is the following:

  • start a timer in main, which will cause a TimerNotify event few milliseconds later;
  • upon receipt of this TimerNotify event in the window callback, we do three things:
    • we increment a counter (or coordinates, or whatever) in order to change the position of the object to animate;
    • we send an Expose event to refresh display;
    • last, we restart the timer such that there will be a next TimerNotify event (what we obtain is a kind of timed loop of TimerNotify events);
  • each time an Expose event is received, we redraw the window content tacking account of the counter (or the coordinates) to draw the animated object in its current position. Never draw for another event, you would disturb the double display buffer. Moreover, EZ-Draw optimize displaying by eliminating useless Expose events.

Here is a first example demo-09.c below. The animation shows a growing circle at the middle of the window; it also adapts the drawings to the window size changes.

 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);
}

This window is obtained:

demo-09

Another example is provided by demo-10.c to illustrate multiple animations: in one window we turn the hands of a watch (type space for pause), while in another window a ball is bouncing on a racket (the window can be enlarged).

These windows are obtained:

demo-10-1 demo-10-2

1.11. Images

In the previous chapters we have seen what we can do with the basic module.

EZ-Draw comes with a second module, ez-image.c, that allows to load and display color images in PNG, JPEG, GIF or BMP format, or create an image in memory and draw into. These possibilities are detailed in sections The image type and following.

To use this module, just include ez-image.h. Here is the example demo-13.c where we get a file name as argument of the command line, then we load the image and display it:

 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);
}

This window is obtained:

demo-13

To compile this file demo-13.c on Unix, type:

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

or on Windows, type:

gcc -Wall demo-13.c ez-draw.c ez-image.c -o demo-13.exe -lgdi32 -lmsimg32 -lm

You can also append the executable file name at the end of EXECS_IM = in the Makefile on Unix, or in Makefile.win on Windows; then type make all to compile.

The formats PNG, GIF and BMP allow to store a transparency level, in what is called the alpha channel. The formats GIF and BMP store the alpha channel over 1 bit; the pixels are either transparent (0), or opaque (255). The PNG format stores the alpha channel over 8 bits (from 0 for transparent, to 255 for opaque).

The module ez-image is able to display an image by taking in account the transparency, using an opacity threshold on the alpha channel: the pixels are either opaques (displayed) or transparents (not displayed).

The next example demo-14.c gets two file names as arguments of the command line, then superimpose both images. You can move the second image using arrow keys, or change the opacity threshold using keys + and -.

  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);
}

This window is obtained:

demo-14

It is also possible to create an image in memory, then set the pixels colors. The example demo-12.c displays the HSV palette computed in this manner.

This window is obtained:

demo-12

You will find more informations about images in the Reference manual.