forked from enlightenment/www-content
205 lines
8.2 KiB
Plaintext
205 lines
8.2 KiB
Plaintext
---
|
|
~~Title: Main Loop Programming Guide~~
|
|
~~NOCACHE~~
|
|
---
|
|
|
|
# Main Loop Programming Guide #
|
|
|
|
The EFL is event-driven. This means that execution usually takes place within an internal EFL *Main Loop*. The application is notified through function callbacks of virtually any event happening on the computer. This is typically more efficient than *polling* for events, whereby the application has to repeatedly ask if a certain event has occurred. When nothing is happening (no events are pending) the main loop enters the *idle state* during which the CPU consumes very little power.
|
|
|
|
EFL manages timers, file descriptors, user interface events amongst other things and even provides a simple mechanism for applications to perform conventional data polling if required.
|
|
|
|
## Prerequisites ##
|
|
|
|
* Read the [Introduction to Eo](/develop/tutorials/c/eo-intro.md) to understand how Eo objects are created and destroyed.
|
|
|
|
* Read the [Events Programming Guide](events.md) to learn how to register callbacks, which can be triggered by events.
|
|
|
|
## The Application Main Loop ##
|
|
|
|
For convenience, when your application starts, EFL creates one Main Loop for you, called the *Application Main Loop*. You can use it as the parent for any object you create that requires a main loop (Like [Promises and Futures](/develop/guides/c/eina/futures.md), for example).
|
|
|
|
In the [Hello World](/develop/tutorials/c/hello-world.md) tutorial you learned that all EFL applications start with the ``efl_main()`` method. You can retrieve the Application Main Loop from the ``Efl_Event *`` parameter like this:
|
|
|
|
```c
|
|
EAPI_MAIN void
|
|
efl_main(void *data, const Efl_Event *ev)
|
|
{
|
|
Efl_Loop *loop = ev->object;
|
|
}
|
|
```
|
|
|
|
## Timers ##
|
|
|
|
Timers allow events to be triggered periodically after the given time has elapsed. After an event callback has been registered with the timer, it will be called at regular intervals.
|
|
|
|
You can find usage examples in the EFL repository: [reference/c/core/src/core_idler.c](https://git.enlightenment.org/enlightenment/examples/src/branch/master/reference/c/core/src/core_idler.c) and [reference/c/core/src/core_poll.c](https://git.enlightenment.org/enlightenment/examples/src/branch/master/reference/c/core/src/core_poll.c).
|
|
|
|
### Creating and Destroying Timers ###
|
|
|
|
Timers are Eo objects. You can create and destroy them with ``efl_add()`` as for all other Eo objects (see [Introduction to Eo](/develop/tutorials/c/eo-intro.md)). Their class is `EFL_LOOP_TIMER_CLASS`:
|
|
|
|
```c
|
|
timer_object = efl_add(EFL_LOOP_TIMER_CLASS, ...);
|
|
```
|
|
|
|
Timers do not need to have a parent. However it is convenient to create them under the application Main Loop, so the parent can manage destroying the timer.
|
|
|
|
If you want to manually destroy a timer use the regular ``efl_unref()`` or ``efl_del()``.
|
|
|
|
### The Timer Callback ###
|
|
|
|
Register the callback using ``efl_event_callback_add()`` and the ``EFL_LOOP_TIMER_EVENT_TIMER_TICK`` event as explained in the [Events Programming Guide](events.md).
|
|
|
|
```c
|
|
efl_event_callback_add(timer_object, EFL_LOOP_TIMER_EVENT_TIMER_TICK, _timer_cb, NULL);
|
|
```
|
|
|
|
The callback has the usual event handler signature:
|
|
|
|
```c
|
|
static void
|
|
_timer_cb(void *data, const Efl_Event *event)
|
|
{
|
|
Efl_Loop_Timer *timer = event->object;
|
|
|
|
[...]
|
|
}
|
|
```
|
|
|
|
Notice how the timer object is recovered from the event structure.
|
|
|
|
### Configuring a Timer ###
|
|
|
|
The ``interval`` property controls the amount of time between callback triggers in *seconds*:
|
|
|
|
```c
|
|
efl_loop_timer_interval_set(timer_object, seconds);
|
|
efl_loop_timer_interval_get(timer_object);
|
|
```
|
|
|
|
The time left before the next trigger of the timer can be retrieved through the ``pending`` read-only property:
|
|
|
|
```c
|
|
efl_loop_timer_pending_get(timer_object);
|
|
```
|
|
|
|
The current interval can be reset with:
|
|
|
|
```c
|
|
efl_loop_timer_reset(timer_object);
|
|
```
|
|
|
|
The current interval can also be extended, effectively delaying all future triggers of the timer by a given number of *seconds*:
|
|
|
|
```c
|
|
efl_loop_timer_delay(timer_object, seconds);
|
|
```
|
|
|
|
### Pausing a Timer ###
|
|
|
|
Pause a timer with:
|
|
|
|
```c
|
|
efl_event_freeze(timer_object);
|
|
```
|
|
|
|
Resume from the previous time index:
|
|
|
|
```c
|
|
efl_event_thaw(timer_object);
|
|
```
|
|
|
|
## File Descriptor Monitors ##
|
|
|
|
EFL can monitor the system's file descriptor activity through ``Efl.Loop.Fd`` objects and trigger relevant events.
|
|
|
|
You can find usage examples in the EFL examples repository: [reference/c/core/src/core_loop.c](https://git.enlightenment.org/enlightenment/examples/src/branch/master/reference/c/core/src/core_loop.c)
|
|
|
|
### Creating and Destroying FD Monitors ###
|
|
|
|
FD monitors are Eo objects that wrap system file descriptors, and are instantiated and destroyed with ``efl_add()`` as for all other Eo objects (see [Introduction to Eo](/develop/tutorials/c/eo-intro.md)). Their class is `EFL_LOOP_FD_CLASS,`:
|
|
|
|
```c
|
|
fd_object = efl_add(EFL_LOOP_FD_CLASS, ...);
|
|
```
|
|
|
|
To destroy an FD monitor use the regular ``efl_unref()`` or ``efl_del()``.
|
|
|
|
### FD Monitor callbacks ###
|
|
|
|
Register to receive events on the FD monitor object with ``efl_event_callback_add()``:
|
|
|
|
```c
|
|
efl_event_callback_add(fd_object, EFL_LOOP_FD_EVENT_READ, _read_fd_cb, NULL);
|
|
```
|
|
|
|
The available events include:
|
|
|
|
* ``EFL_LOOP_FD_EVENT_READ``: The file descriptor is ready to be read.
|
|
|
|
* ``EFL_LOOP_FD_EVENT_WRITE``: The file descriptor is ready to be written to.
|
|
|
|
* ``EFL_LOOP_FD_EVENT_ERROR``: An error has occurred on the file descriptor. You can retrieve more information from the callback using the ``event->info`` field:
|
|
```c
|
|
void
|
|
_fd_error(void *data, const Efl_Event *event)
|
|
{
|
|
const Eina_Error *perr = event->info;
|
|
|
|
fprintf(stderr, " %s error: #%d '%s'\n",
|
|
efl_name_get(event->object), *perr, eina_error_msg_get(*perr));
|
|
```
|
|
|
|
### Accessing the File Descriptor ###
|
|
|
|
Set and get the internal file descriptor wrapped by an FD monitor object with the ``fd`` property:
|
|
|
|
```c
|
|
efl_loop_fd_get(fd_object);
|
|
efl_loop_fd_set(fd_object, fd);
|
|
```
|
|
|
|
When the file descriptor corresponds to a file instead of a network socket, for instance, use ``fd_file`` instead. Otherwise you may face errors when using Windows:
|
|
|
|
```c
|
|
efl_loop_fd_file_get(fd_object);
|
|
efl_loop_fd_file_set(fd_object, fd);
|
|
```
|
|
|
|
## Idlers ##
|
|
|
|
When there are no events to process the EFL main loop enters the Idle state, consuming very little CPU power. You can receive a notification when the Idle state starts or stops.
|
|
|
|
EFL defines three different events you can use to be notified of the above conditions:
|
|
|
|
* ``EFL_LOOP_EVENT_IDLE_ENTER``: Main loop enters the idle state.
|
|
|
|
* ``EFL_LOOP_EVENT_IDLE_EXIT``: Main loop exits the idle state.
|
|
|
|
* ``EFL_LOOP_EVENT_IDLE``: Main loop is in the idle state, meaning that it has nothing else to do. **Be warned** that this callback will be invoked frequently consuming a significant amount of CPU resources. This also defeats the point of an event-driven application: heavy calculations should be performed on a separate thread or they will block the main loop. This can lead to an unresponsive user interface and other issues.
|
|
|
|
Register a callback to be notified of these events using ``efl_event_callback_add()``, as outlined in the [Events Programming Guide](events.md).
|
|
|
|
You can also view the example in the EFL examples repository: [reference/c/core/src/core_idler.c](https://git.enlightenment.org/enlightenment/examples/src/branch/master/reference/c/core/src/core_idler.c).
|
|
|
|
## Polling ##
|
|
|
|
In the rare case where EFL does not provide the event you want, you can resort to conventional application-driven polling. EFL can still manage the scheduling of the periodic polling, calling you back when it is time to perform the poll.
|
|
|
|
You can choose from among the predefined polling priorities depending on which event you use to register your callback:
|
|
|
|
* ``EFL_LOOP_EVENT_POLL_HIGH``: For events that may happen multiple times per second.
|
|
|
|
* ``EFL_LOOP_EVENT_POLL_MEDIUM``: For events that may happen multiple times per minute.
|
|
|
|
* ``EFL_LOOP_EVENT_POLL_LOW``: For events that may happen multiple times every 15 minutes.
|
|
|
|
The actual polling period is controlled by EFL and can be changed system-wide.
|
|
|
|
You can find usage examples in the EFL examples repository: [reference/c/core/src/core_poll.c](https://git.enlightenment.org/enlightenment/examples/src/branch/master/reference/c/core/src/core_poll.c)
|
|
|
|
## Further Reading ##
|
|
[``Efl.Loop`` API Reference](/develop/api/efl/loop)
|
|
: Detailed documentation for the Loop object
|