'input'에 해당되는 글 12건

  1. 2010.07.18 WinApi 타이머2 [SendMessage]
  2. 2010.07.18 WinApi 마우스 입력.
posted by nsakura 2010. 7. 18. 18:39

바로 전 타이머 포스팅을 보면 약간의 문제가 있다.

프로그램이 실행되면 바로 약간 갭이 있는후에 시계가 뜬다

이유가 뭘까?

WM_PAINT가 문제일까 WM_TIMER가 문제일까?

자세히 살펴보자

WM_PAINT는 단지 화면에 글씨를 출력시켜주는 일밖에 안한다.

그러니 문제가 없다고 본다.

그렇다면 WM_TIMER에서 문제일까?

그렇다 WM_TIMER의문제 인데. 이유는 간단!

WM_TIMER에서 현 시간을 조사하는데 걸리는 시간이 1초라는것이다.

간단하게 말하면 WM_TIMER가 프로그램이 실행되고 1초후라는거다.

그러니 1초라는 갭이있는데 이 갭을 없애고 싶다는것이다.

그러기 위해서 사용하는 함수가 SendMessage함수이다.

원형을 보자.

LRESULT SendMessage(HWND hWnd, UINT Msg, WPARAM wParam, LPARAM lParam);

즉 이 함수는 강제적으로 메시지가 발생한 것처럼 만들어주는 함수라고 보면 된다.

첫 번째 인수 윈도우를 말하는것

두 번째 인수 보내고자하는 메세지

세 번째 네 번째 인수는 메세지의 추가 정보인 wParam,lParam이며 보내는 메세지에따라 달라진다.

wParam은 타이머 Id를 보내도록 되어있다.

타이머 포스트의 소스의 일부를

 case WM_CREATE:
  SetTimer(hWnd,1,1000,NULL);
  SendMessage(hWnd,WM_TIMER,1,0);
  return 0;

이렇게 추가하자.

본인이 프로그램을 짤때는 문제는 없으니 책을 보니 두번째 문제가 있다고 하는데.

바로 시간을 갱신할때 화면이 껌뻑거릴수있다고 하는데. 이것에 대한 문재는.

InvalidateRect(hWnd,NULL,true);

두번째 인자가 null이다 즉 전체 화면에 대해 다시 그린다 인데 이게 문제가되어서 화면이 깜빡거릴수있다고 한다.

두번째 인자는 앞 포스팅에서 말했듯 다시그릴 영역 즉 범위를 설정한다고 했다.

코드 전체를 보면

#include <windows.h>

LRESULT CALLBACK WndProc(HWND,UINT,WPARAM,LPARAM);

HINSTANCE g_Inst;
LPCTSTR wndname=TEXT("GFP TIME");

int APIENTRY WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine,int nShowCmd){
 HWND hWnd;
 MSG Message;
 WNDCLASS WndClass;
 g_Inst=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;
 WndClass.lpszClassName=wndname;
 WndClass.lpszMenuName=NULL;
 WndClass.style=CS_HREDRAW|CS_VREDRAW;
 RegisterClass(&WndClass);

 hWnd=CreateWindow(wndname,wndname,WS_OVERLAPPEDWINDOW,CW_USEDEFAULT,CW_USEDEFAULT,CW_USEDEFAULT,CW_USEDEFAULT,NULL,(HMENU)NULL,hInstance,NULL);

 ShowWindow(hWnd,nShowCmd);

 while(GetMessage(&Message,NULL,0,0)){
  TranslateMessage(&Message);
  DispatchMessage(&Message);
 }
 return (int)Message.wParam;

}

LRESULT CALLBACK WndProc(HWND hWnd,UINT iMessage,WPARAM wParam,LPARAM lParam){
 HDC hdc;
 PAINTSTRUCT ps;
 SYSTEMTIME st;
 
 static RECT table={100,100,400,120};

 static TCHAR sTime[128];

 switch(iMessage){
 case WM_DESTROY:
  KillTimer(hWnd,1);
  PostQuitMessage(0);
  return 0;
 case WM_CREATE:
  SetTimer(hWnd,1,1000,NULL);
  SendMessage(hWnd,WM_TIMER,1,0);
  return 0;
 case WM_TIMER:
  GetLocalTime(&st);
  wsprintf(sTime,TEXT("%d : %d: %d"),st.wHour,st.wMinute,st.wSecond);
  InvalidateRect(hWnd,&table,true);
  return 0;
 case WM_PAINT:
  hdc=BeginPaint(hWnd,&ps);
  TextOut(hdc,100,100,sTime,lstrlen(sTime));
  EndPaint(hWnd,&ps);
  return 0;
 }
 return (DefWindowProc(hWnd,iMessage,wParam,lParam));


}


