2009年6月24日 星期三

Windows 7 Touch Sample Application - Scratchpad

之前介紹很多關於Windows 7 Touch SDK的用法, 我們就用它來寫一個簡單的multi-touch應用程式,如下圖所示的Scratchpad.



基本上這隻Scratchpad應用程式的功能很簡單, 就是將touch points畫出來, 部分程式碼如下所示:


1.
註冊可以收到multi-touch message的視窗

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

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

// Create the application window
hWnd = CreateWindow(g_wszWindowClass, g_wszTitle, WS_OVERLAPPEDWINDOW,
CW_USEDEFAULT, 0, CW_USEDEFAULT, 0, NULL, NULL, hInstance, NULL);
if (!hWnd)
{
return FALSE;
}

// Register application window for receiving multi-touch input. Use default settings.
if(!RegisterTouchWindow(hWnd, 0))
{
MessageBox(hWnd, L"Cannot register application window for multi-touch input", L"Error", MB_OK);
return FALSE;
}
ASSERT(IsTouchWindow(hWnd, NULL));

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

return TRUE;
}

2. 處理Multi-Touch Message

WndProc的函式中, 我們處理收到的WM_TOUCH訊息, 如下所示:

// WM_TOUCH message handlers
case WM_TOUCH:
{
// WM_TOUCH message can contain several messages from different contacts packed together.
// Message parameters need to be decoded:
unsigned int numInputs = (unsigned int) wParam; //將wParam轉換成touch contact的數目
TOUCHINPUT* ti = new TOUCHINPUT[numInputs]; // 產生一組大小為numInputs的TOUCHINPUT structure

if(ti == NULL)
{
break;
}

// 利用GetTouchInputInfo函式取得
TOUCHINPUT structures的相關資訊
if(GetTouchInputInfo((HTOUCHINPUT)lParam, numInputs, ti, sizeof(TOUCHINPUT)))
{

// 根據不同的touch state分別呼叫TouchDown, TouchMove和TouchUp等函式, 而每個函式implement的方法, 會在下面的文字中介紹.

for(unsigned int i=0; i <>numInputs; ++i)
{
if(ti[i].dwFlags & TOUCHEVENTF_DOWN)
{
OnTouchDownHandler(hWnd, ti[i]);
}
else if(ti[i].dwFlags & TOUCHEVENTF_MOVE)
{
OnTouchMoveHandler(hWnd, ti[i]);
}
else if(ti[i].dwFlags & TOUCHEVENTF_UP)
{
OnTouchUpHandler(hWnd, ti[i]);
}
}
}
CloseTouchInputHandle((HTOUCHINPUT)lParam);
delete [] ti;
}
break;


//和處理mouse message的方法類似, 我們可以將touch state作以下處理:

// Handler for touch-down message.
// Starts a new stroke and assigns a color to it.
// hWnd window handle
// ti TOUCHINPUT structure (info about contact)
void OnTouchDownHandler(HWND hWnd, const TOUCHINPUT& ti)
{
// Extract contact info: point of contact and ID
POINT pt = GetTouchPoint(hWnd, ti);
int iCursorId = GetTouchContactID(ti);

// We have just started a new stroke, which must have an ID value unique
// among all the strokes currently being drawn. Check if there is a stroke
// with the same ID in the collection of the strokes in drawing.
ASSERT(g_StrkColDrawing.FindStrokeById(iCursorId) == -1);

// Create new stroke, add point and assign a color to it.
CStroke* pStrkNew = new CStroke;
pStrkNew->Add(pt);
pStrkNew->SetColor(GetTouchColor((ti.dwFlags & TOUCHEVENTF_PRIMARY) != 0));
pStrkNew->SetId(iCursorId);

// Add new stroke to the collection of strokes in drawing.
g_StrkColDrawing.Add(pStrkNew);
}

// Handler for touch-move message.
// Adds a point to the stroke in drawing and draws new stroke segment
// hWnd window handle
// ti TOUCHINPUT structure (info about contact)
void OnTouchMoveHandler(HWND hWnd, const TOUCHINPUT& ti)
{
// Extract contact info: contact ID
int iCursorId = GetTouchContactID(ti);

// Find the stroke in the collection of the strokes in drawing.
int iStrk = g_StrkColDrawing.FindStrokeById(iCursorId);

POINT pt;
pt = GetTouchPoint(hWnd, ti);

// Add contact point to the stroke
g_StrkColDrawing[iStrk]->Add(pt);

// Partial redraw: only the last line segment
HDC hDC = GetDC(hWnd);
g_StrkColDrawing[iStrk]->DrawLast(hDC);
ReleaseDC(hWnd, hDC);
}

// Handler for touch-up message.
// Finishes the stroke and moves it to the collection of finished strokes.:
// hWnd window handle
// ti TOUCHINPUT structure (info about contact)
void OnTouchUpHandler(HWND hWnd, const TOUCHINPUT& ti)
{
// Extract contact info: contact ID
int iCursorId = GetTouchContactID(ti);

// Find the stroke in the collection of the strokes in drawing.
int iStrk = g_StrkColDrawing.FindStrokeById(iCursorId);

g_StrkColFinished.Add(g_StrkColDrawing[iStrk]);

// Remove this stroke from the collection of strokes in drawing.
g_StrkColDrawing.Remove(iStrk);

// Request full redraw.
InvalidateRect(hWnd, NULL, FALSE);
}


3. 視窗重繪時呼叫繪圖功能
每當程式接收到WM_PAINT指令時,就會把整個視窗或部分視窗重繪, 我們可以在處理WM_PAINT訊息時加入以下繪圖指令, 避免視窗放大縮小時圖案被清除.

case WM_PAINT:
hdc = BeginPaint(hWnd, &ps);
// Full redraw: draw complete collection of finished strokes and
// also all the strokes that are currently in drawing.
g_StrkColFinished.Draw(hdc);
g_StrkColDrawing.Draw(hdc);
EndPaint(hWnd, &ps);
break;

4. 清除資料
在Windows destory時, 我們必需unregister multi-touch
windows, 和清除之前new出來的物件, 以免造成memory leak的問題, 如下所示:

case WM_DESTROY:
// Clean up of application data: unregister window for multi-touch.
if(!UnregisterTouchWindow(hWnd))
{
MessageBox(NULL, L"Cannot unregister application window for touch input", L"Error", MB_OK);
}
ASSERT(!IsTouchWindow(hWnd, NULL));
// Destroy all the strokes
{
int i;
for(i=0; i < > g_StrkColDrawing.Count(); ++i)
{
delete g_StrkColDrawing[i];
}
for(i=0; i <> g_StrkColFinished.Count(); ++i)
{
delete g_StrkColFinished[i];
}
}
PostQuitMessage(0);
break;

沒有留言:

張貼留言