ecore events and handlers - Setup and use

This example shows how to create a new type of event, setup some event handlers to it, fire the event and have the callbacks called.

After finishing, we delete the event handlers so no memory will leak.

See the full source code for this example here.

Let's start the example from the beginning:

//Compile with:
// gcc -g -Wall -o ecore_event_example_02 ecore_event_example_02.c `pkg-config --cflags --libs ecore`
#include <Ecore.h>
#include <unistd.h>
struct context // helper struct to give some context to the callbacks
{
const char *str1, *str2;
Ecore_Event_Handler *handler1;
Ecore_Event_Handler *handler2;
};
static int _event_type = 0; // a new type of event will be defined and stored here
Ecore_Event_Handler
struct _Ecore_Event_Handler Ecore_Event_Handler
A handle for an event handler.
Definition Ecore_Common.h:576

First thing is to declare a struct that will be passed as context to the event handlers. In this structure we will store the event handler pointers, and two strings that will be used by the first event handler. We also will use a global integer to store the event type used for our event. It is initialized with 0 in the beginning because the event wasn't created yet. Later, in the main function we will use ecore_event_type_new() to associate another value to it. Now the event handler callbacks:

static Eina_Bool
_event_handler1_cb(void *data, int type EINA_UNUSED, void *event)
{
int *number = event;
const char *str = data;
printf("event_handler1: number=%d, data=\"%s\".\n", *number, str);
if ((*number % 2) == 0)
return ECORE_CALLBACK_DONE;
return ECORE_CALLBACK_PASS_ON;
}
ECORE_CALLBACK_PASS_ON
#define ECORE_CALLBACK_PASS_ON
Return value to pass event to next handler.
Definition Ecore_Common.h:155
ECORE_CALLBACK_DONE
#define ECORE_CALLBACK_DONE
Return value to stop event handling.
Definition Ecore_Common.h:156
Eina_Bool
unsigned char Eina_Bool
Type to mimic a boolean.
Definition eina_types.h:527
EINA_UNUSED
#define EINA_UNUSED
Used to indicate that a function parameter is purposely unused.
Definition eina_types.h:339

This is the first event handler callback. It prints the event data received by the event, and the data passed to this handler when it was added. Notice that this callback already knows that the event data is an integer pointer, and that the handler data is a string. It knows about the first one because this is based on the type of event that is going to be handled, and the second because it was passed to the ecore_event_handler_add() function when registering the event handler.

Another interesting point about this callback is that it returns ECORE_CALLBACK_DONE (0) if the event data is even, swallowing the event and don't allowing any other callback to be called after this one for this event. Otherwise it returns ECORE_CALLBACK_PASS_ON, allowing the event to be handled by other event handlers registered for this event. This makes the second event handler be called just for "odd" events.

static Eina_Bool
_event_handler2_cb(void *data, int type EINA_UNUSED, void *event) // event callback
{
struct context *ctxt = data;
int *number = event;
printf("event_handler2: number=%d.\n", *number);
if (*number == 5)
{
const char *old = NULL;
old = ecore_event_handler_data_set(ctxt->handler1, (void *)ctxt->str2);
printf("changed handler1 data from \"%s\" to \"%s\".\n",
old, ctxt->str2);
}
else if (*number >= 10)
{
printf("finish main loop.\n");
ecore_main_loop_quit();
}
return ECORE_CALLBACK_DONE; // same as EINA_FALSE
ecore_event_handler_data_set
void * ecore_event_handler_data_set(Ecore_Event_Handler *eh, const void *data)
Sets the data associated with an Ecore_Event_Handler.
Definition ecore_events.c:44
ecore_main_loop_quit
void ecore_main_loop_quit(void)
Quits the main loop once all the events currently on the queue have been processed.
Definition ecore_main.c:1321
}

The second event handler will check if the event data is equal to 5, and if that's the case, it will change the event handler data of the first event handler to another string. Then it checks if the event data is higher than 10, and if so, it will request the main loop to quit.

An interesting point of this example is that although the second event handler requests the main loop to finish after the 11th event being received, it will process all the events that were already fired, and call their respective event handlers, before the main loop stops. If we didn't want these event handlers to be called after the 11th event, we would need to unregister them with ecore_event_handler_del() at this point.

Now some basic initialization of the context, and the Ecore library itself:

int
main(void)
{
struct context ctxt = {0};
int i;
ctxt.str1 = "dataone";
ctxt.str2 = "datatwo";
if (!ecore_init())
{
printf("ERROR: Cannot init Ecore!\n");
return -1;
}
_event_type = ecore_event_type_new();
ecore_event_type_new
int ecore_event_type_new(void)
Allocates a new event type id sensibly and returns the new id.
Definition ecore_events.c:80
ecore_init
EAPI int ecore_init(void)
Sets up connections, signal handlers, sockets etc.
Definition ecore.c:230

This last line is interesting. It creates a new type of event and returns a unique ID for this event inside Ecore. This ID can be used anywhere else in your program to reference this specific type of event, and to add callbacks to it.

It's common if you are implementing a library that declares new types of events to export their respective types as extern in the header files. This way, when the library is initialized and the new type is created, it will be available through the header file to an application using it add some callbacks to it. Since our example is self-contained, we are just putting it as a global variable.

Now we add some callbacks:

ctxt.handler1 = ecore_event_handler_add(_event_type,
_event_handler1_cb,
ctxt.str1);
ctxt.handler2 = ecore_event_handler_add(_event_type,
_event_handler2_cb,
&ctxt);
ecore_event_handler_add
Ecore_Event_Handler * ecore_event_handler_add(int type, Ecore_Event_Handler_Cb func, const void *data)
Adds an event handler.
Definition ecore_events.c:13

This is very simple. Just need to call ecore_event_handler_add() with the respective event type, the callback function to be called, and a data pointer that will be passed to the callback when it is called by the event.

Then we start firing events:

for (i = 0; i <= 15; i++)
{
int *event_data = malloc(sizeof(*event_data));
*event_data = i;
ecore_event_add(_event_type, event_data, NULL, NULL);
}
ecore_event_add
Ecore_Event * ecore_event_add(int type, void *ev, Ecore_End_Cb func_free, void *data)
Adds an event to the event queue.
Definition ecore_events.c:52

This `for` will fire 16 events of this type. Notice that the events will be fired consecutively, but any callback will be called yet. They are just called by the main loop, and since it wasn't even started, nothing happens yet. For each event fired, we allocate an integer that will hold the number of the event (we are arbitrarily creating these numbers just for demonstration purposes). It's up to the event creator to decide which type of information it wants to give to the event handler, and the event handler must know what is the event info structure for that type of event.

Since we are not allocating any complex structure, just a simple integer, we don't need to pass any special free function to ecore_event_add(), and it will use a simple `free` on our data. That's the default behavior.

Now finishing our example:

printf("start the main loop.\n");
ecore_main_loop_begin();
ecore_shutdown();
return 0;
}
ecore_shutdown
EAPI int ecore_shutdown(void)
Shuts down connections, signal handlers sockets etc.
Definition ecore.c:371
ecore_main_loop_begin
void ecore_main_loop_begin(void)
Runs the application main loop.
Definition ecore_main.c:1311

We just start the main loop and watch things happen, waiting to shutdown Ecore when the main loop exits and return.