-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathConsole_Input_Windows.hpp
More file actions
242 lines (204 loc) · 5.61 KB
/
Console_Input_Windows.hpp
File metadata and controls
242 lines (204 loc) · 5.61 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
#pragma once
#include <functional>
#include <unordered_set>
#include <unordered_map>
#include <stdexcept>
#include <optional>
#include <stdio.h>
#include <conio.h>
#include <string.h>
#include <limits.h>
#include <stdint.h>
//保证未定义的情况下才定义,且在自己定义的情况下清除定义
#ifndef EOL
#define EOL -1
#define MY_DEF_EOL
#endif // !EOL
class Console_Input//用户交互
{
public:
enum LeadCode : uint16_t//指定u16类型
{
Code_NL = 0,
Code_00 = 1,
Code_E0 = 2,
};
struct Key//用两个u16这样Key刚好是32bit
{//[前导字节][按键码]
uint16_t u16KeyCode;
LeadCode enLeadCode = Code_NL;//带默认值方便只初始化u16KeyCode
bool operator==(const Key &_Right) const noexcept
{
return u16KeyCode == _Right.u16KeyCode && enLeadCode == _Right.enLeadCode;
}
bool operator!=(const Key &_Right) const noexcept
{
return u16KeyCode != _Right.u16KeyCode || enLeadCode != _Right.enLeadCode;
}
size_t Hash() const noexcept
{
//正常情况下,u16KeyCode只会在0~255,而LeadCode只会在0~3
uint16_t u16HashCode =
(u16KeyCode & 0x00FF) |//留下低8bit
((enLeadCode & 0x0003) << 8);//把低2bit移动到8bit前面组成10bit
//求hash
return std::hash<uint16_t>{}(u16HashCode);
}
};
struct KeyHash
{
size_t operator()(const Key &stKeyHash) const noexcept
{
return stKeyHash.Hash();
}
};
using CallBackFunc = long(const Key& stKey);
using Func = std::function<CallBackFunc>;
private:
std::unordered_map<Key, Func, KeyHash> mapRegisterTable;
public:
Console_Input(void) = default;
~Console_Input(void) = default;
//可以移动
Console_Input(Console_Input &&) = default;
Console_Input &operator = (Console_Input &&) = default;
//禁止拷贝
Console_Input(const Console_Input &) = delete;
Console_Input &operator = (const Console_Input &) = delete;
//测试按键的值
[[noreturn]] static void KeyCodeTest(void) noexcept//函数不会返回
{
/*
上下左右方向键
0xE0开始后跟
0x48
0x4B 0x50 0x4D
复合按键不是0x00开头就是0xE0开头
*/
while (true)
{
printf("0x%02X ", _getch());
}
}
//注册键,重复注册则最新的按键替换最旧的
void RegisterKey(const Key &stKey, Func fFunc)
{
mapRegisterTable[stKey] = fFunc;
}
//通过拷贝注册相同功能按键
void CopyRegisteredKey(const Key &stTarget, const Key &stSource)
{
mapRegisterTable[stTarget] = mapRegisterTable[stSource];
}
//取消注册
void UnRegisterKey(const Key &stKey) noexcept
{
mapRegisterTable.erase(stKey);
}
//查询是否已经注册
bool IsKeyRegister(const Key &stKey) const noexcept
{
return mapRegisterTable.contains(stKey);
}
//重置所有已注册按键
void Reset(void) noexcept
{
mapRegisterTable.clear();
}
//获取按键转义码(如果有转义)并返回
static Key GetTranslateKey(void)
{
Key stKeyRet;
int iInput = _getch();//获取第一次输入
switch (iInput)
{
case 0x00://转义
stKeyRet.enLeadCode = Code_00;
stKeyRet.u16KeyCode = _getch();//重新获取
break;
case 0xE0://转义
stKeyRet.enLeadCode = Code_E0;
stKeyRet.u16KeyCode = _getch();//重新获取
break;
case EOL:
throw std::runtime_error("Error: _getch() return EOL!");
default://正常按键
stKeyRet.enLeadCode = Code_NL;
stKeyRet.u16KeyCode = iInput;//不用重新获取,直接就是按键
break;
}
return stKeyRet;
}
//等待一个按键被按下
static void WaitForKey(Key stKeyWait)
{
Key stKeyGet;
do
{
stKeyGet = GetTranslateKey();
} while (stKeyGet != stKeyWait);
//执行到此说明已经等到目标键
return;
}
//等待多个按键中的任意一个被按下并返回按下的按键
static Key WaitForKeys(const std::unordered_set<Key, KeyHash> &setKeysWait)
{
Key stKeyGet;
do
{
stKeyGet = GetTranslateKey();
} while (!setKeysWait.contains(stKeyGet));
//执行到此说明已经等到任一目标键
return stKeyGet;//顺便返回一下让用户知道是哪个
}
//仅处理一次按键,可能按键未注册,所以返回std::optional
std::optional<long> Once(void) const//不保证函数会不会抛出异常
{
Key stKetGet = GetTranslateKey();
//获取函数
auto it = mapRegisterTable.find(stKetGet);
if (it == mapRegisterTable.end())
{
return {};//构造空optional
}
//不为空则调用
return { it->second(it->first) };
}
//等待至少一次成功的按键调用,如果按键不存在则持续循环,直到至少触发一次注册的按键调用
//因为必然至少成功一次,所以返回其中的值,而不是std::optional
long AtLeastOne(void) const
{
std::optional<long> lRet{};
do
{
lRet = Once();
} while (!lRet.has_value());//为空则持续循环直到成功
return lRet.value();
}
//循环处理按键并触发回调,直到抛出异常或回调返回非0值
//因为必然至少成功一次,所以返回其中的值,而不是std::optional
long Loop(void) const
{
std::optional<long> lRet{};
do
{
lRet = Once();
} while (!lRet.has_value() || lRet.value() == 0);
return lRet.value();
}
//等待任一按键被按下
static Key WaitAnyKey(void) noexcept
{
return GetTranslateKey();
}
//判断缓冲区内是否存在按键
static bool InputExists(void) noexcept
{
return _kbhit() != 0;
}
};
//防止泄露
#ifdef MY_DEF_EOL
#undef EOL
#undef MY_DEF_EOL
#endif // MY_DEF_EOL