풍쿠 winapi 게임프로그래밍 3강 키다운 키업





// winmain.cpp


#define WIN32_LEAN_AND_MEAN


#include <windows.h>


//함수 프로토타입

// Function prototypes

int WINAPI WinMain(HINSTANCE, HINSTANCE, LPSTR, int); 

bool CreateMainWindow(HINSTANCE, int);

LRESULT WINAPI WinProc(HWND, UINT, WPARAM, LPARAM); 


//전역 변수

// Global variables

HINSTANCE hinst;

HDC    hdc;                 // 디바이스 컨텍스트의 핸들 handle to device context

TCHAR ch = ' ';             // 입력한 문자 character entered

RECT rect;                  // 사각형 구조체 rectangle

PAINTSTRUCT ps;             // WM_PAINT 에서 사용 used in WM_PAINT

bool vkKeys[256];           // 가상 키들의 상태 state of virtual keys, 거짓 혹은 참 false or true


//상수

// Constants

const char CLASS_NAME[]  = "Keyboard";

const char APP_TITLE[]   = "Keys Down"; //제목 표시줄의 텍스트 

const int  WINDOW_WIDTH  = 400;  // 윈도우의 폭 width of window

const int  WINDOW_HEIGHT = 400;  // 윈도우의 높이 height of window


//=============================================================================

// 윈도우 애플리케이션의 시작 위치 Starting point for a Windows application

//=============================================================================

int WINAPI WinMain(HINSTANCE hInstance,

                   HINSTANCE hPrevInstance,

                   LPSTR     lpCmdLine,

                   int       nCmdShow)

{

    MSG     msg;


//메인 윈도우 생성 

    // Create the main window

    if (!CreateMainWindow(hInstance, nCmdShow))

        return false;


    for (int i=0; i<256; i++)   // 가상 키 배열 초기화 initialize virtual key array

        vkKeys[i] = false;


//메인 메시지 루프 

    // main message loop

    int done = 0;

    while (!done)

    {

        if (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE)) 

        {

//종료 메시지 찾는다.

            //look for quit message

            if (msg.message == WM_QUIT)

                done = 1;


//해석한뒤 메시지를 WinProc에 전달한다. 

            //decode and pass messages on to WinProc

            TranslateMessage(&msg);

            DispatchMessage(&msg);

        }

    }


    return msg.wParam;

}


//=============================================================================

// 윈도우 이벤트 콜백 함수 window event callback function

//=============================================================================

LRESULT WINAPI WinProc( HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam )

