2020년 7월 1일 수요일

WinAPI - Idling, 키보드, subclassing, superclassing

1. Idling

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

BOOL AllowIdle = TRUE;

for (;;)

{

if (PeekMessage(&Message, NULL, 0, 0, PM_REMOVE)) {

if (Message.message == WM_QUIT) break;

AllowIdle = TRUE;

TranslateMessage(&Message);

DispatchMessage(&Message);

}

else {

if (AllowIdle) {

OnIdle();

AllowIdle = FALSe;

}

WaitMessage();

}

}

cpu 를 다 쓰지 않고 노는 시간에 상대적으로 덜 중요한 작업을 하는 법

이 예제는 그 작업이 한번이면 된다는 가정하에 쓰임

WaitMessage() 는 메시지가 들어올 때까지 대기하는 함수.

2. 키 상태

SHORT GetKeyState(int nVirtKey)

SHORT GetAsyncKeyState(int vKey)

전자는 메시지 입력 시점의 키 상태를 조사

후자는 메시지 처리 시점의 키 상태를 조사

둘다 가상키를 입력으로 받고

눌러져 있으면 촤상위 비트(MSB)가 1로

Caps Locks 같은 토글키가 눌러져 있으면 최하위 비트(LSB)가 1로 설정

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

bool KeyManager::isOnceKeyDown(int key)

{

// 0x8000 이전엔 눌려진 적 없고 호출된 시점에는 눌려져 있는 상태

if (GetAsyncKeyState(key) & 0x8000)

{

if (!this->getKeyDown()[key])

{

this->setKeyDown(key, true);

return true;

}

else this->setKeyDown(key, false);

return false;

}

bool KeyManager::isStayKeyDown(int key)

{

if (GetAsyncKeyState(key) & 0x8000) return true;

else return false;

}

위 코드는 키보드를 한번 누를 때와 쭉 누를 때를 구별하는 코드다.

원리는 256크기의 bool 배열을 만들어서 현 상태를 저장하는 것에 있다. 각 함수는 주기적으로 사용되므로 두 배열은 키보드 상태를 실시간으로 반양한다. 꾹 누를 때는 간단하다. 위의 함수를 쓰기만 하면 된다.

한번 누를 때는 조금 복잡하다. 특정 키가 눌려지지 않을 때는 배열에서 그 부분을 False로 만들어 둔다. 그러면 처음 누르기 시작할 때는 그 배열이 False로 되어있을 것이다. 이 경우를 if 문으로 찾아서 배열을 True로 만들고 처음 눌렀다고 return 한다. 그러면 다음 루프에서 배열은 True로 되어있을 것이고 처음 누른것이 아닌 것이 되어 함수는 False를 리턴한다. 그냥 코드 설명일 뿐이다.

이렇게 메시지를 받지 않는 방식을 Polling 이라고 한다.

3. Sub Classing

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

42

43

44

45

46

47

48

49

50

51

52

53

54

55

56

57

58

#define ID_EDIT1 100

#define ID_EDIT2 101

HWND hEdit1, hEdit2;

WNDPROC OldEditProc;

LRESULT CALLBACK EditSubProc(HWND hWnd, UINT iMessage, WPARAM wParam, LPARAM lParam)

{

switch (iMessage)

{

case WM_KEYDOWN:

if (wParam == VK_RETURN) {

MessageBox(hWnd, TEXT("Enter is Pressed"), TEXT("Edit"), MB_OK);

SetFocus(hWnd);

}

if (wParam == VK_TAB) {

SetFocus(hEdit2);

}

break;

}

return CallWindowProc(OldEditProc, hWnd, iMessage, wParam, lParam);

}

LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)

