웬디의 기묘한 이야기

글 작성자: WENDYS
반응형

마우스 후킹을 이용한 매크로 프로그램 제작

저번 포스팅에 이어 이번에도 마우스 후킹에 관련된 내용입니다.

아직 못 보셨다면 기본 마우스 후킹에 대한 내용을 보시고 보셔도 좋습니다.

https://wendys.tistory.com/109

 

[C/C++] 윈도우 마우스 후킹으로 제스처 인식 프로그램 만들기 (마우스 이동거리 계산하기)

마우스 후킹을 이용한 매크로 프로그램 제작 저번 포스팅에 이어 이번에도 마우스 후킹에 관련된 내용입니다. 아직 못 보셨다면 기본 마우스 후킹에 대한 내용을 보시고 보셔도 좋습니다. https://wendys.tistory...

wendys.tistory.com

저번 시간에 이어서 시작하겠습니다.

저번 포스팅에서는 마우스의 이동 거리를 계산하는 부분까지 처리를 했었는데요 오늘은 그 거리를 이용하여 마우스의 이동 방향을 구하도록 해보겠습니다.

 

마지막으로 저번 시간에 적용된 코드를 먼저 보겠습니다.

최종 적용된 코드를 간단하게 설명을 하자면 마우스 우클릭 상태에서 마우스를 이동하게 되면 값을 계산하여 얼마만큼 이동하였는지 거리를 계산하는 코드입니다.

inline double get_distance(
    __in const POINT p1,
    __in const POINT p2
    ) {
    double distance = 0.0;
    distance = sqrt(pow(p1.x - p2.x, 2) + pow(p1.y - p2.y, 2));

    return distance;
}

LRESULT hook_mouse_callback::new_function(
    __in int code,
    __in WPARAM wparam,
    __in LPARAM lparam
    ) {

    do {
        if (code < HC_ACTION) {
            break;
        }

        MOUSEHOOKSTRUCT* mouse_param = (MOUSEHOOKSTRUCT*)lparam;

        if (wparam == WM_RBUTTONDOWN) {
            _is_right_button_down = true;

            _latest_mouse_point = mouse_param->pt;
        }
        else if (wparam == WM_MOUSEMOVE) {
            if (_is_right_button_down) {

                POINT current_point = mouse_param->pt;

                double distance = get_distance(_latest_mouse_point, current_point);
                if (distance < 50) {
                    break;
                }

                //
                // 방향 결정 ← ↑ ↓ → 목록 추가
                //

                //
                // 기준 포인트를 현재 포인트로 변환
                //

                _latest_mouse_point = current_point;
            }
        }
        else if (wparam == WM_RBUTTONUP) {
            _is_right_button_down = false;
        }
        else {
            break;
        }

    } while (false);

    return ::CallNextHookEx(_hook, code, wparam, lparam);
}

 

distance 값을 구했으니 일정 거리 이상 마우스를 움직이게 되면 그에 따른 방향을 결정할 수 있도록 할 것입니다.

제어 방향은 4방향으로 의 각도는 아래 그림에서 보시는바와 같이 판단이 가능합니다.

 

마우스 움직임에 대한 각도 정의

방향 각도
← LEFT 0˚ ~ 35˚ && 315˚ ~ 360˚
↑ UP 35˚ ~ 135˚
→ RIGHT 135˚ ~ 225˚
↓ DOWN 225˚ ~ 315˚

 

각각의 방향성은 해당 각도로 판단할 수 있습니다.

 

그렇다면 이동 거리에 대해 각도를 구하기 위해선 약간의 수학 공식을 대입하면 됩니다.

역시나 수학들은 잘하시겠지만 귀찮으신 부분을 대신해서 채워드리겠습니다.

double dx = _latest_mouse_point.x - current_point.x;
double dy = _latest_mouse_point.y - current_point.y;

double radian = atan2(dy, dx);
double degree = (radian * 180) / __pi; // 3.141591653589793

if (degree < 0.0) {
    degree = degree + 360.0;
}

if (degree < 35 || degree > 315) {
    // left
}
else if (degree > 135) {
    if (degree > 225) {
        // down
    }
    else {
        // right
    }
}
else {
    // up
}

