일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
1 | 2 | |||||
3 | 4 | 5 | 6 | 7 | 8 | 9 |
10 | 11 | 12 | 13 | 14 | 15 | 16 |
17 | 18 | 19 | 20 | 21 | 22 | 23 |
24 | 25 | 26 | 27 | 28 | 29 | 30 |
- Trie
- DP
- union find
- binary search
- two pointer
- Brute Force
- Hash
- String
- 그래프
- SQL
- 다익스트라
- Dijkstra
- 스토어드 프로시저
- Two Points
- Stored Procedure
- 이진탐색
- MYSQL
- Today
- Total
codingfarm
4. 입력 - 마우스 입력 본문
docs.microsoft.com/en-us/windows/win32/inputdev/mouse-input
Mouse
마우스란 일반적으로 쓰는 쥐처럼 생긴 마우스와 노트북의 터치패드, 트랙볼과 그림용 타블릿 등을 모두 포함하는 명칭이다.
마우스 입력에 관한 메시지는 아래와 같은 종류가 있다.
버튼 | 누름 | 놓음 | 더블클릭 |
좌측 | WM_LBUTTONDOWN | WM_LBUTTONUP | WM_LBUTTONDBLCLK |
우측 | WM_RBUTTONDOWN | WM_RBUTTONUP | WM_RBUTTONDBLCLK |
중앙 | WM_MBUTTONDOWN | WM_MBUTTONUP | WM_MBUTTONDBLCLK |
마우스 메시지는 lParam의 상위 워드에 마우스 버튼이 눌러진 y좌표, 하위워드에 x좌표를 가지며 좌표값을 검출해 내기 위해 HIWORD, LOWORD 등의 매크로 함수를 사용한다. 즉 마우스 메시지가 발생한 위치의 좌표는 (LOWORD(lParam), HIWORD(lParam))이 된다.
좌표값은 경우에 따라 음수가 쓰일 수 있다. 가령 98이후에는 2개 이상의 모니터가 사용될때 오른쪽 모니터 입장에서 보면 좌표값이 음수가 되는 경우가 있다. 이때는 반드시 (int)나 (short)형으로 캐스팅하여 부호를 제대로 변환해야 한다. 양수좌표만 전달되는 경우가 보장된다면 캐스팅할 필요는 없다.
wParam에는 마우스 버튼의 상태와 키보드 조합키(Shift, Ctrl)의 상태가 전달된다. 조합키 상태는 다음 값들과 비트 연산을 해보면 알 수 있다. 이 값을 참조하면 쉬프트 클릭, 좌우 동시 누름 등의 조건을 검출할 수 있다.
값 | 설명 |
MK_CONTROL | Ctrl 키가 눌러져 있다. |
MK_LBUTTON | 마우스 왼쪽 버튼이 눌러져 있다. |
MK_RBUTTON | 마우스 오른쪽 버튼이 눌러져 있다. |
MK_MBUTTON | 마우스 중간 버튼이 눌러져 있다. |
MK_SHIFT | Shift 키가 눌러져 있다. |
WM_MOUSEMOVE
마우스가 이동할때마다 전달되는 메시지이다. 이 메시지도 다른 마우스 메시지와 마찬가지로 lParam에 마우스 커서의 위치가 전달되며 wParam에 조합티 상태가 전달된다.
WM_MOUSEWHEEL
마우스 휠이 회전할때 전달되는 메시지이다.
메시지의 추가정보로 전달되는 wParam, lParam은 둘다 32비트 크기를 가지므로 총 64비트의 정보를 전달할 수 있다. 인수의 개수는 둘인데 좀 더 많은 정보를 전달해야 하는 메시지들이 있을 경우 32비트 인수의 상하위 워드나 바이트를 잘라 여러개의 인수를 합쳐서 전달한다. 그러므로 받는쪽에서는 wParam과 lParam의 정보를 잘 파싱하여 써야한다.
아래와 같은 매크로를 사용하여 개별정보를 분리해 낸다.
1
2
3
4
|
#define LOWORD(l) ((WORD)(l))
#define HIWORD(l) ((WORD)(((DWORD)(l)) >> 16) & 0xFFFF ) // 상위 16개의 값을 가저온다.
#define LOBYTE(w) ((BYTE)(w))
#define HIBYTE(w) ((BYTE)((WORD)(w) >> 8) & 0xFF)) // 하위 8개의 값만 남긴다.
|
cs |
2개의 16비트 값을 가지고 32비트 값을 조립해야 할 경우 아래의 매크로를 사용한다.
1
2
|
#define MAKEWORD(a, b) ((WORD)(((BYTE)(a)) | ((WORD)((BYTE)(b))) << 8))
#define MAKELONG(a, b) ((LONG)(((WORD)(a)) | ((DWORD)((WORD)(b))) << 16))
|
cs |
MAKEWORD : 2개의 byte로 16bit 값을 만든다.
MAKELONG : 2개의 16bit 값으로 32bit 값을 만든다.
이외에 MAKEWPARAM과 MMAKELPARAM 매크로가 정의되어 있는데 메시지를 직접 보내고자 할때 파라미터 조립에 가끔 사용된다.
가령 (123, 98) 좌표를 lParam에 실어 보내려면 MAKELPARAM(123, 98) 매크로를 사용한다.
더블클릭
WM_LBUTTONDBLCLK 메시지를 전달하는것으로 더블클릭에 대한 행동을 정의할 수 있다.
하지만 더블클릭 메시지를 받고자 한다면 윈도우 클래스의 스타일에 아래아 같은 의사표시를 추가해 주어야 한다.
WndClass.style = CS_HREDRAW | CS_VREDRAW | CS_DBLCLKS;
이 플래그를 추가하면 두번째 WM_LBUTTONDOWN 메시지가 WM_LBUTTONDBLCLK 메시지로 변경되어 전달된다.
사용자가 직접 알고리즘을 구성하면 더블클릭 이외에 트리플 클릭의 구현도 가능해진다.
아래는 마우스 메시지를 사용하여 작업영역에 그림을 그리는 예제코드이다.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
|
#include <windows.h>
LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM);
HINSTANCE g_hInst;
LPCTSTR lpszClass = TEXT("Mouse");
int APIENTRY WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance
, LPSTR lpszCmdParam, int nCmdShow)
{
HWND hWnd;
MSG Message;
WNDCLASS WndClass;
g_hInst = hInstance;
WndClass.cbClsExtra = 0;
WndClass.cbWndExtra = 0;
WndClass.hbrBackground = (HBRUSH)GetStockObject(WHITE_BRUSH);
WndClass.hCursor = LoadCursor(NULL, IDC_ARROW);
WndClass.hIcon = LoadIcon(NULL, IDI_APPLICATION);
WndClass.hInstance = hInstance;
WndClass.lpfnWndProc = (WNDPROC)WndProc;
WndClass.lpszClassName = lpszClass;
WndClass.lpszMenuName = NULL;
WndClass.style = CS_HREDRAW | CS_VREDRAW | CS_DBLCLKS;
RegisterClass(&WndClass);
hWnd = CreateWindow(lpszClass, lpszClass, WS_OVERLAPPEDWINDOW,
CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT,
NULL, (HMENU)NULL, hInstance, NULL);
ShowWindow(hWnd, nCmdShow);
while (GetMessage(&Message, 0, 0, 0)) {
TranslateMessage(&Message);
DispatchMessage(&Message);
}
return Message.wParam;
}
LRESULT CALLBACK WndProc(HWND hWnd, UINT iMessage, WPARAM wParam, LPARAM lParam)
{
HDC hdc;
static int x;
static int y;
static BOOL bnowDraw = FALSE;
switch (iMessage) {
case WM_LBUTTONDOWN:
x = LOWORD(lParam);
y = HIWORD(lParam);
bnowDraw = TRUE;
return 0;
case WM_LBUTTONDBLCLK:
InvalidateRect(hWnd, NULL, TRUE);
return 0;
case WM_MOUSEMOVE:
if (bnowDraw == TRUE) {
hdc = GetDC(hWnd);
MoveToEx(hdc, x, y, NULL);
x = LOWORD(lParam);
y = HIWORD(lParam);
LineTo(hdc, x, y);
ReleaseDC(hWnd, hdc);
}
return 0;
case WM_LBUTTONUP:
bnowDraw = FALSE;
return 0;
case WM_DESTROY:
PostQuitMessage(0);
return 0;
}
return(DefWindowProc(hWnd, iMessage, wParam, lParam));
}
|
cs |
좌클릭 상태에서 마우스를 움직일때마다. 즉, 선을 그릴때마다 DC 핸들을 발급받는것 이외에는 알고리즘이 단순하다.
WM_PAINT 메시지 이외의 부분에서 화면에 출력해야 할 때는 GetDC함수를 호출하여 DC 핸들을 발급 받아야 함을 유의한다.
주의할점은 창을 최소화 시켰다가 다시 키우면 기존의 그림들이 모두다 사라진다. WM_PAINT에 관한 처리를 하지 않았기 때문이다. 그래서 더블클릭으로 InvalidateRect만 호출해도 화면을 지울 수 있다. 그림을 보존하기 위해서는 그려지는 그림을 일일이 저장해야 하는데, 화면 전체를 일일이 비트맵으로 저장하던가 아니면 연결리스트를 통해 마우스의 움직임을 일일이 저장해야한다. 이런 의문점은 앞으로 차차 공부해 나가도록 한다.
비작업영역(타이틀 바, 경계선, 메뉴, 스크롤 바...) 에서의 마우스 메시지는 모두 작업영역 메시지의 이름에 NC(Non Client)가 붙여진다. 가령 비작업영역에서 마우스 왼쪽 버튼을 누르면 WM_NCLBUTTONDOWN 메시지가 발생한다. 비 작업영역 메시지는 시스템이 내부적인 용도로 사용한다. 가령 마우스를 경계선에 올렸을때 화살표 모양으로 바뀌는 효과가 이에 속한다. 이런 메시지의 처리는 DefWindowProc에서 도맡아 하므로 프로그램이 직접 처리할 필요는 없다. 그러나 꼭 필요할 경우 굳이 이 메시지를 받겠다면 처리한 후 반드시 DefWindowProc으로 보내주어야 한다. 그렇지 않으면 표준 마우스 인터페이스가 동작하지 않는다.
'Windows > 윈도우즈 API' 카테고리의 다른 글
4. 윈도우 관리 메시지 (0) | 2021.04.29 |
---|---|
4. 타이머, 콜백함수 (0) | 2021.01.10 |
4. 입력 - 키보드 입력 (0) | 2020.11.30 |
3. 메시지 비프 (0) | 2020.11.30 |
3. 메시지 박스 (Message Box) (0) | 2020.11.23 |