Tutorial: Create a gesture-handling application

Updated: April 19, 2023

This tutorial shows you the basics for creating a gesture application using system-supported gesture recognizers.

You will learn to:

Create your gesture callback function

The gesture callback function defines what the application does when a gesture is recognized or updated:

void(*gesture_callback_f)(struct gesture_base* gesture,
                          mtouch_event_t* event,
                          void* param, int async);
            

The argument gesture contains information about the gesture and the parameter event contains information about the mtouch event that caused the gesture. The parameter async identifies whether this callback was invoked from an event (async = 0) or from a timer (async = 1).

If you have gestures that are transitioning based on timer events, this callback function could be invoked as a result of either a timer event (from the context of the timer thread) or an mtouch event. Your application code needs to implement the synchronization mechanism between mtouch callback functions and timer-event callback functions. Your application needs to check the async parameter and implement synchronization accordingly.

This function's main component is a switch statement that defines the application's actions based on the gesture received. Typically, your gesture application copies information from the incoming gesture to a local structure and uses that information accordingly. The type of local structure depends on the incoming gesture. Usually, you're interested only if a certain gesture has been detected (i.e., the state of the gesture recognizer is GESTURE_STATE_COMPLETE). However, your callback function may look for other states and behave accordingly for your application. For an example of such switch statement, see the following code:
switch (gesture->type) {
    case GESTURE_TWO_FINGER_PAN: {
        gesture_tfpan_t* tfpan = (gesture_tfpan_t*)gesture;
        if (tfpan->base.state == GESTURE_STATE_COMPLETE)
        {
           printf("Two-finger pan gesture detected: %d, %d",
                                                   tfpan->centroid.x, tfpan->centroid.y);
        }   
        break;
    }
    case GESTURE_ROTATE: {
        gesture_rotate_t* rotate = (gesture_rotate_t*)gesture;
        if (rotate->base.state == GESTURE_STATE_COMPLETE)  {
           if (rotate->angle != rotate->last_angle) {
               printf("Rotate: %d degs", rotate->angle - rotate->last_angle);
           }
        }
        break;
    }
    case GESTURE_SWIPE: {
        gesture_swipe_t* swipe = (gesture_swipe_t*)gesture;
        if (swipe->base.state == GESTURE_STATE_COMPLETE)  {
           if (swipe->direction & GESTURE_DIRECTION_UP) {
               printf("up %d", swipe->last_coords.y - swipe->coords.y);
           } else if (swipe->direction & GESTURE_DIRECTION_DOWN) {
              printf("down %d", swipe->coords.y - swipe->last_coords.y);
           } else if (swipe->direction & GESTURE_DIRECTION_LEFT) {
               printf("left %d", swipe->last_coords.x - swipe->coords.x);
           } else if (swipe->direction & GESTURE_DIRECTION_RIGHT) {
               printf("right %d", swipe->coords.x - swipe->last_coords.x);
           }
        }
        break;
    }
    case GESTURE_PINCH: {
        gesture_pinch_t* pinch = (gesture_pinch_t*)gesture;
        if (pinch->base.state == GESTURE_STATE_COMPLETE)  {
           printf("Pinch %d, %d", (pinch->last_distance.x - pinch->distance.x),
                                  (pinch->last_distance.y - pinch->distance.y));
        }
        break;
    }
    case GESTURE_TAP: {
        gesture_tap_t* tap = (gesture_tap_t*)gesture;
        if (tap->base.state == GESTURE_STATE_COMPLETE)  {
           printf("Tap x:%d y:%d",tap->touch_coords.x, tap->touch_coords.y);
        }
        break;
    }
    case GESTURE_DOUBLE_TAP: {
        gesture_double_tap_t* d_tap = (gesture_double_tap_t*)gesture;
        if (d_tap->base.state == GESTURE_STATE_COMPLETE)  {
           printf("Double tap first_touch x:%d y:%d", d_tap->first_touch.x,
                                                      d_tap->first_touch.y);
           printf("Double tap first_release x:%d y:%d", d_tap->first_release.x,
                                                        d_tap->first_release.y);
           printf("Double tap second_touch x:%d y:%d", d_tap->second_touch.x,
                                                       d_tap->second_touch.y);
           printf("Double tap second_release x:%d y:%d", d_tap->second_touch.x,
                                                         d_tap->second_release.y);
        }
        break;
    }
    case GESTURE_TRIPLE_TAP: {
        gesture_triple_tap_t* t_tap = (gesture_triple_tap_t*)gesture;
        if (t_tap->base.state == GESTURE_STATE_COMPLETE)  {
           printf("Triple tap first_touch x:%d y:%d", t_tap->first_touch.x,
                                                      t_tap->first_touch.y);
           printf("Triple tap first_release x:%d y:%d", t_tap->first_release.x,
                                                        t_tap->first_release.y);
           printf("Triple tap second_touch x:%d y:%d", t_tap->second_touch.x,
                                                       t_tap->second_touch.y);
           printf("Triple tap second_release x:%d y:%d", t_tap->second_touch.x,
                                                         t_tap->second_release.y);
           printf("Triple tap third_touch x:%d y:%d", t_tap->third_touch.x,
                                                      t_tap->second_touch.y);
           printf("Triple tap third_release x:%d y:%d", t_tap->third_touch.x,
                                                        t_tap->second_release.y);
        }
        break;
    }
    case GESTURE_PRESS_AND_TAP: {
        gesture_pt_t* pt_t = (gesture_pt_t*)gesture;
        if (pt_t->base.state == GESTURE_STATE_COMPLETE)  {
           printf("Initial press x:%d y:%d", pt_t->initial_coords[0].x,
                                             pt_t->initial_coords[0].y);
           printf("Initial tap x:%d y:%d", pt_t->initial_coords[1].x,
                                           pt_t->initial_coords[1].y);
           printf("Press x:%d y:%d", pt_t->coords[0].x, pt_t->coords[0].y);
           printf("Tap x:%d y:%d", pt_t->coords[1].x, pt_t->coords[1].y);
        }
        break;
    }
    case GESTURE_TWO_FINGER_TAP: {
        gesture_tft_t* tft_t = (gesture_tft_t*)gesture;
        if (tft_t->base.state == GESTURE_STATE_COMPLETE)  {
           printf("Coordinates of touch event (finger 1) x:%d y:%d",
                                                         tft_t->touch_coords[0].x,
                                                         tft_t->touch_coords[0].y);
           printf("Coordinates of touch event (finger 2) x:%d y:%d",
                                                         tft_t->touch_coords[1].x,
                                                         tft_t->touch_coords[1].y);
           printf("Coordinates of release event (finger 1) x:%d y:%d",
                                                         tft_t->release_coords[0].x,
                                                         tft_t->release_coords[0].y);
           printf("Coordinates of release event (finger 2) x:%d y:%d",
                                                         tft_t->release_coords[1].x,
                                                         tft_t->release_coords[1].y);
           printf("Midpoint between two touches x:%d y:%d", tft_t->centroid.x,
                                                            tft_t->centroid.y);
        }
        break;
    }
    case GESTURE_LONG_PRESS: {
        gesture_long_press_t* lp_t = (gesture_long_press_t*)gesture;
        if (lp_t->base.state == GESTURE_STATE_COMPLETE)  {
           printf("Long press x:%d y:%d",lp_t->coords.x, lp_t->coords.y);
           printf("Timer ID:%d",lp_t->success_timer);
        }
        break;
    }
    case GESTURE_USER: {
        printf("User-defined gesture detected.");
        break;
    }
    default:
        printf("Unknown");
        break;
    }
                

