terminal.cpp
1 #include "terminal.h" 2 3 #include <string.h> 4 5 //------------------------------------------ 6 // Terminal 7 //------------------------------------------ 8 console::Terminal::Terminal(char *buf, size_t cap) 9 : buf_(buf), cap_(cap), len_(0), cursor_(0), 10 last_char_(0), escape_state_(Normal) { 11 buf_[0] = '\0'; 12 } 13 14 console::KeyCode console::Terminal::process_byte(uint8_t byte) { 15 switch (escape_state_) { 16 case Normal: 17 switch (byte) { 18 case '\r': case '\n': return KeyCode::Enter; 19 case 0x01: return KeyCode::CtrlA; 20 case 0x02: return KeyCode::CtrlB; 21 case 0x03: return KeyCode::CtrlC; 22 case 0x04: return KeyCode::CtrlD; 23 case 0x05: return KeyCode::CtrlE; 24 case 0x06: return KeyCode::CtrlF; 25 case 0x08: return KeyCode::Backspace; 26 case 0x09: return KeyCode::Tab; 27 case 0x0B: return KeyCode::CtrlK; 28 case 0x0C: return KeyCode::CtrlL; 29 case 0x0E: return KeyCode::CtrlN; 30 case 0x10: return KeyCode::CtrlP; 31 case 0x12: return KeyCode::CtrlR; 32 case 0x14: return KeyCode::CtrlT; 33 case 0x15: return KeyCode::CtrlU; 34 case 0x17: return KeyCode::CtrlW; 35 case 0x1B: 36 escape_state_ = Escape; 37 return KeyCode::None; 38 case 0x7F: return KeyCode::Backspace; 39 default: 40 if (byte >= 0x20 && byte < 0x7F) { 41 last_char_ = byte; 42 return KeyCode::Char; 43 } 44 return KeyCode::None; 45 } 46 47 case Escape: 48 if (byte == '[') { 49 escape_state_ = Bracket; 50 return KeyCode::None; 51 } 52 escape_state_ = Normal; 53 return KeyCode::Escape; 54 55 case Bracket: 56 escape_state_ = Normal; 57 switch (byte) { 58 case 'A': return KeyCode::ArrowUp; 59 case 'B': return KeyCode::ArrowDown; 60 case 'C': return KeyCode::ArrowRight; 61 case 'D': return KeyCode::ArrowLeft; 62 case 'H': return KeyCode::Home; 63 case 'F': return KeyCode::End; 64 case '1': return KeyCode::Home; // ESC[1~ (some terminals) 65 case '3': return KeyCode::Delete; // ESC[3~ 66 case '4': return KeyCode::End; // ESC[4~ (some terminals) 67 default: return KeyCode::None; 68 } 69 } 70 71 return KeyCode::None; 72 } 73 74 console::TerminalEvent console::Terminal::handle_key(KeyCode key, uint8_t ch) { 75 switch (key) { 76 case KeyCode::Enter: 77 return len_ == 0 ? TerminalEvent::EmptyCommand : TerminalEvent::CommandReady; 78 79 case KeyCode::Backspace: 80 if (cursor_ > 0 && len_ > 0) { 81 memmove(buf_ + cursor_ - 1, buf_ + cursor_, len_ - cursor_); 82 cursor_--; 83 len_--; 84 buf_[len_] = '\0'; 85 return TerminalEvent::BufferChanged; 86 } 87 return TerminalEvent::None; 88 89 case KeyCode::Delete: 90 if (cursor_ < len_) { 91 memmove(buf_ + cursor_, buf_ + cursor_ + 1, len_ - cursor_ - 1); 92 len_--; 93 buf_[len_] = '\0'; 94 return TerminalEvent::BufferChanged; 95 } 96 return TerminalEvent::None; 97 98 case KeyCode::ArrowLeft: 99 case KeyCode::CtrlB: 100 if (cursor_ > 0) { 101 cursor_--; 102 return TerminalEvent::CursorMoved; 103 } 104 return TerminalEvent::None; 105 106 case KeyCode::ArrowRight: 107 case KeyCode::CtrlF: 108 if (cursor_ < len_) { 109 cursor_++; 110 return TerminalEvent::CursorMoved; 111 } 112 return TerminalEvent::None; 113 114 case KeyCode::Home: 115 case KeyCode::CtrlA: 116 if (cursor_ > 0) { 117 cursor_ = 0; 118 return TerminalEvent::CursorHome; 119 } 120 return TerminalEvent::None; 121 122 case KeyCode::End: 123 case KeyCode::CtrlE: 124 if (cursor_ < len_) { 125 cursor_ = len_; 126 return TerminalEvent::CursorEnd; 127 } 128 return TerminalEvent::None; 129 130 case KeyCode::ArrowUp: 131 case KeyCode::CtrlP: 132 return TerminalEvent::HistoryPrevious; 133 134 case KeyCode::ArrowDown: 135 case KeyCode::CtrlN: 136 return TerminalEvent::HistoryNext; 137 138 case KeyCode::CtrlC: 139 return TerminalEvent::Interrupt; 140 141 case KeyCode::CtrlD: 142 if (len_ == 0) return TerminalEvent::EndOfFile; 143 if (cursor_ < len_) { 144 memmove(buf_ + cursor_, buf_ + cursor_ + 1, len_ - cursor_ - 1); 145 len_--; 146 buf_[len_] = '\0'; 147 return TerminalEvent::BufferChanged; 148 } 149 return TerminalEvent::None; 150 151 case KeyCode::CtrlK: 152 if (cursor_ < len_) { 153 len_ = cursor_; 154 buf_[len_] = '\0'; 155 return TerminalEvent::KillToEnd; 156 } 157 return TerminalEvent::None; 158 159 case KeyCode::CtrlL: 160 return TerminalEvent::ClearScreen; 161 162 case KeyCode::CtrlR: 163 return TerminalEvent::Redraw; 164 165 case KeyCode::CtrlT: 166 if (cursor_ > 0 && cursor_ < len_) { 167 char tmp = buf_[cursor_ - 1]; 168 buf_[cursor_ - 1] = buf_[cursor_]; 169 buf_[cursor_] = tmp; 170 if (cursor_ < len_) cursor_++; 171 return TerminalEvent::SwapChars; 172 } 173 return TerminalEvent::None; 174 175 case KeyCode::CtrlW: 176 return TerminalEvent::DeleteWord; 177 178 case KeyCode::CtrlU: 179 return TerminalEvent::ClearLine; 180 181 case KeyCode::Char: 182 if (len_ < cap_ - 1) { 183 if (cursor_ < len_) 184 memmove(buf_ + cursor_ + 1, buf_ + cursor_, len_ - cursor_); 185 buf_[cursor_] = (char)last_char_; 186 cursor_++; 187 len_++; 188 buf_[len_] = '\0'; 189 return TerminalEvent::BufferChanged; 190 } 191 return TerminalEvent::None; 192 193 default: 194 return TerminalEvent::None; 195 } 196 } 197 198 const char *console::Terminal::buffer_str() const { 199 return buf_; 200 } 201 202 size_t console::Terminal::buffer_length() const { 203 return len_; 204 } 205 206 size_t console::Terminal::cursor_position() const { 207 return cursor_; 208 } 209 210 void console::Terminal::set_buffer(const char *content) { 211 size_t slen = strlen(content); 212 if (slen >= cap_) slen = cap_ - 1; 213 memcpy(buf_, content, slen); 214 buf_[slen] = '\0'; 215 len_ = slen; 216 cursor_ = slen; 217 } 218 219 void console::Terminal::clear_buffer() { 220 len_ = 0; 221 cursor_ = 0; 222 buf_[0] = '\0'; 223 } 224 225 const char *console::Terminal::take_command() { 226 buf_[len_] = '\0'; 227 len_ = 0; 228 cursor_ = 0; 229 return buf_; 230 }