다음을 통해 공유


비관리 코드에서 관성 처리

이 섹션에서는 관리되지 않는 코드에서 관성 처리를 위해 IInertiaProcessor 인터페이스를 사용하는 방법을 설명합니다.

개요

비관리 코드에서 관성 을 사용하려면 조작 프로세서와 관성 프로세서 모두에 대한 이벤트 싱크를 구현해야 합니다. 먼저 관리되지 않는 코드에 조작 지원 추가 섹션에 설명된 대로 애플리케이션에 조작 지원을 추가합니다. 조작 지원을 사용하려면 제스처 메시지 대신 터치 메시지를 사용하여 이벤트 데이터를 조작 프로세서에 공급해야 합니다. 조작이 작동한 후에는 IInertiaProcessor 인터페이스가 생성할 이벤트에 대해 두 번째 이벤트 싱크를 구현해야 합니다. 또는 IInertiaProcessorIManipulationProcessor 인터페이스에서 생성된 이벤트를 모두 수용하도록 기존 이벤트 싱크를 수정해야 합니다. 이 예제에서는 관리되지 않는 코드에 조작 지원 추가 섹션에 대해 만든 이벤트 싱크에서 시작하고 조작 프로세서 대신 관성 프로세서에서 작동하는 두 번째 생성자를 추가하는 것이 더 쉽습니다. 이렇게 하면 이벤트 싱크 구현이 조작 프로세서 또는 관성 프로세서에 대해 작동할 수 있습니다. 두 번째 생성자를 추가하는 것 외에도 이벤트 싱크에는 조작 입력이 아닌 관성 입력을 기반으로 작업을 수행할지 여부를 나타내는 변수가 있습니다.

조작 프로세서 이벤트 싱크에 관성 지원 추가

다음 코드에서는 새 이벤트 싱크 생성자, IInertiaProcessor 인터페이스에 대한 새 멤버 변수 및 싱크가 관성으로 추정되는지 여부를 나타내는 플래그를 보여 줍니다.

    CManipulationEventSink(IManipulationProcessor *pManip, IInertiaProcessor *pInert, HWND hWnd);
    CManipulationEventSink(IInertiaProcessor *pInert, HWND hWnd);
    IInertiaProcessor*      m_pInert;
    BOOL fExtrapolating; 

클래스 헤더에 추정 여부를 나타내는 새 생성자와 플래그가 있으면 IManipulationProcessor 이벤트 및 IInertiaProcessor 이벤트에 대한 별도의 처리 블록을 갖도록 이벤트 싱크를 구현할 수 있습니다. IManipulationProcessorIInertiaProcessor를 허용하는 생성자는 fExtrapolating 플래그를 false로 설정해야 합니다. 이는 IManipulationProcessor 이벤트 처리기임을 나타냅니다. 다음 코드에서는 IManipulationProcessor 를 사용하는 이벤트 싱크의 생성자를 구현하는 방법을 보여 줍니다.

CManipulationEventSink::CManipulationEventSink(IManipulationProcessor *pManip, IInertiaProcessor *pInert, HWND hWnd)
{
    m_hWnd = hWnd;

    //Set initial ref count to 1.
    m_cRefCount = 1;

    fExtrapolating=FALSE;

    m_pManip = pManip;
    
    m_pInert = pInert;
    
    m_pManip->put_PivotRadius(-1);

    m_cStartedEventCount = 0;
    m_cDeltaEventCount = 0;
    m_cCompletedEventCount = 0;

    HRESULT hr = S_OK;

    //Get the container with the connection points.
    IConnectionPointContainer* spConnectionContainer;
    
    hr = pManip->QueryInterface(
      IID_IConnectionPointContainer, 
          (LPVOID*) &spConnectionContainer
        );
    //hr = manip->QueryInterface(&spConnectionContainer);
    if (spConnectionContainer == NULL){
        // something went wrong, try to gracefully quit
        
    }

    //Get a connection point.
    hr = spConnectionContainer->FindConnectionPoint(__uuidof(_IManipulationEvents), &m_pConnPoint);
    if (m_pConnPoint == NULL){
        // something went wrong, try to gracefully quit
    }

    DWORD dwCookie;

    //Advise.
    hr = m_pConnPoint->Advise(this, &dwCookie);
}

다음 코드에서는 IInertiaProcessor 를 사용하는 이벤트 싱크의 생성자를 구현하는 방법을 보여 줍니다. 이 생성자는 fExtrapolating 플래그를 true로 설정하여 이벤트 싱크 클래스의 이 instance 추정을 수행하고 조작 프로세서 이벤트에서 이전에 수행한 모든 이동 작업을 수행함을 나타냅니다.