Create your failure callback function (optional)

Sometimes, you may want your application to create a failure callback function to be invoked when all gestures in a gesture set have transitioned to the GESTURE_STATE_FAILED state:
void(*gestures_set_fail_f)(struct gestures_set* set, struct event_list* list, int async);
                
This sample failure callback function shows how to copy an event list that's received as part of a gesture set failure callback.
void fail_callback(struct gestures_set* set, struct event_list* list, int async)
{
    /* first_set should be defined in your application as
     * struct gestures_set* first_set;
     * and then be allocated and initialized in your application.
     */
    if (set == first_set) {
        /* An example of list copy.
         * This isn't necessary if events don't need to be kept following the
         * call to gestures_set_process_event_list()
         */
        struct event_list* new_list = event_list_alloc(0, 0, 0, 1);
        if (new_list) {
            event_list_copy(list, new_list);
            gestures_set_process_event_list(second_set, new_list, NULL);
            event_list_free(new_list);
        }
    }
}
                

Initialize your gesture sets

Your application needs to register the callback function with the Gestures library so that it can be invoked when a gesture occurs.

To register the gesture callback function, your application needs to first allocate the gesture set. If you have defined a failure callback for your gesture set, then you must call:

gestures_set_register_fail_cb(struct gestures_set* set, gestures_set_fail_f callback);
            