{

    short nVirtKey;                 // 가상 키 코드 virtual-key code 

    const short SHIFTED = (short)0x8000; 

    TEXTMETRIC tm;                  // 텍스트 매트릭스의 구조체 structure for text metrics 

    DWORD chWidth = 20;             // 문자들의 폭 width of characters

    DWORD chHeight = 20;            // 문자들의 높이 height of characters


    switch( msg )

    {

        case WM_CREATE:

//텍스트 매트릭스를 가져온다. 

            // get the text metrics

            hdc = GetDC(hwnd);

            GetTextMetrics(hdc, &tm);

            ReleaseDC(hwnd, hdc);

            chWidth = tm.tmAveCharWidth;        // 평균 문자 폭 average character width

            chHeight = tm.tmHeight;             // 문자 높이 character height

            return 0;


        case WM_DESTROY:

            //윈도우에 말한다 이 프로그램 죽이라고 tell Windows to kill this program

            PostQuitMessage(0);

            return 0;


        case WM_KEYDOWN:                                // 키를 누른 경우 key down

            vkKeys[wParam] = true;

            switch(wParam)

            {

                case VK_SHIFT:                          // 쉬프트 shift 키 key

                    nVirtKey = GetKeyState(VK_LSHIFT);  // 왼쪽 쉬프트의 상태 를 가져온다 get state of left shift

                    if (nVirtKey & SHIFTED)             // 만약 왼쪽 쉬프트를 눌렀다면 if left shift

                        vkKeys[VK_LSHIFT] = true;

                    nVirtKey = GetKeyState(VK_RSHIFT);  // 오른쪽 쉬프트의 상태를 가져온다. get state of right shift

                    if (nVirtKey & SHIFTED)             // 만약 오른쪽 쉬프트를 눌렀다면 if right shift

                        vkKeys[VK_RSHIFT] = true;

                    break;

                case VK_CONTROL:                        // 컨트롤 키 control key

                    nVirtKey = GetKeyState(VK_LCONTROL);

                    if (nVirtKey & SHIFTED)             // 만약 왼쪽 컨트롤을 눌렀다면 if left control

                        vkKeys[VK_LCONTROL] = true;

                    nVirtKey = GetKeyState(VK_RCONTROL);

                    if (nVirtKey & SHIFTED)             // 만약 오른쪽 컨트롤을 눌렀다면 if right control

                        vkKeys[VK_RCONTROL] = true;

                    break;

            }

            InvalidateRect(hwnd, NULL, TRUE);           // WM_PAINT를 강제한다. force WM_PAINT

            return 0;

            break;


        case WM_KEYUP:                                  // 키를 뗀 경우 key up

            vkKeys[wParam] = false;

            switch(wParam)

            {

                case VK_SHIFT:                          // 쉬프트 키 shift key

                    nVirtKey = GetKeyState(VK_LSHIFT); 

                    if ((nVirtKey & SHIFTED) == 0)      // 만약 왼쪽 쉬프트를 눌렀다면 if left shift

                        vkKeys[VK_LSHIFT] = false;

                    nVirtKey = GetKeyState(VK_RSHIFT); 

                    if ((nVirtKey & SHIFTED) == 0)      // 만약 오른쪽 쉬프트를 눌렀다면 if right shift

                        vkKeys[VK_RSHIFT] = false;

                    break;

                case VK_CONTROL:                        // 컨트롤 키 control key

                    nVirtKey = GetKeyState(VK_LCONTROL);

                    if ((nVirtKey & SHIFTED) == 0)      // 만약 왼쪽 컨트롤을 눌렀다면 if left control

                        vkKeys[VK_LCONTROL] = false;

                    nVirtKey = GetKeyState(VK_RCONTROL);

                    if ((nVirtKey & SHIFTED) == 0)      // 만약 오른쪽 컨트롤을 눌렀다면 if right control

                        vkKeys[VK_RCONTROL] = false;

                    break;

            }

            InvalidateRect(hwnd, NULL, TRUE);    // WM_PAINT 를 강제한다. force WM_PAINT

            return 0;

            break;


        case WM_CHAR:               // 한 문자가 키보드에 눌러졌다면 a character was entered by the keyboard

            switch (wParam)         // 그 문자는 더블유파람에 들어간다 the character is in wParam

            {

                case 0x08:              // 백스페이스backspace

                case 0x09:              // 탭tab

                case 0x0A:              // 라인피드linefeed

                case 0x0D:              // 캐리지 리턴 carriage return

                case 0x1B:              // 이스케이프escape

                    return 0;           // 비 표시 문자 non displayable character

                default:                // 표시 문자 displayable character

                    ch = (TCHAR) wParam;    // 문자 가져오기 get the character

                    InvalidateRect(hwnd, NULL, TRUE);   // WM_PAINT 강제 force WM_PAINT

                    return 0;

            }


        case WM_PAINT:

            hdc = BeginPaint(hwnd, &ps);    // 디바이스 컨텍스트의 핸들을 가져온다 get handle to device context

            TextOut(hdc, 0, 0, &ch, 1);     // 문자를 표시한다 display the character

            

            // 가상키들 배열의 상태를 보여준다 Display the state of vkKeys array

            // 만약 키가 눌러지고 그리고 F 가 키가 떼어지면 'T'를 보여준다. Display 'T' if key is down and 'F' is key is up

            for (int r=0; r<16; r++)

            {

                for (int c=0; c<16; c++)

                {

                    if (vkKeys[r*16+c])

                    {

                        SetBkMode(hdc, OPAQUE);         // 불투명한 텍스트 배경 opaque text background

                        TextOut(hdc,c*chWidth+chWidth*2,r*chHeight+chHeight*2,"T ", 2);

                    } else {

                        SetBkMode(hdc, TRANSPARENT);    // 투명한 텍스트 배경 transparent text background

                        TextOut(hdc,c*chWidth+chWidth*2,r*chHeight+chHeight*2,"F ", 2);

                    }

                }

            }


            EndPaint(hwnd, &ps);

            return 0;


        default:

            return DefWindowProc( hwnd, msg, wParam, lParam );

    }

}


