FTXUI 6.1.9
C++ functional terminal UI.
载入中...
搜索中...
未找到
terminal_input_parser.cpp
浏览该文件的文档.
1// 版权所有 2020 Arthur Sonzogni。保留所有权利。
2// 本源代码的使用受 MIT 许可证的约束,该许可证可在
3// LICENSE 文件中找到。
5
6#include <cstdint> // for uint32_t
7#include <ftxui/component/mouse.hpp> // for Mouse, Mouse::Button, Mouse::Motion
8#include <functional> // for std::function
9#include <map>
10#include <memory> // for unique_ptr, allocator
11#include <utility> // for move
12#include <vector>
13#include "ftxui/component/event.hpp" // for Event
14#include "ftxui/component/task.hpp" // for Task
15
16namespace ftxui {
17
18// NOLINTNEXTLINE
19const std::map<std::string, std::string> g_uniformize = {
20 // Microsoft 的终端为回车键使用不同的换行符。
21 // Linux 在使用 `bind` 命令时也会出现这种情况:
22 // 参见 https://github.com/ArthurSonzogni/FTXUI/issues/337
23 // 在这里,我们将换行符统一为 `\n`。
24 {"\r", "\n"},
25
26 // 参见: https://github.com/ArthurSonzogni/FTXUI/issues/508
27
28 // 参见: https://github.com/ArthurSonzogni/FTXUI/issues/626
29 //
30 // 根据光标键模式 (DECCKM),终端发送不同的
31 // 转义序列:
32 //
33 // 键 普通 应用程序
34 // ----- -------- -----------
35// 上 ESC [ A ESC O A
36// 下 ESC [ B ESC O B
37// 右 ESC [ C ESC O C
38// 左 ESC [ D ESC O D
39// Home ESC [ H ESC O H
40// End ESC [ F ESC O F
41 //
42 {"\x1BOA", "\x1B[A"}, // 上
43 {"\x1BOB", "\x1B[B"}, // 下
44 {"\x1BOC", "\x1B[C"}, // 右
45 {"\x1BOD", "\x1B[D"}, // 左
46 {"\x1BOH", "\x1B[H"}, // Home
47 {"\x1BOF", "\x1B[F"}, // End
48
49 // FN 键的变体。
50 // 内部,我们使用:
51 // vt220, xterm-vt200, xterm-xf86-v44, xterm-new, mgt, screen
52 // 参见: https://invisible-island.net/xterm/xterm-function-keys.html
53
54 // 对于不属于任何
55 // 真实标准的 Linux 操作系统控制台 (CTRL+ALT+FN)。
56 // 参见: https://github.com/ArthurSonzogni/FTXUI/issues/685
57 {"\x1B[[A", "\x1BOP"}, // F1
58 {"\x1B[[B", "\x1BOQ"}, // F2
59 {"\x1B[[C", "\x1BOR"}, // F3
60 {"\x1B[[D", "\x1BOS"}, // F4
61 {"\x1B[[E", "\x1B[15~"}, // F5
62
63 // xterm-r5, xterm-r6, rxvt
64 {"\x1B[11~", "\x1BOP"}, // F1
65 {"\x1B[12~", "\x1BOQ"}, // F2
66 {"\x1B[13~", "\x1BOR"}, // F3
67 {"\x1B[14~", "\x1BOS"}, // F4
68
69 // vt100
70 {"\x1BOt", "\x1B[15~"}, // F5
71 {"\x1BOu", "\x1B[17~"}, // F6
72 {"\x1BOv", "\x1B[18~"}, // F7
73 {"\x1BOl", "\x1B[19~"}, // F8
74 {"\x1BOw", "\x1B[20~"}, // F9
75 {"\x1BOx", "\x1B[21~"}, // F10
76
77 // scoansi
78 {"\x1B[M", "\x1BOP"}, // F1
79 {"\x1B[N", "\x1BOQ"}, // F2
80 {"\x1B[O", "\x1BOR"}, // F3
81 {"\x1B[P", "\x1BOS"}, // F4
82 {"\x1B[Q", "\x1B[15~"}, // F5
83 {"\x1B[R", "\x1B[17~"}, // F6
84 {"\x1B[S", "\x1B[18~"}, // F7
85 {"\x1B[T", "\x1B[19~"}, // F8
86 {"\x1B[U", "\x1B[20~"}, // F9
87 {"\x1B[V", "\x1B[21~"}, // F10
88 {"\x1B[W", "\x1B[23~"}, // F11
89 {"\x1B[X", "\x1B[24~"}, // F12
90};
91
93 : out_(std::move(out)) {}
94
96 timeout_ += time;
97 const int timeout_threshold = 50;
98 if (timeout_ < timeout_threshold) {
99 return;
100 }
101 timeout_ = 0;
102 if (!pending_.empty()) {
103 Send(SPECIAL);
104 }
105}
106
108 pending_ += c;
109 timeout_ = 0;
110 position_ = -1;
111 Send(Parse());
112}
113
114unsigned char TerminalInputParser::Current() {
115 return pending_[position_];
116}
117
118bool TerminalInputParser::Eat() {
119 position_++;
120 return position_ < static_cast<int>(pending_.size());
121}
122
123void TerminalInputParser::Send(TerminalInputParser::Output output) {
124 switch (output.type) {
125 case UNCOMPLETED:
126 return;
127
128 case DROP:
129 pending_.clear();
130 return;
131
132 case CHARACTER:
133 out_(Event::Character(std::move(pending_)));
134 pending_.clear();
135 return;
136
137 case SPECIAL: {
138 auto it = g_uniformize.find(pending_);
139 if (it != g_uniformize.end()) {
140 pending_ = it->second;
141 }
142 out_(Event::Special(std::move(pending_)));
143 pending_.clear();
144 }
145 return;
146
147 case MOUSE:
148 out_(Event::Mouse(std::move(pending_), output.mouse)); // NOLINT
149 pending_.clear();
150 return;
151
152 case CURSOR_POSITION:
153 out_(Event::CursorPosition(std::move(pending_), // NOLINT
154 output.cursor.x, // NOLINT
155 output.cursor.y)); // NOLINT
156 pending_.clear();
157 return;
158
159 case CURSOR_SHAPE:
160 out_(Event::CursorShape(std::move(pending_), output.cursor_shape));
161 pending_.clear();
162 return;
163 }
164 // NOT_REACHED().
165}
166
167TerminalInputParser::Output TerminalInputParser::Parse() {
168 if (!Eat()) {
169 return UNCOMPLETED;
170 }
171
172 if (Current() == '\x1B') {
173 return ParseESC();
174 }
175
176 if (Current() < 32) { // C0 NOLINT
177 return SPECIAL;
178 }
179
180 if (Current() == 127) { // Delete // NOLINT
181 return SPECIAL;
182 }
183
184 return ParseUTF8();
185}
186
187// 码点 <-> UTF-8 转换
188//
189// ┏━━━━━━━━┳━━━━━━━━┳━━━━━━━━┳━━━━━━━━┓
190// ┃Byte 1 ┃Byte 2 ┃Byte 3 ┃Byte 4 ┃
191// ┡━━━━━━━━╇━━━━━━━━╇━━━━━━━━╇━━━━━━━━┩
192// │0xxxxxxx│ │ │ │
193// ├────────┼────────┼────────┼────────┤
194// │110xxxxx│10xxxxxx│ │ │
195// ├────────┼────────┼────────┼────────┤
196// │1110xxxx│10xxxxxx│10xxxxxx│ │
197// ├────────┼────────┼────────┼────────┤
198// │11110xxx│10xxxxxx│10xxxxxx│10xxxxxx│
199// └────────┴────────┴────────┴────────┘
200//
201// 如果存在相同码点的较短表示,则某些序列是非法的。
202TerminalInputParser::Output TerminalInputParser::ParseUTF8() {
203 auto head = Current();
204 unsigned char selector = 0b1000'0000; // NOLINT
205
206 // 第一个字节的非码点部分。
207 unsigned char mask = selector;
208
209 // 查找第一个字节中的第一个零。
210 unsigned int first_zero = 8; // NOLINT
211 for (unsigned int i = 0; i < 8; ++i) { // NOLINT
212 mask |= selector;
213 if (!(head & selector)) {
214 first_zero = i;
215 break;
216 }
217 selector >>= 1U;
218 }
219
220 // 累加第一个字节的值。
221 auto value = uint32_t(head & ~mask); // NOLINT
222
223 // 无效的 UTF8,超过 5 个字节。
224 const unsigned int max_utf8_bytes = 5;
225 if (first_zero == 1 || first_zero >= max_utf8_bytes) {
226 return DROP;
227 }
228
229 // 多字节 UTF-8。
230 for (unsigned int i = 2; i <= first_zero; ++i) {
231 if (!Eat()) {
232 return UNCOMPLETED;
233 }
234
235 // 无效的续字节。
236 head = Current();
237 if ((head & 0b1100'0000) != 0b1000'0000) { // NOLINT
238 return DROP;
239 }
240 value <<= 6; // NOLINT
241 value += head & 0b0011'1111; // NOLINT
242 }
243
244 // 检查过长的 UTF8 编码。
245 int extra_byte = 0;
246 if (value <= 0b000'0000'0111'1111) { // NOLINT
247 extra_byte = 0; // NOLINT
248 } else if (value <= 0b000'0111'1111'1111) { // NOLINT
249 extra_byte = 1; // NOLINT
250 } else if (value <= 0b1111'1111'1111'1111) { // NOLINT
251 extra_byte = 2; // NOLINT
252 } else if (value <= 0b1'0000'1111'1111'1111'1111) { // NOLINT
253 extra_byte = 3; // NOLINT
254 } else { // NOLINT
255 return DROP;
256 }
257
258 if (extra_byte != position_) {
259 return DROP;
260 }
261
262 return CHARACTER;
263}
264
265TerminalInputParser::Output TerminalInputParser::ParseESC() {
266 if (!Eat()) {
267 return UNCOMPLETED;
268 }
269 switch (Current()) {
270 case 'P':
271 return ParseDCS();
272 case '[':
273 return ParseCSI();
274 case ']':
275 return ParseOSC();
276
277 // 预期 2 个字符。
278 case ' ':
279 case '#':
280 case '%':
281 case '(':
282 case ')':
283 case '*':
284 case '+':
285 case 'O':
286 case 'N': {
287 if (!Eat()) {
288 return UNCOMPLETED;
289 }
290 return SPECIAL;
291 }
292 // 预期 1 个字符:
293 default:
294 return SPECIAL;
295 }
296}
297
298// ESC P ... ESC 反斜杠
299TerminalInputParser::Output TerminalInputParser::ParseDCS() {
300 // 解析直到字符串终止符 ST。
301 while (true) {
302 if (!Eat()) {
303 return UNCOMPLETED;
304 }
305
306 if (Current() != '\x1B') {
307 continue;
308 }
309
310 if (!Eat()) {
311 return UNCOMPLETED;
312 }
313
314 if (Current() != '\\') {
315 continue;
316 }
317
318 if (pending_.size() == 10 && //
319 pending_[2] == '1' && //
320 pending_[3] == '$' && //
321 pending_[4] == 'r' && //
322 true) {
323 Output output(CURSOR_SHAPE);
324 output.cursor_shape = pending_[5] - '0';
325 return output;
326 }
327
328 return SPECIAL;
329 }
330}
331
332TerminalInputParser::Output TerminalInputParser::ParseCSI() {
333 bool altered = false;
334 int argument = 0;
335 std::vector<int> arguments;
336 while (true) {
337 if (!Eat()) {
338 return UNCOMPLETED;
339 }
340
341 if (Current() == '<') {
342 altered = true;
343 continue;
344 }
345
346 if (Current() >= '0' && Current() <= '9') {
347 argument *= 10; // NOLINT
348 argument += Current() - '0';
349 continue;
350 }
351
352 if (Current() == ';') {
353 arguments.push_back(argument);
354 argument = 0;
355 continue;
356 }
357
358 // CSI 由 0x40–0x7E 范围内的字符终止
359 // (ASCII @A–Z[\\\]^_`a–z{|}~),
360 if (Current() >= '@' && Current() <= '~' &&
361 // 注意:我不记得为什么我们排除 '<'
362 Current() != '<' &&
363 // 为了处理 F1-F4,我们排除 '['。
364 Current() != '[') {
365 arguments.push_back(argument);
366 argument = 0; // NOLINT
367
368 switch (Current()) {
369 case 'M':
370 return ParseMouse(altered, true, std::move(arguments));
371 case 'm':
372 return ParseMouse(altered, false, std::move(arguments));
373 case 'R':
374 return ParseCursorPosition(std::move(arguments));
375 default:
376 return SPECIAL;
377 }
378 }
379
380 // CSI 中无效的 ESC。
381 if (Current() == '\x1B') {
382 return SPECIAL;
383 }
384 }
385}
386
387TerminalInputParser::Output TerminalInputParser::ParseOSC() {
388 // 解析直到字符串终止符 ST。
389 while (true) {
390 if (!Eat()) {
391 return UNCOMPLETED;
392 }
393 if (Current() != '\x1B') {
394 continue;
395 }
396 if (!Eat()) {
397 return UNCOMPLETED;
398 }
399 if (Current() != '\\') {
400 continue;
401 }
402 return SPECIAL;
403 }
404}
405
406TerminalInputParser::Output TerminalInputParser::ParseMouse( // NOLINT
407 bool altered,
408 bool pressed,
409 std::vector<int> arguments) {
410 if (arguments.size() != 3) {
411 return SPECIAL;
412 }
413
414 (void)altered;
415
416 Output output(MOUSE);
417 output.mouse.motion = Mouse::Motion(pressed); // NOLINT
418
419 // 位 值 修饰符 注释
420 // ---- ----- ------- ---------
421 // 0 1 1 2 按钮 0 = 左键, 1 = 中键, 2 = 右键, 3 = 释放
422 // 2 4 Shift
423 // 3 8 Meta
424 // 4 16 Control
425 // 5 32 Move
426 // 6 64 Wheel
427
428 // clang-format off
429 const int button = arguments[0] & (1 + 2); // NOLINT
430 const bool is_shift = arguments[0] & 4; // NOLINT
431 const bool is_meta = arguments[0] & 8; // NOLINT
432 const bool is_control = arguments[0] & 16; // NOLINT
433 const bool is_move = arguments[0] & 32; // NOLINT
434 const bool is_wheel = arguments[0] & 64; // NOLINT
435 // clang-format on
436
437 output.mouse.motion = is_move ? Mouse::Moved : Mouse::Motion(pressed);
438 output.mouse.button = is_wheel ? Mouse::Button(Mouse::WheelUp + button) //
439 : Mouse::Button(button);
440 output.mouse.shift = is_shift;
441 output.mouse.meta = is_meta;
442 output.mouse.control = is_control;
443 output.mouse.x = arguments[1]; // NOLINT
444 output.mouse.y = arguments[2]; // NOLINT
445
446 // 移动事件。
447 return output;
448}
449
450// NOLINTNEXTLINE
451TerminalInputParser::Output TerminalInputParser::ParseCursorPosition(
452 std::vector<int> arguments) {
453 if (arguments.size() != 2) {
454 return SPECIAL;
455 }
456 Output output(CURSOR_POSITION);
457 output.cursor.y = arguments[0]; // NOLINT
458 output.cursor.x = arguments[1]; // NOLINT
459 return output;
460}
461
462} // namespace ftxui
TerminalInputParser(std::function< void(Event)> out)
static Event Special(std::string)
一个自定义事件,其含义由库用户定义。
代表一个事件。它可以是按键事件、终端大小调整等等...
#include "ftxui/component/component_base.hpp" // 用于 ComponentBase
const std::map< std::string, std::string > g_uniformize