관리 메뉴

ㄴrㅎnㅂrㄹrㄱi

[강좌 7] 마우스, 타이머 본문

API 관련/API 강좌모음

[강좌 7] 마우스, 타이머

님투 2007. 10. 26. 03:38
반응형

[API]강좌(7)<--마우스,타이머





이번에는 마우스에 관련된 예제 두가지에 대해 알아보겠습니다.
윈도우즈 프로그램은 메시지 프로그램이라고 이미 여러번 설명드렸을 겁니다.
마우스도 마찬가지죠. 사용자가 마우스를 움직이다가 작업영역에 클릭하게
되면 마우스가 클릭되었다는 메시지와 함께 그 좌표가 메시지로 발생됩니다.
우리는 이 메시지를 받아서 원하는 루틴을 실행하기만 하면 됩니다.
도스에서 마우스를 처리해본 유저라면 이것이 얼마나 프로그램을 만드는 사람
에게 편리성을 제공하는지 쉽게 알수 있을 겁니다.
WM_LBUTTONDOWN
마우스의 왼쪽 버튼이 눌리면 위 메시지가 발생됩니다. 눌렸던것이 띄어지면
WM_LBUTTONUP이라는 메시지가 발생되죠. 그러나 이 메시지는 잘 사용하지 않습니
다. 마우스가 눌렸는지가 실제로 많이 사용되는 거죠.
WM_RBUTTONDOWN
오른쪽 버튼이 눌리면 위 메시가 발생됩니다. 메시지 외우기가 쉽죠?
그러면 눌린 좌표는 어떻게 알수 있을까요? 메시지의 마지막 파라미터인 LPARAM 값
으로 알수 있습니다.
하위워드에눌린 X값이 상위워드에 눌린 Y값이 들어갑니다.
nX = LOWORD(lParam);
nY = HIWORD(lParam);
이런식으로 처리해주면 nX에 X좌표가 nY에 Y좌표가 들어갑니다. 자 그러면 실제로
소스를 보도록합시다. 아래 소스는 마우스가 눌렸을때 눌린 좌표에 눌렸다는 것을
출력해주는 예제입니다.
#include <windows.h>
#include <stdio.h>
#include <string.h>
LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM);
int WINAPI WinMain
(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpszArg, int nCmdShow)
{
HWND hWnd;
MSG msg;
WNDCLASS WndClass;
char szAppName[] = "This program is to test mouse";
WndClass.style = NULL;
WndClass.lpfnWndProc = WndProc;
WndClass.cbClsExtra = 0;
WndClass.cbWndExtra = 0;
WndClass.hInstance = hInstance;
WndClass.hIcon = LoadIcon(NULL, IDI_APPLICATION);
WndClass.hCursor = LoadCursor(NULL, IDC_ARROW);
WndClass.hbrBackground = GetStockObject(WHITE_BRUSH);
WndClass.lpszMenuName = NULL;
WndClass.lpszClassName = szAppName;
if(!RegisterClass(&WndClass)) return NULL;
hWnd = CreateWindow(
szAppName,
szAppName,
WS_OVERLAPPEDWINDOW,
CW_USEDEFAULT,
CW_USEDEFAULT,
CW_USEDEFAULT,
CW_USEDEFAULT,
NULL,
NULL,
hInstance,
NULL
);
ShowWindow(hWnd, nCmdShow);
UpdateWindow(hWnd);
while(GetMessage(&msg, NULL, 0, 0))
{
TranslateMessage(&msg);
DispatchMessage(&msg);
}
return msg.wParam;
}
LRESULT CALLBACK WndProc(HWND hWnd, UINT mesg, WPARAM wParam, LPARAM lParam)
{
HDC hDC;
PAINTSTRUCT ps;
static int nX, nY;
static char szMouse[80];
switch(mesg)
{
case WM_PAINT :
hDC = BeginPaint(hWnd, &ps);
TextOut(hDC, nX, nY, szMouse, strlen(szMouse));
EndPaint(hWnd, &ps);
return FALSE;
case WM_LBUTTONDOWN :
nX = LOWORD(lParam);
nY = HIWORD(lParam);
sprintf(szMouse, "Left Mouse Button Down = X : %d, Y : %d",
nX, nY);
InvalidateRect(hWnd, NULL, FALSE);
return FALSE;
case WM_RBUTTONDOWN :
nX = LOWORD(lParam);
nY = HIWORD(lParam);
sprintf(szMouse, "Right Mouse Button Down = X : %d, Y : %d",
nX, nY);
InvalidateRect(hWnd, NULL, FALSE);
return FALSE;
case WM_DESTROY :
PostQuitMessage(0);
return FALSE;
}
return DefWindowProc(hWnd, mesg, wParam, lParam);
}
case WM_PAINT :
hDC = BeginPaint(hWnd, &ps);
TextOut(hDC, nX, nY, szMouse, strlen(szMouse));
EndPaint(hWnd, &ps);
return FALSE;
윈도우가 그려질때 위 메시지가 발생된다고 했죠? 그런데 szMouse에 아무런 값이
없기때문에 처음에는 아무것도 출력되지 않을 겁니다.
case WM_LBUTTONDOWN :
마우스의 왼쪽 버튼이 눌렸을때 처리해주는 구분입니다.
nX = LOWORD(lParam);
nY = HIWORD(lParam);
sprintf(szMouse, "Left Mouse Button Down = X : %d, Y : %d", nX, nY);
눌린 좌표와 문자열을 조합해서 szMouse라는 버퍼에 기억시키고 있습니다.
InvalidateRect(hWnd, NULL, FALSE);
이 함수에 대해서 설명 드렸나요? 기억이 잘 안나네..... 이 함수는 지정한
작업영역의 일부를 다시 그리라는 메시지를 보내는 역할을 합니다. 두번째 파라
미터가 NULL이면 전체 영역을 다시 그리라는 의미이죠. 이 함수를 이용해서
윈도우의 영역을 다시 그리면 어떤 메시지가 발생될까요? WM_PAINT라는 메시지가
발생되겠죠? 왜냐하면 이 메시지는 윈도우가 그려질때 발생하기 때문이죠.
szMouse에 빈 값이 들어가지 않고 sprintf()함수로 넣어준 값이 들어갔기때문에
출력이 제대로 되겠네요. 그렇죠?
return FALSE;
case WM_RBUTTONDOWN :
nX = LOWORD(lParam);
nY = HIWORD(lParam);
sprintf(szMouse, "Right Mouse Button Down = X : %d, Y : %d", nX, nY);
InvalidateRect(hWnd, NULL, FALSE);
return FALSE;
마우스의 오른쪽 버튼이 눌렸을때의 처리 루틴입니다. 어때요? 마우스 처리하는 것
이 별로 어렵지 않죠.
자 이번에는 마우스가 움직일때 발생하는 메시지와 더블클릭했을때 어떤 메시지가
발생하는지에 대해 알아봅시다.
WndClass.style = CS_HREDRAW|CS_VREDRAW|CS_DBLCLKS;
마우스가 더블클릭되었을때 메시지를 받아서 처리할려면 위와같이 윈도우의 스타일
에 CS_DBLCLKS를 지정해 주어야 합니다. CS_HREDRAW|CS_VREDRAW는 우리가 예전부터
지정해준 NULL과 같다고 생각하면 됩니다.
WM_MOUSEMOVE
마우스가 움직이면 위 메시지가 발생되고 lParam에 그 좌표가 저장됩니다.
WM_LBUTTONDBLCLK
마우스의 왼쪽 버튼이 더블클릭되면 위 메세지가 발생되고 오른쪽 버튼이 더블클릭
되면 아래 메시지가 발생됩니다.
WM_RBUTTONDBLCLK
자 그러면 예제를 보도록 합시다. 아래 에제는 마우스가 움직일때 현재좌표를 화면
에 출력해주고 또 더블클릭되었을때 그 좌표에 더블클릭되었다는 것을 표시해주는
예제입니다.
#include <windows.h>
#include <stdio.h>
#include <string.h>
LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM);
int WINAPI WinMain
(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpszArg, int nCmdShow)
{
HWND hWnd;
MSG msg;
WNDCLASS WndClass;
char szAppName[] = "This program is to test mouse";
WndClass.style = CS_HREDRAW|CS_VREDRAW|CS_DBLCLKS;
WndClass.lpfnWndProc = WndProc;
WndClass.cbClsExtra = 0;
WndClass.cbWndExtra = 0;
WndClass.hInstance = hInstance;
WndClass.hIcon = LoadIcon(NULL, IDI_APPLICATION);
WndClass.hCursor = LoadCursor(NULL, IDC_ARROW);
WndClass.hbrBackground = GetStockObject(WHITE_BRUSH);
WndClass.lpszMenuName = NULL;
WndClass.lpszClassName = szAppName;
if(!RegisterClass(&WndClass)) return NULL;
hWnd = CreateWindow(
szAppName,
szAppName,
WS_OVERLAPPEDWINDOW,
CW_USEDEFAULT,
CW_USEDEFAULT,
CW_USEDEFAULT,
CW_USEDEFAULT,
NULL,
NULL,
hInstance,
NULL
);
ShowWindow(hWnd, nCmdShow);
UpdateWindow(hWnd);
while(GetMessage(&msg, NULL, 0, 0))
{
TranslateMessage(&msg);
DispatchMessage(&msg);
}
return msg.wParam;
}
LRESULT CALLBACK WndProc(HWND hWnd,UINT mesg, WPARAM wParam, LPARAM lParam)
{
HDC hDC;
PAINTSTRUCT ps;
static int nX, nY;
static char szMouse[80];
switch(mesg)
{
case WM_PAINT :
hDC = BeginPaint(hWnd, &ps);
TextOut(hDC, nX, nY, szMouse, strlen(szMouse));
EndPaint(hWnd, &ps);
return FALSE;
case WM_MOUSEMOVE :
nX = LOWORD(lParam);
nY = HIWORD(lParam);
sprintf(szMouse, "Mouse Position = X : %d, Y : %d", nX, nY);
InvalidateRect(hWnd, NULL, FALSE);
return FALSE;
case WM_LBUTTONDBLCLK :
nX = LOWORD(lParam);
nY = HIWORD(lParam);
sprintf(szMouse, "Left Mouse Double Click = X : %d, Y : %d",
nX, nY);
InvalidateRect(hWnd, NULL, FALSE);
return FALSE;
case WM_RBUTTONDBLCLK :
nX = LOWORD(lParam);
nY = HIWORD(lParam);
sprintf(szMouse, "Right Mouse Double Click = X : %d, Y : %d",
nX, nY);
InvalidateRect(hWnd, NULL, FALSE);
return FALSE;
case WM_DESTROY :
PostQuitMessage(0);
return FALSE;
}
return DefWindowProc(hWnd, mesg, wParam, lParam);
}
처음 예제와 크게 다른점이 없어서 설명드릴것이 없네요.. 그렇죠?
 