//=============================================================================

// 윈도우 생성 Create the window

// 에러 발생시 false를 반환Returns: false on error

//=============================================================================

bool CreateMainWindow(HINSTANCE hInstance, int nCmdShow) 

    WNDCLASSEX wcx; 

    HWND hwnd;

 

    // 윈도우 클래스 구조체 안을 매개변수로 채운다 Fill in the window class structure with parameters 

    // 메인 윈도우를 묘사하는 that describe the main window. 

    wcx.cbSize = sizeof(wcx);           // 구조체 크기 size of structure 

    wcx.style = CS_HREDRAW | CS_VREDRAW;    // 크기가 변경되면 다시 그린다 redraw if size changes 

    wcx.lpfnWndProc = WinProc;          // 윈도우 프로시저를 가리킨다 points to window procedure 

    wcx.cbClsExtra = 0;                 // 여분의 클래스 메모리 노필요 no extra class memory 

    wcx.cbWndExtra = 0;                 // 여분의 윈도우 메모리 노필요 no extra window memory 

    wcx.hInstance = hInstance;          // 인스턴스의 핸들 handle to instance 

    wcx.hIcon = NULL; 

    wcx.hCursor = LoadCursor(NULL,IDC_ARROW);   // 미리 정의된 화살표 predefined arrow


//배경 브러쉬 

    wcx.hbrBackground = (HBRUSH)GetStockObject(GRAY_BRUSH);    // 회색 배경 브러쉬 gray background brush 

    wcx.lpszMenuName =  NULL;           // 메뉴 리소스의 이름 name of menu resource 

    wcx.lpszClassName = CLASS_NAME;     // 윈도우 클래스의 이름 name of window class 

    wcx.hIconSm = NULL;                 // 작은 클래스 아이콘 small class icon 

 

    // 윈도우 클래스를 등록한다 Register the window class. 

    // RegisterClassEx 함수는 에러가 발생할 경우 0을 반환한다 returns 0 on error.

    if (RegisterClassEx(&wcx) == 0)    // 만약 에러 나면 if error

        return false;


//윈도우 생성 

    // Create window

    hwnd = CreateWindow(

        CLASS_NAME,             // 윈도우 클래스의 이름 name of the window class

        APP_TITLE,              // 제목 표시줄의 텍스트 title bar text

        WS_OVERLAPPEDWINDOW,    // 윈도우 스타일 window style

        CW_USEDEFAULT,          // 윈도우의 기본 수평 위치 default horizontal position of window

        CW_USEDEFAULT,          // 윈도우의 기본 수직 위치 default vertical position of window

        WINDOW_WIDTH,           // 윈도우의 폭 width of window

        WINDOW_HEIGHT,          // 윈도우의 높이 height of the window

        (HWND) NULL,            // 부모 윈도우 없음 no parent window

        (HMENU) NULL,           // 메뉴 없음 no menu

        hInstance,              // 애플리케이션 인스턴스의 핸들 handle to application instance

        (LPVOID) NULL);         // 윈도우 매개변수 없음 no window parameters


    // 만약 윈도우를 생성하는 동안 에러가 발생한다면 if there was an error creating the window

    if (!hwnd)

        return false;


//윈도우를 표시한다

    // Show the window

    ShowWindow(hwnd, nCmdShow);


//윈도우 프로시저에게 WM_PAINT 메시지를 보낸다. 

    // Send a WM_PAINT message to the window procedure

    UpdateWindow(hwnd);

    return true;

}