在Windows 中,我们在键盘上按下了某一个按键,会触发相应的事件,大致过程如下:

1.键盘会检测到这个动作,并通过键盘控制器把扫描码(scan code)传送到计算机;

  键盘扫描码跟具体的硬件有关的,不同厂商对同一个键的扫描码有可能不同。

2.计算机接收到扫描码后,将其交给键盘驱动程序;

3.键盘驱动程序把这个扫描码转换为键盘虚拟码;

  虚拟码与具体硬件无关,不同厂商的键盘,同一个键的虚拟码总是相同的。

3.然后,键盘驱动程序把该键盘操作的扫描码和虚拟码以及其它信息传递给操作系统;

4.操作系统将获得的信息封装在一个键盘消息中,并把该键盘消息插入到消息列队。

5.通过Windows的消息系统,该键盘消息被送到某个窗口中;

6.窗口所在的应用程序接收到消息后,可以了解到有关键盘操作的信息,然后决定作出一定的响应。

而我们的应用程序从Windows接受的关于键盘事件的消息可以分为击键消息和字符消息,即按下或松开键盘上的按键消息为击键消息,而字符消息简单理解就是操作的是哪个按键。

在UE4里面,UE4首先自定义了FKey类型来表示按键,并通过结构体EKeys声明了包括所有键鼠按键但不限于键鼠的按键输入类型,如:移动端、VR、PS等触屏或手势输入,本文仅讨论键鼠相关的输入。

通过FGenericPlatformInput及其子类FWindowsPlatformInput里的两个函数拿到windows系统的按键映射