{

switch (message)

{

case WM_CREATE:

hEdit1 = CreateWindow(TEXT("Edit"), NULL,

WS_CHILD | WS_VISIBLE | WS_BORDER | ES_AUTOHSCROLL,

10, 10, 200, 25, hWnd, (HMENU)ID_EDIT1, hInst, NULL);

hEdit2 = CreateWindow(TEXT("Edit"), NULL,

WS_CHILD | WS_VISIBLE | WS_BORDER | ES_AUTOHSCROLL,

10, 50, 200, 25, hWnd, (HMENU)ID_EDIT2, hInst, NULL);

SetFocus(hEdit1);

OldEditProc = (WNDPROC)SetWindowLongPtr(hEdit1, GWLP_WNDPROC, (LONG_PTR)EditSubProc);

break;

case WM_PAINT:

{

LPCTSTR Mes = TEXT("enter detection");

PAINTSTRUCT ps;

HDC hdc = BeginPaint(hWnd, &ps);

TextOut(hdc, 10, 100, Mes, lstrlen(Mes));

// TODO: Add any drawing code that uses hdc here...

EndPaint(hWnd, &ps);

}

break;

case WM_DESTROY:

SetWindowLongPtr(hEdit1, GWLP_WNDPROC, (LONG_PTR)OldEditProc);

PostQuitMessage(0);

break;

default:

return DefWindowProc(hWnd, message, wParam, lParam);

}

return 0;

}

OldEditProc = (WNDPROC)SetWindowLongPtr(hEdit1, GWLP_WNDPROC, (LONG_PTR)EditSubProc);

으로 Edit 윈도우가 디폴트로 가지는 proc을 새로 정의한 것으로 바꾸고 기존 것은 따로 저장한다.

return CallWindowProc(OldEditProc, hWnd, iMessage, wParam, lParam);

으로 새로 만든 프로시저가 기존의 프로시저를 불러 나머지 메시지를 처리한다.

위 예제는 엔터를 감지해 메시지박스를 내놓고, 탭을 누르면 다음 칸으로 옮긴다.

4. Super Classing

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

42

43

44

45

46

47

48

49

50

51

52

53

#define ID_EDIT1 100

HWND hEdit1;

WNDPROC OldEditProc;

LRESULT CALLBACK SuperProc(HWND hWnd, UINT iMessage, WPARAM wParam, LPARAM lParam)

{

switch (iMessage)

{

case WM_KEYDOWN:

MessageBoxW(hWnd, TEXT("벌판에서"), TEXT("시베리아"), MB_OK);

break;

}

SetFocus(hEdit1);

return CallWindowProc(OldEditProc, hWnd, iMessage, wParam, lParam);

}

LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)

{

switch (message)

{

case WM_CREATE:

WNDCLASS WndClass;

GetClassInfo(NULL, TEXT("edit"), &WndClass);

WndClass.hInstance = hInst;

WndClass.lpszClassName = TEXT("SuperEdit");

WndClass.hCursor = LoadCursor(NULL, IDC_WAIT);

OldEditProc = WndClass.lpfnWndProc;

WndClass.lpfnWndProc = SuperProc;

RegisterClass(&WndClass);

hEdit1 = CreateWindow(TEXT("SuperEdit"), NULL, WS_CHILD | WS_VISIBLE,

10, 10, 200, 25, hWnd, (HMENU)ID_EDIT1, hInst, NULL);

SetFocus(hEdit1);

break;

case WM_PAINT:

{

LPCTSTR Mes = TEXT("SuperEditExam");

PAINTSTRUCT ps;

HDC hdc = BeginPaint(hWnd, &ps);

TextOut(hdc, 10, 100, Mes, lstrlen(Mes));

// TODO: Add any drawing code that uses hdc here...

EndPaint(hWnd, &ps);

}

break;

case WM_DESTROY:

SetWindowLongPtr(hEdit1, GWLP_WNDPROC, (LONG_PTR)OldEditProc);

PostQuitMessage(0);

break;

default:

return DefWindowProc(hWnd, message, wParam, lParam);

}

return 0;

}

GetClassInfo(NULL, TEXT("edit"), &WndClass);

함수로 빈 클래스에 원하는 클래스의 옵션을 그대로 복사 한 후 변경할 게 있으면 변경한다.

RegisterClass(&WndClass);

그리고 등록하면 새로운 클래스를 사용할 수 있다.

댓글 없음:

댓글 쓰기

List