libxr 1.0
Want to be the best embedded framework
Loading...
Searching...
No Matches
terminal.hpp
1#pragma once
2
3#include <cctype>
4#include <cstddef>
5#include <cstdint>
6#include <cstdio>
7#include <cstring>
8#include <utility>
9
10#include "libxr_rw.hpp"
11#include "libxr_string.hpp"
12#include "ramfs.hpp"
13#include "semaphore.hpp"
14#include "stack.hpp"
15
16namespace LibXR
17{
18
28template <size_t READ_BUFF_SIZE = 32, size_t MAX_LINE_SIZE = READ_BUFF_SIZE,
29 size_t MAX_ARG_NUMBER = 5, size_t MAX_HISTORY_NUMBER = 5>
31{
32 private:
33 static constexpr char CLEAR_ALL[] =
34 "\033[2J\033[1H";
35 static constexpr char CLEAR_LINE[] =
36 "\033[2K\r";
37 static constexpr char CLEAR_BEHIND[] =
38 "\033[K";
39 static constexpr char KEY_RIGHT[] = "\033[C";
40 static constexpr char KEY_LEFT[] = "\033[D";
41 static constexpr char KEY_SAVE[] = "\033[s";
42 static constexpr char KEY_LOAD[] = "\033[u";
43 static constexpr char DELETE_CHAR[] =
44 "\b \b";
45
54 char *StrchrRev(char *str, char c)
55 {
56 auto len = strlen(str);
57 for (int i = static_cast<int>(len - 1); i >= 0; i--)
58 {
59 if (str[i] == c)
60 {
61 return str + i;
62 }
63 }
64 return nullptr;
65 }
66
67 public:
73 enum class Mode : uint8_t
74 {
75 CRLF = 0,
76 LF = 1,
77 CR = 2
78 };
79
91 ReadPort *read_port = STDIO::read_, WritePort *write_port = STDIO::write_,
93 : read_status_(ReadOperation::OperationPollingStatus::READY),
94 write_status_(WriteOperation::OperationPollingStatus::READY),
95 MODE(MODE),
96 write_op_(write_status_),
97 read_(read_port),
98 write_(write_port),
103 {
104 }
105
108
109 const Mode MODE;
115
118 int offset_ = 0;
121 size_t arg_number_ = 0;
123 int history_index_ = -1;
124 bool linefeed_flag_ = false;
125
130 void LineFeed()
131 {
132 if (MODE == Mode::CRLF)
133 {
134 (*write_)(ConstRawData("\r\n"), write_op_);
135 }
136 else if (MODE == Mode::LF)
137 {
138 (*write_)(ConstRawData('\n'), write_op_);
139 }
140 else if (MODE == Mode::CR)
141 {
142 (*write_)(ConstRawData('\r'), write_op_);
143 }
144 }
145
151 {
152 (*write_)(ConstRawData(KEY_SAVE), write_op_);
155 write_op_);
156 (*write_)(ConstRawData(KEY_LOAD), write_op_);
157 }
158
164 bool CanDisplayChar() { return input_line_.EmptySize() > 1; }
165
171 bool CanDeleteChar() { return input_line_.Size() + offset_ > 0; }
172
178 void AddCharToInputLine(char data)
179 {
180 if (offset_ == 0)
181 {
182 input_line_.Push(data);
183 }
184 else
185 {
187 }
188 input_line_[input_line_.Size()] = '\0';
189 }
190
197 void DisplayChar(char data)
198 {
199 bool use_history = false;
200
201 if (history_index_ >= 0)
202 {
204 use_history = true;
205 }
206
207 if (CanDisplayChar())
208 {
209 AddCharToInputLine(data);
210 if (use_history)
211 {
212 ShowHistory();
213 }
214 else
215 {
217 }
218 if (offset_ != 0)
219 {
221 }
222 }
223 }
224
231 {
232 if (offset_ == 0)
233 {
235 }
236 else
237 {
239 }
240 input_line_[input_line_.Size()] = '\0';
241 }
242
249 {
250 bool use_history = false;
251
252 if (history_index_ >= 0)
253 {
255 use_history = true;
256 }
257
258 if (CanDeleteChar())
259 {
261 if (use_history)
262 {
263 ShowHistory();
264 }
265 else
266 {
268 }
269
270 if (offset_ != 0)
271 {
273 }
274 }
275 }
276
282 {
283 (*write_)(ConstRawData(ramfs_.root_->name, strlen(ramfs_.root_->name)), write_op_);
284 if (current_dir_ == &ramfs_.root_)
285 {
286 (*write_)(ConstRawData(":/"), write_op_);
287 }
288 else
289 {
290 (*write_)(ConstRawData(":"), write_op_);
291 (*write_)(ConstRawData(current_dir_->data_.name), write_op_);
292 }
293
294 (*write_)(ConstRawData("$ "), write_op_);
295 }
296
302
307 void Clear() { (*write_)(ConstRawData(CLEAR_ALL), write_op_); }
308
314 {
315 ClearLine();
316 ShowHeader();
317 offset_ = 0;
318 if (history_index_ >= 0)
319 {
320 (*write_)(ConstRawData(history_[-history_index_ - 1].Raw(),
321 history_[-history_index_ - 1].Length()),
322 write_op_);
323 }
324 else
325 {
327 }
328 }
329
336 {
338 for (size_t i = 0; i < history_[-history_index_ - 1].Length(); i++)
339 {
341 }
342 history_index_ = -1;
343 offset_ = 0;
344 }
345
351 {
352 input_line_.Push('\0');
353
354 if (history_.EmptySize() == 0)
355 {
356 history_.Pop();
357 }
358 history_.Push(*reinterpret_cast<String<MAX_LINE_SIZE> *>(&input_line_[0]));
359 }
360
365 void GetArgs()
366 {
367 for (int i = 0; input_line_[i] != '\0'; i++)
368 {
369 if (input_line_[i] == ' ')
370 {
371 input_line_[i] = '\0';
372 }
373 else if (i == 0 || input_line_[i - 1] == '\0')
374 {
376 {
377 return;
378 }
380 }
381 }
382 }
383
392 {
393 size_t index = 0;
395
396 if (*path == '/')
397 {
398 index++;
399 dir = &ramfs_.root_;
400 }
401
402 for (size_t i = 0; i < MAX_LINE_SIZE; i++)
403 {
404 auto tmp = strchr(path + index, '/');
405 if (tmp == nullptr)
406 {
407 return dir->FindDir(path + index);
408 }
409 else if (tmp == path + index)
410 {
411 return nullptr;
412 }
413 else
414 {
415 tmp[0] = '\0';
416 dir = dir->FindDir(path + index);
417 tmp[0] = '/';
418 index += tmp - path + 1;
419 if (path[index] == '\0' || dir == nullptr)
420 {
421 return dir;
422 }
423 }
424 }
425
426 return nullptr;
427 }
428
436 RamFS::File *Path2File(char *path)
437 {
438 auto name = StrchrRev(path, '/');
439
440 if (name == nullptr)
441 {
442 return current_dir_->FindFile(path);
443 }
444
445 if (name[1] == '\0')
446 {
447 return nullptr;
448 }
449
450 *name = '\0';
452 *name = '/';
453 if (dir != nullptr)
454 {
455 return dir->FindFile(name + 1);
456 }
457 else
458 {
459 return nullptr;
460 }
461 }
462
468 {
469 AddHistory();
470
471 GetArgs();
472
474 {
475 return;
476 }
477
478 if (strcmp(arg_tab_[0], "cd") == 0)
479 {
481 if (dir != nullptr)
482 {
484 }
485 LineFeed();
486 return;
487 }
488
489 if (strcmp(arg_tab_[0], "ls") == 0)
490 {
492 {
493 switch (item->type)
494 {
496 (*(this->write_))(ConstRawData("d "), this->write_op_);
497 break;
499 (*(this->write_))(ConstRawData("f "), this->write_op_);
500 break;
502 (*(this->write_))(ConstRawData("c "), this->write_op_);
503 break;
505 (*(this->write_))(ConstRawData("b "), this->write_op_);
506 break;
507 default:
508 (*(this->write_))(ConstRawData("? "), this->write_op_);
509 break;
510 }
511 (*(this->write_))(ConstRawData(item.data_.name), this->write_op_);
512 this->LineFeed();
513 return ErrorCode::OK;
514 };
515
516 current_dir_->data_.rbt.Foreach<RamFS::FsNode>(ls_fun);
517 return;
518 }
519
520 auto ans = Path2File(arg_tab_[0]);
521 if (ans == nullptr)
522 {
523 (*write_)(ConstRawData("Command not found."), write_op_);
524 LineFeed();
525 return;
526 }
527
528 if ((*ans)->type != RamFS::FileType::EXEC)
529 {
530 (*write_)(ConstRawData("Not an executable file."), write_op_);
531 LineFeed();
532 return;
533 }
534
535 (*ans)->Run(arg_number_, arg_tab_);
536 }
537
545 {
546 char *buff = static_cast<char *>(raw_data.addr_);
547 for (size_t i = 0; i < raw_data.size_; i++)
548 {
549 HandleCharacter(buff[i]);
550 }
551 }
552
559 void HandleAnsiCharacter(char data)
560 {
561 if (flag_ansi_ == 1)
562 {
563 if (isprint(data))
564 {
565 flag_ansi_++;
566 }
567 else
568 {
569 flag_ansi_ = 0;
570 }
571 }
572 else if (flag_ansi_ == 2)
573 {
574 switch (data)
575 {
576 case 'A':
577 if (history_index_ < int(history_.Size()) - 1)
578 {
580 ShowHistory();
581 }
582 break;
583 case 'B':
584 if (history_index_ >= 0)
585 {
587 ShowHistory();
588 }
589 break;
590 case 'C':
591 if (history_index_ >= 0)
592 {
594 ShowHistory();
595 }
596 if (offset_ < 0)
597 {
598 offset_++;
599 (*write_)(ConstRawData(KEY_RIGHT, sizeof(KEY_RIGHT) - 1), write_op_);
600 }
601
602 break;
603 case 'D':
604 if (history_index_ >= 0)
605 {
607 ShowHistory();
608 }
609 if (offset_ + input_line_.Size() > 0)
610 {
611 offset_--;
612 (*write_)(ConstRawData(KEY_LEFT, sizeof(KEY_LEFT) - 1), write_op_);
613 }
614 break;
615 default:
616 break;
617 }
618
619 flag_ansi_ = 0;
620 }
621 }
622
628 {
629 /* skip space */
630 char *path = &input_line_[0];
631 while (*path == ' ')
632 {
633 path++;
634 }
635
636 /* find last '/' in first argument */
637 char *tmp = path, *path_end = path;
638
639 while (*tmp != ' ' && *tmp != '\0')
640 {
641 if (*tmp == '/')
642 {
643 path_end = tmp;
644 }
645 tmp++;
646 }
647
648 /* return if not need complete */
649 if (tmp - &input_line_[0] != static_cast<int>(input_line_.Size() + offset_))
650 {
651 return;
652 }
653
654 /* get start of prefix */
655 char *prefix_start = nullptr;
656 RamFS::Dir *dir = nullptr;
657
658 if (path_end == path)
659 {
662 }
663 else
664 {
666 }
667
668 /* find dir*/
669 if (dir == nullptr)
670 {
671 *path_end = '\0';
672 dir = Path2Dir(path);
673 *path_end = '/';
674 if (dir == nullptr)
675 {
676 return;
677 }
678 }
679
680 /* prepre for match */
682 uint32_t number = 0;
683 size_t same_char_number = 0;
684
685 if (*prefix_start == '/')
686 {
687 prefix_start++;
688 }
689
690 int prefix_len = static_cast<int>(tmp - prefix_start);
691
693 {
694 if (strncmp(node->name, prefix_start, prefix_len) == 0)
695 {
696 ans_node = &node;
697 number++;
698 }
699
700 return ErrorCode::OK;
701 };
702
703 /* start match */
704 (*dir)->rbt.Foreach<RamFS::FsNode>(foreach_fun_find);
705
706 if (number == 0)
707 {
708 return;
709 }
710 else if (number == 1)
711 {
712 auto name_len = strlen(ans_node->data_.name);
713 for (size_t i = 0; i < name_len - prefix_len; i++)
714 {
715 DisplayChar(ans_node->data_.name[i + prefix_len]);
716 }
717 }
718 else
719 {
720 ans_node = nullptr;
721 LineFeed();
722
724 {
725 if (strncmp(node->name, prefix_start, prefix_len) == 0)
726 {
727 auto name_len = strlen(node->name);
728 (*this->write_)(ConstRawData(node->name, name_len), this->write_op_);
729 this->LineFeed();
730 if (ans_node == nullptr)
731 {
732 ans_node = &node;
734 return ErrorCode::OK;
735 }
736
737 for (size_t i = 0; i < name_len; i++)
738 {
739 if (node->name[i] != ans_node->data_.name[i])
740 {
742 break;
743 }
744 }
745
747 {
749 }
750
751 ans_node = &node;
752 }
753
754 return ErrorCode::OK;
755 };
756
757 (*dir)->rbt.Foreach<RamFS::FsNode>(foreach_fun_show);
758
759 ShowHeader();
761
762 for (size_t i = 0; i < same_char_number - prefix_len; i++)
763 {
764 DisplayChar(ans_node->data_.name[i + prefix_len]);
765 }
766 }
767 }
768
775 {
776 if (data != '\r' && data != '\n')
777 {
778 linefeed_flag_ = false;
779 }
780
781 switch (data)
782 {
783 case '\n':
784 case '\r':
785 if (linefeed_flag_)
786 {
787 linefeed_flag_ = false;
788 return;
789 }
790 if (history_index_ >= 0)
791 {
793 }
794 LineFeed();
795 if (input_line_.Size() > 0)
796 {
798 arg_number_ = 0;
799 }
800 ShowHeader();
802 input_line_[0] = '\0';
803 offset_ = 0;
804 break;
805 case 0x7f:
806 DeleteChar();
807 break;
808 case '\t':
809 AutoComplete();
810 break;
811 case '\033':
812 flag_ansi_ = 1;
813 break;
814 default:
815 break;
816 }
817 }
818
824 void HandleCharacter(char data)
825 {
826 if (flag_ansi_)
827 {
829 }
830 else if (isprint(data))
831 {
832 DisplayChar(data);
833 }
834 else
835 {
837 }
838 }
839
856 static void ThreadFun(Terminal *term)
857 {
858 RawData buff = term->read_buff_;
859 buff.size_ = LibXR::min(LibXR::max(1u, term->read_->Size()), READ_BUFF_SIZE);
860
861 Semaphore sem;
862 ReadOperation op(sem);
863
864 while (true)
865 {
866 buff.size_ = LibXR::min(LibXR::max(1u, term->read_->Size()), READ_BUFF_SIZE);
867 if ((*term->read_)(buff, op) == ErrorCode::OK)
868 {
869 buff.size_ = term->read_->read_size_;
870 term->Parse(buff);
871 }
872 }
873 }
874
890 static void TaskFun(Terminal *term)
891 {
892 RawData buff = term->read_buff_;
893 buff.size_ = LibXR::min(LibXR::max(1u, term->read_->Size()), READ_BUFF_SIZE);
894
895 ReadOperation op(term->read_status_);
896
897 while (true)
898 {
899 switch (term->read_status_)
900 {
901 case ReadOperation::OperationPollingStatus::READY:
902 buff.size_ = LibXR::min(LibXR::max(1u, term->read_->Size()), READ_BUFF_SIZE);
903 (*term->read_)(buff, op);
904 continue;
905 case ReadOperation::OperationPollingStatus::RUNNING:
906 return;
907 case ReadOperation::OperationPollingStatus::DONE:
908 buff.size_ = term->read_->read_size_;
909 if (buff.size_ > 0)
910 {
911 term->Parse(buff);
912 }
913 buff.size_ = LibXR::min(LibXR::max(1u, term->read_->Size()), READ_BUFF_SIZE);
914 (*term->read_)(buff, op);
915 return;
916 }
917 }
918 }
919};
920} // namespace LibXR
常量原始数据封装类。 A class for encapsulating constant raw data.
基于 BaseQueue 的泛型队列模板类 (Generic queue template class based on BaseQueue).
Definition queue.hpp:354
红黑树的泛型数据节点,继承自 BaseNode (Generic data node for Red-Black Tree, inheriting from BaseNode).
Definition rbt.hpp:64
Data data_
存储的数据 (Stored data).
Definition rbt.hpp:99
目录类,继承自 RBTree 节点,用于管理文件、子目录和设备 Directory class, inheriting from RBTree node, used for managing files...
Definition ramfs.hpp:250
File * FindFile(const char *name)
查找当前目录中的文件 Finds a file in the current directory
Definition ramfs.hpp:290
文件系统节点基类,所有文件和目录均继承自该类 Base class for file system nodes; all files and directories inherit from this
Definition ramfs.hpp:81
轻量级的内存文件系统,实现基本的文件、目录和设备管理 A lightweight in-memory file system implementing basic file,...
Definition ramfs.hpp:20
@ STORAGE
存储 Storage
@ DIR
目录 Directory
@ EXEC
可执行 Executable
Dir root_
文件系统的根目录 Root directory of the file system
Definition ramfs.hpp:624
原始数据封装类。 A class for encapsulating raw data.
size_t size_
数据大小(字节)。 The size of the data (in bytes).
ReadPort class for handling read operations.
Definition libxr_rw.hpp:309
static ReadPort * read_
Read port instance. 读取端口。
Definition libxr_rw.hpp:693
static WritePort * write_
Write port instance. 写入端口。
Definition libxr_rw.hpp:694
信号量类,实现线程同步机制 Semaphore class implementing thread synchronization
Definition semaphore.hpp:23
线程安全的栈数据结构 / Thread-safe stack data structure
Definition stack.hpp:21
ErrorCode Delete(uint32_t index)
删除指定位置的数据 / Deletes data at a specified position
Definition stack.hpp:195
uint32_t EmptySize() const
获取栈的剩余可用空间 / Returns the remaining available space in the stack
Definition stack.hpp:63
ErrorCode Insert(const Data &data, uint32_t index)
在指定位置插入数据 / Inserts data at a specified position
Definition stack.hpp:160
void Reset()
重置栈 / Resets the stack
Definition stack.hpp:219
ErrorCode Push(const Data &data)
向栈中推入数据 / Pushes data onto the stack
Definition stack.hpp:72
ErrorCode Pop(Data &data)
从栈中弹出数据 / Pops data from the stack
Definition stack.hpp:93
uint32_t Size() const
获取栈中当前元素数量 / Returns the number of elements currently in the stack
Definition stack.hpp:58
A fixed-length string class with safe operations.
终端类,实现一个基于 RamFS 的基本命令行接口 Terminal class implementing a basic command-line interface based on RamFS
Definition terminal.hpp:31
bool CanDisplayChar()
检查是否可以显示字符 Checks if a character can be displayed
Definition terminal.hpp:164
RamFS::Dir * Path2Dir(char *path)
将路径字符串解析为目录对象 Converts a path string into a directory object
Definition terminal.hpp:391
char * arg_tab_[MAX_ARG_NUMBER]
命令参数列表 Command argument list
Definition terminal.hpp:120
void HandleAnsiCharacter(char data)
解析输入数据流,将其转换为字符并处理 Parses the input data stream, converting it into characters and processing them
Definition terminal.hpp:559
Queue< LibXR::String< MAX_LINE_SIZE > > history_
历史命令 History of commands
Definition terminal.hpp:122
void UpdateDisplayPosition()
更新光标位置 Updates cursor position
Definition terminal.hpp:150
static constexpr char KEY_LEFT[]
左箭头键 Left arrow key
Definition terminal.hpp:40
size_t arg_number_
参数数量 Number of arguments
Definition terminal.hpp:121
uint8_t flag_ansi_
ANSI 控制字符状态 ANSI control character state.
Definition terminal.hpp:117
Terminal(LibXR::RamFS &ramfs, RamFS::Dir *current_dir=nullptr, ReadPort *read_port=STDIO::read_, WritePort *write_port=STDIO::write_, Mode MODE=Mode::CRLF)
终端构造函数,初始化文件系统、I/O 端口和当前目录 Constructor to initialize the terminal with file system,...
Definition terminal.hpp:90
void AutoComplete()
实现命令自动补全,匹配目录或文件名 Implements command auto-completion by matching directories or file names
Definition terminal.hpp:627
void HandleCharacter(char data)
处理输入字符,根据类型调用相应的处理函数 Handles input characters, dispatching them to the appropriate handler
Definition terminal.hpp:824
void HandleControlCharacter(char data)
处理控制字符,包括换行、删除、制表符等 Handles control characters such as newline, delete, and tab
Definition terminal.hpp:774
WriteOperation write_op_
终端写操作 Terminal write operation
Definition terminal.hpp:110
bool CanDeleteChar()
检查是否可以删除字符 Checks if a character can be deleted
Definition terminal.hpp:171
static constexpr char KEY_LOAD[]
恢复光标位置 Restore cursor position
Definition terminal.hpp:42
char read_buff_[READ_BUFF_SIZE]
读取缓冲区 Read buffer
Definition terminal.hpp:114
int history_index_
当前历史索引 Current history index
Definition terminal.hpp:123
RamFS & ramfs_
关联的文件系统 Associated file system
Definition terminal.hpp:113
char * StrchrRev(char *str, char c)
反向查找字符串中的特定字符 Finds a specific character in a string from the end
Definition terminal.hpp:54
int offset_
光标偏移 Cursor offset
Definition terminal.hpp:118
void ClearLine()
清除当前行 Clears the current line
Definition terminal.hpp:301
void CopyHistoryToInputLine()
将历史命令复制到输入行,并重置历史索引和光标偏移 Copies the history command to the input line and resets history index and cu...
Definition terminal.hpp:335
void AddHistory()
将当前输入行添加到历史记录 Adds the current input line to the history
Definition terminal.hpp:350
void DisplayChar(char data)
在终端上显示字符,并根据历史记录模式进行相应操作 Displays a character on the terminal and updates accordingly if history mode...
Definition terminal.hpp:197
ReadPort * read_
读取端口 Read port
Definition terminal.hpp:111
RamFS::File * Path2File(char *path)
将路径字符串解析为文件对象 Converts a path string into a file object
Definition terminal.hpp:436
static constexpr char CLEAR_LINE[]
清除当前行命令 Clear current line command
Definition terminal.hpp:35
void ShowHistory()
显示历史记录中的输入行,更新终端显示 Displays the input line from history and updates the terminal display
Definition terminal.hpp:313
static constexpr char DELETE_CHAR[]
退格删除字符 Backspace delete character
Definition terminal.hpp:43
static constexpr char CLEAR_ALL[]
清屏命令 Clear screen command
Definition terminal.hpp:33
void LineFeed()
执行换行操作 Performs a line feed operation
Definition terminal.hpp:130
void Clear()
清除整个终端屏幕 Clears the entire terminal screen
Definition terminal.hpp:307
void RemoveCharFromInputLine()
从输入行中删除字符,支持在光标位置删除 Removes a character from the input line, supports deletion at the cursor position
Definition terminal.hpp:230
WritePort * write_
写入端口 Write port
Definition terminal.hpp:112
static constexpr char CLEAR_BEHIND[]
清除光标后内容命令 Clear content after cursor command
Definition terminal.hpp:37
static void TaskFun(Terminal *term)
终端任务函数,以定时器任务方式驱动终端 Terminal task function, drives the terminal using a scheduled task
Definition terminal.hpp:890
void GetArgs()
解析输入行,将其拆分为参数数组 Parses the input line and splits it into argument array
Definition terminal.hpp:365
void Parse(RawData &raw_data)
解析输入数据流,将其转换为字符并处理 Parses the input data stream, converting it into characters and processing them
Definition terminal.hpp:544
void ShowHeader()
显示终端提示符,包括当前目录信息 Displays the terminal prompt, including the current directory information
Definition terminal.hpp:281
RamFS::Dir * current_dir_
当前目录 Current directory
Definition terminal.hpp:116
Stack< char > input_line_
输入行缓冲区 Input line buffer
Definition terminal.hpp:119
static constexpr char KEY_SAVE[]
保存光标位置 Save cursor position
Definition terminal.hpp:41
static void ThreadFun(Terminal *term)
终端线程函数,以独立线程方式持续驱动终端 Terminal thread function, continuously drives the terminal as an independent thr...
Definition terminal.hpp:856
void ExecuteCommand()
解析并执行输入的命令 Parses and executes the entered command
Definition terminal.hpp:467
void DeleteChar()
处理删除字符操作,支持回退删除,并在历史模式下更新显示 Handles the delete character operation, supports backspace deletion,...
Definition terminal.hpp:248
Mode
终端换行模式 Line feed modes for the terminal
Definition terminal.hpp:74
@ CR
仅回车 Carriage Return (\r)
@ CRLF
回车换行 Carriage Return + Line Feed (\r )
@ LF
仅换行 Line Feed ( )
bool linefeed_flag_
换行标志 Line feed flag
Definition terminal.hpp:124
const Mode MODE
终端换行模式 Terminal line feed mode
Definition terminal.hpp:109
static constexpr char KEY_RIGHT[]
右箭头键 Right arrow key
Definition terminal.hpp:39
void AddCharToInputLine(char data)
向输入行中添加字符,支持在光标位置插入 Adds a character to the input line, supports insertion at the cursor position
Definition terminal.hpp:178
WritePort class for handling write operations.
Definition libxr_rw.hpp:503
LibXR Color Control Library / LibXR终端颜色控制库
constexpr auto max(T1 a, T2 b) -> typename std::common_type< T1, T2 >::type
计算两个数的最大值
constexpr auto min(T1 a, T2 b) -> typename std::common_type< T1, T2 >::type
计算两个数的最小值