이런식이 되겠다.


'Computer > Win API' 카테고리의 다른 글

WinApi 두개의 타이머  (0) 2010.07.18
WinApi 타이머 확장 문제.  (0) 2010.07.18
WinApi 타이머  (0) 2010.07.18
WinApi 마우스 입력.  (0) 2010.07.18
WinApi WM_KEYDOWN  (0) 2010.07.18
posted by nsakura 2010. 7. 18. 15:12

지금까지 키보드 입력에 대해서 알아봤다.

그런데 우리는 윈도우를 사용하면서 키보드 외로 사용하는 입력 장치가 있는데 바로 마우스 이다.

마우스에 관한 메세지는 다음과 같다.
 버튼 누름  놓음    더블클릭
 좌측  WM_LBUTTONDOWN  WM_LBUTTONUP  WM_LBUTTONDBLCLK
 우측  WM_RBUTTONDOWN  WM_RBUTTONUP  WM_RBUTTONDBLCLK
 중앙  WM_MBUTTONDOWN  WM_MBUTTONUP  WM_MBUTTONDBLCLK
 마우스 이동에 대한 메세지  WM_MOUSEMOVE
 마우스 휠에 대한 메세지  WM_MOUSEWHIEEL


요즘에 나온 기능성 마우스를 보면 기능성 버튼이 달려있는 마우스가 있는데.

그것에 대한 메세지는 WM_XBUTTON*가 있는데 이건 그냥 있다는것만 알아두세요.

마우스 메세지는 lParam의 상위 워드는 Y좌표 하위 워드는 X좌표를 가진다. 좌표값을 알아내기 위해서는

HIWORD,LOWORD 등 메크로 함수를 사용한다.

(LOWORD(lParam),HIWORD(lParam))이 된다.

좌표값은 양수도 있고 음수도 있으니 주의하자.

[이 경우는 모니터가 다중일경우인데 이경우는 (int)(short)형을 다시 캐스팅 해야한다.

헌데... 우리는 키보드와 마우스를 혼합해서 쓰는 경우도 있다.

컨트롤과 마우스이런식으로 말이지.

다음 표를 보고 각 이벤트를 검출 할 수 있다.

 값  설명 
 MK_CONTROL  ctrl 키가 눌러져있다.
 MK_LBUTTON  마우스 왼쪽 버튼이 눌려져있다.
 MK_RBUTTON  마우스 오른쪽 버튼이 눌러져있다.
 MK_MBUTTON  마우스 중앙 버튼이 눌러졌다.
 MK_SHIFT  쉬프트키가 눌러져 있다.


메세지의 추가 정보로 전달하는 wParam,lParam은 둘다 32비트 총 64비트인데.

인수의 개수는 두개인데. 좀 더 많은 정보를 전달할때가 있다.

그래서 32비트 인수의 상위 워드나 바이트를 잘라서 여러개의 인수를 합쳐서 전달하며 메시지를 받는 쪽에는 wParam,lParam에

묶어서 전달되는 값을 잘내서 사용 해야하는데 이럴때는 다음과같이 정의된 매크로를 사용하여 잘라낼수있다.

#define LOWORD(l) ((WORD)(l))
#define HIWORD(l) ((WORD)((DWORD)(l)>>16)&&0xFFFF))
#define LOWBYTE(w) ((BYTE)(w))
#define HIBYTE(w)((BYTE)(((WORD)(w)>>8)&0xFF))

뭐 간단하게 상위 워드와 하위비트를 추출하는 것인데.. C언어 좀 하시는 분이라면 설명 안해도 되고 이 곳에 할만한 문제도 아니다.

위의 메크로는 잘라내는거라면 다시 붙이는 것도 있지 않겠나?

그것은...

#define MAKEWORD(a,b) ((WORD)(((BYTE)(a))|((WORD)((BYTE)(b)))<<8))
#define MAKELONG(a,b)  ((LONG)(((WORD)(a)|((DWORD)((WORD)(b)))<<16))

MAKEWORD는 두 개의 바이트로부터 16비트값을 만든다
MAKELONG은 두 개의 16비트 값으로 32비트 값을 만든다,

이 외에 MAKELONG과 똑같이 정의된

MAKEWPARAM,MAKELPARAM 매크로가 있다.

만일 25,100좌표를 lParam에 실어 보내려면 MAKELPARAM(25,100) 매크로를 사용한다.

그럼 이제 예제를 해보자.

#include <windows.h>
LRESULT CALLBACK WndProc(HWND,UINT,WPARAM,LPARAM);

HINSTANCE gfpisnt;
LPCTSTR winName=TEXT("GFP MOUSE");


int APIENTRY WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nShowCmd){
 HWND hWnd;
 MSG Message;
 WNDCLASS WndClass;
 gfpisnt=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;
 WndClass.lpszClassName=winName;
 WndClass.lpszMenuName=NULL;
 WndClass.style=CS_HREDRAW|CS_VREDRAW;
 
 RegisterClass(&WndClass);

 hWnd=CreateWindow(winName,winName,WS_OVERLAPPEDWINDOW,CW_USEDEFAULT,CW_USEDEFAULT,CW_USEDEFAULT,CW_USEDEFAULT,NULL,(HMENU)NULL,hInstance,NULL);

 ShowWindow(hWnd,nShowCmd);

 while(GetMessage(&Message,NULL,0,0)){
  TranslateMessage(&Message);
  DispatchMessage(&Message);
 }
 return (int)Message.wParam;
}

