다음을 통해 공유


마우스 이동

마우스가 이동하면 Windows에서 WM_MOUSEMOVE 메시지를 게시합니다. 기본적으로 WM_MOUSEMOVE는 커서가 포함된 창으로 이동합니다. 마우스를 캡처하여 이 동작을 재정의할 수 있으며 이 내용은 다음 섹션에서 설명합니다.

WM_MOUSEMOVE 메시지에는 마우스 클릭에 대한 메시지와 동일한 매개 변수가 포함됩니다. lParam의 하위 16비트에는 x 좌표가 포함되고 다음 16비트에는 y 좌표가 포함됩니다. GET_X_LPARAMGET_Y_LPARAM 매크로를 사용하여 lParam에서 좌표를 분석합니다. wParam 매개 변수에는 다른 마우스 단추의 상태와 Shift 및 Ctrl 키를 나타내는 비트 OR 플래그가 포함되어 있습니다. 다음 코드는 lParam에서 마우스 좌표를 가져옵니다.

int xPos = GET_X_LPARAM(lParam); 
int yPos = GET_Y_LPARAM(lParam);

이러한 좌표는 DIP(장치 독립적 픽셀)가 아닌 픽셀 단위입니다. 이 항목의 뒷부분에서는 두 단위 간을 변환하는 코드를 살펴보겠습니다.

창을 기준으로 커서의 위치가 변경되면 창에서 WM_MOUSEMOVE 메시지를 받을 수도 있습니다. 예를 들어 커서가 창 위에 배치되고 사용자가 창을 숨기면 마우스가 움직이지 않더라도 창이 WM_MOUSEMOVE 메시지를 수신합니다. 이 동작의 한 가지 결과는 마우스 좌표가 WM_MOUSEMOVE 메시지 간에 변경되지 않을 수 있다는 것입니다.

창 밖에서 마우스 이동 캡처

기본적으로 창은 마우스가 클라이언트 영역의 가장자리를 지나 이동하는 경우 WM_MOUSEMOVE 메시지 수신을 중지합니다. 그러나 일부 작업의 경우 이 지점을 벗어나는 마우스 위치를 추적해야 할 수 있습니다. 예를 들어 그리기 프로그램을 사용하면 사용자가 다음 다이어그램과 같이 선택 영역 사각형을 창 가장자리 너머로 끌 수 있습니다.

마우스 캡처의 일러스트레이션

창 가장자리를 지나 마우스 이동 메시지를 받으려면 SetCapture 함수를 호출합니다. 이 함수가 호출되면 마우스가 창 밖으로 이동하더라도 사용자가 마우스 단추를 하나 이상 누르는 동안에는 창에서 WM_MOUSEMOVE 메시지가 계속 수신됩니다. 캡처 창은 포그라운드 창이어야 하며 한 번에 하나의 창만 캡처 창이 될 수 있습니다. 마우스 캡처를 해제하려면 ReleaseCapture 함수를 호출합니다.

일반적으로 다음과 같은 방법으로 SetCaptureReleaseCapture를 사용합니다.

  1. 사용자가 마우스 왼쪽 단추를 누르면 SetCapture를 호출하여 마우스 캡처를 시작합니다.
  2. 마우스 이동 메시지에 응답합니다.
  3. 사용자가 마우스 왼쪽 단추를 놓으면 ReleaseCapture를 호출합니다.

예: 원 그리기

사용자가 마우스로 원을 그릴 수 있도록 하여 모듈 3의 Circle 프로그램을 확장해 보겠습니다. Direct2D Circle 샘플 프로그램으로 시작합니다. 간단한 그리기를 추가하도록 이 샘플의 코드를 수정합니다. 먼저 MainWindow 클래스에 새 멤버 변수를 추가합니다.

D2D1_POINT_2F ptMouse;

이 변수는 사용자가 마우스를 끄는 동안 마우스 누름 위치를 저장합니다. MainWindow 생성자에서 ellipseptMouse 변수를 초기화합니다.

    MainWindow() : pFactory(NULL), pRenderTarget(NULL), pBrush(NULL),
        ellipse(D2D1::Ellipse(D2D1::Point2F(), 0, 0)),
        ptMouse(D2D1::Point2F())
    {
    }

MainWindow::CalculateLayout 메서드의 본문을 제거합니다. 이 예제에서는 필요하지 않습니다.

void CalculateLayout() { }

다음으로, 왼쪽 단추 누름, 왼쪽 단추 놓음 및 마우스 이동 메시지에 대한 메시지 처리기를 선언합니다.

void OnLButtonDown(int pixelX, int pixelY, DWORD flags);
void OnLButtonUp();
void OnMouseMove(int pixelX, int pixelY, DWORD flags);

