1. Tutorial

The easiest way to proceed is to work directly in the EZ-Draw directory, starting from an example, and to complete the provided Makefile, 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 demo-01.c in a terminal on Unix, type

gcc -Wall demo-01.c ez-draw.c -o demo-01 -lX11 -lXext

or on Windows, type

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

To run the program in a terminal on Unix, type

./demo-01

or on Windows, type

demo-01

bullet It is more handy to use the command make instead of typing over and over the gcc command line. The command make use the same Makefile on Unix and on Windows.

To compile everything the first time, or to compile again the modified C files, it is sufficient to type in the terminal, whichever your system is:

make all

On error, see chapter Installation. If everything is up to date, make will display: make: Nothing to be done for `all'. You can always force a general re-compilation by typing:

make clean all

Finally if you want to clean your directory, for instance before making a backup, type:

make distclean

This will erase all executable and temporary files, and will keep only the source files.

Exercise :
Edit the file demo-01.c, and change the title with "My first program". Compile with make then run the program.

bullet Let us see now how to add a new program. Just edit the file Makefile , and add in section C the name of the executable in the list EXECS, EXECS_M or EXECS_IM, then save. We can then type make all to compile the new program. There are several lists to distinguish between cases:

Here are the current lists in the 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 \

As you can see, when a list is on several lines, the intermediary lines are ended by a \.

Exercise :
Copy the file demo-01.c as trial1.c, and add trial1 in the Makefile. Compile with make then run the program.

bullet You can use the provided Makefile for more advanced projects divided in several modules. Suppose that your executable is named myappli, that your project uses the modules ez-draw.c, ez-image.c, myprog.c and misc.c, and that you need the libraries -lm and -lgomp. You just have to complete the fields EXECS_PRO, OBJS_PRO and LIBS_PRO in the Makefile section D like that:

EXECS_PRO = myappli
OBJS_PRO  = ez-draw.o ez-image.o myprog.o misc.o
LIBS_PRO  = -lm -lgomp

bullet Finally, if you want to create your project in a new directory, here are the files that you have to copy in it:

Just clear the EXECS lists in the Makefile and fill the necessary fields.

1.3. Event handler

We will now take a look at the handling of events in the windows.

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

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

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

The result of ez_window_create() has type Ez_window; it is used to identify the window, and can be printed in the terminal as an hexadecimal value:

Ez_window win1;
win1 = ez_window_create (400, 300, "Demo 0: Hello World", NULL);
printf ("win1 = 0x%x\n", ez_window_get_id(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 on_event as a fourth parameter of ez_window_create().

The event handler on_event (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 on_event has the following prototype:

void on_event (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_on_event (Ez_event *ev)                /* Called by ez_main_loop() */
{                                                /* for each event on win1   */
    switch (ev->type) {

        case Expose :                           /* We must redraw everything */
            ez_set_color (ez_red);
            ez_draw_text (ev->win, EZ_MC, 200, 150, 
                "To quit, press the key 'q', or click\n"
                "on the Close button of the window");
            break;

        case KeyPress :                                 /* A key was pressed */
            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: Window and events", win1_on_event);

    ez_main_loop ();
    exit(0);
}

This window is obtained:

demo-02

This example demo-02.c is a first attempt to manage the events, using a big switch into win1_on_event. The drawback of this method is the rapid growing of the switch when enhancing the program, leading the program potentially unreadable. That is why it is better to split win1_on_event in functions (still using a switch), each of them being specialized for an event; we do it in the next section.

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, "Thickness 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, "Thickness 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, "Thickness 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_on_event (Ez_event *ev)                /* Called by ez_main_loop() */
{                                                /* for each event on win1   */
    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: All drawings", win1_on_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), 
            "Font number %d", i);                           /* like a 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_on_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: Displaying text", win1_on_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)              /* We must redraw everything */
{
    ez_draw_text (ev->win, EZ_MC, 200, 150, 
        "Events are traced\nin the terminal.\n\n"
        "Type 'q' to quit.");
    printf ("Expose           win = 0x%x\n", ez_window_get_id(ev->win));
}


void win1_on_button_press (Ez_event *ev)             /* Mouse button pressed */
{
    printf ("ButtonPress      win = 0x%x  mx = %d  my = %d  mb = %d\n",
        ez_window_get_id(ev->win), ev->mx, ev->my, ev->mb);
}


void win1_on_button_release (Ez_event *ev)          /* Mouse button released */
{
    printf ("ButtonRelease    win = 0x%x  mx = %d  my = %d  mb = %d\n",
        ez_window_get_id(ev->win), ev->mx, ev->my, ev->mb);
}


void win1_on_motion_notify (Ez_event *ev)                     /* Mouse moved */
{
    printf ("MotionNotify     win = 0x%x  mx = %d  my = %d  mb = %d\n",
        ez_window_get_id(ev->win), ev->mx, ev->my, ev->mb);
}


void win1_on_key_press (Ez_event *ev)                         /* Key pressed */
{
    printf ("KeyPress         win = 0x%x  mx = %d  my = %d  "
            "key_sym = 0x%x  key_name = %s  key_count = %d  key_string = \"%s\"\n",
        ez_window_get_id(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)                      /* Key released */
{
    printf ("KeyRelease       win = 0x%x  mx = %d  my = %d  "
            "key_sym = 0x%x  key_name = %s  key_count = %d  key_string = \"%s\"\n",
        ez_window_get_id(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)          /* Window size changed */
{
    printf ("ConfigureNotify  win = 0x%x  width = %d  height = %d\n",
        ez_window_get_id(ev->win), ev->width, ev->height);
}


void win1_on_window_close (Ez_event *ev)             /* Close button pressed */
{
    printf ("WindowClose      win = 0x%x\n", ez_window_get_id(ev->win));
}


void win1_on_event (Ez_event *ev)                /* Called by ez_main_loop() */
{                                                /* for each event on 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 ("Unknown event: %d\n", ev->type);
   }
}


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

    ez_window_create (400, 300, "Demo 05: Tracing events", win1_on_event);

    ez_auto_quit (0);  /* to get WindowClose event */

    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 (we could also avoid global variables, see Client-data). 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
110
111
112
113
#include "ez-draw.h"

#define VER_MAX 100
int ver_nb = 0, ver_x[VER_MAX], ver_y[VER_MAX];


void vertex_clear ()
{
    ver_nb = 0;
}


void vertex_insert (int x, int y)
{
    if (ver_nb >= VER_MAX) return;
    ver_x[ver_nb] = x;
    ver_y[ver_nb] = y;
    ver_nb++;
}


void vertex_move (int x, int y)
{
    if (ver_nb <= 0 || ver_nb >= VER_MAX) return;
    ver_x[ver_nb-1] = x;
    ver_y[ver_nb-1] = y;
}


void draw_vertices (Ez_window win)
{
    int i;

    ez_set_color (ez_blue);
    for (i = 0; i < ver_nb; i++)
        ez_draw_rectangle (win, ver_x[i]-2, ver_y[i]-2, ver_x[i]+2, ver_y[i]+2);
}

void draw_segments (Ez_window win)
{
    int i;

    ez_set_color (ez_grey);
    for (i = 1; i < ver_nb; i++)
        ez_draw_line (win, ver_x[i-1], ver_y[i-1], ver_x[i], ver_y[i]);
}


void win1_on_expose (Ez_event *ev)
{
    ez_set_color (ez_black);
    ez_draw_text (ev->win, EZ_TL, 10, 10,
        "Click and drag the mouse in the window to draw.\n"
        "Type Space to clear the window, 'q' to quit.");
    draw_segments (ev->win);
    draw_vertices (ev->win);
}


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


void win1_on_motion_notify (Ez_event *ev)
{
    if (ev->mb == 0) return;                            /* No button pressed */
    vertex_move (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 : 
            vertex_clear ();
            ez_send_expose (ev->win); 
            break;
    }
}


void win1_on_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: Drawing wih the mouse", win1_on_event);

    /* Enable double buffer to prevent window flashes */
    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"

/* Global variables */
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,
        "If you close this window, it will be destroyed.");
}


/* The user has clicked on the Close button of the window */

void win3_on_window_close (Ez_event *ev)
{
    (void) ev;  /* Tell the compiler that ev is unused */
    ez_window_destroy (win3); win3 = None;
}


void win3_on_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,
        "If you close this window, it will be hidden.");
}


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


void win2_on_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,
        "Click in this window (to get the keyboard focus),\n"
        "then type :\n"
        "    - on 'm' to show or hide window 2;\n"
        "    - on 'c' to create or destroy window 3;\n"
        "    - on 'q' to quit.\n"
        "\n"
        "If you close this window, the program will end.");
}


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

        case XK_m :
            show2 = !show2;                       /* show or hide the window */
            ez_window_show (win2, show2);
        break;

        case XK_c :
            if (win3 == None)      /* if the window doesn't exist, create it */
                win3 = ez_window_create (380, 220, "Window 3", win3_on_event);
            else { ez_window_destroy (win3); win3 = None; }
        break;
    }
}


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


void win1_on_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: Several windows", win1_on_event);
    win2 = ez_window_create (400, 200, "Window 2", win2_on_event);
    ez_window_show (win2, show2);

    /* By default, closing any window will cause the end of the program.
       We change this behaviour: for now on, closing any window will send 
       a WindowClose event for this 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] = "";


/* Return 1 if text must be displayed again, 2 if text is validated, else 0 */

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

    switch (ev->key_sym) {

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

        case XK_Return :                                        /* Enter key */
            return 2;

        default :                                      /* Insert a character */
            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 text_display (Ez_window win, int x, int y, char *s1, char *s2)
{
    ez_set_color (ez_black);
    ez_draw_text (win, EZ_TL, x, y, "Text: %s_", s1);

    if (strcmp (buf2, "") != 0) {
        ez_set_color (ez_blue);
        ez_draw_text (win, EZ_TC, 200, 70,
            "You have validated this text:\n%s", s2);
    }
}


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


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


void win1_on_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: Text input", win1_on_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

/* We can avoid global variables by using ez_set_data(), see demo-10.c
   and the next examples */
int count1 = 0, win1_w = 300, win1_h = 200, delay1 = 30;


void win1_on_expose (Ez_event *ev)
{
    /* We draw based on count1 */
    int xc = win1_w/2, rx = xc * count1 / MAX_CPT1,
        yc = win1_h/2, ry = yc * count1 / 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: quit");
}


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)            /* The timer has expired */
{
    /* We increment the counter count1 so as to modify the position of the
       object to animate */
    count1 = (count1 + 1) % MAX_CPT1;
    /* We send an Expose event so that the window will be displayed again */
    ez_send_expose (ev->win);
    /* We restart the timer to maintain a "loop" or TimerNotify events,
       which is iterated each delay1 milliseconds */
    ez_start_timer (ev->win, delay1);
}


void win1_on_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: Hypnosis", win1_on_event);

    /* Enable double buffer to prevent window flashes */
    ez_window_dbuf (win1, 1);

    /* Start a timer to get a TimerNotify event in delay1 milliseconds:
       this is the starting point of the "loop" of TimerNotify events. */
    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

This example demo-10.c demonstrates also how to attach an information (e.g. a struct) to a window, so as to avoid global variables. For more explanations, see Client-data. We use this possibility in the whole next part.

1.11. Images

In the previous sections 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);                   /* Load an image */
    if (a->image1 == NULL) exit (1);
}


void app_data_destroy (App_data *a)
{
    ez_image_destroy (a->image1);                           /* Destroy 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);              /* Display image */
}


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


void win1_on_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 (                   /* Resize window for image */
        a.image1->width, a.image1->height, 
        filename, win1_on_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 -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; 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);

    /* Initial position is centered */
    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, 
        "[Arrows] to move");
    ez_draw_text (a->win1, EZ_BRF, a->image1->width-10, a->image1->height+15, 
        "Opacity [+-] : %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_on_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 with transparency", win1_on_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.