API

GetAsyncKeyState 올바른 이해

디버그정 2011. 7. 7. 09:20

위 API를 쓸 데가 있어서 구글링하다가
잘못된 정보가 많이 포스팅된 걸 볼 수 있었다.

리턴값은 short 형이고 최상위 비트는 현재 눌린지를 말해주고
최하위 비트는 이전에 눌렀던 적이 있는지를 말해준다.
리턴값이 0, 0x8000, 0x8001, 1 이렇게 4가지가 가능하다.

그런데 대부분 잘못된 포스팅에서는
호출시 안 눌린 상태면 결과값이 0이라고 해놨는데 틀린 설명이다.
가령 이전 GetAsyncKeyState('A')가 실행되고 0.5초 후 A키를 눌렀다 뗀 상태이고
다음 GetAsyncKeyState('A')를 실행한 경우 1을 리턴한다.

각 리턴값에 따른 상황은 다음과 같이 설명될 수 있다.
0:           이전에 누른 적이 없고 호출시점에 안 눌린 상태
0x8000: 이전에 누른 적이 없고 호출시점에 눌린 상태
0x8001: 이전에 누른 적이 있고 호출시점에 눌린 상태
1:           이전에 누른 적이 있고 호출시점에 안 눌린 상태

그렇다면 이전 시점은 어떻게 판단하는가 궁금할 것이다.
이전 GetAsyncKeyState 후 ~ 호출시의 GetAsyncKeyState의 바로 전까지의 기간이다.

가장 많이 쓰이는 최상위 비트 연산(0x8000 & 결과값)은
즉각적인 반응이 필요한 게임 프로그래밍에서 많이들 사용한다.
#define KEY_DOWN(vk_code) ((GetAsyncKeyState(vk_code) & 0x8000) ? 1 : 0)
#define KEY_UP(vk_code)       ((GetAsyncKeyState(vk_code) & 0x8000) ? 0 : 1)
이런 식으로 매크로 정의해서 자주 사용함을 볼 수 있다.

결과값의 최하위 비트가 1인지 판단하는 것 역시 쓸모가 있다.
가령 어떤 스레드가 작동중인데 S키를 누르면 정지하게 하고 싶다.
그런데  0x8000 & 연산으로 처리하면 호출시점에 반드시 S키가 눌린 상태여야 된다.
실제로 이렇게 코딩하고 실행해봤는데 사용하기에 불편하다.
GetAsyncKeyState와 맞물린 어떤 일처리루틴이 1.5초 소요되면  
때에 따라 0~1.5초 시간만큼 눌러줘야 원하는 결과를 얻을 수 있다.

이 경우 결과값과 0x8000의 &연산이 참인 경우에만 처리하지 않고
1, 0x8000, 0x8001 즉 0이 아닌 모든 경우에 처리하면
GetAsyncKeyState 호출시 안 눌린 상태여도 이전에 누른 적이 있으면 정지가능하다.
실제로 일처리루틴이 실행중인 상태에서도 S키 잠깐 눌렀다 떼면
일처리 후에 자동 정지되어 편리했다.