[MFC/윈도우 7 멀티터치] #5 : WM_TOUCH 메세지를 이용한 구현(下)

MFC 2010. 7. 13. 09:00 Posted by 알 수 없는 사용자

Intro

안녕하세요 ~ MFC 카테고리의 꽃집총각 입니다.

지난 포스팅에서는 드디어 WM_TOUCH를 이용한 멀티터치 UX의 구현방법에 대한 이야기가 시작되었습니다. channel9에 공개된 예제를 함께 작성해 보면서 WM_TOUCH 메세지를 다루는 방법을 알아보고 있는데요, 어떨 때 WM_TOUCH를 이용해야 하는지, WM_GESTURE가 아닌 WM_TOUCH를 받고 싶을 땐 어떻게 처리해 주어야 하는지, 그리고 메세지를 받을 때마다 호출되는 CWnd::OnTouchInput 등에 대한 설명이 있었습니다.

그리고 실제로 OnTouchInput() 함수에서 터치 메세지를 제어할 예제 프로그램인 ‘TouchPad’ 프로젝트를 생성하고 기본적인 전처리 단계를 알아보았지요. 이에 이어서 오늘은 OnTouchInput() 함수의 구현부터 본격적으로 알아보도록 하겠습니다 ^^

 

Task 3 : 드로잉 코드는 가져다 씁시다. 터치에 집중해야죠 ㅎㅎ

지금 함께 작성해 보려고 하는 TouchPad는 윈도우 그림판처럼 멀티 터치 입력에 대해 라인을 그려주는 예제입니다. 한 발 더 나가서, 서로 다른 입력으로 드로잉 하는 라인은 색상도 다르게 표시되도록 할거예요. 하지만 사용자로부터 입력된 터치 정보의 처리방법에 집중하기 위해, 드로잉 코드는 샘플에서 제공된 클래스를 사용하도록 하겠습니다. 먼저 예제 프로젝트에 아래의 파일을 추가해 주세요.

 이 클래스들을 이용해 우리가 구현할 코드에 대해 간략히 설명해 보기로 하지요. 우리는 각각의 터치 입력에 대해서 라인(stroke. 스트로크라고 부르겠습니다.)을 그려내는 기능을 추가할 것인데, 이를 이해서 두 개의 스트로크 모음을 유지할겁니다. 한 쪽에서는 이미 드로잉이 끝난 스트로크 데이터를 모아 유지하게 되고, 또 다른 나머지 한 쪽에서는 현재 터치입력으로 드로잉하고 있는 데이터를 유지하게 됩니다. 스크린을 터치하는 한 개 혹은 다수의 입력을 받아 m_StrkColDrawing 멤버변수가 유지하는 스트로크 정보에 CPoint 형식의 좌표 데이터를 추가합니다. 그리고 스크린에서 손가락을 떼면, 손가락을 따라 그려지고 있던 스트로크 정보를 m_StrkColDrawing 변수에서 m_StrkColFinished로 이동시킵니다. 이 때 동시에 다수의 터치 입력이 동시에 일어나면 이를 구분하기 위해 서로가 다른 색상을 가진 스트로크를 출력하게 할겁니다.

 그럼 소스코드 상에서의 수정사항을 알아보기로 하지요. 먼저 위에 첨부되어있는 파일들을 다운받아서 프로젝트에 넣은 후, StdAfx.h 헤더파일에 새로 추가된 헤더들을 인클루드 해주세요.

// stdafx.h 파일에 추가해 주세요.
#include "Stroke.h"  
#include "StrokeCollection.h" 

그리고 ChildView.h 파일로 가서 CChildView 클래스에 private 멤버 변수로 아래의 세 변수를 추가해 줍니다.

private:
    int m_iCurrColor;                    // The current stroke color
    CStrokeCollection m_StrkColFinished; // The user finished entering strokes
                                         // after user lifted his or her finger.
    CStrokeCollection m_StrkColDrawing;  // The Strokes collection the user is
                                         // currently drawing.

이 중에서 m_iCurrColor는 드로잉 되는 스트로크의 색상을 결정하기 위한 인덱스로 쓰일 겁니다. 생성자에서 0으로 초기화 해주세요.

CChildView::CChildView() : m_iCurrColor(0)
{
}

드로잉이 다 끝나서 m_StrkColFinished 변수에 담겨 있을 스트로크 정보들을 화면에 출력해 주기 위해 CChileView::OnPaint() 함수에 아래의 코드를 추가해 줍니다.

// 드로잉이 완료된 스트로크 정보를 출력한다.
m_StrkColFinished.Draw(&dc);

 

Task 4 : 터치 데이터를 다뤄보자 (드디어!)

이제 드로잉 코드의 기본적인 설정들은 끝을 냈고, 지금부터 우리는 WM_TOUCH 메세지에 대해 아래의 세 가지 경우에 따른 코딩을 추가해 넣을 것입니다.

  1. 사용자가 스크린에 터치를 시작한 시점의 처리. – Touch Input Down
  2. 터치한 손가락을 스크린 상에서 움직이는 동안의 처리. – Touch Input Move
  3. 스크린에서 이동하던 손가락을 떼어 터치 입력이 끝나는 시점의 처리. – Touch Input Up

각각의 경우를 처리하기 위한 코드를 클래스에 추가해 봅시다. ChildView.h 헤더파일에서 위의 세가지 경우에 호출할 CChildView 클래스의 멤버함수를 선언해 줍니다.

protected:
    // Handlers for different touch input events
    BOOL OnTouchInputDown(CPoint pt, PTOUCHINPUT pInput);
    BOOL OnTouchInputMove(CPoint pt, PTOUCHINPUT pInput);
    BOOL OnTouchInputUp(CPoint pt, PTOUCHINPUT pInput);

그리고 ChildView.cpp 파일에 아래의 구현을 넣어주세요.

// OnTouchInputDown - 멀티터치 입력이 시작될 때 처리를 구현하는 함수.
BOOL CChildView::OnTouchInputDown(CPoint pt, PTOUCHINPUT pInput)
{
    // 새로운 스트로크 객체를 생성하고 Point 정보를 추가합니다.
    COLORREF strokeColor = GetTouchColor((pInput->dwFlags & TOUCHEVENTF_PRIMARY) != 0);
    
    CStroke* pStrkNew = new CStroke(pInput->dwID, strokeColor);
    pStrkNew->Add(pt);
    
    // 새로 생성한 스트로크 객체를 현재 드로잉 중인 스트로크 정보를 보관하는 
    // m_StrkColDrawing 변수에 추가해 줍니다.
    m_StrkColDrawing.Add(pStrkNew);
 
    return TRUE;
}
 
// OnTouchInputMove - 멀티터치 입력이 지속적으로 들어오고 있을 때의 처리를 구현하는 함수.
BOOL CChildView::OnTouchInputMove(CPoint pt, PTOUCHINPUT pInput)
{
    // 한 번에 다수의 터치 입력이 들어올 수 있다.
    // TOUCHINPUT 구조체의 dwID 변수를 이용해 
    // 현재 드로잉 하고 있는 스트로크들의 데이터 집합을 찾아낸다.
    int strokeIndex = m_StrkColDrawing.FindStrokeById(pInput->dwID);
 
    if (strokeIndex >= 0) // 해당 ID의 데이터를 찾아낸 경우.
    {
        CStroke* pStrk =  m_StrkColDrawing[strokeIndex];
    
        // 터치 입력이 들어온 좌표값 Point 정보를 스트로크에 추가해 준다.
        pStrk->Add(pt);
 
        // 스트로크를 다시 그려준다. 
        pStrk->Draw(GetDC());
    }
    
    return TRUE;
}
 
