mirror of
https://github.com/IoTManagerProject/IoTManager.git
synced 2026-03-27 14:42:18 +03:00
Terminal
This commit is contained in:
322
src/Module/Terminal.cpp
Normal file
322
src/Module/Terminal.cpp
Normal file
@@ -0,0 +1,322 @@
|
||||
#include "Module/Terminal.h"
|
||||
|
||||
#include "Utils/TimeUtils.h"
|
||||
|
||||
#define INPUT_MAX_LENGHT 255
|
||||
|
||||
Terminal::Terminal(Stream *stream) : _stream{stream},
|
||||
_line(INPUT_MAX_LENGHT),
|
||||
_cc_pos(0),
|
||||
_color(false),
|
||||
_controlCodes(false),
|
||||
_echo(false),
|
||||
_eol(CRLF){};
|
||||
|
||||
void Terminal::setStream(Stream *stream) {
|
||||
_stream = stream;
|
||||
}
|
||||
|
||||
Terminal::~Terminal() {}
|
||||
|
||||
void Terminal::setOnReadLine(TerminalInputEventHandler h) { inputHandler_ = h; }
|
||||
|
||||
void Terminal::setOnEvent(TerminalEventHandler h) { eventHandler_ = h; }
|
||||
|
||||
bool Terminal::available() {
|
||||
return _stream != nullptr ? _stream->available() : false;
|
||||
}
|
||||
|
||||
void Terminal::setEOL(EOLType_t eol) {
|
||||
_eol = eol;
|
||||
}
|
||||
|
||||
void Terminal::enableEcho(bool enabled) {
|
||||
_echo = enabled;
|
||||
}
|
||||
|
||||
void Terminal::enableColors(bool enabled) {
|
||||
_color = enabled;
|
||||
}
|
||||
|
||||
void Terminal::enableControlCodes(bool enabled) {
|
||||
_controlCodes = enabled;
|
||||
}
|
||||
|
||||
void Terminal::quit() {}
|
||||
|
||||
void Terminal::loop() {
|
||||
if (_stream == nullptr || !_stream->available()) return;
|
||||
|
||||
sint8_t moveX = 0;
|
||||
sint8_t moveY = 0;
|
||||
|
||||
char c = _stream->read();
|
||||
|
||||
_lastReceived = millis();
|
||||
|
||||
if (state == ST_INACTIVE) {
|
||||
// wait for CR
|
||||
if (c == CHAR_CR) {
|
||||
if (eventHandler_) {
|
||||
eventHandler_(EVENT_OPEN, _stream);
|
||||
state = ST_NORMAL;
|
||||
}
|
||||
}
|
||||
// or ignore all other
|
||||
return;
|
||||
}
|
||||
|
||||
if (c == CHAR_LF || c == CHAR_NULL || c == CHAR_BIN)
|
||||
return;
|
||||
|
||||
// Esc
|
||||
if (c == CHAR_ESC || c == 195) {
|
||||
state = ST_ESC_SEQ;
|
||||
_cc_pos = 0;
|
||||
for (size_t i = 0; i < 2; ++i) {
|
||||
bool timeout = false;
|
||||
while (!_stream->available() &&
|
||||
!(timeout = millis_since(_lastReceived) > 10)) {
|
||||
delay(0);
|
||||
}
|
||||
if (timeout) {
|
||||
state = ST_NORMAL;
|
||||
break;
|
||||
}
|
||||
_lastReceived = millis();
|
||||
c = _stream->read();
|
||||
_cc_buf[_cc_pos] = c;
|
||||
if ((c == '[') || ((c >= 'A' && c <= 'Z') || c == '~')) {
|
||||
_cc_pos++;
|
||||
_cc_buf[++_cc_pos] = '\x00';
|
||||
}
|
||||
}
|
||||
uint8_t i;
|
||||
for (i = 0; i < 10; ++i) {
|
||||
if (strcmp(_cc_buf, keyMap[i].cc) == 0) {
|
||||
c = keyMap[i].ch;
|
||||
state = ST_NORMAL;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (state == ST_ESC_SEQ) {
|
||||
state = ST_NORMAL;
|
||||
return;
|
||||
}
|
||||
|
||||
// WHEN NORMAL
|
||||
if (state == ST_NORMAL) {
|
||||
if (c == CHAR_ESC) {
|
||||
if (!_line.available()) {
|
||||
// QUIT
|
||||
state = ST_INACTIVE;
|
||||
if (eventHandler_)
|
||||
eventHandler_(EVENT_CLOSE, _stream);
|
||||
} else {
|
||||
// CLEAR
|
||||
_line.clear();
|
||||
if (_controlCodes) {
|
||||
clear_line();
|
||||
} else {
|
||||
println();
|
||||
}
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
switch (c) {
|
||||
case CHAR_CR:
|
||||
println();
|
||||
if (inputHandler_)
|
||||
inputHandler_(_line.c_str());
|
||||
_line.clear();
|
||||
moveY++;
|
||||
break;
|
||||
case CHAR_TAB:
|
||||
if (eventHandler_)
|
||||
eventHandler_(EVENT_TAB, _stream);
|
||||
return;
|
||||
case KEY_LEFT:
|
||||
if (_line.prev())
|
||||
moveX--;
|
||||
break;
|
||||
case KEY_RIGHT:
|
||||
if (_line.next())
|
||||
moveX++;
|
||||
break;
|
||||
case KEY_HOME:
|
||||
moveX = -1 * _line.home();
|
||||
break;
|
||||
case KEY_END:
|
||||
moveX = _line.end();
|
||||
break;
|
||||
case CHAR_BS:
|
||||
case KEY_DEL:
|
||||
if (_line.backspace()) {
|
||||
backsp();
|
||||
moveX--;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
// printable ascii 7bit or printable 8bit ISO8859
|
||||
if ((c & '\x7F') >= 32 && (c & '\x7F') < 127)
|
||||
if (_line.write(c)) {
|
||||
if (_echo) write(c);
|
||||
moveX++;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
// if (controlCodesEnabled)
|
||||
// move(startY + moveY, startX + moveX);
|
||||
}
|
||||
}
|
||||
|
||||
bool Terminal::setLine(const uint8_t *ptr, size_t size) {
|
||||
_line.clear();
|
||||
if (_line.write(ptr, size))
|
||||
print(_line.c_str());
|
||||
return true;
|
||||
}
|
||||
|
||||
CharBuffer &Terminal::getLine() { return _line; }
|
||||
|
||||
void Terminal::start() {
|
||||
if (_controlCodes) initscr();
|
||||
println();
|
||||
}
|
||||
|
||||
void Terminal::initscr() {
|
||||
write_P(SEQ_LOAD_G1);
|
||||
attrset(A_NORMAL);
|
||||
move(0, 0);
|
||||
clear();
|
||||
}
|
||||
|
||||
void Terminal::attrset(const uint16_t attr) {
|
||||
uint8_t i;
|
||||
|
||||
if (attr != this->attr) {
|
||||
this->write_P(SEQ_ATTRSET);
|
||||
|
||||
i = (attr & F_COLOR) >> 8;
|
||||
|
||||
if (i >= 1 && i <= 8) {
|
||||
this->write_P(SEQ_ATTRSET_FCOLOR);
|
||||
this->write(i - 1 + '0');
|
||||
}
|
||||
|
||||
i = (attr & B_COLOR) >> 12;
|
||||
|
||||
if (i >= 1 && i <= 8) {
|
||||
this->write_P(SEQ_ATTRSET_BCOLOR);
|
||||
this->write(i - 1 + '0');
|
||||
}
|
||||
|
||||
if (attr & A_REVERSE)
|
||||
this->write_P(SEQ_ATTRSET_REVERSE);
|
||||
if (attr & A_UNDERLINE)
|
||||
this->write_P(SEQ_ATTRSET_UNDERLINE);
|
||||
if (attr & A_BLINK)
|
||||
this->write_P(SEQ_ATTRSET_BLINK);
|
||||
if (attr & A_BOLD)
|
||||
this->write_P(SEQ_ATTRSET_BOLD);
|
||||
if (attr & A_DIM)
|
||||
this->write_P(SEQ_ATTRSET_DIM);
|
||||
this->write('m');
|
||||
this->attr = attr;
|
||||
}
|
||||
}
|
||||
|
||||
void Terminal::clear() { write_P(SEQ_CLEAR); }
|
||||
|
||||
void Terminal::clear_line() {
|
||||
write(CHAR_CR);
|
||||
write_P(ESC_CLEAR_EOL);
|
||||
}
|
||||
|
||||
void Terminal::move(uint8_t y, uint8_t x) {
|
||||
write_P(SEQ_CSI);
|
||||
writeByDigit(y + 1);
|
||||
write(';');
|
||||
writeByDigit(x + 1);
|
||||
write('H');
|
||||
curY = y;
|
||||
curX = x;
|
||||
}
|
||||
|
||||
void Terminal::writeByDigit(uint8_t i) {
|
||||
uint8_t ii;
|
||||
if (i >= 10) {
|
||||
if (i >= 100) {
|
||||
ii = i / 100;
|
||||
write(ii + '0');
|
||||
i -= 100 * ii;
|
||||
}
|
||||
ii = i / 10;
|
||||
write(ii + '0');
|
||||
i -= 10 * ii;
|
||||
}
|
||||
write(i + '0');
|
||||
}
|
||||
|
||||
void Terminal::backsp() {
|
||||
write(CHAR_BS);
|
||||
write(CHAR_SPACE);
|
||||
write(CHAR_BS);
|
||||
}
|
||||
|
||||
size_t Terminal::println(const char *str) {
|
||||
size_t n = print(str);
|
||||
return n += println();
|
||||
}
|
||||
|
||||
size_t Terminal::println(void) {
|
||||
size_t n = 0;
|
||||
switch (_eol) {
|
||||
case CRLF:
|
||||
n += write(CHAR_CR);
|
||||
n += write(CHAR_LF);
|
||||
break;
|
||||
case LF:
|
||||
n += write(CHAR_LF);
|
||||
break;
|
||||
case LFCR:
|
||||
n += write(CHAR_LF);
|
||||
n += write(CHAR_CR);
|
||||
break;
|
||||
case CR:
|
||||
n += write(CHAR_CR);
|
||||
break;
|
||||
}
|
||||
return n;
|
||||
}
|
||||
|
||||
size_t Terminal::write(uint8_t ch) {
|
||||
size_t n = 0;
|
||||
if (_stream)
|
||||
n = _stream->write(ch);
|
||||
return n;
|
||||
}
|
||||
|
||||
size_t Terminal::write_P(PGM_P str) {
|
||||
uint8_t ch;
|
||||
size_t n = 0;
|
||||
while ((ch = pgm_read_byte(str + n)) != '\x0') {
|
||||
_stream->write(ch);
|
||||
n++;
|
||||
}
|
||||
return n;
|
||||
}
|
||||
|
||||
size_t Terminal::write(const uint8_t *buf, size_t size) {
|
||||
size_t n = 0;
|
||||
while (size--) {
|
||||
if (_stream->write(*buf++))
|
||||
n++;
|
||||
else
|
||||
break;
|
||||
}
|
||||
return n;
|
||||
}
|
||||
Reference in New Issue
Block a user