【来源】
对于DirectInput来说,键盘是作为一个高效的游戏控制器来处理的。如果你要输入文本,那么你可以使用Windows提供的标准服务。在独占模式中,我们是不能获取键盘数据的,我们所能获得的是WM_KEYDOWN、 WM_KEYUP和WM_CHAR消息以及由GetDeviceState和GetDeviceData所返回的输入值。在DInput例子程序中,应用 程序通过对Ctrl-F的响应就说明了这一点。
1、一个有101个按钮的游戏板
就像DirectX SDK文档中所说的一样,在DirectInput情况下,键盘并不是文本输入设备,而是一块带有很多按钮的游戏板。虽然在键盘中并没有轴(游戏板中有),但它们还是具有很大的相似性的。在消息传递系统中,Windows在把一个按钮消息发送到消息队列之前要对它做很多处理。它要判断一个键是否被按下,如果是这样的话,那么就检查所按下的时间并与控制面板中设置好的重复延时和重复速率相比较。在生成消息的时候,Windows还要处理物理键所代表的虚拟键(VirtualKey)以及组合键的使用等。对于GetAsyncKeyState这类函数,它所返回的也是Windows处理过的数据。
在DirectInput中,情况却完全不是这样的,它所关心的仅是两件事情:某个键是否被按下和这个键的代码是什么。为了实现事件通知和基于缓冲区的数据,DirectInput把键状态的改变翻译成一次按键事件和一次释放事件。但在一个键被按下时,不管按下多长时间,它所触发的仅是一个单一的事件。
用一对一的方法,DirectInput把扫描码转换成一个从0到255之间的数值,并且这些数值都用DIK_*的方式来定义(参见附表)。与 Windows的键盘服务不一样,DirectInput要区分两个具有相同名字的键,如主键盘和数字键盘上的Enter键。另外与Windows服务不 一样的是,DirectInput以相同的方式处理每一个键,而不管这个键是字母、数字还是Alt、Scroll Lock等键。
当 然,DirectInput在某些特殊的情况下还是会执行一些转换操作。例如,NEC键盘所生成的并不是标准的扫描码,这时DirectInput就会自 动把它转换成标准的PC键盘扫描码。如果你们所开发的应用程序是面对国际市场的,那么还要注意日本的键盘上有些键是不存在的,而在美国式的键盘上,则有一些非标准的附加键。关于这些信息,请参考DirectInput SDK的文档。
2、直接的键盘数据
读取键盘的状态再也没有这么简单了,你只需申明一个256字节的数组然后再调用 IDirectInputDevice::GetDeviceState。注意,你并不需要在调用 IDirectInputDevice::GetDeviceState之前调用Poll,这是因为键盘数据总是由中断产生的。
char keys[256];
HRESULT hr = g_lpdid2->GetDeviceState( sizeof( keys ), keys );
现在你可以访问这个数组并用DIK_*数值来判断任何键的状态。与以前一样,如果字节的高位被置成1,则说明键是被按下;如果是0说明键没有被按下。
If ( keys[DIK_LMENU] & 0 ×80 ) {;} // Ieft Alt is down.
3、基于缓冲区的键盘数据
DirectInput是用基于缓冲区数据的方法来处理键盘输入的,对于这一点我们不必感到奇怪。从键盘中读取基于缓冲区的输入就像读取游戏杆和鼠标的按键输入一样。
#define BUFFERSIZE 32 /* Remember to set the device’s
DIPROP_BUFFERSIZE property to this value */
DIDEVICEOBJECTDATA rgdod[BUFFERSIZE];
DWORD dwItmes = BUFFERSIZE; // Could be fewer.
HRESULT = g_lpdid2->GetDeviceData(
sizeof( DIDEVICEOBJECTDATA ),
rgdod, // Where to put data?
&dwItems, // How many items?
0); // Flags.
与以前一样,每一个基于缓冲区的事件都与DIDEVICEOBJECTDATA结构的dwOfs成员变量中的设备物相联系。在这种情况 下,......。如果事件被按下,那么dwData成员变量的0×80位被置成1;如果事件被释放,那么dwData成员变量的0×80位被置成0。
3.1 处理数据的丢失
到目前为止,我们还没有谈到这样的一个问题:对于基于缓冲区的数据,怎样处理数据流被打断的情况。这种情况不仅仅会出现在键盘设备上,在其他的任何设备上它都可能会出现。数据流的打断会以如下两种方式出现:
1)设备被强制性地失去获取状态——例如,当原来具有前端协作级别的应用程序被移到后端时,GetDeviceData函数失败并且返回 DIERR_INPUTLOST。如果应用程序在重新获取设备时失败,那么下一次调用GetDeviceData将返回 DIERR_NOTACQUIRED。
2)缓冲区溢出。GetDeviceData函数执行成功,但返回DI_BUFFEROVERFLOW。这表明某些数据没有被返回而且这些数据永远地丢失了。
假设应用程序在GetDeviceData函数获取空格键被按下事件时开始进行射击,而在空格键被释放时停止射击。如果用户在按下空格键和释放空格键中间碰巧用鼠标单击了另一个窗口程序,那么应用程序是不会停止射击的,这是因为应用程序将丢失键盘的信息,从而导致它不能获取空格键的释放事件。为了从上述 情形下恢复正常,我们将不得不立即调用GetDeviceState函数以重新获取设备。对于出现DI_BUFFEROVERFLOW结果的情况也一样。
附表 DirectInput键盘常数
键名 | 别名 | |
DIK_ESCAPDIK_1 | 在主键盘上 | |
DIK_2 | 在主键盘上 | |
DIK_3 | 在主键盘上 | |
DIK_4 | 在主键盘上 | |
DIK_5 | 在主键盘上 | |
DIK_6 | 在主键盘上 | |
DIK_7 | 在主键盘上 | |
DIK_8 | 在主键盘上 | |
DIK_9 | 在主键盘上 | |
DIK_0 | 在主键盘上 | |
DIK_MINUS | 在主键盘上 | |
DIK_EQUALS | 在主键盘上 | |
DIK_BACK | DIK_BACKSPACE | Backspace键 |
DIK_TAB | ||
DIK_Q | ||
DIK_W | ||
DIK_E | ||
DIK_R | ||
DIK_T | ||
DIK_Y | ||
DIK_U | ||
DIK_I | ||
DIK_O | ||
DIK_P | ||
DIK_LBRACKET | [键 | |
DIK_RBRACKET | ]键 | |
DIK_RETURN | 键盘上的回车键 | |
DIK_LCONTROL | 左Ctrl键 | |
DIK_A | ||
DIK_S | ||
DIK_D | ||
DIK_F | ||
DIK_G | ||
DIK_H | ||
DIK_J | ||
DIK_K | ||
DIK_L | ||
DIK_SEMICOLON | ||
DIK_APOSTROPHE | ||
DIK_GRAVE | ‘键 | |
DIK_LSHIFT | 左Shift键 | |
DIK_BACKSLASH | ||
DIK_Z | ||
DIK_X | ||
DIK_C | ||
DIK_V | ||
DIK_B | ||
DIK_N | ||
DIK_M | ||
DIK_COMMA | ||
DIK_PERIOD | 在主键盘上 | |
DIK_SLASH | 在主键盘上 | |
DIK_RSHIFT | 右Shift键 | |
DIK_MULTIPLY | DIK_NUMPADSTAR | 数字键盘上的*键 |
DIK_LMENU | ||
DIK_LALT | 左Alt键 | |
DIK_SPACE | 空格 | |
DIK_CAPITAL | DIK_CAPSLOCK | |
DIK_F1 | ||
DIK_F2 | ||
DIK_F3 | ||
DIK_F4 | ||
DIK_F5 | ||
DIK_F6 | ||
DIK_F7 | ||
DIK_F8 | ||
DIK_F9 | ||
DIK_F10 | ||
DIK_NUMLOCK | ||
DIK_SCROLL | Scroll Lock键 | |
DIK_NUMPAD7 | ||
DIK_NUMPAD8 | ||
DIK_NUMPAD9 | ||
DIK_SUBTRACT | DIK_NUMPADMINUS | 数字键盘上的-键 |
DIK_NUMPAD4 | ||
DIK_NUMPAD5 | ||
DIK_NUMPAD6 | ||
DIK_ADD | DIK_NUMPADPLUS | 数字键盘上的+键 |
DIK_NUMPAD1 | ||
DIK_NUMPAD2 | ||
DIK_NUMPAD3 | ||
DIK_NUMPAD0 | ||
DIK_DECIMAL | DIK_NUMPADPERIOD | 数字键盘上的.键 |
DIK_F11 | ||
DIK_F12 | ||
DIK_F13 | ||
DIK_F14 | ||
DIK_F15 | ||
DIK_KANA | 在日本键盘上 | |
DIK_CONVERT | 在日本键盘上 | |
DIK_NOCONVERT | 在日本键盘上 | |
DIK_YEN | 在日本键盘上 | |
DIK_NUMPADEQUALS | 在数字键盘上(NEC PC98) | |
DIK_CIRCUMFLEX | 在日本键盘上 | |
DIK_AT | 在日本键盘上 | |
DIK_COLON | 在日本键盘上 | |
DIK_UNDERLINE | 在日本键盘上 | |
DIK_KANJI | 在日本键盘上 | |
DIK_STOP | 在日本键盘上 | |
DIK_AX | 在日本键盘上 | |
DIK_UNLABELED | 在日本键盘上 | |
DIK_NUMPADENTER | ||
DIK_RCONTROL | 右Ctrl键 | |
DIK_NUMPADCOMMANEC | PC98数字键盘上的逗号 | |
DIK_DIVIDE | DIK_NUMPADSLASH | 数字键盘上的/号 |
DIK_SYSRQ | ||
DIK_RMENU | DIK_RALT | 右Alt键 |
DIK_HOME | ||
DIK_UP | DIK_UPARROW | |
DIK_PRIOR | DIK_PGUP | PageUp键 |
DIK_LEFT | DIK_LEFTARROW | |
DIK_RIGHT | DIK_RIGHTARROW | |
DIK_END | ||
DIK_DOWN | DIK_DOWNARROW | 下向箭头键 |
DIK_NEXT | DIK_PGDN | PageDown键 |
DIK_INSERT | ||
DIK_DELETE | ||
DIK_LWIN | 左边Windows徽标键 | |
DIK_RWIN | 右边Windows徽标键 | |
DIK_APPS | 应用程序键 |