Multitouch Part 2: Support for Gestures in Windows 7

As mentioned in yesterday’s multitouch post, there are a number of gestures that are recognized by Windows 7 out of the box:

  • Pan (also called Translate) – put a finger or fingers down and drag
  • Rotate – touch with two fingers at opposite ends and turn fingers in a circle
  • Zoom – touch with two fingers at opposite ends and move the fingers closer or further apart
  • Tap – touching and quickly lifting with a finger; equivalent to a click
  • Double-tap – quickly tapping twice; equivalent to a double-click
  • Press and tap (also called Finger Roll) – place one finger on the screen, place second finger on the screen, lift the second finger immediately, and then lift the first finger.  This is essentially holding one finger down while tapping with a second finger.  This gesture, by default, is equivalent to a right-click. 

Now, let’s look at how to code for gestures. 

How can I tell if the machine supports multitouch? 

First, it would be good to know if the machine on which your application is running supports multitouch, so if it doesn’t, you can degrade gracefully. 

 #include <windows.h>

// Test for touch
bool bMultiTouch = false;
int value = GetSystemMetrics(SM_DIGITIZER);
if (value & 0x40)
{
bMultiTouch = true; /* digitizer is multitouch */
}

There is a method called GetSystemMetrics that retrieves system configuration settings.  If you pass in SM_DIGITIZER, it will return a bit field with the following settings:

Value Hex value Meaning
TABLET_CONFIG_NONE 0x00 The input digitizer does not have touch capabilities.

NID_INTEGRATED_TOUCH

0x01

The device has an integrated touch digitizer.

NID_EXTERNAL_TOUCH

0x02

The device has an external touch digitizer.

NID_INTEGRATED_PEN

0x04

The device has an integrated pen digitizer.

NID_EXTERNAL_PEN

0x08

The device has an external pen digitizer.

NID_MULTI_INPUT

0x40

The device supports multiple sources of digitizer input.

NID_READY

0x80

The device is ready to receive digitizer input.

There are some limitations for GetSystemMetrics.  For example, there is no support for plug and play.  Therefore, be careful of using this function for permanent configuration.  If you add a multitouch digitizer later, this function would need to be called again to know that multitouch is supported. 

 

How can my application recognize that a gesture has occurred? 

By default, your window will receive notifications when gestures occur, in the form of WM_GESTURE messages. 

A window can receive gestures or raw touches, but not both.  If you want to work at the raw touch level as opposed to the gesture level, you can call RegisterTouchWindow.  You will then stop receiving WM_GESTURE messages and instead receive WM_TOUCH messages. 

What if I want my application to respond to only one or two of the known gestures, and ignore the rest?

By default, the application receives all gesture messages.  However, you may have an application in which the user can move a marker on a game board around on the screen, but you don’t want the marker to be resized.  In that case, you don’t care about the Zoom gesture, but you perhaps do want the Translate and Rotate gestures.  You can configure which gestures will be sent using SetGestureConfig.  It takes an array of GestureConfig structures, which each contain an ID, messages to enable (the dwWant parameter), and messages to disable (the dwBlock parameter).  This will change the gesture configuration for the lifetime of the window, not just for the next gesture. 

Here’s an example.  I create a GestureConfig which blocks nothing and wants all gestures. The GestureConfig struct is passed into the SetGestureConfig method.

 GESTURECONFIG gestureConfig;
gestureConfig.dwID = 0;
gestureConfig.dwBlock = 0;
gestureConfig.dwWant = GC_ALLGESTURES;

SetGestureConfig(hWnd, 0, 1,
            &gestureConfig, 
            sizeof(gestureConfig));

You can also dynamically change your gesture configuration.  The WM_GESTURENOTIFY message is sent to your window to indicate that a gesture is about to be sent, which gives you an opportunity to set your gesture configuration. 

OK, now I’m set up to get the gestures I want. How can my application respond to gestures?

You will receive notifications that gestures occurred as WM_GESTURE messages.  Use a switch statement to discover what gesture you received, and respond appropriately.

Information about the gesture is stored in the GESTUREINFO structure. 

 // Create a structure to populate and retrieve the extra message info.
GESTUREINFO gi;  
ZeroMemory(&gi, sizeof(GESTUREINFO));
gi.cbSize = sizeof(GESTUREINFO);
BOOL bResult  = GetGestureInfo((HGESTUREINFO)lParam, &gi);

BOOL bHandled = FALSE;

if (bResult)
{
    // now interpret the gesture
    switch (gi.dwID){
       case GID_ZOOM:
           // Code for zooming goes here     
           bHandled = TRUE;
           break;
       case GID_PAN:
           // Code for panning goes here
           bHandled = TRUE;
           break;
       case GID_ROTATE:
           // Code for rotation goes here
           bHandled = TRUE;
           break;
       case GID_TWOFINGERTAP:
           // Code for two-finger tap goes here
           bHandled = TRUE;
           break;
       case GID_PRESSANDTAP:
           // Code for roll over goes here
           bHandled = TRUE;
           break;
       default:
           // A gesture was not recognized
           break;
    }
}
else
{
    // Handle error...
}

In tomorrow’s post, we will talk about managed code support for multitouch. 

Other blog posts in this series:

Multitouch Part 1: Getting Started with Multitouch in Windows 7

Multitouch Part 2: Support for Gestures in Windows 7

Multitouch Part 3: Multitouch in managed code and WPF

Multitouch Part 4: Multitouch in Silverlight

Multitouch Part 5: User Experience with Multitouch