Detectando e rastreando vários pontos de toque

As etapas a seguir explicam como acompanhar vários pontos de toque usando o Windows Touch.

  1. Crie um aplicativo e habilite o Windows Touch.
  2. Adicione um manipulador para WM_TOUCH e rastrear pontos.
  3. Desenhe os pontos.

Depois que o aplicativo estiver em execução, ele renderizará círculos em cada toque. A captura de tela a seguir mostra a aparência do aplicativo durante a execução.

captura de tela mostrando um aplicativo que renderiza pontos de toque como círculos verdes e amarelos

Criar um aplicativo e habilitar o Windows Touch

Comece com um aplicativo Microsoft Win32 usando o assistente do Microsoft Visual Studio. Depois de concluir o assistente, adicione suporte para mensagens do Windows Touch definindo a versão do Windows em targetver.h e incluindo windows.h e windowsx.h em seu aplicativo. O código a seguir mostra como definir a versão do Windows em targetver.h.

#ifndef WINVER                  // Specifies that the minimum required platform is Windows 7.
#define WINVER 0x0601           // Change this to the appropriate value to target other versions of Windows.
#endif

#ifndef _WIN32_WINNT            // Specifies that the minimum required platform is Windows 7.
#define _WIN32_WINNT 0x0601     // Change this to the appropriate value to target other versions of Windows.
#endif     

#ifndef _WIN32_WINDOWS          // Specifies that the minimum required platform is Windows 98.
#define _WIN32_WINDOWS 0x0410 // Change this to the appropriate value to target Windows Me or later.
#endif

#ifndef _WIN32_IE                       // Specifies that the minimum required platform is Internet Explorer 7.0.
#define _WIN32_IE 0x0700        // Change this to the appropriate value to target other versions of IE.
#endif

O código a seguir mostra como suas diretivas de inclusão devem ser adicionadas. Além disso, você pode criar algumas variáveis globais que serão usadas posteriormente.

#include <windows.h>    // included for Windows Touch
#include <windowsx.h>   // included for point conversion

#define MAXPOINTS 10

// You will use this array to track touch points
int points[MAXPOINTS][2];

// You will use this array to switch the color / track ids
int idLookup[MAXPOINTS];


// You can make the touch points larger
// by changing this radius value
static int radius      = 50;

// There should be at least as many colors
// as there can be touch points so that you
// can have different colors for each point
COLORREF colors[] = { RGB(153,255,51), 
                      RGB(153,0,0), 
                      RGB(0,153,0), 
                      RGB(255,255,0), 
                      RGB(255,51,204), 
                      RGB(0,0,0),
                      RGB(0,153,0), 
                      RGB(153, 255, 255), 
                      RGB(153,153,255), 
                      RGB(0,51,153)
                    };

Adicionar manipulador para WM_TOUCH e rastrear pontos

Primeiro, declare algumas variáveis que são usadas pelo manipulador de WM_TOUCH no WndProc.

int wmId, wmEvent, i, x, y;

UINT cInputs;
PTOUCHINPUT pInputs;
POINT ptInput;   

Agora, inicialize as variáveis usadas para armazenar pontos de toque e registre a janela para entrada por toque do método InitInstance .

BOOL InitInstance(HINSTANCE hInstance, int nCmdShow)
{
   HWND hWnd;

   hInst = hInstance; // Store instance handle in our global variable

   hWnd = CreateWindow(szWindowClass, szTitle, WS_OVERLAPPEDWINDOW,
      CW_USEDEFAULT, 0, CW_USEDEFAULT, 0, NULL, NULL, hInstance, NULL);

   if (!hWnd) {
      return FALSE;
   }

   // register the window for touch instead of gestures
   RegisterTouchWindow(hWnd, 0);  

   // the following code initializes the points
   for (int i=0; i< MAXPOINTS; i++){
     points[i][0] = -1;
     points[i][1] = -1;
     idLookup[i]  = -1;
   }  

   ShowWindow(hWnd, nCmdShow);
   UpdateWindow(hWnd);

   return TRUE;
}

Em seguida, manipule a mensagem WM_TOUCH do método WndProc . O código a seguir mostra uma implementação do manipulador para WM_TOUCH.

case WM_TOUCH:        
  cInputs = LOWORD(wParam);
  pInputs = new TOUCHINPUT[cInputs];
  if (pInputs){
    if (GetTouchInputInfo((HTOUCHINPUT)lParam, cInputs, pInputs, sizeof(TOUCHINPUT))){
      for (int i=0; i < static_cast<INT>(cInputs); i++){
        TOUCHINPUT ti = pInputs[i];
        index = GetContactIndex(ti.dwID);
        if (ti.dwID != 0 && index < MAXPOINTS){                            
          // Do something with your touch input handle
          ptInput.x = TOUCH_COORD_TO_PIXEL(ti.x);
          ptInput.y = TOUCH_COORD_TO_PIXEL(ti.y);
          ScreenToClient(hWnd, &ptInput);
          
          if (ti.dwFlags & TOUCHEVENTF_UP){                      
            points[index][0] = -1;
            points[index][1] = -1;                
          }else{
            points[index][0] = ptInput.x;
            points[index][1] = ptInput.y;                
          }
        }
      }

      InvalidateRect(hWnd, NULL, FALSE);
    }
    // If you handled the message and don't want anything else done with it, you can close it
    CloseTouchInputHandle((HTOUCHINPUT)lParam);
    delete [] pInputs;
  }else{
    // Handle the error here 
  }  