// OnTouchInputUp - 멀티터치 입력이 끝날 때의 처리를 구현하는 함수.
BOOL CChildView::OnTouchInputUp(CPoint pt, PTOUCHINPUT pInput)
{
    // 한 번에 다수의 터치 입력이 들어올 수 있다.
    // TOUCHINPUT 구조체의 dwID 변수를 이용해 
    // 현재 드로잉 하고 있는 스트로크들의 데이터 집합을 찾아낸다.
    int strokeIndex = m_StrkColDrawing.FindStrokeById(pInput->dwID);
 
    if (strokeIndex >= 0) // 해당 ID의 데이터를 찾아낸 경우.
    {
        CStroke* pStrkCopy = m_StrkColDrawing[strokeIndex];
 
        // 찾아낸 스트로크 정보를 m_StrkColDrawing 변수에서 제거하고...
        m_StrkColDrawing.RemoveAt(strokeIndex);
        
        // 제거한 CStroke 클래스의 포인터를 m_StrkColFinished 변수에 추가해준다.
        m_StrkColFinished.Add(pStrkCopy);
    }
    
    return TRUE;
}
 

 각각의 함수 본문에는 터치가 발생한 시점에 걸맞는 드로잉 코드들이 구현되어 있습니다. 샘플 원본에는 영어로 달려있던 주석을 제가 한글로 바꾸고 추가 설명도 좀 덧붙여 두었습니다. 아직까지는 별다른 설명 없이 코드만 보더라도 어렵지 않게 이해하실 수 있을 거예요.

 이 함수들의 본문보다 더 중요한 부분은, 이 함수들을 '언제 호출해 주어야 하느냐' 하는 점입니다. 멀티 터치 데이터의 입력 시점에 따른 상태를 확인하기 위해 주의깊게 확인해야 할 부분은 지난 포스팅에서 선언만 해두고 빈 함수 몸통만 덩그라니 남겨두었던 CChildView::OnTouchInput(...) 함수입니다. 이곳에서 현재 멀티터치 데이터를 조회해 현재 입력 시점이 어떻게 되는지, 혹은 부가적인 다른 정보들을 어떻게 얻어내는지를 알아보는 것이 이번 포스팅의 중요한 목표라고 볼 수 있죠. 아래의 코드를 유심히 봐주세요.

BOOL CChildView::OnTouchInput(CPoint pt, int nInputNumber, int nInputsCount, PTOUCHINPUT pInput)
{     
    if ((pInput->dwFlags & TOUCHEVENTF_DOWN) == TOUCHEVENTF_DOWN)
    {
        return OnTouchInputDown(pt, pInput); // 터치 시작!
    }
    else if ((pInput->dwFlags & TOUCHEVENTF_MOVE) == TOUCHEVENTF_MOVE)
    {
        return OnTouchInputMove(pt, pInput); // 터치 입력 진행중.
    }
    else if ((pInput->dwFlags & TOUCHEVENTF_UP) == TOUCHEVENTF_UP)
    {
        return OnTouchInputUp(pt, pInput);    // 터치 입력 마무리.
    }
 
    return FALSE;
}

OnTouchInput 함수의 인자로 TOUCHINPUT 구조체의 포인터가 전달되는데, 이 구조체의 멤버 변수인 dwFlag에 담겨있는 flag의 상태를 조회함으로써 현재 터치 입력에 대한 여러 가지 정보들을 알아낼 수 있습니다. 위에 적힌 코드에서도 dwFlag를 이용해 터치 데이터의 추가 정보들을 알아내고 있습니다. 위의 함수 본문을 복사해 OnTouchInput 함수의 구현을 대체해 주세요.

이 중 일련의 멀티터치 입력이 처음 시작될 때의 처리를 위한 OnTouchInputDown(…) 함수를 한 번 보기로 하죠. 여기에서도 TOUCHINPUT 구조체의 dwFlag 를 조회하는 코드를 볼 수 있습니다. 바로 TOUCHEVENTF_PRIMARY 플래그가 켜져 있는지를 확인해 보는 코드네요. 입력중인 터치 정보가 여러 개인 경우, 이 중 가장 먼저 시작된 (PRIMARY) 터치 정보 인지를 확인하는 부분입니다. PRIMARY 터치는 스트로크의 색상을 검정으로 유지하고, 그렇지 않은 경우는 미리 정의된 몇 가지 색상 정보를 rotation해가며 결정해 주기 위해서 PRIMARY 데이터 여부를 확인하고 있습니다. 스트로크의 색상을 결정해주는 GetTouchColor() 함수의 선언 및 구현도 아래와 같이 추가해 주세요.

// in ChildView.h 
private:
    COLORREF GetTouchColor(bool bPrimaryContact);
COLORREF CChildView::GetTouchColor(bool bPrimaryContact)
{
    static COLORREF c_arrColor[] =  // 2번째 색상 정보를 미리 정의하는 static array.
    {
        RGB(255, 0, 0),             // Red
        RGB(0, 255, 0),             // Green
        RGB(0, 0, 255),             // Blue
        RGB(0, 255, 255),           // Cyan
        RGB(255, 0, 255),           // Magenta
        RGB(255, 255, 0)            // Yellow
    };
 
    COLORREF color;
    if (bPrimaryContact)
    {
        // primary contact 는 항상 black으로 표시한다.
        color = RGB(0,0,0);         // Black
    }
    else
    {
        // secondary 색상을 결정 한 후,
        color = c_arrColor[m_iCurrColor];
 
        // array 의 다음 색상으로 인덱스를 이동시켜 준다.
        m_iCurrColor = (m_iCurrColor + 1) % (sizeof(c_arrColor)/sizeof(c_arrColor[0]));
    }
 
    return color;
}

 이제 추가해야 할 코딩의 마지막 입니다. 드로잉을 하면서 추가해 주었던 스트로크 정보들의 해제 처리를 소멸자에 넣어주세요. 멀티터치 데이터의 조작 방식을 알아보기 위한 이번 예제는 동적 메모리 할당의 효율적인 처리 같은 관점에서는 그다지 효율적이지 못합니다. 그래서 실행해보면 다소 반응 속도가 느리다고 느껴지실 수도 있습니다만, 이번 예제가 어디까지나 MFC 프로젝트에서 WM_TOUCH 메세지의 조작 방식을 알아보기 위함임을 감안해 주세요 ㅎㅎㅎ 그래도 양심상 할당한 메모리의 해제는 해주어야겠지요 ^^;… CChildView의 소멸자에 아래 코드를 넣어주시면 이제 코드 추가는 모두 완료입니다!

CChildView::~CChildView()
{
    for (int i = 0; i < m_StrkColDrawing.GetCount(); ++i)
    {
        delete m_StrkColDrawing[i];
    }
 
    for (int i = 0; i < m_StrkColFinished.GetCount(); ++i)
    {
        delete m_StrkColFinished[i];
    }
}

 

Task 5 : 프로그램을 실행하고 직접 스크린을 ‘멀티’터치해 봅니다.

이제 빌드하고 프로그램을 실행해 봅니다. 스크린 터치를 통해서 view 영역에 동시에 서로 다른 라인이 그려지는 기능을 직접 확인해 보세요. 샘플 프로그램을 훌륭하게 서로 다른 panning 제스처를 인식해 각각의 입력에 따른 스트로크를 출력하게 됩니다.

 

  

Outro

이번 글에서는 지난번 포스팅에 이어서 WM_TOUCH 메세지를 사용해 멀티터치 UX를 구현하는 TouchPad 예제를 완성해 보았습니다. WM_TOUCH 메세지가 발생할 때마다 호출되는 CWnd::OnTouchInput() 함수에서

  1. Touch 입력의 시작, 진행 중, 종료 시점을 알아내는 방법
  2. DWORD 타입의 ID값을 이용해 서로 다른 터치 입력을 구분하는 방법
  3. 동시에 여러 개의 멀티터치가 발생할 때, 먼저 일어난(primary) 터치 데이터를 구별하는 방법

… 등을 알아보았습니다. 또한 이를 활용해 동시 다발적인 멀티터치 입력에 대해 각기 다른 panning 제스처로 인식해 궤적을 그려내는 방법을 예제 구현과 함께 알아보았습니다. 이를 통해 WM_TOUCH 메세지를 제어하는 방법을 보다 쉽게 이해할 수 있으셨을 겁니다.

Native 코드로 멀티터치 UX를 구현하는 방법 중에 아직 소개해 드리지 않은 영역이 있습니다. MFC로 제공되는 기능은 아니지만, Com-Interface 형태로 제공되는 Manipulation(MS Surface에서와 같은 위젯 처리 기법. 한번에 여러 개의 제스처를 동시 적용한다.), Inertia(관성)의 제어기법 등이 그것입니다. 다음 포스팅에서는 이러한 부분에 대해서 알아보도록 하겠습니다.

그럼 다음에도 재미있는 내용으로 다시 찾아 뵙도록 하겠습니다. 

감사합니다 ^^*

 

