Tutorial: Create the Gesture application

In this tutorial, we'll explore the Gesture sample application that is included with the BlackBerry 10 Native SDK. This application demonstrates how to process gestures on the screen, such as:
  • tap
  • double tap
  • pan
  • pinch

Device image showing the Gesture sample app.

You will learn to:

  • Configure your project
  • Create a gesture callback function
  • Register the gesture callback function
  • Detect gestures
  • Clean up gestures

Before you begin

You should have the following things ready:
  • The BlackBerry 10 Native SDK
  • Your BlackBerry 10 device
  • A basic understanding of the C language and some experience running applications with the Native SDK

Note that the Gesture sample application does not work properly on the simulator. You can still code, build, and deploy but the gesture behavior will not work.

Configure your project

This tutorial walks through the Gestures sample application available from the NDK-Samples repository in GitHub.

To import the complete project:
  1. From the NDK-Samples repository in GitHub, download and extract the sample application.
  2. Launch the Native SDK.
  3. On the File menu, click Import.
  4. Expand General, then select Existing Projects into Workspace. Click Next.
  5. Browse to the location where you extracted the sample app and click OK.
  6. Click Finish to import the project into your workspace

Configure your linker settings for the libraries

The sample application needs the appropriate libraries defined for it to work. To verify that they are defined:

  1. In the Project Explorer view, right-click the Gesture project and click Properties.
  2. In the left-hand navigation pane, click C/C++ Build > Settings.
  3. In the Tool Settings tab, click QCC Linker > Libraries.
  4. In the Libraries section, verify that the following entries are correct and add any that are missing:
    • bps
    • screen
    • m
    • img
    • gestures

Using the application

This sample application allows users to interact with an image displayed on the screen in the following ways:

Action Result

Two-finger pan

Adjusts the viewport position in the direction of the pan and prints the coordinates to stderr

Pinch the screen

Adjusts the viewport size in the direction of the pinch and prints the new size to stderr

Tap the screen

Prints the tap coordinates to stderr

Double-tap the screen

Prints the double-tap coordinates to stderr

When a user interacts with the screen, the application should capture the gesture and handle it accordingly. To use the gestures library, you define gesture sets that contain movements, such as pinch, tap, and two-finger pan. The gesture sets receive mtouch events when one or more finger movements are detected on the screen.

Create and register the callback function

When a gesture set receives an event, it invokes a user-defined callback function, where the application-specific behavior is defined. For this to work, the application must register a callback function with the gestures library for every gesture set to be handled.

Create the callback function

