Evfilter library documentation
Welcome, this part of documentation describes how filters works and how to create filter yourself.
Generally filter in this library is black box that has some events on input and sends some events to output. For every event on input, filter can generate none or many events on output.
In the following text there are two meaings for word filter, the first one is filter implementation and the second one is filter structure allocated on heap. Filter implementation gererally are static functions that belongs to filter and two non static functions, so they are propagated to object files and can be used by linker. Any of these two non static functions can be used to allocate and initalize filter on the heap. First one does the allocation and initalization, but in contrast to the second one takes some number of filter parameters. This function is inteded to be used directly from C code. The second one takes parameters as one string, parses them and usually calls the first one that does the allocation. This function is called when filter configuration is loaded from file.
Filter parameters comes to the filter as signle string consisting of name=value pairs separated by whitespaces. Whitespaces in parameter name are not allowed and parameter value containing whitespaces must be enclosed in quotes or double quotes. There are no escape characters for quotes. The rest of the syntax is defined freely, multiple whitespaces are counted as one, pamameter name is terminated with either whitespace and/or equality sign so all following examples are correct; name value, name=value, name= 'value', name = "value", ...
Don't worry, functions for parsing filter parameters are also available. All you need to do is to create parameter description. Parameter description is constant field of struct evf_param terminated with {NULL, 0, NULL}.
Typical parameter description example:
static struct evf_param my_filter_params[] = {
{ "treshold", evf_int, NULL },
{ "key" , evf_key, NULL },
{ NULL , 0, NULL },
};
Closer description of struct evf_param:
struct evf_param {
char *name;
enum evf_param_t type;
void *lim;
};
| char *name; | Null terminated string, parameter name in configuration. |
| enum evf_param_t type; |
Parameter type, one of following:
|
| void *lim; | Additional limits for the type. Usually pointer to another structure, see evfilter_param.h for details. |
On filter initalization, parameter description along with corresponding variables is passed to evf_load_param() function. This function has variable number of parameters and it's used in similar manner as printf.
int evf_load_params(union evf_err *err, char *cfg, struct evf_param params[], ...);
Don't concentrate on the union evf_err *err and char *cfg now, they are coming as an part of filter initalization to us. For previous example the initialization would look like:
int treshold, key; if (evf_load_params(err, cfg, my_filter_params, &treshold, &key) == -1) return NULL;On success zero is returned, parameters are parsed and filled into corresponding variables. On failure -1 is returned and struct evf_err *err is filled, so we need just to return NULL to inform higher layers that filter initalization has failed.
Lets start with brief evfilter structure description. When filter initalization routine is called, this structure is allocated on the heap and it's members are assigned.
You can find struct evf_filter definition in file evfilter_struct.h.
struct evf_filter {
void (*modify)(struct evf_filter *self, struct input_event *ev);
void *(*free)(struct evf_filter *self);
char *name;
char *desc;
struct evf_filter *next;
char data[];
}
| void (*modify)(struct evf_filter *self, struct input_event *ev); | This is the most important member of struct evf_filter every event passed to your filter comes through this function. |
| void *(*free)(struct evf_filter *self); |
Additional free helper. Either set to NULL or to pointer to the
function that does additional cleanups. Note that evfilter structure itself
is freed automaticaly. Ignore mind the void* return type here, that's for internal purposes. Just return NULL in your implementation. |
| char *name; | Filter name, null terminated string. |
| desc *name; | Short filter description, null terminated string. |
| struct evf_filter *next; | Next filter in linked list. Set initially to NULL. |
| char *data[]; | Pointer to private data. In case your filter is not stateless, so that it needs to hold some data per instance, allocate some extra space after filter structure and use this as a pointer to it. |
As you may see, most of the structure members are straightforward to
understand. However at least one member deserves closer description. We will
look on the modify function now.
This function gets two parameters, the first one is filter itself or it's
better to say pointer to its instance allocated on the heap. The second
parameter is input event structure as defined in
/usr/include/linux/input.h.
All filter logic is implemented in this function or in functions called from
it's context. As all input events comes through this function it must be
atomic, that means all functions that can cause long sleep are forbiden.
Moreover the time spend in this function should be minimal, in order not to
create lags.
Let's start with simple example that sends all events to the next filter.
void modify(struct evf_filter *self, struct input_event *ev) {
self->next(self->next, ev);
}
We don't need to check for the end of the linked list as there is special filter named commit on the end.
Now we will create filter that generates mouse clicks when pressure on
touchscreen overruns some value. To do that we need to know a little about
anatomy of input event.
Every input event carries part of an information, for example mouse has moved
five points on the X scale up and the coputer has registered this information
at specified time. In order not to move only diagonally special sync event
was introduced. The sync event means, that's all folks now you can compute
the position of the pointer and draw it on the screen.
When creating filter one must to think about the right timing of the events,
it's quite easy to forgotten to filter out sync events correctly.
void modify(struct evf_filter *self, struct input_event *ev) {
struct my_filter_state *state = (struct my_filter_state*) self->data;
/* Touchscreen pressure is absolute event */
if (ev->type == EV_ABS && ev->code == ABS_PRESSURE) {
if (!state->is_pressed && ev->value > TRESHOLD) {
ev->type = EV_KEY;
ev->code = BTN_LEFT;
ev->value = 1;
self->next(self->next, ev);
state->is_pressed = 1;
}
if (state->is_pressed && ev->value < TRESHOLD) {
ev->type = EV_KEY;
ev->code = BTN_LEFT;
ev->value = 0;
self->next(self->next, ev);
state->is_pressed = 0;
}
return;
}
/*
* We are propagating rest of the events to the next filter
* and trust that the sync events are send by the touchscreen
* driver.
*/
self->next(self->next, ev);
}