일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
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 |
- Javascript
- 연산자
- Tips프로그래밍강좌
- 이지스퍼블리싱
- c#
- Tips강좌
- Windows
- 배열
- 리뷰
- 지식나눔강좌
- tipssoft
- c++
- Direct2D
- VS ERROR
- Visual Studio
- 김성엽
- Programming
- 백준
- 프로그래밍
- doit코틀린프로그래밍
- 알고리즘
- CS
- Desktop
- 티스토리
- 포인터
- 문법
- Kotlin
- 함수
- c
- Win32
- Yesterday
- Today
- Total
F.R.I.D.A.Y.
Win32 프로그램 생성하기 7: 사진 암호 part2. 마우스 입력 본문
마우스 입력을 받도록 프로그램을 구성해보겠습니다.
마우스 입력
우리는 마우스 왼쪽 버튼[# Windows에서는 물리버튼의 위치를 말하지 않습니다. 오른손잡이는 왼쪽 버튼이 되겠지만, 왼손잡이들에게는 오른쪽 버튼이 되기 때문이죠. 그래서 여기에서 설명하는 버튼도 Windows에서 설명하는 것처럼 왼쪽 버튼이 주버튼, 오른쪽이 보조버튼입니다.]의 입력만 받도록 하겠습니다.
WM_LBUTTONDOWN
마우스 왼쪽 버튼이 눌렸을 때 발생하는 메시지는 WM_LBUTTONDOWN입니다. 이 메시지의 WPARAM과 LPARAM은 다음과 같은 값을 가지고 있습니다.
wParam
마우스 왼쪽 버튼이 눌린 시점에 어떤 가싱 키가 추가로 눌려있는지를 확인할 수 있는 플래그 집합이 들어 있습니다. 이 플래그 집합에서는 CTRL, SHIFT, 마우스 왼쪽, 휠, 오른쪽, 보조버튼 1, 보조버튼 2의 눌림 상태를 확인할 수 있습니다.
lParam
32비트로 구성되어 있습니다. 이 값에서 하위 16비트는 x좌표, 상위 16비트는 y좌표를 가지고 있습니다.
WindowProc에 바인딩하기
WM_PAINT를 처리했던 switch 구문에 아래 코드를 추가해줍니다.
case WM_LBUTTONDOWN:
{
POINT temp;
temp.x = LOWORD(lParam);
temp.y = HIWORD(lParam);
return OnLButtonDown(temp);
}
POINT는 좌표를 저장하는데 최적화된 구조체입니다. 구조체 멤버로 x와 y가 들어있습니다.
LOWORD, HIWORD
코드를 보면 LOWORD와 HIWORD라는 함수[# 사실 매크로입니다.]가 있습니다. 두 매크로 함수는 32비트 값에서 상위/하위 16비트 값을 추출하는 함수입니다. 만일 두 매크로를 이용하지 않았다면 아래의 코드로 대신해야 했습니다.
temp.x = lParam & 0xFFFF;
temp.y = (lParam >> 16) & 0xFFFF;
// temp.y = lParam >> 16 & 0xFFFF;
// 우선순위가 >> 연산자가 높아서 괄호를 사용하지 않아도 되지만
// 더 명확히 우선 순위를 두기 위해서 괄호를 사용했습니다.
더 간편해졌다고 생각할 수 있죠.
OnLButtonDown
이 함수는 마우스 왼쪽이 눌림을 감지했을 때 처리하기 위해 가져온 녀석입니다. 마우스 입력이므로 마우스 클릭이 발생한 좌표를 받기 위해 POINT 타입 인자 하나를 가지고 있습니다.
LRESULT OnLButtonDown(POINT pos){
return 0;
}
제대로 바인딩이 되었는지 확인하기 위해 메시지창을 하나 띄워보겠습니다. OnLButtonDown 함수 안에 넣어주면 됩니다.
MessageBox(nullptr, L"Click", L"ClickMessage", MB_OK);
# MessageBox 각 인자 설명
- HWND hWnd
이 메시지의 소유 윈도우 핸들입니다. 만일 프로그램의 윈도우 핸들을 넘기면, 메시지를 닫지 않았을 때 다른 작업은 할 수 없습니다. - LPCWSTR lpText
메시지에 표시할 설명입니다. - LPCWSTR lpCaption
메시지 타이틀입니다. - UINT uType
메시지 특성입니다. 여기에서 비트 OR 연산으로 메시지의 버튼 종류를 제어하거나 경고/안내 등의 메시지 이미지를 띄울 수 있습니다.
자세한 내용은 MS DOCS를 참고하세요.
메시지에 소유 윈도우 핸들을 입력하지 않았기 때문에 윈도우의 클라이언트 영역[# 타이틀바와 주변 테두리를 제외한 내부 회색 공간]을 누르면 메시지가 계속해서 생성됩니다.
바인딩은 잘된 것을 보았습니다. 이제 사진 암호의 핵심, 잠금을 해제할 때 사용할 위치를 만들겠습니다.
좌표 저장 구조체 ITEM
암호를 구성하기 위해선 두 가지 옵션이 필요합니다. 하나는 암호의 위치를 가리키는 좌표 변수와, 해당 암호를 입력한 뒤 다음 암호로 넘어갈 수 있는가를 확인할 수 있는 변수입니다. 이미지로 설명하면 다음과 같습니다.
윈도우의 각 좌표를 순서대로 클릭하면 접근이 성공하는 것과 같은 설계인 것이죠. 따라서 구조체는 POINT와 bool 변수를 가지고 있도록 구성합니다.
typedef struct _ITEM {
POINT pos;
bool isNext;
}ITEM;
이제 암호 위치를 저장할 변수를 선언합니다. 격자를 50px 단위로 생성했으므로, 편하게 50px 단위로 구성하겠습니다.
ITEM password[5] = {
{50, 50},
{100, 100},
{150, 150},
{200, 200},
{250, 250}
};
여기까지 따라오면 아래와 같은 코드가 구성됩니다.
# 중간 코드 점검
typedef struct _ITEM {
POINT pos;
bool isNext;
}ITEM;
ITEM password[5] = {
{50, 50},
{100, 100},
{150, 150},
{200, 200},
{250, 250}
};
LRESULT OnLButtonDown(POINT pos) {
return 0;
}
오차 범위 만들기
이렇게 구성하고 암호를 비교하는 코드를 구성하면 접근하기 굉장히 어려워집니다. 정확히 해당 공간을 클릭하는 짓을 다섯 번이나 반복해야하니까요. 안그래도 작은 픽셀인데 어떻게 다섯 번이나 할 수 있을까요? 그러니 우리는 오차 범위를 만들어서 지정된 위치와 비슷한 위치를 클릭하면 지정된 위치를 클릭한 것과 같도록 구성해야합니다.
오차 범위를 구성하는 방법은 다양합니다만, 여기에서는 간단히 지정된 점을 기준으로 반지름 r의 원 내부를 클릭했을 때 통과한 것으로 구성하겠습니다.
여기에선 약간의 수학 공식이 필요합니다. 원의 방정식이 말이죠.
이 방정식을 따라 아래의 코드를 구성합니다.
#include <cmath>
// ...
bool isDetect(POINT p1, POINT p2, double range = 20.0) {
double result;
result = pow(p1.x - p2.x, 2) + pow(p1.y - p2.y, 2);
if (result < pow(range, 2)) return true;
return false;
}
pow 함수[# 사용하기 위해서는 math.h나 cmath를 파일 머리에 추가해야합니다. 여기에서는 C++ 기반으로 하는 cmath를 추가했습니다.]는 첫 인자의 n제곱을 반환하는 함수입니다. 두 번째 인자가 n입니다. 기본 값으로 범위 20 안에 들어오면 넘어가는 것으로 했습니다.
OnLButtonDown 구성하기
이제 OnLButtonDown을 구성하겠습니다. 반복문을 이용할 것이며, 처음 파악해야할 것은 이번 입력이 어느번째 입력인가입니다. 총 다섯 번에 걸쳐 순서대로 입력이 이루어져야 합니다.
OnLButtonDown 함수 안에 아래의 내용을 채우겠습니다.
for (int i = 0; i < 5; ++i) {
if (password[i].isNext) continue;
}
password[i]번째의 isNext를 확인해서 넘어갈 수 있다면 다음 체크로 넘어갑니다. 만일 isNext가 false인 변수를 만난다면 해당 변수가 우리가 찾고자 하는 클릭 위치를 가지고 있을 겁니다.
if (isDetect(password[i].pos, pos)) {
password[i].isNext = true;
break;
}
만일 오자 범위 안에 존재한다면 정상으로 판단하고 현재 password의 isNext 요소를 true로 변경해 다음 암호에 접근할 수 있도록 합니다. 만일 정상으로 판단되지 않으면 모든 암호를 취소하고 다시 입력할 수 있도록 아래처럼 isNext를 false를 돌려줍니다.
if (isDetect(password[i].pos, pos)) {
password[i].isNext = true;
break;
}
else {
for (int k = 0; k < i; ++k) {
password[i].isNext = false;
}
break;
}
다음으로 모든 입력이 성공했는지를 확인하는 코드를 작성합니다.
bool isCorrect = true;
for (int i = 0; i < 5; ++i) {
isCorrect = password[i].isNext;
if (!isCorrect) break;
}
if (isCorrect) {
MessageBox(nullptr, L"Correct", L"Login", MB_OK);
for (int i = 0; i < 5; ++i) password[i].isNext = false;
}
password 배열의 모든 isNext가 true인 경우에는 아래의 if 구문이 통과하고 메시지가 나오도록 구성되어 있습니다. 모두 성공한 경우 다시 isNext 변수를 false로 수정해줍니다.
최종 코드
아래 링크에서 "Win32 Basic.cpp" 파일을 검색하세요.
# index
'DEV > C C++' 카테고리의 다른 글
Win32 MainWindow Class 생성하기 (0) | 2021.04.05 |
---|---|
Win32 BaseWindow Class 생성하기 (0) | 2021.04.05 |
Win32 프로그램 생성하기 6: 사진 암호 part1. 격자 생성 (0) | 2021.04.02 |
Win32 프로그램 생성하기 5 (0) | 2021.04.02 |
Win32 프로그램 생성하기 4 (2) | 2021.04.02 |