앞서 구했던 distnace 기준을 통과했다면 해당 벡터 값에 대한 각도를 구하도록 합니다.

사실 수학을 알려드리는 블로그는 아니기 때문에 자세한 설명은 패스하도록 하겠습니다.

 

radian 값을 구한 atan2에 대해 궁금하신 분은 아래 위키디피아를 통해서 확인하실 수 있습니다.

https://en.wikipedia.org/wiki/Atan2

 

atan2 - Wikipedia

atan2(y,x) returns the angle θ between the ray to the point (x,y) and the positive x-axis, confined to (-π, π]. The function atan2 ⁡ ( y , x ) {\displaystyle \operatorname {atan2} (y,x)} or arctan2 ⁡ ( y , x ) {\displaystyle \operatorname {arctan2} (y,x)}

en.wikipedia.org

이제 현재 마우스의 이동 방향까지 구할 수 있게 되었습니다!

문제였던 부분들이 모두 해결되었으며, 이제는 마우스 방향에 대한 리스트를 작성하고 WM_RBUTTONUP Event가 발생했을 때 해당 목록을 기준으로 액션을 동작하기만 하면 됩니다.

 

현재까지 작성된 코드를 보겠습니다.

80%정도의 완성도를 가진 코드 입니다.

inline double get_distance(
    __in const POINT p1,
    __in const POINT p2
    ) {
    double distance = 0.0;
    distance = sqrt(pow(p1.x - p2.x, 2) + pow(p1.y - p2.y, 2));

    return distance;
}

LRESULT hook_mouse_callback::new_function(
    __in int code,
    __in WPARAM wparam,
    __in LPARAM lparam
    ) {

    do {
        if (code < HC_ACTION) {
            break;
        }

        MOUSEHOOKSTRUCT* mouse_param = (MOUSEHOOKSTRUCT*)lparam;

        if (wparam == WM_RBUTTONDOWN) {
            _is_right_button_down = true;

            _latest_mouse_point = mouse_param->pt;
        }
        else if (wparam == WM_MOUSEMOVE) {
            if (_is_right_button_down) {

                POINT current_point = mouse_param->pt;

                double distance = get_distance(_latest_mouse_point, current_point);
                if (distance < __min_distance) {
                    break;
                }

                //
                // 방향 결정 ← ↑ ↓ → 목록 추가
                //

                double dx = _latest_mouse_point.x - current_point.x;
                double dy = _latest_mouse_point.y - current_point.y;

                double radian = atan2(dy, dx);
                double degree = (radian * 180) / __pi;

                if (degree < 0.0) {
                    degree = degree + 360.0;
                }

                if (degree < 35 || degree > 315) {
                    // left
                }
                else if (degree > 135) {
                    if (degree > 225) {
                        // down
                    }
                    else {
                        // right
                    }
                }
                else {
                    // up
                }

                //
                // 해당 방향을 list에 저장
                //


                //
                // 기준 포인트를 현재 포인트로 변환
                //

                _latest_mouse_point = current_point;
            }
        }
        else if (wparam == WM_RBUTTONUP) {
            _is_right_button_down = false;
        }
        else {
            break;
        }

    } while (false);

    return ::CallNextHookEx(_hook, code, wparam, lparam);
}

해당 코드는 아직 완성되지 않았지만 리스트 멤버를 생성해서 방향을 List 형태로 관리하고 WM_RBUTTONUP 시점에 판단할 수 있는 코드를 작성해 보시는 것도 좋을 것 같습니다.

 

TIP을 드릴만한게 딱히 없지만 굳이 드리자면 left, down, right, up 방향은 enum 형태로 선언을 해서 판단을 하는 것이 작업하기가 조금 편할 것으로 보입니다.

enum mouse_gesture : long {
    unknown = 0,
    left,
    right,
    up,
    down
};

 

혹시나 해봤는데 잘 안되시는분은 다음 포스팅을 기다려주세요 :)

 

Sample Download

analog_note_mouse_hook.zip
0.07MB

 

 

 

 

반응형