마우스 좌표는 실제 픽셀로 제공되지만 Direct2D에는 DIP(장치 독립적 픽셀)로 제공되어야 합니다. 높은 DPI 설정을 올바르게 처리하려면 픽셀 좌표를 DIP로 변환해야 합니다. DPI에 대한 자세한 내용은 DPI 및 장치 독립적 픽셀을 참조하세요. 다음 코드에서는 픽셀을 DIP로 변환하는 도우미 클래스를 보여 줍니다.

class DPIScale
{
    static float scale;

public:
    static void Initialize(HWND hwnd)
    {
        float dpi = GetDpiForWindow(hwnd);
        scale = dpi/96.0f;
    }

    template <typename T>
    static D2D1_POINT_2F PixelsToDips(T x, T y)
    {
        return D2D1::Point2F(static_cast<float>(x) / scale, static_cast<float>(y) / scale);
    }
};

float DPIScale::scale = 1.0f;

Direct2D 팩터리 개체를 만든 후 WM_CREATE 처리기에서 DPIScale::Initialize를 호출합니다.

case WM_CREATE:
    if (FAILED(D2D1CreateFactory(D2D1_FACTORY_TYPE_SINGLE_THREADED, &pFactory)))
    {
        return -1;  // Fail CreateWindowEx.
    }
    DPIScale::Initialize(hwnd);
    return 0;

마우스 메시지에서 DIP의 마우스 좌표를 얻으려면 다음을 수행합니다.

  1. GET_X_LPARAMGET_Y_LPARAM 매크로를 사용하여 픽셀 좌표를 가져옵니다. 이러한 매크로는 WindowsX.h에서 정의되므로 프로젝트에 해당 헤더를 포함해야 합니다.
  2. 를 호출 DPIScale::PixelsToDips 하여 픽셀을 DIP로 변환합니다.

이제 창 프로시저에 메시지 처리기를 추가합니다.

case WM_LBUTTONDOWN: 
    OnLButtonDown(GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam), (DWORD)wParam);
    return 0;

case WM_LBUTTONUP: 
    OnLButtonUp();
    return 0;

case WM_MOUSEMOVE: 
    OnMouseMove(GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam), (DWORD)wParam);
    return 0;

마지막으로 메시지 처리기 자체를 구현합니다.

왼쪽 단추 누름

왼쪽 단추 누름 메시지의 경우 다음을 수행합니다.

  1. SetCapture를 호출하여 마우스 캡처를 시작합니다.
  2. 마우스 클릭 위치를 ptMouse 변수에 저장합니다. 이 위치는 타원에 대한 경계 상자의 왼쪽 위 모서리를 정의합니다.
  3. 타원 구조를 다시 설정합니다.
  4. InvalidateRect를 호출합니다. 이 함수는 창을 강제로 다시 페인팅합니다.
void MainWindow::OnLButtonDown(int pixelX, int pixelY, DWORD flags)
{
    SetCapture(m_hwnd);
    ellipse.point = ptMouse = DPIScale::PixelsToDips(pixelX, pixelY);
    ellipse.radiusX = ellipse.radiusY = 1.0f; 
    InvalidateRect(m_hwnd, NULL, FALSE);
}

마우스 이동

마우스 이동 메시지의 경우 마우스 왼쪽 단추가 누름 상태인지 여부를 확인합니다. 이 경우 타원을 다시 계산하고 창을 다시 페인팅합니다. Direct2D에서 타원은 중심점과 x 및 y 반경으로 정의됩니다. 마우스 누름 포인트(ptMouse)와 현재 커서 위치(x, y)로 정의된 경계 상자에 맞는 타원을 그리려고 하므로 타원의 너비, 높이 및 위치를 찾으려면 약간의 산술 연산이 필요합니다.

다음 코드는 타원을 다시 계산한 다음, InvalidateRect를 호출하여 창을 다시 페인팅합니다.

x 및 y 반경의 타원을 보여 주는 다이어그램

void MainWindow::OnMouseMove(int pixelX, int pixelY, DWORD flags)
{
    if (flags & MK_LBUTTON) 
    { 
        const D2D1_POINT_2F dips = DPIScale::PixelsToDips(pixelX, pixelY);

        const float width = (dips.x - ptMouse.x) / 2;
        const float height = (dips.y - ptMouse.y) / 2;
        const float x1 = ptMouse.x + width;
        const float y1 = ptMouse.y + height;

        ellipse = D2D1::Ellipse(D2D1::Point2F(x1, y1), width, height);

        InvalidateRect(m_hwnd, NULL, FALSE);
    }
}

왼쪽 단추 놓음

왼쪽 단추 놓음 메시지의 경우 ReleaseCapture를 호출하여 마우스 캡처를 해제합니다.

void MainWindow::OnLButtonUp()
{
    ReleaseCapture(); 
}

다음