to register your failure callback function with your gesture set.

In this example, two gesture sets are initialized and a failure callback is registered with the first gesture set:
struct gestures_set* first_set;
struct gestures_set* second_set;

static void init_gestures()
{
    gesture_tap_t* tap;
    gesture_double_tap_t* double_tap;
    gesture_triple_tap_t* triple_tap;
    gesture_tft_t* tft;

    first_set = gestures_set_alloc();
        long_press_gesture_alloc(NULL, gesture_callback, first_set);
        tap = tap_gesture_alloc(NULL, gesture_callback, first_set);
        double_tap = double_tap_gesture_alloc(NULL, gesture_callback, first_set);
        triple_tap = triple_tap_gesture_alloc(NULL, gesture_callback, first_set);
        tft = tft_gesture_alloc(NULL, gesture_callback, first_set);
        gesture_add_mustfail(&tap->base, &double_tap->base);
        gesture_add_mustfail(&double_tap->base, &triple_tap->base);
        gestures_set_register_fail_cb(first_set, fail_callback);

    second_set = gestures_set_alloc();
        swipe_gesture_alloc(NULL, gesture_callback, second_set);
        pinch_gesture_alloc(NULL, gesture_callback, second_set);
        rotate_gesture_alloc(NULL, gesture_callback, second_set);
        pt_gesture_alloc(NULL, gesture_callback, second_set);
        tfpan_gesture_alloc(NULL, gesture_callback, second_set);
        tft_gesture_alloc(NULL, gesture_callback, second_set);
}
                

If you're using custom gestures that you have defined yourself, then you need to allocate your custom gesture recognizer and add it to the gesture set as part of the gesture-set initialization using the alloc() function you've defined. For example, in the code snippet shown, you can add your custom gesture recognizer to your second gesture set by calling your custom_gesture_alloc() function in init_gestures():

static void init_gestures()
{
   ...
   second_set = gestures_set_alloc();
   ...
      gesture_custom_t* user_gesture = custom_gesture_alloc(custom_params,
                                                            gesture_callback,
                                                            set);
}
                

Detect the gestures

Now, your application needs a way of triggering the gesture callback function when a touch event occurs. When such a touch event is detected, your application calls gestures_set_process_event().

Touch events can be detected through Screen events in a main application loop. If the incoming event is a touch, move, or release event, you need to populate an mtouch event with data from the Screen event. The helper function, screen_get_mtouch_event() can do this for you; it's part of the Input Events library. See file input/screen_helpers.h for more information. Then, your application calls gestures_set_process_event().
while (1) {
    while (screen_get_event(screen_ctx, screen_ev, ~0L) == EOK) {
        rc = screen_get_event_property_iv(screen_ev, SCREEN_PROPERTY_TYPE, &screen_val);
        if (rc || screen_val == SCREEN_EVENT_NONE) {
            break;
        }
        switch (screen_val) {
            case SCREEN_EVENT_MTOUCH_TOUCH:
            case SCREEN_EVENT_MTOUCH_MOVE:
            case SCREEN_EVENT_MTOUCH_RELEASE:
                rc = screen_get_mtouch_event(screen_ev, &mtouch_event, 0);
                if (rc) {
                    fprintf(stderr, "Error: failed to get mtouch event\n");
                    continue;
                }
                gestures_set_process_event(first_set, &mtouch_event, NULL);
                break;
        }
    }
}
                

Clean up gestures

Before exiting your application, you need to free the memory associated with your gesture sets:

static void gestures_cleanup()
{
    if (NULL != first_set) {
        gestures_set_free(first_set);
        first_set = NULL;
    }
    if (NULL != second_set) {
        gestures_set_free(second_set);
        second_set = NULL;
    }
}