图片来源:游戏《ATRI》
应用应该能够接收来自用户的键盘输入。键盘输入以投递的方式让应用进行接收。
键盘的输入模式
通过为当前键盘安装合适的键盘设备驱动,系统提供了设备独立的键盘支持。通过使用语言特定的键盘布局,系统提供了语言独立的键盘布局(keyboard layout)支持,这个可以由用户或应用设置。键盘设备驱动从键盘接收扫描码,它被发送到键盘布局,在那里它被翻译为消息并投递到应用合适的窗口中。
分配给键盘上每个键的是一个个唯一的扫描码(scan code),它是键盘按键的设备独立标识符。键盘在用户按下一个键时生成两个扫描码 —— 用户按下和释放按键。
键盘设备驱动解释扫描码并将它翻译(映射)为一个虚拟键码(virtual-key code),这是系统定义的设备独立值,它标明了键盘的按键。在翻译扫描码后,键盘布局会创建一个包含扫描码、虚拟键码和其他关于击键信息的消息,随后将消息放入系统队列中。系统将它从系统队列中移除并投递到合适线程的消息队列。最终,线程的消息循环移除该消息并将他传递到合适的窗口过程进行处理。
键盘焦点与活跃窗口
系统将键盘消息投递到创建了窗口且窗口带有键盘焦点的线程的消息队列中。键盘焦点 是窗口的一个暂时属性。系统通过移动键盘焦点来让所有显示的窗口共享键盘,它根据用户的指令,从一个窗口移动到另一个。拥有键盘焦点的窗口接收所有的键盘消息(从创建它的线程的消息队列中)直到焦点移动到了另一个窗口。
线程可以调用 GetFocus 函数来得知它的哪一个窗口(如果有的话)现在拥有键盘焦点。线程可以通过调用 SetFocus 函数来将键盘焦点交给它的一个窗口。当键盘焦点从一个窗口变化到另一个时,系统会向丢失焦点的窗口发送 WM_KILLFOCUS 消息,并向获得焦点的窗口发送 WM_SETFOCUS 消息。
键盘焦点的概念与活跃窗口是相关的。活跃窗口 是用户正在工作的顶级窗口。拥有键盘焦点的窗口要么是活跃窗口,要么是活跃窗口的子窗口。为了帮助用户识别活跃窗口,系统将活跃窗口放在 Z order 的最前面并将它的标题栏和边框高亮。
用户可以通过点击、使用 ALT + TAB
或 ALT + ESC
组合键来激活一个顶级窗口。线程可以通过使用 SetActiveWindow 函数来激活一个顶级窗口。它也可以通过 GetActiveWindow 函数来判断他创建的哪个顶级窗口处于激活状态。
当一个窗口被停用而另一个窗口被激活时,系统会发送 WM_ACTIVATE 消息。wParam 的低位字如果是 0 则说明窗口被停用,是非零则说明窗口被激活。当默认窗口过程接收到 WM_ACTIVATE 消息时,它会将键盘焦点设置到活跃窗口上。
要想将键盘和鼠标输入事件从它们将要到达的窗口屏蔽掉,可以使用 BlockInput 函数。注意,BlockInput 函数不会一影响异步键盘输入状态表(input-state table)。这就意味着调用 SendInput 函数会改变异步键盘输入状态表。
击键消息
按下一个键会导致 WM_KEYDOWN 或 WM_SYSKEYDOWN 消息被放在拥有键盘焦点的窗口所在线程的消息队列中。释放是个键会导致 WM_KEYUP 或 WM_SYSKEYUP 消息放在队列中。
按下键和放开键一般是成对出现的,但是如果用户长时间按住一个键,来使用键盘的自动重复特性,系统会连续生成大量的 WM_KEYDOWN 或 WM_SYSKEYDOWN 消息。它会在用户释放键时生成一个 WM_KEYUP 或 WM_SYSKEYUP 消息。
系统和非系统击键
系统区分系统和非系统击键。系统击键会产生系统击键消息,WM_SYSKEYDOWN 和 WM_SYSKEYUP。非系统击键会产生非系统击键消息, WM_KEYDOWN 和 WM_KEYUP。