Setup

Before you get started, make sure that you've set up your development environment correctly, that you have a BlackBerry 10 device or simulator ready for testing, and that you've imported the Gesture sample project into your workspace. The Getting Started Guide has all the information you need to get these things configured.

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

The sample application needs the appropriate libraries defined in order 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:

ActionResult

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. Once 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);
Once 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.

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.

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_tap_t* d_tap = (gesture_tap_t*)gesture;
   fprintf(stderr,"Double tap x:%d y:%d", d_tap->touch_coords.x, 
      d_tap->touch_coords.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");
}