用C / C ++滚动自己的键盘/输入系统

时间:2012-01-05 04:15:57

标签: c++ c winapi visual-c++ keyboard

问题:

学习如何创建自己的输入/输出系统需要哪些资源?

我自己的理解:

我知道它依赖于操作系统,所以让我们分开Linux和Windows并列出两个操作系统的资源(如果可能的话)。对于Linux,我猜测需要对X Window系统有一定的了解。对于Windows,我猜测win32 API。尽管如此,我猜测还有更多内容,而不仅仅是知道这些,好像我希望用C ++编写输入系统。

提问的理由:

我尝试阅读OIS源代码(因为这将用CC++编写),并且不喜欢它的编写方式。因此,我自己学会了如何编写自己的键盘输入/输出系统来进行简单的乒乓球游戏(用C++编写)。

2 个答案:

答案 0 :(得分:37)

更新:这是我为处理键盘输入而编写的库。它使用FreeBSD许可证。我甚至将其标记为v1.0,因此我认为它是“发布质量”。

https://github.com/depp/keycode

我最近努力工作以使游戏“恰到好处”,而我还没有完成。我会分享我所知道的。

密钥代码

对于游戏,键码通常是您想要的。

当您按键盘上的某个键时,操作系统会首先将按键按下转换为键码。密钥代码指定键盘上键的物理位置。例如,代码4可能对应于美国键盘上标记为 A 的键(即使该键在法国或俄罗斯具有不同的标签)。每个平台都有一组不同的密钥代码,或者可能有多组密钥代码。您可以通过其他名称了解它们,例如扫描码虚拟键码

  • Windows使用虚拟密钥代码MSDN documentation)。它们在各种硬件和软件配置中都很稳定。您可以在<Winuser.h>头文件中找到定义。在Windows上,如果按最左侧的主行键(美国的 A ,法国的 Q ),则会得到代码65。

  • Mac OS X具有自80年代以来一直稳定的密钥代码。它们在<Carbon/Events.h>中定义。您实际上不需要链接到Carbon来使用密钥代码,但您需要标头。在OS X上,如果按最左侧的主行键,则会得到代码4。

  • Linux有几组不同的密钥代码。所以在Linux上,你有几个选择。你可以使用关键的syms(它有我将在下面解释的缺点),你可以假设用户正在使用特定的输入驱动程序(Evdev这些天是非常好猜测),或者你可以以某种方式弄清楚机器使用哪个输入驱动程序。要获取密钥代码,您必须阅读键盘定义文件。例如,查看/usr/share/X11/xkb/keycodes/evdev以获取Evdev密钥代码。使用Evdev,如果按最左侧的主行键,则会得到代码38。

当然,如果关键代码在不同平台上相同,那就太容易了。您可以使用特定于平台的密钥代码,也可以将其转换为与平台无关的值。我建议使用USB HID代码(pdf)作为与平台无关的代码,因为许多聪明的人已经遇到了同意每个密钥调用的麻烦。

我上面发布的库包含每个平台的表格,例如WIN_NATIVE_TO_HID,用于将密钥代码转换为USB HID代码。

困难的部分是向用户传达他们应该按什么按钮,但至少来自其他国家的人可以玩你的游戏。

字符代码

您不想使用字符代码,即使您居住在美国并且您的受众也居住在美国,它们也更容易使用。

将按钮翻译成按键代码后,操作系统会将密钥代码转换为字符代码。字符代码受当前键盘布局的影响,并且通常也会受到修改键的影响。

因此,如果您按键盘上的 A ,您将获得键码65,4或38,具体取决于您所使用的平台。但是,您将获得字符代码'a''A',具体取决于shift键是否已关闭,或者如果键盘布局设置为法语,则可能会获得'Q',或{{1如果键盘布局设置为俄语。因此,如果您将WASD编码到游戏中并使用字符代码,当来自其他国家/地区的某人玩您的游戏时,输入将完全被破坏。你必须在法国使用ZQSD,在俄罗斯使用ÖФЫВ,很快你就会头疼。

我自己使用非QWERTY布局(Dvorak),大多数游戏都完全被打破了。在 W 键的位置是,如果您按下shift键,则变为&lt; ,并且某些游戏无法识别那同样的关键。例如,我按向前移动,但是如果我在Shift键关闭时释放按钮,则游戏会认为我已经释放了&lt; 并认为仍在下降,所以我继续前进。即使我切换到美国键盘布局(我认为这是SDL中的一个小故障),大多数使用SDL的游戏都会在我的Mac上被打破。

LibSDL

SDL 2.0提供与平台无关的密钥代码,称为扫描代码。使用'Ф'标题。 SDL开发人员得出的结论是扫描代码应该转换回USB HID代码,因此SDL扫描代码与我上面发布的库完全兼容(参见keycode.hSDL_scancode.h,数值相同)。

由于这个原因和其他原因,如果您使用的是SDL 1.2,我强烈建议您升级到2.0。

答案 1 :(得分:0)

以下内容仅与在Windows上处理输入有关。

正如capr在对Dietrich Epp的答案的评论中所提到的那样,VK_代码会根据您所使用的键盘布局的语言而变化。因此,如果您的键盘布局是法语,并且您在QWERTY键盘上按“ Q”,则将产生“ A”的虚拟键。如果您的布局是例如在美国,按预期方式生成了“ Q”的虚拟密钥。

由于实际的扫描代码取决于硬件(因此无法使用),因此我通过将生成的扫描代码转换为与en-US键盘布局相对应的虚拟键来解决布局依赖性问题。然后可以按照Dietrich Epp所述的方式将其转换为USB HID代码。无论布局的语言如何,这都会产生相同的预期USB HID代码。

将扫描代码转换为en-US的VK的方法,

// Get and store the name of the currently used locale
wchar_t defaultLayoutName[KL_NAMELENGTH];
GetKeyboardLayoutName(defaultLayoutName)

// Load and store a handle to the locale for en-US ("00000409")
HKL defaultEnUSInputLocale = LoadKeyboardLayout(TEXT("00000409"), KLF_NOTELLSHELL);

// Load the locale that was in use before so as not to change the keyboard layout of the user
LoadKeyboardLayout(defaultLayoutName, KLF_ACTIVATE);

--- (at windowProc) ---
case WM_INPUT:
    // Use the handle to the en-US locale to translate the scan codes to VK_.
    virtualKey = MapVirtualKeyExW(scanCode, MAPVK_VSC_TO_VK_EX, defaultEnUSInputLocale);

    // translate virtualKey to USB HID by using e.g. the codes supplied by Dietrich Epp
    int8_t usbHIDkey = VK_TO_HID[virtualKey];

请注意,在Windows上还有一些与虚拟键相关的障碍。我发现此网站在此方面提供了出色的帮助:https://blog.molecular-matters.com/2011/09/05/properly-handling-keyboard-input/