CManipulationEventSink::CManipulationEventSink(IInertiaProcessor *pInert, HWND hWnd)
{
    m_hWnd = hWnd;

    m_pInert = pInert;
    //Set initial ref count to 1.
    m_cRefCount = 1;

    fExtrapolating=TRUE;

    m_cStartedEventCount = 0;
    m_cDeltaEventCount = 0;
    m_cCompletedEventCount = 0;

    HRESULT hr = S_OK;

    //Get the container with the connection points.
    IConnectionPointContainer* spConnectionContainer;
    
    hr = pInert->QueryInterface(
      IID_IConnectionPointContainer, 
          (LPVOID*) &spConnectionContainer
        );
    //hr = manip->QueryInterface(&spConnectionContainer);
    if (spConnectionContainer == NULL){
        // something went wrong, try to gracefully quit        
    }

    //Get a connection point.
    hr = spConnectionContainer->FindConnectionPoint(__uuidof(_IManipulationEvents), &m_pConnPoint);
    if (m_pConnPoint == NULL){
        // something went wrong, try to gracefully quit
    }
    DWORD dwCookie;

    //Advise.
    hr = m_pConnPoint->Advise(this, &dwCookie);
}   

참고

조작 프로세서 이벤트 싱크의 이벤트 싱크 클래스 구현은 관성 프로세서에 대한 이벤트 싱크로 재사용됩니다.

 

이제 이 클래스 CManipulationEventSink를 생성할 때 조작 프로세서의 이벤트 싱크 또는 관성 프로세서의 이벤트 싱크로 생성할 수 있습니다. 관성 프로세서 이벤트 싱크로 생성되면 fExtrapolating 플래그가 true로 설정되어 조작 이벤트를 추정해야 함을 나타냅니다.

참고

ManipulationStartedIManipulationProcessorIInertiaProcessor 인터페이스 둘 다에서 발생합니다.

 

조작이 시작되면 IInertiaProcessor 인터페이스 속성이 설정됩니다. 다음 코드에서는 시작 이벤트가 처리되는 방법을 보여 있습니다.

HRESULT STDMETHODCALLTYPE CManipulationEventSink::ManipulationStarted( 
    /* [in] */ FLOAT x,
    /* [in] */ FLOAT y)
{
    m_cStartedEventCount ++;       

    // set origins in manipulation processor
    m_pInert->put_InitialOriginX(x);
    m_pInert->put_InitialOriginY(y);
    
    RECT screenRect;

    HWND desktop = GetDesktopWindow();
    GetClientRect(desktop, &screenRect);

    // physics settings
    // deceleration is units per square millisecond
    m_pInert->put_DesiredDeceleration(.1f);

    // set the boundaries        
    screenRect.left-= 1024;
    m_pInert->put_BoundaryLeft  ( static_cast<float>(screenRect.left   * 100));
    m_pInert->put_BoundaryTop   ( static_cast<float>(screenRect.top    * 100));
    m_pInert->put_BoundaryRight ( static_cast<float>(screenRect.right  * 100));
    m_pInert->put_BoundaryBottom( static_cast<float>(screenRect.bottom * 100));
    
    
    // Elastic boundaries - I set these to 90% of the screen 
    // so... 5% at left, 95% right, 5% top,  95% bottom
    // Values are whole numbers because units are in centipixels
    m_pInert->put_ElasticMarginLeft  (static_cast<float>(screenRect.left   * 5));
    m_pInert->put_ElasticMarginTop   (static_cast<float>(screenRect.top    * 5));
    m_pInert->put_ElasticMarginRight (static_cast<float>(screenRect.right  * 95));
    m_pInert->put_ElasticMarginBottom(static_cast<float>(screenRect.bottom * 95));
    
    
    return S_OK;
}

이 예제에서는 조작 델타를 사용하여 창을 이동합니다. 다음 코드는 델타 이벤트가 처리되는 방법을 보여줍니다.

HRESULT STDMETHODCALLTYPE CManipulationEventSink::ManipulationDelta( 
    /* [in] */ FLOAT x,
    /* [in] */ FLOAT y,
    /* [in] */ FLOAT translationDeltaX,
    /* [in] */ FLOAT translationDeltaY,
    /* [in] */ FLOAT scaleDelta,
    /* [in] */ FLOAT expansionDelta,
    /* [in] */ FLOAT rotationDelta,
    /* [in] */ FLOAT cumulativeTranslationX,
    /* [in] */ FLOAT cumulativeTranslationY,
    /* [in] */ FLOAT cumulativeScale,
    /* [in] */ FLOAT cumulativeExpansion,
    /* [in] */ FLOAT cumulativeRotation)
{
    m_cDeltaEventCount ++;
        
    RECT rect;
            
    GetWindowRect(m_hWnd, &rect);
        
    int oldWidth =  rect.right-rect.left;
    int oldHeight = rect.bottom-rect.top;            

    // scale and translate the window size / position    
    MoveWindow(m_hWnd,                                              // the window to move
        static_cast<int>(rect.left + (translationDeltaX / 100.0f)), // the x position
        static_cast<int>(rect.top + (translationDeltaY/100.0f)),    // the y position
        static_cast<int>(oldWidth * scaleDelta),                    // width
        static_cast<int>(oldHeight * scaleDelta),                   // height
        TRUE);                                                      // redraw
                     
    return S_OK;
}