Reference

    [MFC/윈도우 7 멀티터치] #3 : 제스처(gesture)를 이용한 구현(下)

    MFC 2010. 6. 16. 09:00 Posted by 알 수 없는 사용자

    Intro

    안녕하세요~ MFC 카테고리의 꽃집총각 입니다.

    이번 포스팅에서는 앞 글에서 이야기 중이었던 예제 프로그램의 나머지 부분을 마저 작성하고, MFC 기반의 프로젝트에서 제스처를 이용해 멀티터치 프로그래밍을 제어하는 방법에 대한 보충 설명을 정리해 보도록 하겠습니다. 앞에 적은 글과 이어지는 내용이니까, 앞부분을 보지 못하신 분들은 ( http://vsts2010.net/292 ) 이 글을 먼저 읽어주세요 ^^ 지난번 글에서는 멀티터치 동작을 확인하기 위한 Drawing 처리까지 함께 알아봤습니다. 앞부분에서 진행됐던 내용을 소제목만 간추려서 알아보면,

    * 제스처를 인식하는 첫 번째 예제 프로그램 *

    1. MFC Application 프로젝트를 만들자
    2. 하드웨어 상태를 알아봅시다
    3. View 영역에 사각형을 그리자!

    …와 같은 내용이었습니다. 두 번째 스텝에서 알아봤던 하드웨어의 멀티터치 인식 가능여부 확인방법을 제외하고는 멀티터치와 직접적으로 연관된 내용은 많이 없었군요. 이번 시간부터는 본격적으로 터치 입력을 제어하는 코드를 함께 알아보겠습니다.

     

    Task 4 : 터치 인식 코드를 넣을 차례!

    자, 이제 드디어 제스처를 제어하는 코드를 알아볼 시간입니다. 사용자 경험(UX;User Experience) 분야에서 가장 뜨거운 이슈인 멀티터치! 뭔가 색다르고 놀라운 구현방식이 있을 거 같지만 실망(?)스럽게도 구현방법은 기존의 MFC 프로그래밍과 별다른 바가 없습니다. 그저 적당한 함수를 오버라이딩 해서 필요한 코드를 넣어주면 그만이거든요. 바로 이 점이 새롭게 변화된 MFC 추가기능들이 마음에 드는 이유 중에 하나입니다. 바로 ‘친숙한 인터페이스’ 말입니다 :)

    MFC의 윈도우 랩핑 클래스인 CWnd에는 이번에 새롭게 추가된 제스처 관련 함수들이 있습니다. 이 중에서 아래의 다섯 가지 제스처 핸들링 함수를 오버라이딩 합니다. 각각의 함수들은 이름에 명시되어 있는 제스처가 입력됐을 때 호출됩니다. 아래의 코드를 ChildView.h 파일의 class CChildView 선언에 넣어줍니다.

    // Overrides
    protected:
        // Gesture handlers
        virtual BOOL OnGestureZoom(CPoint ptCenter, long lDelta);
        virtual BOOL OnGesturePan(CPoint ptFrom, CPoint ptTo);
        virtual BOOL OnGestureRotate(CPoint ptCenter, double dblAngle);
        virtual BOOL OnGesturePressAndTap(CPoint ptFirstFinger, long lDelta);
        virtual BOOL OnGestureTwoFingerTap(CPoint ptCenter);

    기본적으로 터치가 인식되면 애플리케이션으로 WM_GESTURE 메세지가 날아옵니다. Win32 API만을 이용해 제스처를 제어하려면 WndProc 프로시저 함수에서 WM_GESTURE를 잡아서 처리해야 하지만, MFC 애플리케이션의 경우 자체적으로 각각의 제스처에 대한 전용 핸들링 함수들이 나뉘어져 있기 때문에, 제어가 필요한 제스처에 해당하는 함수를 용도에 맞게 따로따로 오버라이딩 해서 사용하면 됩니다.

    Note : 그런데 CWnd의 선언을 살펴보면 (afxwin.h 파일에 있습니다.) 위에 나열된 함수 말고도 OnGesture 함수가 정의되어 있는 것을 확인하실 수 있습니다. 함수의 원형은 아래와 같습니다.

    afx_msg LRESULT OnGesture(WPARAM wParam, LPARAM lParam);

    오호라~ 이것은 WM_GESTURE 메세지가 넘어올 때마다 해당 메세지를 직접 제어할 수 있는 함수인가 보군요~ 함수의 인자로 wParam, lParam이 모두 날아오니 win32 프로젝트에서 하는 것처럼 코딩하고 싶을 땐 이걸 상속받아서 작업하면 되겠구나 ~ … 라고 생각하고, 실제로 테스트 코드도 만들어 봤었지만 안되더군요 @.@… 똑똑한 여러분들은 이유가 무엇인지 바로 찾으셨을 거라고 생각합니다만… 저 함수는 가상 함수가 아닙니다 ^^;… 그저 이름만 보고 상속받아 써야지 했는데, 나중에 보니 재정의 할 수 없는 일반 멤버함수더라고요 ㅎㅎ 아마도 CWnd가 자체적으로 처리하는 코드를 구현한 부분이 아닌가 생각됩니다. 일반적으로는 각각의 제스처마다 독립적으로 호출되는 위의 다섯 가지 함수를 이용하면 되고요, 경우에 따라 부득이하게 WM_GESTURE 메세지를 직접 제어하고 싶을 때엔… 이전에 그랬던 것처럼 WndProc을 직접 제어하도록 하고 그곳에서 WM_GESTURE를 받은 경우에 대한 switch-case 문을 넣어주면 되겠죠 ^^

    이제 위에 소개된 다섯 개의 함수들에서 view 영역에 그려지고 있는 사각형을 제어하는 코드들을 넣어줍니다. 아래에 함수들의 본문 코드가 있습니다.

    BOOL CChildView::OnGesturePan(CPoint ptFrom, CPoint ptTo)
    {
        int dx = ptTo.x - ptFrom.x;
        int dy = ptTo.y - ptFrom.y;
        
        if (dx != 0 || dy != 0)
        {
            m_drawingObject.Move(dx, dy);
            RedrawWindow();
        }
        return TRUE;
    }
     
    BOOL CChildView::OnGestureZoom(CPoint ptCenter, long lDelta)
    {
        if ((m_pCurrentGestureInfo->dwFlags & GF_BEGIN) == GF_BEGIN)
        {
            m_dblZoomRatioStart = m_dblZoomRatioTotal = lDelta;
        }
        else if (lDelta != 0)
        {
            m_dblZoomRatioTotal += lDelta;
            double zoomFactor = (double)m_dblZoomRatioTotal / m_dblZoomRatioStart;
            
            m_drawingObject.Zoom(zoomFactor, ptCenter.x, ptCenter.y);
            
            m_dblZoomRatioStart = m_dblZoomRatioTotal;
            RedrawWindow();
        }
        return TRUE;
    }
     
    BOOL CChildView::OnGestureRotate(CPoint ptCenter, double dblAngle)
    {
        if ((m_pCurrentGestureInfo->dwFlags & GF_BEGIN) == GF_BEGIN)
        {
            // Make the first center, the rotating one
            m_ptCenter = ptCenter;
        }
        else if (dblAngle != 0.)
        {
            m_drawingObject.Rotate(dblAngle * PI / 100.0, m_ptCenter.x, m_ptCenter.y);
            RedrawWindow();
        }
        
        return TRUE;
    }
     
    BOOL CChildView::OnGesturePressAndTap(CPoint ptFirstFinger, long lDelta)
    {
        if ((m_pCurrentGestureInfo->dwFlags & GF_BEGIN) != 0)
        {
            m_drawingObject.ShiftColor();
            RedrawWindow();
        }
        
        return TRUE;
    }
     
    BOOL CChildView::OnGestureTwoFingerTap(CPoint ptCenter)
    {
        m_drawingObject.TogleDrawDiagonals();
        RedrawWindow();
        
        return TRUE;
    }
     

    함수의 이름과 전달되는 인자들 모두 직관적입니다. 함수 본문의 예제 코드들도 그리 어렵지 않군요. 위의 코드들을 복사해 적어준 뒤 빌드하고 프로그램을 실행해 봅니다. 애플리케이션이 뜨면 사각형을 손으로 움직여 보세요. 터치 입력에 따라 사각형은 이동하고, 늘어나고, 회전할겁니다 :)

    네…? 근데 뭔가가 안 된다고요?

     

    Task 5 : 다 되는 거 같은데 회전만 안되네요 ㅡㅜ…

    여기까지 진행하고 테스트를 해보면 다른 제스처는 다 인식을 하는데, 회전(rotate)만 제대로 안 되는 현상을 겪으실 겁니다. 참고로 이번 예제에서 확인하실 수 있는 다섯 가지 제스처에 대한 동작을 간략히 설명 드리면 아래와 같습니다.

    panning 손가락을 스크린에 대고 이동한다.
    zoom 두 손가락을 스크린에 대고 벌리거나 모은다.
    rotate 두 손가락을 스크린에 대고 회전시킨다.
    press and tab 한 손가락을 스크린에 댄 상태에서 다른 손가락으로 스크린을 빠르게 터치한다.
    two finger tab 두 손가락을 동시에 스크린에 붙였다 뗀다.

    그런데 다른 건 다 잘되는데 아마 rotate 제스처만 동작하지 않을 거예요. 그리고 다른 제스처들은 다 인식할 테지만… 사각형 내부에서 일어난 제스처인지를 판단하는 처리가 없었기 때문에, 꼭 사각형 내부가 아니더라도 client 영역 내에서 발생한 유효한 제스처라면 모두 다 인식하는 걸 확인할 수 있으실 겁니다.

    그 이유는 코드가 잘못된 것이 아니라, 윈도우 자체에서 다른 제스처들은 모두 기본적으로 활성화 되어있는데, rotate만은 기본적으로 비활성화 되어있기 때문입니다. 활성화 하려면 다 해두든가 아님 말든가 할 것이지 왜 rotate만 천대(?)하는지 자세한 내막은 모르겠습니다만… 그것이 현실이군요. rotate를 활성화하기 위해, 제스처의 설정(config)를 제어하는 방법을 알아보도록 합시다.

    MFC에서는 제스처의 설정을 쉽게 컨트롤 할 수 있도록 CGestureConfig라는 클래스를 제공합니다. CChildView의 선언( in ChildView.h )에 CGestureConfig 타입의 멤버변수를 하나 추가해줍니다.

    class CChildView 
    {
        // ...
     
    // Fields
    protected:
        // Holds gesture configuration
        CGestureConfig m_gestureConfig;
        
        // ...
    }

    그리고 새롭게 부활한 반가운 인터페이스, 마법사 중의 마법사 MFC Class Wizard를 띄워서 OnCreate 함수를 오버라이딩 합니다. (단축키 Ctrl + Shift + X 입니다 ^^)

    그리고 아래의 코드를 넣어주세요.

     
    int CChildView::OnCreate(LPCREATESTRUCT lpCreateStruct)
    {
        if (CWnd::OnCreate(lpCreateStruct) == -1)
            return -1;
     
        // TODO: Add your specialized creation code here
        GetGestureConfig(&m_gestureConfig);
        
        // Only rotation is not enabled by default
        m_gestureConfig.EnableRotate();
        
        SetGestureConfig(&m_gestureConfig);
     
        return 0;
    }
     
     
     
    그리고 나서 다시 한번 빌드하고 실행해 봅니다. 이제 rotate 제스처도 제대로 인식하는 첫 번째 예제 프로그램이 완성 되었습니다 ^^*
     
    자 드디어 첫 번째 예제의 완성입니다~ !! 짝짝짝 ~
     

    Task 6 : panning이 왜 이렇게 뻑뻑하지? 아이폰은 안 그런뎅..

     
    task5 까지가 channel9에서 소개한 MFC gesture 기능의 첫 번째 예제입니다. 필요한 부분과 불필요한 부분을 적절하게 구분해 잘 설명해 두어서, 한번 설명을 따라 작성해 보시는 것만으로 제스처 프로그래밍에 대한 대략적인 흐름을 손쉽게 파악하셨을 거라고 생각합니다.
     
    이대로 끝내기엔 좀 아쉬운 감이 있어서… 예제 코드에 대한 추가 설명을 드리고자 합니다. 사실 글 중간중간에도 제가 추가로 적은 글이 제법 있었지만… 마땅히 말씀드릴 타이밍을 못 찾았던 요것 한 가지만 더 말씀 드리도록 할게요 ㅎㅎ
     
    일단 예제를 조작해 보면 제일 먼저 거슬리는 부분이 바로 panning 제스처에 대한 조작감(?) – 게임개발만 오래 하다 보니 이런 표현이…^^; – 인데요. 아이폰에서 느껴지는 부드러운 반응성에 비해 사각형 움직임이 무척이나 둔하고 불만족스럽다는 걸 느끼실 겁니다. 사각형이 한 번 움직이고 나면 괜찮은데, panning 제스처를 인식해 움직여지기 시작할 때 까지가 뭔가 걸리는 느낌입니다. x, y 한 방향으로만 움직이고, 다른 방향으로는 안 움직이는 경우도 아마 겪으셨을 거예요. 왜 이럴까요? 기기의 한계? 아님 OS 자체의 한계?
     
    이런 현상은 하드웨어의 문제나 OS 기능 자체의 문제가 아닙니다. 이것도 역시 panning 제스처의 옵션에서 GC_PAN_WITH_GUTTER 플래그가 기본적으로 켜져 있기 때문에 나타나는 현상입니다. 저도 처음에 제스처(gesture)를 이용한 멀티터치 예제들을 실행해 보다가 panning의 무딘 반응성이 너무 눈에 거슬려서, 부드러운 움직임을 보려면 결국 WM_GESTURE에서는 한계가 있고, WM_TOUCH를 써야 하는가 보다 했었는데, 간단히 플래그만 조절해주면 좀 더 부드러운 움직임을 느낄 수가 있게 됩니다.

    GC_PAN_WITH_GUTTER에서 gutter는 홈통이나 배수로 등의 뜻으로, panning 제스처가 발생했을 때 가장 메인이 되는 방향으로의 움직임 이외의 방향성에 대한 반응은 특정 임계값을 넘기지 않는 이상 무시하게 하는 효과를 줍니다. 이 때문에 손가락에서의 미세한 움직임들이 무시되고, 반응성이 안좋다는 느낌을 받게 되죠. 이를 해결하기 위해서는 rotate 제스처의 활성화를 위해 오버라이딩 했었던 OnCreate 함수에서 panning에 대한 아래의 설정도 함께 처리해 주면 됩니다.

     
    config.EnablePan( TRUE, 
        GC_PAN_WITH_SINGLE_FINGER_VERTICALLY | 
        GC_PAN_WITH_SINGLE_FINGER_HORIZONTALLY |
        GC_PAN_WITH_INERTIA );
     
    pan 제스처를 활성화 하되, GC_PAN_WITH_GUTTER 플래그만을 제외한 나머지 플래그들을 활성화 해주는 코드입니다. 위 코드를 넣고 나서 빌드하고 다시 실행해보면 이전보다 훨씬 더 부드럽게 사각형이 이동되는 것을 느낄 수 있으실 겁니다.

     
     

    Outro

    이것으로 MFC를 이용해서 제스처를 이용한 멀티터치 프로그래밍 방법의 첫 번째 예제를 소개와 설명을 모두 마쳤습니다. 제스처의 설정을 컨트롤 하는 방법, 각각의 제스처에 대한 핸들링 코드를 넣어주는 방법 등을 알아보았는데, 대부분 예전 MFC 프로그래밍의 마우스/키보드 이벤트 처리 등과 비슷한 방식이었기 때문에 그리 어렵지는 않은 난이도였습니다.

    다음 포스팅 에서는 이제 가장 자유롭고 확장성 있는 (… 하지만 대신 좀 더 까다로운 ) WM_TOUCH를 이용한 멀티터치 프로그래밍 방법을 알아 보도록 하겠습니다. 혹시 그 전에 질문 사항들이나, 제스처 프로그래밍 방법들에 대한 추가 학습사항이 있다면 다시 한 번 정리하는 기회를 갖도록 하겠습니다.

    그럼 다음 포스팅에서 다시 인사 드리도록 하겠습니다. 그 때까지 더운 날씨에 모두들 건강하시고 공부 열심히 하세요 ~ ^^*

    감사합니다 ~ 꾸벅 ~

    Reference

    [MFC/윈도우 7 멀티터치] #2 : 제스처(gesture)를 이용한 구현(上)

    MFC 2010. 5. 10. 08:30 Posted by 알 수 없는 사용자

    Intro

    안녕하세요. MFC 카테고리의 꽃집총각입니다.

    한동안 여러 가지 다른 행사와 게으름으로(;;) 포스팅이 많이 늦어졌습니다 ^^; 지난 글에서는 ( [윈도우 7 멀티터치] #1 : 멀티터치 UX를 적용하는 3단계 전략 ) 멀티터치 프로그래밍을 내 애플리케이션에 적용하는 방법을 3단계로 나누어 설명하였습니다. 이 중 첫 번째 단계인 ‘추가 코딩 없이도 기본적으로 얻을 수 있는 기능들’에 대해 자세히 알아보았고, 나머지 두 단계는 기본적인 내용만 소개해 드렸습니다. 이번엔 이중에서 두 번째 단계인 제스처(gesture) 이용한 프로그래밍 방법에 대해 자세히 알아보도록 하겠습니다.

    note : 지난번 까지는 프로그래밍 이야기 보다는 개념적인 설명이 위주였기 때문에 ‘Windows 7 Development’ 카테고리에 글을 올렸습니다만… 이제부터는 본격적으로 구현에 대한 이야기가 시작되니까 ‘MFC’ 카테고리에서 연재하도록 하겠습니다. 앞부분은 어느 언어를 사용하든 멀티터치 프로그래밍을 위해 알아두어야 할 공통적인 내용들이었고요, 제가 앞으로 말씀드릴 부분은 주로 MFC를 위한 구현방법이니까, 이게 맞는 거겠지요. :)

     

    제스처를 인식하는 첫 번째 예제 프로그램

    이제까지는 제가 주로 개념설명이나 이론적인 부분들을 모두 말씀 드리고 나서 예제 코드를 알아봤었는데, 이런 쌍팔년도식 학습법은 요즘은 별로 어울리지 않는 듯 합니다 ^^;… 일단은 돌아가는 예제 프로그램을 간단하게 한 번 작성해 보기로 하지요. 그게 자질구레한 설명도 줄여주고, 공부하기도 좀 더 쉬울 것 같네요 ㅎㅎ

    첫 번째로 구현해볼 예제는 view 영역에 그려진 box를 터치로 조작, 변환하는 프로그램 입니다.

    view 영역에 box를 하나 표시하고, 손가락으로 사각형을 이동, 회전, 확대/축소 할 수도 있고, Press  & Tap 제스처를 통해 색상을 변경할 수도 있고, Twi Finger Tap 제스처로 X표시를 하거나 지울 수도 있는 프로그램입니다. 예제를 따라서 작성해 보면 MFC 애플리케이션에서 어떻게 제스처를 다룰 수 있는지 쉽게 이해하실 수 있습니다. 해당 예제는 Windows 7 SDK에 들어있는 MFC 샘플 중에 하나입니다. 영어에 부담이 없으신 분들은 직접 Windows 7 SDK를 확인해 보셔도 좋을 것 같군요 ^^ ( http://channel9.msdn.com/learn/courses/Windows7/Multitouch/Win7MultiTouchGestureMFC/Exercise-1-Build-a-Multitouch-Gesture-Application/ )

     

    Task 1 : MFC Application 프로젝트를 만들자

    우선 Visual Studio 2010에서 MFC Application 프로젝트를 하나 만드세요. Application Wizard에서는 고전 MFC 스타일의 SDI 타입을 지정해 줍니다. Wizard의 설정 화면을 활자 설명 대신 스크린샷으로 대신하겠습니다.

     

    Task 2 : 하드웨어 상태를 알아봅시다

    본격적인 멀티터치 UX를 구현하기에 앞서, 현재 구동중인 실행환경이 멀티터치가 가능한 상태인지를 확인해야 합니다. 멀티터치를 인식 가능한 하드웨어가 연결되어 있는지, 그리고 사용 가능한 상태인지를 알 수 있어야겠지요. 이런 정보를 얻고자 할 때는 예전부터 익히 사용해오던  ::GetSystemMetrics(…) 함수를 이용하면 됩니다.

    int WINAPI GetSystemMetrics( __in  int nIndex );
    http://msdn.microsoft.com/en-us/library/ms724385(VS.85).aspx

    GetSystemMetrics는 운영체제의 여러 가지 설정 정보들을 얻어올 수 있는 API로 예전부터 자주 사용되던 함수입니다. 윈도우 캡션 영역, 메뉴 영역 등의 너비를 얻을 수 있기 때문에 UI 작업할 때 많이 사용해 보셨을 겁니다. MFC/Win32 API에 새롭게 추가되는 기능들은 상당 부분 이렇게 이전 인터페이스를 그대로 사용하고 있기 때문에 공부하기가 특히 어렵지 않은 느낌이어서 마음에 듭니다 ㅎㅎ 이 함수로 터치인식 정보를 얻고 싶을 땐 아래의 새로운 flag들을 사용합니다.

    • SM_DIGITIGER : 입력장치 상태 확인
    • SM_TABLETPC : 타블렛 pc 여부 확인
    • SM_MAXIMUMTOUCHES : 최대 동시입력 가능한 터치 개수

    이번에 공부하면서 보니, 윈도우 XP가 타블렛 PC edition이란게 따로 있었더군요… SM_TABLETPC는 이런 정보를 확인할 때 쓰는 값이고, 나머지 두 flag는 Vista 이하 OS에서는 인식하지 않습니다. 각 항목의 자세한 값은 MSDN을 참고하시면 되고요, 기본적인 경우라면 아래의 코드를 가져다 사용하시면 되겠습니다. 현재 멀티터치 입력 가능 여부와 동시 입력 개수를 알아보는 코드입니다.

       1: BYTE digitizerStatus = (BYTE) GetSystemMetrics(SM_DIGITIZER);
       2: if ((digitizerStatus & (NID_READY | NID_MULTI_INPUT)) == 0) //Stack Ready + MultiTouch
       3: {
       4:     AfxMessageBox(L"현재 터치 입력이 불가능한 상태입니다.");
       5:     return FALSE;
       6: }
       7:  
       8: BYTE nInputs = (BYTE) GetSystemMetrics(SM_MAXIMUMTOUCHES);
       9:  
      10: CString str;
      11: str.Format(L"현재 %d개의 터치를 동시 인식할 수 있습니다.", nInputs);
      12: AfxMessageBox(str);

    그리고… 멀티터치 프로그래밍 공부가 하고 싶은데 하드웨어가 마땅치 않을 때… 제가 예전에 말씀 드렸던 시뮬레이션 환경 설정 ( http://multitouchvista.codeplex.com/ ) 기억하고 계시죠? 기억이 안 나시는 분은 ( [멀티터치]멀티터치 프로그래밍 환경 구축하기 ) 링크를 참고해 주세요 ^^

    위에 있는 코드를 그대로 복사해서 CTouchGestureDemoApp::InitInstance() 함수에 넣어주세요. 그리고 애플리케이션을 실행하면 아래와 같은 창이 뜹니다.

    아따, 많기도 하여라 @.@… 동시에 255개의 터치를 인식할 수 있군요. 저도 터치 입력 환경을 시뮬레이션 해서 공부하고 있는데, 이런 경우 동시 입력 개수가 255개로 나오게 됩니다 ㅎㅎ

     

    Task 3 : View 영역에 사각형을 그리자!

    샘플을 따라 해보기 위해서 화면에 box를 그려주는 코드를 우리가 일일이 직접 작성할 필요는 없겠죠. 지금은 제스처의 인식 방법을 알아보고 싶은 거니까요. Windows 7 SDK에서 box 드로잉을 해주는 코드를 제공하고 있으니, 이 코드를 사용하도록 하겠습니다. 그리고 우리는 멀티터치 제스처를 이용하는 방법에만 집중하기로 합시다. box를 그려주는 CDrawingObject라는 클래스의 소스는 아래에서 다운 받으실 수 있습니다.

    위의 파일을 다운 받으셔서 프로젝트에 포함하고, stdafx.h 파일의 아래쪽에 헤더파일 인클루드 구문을 넣어줍니다. 기본적으로 작성되어 있는 상태에서 #include <afxcontrolbars.h> 아래에 넣어주면 됩니다.

       1: #include "DrawingObject.h"

    그리고 ChildView.h를 열고 View 클래스의 멤버 변수로 CDrawingObject 객체를 하나 선언해 줍니다.

       1: // The drawing object
       2: CDrawingObject m_drawingObject;

    마찬가지로 View 클래스의 멤버 변수로, box의 확대/축소와 이동을 처리하기 위해 필요한 변수들을 몇 가지 더 추가해 줍니다.

       1: // Needed for drawing object position and size calculations
       2: double m_dblZoomRatioStart;
       3: double m_dblZoomRatioTotal;
       4: CPoint m_ptCenter;

    그리고 box를 그려주기 위해서, CChildView.cpp에 있는 CChildView::OnPaint() 함수의 제일 아래쪽에 box drawing 코드를 넣어줍니다.

       1: m_drawingObject.Paint(&dc);

    그다음, 윈도우 크기를 변경할 때 box의 크기와 위치를 기본값으로 초기화 해주는 코드를 넣어줍니다. box가 화면영역을 벗어나거나 한 경우 초기화 코드가 있으면 좋겠지요. 그러기 위해서 먼저 WM_SIZE 메세지 핸들러를 추가합니다.

    Ctrl + Shift + X 키를 눌러 MFC 클래스 위자드(MFC Class Wizard)를 띄웁니다. MFC 클래스 위자드는 VC++ 6.0에 있던 툴인데, Visual Studio .NET 2002 통합 환경으로 변경되면서 사라졌지만 이번에 Vusial Studio 2010에서 새롭게 부활했습니다. 거의 10년 만이라고 할 수 있군요. 예전에 사용해 보신 분들이라면 아주 익숙한 인터페이스 일겁니다. 여기에서 CChildView의 WM_SIZE 메세지를 선택해서 핸들러를 추가해 줍니다. 아래 스크린샷을 참고하세요.

    MFC 클래스 마법사의 부활은 VS2010의 핵심적인 변화 요소중에 하나입니다. 클래스 마법사의 활용 부분은 다음 기회에 별도의 포스팅으로 자세하게 설명하도록 하겠습니다.

    생성된 CChildView::OnSize 함수에 아래의 코드를 넣어줍니다.

       1: m_drawingObject.ResetObject(cx, cy);

    여기까지 진행했다면 이제 drawing 처리의 완성입니다. 프로젝트를 빌드하고 애플리케이션을 실행하면 아래와 같이 빨간 box가 drawing 되는 모습을 확인하실 수 있습니다.

     

    Outro

    한 번의 포스팅에서 예제의 완성과 추가설명까지 모두 다루려고 했지만 그러기엔 글이 너무 길어질 것 같네요. 이쯤에서 한 번 호흡을 끊고, 다음 포스팅에서 실질적인 제스처 인식 코딩 방법과 추가설명을 정리하도록 하겠습니다. (下편은 바로 올릴 예정이니 조금만 기다려 주세요)

    이번 글에서는 제스처를 인식하는 예제 프로그램의 작성 방법을 단계적으로 소개하고, 하드웨어의 멀티터치 인식 여부 확인 방법을 알아보았고, 아주 짧게나마 VS2010에서 새롭게 부활한 MFC 클래스 마법사에 대한 언급이 있었고, 첫 번째 멀티터치 예제의 작성 방법을 절차적으로 알아보았습니다. 제스처를 이용한 멀티터치 구현 하(下)편에서는 제스처를 인식해 box를 이동, 변환 시키는 코드의 작성과 샘플을 통해 알아볼 제스처 인식 프로그래밍의 부가 정보 및 팁들을 소개해 드리도록 하겠습니다.

    그럼 곧바로 하(下)편으로 찾아 뵙겠습니다.
    감사합니다 ^^*

    Reference

    Intro

    안녕하세요. MFC 카테고리의 꽃집총각 입니다.

    지난 포스팅에서는 2회에 걸쳐 멀티터치 인터페이스의 비전과(사람이 기계와 만나는 진정한 방법 – 멀티터치), 멀티터치 프로그래밍을 하기 위한 환경 구축([멀티터치]멀티터치 프로그래밍 환경 구축하기)에 대해서 각각 알아보았습니다. 오늘부터는 실제로 터치 UX 프로그래밍을 하기 위한 내용들을 살펴보고자 합니다. 이제부터 점점 내용이 재미있어 지겠군요! 자 이제 시작하겠습니다!!

    MSDN 매거진이나 channel9에 올라오는 article들을 보면, 응용 프로그램에 멀티터치 UX를 적용하는 방법을 3단계로 나누어 설명하고 있습니다.

    1. ‘Good’ step : 코딩하지 않아도 공짜로 얻을 수 있는 기능들.
    2. ‘Better’ step : 제스처(Gesture)를 이용해 멀티터치 프로그래밍 하기.
    3. ‘Best’ step : 원시 데이터(raw-data)를 직접 제어해, 가장 디테일한 터치 UX 구현하기.

    위와 같이 터치 적용 수준에 따라 세 단계로 나누고, 각각의 단계에 Good / Better / Best 라는 이름을 붙이고 있습니다. 멀티터치 UX를 적용하는 좋은 방법, 더 좋은 방법, 가장 좋은 방법 정도가 되겠네요 ㅎ 단계성을 표현하려는 의도는 이해합니다만 조금 우습군요 (피식). 각 단계가 어떤 내용을 담고 있는지 하나씩 좀 더 자세하게 이야기해보도록 하겠습니다.

     

    1. ‘Good’ step : 코딩하지 않아도 공짜로 얻는 터치 UX

    우선 기본적으로 첫 번째 단계는, 소스코드를 손대지 않고도 얻을 수 있는 기본적인 터치 UX에 대한 내용입니다. 기본으로 제공되는 컨트롤들이 윈도우 7의 터치 인터페이스 환경에 맞게 버전업 되면서, 자체적으로 멀티터치 입력에 반응하는 기능들을 제공하게 되었습니다. 이런 기능들은 기존에 작성된 다수의 레거시(legacy) 프로그램에도 그대로 적용됩니다. 다시 말해 예전에 제작했던 응용 프로그램이 별다른 호환성의 문제 없이 WIndows7에서 실행되기만 한다면 바로 얻을 수 있는 기능들입니다.

    스크롤바의 스크롤 기능, 에디트 컨트롤의 화상키보드 호출기능, 윈도우 탐색기나 인터넷 익스플로러와 같은 프로그램의 네비게이션 기능 (쉽게 말해, ‘앞으로’, ‘뒤로’ 버튼 기능. WM_APPCOMMAND 메세지) 등이 그것입니다. 이런 것들은 직접 한 번 조작해 보시는 게 훨씬 나을 겁니다. 이전 글에서 소개해 드린 Multi-Touch Vista의 터치 인식 디바이스 드라이버를 구동하면 터치 입력을 시뮬레이션 해 볼 수 있으니까요 :) 윈도우 탐색기의 파일 리스트 영역에서 손가락을 좌/우로 빠르게 드래그 해주면 – 아이폰에서 사진을 넘기는 동작입니다. 뒷부분에 다시 정리하겠지만 Flicks 라는 이름의 제스쳐 입니다 – 이전 / 다음 버튼을 누른 것처럼 네비게이션 히스토리를 따라 경로가 변경됩니다.

    윈도우 탐색기에서 왼쪽 –> 오른쪽으로 문지르는 Filcks 제스처를 실행한 모습.

    윈도우 탐색기에서 오른쪽 -> 왼쪽으로 문지르는 Flicks 제스처를 실행한 모습.

    스크린샷을 보시면 일반 아이콘 크기 정도의 ‘앞으로’, ‘뒤로’ 이미지가 손가락을 갖다 댄 곳에 잠시 나타나는 것을 보실 수 있습니다. 이렇게 되면서 윈도우 탐색기의 탐색 경로가 앞/뒤로 변경됩니다. 이 시점을 Spy++로 조회해 보니 WM_APPCOMMAND 메세지가 넘어오는군요. 이전에 작성된 응용 프로그램이 WM_APPCOMMAND 를 받아서 네비게이션 기능을 처리하고 있었다면, 윈도우 7에서 실행하는 것 만으로도 Flicks 터치 제스처를 얻게 되는 겁니다.

    손가락 터치로 스크롤바의 스크롤이 가능하다는 사실은 제가 이미지 스크린샷을 찍어서 보여드릴 방법이 딱히 없군요 ^^; 이 부분은 직접 실행해서 확인해 주시길 부탁 드리면서 나머지 한가지만 더 같이 보기로 하죠. 메모장을 열어서 View 영역을 손가락으로 탭 해주면 위 스크린샷에서 보이는 것 같이 키보드 그림이 그려진 작은 윈도우가 잠깐 동안 노출됩니다. 이 녀석을 다시 탭 하면 터치로 텍스트를 입력할 수 있는 화상키보드가 화면에 나타납니다.

     

    오… 요즘 인기를 얻고 있는 블록키보드 타입이군요. 먼지의 유입이 적고 간격이 넓어 오타가 줄어든다는 바로 그 방식 +_+ 아… 근데 어차피 화상 키보드라 아무 상관 없겠습니다…;;

    이 화상키보드 호출 기능은 텍스트를 입력하는 방식의 컨트롤들이면 어느 곳에나 기본 구현되어 있습니다. 기본 에디트 컨트롤, 리치 에디트 컨트롤, IP Address 입력 컨트롤 등이 이에 해당됩니다. 텍스트를 입력할 수 있도록 캐럿이 깜박이는 방식의 인터페이스를 가진 컨트롤들이면 이미 화상키보드 호출 기능도 공짜로 얻게 된 겁니다. 윈도우 7에서 실행하기만 한다면 말이지요 :)

    이런 기능들은 그저 공짜로 얻게 된 추가기능 치고는 꽤나 근사합니다. 하지만 모든 응용 프로그램에 공통 적용되는 사항이기 때문에 내가 만든 응용프로그램만의 고유한 기능이라기보다는 윈도우 7 OS의 새로운 기능이라고 보는 편이 더 적합합니다. 프로그램의 실제 사용자에게도 해당 기능들은 아주 당연한 인터페이스로 인식될 것이고, 금새 적응하게 될 것입니다.

    그렇다면 좀 더 터치 인터페이스에 친화적인 응용 프로그램을 만들기 위해서는 어떻게 해야 할까요?

    터치를 인식하도록 프로그래밍 하는 방법은 두 가지가 있습니다. 하나는 적은 수정비용으로 기본적인 동작들을 재빠르고 심플하게 구현하는 방법이고, 또 다른 한가지는 보다 강력하고 디테일한 움직임을 다루는 방법입니다. 이 두 가지 방법들을 각각 2. ‘Better’ step과 3. ‘Best’ step에서 이야기해 보도록 하겠습니다.

     

    2. ‘Better’ step : 제스처(Gesture) 이용하기.

    터치 UX를 응용 프로그램에 적용하는 두 번째 방법은 제스처(Gesture)를 이용하는 방법입니다. 여기에서 ‘제스처’란 터치 인터페이스를 통해 손으로 입력할 수 있는 한가지의 입력 패턴을 말합니다. 예를 들어 멀티터치라고 하면 가장 대표적으로 떠오르는 입력방식 중에 하나인 사진 확대/축소 방법은 두 손가락을 스크린에 갖다 대고 간격을 벌리거나 좁히는 식으로 주로 쓰입니다. MSDN에서는 이런 입력 방식을 Zoom이라는 이름의 제스처로 부르고 있습니다. 멀티터치는 앞으로도 다양한 제스처가 적절한 기능들과 보다 직관적으로 연결되어 확장해 나갈 수 있는 무한한 가능성을 가지고 있지요.

    MSDN에 보면 (http://msdn.microsoft.com/en-us/library/dd940543(VS.85).aspx) 여러 가지 제스처들 중에서 가장 흔하고 쉽게 사용되는 대표적인 9가지 제스처에 대해 이름을 정의하고 있습니다.

     

    이미지가 작아서 잘 안보이실 텐데요. MSDN 링크를 열고 보다 큰 이미지로 내용을 확인하시는 게 좋겠습니다. 비록 도표는 영어로 되어있지만 그다지 내용도 없을 뿐더러 Action 그림과 제스처 이름만 보면 대충 어떤 방식인지 직관적으로 이해하실 수 있습니다. 앞으로 제스처를 이용한 터치 프로그래밍을 구현하고자 하면 각 제스처의 이름을 자주 확인하게 될 테니 위의 도표는 꽤나 유용하게 쓰일겁니다. 한 번 정도는 눈여겨봐두시기 바랍니다.

    제스처를 이용한 터치 구현 방법은 9가지 제스처가 입력된 경우 이에 따른 기능들이 실행되게 하는 것입니다. Windows 7은 기본적으로 터치 입력이 감지되면 위의 9가지 제스처에 해당하는지 여부를 자체적으로 판단합니다. 이에 해당하는 적절한 Gesture가 있을 경우 WM_GESTURE라는 메세지를 만들어 응용 프로그램에 전달해줍니다. 해당 메세지에는 각각의 제스처에 대한 좌표정보 등의 추가정보들이 적절하게 가공되어 제공됩니다. 이미 OS 자체에서 터치 인터페이스를 인식해서 한번 가공해둔 상태이기 때문에 프로그래머는 가벼운 마음으로 OS가 해석해준 좌표 정보나 수치 등을 활용해 기능을 연동하기만 하면 그만입니다. 그렇기 때문에 적은 수정 비용으로, 기본적인 제스처에 반응하는 기능들을 빠르게 구현할 수가 있는 점이 장점입니다.

    만약 Win32 API만을 이용하여 프로그램을 만든다면 WndProc 에서 WM_GESTURE 메세지를 잡아서 이에 맞는 코드를 넣어주면 되고, MFC는 한층 더 간단하게 적절한 가상함수를 오버라이딩해서 코드를 넣어주면 되는 식이지요. 윈도우 메세지와 MFC 클래스의 가상함수를 재정의하는 구현 방식은 이미 우리가 오래도록 작업해 왔던 기존의 윈도우 프로그래밍 방법과 크게 다르지 않기 때문에 익숙하고 거부감이 없습니다. 보다 자세한 구현 방법은 다음 포스팅에서 계속 이어 나가도록 하겠습니다.

    제스처를 이용한 구현 방법은 ‘적은 수정 비용으로 기본적인 동작의 구현을 빠르게 만드는 방향’에 초점을 둔 방식입니다. 저수준 단위의 터치 입력 데이터를 프로그래머가 일일이 분석해서 제스처의 패턴을 파악하지 않아도 됩니다.

    하지만 이런 일반적인 제스처 말고 색다른 입력 방식을 구현해야 할 경우도 있을 겁니다. 기존에 없었던 독특한 방식의 제스처를 새롭게 만들고 싶을 수도 있습니다. 혹은 마치 실제로 손가락을 이용해 도화지에 그림을 그리듯 매우 디테일한 레벨의 컨트롤을 구현하고 싶을 수도 있습니다. TED 강연에서 제프한 박사가 보여준 독특한 형태의 멀티터치 데모라던가, Microsoft Surface에서 처리하듯이 화면의 Widget을 직접 손으로 만지는 것 같은 직관적인 움직임을 구현하고 싶을 수도 있습니다. 이런 경우에는 제스처를 이용한 구현방법을 적용하기에는 무리가 있습니다.

    그렇다면 그러한 고급 수준의 터치 인터페이스를 구현하려면 어떻게 해야 할까요?

    그것을 다음 단계인 3 ‘Best’ step이 설명하고 있습니다 :)

     

    3. ‘Best’ step : 가장 디테일한 터치 UX를 구현하는 방법.

    마지막 단계인 3. ‘Best’ step에서는 가장 세밀하고 유연한 터치 인터페이스를 만들 수 있는 방법을 제시하고 있습니다. 2단계에서처럼 윈도우가 자체적으로 터치 메세지를 가공하는 방식이 아니라, 아무 처리도 거치지 않은 원시 터치 데이터(Row Touch Data)를 응용 프로그램으로 직접 넘겨주게 설정하고(WM_TOUCH 메세지), 개발자 스스로가 이를 이용해 저레벨 수준의 구현 부터 직접 담당하는 방식입니다. 이런 식이라면 아무래도 제스처를 이용한 방법 보다는 신경써야 할 부분이 많아지고 구현이 좀 더 까다로워지는 점이 있습니다. 제스처를 이용한 구현방식이 적은 수정비용으로 빠른 구현을 꾀한 방향이었다면, 원시 데이터를 직접 활용하는 방법은 조금 까다롭더라도 고수준의 인터페이스를 구현하는 것에 초점을 두고 있습니다. Microsoft Surface 처럼 말이지요.

    Microsoft Surface ® (이미지 출처 : http://cyberimpacts.com/)

    이러한 low-level의 터치 데이터는 WM_TOUCH라는 새로운 윈도우 메세지를 통해서 넘겨받을 수 있습니다. 역시 기존의 다른 윈도우 메세지를 처리해오던 것처럼 WndProc에서 메세지를 받아서 코드를 넣어주면 됩니다. WM_TOUCH는 WM_MOUSE… 계열의 마우스 메세지와 유사합니다. 이를 활용하는 방법 역시 차례대로 다음 포스팅에서 보다 자세히 알아 보도록 하겠습니다.

     

    Outro

    이번 포스팅에서는 드디어 멀티터치 프로그래밍을 하기 위한 단계적 방법들과 몇 가지 개념들, 용어들에 대해서 정리해 보았습니다. 아직까지도 소스코드나 api들의 직접적인 설명은 없었지만 오늘 알아본 내용들을 통해 터치 UX를 구현하기 위한 전반적인 방향들은 어느 정도 체계가 잡히셨을 거라고 생각합니다.

    다음 포스팅에서는 WM_GESTURE를 이용한 방법부터 시작해서 WM_TOUCH를 이용하는 방법까지 순서대로 멀티터치 관련 API들과 활용 방법들을 정리해 보도록 하겠습니다. Win32 API를 이용하는 방법과 MFC를 이용하는 방법들을 소개할 예정이니 참고해 주세요.

    그럼 빠른 시간 안에 다음 내용을 가지고 다시 인사 드리도록 하겠습니다. 화창한 봄날인데 너무 모니터 앞에만 앉아있지 마시고 봄기운 가득 느끼시면서 재충전 하는 시간들 가져보시기 바랍니다 ^^

    감사합니다 ^^*

     

    reference

    사람이 기계와 만나는 진정한 방법 - 멀티터치

    Windows 7 2010. 3. 29. 09:00 Posted by 알 수 없는 사용자

    안녕하세요. MFC 카테고리의 꽃집총각 입니다.
    오늘은 Technical Article에 앞서서 멀티터치에 대한 개념적인 글을 하나 준비해 보았습니다.

    출처 : http://farm3.static.flickr.com/2152/2547399057_f91154cb87_o.jpg

    이미지 출처 : http://farm3.static.flickr.com/2152/2547399057_f91154cb87_o.jpg

    이제는 ‘멀티터치’라는 기술은 신기술이라고 부르기도 어색할 만큼 많이 일반화 되었습니다.
    애플의 아이폰은 멀티터치의 대중화에 아주 본격적인 역할을 했습니다. 많은 사람들이 손바닥에 올려놓은 아이폰에서 사진을 멀티 터치로 zoom하고 slidng 합니다.

    멀티터치 기술에서 우리가 기대할 수 있는 효과를 아래의 두 가지로 정리할 수 있다고 생각합니다.

    1. 이전에 없었던 새로운 형태의 인터페이스 구현
    2. 컴퓨팅 능력이 부족한 저연령/고연령 세대들의 학습 문턱 제거.

    멀티터치는 좀 더 본능적이고, 직관적인 컴퓨팅을 가능하게 합니다. 굳이 하드웨어나 소프트웨어의 학습과정 없이, 매뉴얼도 없이 생각대로 조작하는 것을 가능하게 해주는 새로운 개념의 인터페이스 입니다.

    멀티터치 기술의 상용화는 이제 겨우 걸음마 단계라고 볼 수 있습니다.
    아직 우리가 상상하지 못한 놀라운 제스처들이 우리의 스크린에서 가능해 질 지도 모르는 일입니다.
    제가 개인적으로 멀티터치라는 기술을 처음 접했던 것은 2006년 제프한 박사의 TED 강연을 통해서 였습니다. 저는 그가 말하는 내용 중, ‘멀티 터치는 사람이 기계와 만나는 진정한 방법이다’ 라는 말이 꽤나 인상적이군요. 강연에서 제프한 박사는 앞으로 멀티터치를 활용한 보다 다양한 분야의 응용이 이루어 질 것이고, 인터페이스의 큰 변화를 가져올 것이라는 메세지를 던지고 있습니다.

    (한글 자막을 켜고 보세요)http://www.ted.com/talks/lang/eng/jeff_han_demos_his_breakthrough_touchscreen.html

    애플은 이런 멀티터치 기술을 아주 발 빠르게 자사의 product에 적용해 나가고 있습니다. 아이팟 터치와 아이폰, 매직마우스 등에서 멀티터치 제스처를 인식하고 있고, 얼마 전에 새롭게 발표된 맥북에서도 터치패드가 멀티터치를 인식하도록 개선되었습니다.

    마이크로소프트의 새로운 OS인 윈도우 7 역시도 멀티터치 인식 기능이 구현되어 있습니다. 하지만 생각 외로 많은 분들이 윈도우 7의 멀티터치 인식에 대해 잘 모르고 계시더군요. 아마도 애플의 제품군은 하드웨어와 소프트웨어가 함께 공급되어 바로 멀티터치를 체험해 볼 수 있는 것과는 달리, 윈도우 7은 멀티터치 인식 하드웨어가 없으면 직접 체험해 볼 수 없기 때문에 비교적 알려지지 않게 된 것이라고 추측해 봅니다.

    저는 얼마 전 3월 9일에 마이크로소프트에서 주최한 Windows 7 Technical Briefing 이라는 기술 세미나에서 ‘Win32 API를 이용해 멀티터치 프로그래밍하기’라는 주제로 세션을 맡아 발표한 적이 있습니다. 그 때를 계기로 윈도우 7에서 동작하는 멀티터치 프로그램의 개발에 대해 많은 관심을 갖게 되었습니다. 발표에 앞서 ① 윈도우 7을 터치로 조작해 보신 분이 있느냐는 질문과 ② 윈도우 7이 멀티터치를 인식한다는 사실을 알고 계셨느냐는 두 가지 질문을 드려보았는데, 개발자 분들을 대상으로 하는 기술 세션이었는데도 불구하고 생각 외로 많은 분들이 처음 듣는다는 반응을 보이셔서 의외라고 생각했습니다.

    아래의 동영상은 PDC 2008에서 윈도우 7의 멀티터치 인식 기능을 시연하는 장면입니다.

    보시다시피 이미 2년 전인 PDC 2008에서 윈도우 7의 멀티터치 기능은 대중들에게 활짝 공개 되었습니다. 말 그대로 ‘새로운 기능이라고 부르기도 어색할 만한’, 새기능 아닌 새기능 – 윈도우 7 터치 인식 인터페이스 입니다.

    앞서 잠깐 말씀 드렸던 것과 같이 윈도우 7 멀티터치의 가장 큰 장애요소는 ‘멀티터치 인식 하드웨어’가 아닌가 생각합니다. 하지만 이미 컴퓨터 시장에는 올인원 PC, 타블렛 PC, 터치 인식 노트북 등 다양한 멀티터치 인식 장치들이 쏟아져 나오고 있습니다. 유명한 d모 가격비교 사이트의 스샷을 올려드리고 싶지만, 특정 브랜드명이 다수 노출되어 그럴 수가 없네요 ^^;… 직접 한 번 검색해 보시기 바랍니다. 분명하게 말씀 드릴 수 있는 것은 향후 적지 않은 시간 동안 ‘멀티터치’ 분야는 하드웨어와 소프트웨어를 막론하고 컴퓨터 산업의 커다란 이슈가 될 것이라는 점 입니다. 아니, 이미 멀티터치는 커다란 이슈가 되었습니다.

    앞으로 팀블로그나 여러가지 활동을 통해서, 윈도우 7의 멀티터치 기능을 소개하는 기회를 가지려고 합니다. 그리고 이러한 무궁한 가능성을 가진 멀티터치를 Visual Studio 2010을 통해 Windows 7의 응용프로그램에 적용하는 방법을 시리즈로 연재하고자 합니다. WPF와 같은 Managed 언어로도 구현 가능하지만 제가 진행할 시리즈들은 MFC와 Win32를 이용한 터치 프로그래밍 방법이 주요 내용이 될 것입니다.

    그럼 앞으로 연재할 멀티터치 구현 포스팅에 많은 관심 부탁 드리면서
    다음 포스팅에서 뵙도록 하겠습니다.

    모두 오늘 좋은 하루 보내세요 ^^*