LRESULT CALLBACK WndProc(HWND hWnd,UINT iMessage,WPARAM wParam,LPARAM lParam){
 HDC hdc;
 static xPos;
 static yPos;

 static BOOL bNowDraw=false;

 switch(iMessage){
 case WM_DESTROY:
  PostQuitMessage(0);
  return 0;
 case WM_LBUTTONDOWN:
  xPos=LOWORD(lParam);
  yPos=HIWORD(lParam);
  bNowDraw=true;
  return 0;
 case WM_LBUTTONUP:
  bNowDraw=false;
  return 0;
 case WM_MOUSEMOVE:
  if(bNowDraw==TRUE){
   hdc=GetDC(hWnd);
   MoveToEx(hdc,xPos,yPos,NULL);
   xPos=LOWORD(lParam);
   yPos=HIWORD(lParam);
   LineTo(hdc,xPos,yPos);
   ReleaseDC(hWnd,hdc);
  }
  return 0;

 }
 return (DefWindowProc(hWnd,iMessage,wParam,lParam));
}



소스 분석을 해보자

마우스 버튼을 누르면 그 지점을 저장한다.
그 부분이.
 case WM_LBUTTONDOWN:
  xPos=LOWORD(lParam);
  yPos=HIWORD(lParam);
  bNowDraw=true;
  return 0;
이부분이 되겠다 그럼 bNowDraw=true 의 역활은. 단순히 버튼을 누르고 있나를 채크하는 부분이다.

마우스를 누른 상태에서 움직이면
 case WM_MOUSEMOVE:
  if(bNowDraw==TRUE){
   hdc=GetDC(hWnd);
   MoveToEx(hdc,xPos,yPos,NULL);
   xPos=LOWORD(lParam);
   yPos=HIWORD(lParam);
   LineTo(hdc,xPos,yPos);
   ReleaseDC(hWnd,hdc);
  }
이부분의 이벤트가 발생하겠지요. 마우스를 누른 상태니 프래그는 true이며 그동안 DC를 얻어 선을 그리는것이다.

마우스 버튼을 때면
 case WM_LBUTTONUP:
  bNowDraw=false;
  return 0;

프래그가 false가 되면서 마우스가 움직임이 발생되는 이벤트에서 조건문에 맞지 않아 아무것도 반응이 없는 것이다.

그런데 WM_PAINT가 아니기 때문에 언커버 되었을경우 지워질수있다. 이부분에 대해서는 차후 설명하겠다.


확장

더블클릭을 하면 화면이 클리어 되는 것을 해보자.

더블클릭은 위의 표를 보듯이. WM_LBUTTONDBLCLK라는게 있었다 그럼

 case WM_LBUTTONDBLCLK:
  InvalidateRect(hWnd,NULL,true);
  return 0;
 }

이렇게 고치면 될 것이다.

"정말? 진짜? 장담 레알?"

답은.. 안된다.

이유는 간단 우리가 만든 윈도우는 아쉽게도 더블클릭을 지원하지 않는다.

그렇다면 더블클릭을 지원하게 만들어 주어야하는데 바로 그것은

WndClass.style=CS_HREDRAW|CS_VREDRAW;

이부분을

WndClass.style=CS_HREDRAW|CS_VREDRAW|CS_DBLCLKS;

이렇게 추가시켜주면~[CS_DBLCLKS] 더블클릭을 지원이 되는 것이다.



'Computer > Win API' 카테고리의 다른 글

WinApi 타이머2 [SendMessage]  (0) 2010.07.18
WinApi 타이머  (0) 2010.07.18
WinApi WM_KEYDOWN  (0) 2010.07.18
WinApi input WM_CHAR  (0) 2010.07.17
WinApi MessageBox  (0) 2010.07.17