학교수업/윈도우즈 프로그래밍

4주차 - KeyDown / static 변수 / UINCODE&MultiByte /선그리기

허세98 2021. 1. 24. 22:53
반응형

KeyDown을 통해 spacebar를 누르면 A와 #을 토글하는 프로그램을 만들어 보자.

방향키 입력시 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비트)

멀티바이트에서  "가ab"       메모리 값
유니코드에서 "가ab" 메모리 값

두 가지 문자체계의 차이점이 보이는가?

유니코드에서는 문자를 표현하는데 2byte(16bit)를 사용한다.

메모리에서 '00ac'는 한글 "가" / '61 00'은 "a"  / '62 00'은 "B"를 나타낸다.

멀티바이트코드에서는 문자 표현에 1byte (8bit)를 사용한다. 싱글바이트(ex. "a")와 멀티바이트(ex. "가")를 바꿔가면서 처리한다.

메모리에서 'b0'은 "ㄱ" /  'a1'은 "ㅏ" / "61"은 "a" / 62 "b"를 나타낸다.

 

멀티바이트코드는 내부에서 유니코드로 움직안다.

다시말해 멀티바이트코드로 명령하더라도 유니코드로 바꿔서 처리하게 된다.

그렇다면 멀티바이트코드를 사용하면 유니코드로 바꾸는 작업을 더 하기 때문에 효율이 떨어질텐데 왜 아직 쓰는걸까?

과거부터 사용하고 있는 미사일시스템이나 나사의 인공위성 등이 아직도 이 멀티바이트코드를 사용하고 있기 때문에

프로그래머들은 멀티바이트 환경이나 유니코드 환경 두 가지에서 다 돌아갈 수 있도록 Neutrul Code를 프로그램을 만든다.

Neutral 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. 무효영역을 콜하고 배경은 지운다.

무효영역에서 배열에 저장된좌표에 다시 그리게 해놔서 그런지 작동하지 않음

 

반응형