이 예제에서 조작 완료 이벤트는 IInertiaProcessor 인터페이스에서 Process를 호출하는 타이머를 시작하거나 중지합니다. 다음 코드는 완료된 조작 이벤트가 처리되는 방법을 보여 있습니다.

HRESULT STDMETHODCALLTYPE CManipulationEventSink::ManipulationCompleted( 
    /* [in] */ FLOAT x,
    /* [in] */ FLOAT y,
    /* [in] */ FLOAT cumulativeTranslationX,
    /* [in] */ FLOAT cumulativeTranslationY,
    /* [in] */ FLOAT cumulativeScale,
    /* [in] */ FLOAT cumulativeExpansion,
    /* [in] */ FLOAT cumulativeRotation)
{
    m_cCompletedEventCount ++;

    m_fX = x;
    m_fY = y;

    // place your code handler here to do any operations based on the manipulation   
    
    if (fExtrapolating){
        //Inertia Complete, stop the timer used for processing      
        KillTimer(m_hWnd,0);        
    }else{ 
        // setup velocities for inertia processor
        float vX = 0.0f;
        float vY = 0.0f;
        float vA = 0.0f;
        m_pManip->GetVelocityX(&vX);
        m_pManip->GetVelocityY(&vY);
        m_pManip->GetAngularVelocity(&vA);

        // complete any previous processing
        m_pInert->Complete();
        
        // Reset sets the  initial timestamp
        m_pInert->Reset();
                
        // 
        m_pInert->put_InitialVelocityX(vX);
        m_pInert->put_InitialVelocityY(vY);        
        
        m_pInert->put_InitialOriginX(x);
        m_pInert->put_InitialOriginY(y);
        
           
        // Start a timer
        SetTimer(m_hWnd,0, 50, 0);        
    }

    return S_OK;
}

다음 코드에서는 WndProc에서 WM_TIMER 메시지를 해석하여 IInertiaProcessor 인터페이스에서 Process에 대한 호출을 수행하는 방법을 보여 줍니다.

case WM_TIMER:       
  if (g_pIInertProc){
    BOOL b;       
    g_pIInertProc->Process(&b);        
  }
  break;

관성 프로세서 및 조작 프로세서를 공동 초기화하고 이벤트 싱크 초기화

IManipulationProcessorIInertiaProcessor를 모두 지원하도록 이벤트 싱크를 수정한 후에는 이벤트 싱크를 초기화하고 애플리케이션에서 실행되도록 설정할 준비가 된 것입니다. 다음 코드에서는 인터페이스 포인터가 할당되는 방법을 보여 줍니다.

//Include windows.h for touch events
#include "windows.h"  

// Manipulation implementation file
#include <manipulations_i.c>
    
// Smart Pointer to a global reference of a manipulation processor, event sink
IManipulationProcessor* g_pIManipProc;
IInertiaProcessor*      g_pIInertProc;

다음 코드 예제에서는 인터페이스를 인스턴스화하는 방법을 보여 줍니다.

   HRESULT hr = CoInitialize(0);
        
   hr = CoCreateInstance(CLSID_ManipulationProcessor,
       NULL,
       CLSCTX_INPROC_SERVER,
       IID_IUnknown,
       (VOID**)(&g_pIManipProc)
   );
   
   hr = CoCreateInstance(CLSID_InertiaProcessor,
       NULL,
       CLSCTX_INPROC_SERVER,
       IID_IUnknown,
       (VOID**)(&g_pIInertProc)
   );

다음 코드 예제에서는 인터페이스 포인터를 제공 하 고 터치 입력에 대 한 창을 등록 하는 이벤트 싱크를 생성 하는 방법을 보여 줍니다.

   g_pManipulationEventSink = new CManipulationEventSink(g_pIManipProc, g_pIInertProc, hWnd);
   g_pManipulationEventSink = new CManipulationEventSink(g_pIInertProc, hWnd);


   RegisterTouchWindow(hWnd, 0);  

관성