struct INPUTCORE_API FWindowsPlatformInput : FGenericPlatformInput
{static uint32 GetKeyMap( uint32* KeyCodes, FString* KeyNames, uint32 MaxMappings );static uint32 GetCharKeyMap(uint32* KeyCodes, FString* KeyNames, uint32 MaxMappings);
};
uint32 FWindowsPlatformInput::GetKeyMap( uint32* KeyCodes, FString* KeyNames, uint32 MaxMappings )
{
#define ADDKEYMAP(KeyCode, KeyName)     if (NumMappings<MaxMappings) { KeyCodes[NumMappings]=KeyCode; KeyNames[NumMappings]=KeyName; ++NumMappings; };uint32 NumMappings = 0;if ( KeyCodes && KeyNames && (MaxMappings > 0) ){ADDKEYMAP( VK_LBUTTON, TEXT("LeftMouseButton") );ADDKEYMAP( VK_RBUTTON, TEXT("RightMouseButton") );ADDKEYMAP( VK_MBUTTON, TEXT("MiddleMouseButton") );ADDKEYMAP( VK_XBUTTON1, TEXT("ThumbMouseButton") );ADDKEYMAP( VK_XBUTTON2, TEXT("ThumbMouseButton2") );ADDKEYMAP( VK_BACK, TEXT("BackSpace") );ADDKEYMAP( VK_TAB, TEXT("Tab") );ADDKEYMAP( VK_RETURN, TEXT("Enter") );ADDKEYMAP( VK_PAUSE, TEXT("Pause") );ADDKEYMAP( VK_CAPITAL, TEXT("CapsLock") );ADDKEYMAP( VK_ESCAPE, TEXT("Escape") );ADDKEYMAP( VK_SPACE, TEXT("SpaceBar") );ADDKEYMAP( VK_PRIOR, TEXT("PageUp") );ADDKEYMAP( VK_NEXT, TEXT("PageDown") );ADDKEYMAP( VK_END, TEXT("End") );ADDKEYMAP( VK_HOME, TEXT("Home") );ADDKEYMAP( VK_LEFT, TEXT("Left") );ADDKEYMAP( VK_UP, TEXT("Up") );ADDKEYMAP( VK_RIGHT, TEXT("Right") );ADDKEYMAP( VK_DOWN, TEXT("Down") );ADDKEYMAP( VK_INSERT, TEXT("Insert") );ADDKEYMAP( VK_DELETE, TEXT("Delete") );ADDKEYMAP( VK_NUMPAD0, TEXT("NumPadZero") );ADDKEYMAP( VK_NUMPAD1, TEXT("NumPadOne") );ADDKEYMAP( VK_NUMPAD2, TEXT("NumPadTwo") );ADDKEYMAP( VK_NUMPAD3, TEXT("NumPadThree") );ADDKEYMAP( VK_NUMPAD4, TEXT("NumPadFour") );ADDKEYMAP( VK_NUMPAD5, TEXT("NumPadFive") );ADDKEYMAP( VK_NUMPAD6, TEXT("NumPadSix") );ADDKEYMAP( VK_NUMPAD7, TEXT("NumPadSeven") );ADDKEYMAP( VK_NUMPAD8, TEXT("NumPadEight") );ADDKEYMAP( VK_NUMPAD9, TEXT("NumPadNine") );ADDKEYMAP( VK_MULTIPLY, TEXT("Multiply") );ADDKEYMAP( VK_ADD, TEXT("Add") );ADDKEYMAP( VK_SUBTRACT, TEXT("Subtract") );ADDKEYMAP( VK_DECIMAL, TEXT("Decimal") );ADDKEYMAP( VK_DIVIDE, TEXT("Divide") );ADDKEYMAP( VK_F1, TEXT("F1") );ADDKEYMAP( VK_F2, TEXT("F2") );ADDKEYMAP( VK_F3, TEXT("F3") );ADDKEYMAP( VK_F4, TEXT("F4") );ADDKEYMAP( VK_F5, TEXT("F5") );ADDKEYMAP( VK_F6, TEXT("F6") );ADDKEYMAP( VK_F7, TEXT("F7") );ADDKEYMAP( VK_F8, TEXT("F8") );ADDKEYMAP( VK_F9, TEXT("F9") );ADDKEYMAP( VK_F10, TEXT("F10") );ADDKEYMAP( VK_F11, TEXT("F11") );ADDKEYMAP( VK_F12, TEXT("F12") );ADDKEYMAP( VK_NUMLOCK, TEXT("NumLock") );ADDKEYMAP( VK_SCROLL, TEXT("ScrollLock") );ADDKEYMAP( VK_LSHIFT, TEXT("LeftShift") );ADDKEYMAP( VK_RSHIFT, TEXT("RightShift") );ADDKEYMAP( VK_LCONTROL, TEXT("LeftControl") );ADDKEYMAP( VK_RCONTROL, TEXT("RightControl") );ADDKEYMAP( VK_LMENU, TEXT("LeftAlt") );ADDKEYMAP( VK_RMENU, TEXT("RightAlt") );ADDKEYMAP( VK_LWIN, TEXT("LeftCommand") );ADDKEYMAP( VK_RWIN, TEXT("RightCommand") );TMap<uint32, uint32> ScanToVKMap;
#define MAP_OEM_VK_TO_SCAN(KeyCode) { const uint32 CharCode = MapVirtualKey(KeyCode,2); if (CharCode != 0) { ScanToVKMap.Add(CharCode,KeyCode); } }MAP_OEM_VK_TO_SCAN(VK_OEM_1);MAP_OEM_VK_TO_SCAN(VK_OEM_2);MAP_OEM_VK_TO_SCAN(VK_OEM_3);MAP_OEM_VK_TO_SCAN(VK_OEM_4);MAP_OEM_VK_TO_SCAN(VK_OEM_5);MAP_OEM_VK_TO_SCAN(VK_OEM_6);MAP_OEM_VK_TO_SCAN(VK_OEM_7);MAP_OEM_VK_TO_SCAN(VK_OEM_8);MAP_OEM_VK_TO_SCAN(VK_OEM_PLUS);MAP_OEM_VK_TO_SCAN(VK_OEM_COMMA);MAP_OEM_VK_TO_SCAN(VK_OEM_MINUS);MAP_OEM_VK_TO_SCAN(VK_OEM_PERIOD);MAP_OEM_VK_TO_SCAN(VK_OEM_102);
#undef  MAP_OEM_VK_TO_SCANstatic const uint32 MAX_KEY_MAPPINGS(256);uint32 CharCodes[MAX_KEY_MAPPINGS];FString CharKeyNames[MAX_KEY_MAPPINGS];//此处调用了GetCharKeyMap函数const int32 CharMappings = GetCharKeyMap(CharCodes, CharKeyNames, MAX_KEY_MAPPINGS);for (int32 MappingIndex = 0; MappingIndex < CharMappings; ++MappingIndex){//移除重复的按键ScanToVKMap.Remove(CharCodes[MappingIndex]);}for (auto It(ScanToVKMap.CreateConstIterator()); It; ++It){ADDKEYMAP(It.Value(), FString::Chr(It.Key()));}}check(NumMappings < MaxMappings);return NumMappings;#undef ADDKEYMAP
}uint32 FWindowsPlatformInput::GetCharKeyMap(uint32* KeyCodes, FString* KeyNames, uint32 MaxMappings)
{return FGenericPlatformInput::GetStandardPrintableKeyMap(KeyCodes, KeyNames, MaxMappings, true, false);
}

