Example: Code snippets of a defining a custom gesture

Updated: May 06, 2022

This example contains most of the code snippets that illustrate what you need in order to define a custom gesture.

custom_gesture.h
#include <gestures/types.h>

/* The stucture custom_gesture_params_t represents the parameters for the custom gesture. */
typedef struct {
   unsigned max_displacement; /* The maximum distance the finger can move before
                                 the custom gesture fails. */
   unsigned max_hold_ms;      /* The maximum time the finger can remain touching
                                 the screen before the custom gesture fails. */
   unsigned max_delay_ms;     /* The time between the first release and the second touch. */
} custom_gesture_params_t;

/* The enumeration custom_gesture_state_e defines additional states the custom
 * gesture can transition between. */
typedef enum {
   CT_STATE_INIT = 0,
   CT_STATE_FIRST_TOUCH,
   CT_STATE_FIRST_RELEASE,
   CT_STATE_SECOND_TOUCH,
   CT_STATE_SECOND_RELEASE
} custom_gesture_state_e;

/* The structure gesture_custom_gesture_t carries data about the custom gesture. */
typedef struct {
   gesture_base_t base;             /* The gesture base data structure. */
   custom_gesture_params_t params;  /* The custom gesture 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 dt_state; /* The intermediate state of the custom gesture. */
   int fail_timer;                  /* The ID of the timer for this gesture. */
} gesture_custom_gesture_t;

/* Allocate and initialize the custom gesture structure */
gesture_custom_gesture_t* custom_gesture_gesture_alloc(custom_gesture_params_t* params,
                                                       gesture_callback_f callback,
                                                       struct gestures_set* set);

/* Initialize the custom parameters */
void custom_gesture_gesture_default_params(custom_gesture_params_t* params);

		   
custom_gesture.c
/*The example below shows the implementation of a custom user-defined gesture. */ 

#include <sys/types.h>
#include <stdlib.h>
#include <malloc.h>
#include <string.h>
#include <stdio.h>
#include <gestures/set.h>
#include <gestures/timer.h>
#include <custom_gesture.h>
#include <input/event_types.h>
#include <gestures/defaults.h>

/* Custom free() function */
void custom_gesture_gesture_free(gesture_base_t* gesture)
{
   free(gesture);
}

/* Custom helper function */
static int _check_valid(gesture_coords_t* coords1, gesture_coords_t* coords2,
                        unsigned max_displacement, unsigned max_ms)
{
   return ((max_displacement_abs(coords1, coords2) <= max_displacement) &&
           (diff_time_ms(coords1, coords2) <= max_ms));
}

/* Custom process_event() function */
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_gesture_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 (custom_gesture->base.state) {
      case GESTURE_STATE_UNRECOGNIZED:
         switch (event->event_type) {
            case INPUT_EVENT_MTOUCH_TOUCH:
               if (set_id > 0) {
                  goto failed;
               } else if (custom_gesture->dt_state > CT_STATE_FIRST_TOUCH) {
                  gesture_timer_clear(gesture, custom_gesture->fail_timer);
                  save_coords(event, &custom_gesture->second_touch);
                  if (!_check_valid(&custom_gesture->first_release,
                                    &custom_gesture->second_touch,
                                    custom_gesture->params.max_displacement,
                                    custom_gesture->params.max_delay_ms)) {
                     goto failed;
                  }
                  custom_gesture->dt_state = CT_STATE_SECOND_TOUCH;
               } else {
                  save_coords(event, &custom_gesture->first_touch);
                  custom_gesture->dt_state = CT_STATE_FIRST_TOUCH;
               }

               goto nochange;
            case INPUT_EVENT_MTOUCH_MOVE:
               save_coords(event, &coords);
               if (custom_gesture->dt_state >= CT_STATE_SECOND_TOUCH) {
                  compare_coords = &custom_gesture->second_touch;
               } else {
                  compare_coords = &custom_gesture->first_touch;
               }

               if (!_check_valid(compare_coords, &coords,
                                 custom_gesture->params.max_displacement,
                                 custom_gesture->params.max_hold_ms)) {
                  goto failed;
               }

               goto nochange;
            case INPUT_EVENT_MTOUCH_RELEASE:
               if (custom_gesture->dt_state >= CT_STATE_SECOND_TOUCH) {
                  save_coords(event, &custom_gesture->second_release);
                  if (!_check_valid(&custom_gesture->second_touch,
                                    &custom_gesture->second_release,
                                    custom_gesture->params.max_displacement,
                                    custom_gesture->params.max_hold_ms)) {
                     goto failed;
                  }
                  custom_gesture->dt_state = CT_STATE_SECOND_RELEASE;
                  goto complete;
               } else {
                  save_coords(event, &custom_gesture->first_release);
                  if (!_check_valid(&custom_gesture->first_touch,
                                    &custom_gesture->first_release,
                                    custom_gesture->params.max_displacement,
                                    custom_gesture->params.max_hold_ms)) {
                     goto failed;
                  }
                  custom_gesture->dt_state = CT_STATE_FIRST_RELEASE;

                  /* Set a timer in case an event doesn't come in */
                  gesture_timer_set_event(gesture,
                                          custom_gesture->fail_timer,
                                          custom_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 custom_gesture->base.state;

failed:
   *consumed = 0;
   return GESTURE_STATE_FAILED;

complete:
   *consumed = 1;
   return GESTURE_STATE_COMPLETE;
}

/* Custom timer callback */
static gesture_state_e
timeout(gesture_base_t* base, void* param)
{
   return GESTURE_STATE_FAILED;
}

/* Custom reset() function */
void custom_gesture_gesture_reset(gesture_base_t* gesture)
{
   gesture_custom_gesture_t* custom_gesture = (gesture_custom_gesture_t*)gesture;
   custom_gesture->dt_state = CT_STATE_INIT;
}

/* Initialize custom gesture parameters with default values. */
void custom_gesture_gesture_default_params(custom_gesture_params_t* params)
{
   params->max_displacement = GESTURE_MAX_MOVE_TOLERANCE_PIX;
   params->max_hold_ms = GESTURE_MAX_TAP_DELAY_MS;
   params->max_delay_ms = GESTURE_MIN_HOLD_DELAY_MS;
}

/* Custom alloc() function */
gesture_custom_gesture_t* custom_gesture_gesture_alloc(custom_gesture_params_t* params,
                                                       gesture_callback_f callback,
                                                       struct gestures_set* set)
{
	gesture_custom_gesture_t* custom_gesture = calloc(1, sizeof(*custom_gesture));
   if (NULL == custom_gesture) {
      return NULL;
   }

   gesture_base_init(&custom_gesture->base);
   gestures_set_add(set, &custom_gesture->base);
   custom_gesture->base.type = GESTURE_USER;
   custom_gesture->base.funcs.free = custom_gesture_gesture_free;
   custom_gesture->base.funcs.process_event = custom_gesture_gesture_process_event;
   custom_gesture->base.funcs.reset = custom_gesture_gesture_reset;
   custom_gesture->base.callback = callback;
   custom_gesture->fail_timer = gesture_timer_create(&custom_gesture->base, timeout, NULL);
   custom_gesture_gesture_reset(&custom_gesture->base);

   if (NULL != params) {
      custom_gesture->params = *params;
   } else {
      custom_gesture_gesture_default_params(&custom_gesture->params);
   }

   return custom_gesture;
}