Tutorial: Create a gesture-handling application

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

You will learn to:

  • Create a gesture callback function
  • Create a gesture set failure callback function
  • Initialize gesture sets
  • Detect gestures
  • Clean up gestures

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 a mtouch event. Your application code must implement the synchronization mechanism between mtouch callback functions and timer-event callback functions. It is also necessary that your application checks 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);
The following 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 must 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 must 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 it is necessary for you 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 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, ensure you 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;
    }
}

Complete code for the gesture-handling app

Now that you have completed the tutorial, you have written code that allows you to create a gesture app and cascade multiple gesture sets using failure callbacks. You can continue to customize your code in your main.c further. You can click main.c to see view the complete code written in this tutorial.

#include <stdio.h>
#include <screen/screen.h>
#include "input/screen_helpers.h"
#include "gestures/types.h"
#include "gestures/set.h"
#include "gestures/event_list.h"
#include "gestures/swipe.h"
#include "gestures/pinch.h"
#include "gestures/press_and_tap.h"
#include "gestures/rotate.h"
#include "gestures/two_finger_pan.h"
#include "gestures/tap.h"
#include "gestures/double_tap.h"
#include "gestures/triple_tap.h"
#include "gestures/long_press.h"
#include "gestures/two_finger_tap.h"

/* The callback invoked when a gesture is recognized or updated. */
void gesture_callback(gesture_base_t* gesture, mtouch_event_t* event, void* param, int async)
{
    if (async) {
        printf("[async] ");
    }
    switch (gesture->type) {
        case GESTURE_TWO_FINGER_PAN: {
            gesture_tfpan_t* tfpan = (gesture_tfpan_t*)gesture;
            printf("Two-finger pan: %d, %d", tfpan->centroid.x, tfpan->centroid.y);
            break;
        }
        case GESTURE_ROTATE: {
            gesture_rotate_t* rotate = (gesture_rotate_t*)gesture;
            if (rotate->angle != rotate->last_angle) {
                printf("Rotate: %d degs", rotate->angle - rotate->last_angle);
            } else {
                return;
            }
            break;
        }
        case GESTURE_SWIPE: {
            gesture_swipe_t* swipe = (gesture_swipe_t*)gesture;
            printf("Swipe ");
            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;
            printf("Pinch %d, %d", (pinch->last_distance.x - pinch->distance.x),
                                   (pinch->last_distance.y - pinch->distance.y));
            break;
        }
        case GESTURE_TAP:
            printf("Tap");
            break;
        case GESTURE_DOUBLE_TAP:
            printf("Double tap");
            break;
        case GESTURE_TRIPLE_TAP:
            printf("Triple tap");
            break;
        case GESTURE_PRESS_AND_TAP:
            printf("Press and tap");
            break;
        case GESTURE_TWO_FINGER_TAP:
            printf("Two-finger tap");
            break;
        case GESTURE_LONG_PRESS:
            printf("Long press");
            break;
        case GESTURE_USER:
            printf("User");
            break;
        default:
            printf("Unknown");
            break;
    }
    printf("\n");
}

/**
 * The set failure callback that's invoked when all gestures in the first set have
 * transitioned to the failed state.
 */

void fail_callback(struct gestures_set* set, struct event_list* list, int async)
{
    if (set == first_set) {
        /* Sample list copy - not 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 the gesture sets */
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();
    if (NULL != first_set) {
        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();

        if (NULL != second_set) {
            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);
        } else {
            gestures_set_free(first_set);
            first_set = NULL;
        }
    } else {
        fprintf(stderr, "Failed to allocate gesture sets\n");
    }
}

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;
    }
}

int main(int argc, const char* argv[])
{
    int screen_val;
    screen_event_t screen_ev;
    int rc;
    ...
    mtouch_event_t mtouch_event;
    init_gestures();
    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;
            }
        }
    }
    gestures_cleanup();
    return 0;
}

Last modified: 2014-05-14



Got questions about leaving a comment? Get answers from our Disqus FAQ.

comments powered by Disqus