然后在全局对象FInputKeyManager的初始化函数中保存从系统拿到的按键映射:

void FInputKeyManager::InitKeyMappings()
{static const uint32 MAX_KEY_MAPPINGS(256);uint32 KeyCodes[MAX_KEY_MAPPINGS], CharCodes[MAX_KEY_MAPPINGS];FString KeyNames[MAX_KEY_MAPPINGS], CharKeyNames[MAX_KEY_MAPPINGS];//从系统获取字符按键映射(主要是字母和数字,它们的虚拟键码是ASCII码)uint32 const CharKeyMapSize(FPlatformInput::GetCharKeyMap(CharCodes, CharKeyNames, MAX_KEY_MAPPINGS));//从系统获取其他按键映射(拥有系统虚拟码的按键)uint32 const KeyMapSize(FPlatformInput::GetKeyMap(KeyCodes, KeyNames, MAX_KEY_MAPPINGS));for (uint32 Idx=0; Idx<KeyMapSize; ++Idx){FKey Key(*KeyNames[Idx]);if (!Key.IsValid()){//把系统按键映射添加到UE4自定义的按键映射EKeys::AddKey(FKeyDetails(Key, Key.GetDisplayName()));}//保存按键映射KeyMapVirtualToEnum.Add(KeyCodes[Idx], Key);}for (uint32 Idx=0; Idx<CharKeyMapSize; ++Idx){// repeated linear search here isn't ideal, but it's just once at startupconst FKey Key(*CharKeyNames[Idx]);if (ensureMsgf(Key.IsValid(), TEXT("Failed to get key for name %s"), *CharKeyNames[Idx])){   //保存按键字符映射KeyMapCharToEnum.Add(CharCodes[Idx], Key);}}
}

KeyMapVirtualToEnum和KeyMapCharToEnum这两个都是TMap类型的变量,然后该对象分别提供了相应的函数来通过输入的Key来获取到相应的虚拟码和字符:

void FInputKeyManager::GetCodesFromKey(const FKey Key, const uint32*& KeyCode, const uint32*& CharCode) const
{CharCode = KeyMapCharToEnum.FindKey(Key);KeyCode = KeyMapVirtualToEnum.FindKey(Key);
}

键鼠按键触发流程参考 Windows 编程 键盘

