This tutorial shows you the basics for defining a custom gesture recognizer.
The definition of your custom gesture recognizer includes the following:
typedef struct {
unsigned max_displacement; /** The maximum distance your finger can move before
this custom gesture fails. */
unsigned max_hold_ms; /** The maximum time your finger can remain touching
the screen before this custom gesture fails. */
unsigned max_delay_ms; /** The time between the first release and the second
touch. */
} custom_gesture_params_t;
typedef enum {
CUSTOM_STATE_INIT = 0,
CUSTOM_STATE_FIRST_TOUCH,
CUSTOM_STATE_FIRST_RELEASE,
CUSTOM_STATE_SECOND_TOUCH,
CUSTOM_STATE_SECOND_RELEASE
} custom_gesture_state_e;
typedef struct {
gesture_base_t base; /* The gesture base data structure. */
custom_gesture_params_t params; /** Your custom gesture recognizer parameters. */
gesture_coords_t first_touch; /** The coordinates of the first touch. */
gesture_coords_t first_release; /** The coordinates of the first release. */
gesture_coords_t second_touch; /** The coordinates of the second touch. */
gesture_coords_t second_release; /** The coordinates of the second release. */
custom_gesture_state_e ct_state; /** The intermediate state of your recognizer. */
int timer; /** The ID of the timer for this gesture. */
} gesture_custom_t;
If your custom gesture recognizer is timer-based, you need to implement a callback function that will be called upon receipt of a timer event. Your gesture_timer_callback_t function returns the new, or unchanged, state based on the timer event received and might look like this:
static gesture_state_e custom_gesture_timer_callback(gesture_base_t* base, void* param)
{
return GESTURE_STATE_FAILED;
}
Here's an example of what your custom gesture recognizer alloc() might look like:
gesture_custom_t*
custom_gesture_alloc(custom_gesture_params_t* params,
gesture_callback_f callback,
struct gestures_set* set)
{
gesture_custom_t* user_gesture = calloc(1, sizeof(*user_gesture));
if (NULL == user_gesture) {
return NULL;
}
gesture_base_init(&user_gesture->base);
gestures_set_add(set, &user_gesture->base);
user_gesture->base.type = GESTURE_USER;
user_gesture->base.funcs.free = user_gesture_gesture_free;
user_gesture->base.funcs.process_event = user_gesture_gesture_process_event;
user_gesture->base.funcs.reset = user_gesture_gesture_reset;
user_gesture->base.callback = callback;
user_gesture->timer = gesture_timer_create(&user_gesture->base,
custom_gesture_timer_callback, NULL);
user_gesture_gesture_reset(&user_gesture->base);
if (NULL != params) {
user_gesture->params = *params;
} else {
user_gesture_gesture_default_params(&user_gesture->params);
}
return user_gesture;
}
You need to implement a process_event() function that's responsible for state-handling and returning the new (or unchanged) gesture set:
gesture_state_e (*process_event)(struct contact_id_map* map,
struct gesture_base* gesture,
mtouch_event_t* event,
int* consumed);
Ensure that your state transitions are valid according to the Gestures library. Refer to State transitions.
gesture_state_e
custom_gesture_gesture_process_event(struct contact_id_map* map,
gesture_base_t* gesture,
mtouch_event_t* event,
int* consumed)
{
gesture_custom_t* custom_gesture = (gesture_custom_gesture_t*)gesture;
gesture_coords_t coords;
gesture_coords_t* compare_coords;
int set_id = map_contact_id(map, event->contact_id);
if (set_id < 0) {
error("process_event() called with event with invalid contact_id");
goto failed;
}
switch (user_gesture->base.state) {
case GESTURE_STATE_UNRECOGNIZED:
switch (event->event_type) {
case INPUT_EVENT_MTOUCH_TOUCH:
if (set_id > 0) {
goto failed;
} else if (user_gesture->ct_state > CUSTOM_STATE_FIRST_TOUCH) {
gesture_timer_clear(gesture, user_gesture->fail_timer);
save_coords(event, &user_gesture->second_touch);
if (!((max_displacement_abs(&user_gesture->first_release,
&user_gesture->second_touch)
<= user_gesture->params.max_displacement) &&
(diff_time_ms(&user_gesture->first_release,
&user_gesture->second_touch)
<= user_gesture->params.max_delay_ms))) {
goto failed;
}
user_gesture->ct_state = CUSTOM_STATE_SECOND_TOUCH;
} else {
save_coords(event, &user_gesture->first_touch);
user_gesture->ct_state = CUSTOM_STATE_FIRST_TOUCH;
}
goto nochange;
case INPUT_EVENT_MTOUCH_MOVE:
save_coords(event, &coords);
if (user_gesture->ct_state >= CUSTOM_STATE_SECOND_TOUCH) {
compare_coords = &user_gesture->second_touch;
} else {
compare_coords = &user_gesture->first_touch;
}
if (!((max_displacement_abs(compare_coords,
&coords)
<= user_gesture->params.max_displacement) &&
(diff_time_ms(compare_coords,
&coords)
<= user_gesture->params.max_hold_ms)))
{
goto failed;
}
goto nochange;
case INPUT_EVENT_MTOUCH_RELEASE:
if (user_gesture->ct_state >= CUSTOM_STATE_SECOND_TOUCH) {
save_coords(event, &user_gesture->second_release);
if (!((max_displacement_abs(&user_gesture->second_touch,
&user_gesture->second_release)
<= user_gesture->params.max_displacement) &&
(diff_time_ms(&user_gesture->second_touch,
&user_gesture->second_release)
<= user_gesture->params.max_hold_ms)))
{
goto failed;
}
user_gesture->ct_state = CUSTOM_STATE_SECOND_RELEASE;
goto complete;
} else {
save_coords(event, &user_gesture->first_release);
if (!((max_displacement_abs(&user_gesture->first_touch,
&user_gesture->first_release)
<= user_gesture->params.max_displacement) &&
(diff_time_ms(&user_gesture->first_touch,
&user_gesture->first_release)
<= user_gesture->params.max_hold_ms)))
{
goto failed;
}
user_gesture->ct_state = CUSTOM_STATE_FIRST_RELEASE;
/* Set a timer in case an event doesn't come in */
gesture_timer_set_event(gesture, user_gesture->timer,
user_gesture->params.max_delay_ms, event);
}
goto nochange;
default:
warn("Unhandled switch/case: %d", event->event_type);
goto failed;
}
case GESTURE_STATE_RECOGNIZED:
error("GESTURE_STATE_RECOGNIZED is an invalid state for double tap");
break;
case GESTURE_STATE_UPDATING:
error("GESTURE_STATE_UPDATING is an invalid state for double tap");
break;
case GESTURE_STATE_COMPLETE:
error("process_event() called on complete gesture");
break;
case GESTURE_STATE_FAILED:
error("process_event() called on failed gesture");
break;
case GESTURE_STATE_NONE:
error("process_event() called on uninitialized gesture");
break;
}
nochange:
*consumed = 0;
return user_gesture->base.state;
failed:
*consumed = 0;
return GESTURE_STATE_FAILED;
complete:
*consumed = 1;
return GESTURE_STATE_COMPLETE;
}
Release the memory that's associated with the custom gesture:
void custom_gesture_free(struct gesture_base* gesture);
A free() function for a custom gesture may simply look like this:
void custom_gesture_free(gesture_base_t* gesture)
{
free(gesture);
}
Reset any specific data structures that are associated with the custom gesture:
void custom_gesture_reset(struct gesture_base* gesture);
A reset() function for a custom gesture may be empty if there are no specific data structures associated with your custom gesture.
void
custom_gesture_reset(gesture_base_t* gesture)
{
gesture_custom_t* user_gesture = (gesture_custom_t*)gesture;
user_gesture->ct_state = CUSTOM_STATE_INIT;
}