/ firmware / src / console / terminal.cpp
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  }