UE4 键盘输入浅析(一)相关推荐

  1. 聚焦到Windows的窗口,激活窗口获取键盘输入,不需要鼠标点击,C++接口,focus, active, foreground

    情景 假设windows上开了2个窗口,分别叫WinA和WinB: 点击WinB后,WinA失去焦点,但我想让WinA获得焦点,获得键盘输入. 做法: 1.激活WinA 2.让WinA置于前面 3.聚 ...

  2. Java缅甸文_Android_Android键盘输入语言设置默认打开myanmar缅甸语的步骤,locale是通过系统设置的地区和l - phpStudy...

    Android键盘输入语言设置默认打开myanmar缅甸语的步骤 locale是通过系统设置的地区和latin输入法语言通过merger出来的,所以在系统地区设置和输入法语言中同时支持才可以在&quo ...

  3. ACMNO.44 C语言-平均分 有N个学生,每个学生的数据包括学号、姓名、3门课的成绩,从键盘输入N个学生的数据,要求打印出3门课的总平均成绩,以及最高分的学生的数据(包括学号、姓名、3门课成绩)

    题目描述 有N个学生,每个学生的数据包括学号.姓名.3门课的成绩, 从键盘输入N个学生的数据, 要求打印出3门课的总平均成绩,以及最高分的学生的数据 (包括学号.姓名.3门课成绩) 输入 学生数量N占 ...

  4. ACMNO.22 C语言-公约公倍2 写两个函数,分别求两个整数的最大公约数和最小公倍数,用主函数调用这两个函数,并输出结果两个整数由键盘输入。 输入 两个数 输出 最大公约数 最小公倍数

    题目描述 写两个函数,分别求两个整数的最大公约数和最小公倍数, 用主函数调用这两个函数,并输出结果两个整数由键盘输入. 输入 两个数 输出 最大公约数 最小公倍数 样例输入 6 15 样例输出 3 3 ...

  5. ACMNO.9求Sn=a+aa+aaa+…+aa…aaa(有n个a)之值,其中a是一个数字。 例如:2+22+222+2222+22222(n=5),n由键盘输入。 输入 n 输出 a=2 时

    题目描述 求Sn=a+aa+aaa+-+aa-aaa(有n个a)之值,其中a是一个数字. 例如:2+22+222+2222+22222(n=5),n由键盘输入. 输入 n 输出 a=2 时的Sn 样例 ...

  6. ACMNO.3 有三个整数a b c,由键盘输入,输出其中的最大的数。 输入 一行数组,分别为a b c 输出 a b c其中最大的数 样例输入 10 20 30 样例输出 30

    基于平台Dev-C++ 5.11 题目描述 有三个整数a b c,由键盘输入,输出其中的最大的数. 输入 一行数组,分别为a b c 输出 a b c其中最大的数 样例输入 10 20 30 样例输出 ...

  7. Keypress - 捕获键盘输入的JavaScript库

    Keypress 是一个强大的 JavaScript 库,用于捕获键盘输入.这是一个有非常特殊的功能的输入捕获库,它是很容易掌握和使用,并且不依赖第三方库.在网站开发中,经常会碰到需要处理键盘输入的场 ...

  8. 有6个候选人,100个选民,每个选民选择一个侯选人投票;从键盘输入每个选民选择的候选人名,统计并输出6个候选人的票数。java,c++实现

    题目: 有6个候选人,100个选民,每个选民选择一个侯选人投票;从键盘输入每个选民选择的候选人名,统计并输出这6个候选人的票数.(10分) 代码部分: #include<iostream> ...

  9. 从键盘输入10个互不相同的整数,找出其中最小的元素将其与数组中的第一个元素进行交换。

    题目: /* 从键盘输入10个互不相同的整数,找出其中最小的元素将其与数组中的第一个元素进行交换. */ 代码: c++做的. #include<iostream> using names ...

  10. c语言fac函数求n的阶乘,急求C语言编辑题:Cnm=n!/m!(n-m)!其中n,m 由键盘输入。要求设计一个函数fac(n)求某个正整数n 的阶乘。...

    急求C语言编辑题:Cnm=n!/m!(n-m)!其中n,m 由键盘输入.要求设计一个函数fac(n)求某个正整数n 的阶乘. 來源:互聯網  2010-05-29 01:44:10  評論 分類: 電 ...

最新文章

  1. 科技互联网公司真的越来越重视数学了吗?
  2. Jan 09 - Number of 1 Bits; Bit Operation;
  3. codevs 1085 数字游戏 dp或者暴搜
  4. 阿里用户体验大师教你如何让产品更加触动人心
  5. Qt笔记-QSerialPort的使用(串口通信简单实例)
  6. Java基础--成员变量和局部变量(区别、重名问题)
  7. OpenShift 4 - 通过 secret 访问受保护的镜像
  8. App后台开发运维和架构实践学习总结(4)——APP的注册和登录功能设计
  9. jsp中实现文件下载   两种方法
  10. HanLP里使用DAT存取字典的方法
  11. RS232 DB9引脚定义
  12. 水果编曲软件除了做电音还能做什么
  13. 能打开pdf格式的软件
  14. 分享一下自己开发 kindle 电子书下载助手
  15. JAVA在线考试系统毕业设计 开题报告
  16. linux sed尾行符号,Linux Sed命令学习笔记
  17. 如何利用python制作一个小游戏
  18. Adobe Reader无法将数值DisableExceptionChainValidation写入键
  19. [Learn Android Studio 汉化教程]Reminders实验(一)
  20. 【2021杭电多校赛】2021“MINIEYE杯”中国大学生算法设计超级联赛(8)

热门文章

  1. 2022最推荐的四款免费bi工具
  2. VB6.0代码窗口不支持鼠标中键操作滚动条解决方法
  3. iPad商标之争或和解
  4. “当前页面的脚本发生错误”如何解决
  5. [程序人生]--深度思考比勤奋更重要
  6. 0517零散问题整理
  7. Java中 ? extends T 和 ? super T 的理解
  8. linux无法识别sas硬盘,关于SNMP监控SAS硬盘问题?
  9. Mybatis 异常(SQL 语法异常 ambiguous)
  10. VS2019创建基于SDL+ffmpeg的工程