The gesture sample application includes a gesture_callback() function that defines what the application does on a swipe or pinch gesture. This function must have the signature:
void(* gesture_callback_f)(struct gesture_base *gesture, mtouch_event_t *event, void *param, int async);
When the gestures library invokes this function, gesture will contain information about the gesture, event will contain information about the touch event that caused the gesture, and async will identify whether the callback was invoked from an event (0) or from a timer callback (1).
Within this function, a switch statement defines the choice of actions the application takes based on the gesture received:
switch (gesture->type) {
   case GESTURE_TWO_FINGER_PAN: {
      ...
   }
   case GESTURE_PINCH: {
      ...
   }
For a two-finger pan, the application copies information from the incoming gesture to a local gesture_tfpan_t structure and uses the information to adjust the position of the viewport on the display. In this case, the viewport is adjusted to follow the direction of the pan by determining the distance of the swipe and adding it to the current x and y position of the viewport.
gesture_tfpan_t* tfpan = (gesture_tfpan_t*)gesture;

fprintf(stderr,"Two finger pan: %d, %d", 
      (tfpan->last_centroid.x - tfpan->centroid.x), 
      (tfpan->last_centroid.y - tfpan->centroid.y));
if (tfpan->last_centroid.x && tfpan->last_centroid.y) {
   viewport_pos[0] = (tfpan->last_centroid.x - tfpan->centroid.x) >> 1;
   viewport_pos[1] = (tfpan->last_centroid.y - tfpan->centroid.y) >> 1;
}
Here, the centroid structure member contains the coordinates of the midpoint between the final two touches and last_centroid contains the coordinates of the midpoint between the previous two touches.

Illustration showing the two-finger pan gesture.

The viewport position updates on the screen for every frame within the handle_events() call of the application loop in main() and takes into account this new position.
while (!shutdown) {
   /* Handle user input */
   handle_events();
}
handle_events() {
   ...
   /* Re-draw the screen after a screen event */
   screen_set_window_property_iv(screen_win, 
         SCREEN_PROPERTY_SOURCE_POSITION, viewport_pos);
   screen_set_window_property_iv(screen_win, 
         SCREEN_PROPERTY_SOURCE_SIZE, viewport_size);
   screen_flush_context(screen_ctx,0);
}
For a pinch, in the gestures callback function, the application copies the gesture information into a local gesture_pinch_t structure and uses it to adjust the size of the viewport. This is done by first determining the change in distance between the two fingers at the start of the pinch and at the end of the pinch:
gesture_pinch_t* pinch = (gesture_pinch_t*)gesture;

fprintf(stderr,"Pinch %d, %d", 
      (pinch->last_distance.x - pinch->distance.x), 
      (pinch->last_distance.y - pinch->distance.y));

int dist_x = pinch->distance.x;
int dist_y = pinch->distance.y;
int last_dist_x = pinch->last_distance.x;
int last_dist_y = pinch->last_distance.y;
Here, the distance structure member is the distance between the current location of the fingers and last_distance is the distance between the previous location.

Illustration showing the pinch gesture.

The application calculates the relative distance between the locations to determine the net increase or decrease of the viewport's size. If either of the relative distance values are greater than zero, the application adjusts the viewport size:
int reldist = sqrt((dist_x)*(dist_x) + (dist_y)*(dist_y));
int last_reldist = sqrt((last_dist_x)*(last_dist_x) + 
      (last_dist_y)*(last_dist_y));

if (reldist && last_reldist) {
   viewport_size[0] += last_reldist - reldist >> 1;
   viewport_size[1] += last_reldist - reldist >> 1;
If the new viewport size does not fit within the screen's limits, the viewport is adjusted:
if (viewport_size[0] < MIN_VIEWPORT_SIZE) {
   viewport_size[0] = MIN_VIEWPORT_SIZE;
} else if (viewport_size[0] > MAX_VIEWPORT_SIZE) {
   viewport_size[0] = MAX_VIEWPORT_SIZE;
}
if (viewport_size[1] < MIN_VIEWPORT_SIZE) {
   viewport_size[1] = MIN_VIEWPORT_SIZE;
} else if (viewport_size[1] > MAX_VIEWPORT_SIZE) {
   viewport_size[1] = MAX_VIEWPORT_SIZE;
}
A final check zooms the viewport position to the center of the image if any of the viewport size parameters are within the limits:
if (viewport_size[0] > MIN_VIEWPORT_SIZE 
      && viewport_size[1] > MIN_VIEWPORT_SIZE 
      && viewport_size[0] < MAX_VIEWPORT_SIZE 
      && viewport_size[1] < MAX_VIEWPORT_SIZE) {
   viewport_pos[0] -= (last_reldist - reldist) >> 2;
   viewport_pos[1] -= (last_reldist - reldist) >> 2;
For taps and double-taps, the application simply prints out the coordinates of the tap location:
case GESTURE_TAP: {
   gesture_tap_t* tap = (gesture_tap_t*)gesture;
   printf(stderr,"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;
    fprintf(stderr,"Double tap first_x:%d first_y:%d second_x:%d
                    second_y:%d", d_tap->first_touch.x,
                    d_tap->first_touch.y, d_tap->second_touch.x,
                    d_tap->second_touch.y);
    break;
}

Register the callback function

The application must register the callback function with the gestures library for it to be invoked when a gesture occurs. The init_gestures() function in the sample application contains this code. This function allocates a gesture set for the application, using the set structure.:
struct gestures_set * set;

set = gestures_set_alloc();
Next, the application allocates and initializes the handling of the different types of gestures, passing in the callback function to register.
if (NULL != set) {
  tap = tap_gesture_alloc(NULL, gesture_callback, set);
  double_tap = double_tap_gesture_alloc(NULL, gesture_callback, set);
  tfpan_gesture_alloc(NULL, gesture_callback, set);
  pinch_gesture_alloc(NULL, gesture_callback, set);
} else {
  fprintf(stderr, "Failed to allocate gestures set\n");
}

Detect gestures and clean up

Detect gestures

The application now needs a way of triggering the gesture callback function when a touch event occurs on the screen. The application must call gestures_set_process_event() when a touch event is detected and needs to be handled. In the sample application, this is called from handle_screen_event() which itself is called from handle_events() when a screen event is detected in the main application loop.
handle_screen_event(bps_event_t *event)
{
    int screen_val, rc;

    screen_event_t screen_event = screen_event_get_event(event);
    mtouch_event_t mtouch_event;
    rc = screen_get_event_property_iv(screen_event, 
            SCREEN_PROPERTY_TYPE, &screen_val);
    if(screen_val == SCREEN_EVENT_MTOUCH_TOUCH 
            || screen_val == SCREEN_EVENT_MTOUCH_MOVE 
            || screen_val == SCREEN_EVENT_MTOUCH_RELEASE) {
        rc = screen_get_mtouch_event(screen_event, &mtouch_event, 0);
        if (rc) {
            fprintf(stderr, "Error: failed to get mtouch event\n");
        }
        rc = gestures_set_process_event(set, &mtouch_event, NULL);
In this function, the determination of a gesture event is based on whether the incoming event is a touch, move, or release event. If it is one of these events, the application calls gestures_set_process_event().
If the call to gestures_set_process_event() does not trigger any callbacks, the function returns 0. This case occurs when a touch, move, or release event was detected but there was no associated gesture. The following code treats this case as a pan and adjusts the viewport position accordingly:
if (!rc) {
   if (mtouch_event.contact_id == 0) {
      if(last_touch[0] && last_touch[1]) {
         fprintf(stderr,"Pan %d %d\n",
               (last_touch[0] - mtouch_event.x),
               (last_touch[1] - mtouch_event.y));
         viewport_pos[0] = (last_touch[0] - mtouch_event.x) >> 1;
         viewport_pos[1] = (last_touch[1] - mtouch_event.y) >> 1;
      }
      last_touch[0] = mtouch_event.x;
      last_touch[1] = mtouch_event.y;
   }
}

Clean up gestures

Now that the application can perform actions based on gestures, at some point it needs to clean itself up before closing. In other words, it's always a good idea to clean up any memory associated with gestures data structures. In gestures_cleanup(), the following lines of code free the memory associated with the gesture set:
if (NULL != set) {
        gestures_set_free(set);
        set = NULL;
}

That's it! The application can now set up the gestures library, process gesture events, and clean up after itself.

Last modified: 2014-06-24



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

comments powered by Disqus