4주차 - KeyDown / static 변수 / UINCODE&MultiByte /선그리기
KeyDown을 통해 spacebar를 누르면 A와 #을 토글하는 프로그램을 만들어 보자.
2주차에 만든 키보드 입력을 통해 A를 계속 그려가는 프로그램에서 space bar를 눌렀을 때 A에서 #을 그리도록 프로그램을 만들어 보자
BOOL A = true; //space 누를 시 값을 변환 해줄 BOOL 값
LRESULT CALLBACK WndProc(HWND hWnd, UINT iMessage, WPARAM wParam, LPARAM lParam)
{
HDC hdc;
PAINTSTRUCT ps;
static int x = 100;
static int y = 100;
TCHAR* str = TEXT("A");
switch (iMessage) {
case WM_CREATE:
hWndMain = hWnd;
return 0;
case WM_KEYDOWN://사용자가 키보드를 누르면
switch (wParam) { //키보드 입력값이 어떤값인지 wParam으로 전달
case VK_LEFT:
x -= 8;
break;
case VK_RIGHT:
x += 8;
break;
case VK_DOWN:
y += 8;
break;
case VK_UP:
y -= 8;
break;
case VK_SPACE:
if (A) //A가 true일 때
A = false;
else //A가 false일때
A = true;
break;
}
InvalidateRect(hWnd, NULL, FALSE); //무효영역
return 0;
case WM_PAINT:
hdc = BeginPaint(hWnd, &ps);
if (A) // A가 true면 A를 그린다
{
str = TEXT("A");
}
else //A가 false이면 #을 그린다
{
str = TEXT("#");
}
TextOut(hdc, x, y, str, lstrlen(str));
EndPaint(hWnd, &ps);
return 0;
case WM_DESTROY:
PostQuitMessage(0);
return 0;
}
return(DefWindowProc(hWnd, iMessage, wParam, lParam));
}
스페이스 입력시 불의 T / F 값을 변환해준다.
WM_PAINT에서 A=T면 'A'를 그리고 A=F면 '#'을 그린다
static 변수
선언된 함수 내부에서만 접근 할 수 있다.(지역변수)
딱 1회 초기화 되고 프로그램 종료 시까지 메모리에 남아있는다.(전역변수)
위에서 본 지역변수와 전역변수의 특징을 가지고 있는게 static변수이다.
유니코드(16비트)와 멀티바이트(8비트)
두 가지 문자체계의 차이점이 보이는가?
유니코드에서는 문자를 표현하는데 2byte(16bit)를 사용한다.
메모리에서 '00ac'는 한글 "가" / '61 00'은 "a" / '62 00'은 "B"를 나타낸다.
멀티바이트코드에서는 문자 표현에 1byte (8bit)를 사용한다. 싱글바이트(ex. "a")와 멀티바이트(ex. "가")를 바꿔가면서 처리한다.
메모리에서 'b0'은 "ㄱ" / 'a1'은 "ㅏ" / "61"은 "a" / 62 "b"를 나타낸다.
멀티바이트코드는 내부에서 유니코드로 움직안다.
다시말해 멀티바이트코드로 명령하더라도 유니코드로 바꿔서 처리하게 된다.
그렇다면 멀티바이트코드를 사용하면 유니코드로 바꾸는 작업을 더 하기 때문에 효율이 떨어질텐데 왜 아직 쓰는걸까?
과거부터 사용하고 있는 미사일시스템이나 나사의 인공위성 등이 아직도 이 멀티바이트코드를 사용하고 있기 때문에
프로그래머들은 멀티바이트 환경이나 유니코드 환경 두 가지에서 다 돌아갈 수 있도록 Neutrul Code를 프로그램을 만든다.
이러한 Neutral Code로 프로그램을 짜면 어느 환경에서도 돌아가는 프로그램을 만들 수 있다.
InvalidateRect
InvalidateRect(hWnd,NULL,FALSE); //OS WM_PAINT 메시지 발생시켜줘
InvalidateRect명령을 받으면 바로 WM_PAINT을 발생시킨다.
lnInvalidateRect(hWnd, NULL, FALSE); //무효 영역(다시 그려하는 영역)을 발생 시킨다.
-다시말해 윈도우창을 최소화했다다시 띄우는 것과 동일한 이벤트이다.
lnInvalidateRect(hWnd, NULL, TRUE) 그 전에 그려져 있는 배경을 지운다
lnInvalidateRect(hWnd, NULL, FALSE) 배경을 그래도 둔다.
선그리기
#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;
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,NULL,0,0)) {
TranslateMessage(&Message);
DispatchMessage(&Message);
}
return (int)Message.wParam;
}
typedef struct _line{
POINT p[500];
int iCount;
}line;
line lines[1000];
int iTempCount;
int iLines;
LRESULT CALLBACK WndProc(HWND hWnd,UINT iMessage,WPARAM wParam,LPARAM lParam)
{
HDC hdc;
static int x;
static int y;
//static BOOL bNowDraw=FALSE;
PAINTSTRUCT ps;
switch (iMessage) {
case WM_LBUTTONDOWN:
x=LOWORD(lParam);
y=HIWORD(lParam);
// bNowDraw=TRUE;
return 0;
case WM_MOUSEMOVE:
if (wParam & MK_LBUTTON) {//왼쪽버튼을 누른상태로 움직였을때
hdc=GetDC(hWnd);
MoveToEx(hdc,x,y,NULL);//x,y좌표를 옮긴다(펜을옮긴다)
x=LOWORD(lParam);
y=HIWORD(lParam);
lines[iLines].p[iTempCount].x = x;
lines[iLines].p[iTempCount].y = y;
iTempCount++;
LineTo(hdc,x,y);//선을 그린다 //선을그릴때 : MoveTo + LineTo
ReleaseDC(hWnd,hdc);
}
return 0;
case WM_LBUTTONUP:
// bNowDraw=FALSE;
lines[iLines].iCount = iTempCount;
iTempCount = 0;
iLines++;
return 0;
case WM_PAINT: //다시 그려야 된다
hdc = BeginPaint(hWnd, &ps);
for (int i = 0; i < iLines; i++)
{
for (int j = 1; j < lines[i].iCount; j++)
{
MoveToEx(hdc, lines[i].p[j-1].x, lines[i].p[j-1].y, NULL);
LineTo(hdc, lines[i].p[j].x, lines[i].p[j].y);
}
}
EndPaint(hWnd, &ps);
return 0;
case WM_LBUTTONDBLCLK:
InvalidateRect(hWnd, NULL, TRUE);
return 0;
case WM_DESTROY:
PostQuitMessage(0);
return 0;
}
return(DefWindowProc(hWnd,iMessage,wParam,lParam));
}
struct _line의 구조체와 전역변수
lines[0].p[0].x = 0번째 선의 0번째 좌표 x / lines[0].p[0].y = 0번째 선의 0번째 좌표 y
lines[0].iCount = 0번째 선의 점의 갯수 / iLines = 선의 갯수 / iTempCount = iCount의 값을 임시로 가지고 있는 변수
switch문 메시지
case WM_LBUTTONDOWN:
x = LOWORD(lParam);
y = HIWORD(lParam);
return 0;
마우스 왼쪽버튼 클릭 시 x, y좌표를 저장한다.
case WM_MOUSEMOVE:
if (wParam & MK_LBUTTON) {//왼쪽버튼을 누른상태로 움직였을때
hdc = GetDC(hWnd);
MoveToEx(hdc, x, y, NULL);//x,y좌표를 옮긴다(펜을옮긴다)
x = LOWORD(lParam);
y = HIWORD(lParam);
lines[iLines].p[iTempCount].x = x;
lines[iLines].p[iTempCount].y = y;
iTempCount++;
LineTo(hdc, x, y);//선을 그린다 //선을그릴때 : MoveTo + LineTo
ReleaseDC(hWnd, hdc);
}
return 0;
if(wParam(마우스 이동) 그리고 왼쪽버튼클릭) true라면
1. 그리기 도구를 얻어온다.
2. 선은 방금 왼쪽 클릭시 받은 x와 y좌표에서 부터시작한다.
3. x와 y좌표 값 다시 설정
4. 나중에 다시 불러오기 위해 x와 y에 입력한 좌표값을 저장해둔다.
5. 마우스가 움직일때마다 다른 배열에 저장하기 위해 iTempCount를 증가시킨다.
6. 선을 그린다
7. 그리기 도구 반환
case WM_LBUTTONUP:
// bNowDraw=FALSE;
lines[iLines].iCount = iTempCount;
iTempCount = 0;
iLines++;
return 0;
마우스 왼쪽버튼 클릭을 해제했을때
1. 지금 선의 iCount에 iTempCount 값을 저장한다.
2. iTempCount의 값을 초기화한다.
3. 한 선이 끝났기 때문에 다음 선값을 저장하기 위해 선의 갯수를 한개 늘린다.
case WM_PAINT: //다시 그려야 된다
hdc = BeginPaint(hWnd, &ps);
for (int i = 0; i < iLines; i++)
{
MoveToEx(hdc, lines[i].p[0].x, lines[i].p[0].y, NULL);
for (int j = 1; j < lines[i].iCount; j++)
{
LineTo(hdc, lines[i].p[j].x, lines[i].p[j].y);
}
}
EndPaint(hWnd, &ps);
return 0;
OS가 다시 그려야된다는 메시지를 보내면(ex. 윈도우창 최소화)
1. 그리기 도구를 얻고
2. 맨처음 lines[iLines(0)].p[0].x와 lines[iLines(0)].p[0].y값을 불러온다.
두번째 값 lines[0].p[1].x와 lines[0].p[1].y부터 점 갯수(iCount)의 값이 끝날때까지 불러온다.
3. for문 처음으로 돌아가 2번째 선을 그린다. 선의 갯수까지 반복
4. 그리기도구 반납
case WM_LBUTTONDBLCLK:
InvalidateRect(hWnd, NULL, TRUE);
return 0;
더블클릭시 발생하는 이벤트
1. 무효영역을 콜하고 배경은 지운다.
무효영역에서 배열에 저장된좌표에 다시 그리게 해놔서 그런지 작동하지 않음