이번에는 타이머에 대해서 알아봅시다.
이 타이머는 게임을 만들거나 또는 시계 프로그램을 만들때 많이 사용
하는것입니다.
원리는 간단합니다.
프로그래머가 10초에 한번씩 신호를 보내라고 코딩하면 윈도우즈는 10초마다 정해
진 메시지를 보냅니다. 그러면 그 루틴에서 원하는 작업을 하면 되죠. 결과적으로
그 프로그램은 10초마다 어떤 작업을 수행하는 프로그램이 되겠네요. 그렇죠?
자 그러면 어떤 함수를 이용하고 어떤 메시지를 이용해서 하는지 알아봅시다.
UINT SetTimer(
HWND hwnd,
UINT idTimer,
UINT uTimeout,
TIMERPROC tmprc
);
위 함수를 이용해서 타이머를 생성할수 있습니다. 두번째 파라미터는 생성한 타이머
의 아이디를 의미합니다. 타이머를 여러개 생성할수 있으니 각 타이머를 구분할때
이 아이디를 참조하면 되겠네요. 세번째 파라미터는 타이머 발생 시간 주기를
지정해주는 것입니다. 예를들어 1000이라고 지정하면 1초에 한번씩 타이머 발생을
의미하는 것입니다. 500이면 0.5초가 되겠죠? 마지막 파라미터는 보통 NULL을 지정
해서 사용합니다. NULL을 지정하지 않고 유저가 만든 함수이름을 지정해 주면 지
정된 시간마다 이 함수를 수행합니다.
WM_TIMER
지정된 시간마다 타이머가 발생되면 WM_TIMER라는 메시지가 발생됩니다.이 메시지
가 발생되려면 SetTimer() 함수의 마지막 파라미터에 NULL을 지정해야 합니다.
타이머의 아이디는 LPARAM값으로 구분하죠. 아래 예제를 보면 알수 있을 겁니다.
BOOL KillTimer(
HWND hwnd,
UINT idEvent
);
타이머를 소멸시킬때 위 함수를 사용합니다. 두번째 파라미터는 소멸 시킬 타이머
의 아이디입니다.
#include <windows.h>
#include <stdio.h>
LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM);
int WINAPI WinMain
(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpszArg, int nCmdShow)
{
HWND hWnd;
MSG msg;
WNDCLASS WndClass;
char szAppName[] = "This program is to test timer";
WndClass.style = NULL;
WndClass.lpfnWndProc = WndProc;
WndClass.cbClsExtra = 0;
WndClass.cbWndExtra = 0;
WndClass.hInstance = hInstance;
WndClass.hIcon = LoadIcon(NULL, IDI_APPLICATION);
WndClass.hCursor = LoadCursor(NULL, IDC_ARROW);
WndClass.hbrBackground = GetStockObject(WHITE_BRUSH);
WndClass.lpszMenuName = NULL;
WndClass.lpszClassName = szAppName;
if(!RegisterClass(&WndClass)) return NULL;
hWnd = CreateWindow(
szAppName,
szAppName,
WS_OVERLAPPEDWINDOW,
CW_USEDEFAULT,
CW_USEDEFAULT,
CW_USEDEFAULT,
CW_USEDEFAULT,
NULL,
NULL,
hInstance,
NULL
);
ShowWindow(hWnd, nCmdShow);
UpdateWindow(hWnd);
while(GetMessage(&msg, NULL, 0, 0))
{
TranslateMessage(&msg);
DispatchMessage(&msg);
}
return msg.wParam;
}
LRESULT CALLBACK WndProc(HWND hWnd, UINT mesg, WPARAM wParam, LPARAM lParam)
{
HDC hDC;
static int n;
static char szTimer[80];
switch(mesg)
{
case WM_CREATE :
SetTimer(hWnd, 100, 1000, NULL);
return FALSE;
case WM_TIMER :
switch(LOWORD(wParam))
{
case 100 :
n++;
hDC = GetDC(hWnd);
TextOut(hDC, 100, 100, szTimer, sprintf(szTimer,
"Count : %d", n));
ReleaseDC(hWnd, hDC);
break;
}
return FALSE;
case WM_DESTROY :
KillTimer(hWnd, 100);
PostQuitMessage(0);
return FALSE;
}
return DefWindowProc(hWnd, mesg, wParam, lParam);
}
자 그러면 소스를 분석해 봅시다.
case WM_CREATE :
SetTimer(hWnd, 100, 1000, NULL);
return FALSE;
윈도우가 생성될때 1초에 한번씩 타이머를 발생시키도록 하고 있군요. 아이디는 100
이네요.
case WM_TIMER :
switch(LOWORD(wParam))
{
case 100 :
n++;
hDC = GetDC(hWnd);
TextOut(hDC, 100, 100, szTimer,
sprintf(szTimer, "Count : %d", n));
ReleaseDC(hWnd, hDC);
break;
}
return FALSE;
타이머의 아이디가 100이면 현재 카운트를 출력하는 간단한 예제죠?
크게 어려운 점은 없을 겁니다.
아래 예제를 보십시요. 이것은 간단하게 디지털 시계를 만든것인데 윈도우즈 프로그
램에 시계가내장되어 있는것을 보면 대부분 아래처럼 구현한 것입니다. 통신 프로
그램을 보면 이런게 많이 들어가죠. 소스 설명은 생략하겠습니다. 한번 보세요.
어렵지는 않을 겁니다.
#include <windows.h>
#include <stdio.h>
#include <time.h>
LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM);
int WINAPI WinMain
(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpszArg, int nCmdShow)
{
HWND hWnd;
MSG msg;
WNDCLASS WndClass;
char szAppName[] = "This program is to test timer";
WndClass.style = NULL;
WndClass.lpfnWndProc = WndProc;
WndClass.cbClsExtra = 0;
WndClass.cbWndExtra = 0;
WndClass.hInstance = hInstance;
WndClass.hIcon = LoadIcon(NULL, IDI_APPLICATION);
WndClass.hCursor = LoadCursor(NULL, IDC_ARROW);
WndClass.hbrBackground = GetStockObject(WHITE_BRUSH);
WndClass.lpszMenuName = NULL;
WndClass.lpszClassName = szAppName;
if(!RegisterClass(&WndClass)) return NULL;
hWnd = CreateWindow(
szAppName,
szAppName,
WS_OVERLAPPEDWINDOW,
CW_USEDEFAULT,
CW_USEDEFAULT,
CW_USEDEFAULT,
CW_USEDEFAULT,
NULL,
NULL,
hInstance,
NULL
);
ShowWindow(hWnd, nCmdShow);
UpdateWindow(hWnd);
while(GetMessage(&msg, NULL, 0, 0))
{
TranslateMessage(&msg);
DispatchMessage(&msg);
}
return msg.wParam;
}
char* TimeCal(void)
{
static int i;
static struct tm *newtime;
static time_t t;
static char cTime[50];
static char *Total;
t = time(NULL);
newtime = localtime(&t);
strcpy(cTime, asctime(newtime));
cTime[strlen(cTime)-1] = '\0';
Total = strtok(cTime, " ");
for(i=0; i<3; i++)
Total = strtok(NULL, " ");
return Total;
}
LRESULT CALLBACK WndProc(HWND hWnd, UINT mesg, WPARAM wParam, LPARAM lParam)
{
HDC hDC;
static char szTimer[100];
switch(mesg)
{
case WM_CREATE :
SetTimer(hWnd, 100, 1000, NULL);
return FALSE;
case WM_TIMER :
switch(LOWORD(wParam))
{
case 100 :
hDC = GetDC(hWnd);
TextOut(hDC, 100, 100, szTimer,
sprintf(szTimer, "Current Time : %s",TimeCal()));
ReleaseDC(hWnd, hDC);
break;
}
return FALSE;
case WM_DESTROY :
KillTimer(hWnd, 100);
PostQuitMessage(0);
return FALSE;
}
return DefWindowProc(hWnd, mesg, wParam, lParam);
}

반응형

'API 관련 > API 강좌모음' 카테고리의 다른 글

[강좌 9] 메뉴2  (0) 2007.10.26
[강좌 8] 메뉴1  (0) 2007.10.26
[강좌 6] 출력부분  (0) 2007.10.26
[강좌 5] 입력부분  (0) 2007.10.26
[강좌 4] 문자열 출력  (0) 2007.10.26
Comments