Observação

Para usar a função ScreenToClient , você deve ter alto suporte a DPI em seu aplicativo. Para obter mais informações sobre como dar suporte a DPI alto, consulte DPI alto.

Agora, quando um usuário toca na tela, as posições que ele está tocando serão armazenadas na matriz de pontos. O membro dwID da estrutura TOUCHINPUT armazena um identificador que dependerá de hardware.

Para resolver o problema de o membro dwID depender do hardware, o manipulador de maiúsculas e minúsculas WM_TOUCH usa uma função , GetContactIndex, que mapeia o membro dwID da estrutura TOUCHINPUT para um ponto desenhado na tela. O código a seguir mostra uma implementação dessa função.

// This function is used to return an index given an ID
int GetContactIndex(int dwID){
  for (int i=0; i < MAXPOINTS; i++){
    if (idLookup[i] == -1){
      idLookup[i] = dwID;
      return i;
    }else{
      if (idLookup[i] == dwID){
        return i;
      }
    }
  }
  // Out of contacts
  return -1;
}

Importante

Windows 11 e mais recentes

Algumas interações por toque de três e quatro dedos não funcionarão mais em aplicativos do Windows por padrão.

Por padrão, as interações por toque de três e quatro dedos agora são consumidas pelo sistema para operações como alternar ou minimizar janelas e alterar áreas de trabalho virtuais. Como essas interações agora são tratadas no nível do sistema, a funcionalidade do aplicativo pode ser afetada por essa alteração.

Para dar suporte a interações de três ou quatro dedos em um aplicativo, foi introduzida uma nova configuração de usuário que especifica se o sistema lida ou não com essas interações:

Dispositivos Bluetooth & Tocam > "Gestos > de toque de três e quatro dedos"

Quando definido como "Ativado" (padrão), o sistema manipulará todas as interações de três e quatro dedos (os aplicativos não poderão dar suporte a elas).

Quando definido como "Desativado", as interações de três e quatro dedos podem ser suportadas por aplicativos (eles não serão manipulados pelo sistema).

Se o aplicativo precisar dar suporte a essas interações, recomendamos que você informe os usuários sobre essa configuração e forneça um link que inicie o aplicativo Configurações para a página relevante (ms-settings:devices-touch). Para obter mais detalhes, consulte Método Launcher.LaunchUriAsync.

Desenhar os pontos

Declare as variáveis a seguir para a rotina de desenho.

    // For double buffering
    static HDC memDC       = 0;
    static HBITMAP hMemBmp = 0;
    HBITMAP hOldBmp        = 0;
   
    // For drawing / fills
    PAINTSTRUCT ps;
    HDC hdc;
    HBRUSH hBrush;
    
    // For tracking dwId to points
    int index;

O memDC de contexto de exibição de memória é usado para armazenar um contexto gráfico temporário que é trocado com o contexto de exibição renderizado, hdc, para eliminar a cintilação. Implemente a rotina de desenho, que usa os pontos que você armazenou e desenha um círculo nos pontos. O código a seguir mostra como você pode implementar o manipulador de WM_PAINT .

  case WM_PAINT:
    hdc = BeginPaint(hWnd, &ps);    
    RECT client;
    GetClientRect(hWnd, &client);        
  
    // start double buffering
    if (!memDC){
      memDC = CreateCompatibleDC(hdc);
    }
    hMemBmp = CreateCompatibleBitmap(hdc, client.right, client.bottom);
    hOldBmp = (HBITMAP)SelectObject(memDC, hMemBmp);          

    hBrush = CreateSolidBrush(RGB(255, 255, 255));
    FillRect(memDC, &client, hBrush);
    DeleteObject(hBrush);

    //Draw Touched Points                
    for (i=0; i < MAXPOINTS; i++){
      hBrush = CreateSolidBrush(colors[i]);        
      SelectObject( memDC, hBrush);           

      x = points[i][0];
      y = points[i][1];
      if  (x >0 && y>0){              
        Ellipse(memDC, x - radius, y - radius, x+ radius, y + radius);
      }

      DeleteObject(hBrush);
    }
  
    BitBlt(hdc, 0,0, client.right, client.bottom, memDC, 0,0, SRCCOPY);      
    EndPaint(hWnd, &ps);

    SelectObject(memDC, hOldBmp);
    DeleteObject(hMemBmp);

    break;

Quando você executa seu aplicativo, ele agora deve ser semelhante à ilustração no início desta seção.

Por diversão, você pode desenhar algumas linhas extras ao redor dos pontos de toque. A captura de tela a seguir mostra como o aplicativo pode ficar com algumas linhas extras desenhadas ao redor dos círculos.

captura de tela mostrando um aplicativo que renderiza pontos de toque como círculos com linhas pelos centros e cruzando as bordas dos pontos de toque

Entrada do Windows Touch