26 #include "freertos/FreeRTOS.h" 27 #include "freertos/task.h" 28 #include "freertos/timers.h" 30 #include "rom/ets_sys.h" 34 #include "soc/uart_reg.h" 35 #include "soc/uart_struct.h" 36 #include "soc/io_mux_reg.h" 37 #include "soc/gpio_sig_map.h" 38 #include "soc/dport_reg.h" 40 #include "esp_intr_alloc.h" 60 const char TERMID[] =
"?64;1;6;22c";
63 const char CSI_7BIT[] =
"\e[";
64 const char CSI_8BIT[] =
"\x9B";
65 const char DCS_7BIT[] =
"\eP";
66 const char DCS_8BIT[] =
"\x90";
67 const char SS2_7BIT[] =
"\eN";
68 const char SS2_8BIT[] =
"\x8E";
69 const char SS3_7BIT[] =
"\eO";
70 const char SS3_8BIT[] =
"\x8F";
71 const char ST_7BIT[] =
"\e\\";
72 const char ST_8BIT[] =
"\x9C";
73 const char OSC_7BIT[] =
"\e]";
74 const char OSC_8BIT[] =
"\x9D";
78 #define ISCTRLCHAR(c) ((c) <= ASCII_US || (c) == ASCII_DEL) 82 static const uint8_t DECGRAPH_TO_CP437[255] = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25,
83 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49,
84 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 65, 66, 67, 68, 69, 70, 71, 72, 73,
85 74, 75, 76, 77, 78, 79, 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94,
88 63, 63, 217, 191, 218,
89 192, 197, 63, 63, 196,
90 63, 63, 195, 180, 193,
91 194, 179, 243, 242, 227, 63,
95 const char * CTRLCHAR_TO_STR[] = {
"NUL",
"SOH",
"STX",
"ETX",
"EOT",
"ENQ",
"ACK",
"BELL",
"BS",
"HT",
"LF",
"VT",
"FF",
"CR",
"SO",
"SI",
"DLE",
"XON",
"DC2",
96 "XOFF",
"DC4",
"NAK",
"SYN",
"ETB",
"CAN",
"EM",
"SUB",
"ESC",
"FS",
"GS",
"RS",
"US",
"SPC"};
101 #define FABGLEXT_STARTCODE '_' 102 #define FABGLEXT_CMD "\e_" 103 #define FABGLEXT_ENDCODE '$' 104 #define FABGLEXT_REPLYCODE '$' 108 #define FABGLEXT_USERSEQ '#' 110 #define FABGLEXTB_GETCURSORPOS 'a' 111 #define FABGLEXTB_GETCURSORCOL 'b' 112 #define FABGLEXTB_GETCURSORROW 'c' 113 #define FABGLEXTB_SETCURSORPOS 'd' 114 #define FABGLEXTB_INSERTSPACE 'e' 115 #define FABGLEXTB_DELETECHAR 'f' 116 #define FABGLEXTB_CURSORLEFT 'g' 117 #define FABGLEXTB_CURSORRIGHT 'h' 118 #define FABGLEXTB_SETCHAR 'i' 119 #define FABGLEXTB_DISABLEFABSEQ 'j' 120 #define FABGLEXTB_SETTERMTYPE 'k' 121 #define FABGLEXTB_ISVKDOWN 'K' 122 #define FABGLEXTB_SETFGCOLOR 'l' 123 #define FABGLEXTB_SETBGCOLOR 'm' 124 #define FABGLEXTB_SETCHARSTYLE 'n' 126 #define FABGLEXTX_SETUPADC 'A' 127 #define FABGLEXTX_CLEAR 'B' 128 #define FABGLEXTX_READADC 'C' 129 #define FABGLEXTX_SETUPGPIO 'D' 130 #define FABGLEXTX_ENABLECURSOR 'E' 131 #define FABGLEXTX_SETCURSORPOS 'F' 132 #define FABGLEXTX_GRAPHICSCMD 'G' 133 #define FABGLEXTX_SHOWMOUSE 'H' 134 #define FABGLEXTX_GETMOUSEPOS 'M' 135 #define FABGLEXTX_GETGPIO 'R' 136 #define FABGLEXTX_SOUND 'S' 137 #define FABGLEXTX_SETGPIO 'W' 138 #define FABGLEXTX_DELAY 'Y' 141 #define FABGLEXT_MAXSUBCMDLEN 16 144 #define FABGLEXT_GCLEAR "CLEAR" 145 #define FABGLEXT_GSETBRUSHCOLOR "BRUSH" 146 #define FABGLEXT_GSETPENCOLOR "PEN" 147 #define FABGLEXT_GSETPIXEL "PIXEL" 148 #define FABGLEXT_GSCROLL "SCROLL" 149 #define FABGLEXT_GPENWIDTH "PENW" 150 #define FABGLEXT_GLINE "LINE" 151 #define FABGLEXT_GRECT "RECT" 152 #define FABGLEXT_GFILLRECT "FILLRECT" 153 #define FABGLEXT_GELLIPSE "ELLIPSE" 154 #define FABGLEXT_GFILLELLIPSE "FILLELLIPSE" 155 #define FABGLEXT_GPATH "PATH" 156 #define FABGLEXT_GFILLPATH "FILLPATH" 157 #define FABGLEXT_GSPRITECOUNT "SPRITECOUNT" 158 #define FABGLEXT_GSPRITEDEF "SPRITEDEF" 159 #define FABGLEXT_GSPRITESET "SPRITESET" 166 Terminal * Terminal::s_activeTerminal =
nullptr;
180 m_uartRXEnabled(true),
181 m_soundGenerator(nullptr),
185 if (s_activeTerminal ==
nullptr)
186 s_activeTerminal =
this;
190 Terminal::~Terminal()
196 if (m_soundGenerator)
197 delete m_soundGenerator;
205 xSemaphoreTake(m_mutex, portMAX_DELAY);
206 if (s_activeTerminal !=
this) {
209 if (m_bitmappedDisplayController) {
211 s_activeTerminal =
nullptr;
212 switch (transition) {
214 for (
int x = 0; x < m_columns; ++x) {
215 m_canvas->
scroll(m_font.width, 0);
216 m_canvas->
setOrigin(-m_font.width * (m_columns - x - 1), 0);
217 for (
int y = 0; y < m_rows; ++y)
218 m_canvas->renderGlyphsBuffer(m_columns - x - 1, y, &m_glyphsBuffer);
220 vTaskDelay(2 / portTICK_PERIOD_MS);
224 for (
int x = 0; x < m_columns; ++x) {
225 m_canvas->
scroll(-m_font.width, 0);
226 m_canvas->
setOrigin(m_font.width * (m_columns - x - 1), 0);
227 for (
int y = 0; y < m_rows; ++y)
228 m_canvas->renderGlyphsBuffer(x, y, &m_glyphsBuffer);
230 vTaskDelay(2 / portTICK_PERIOD_MS);
239 auto map = (uint32_t*) heap_caps_malloc(
sizeof(uint32_t) * m_columns * m_rows, MALLOC_CAP_8BIT | MALLOC_CAP_INTERNAL);
240 memcpy(map, s_activeTerminal->m_glyphsBuffer.map,
sizeof(uint32_t) * m_columns * m_rows);
241 txtCtrl->enableCursor(
false);
242 txtCtrl->setTextMap(map, m_rows);
243 switch (transition) {
245 for (
int x = 0; x < m_columns; ++x) {
246 for (
int y = 0; y < m_rows; ++y) {
247 memmove(map + y * m_columns + 1, map + y * m_columns,
sizeof(uint32_t) * (m_columns - 1));
248 map[y * m_columns] = m_glyphsBuffer.map[y * m_columns + m_columns - x - 1];
254 for (
int x = 0; x < m_columns; ++x) {
255 for (
int y = 0; y < m_rows; ++y) {
256 memmove(map + y * m_columns, map + y * m_columns + 1,
sizeof(uint32_t) * (m_columns - 1));
257 map[y * m_columns + m_columns - 1] = m_glyphsBuffer.map[y * m_columns + x];
265 txtCtrl->setTextMap(m_glyphsBuffer.map, m_rows);
270 s_activeTerminal =
this;
271 vTaskResume(m_keyboardReaderTaskHandle);
272 syncDisplayController();
274 xSemaphoreGive(m_mutex);
280 xSemaphoreTake(m_mutex, portMAX_DELAY);
281 if (s_activeTerminal ==
this) {
282 s_activeTerminal =
nullptr;
284 xSemaphoreGive(m_mutex);
289 void Terminal::syncDisplayController()
291 if (m_bitmappedDisplayController) {
299 auto txtCtrl =
static_cast<TextualDisplayController*
>(m_displayController);
300 txtCtrl->setTextMap(m_glyphsBuffer.map, m_rows);
301 txtCtrl->setCursorBackground(m_emuState.backgroundColor);
302 txtCtrl->setCursorForeground(m_emuState.foregroundColor);
303 txtCtrl->setCursorPos(m_emuState.cursorY - 1, m_emuState.cursorX - 1);
304 txtCtrl->enableCursor(m_emuState.cursorEnabled);
306 updateCanvasScrollingRegion();
313 m_displayController = displayController;
316 m_maxColumns = maxColumns;
319 if (m_bitmappedDisplayController) {
320 m_canvas =
new Canvas(static_cast<BitmappedDisplayController*>(m_displayController));
332 m_logStream =
nullptr;
334 m_glyphsBuffer = (GlyphsBuffer){0, 0,
nullptr, 0, 0,
nullptr};
336 m_emuState.tabStop =
nullptr;
337 m_font.data =
nullptr;
339 m_savedCursorStateList =
nullptr;
341 m_alternateScreenBuffer =
false;
342 m_alternateMap =
nullptr;
344 m_autoXONOFF =
false;
347 m_lastWrittenChar = 0;
349 m_writeDetectedFabGLSeq =
false;
352 m_emuState.conformanceLevel = 4;
353 m_emuState.ctrlBits = 7;
356 m_cursorState =
false;
357 m_emuState.cursorEnabled =
false;
359 m_mutex = xSemaphoreCreateMutex();
361 set132ColumnMode(
false);
364 m_blinkTimer = xTimerCreate(
"", pdMS_TO_TICKS(FABGLIB_DEFAULT_BLINK_PERIOD_MS), pdTRUE,
this, blinkTimerFunc);
365 xTimerStart(m_blinkTimer, portMAX_DELAY);
375 m_serialPort =
nullptr;
378 m_keyboardReaderTaskHandle =
nullptr;
381 m_outputQueue =
nullptr;
383 m_termInfo =
nullptr;
385 bool success = (m_glyphsBuffer.map !=
nullptr);
396 if (m_keyboardReaderTaskHandle)
397 vTaskDelete(m_keyboardReaderTaskHandle);
399 xTimerDelete(m_blinkTimer, portMAX_DELAY);
401 clearSavedCursorStates();
403 vTaskDelete(m_charsConsumerTaskHandle);
404 vQueueDelete(m_inputQueue);
407 vQueueDelete(m_outputQueue);
413 vSemaphoreDelete(m_mutex);
419 s_activeTerminal =
nullptr;
427 vTaskDelete(m_keyboardReaderTaskHandle);
428 m_serialPort = &serialPort;
429 m_autoXONOFF = autoXONXOFF;
444 inline int uartGetRXFIFOCount()
446 uart_dev_t * uart = (
volatile uart_dev_t *)(DR_REG_UART2_BASE);
447 return uart->status.rxfifo_cnt | ((int)(uart->mem_cnt_status.rx_cnt) << 8);
452 static void uartFlushTXFIFO()
454 uart_dev_t * uart = (
volatile uart_dev_t *)(DR_REG_UART2_BASE);
455 while (uart->status.txfifo_cnt || uart->status.st_utx_out)
461 static void uartFlushRXFIFO()
463 uart_dev_t * uart = (
volatile uart_dev_t *)(DR_REG_UART2_BASE);
464 while (uartGetRXFIFOCount() != 0 || uart->mem_rx_status.wr_addr != uart->mem_rx_status.rd_addr)
470 void Terminal::uartCheckInputQueueForFlowControl()
473 uart_dev_t * uart = (
volatile uart_dev_t *)(DR_REG_UART2_BASE);
474 if (uxQueueMessagesWaiting(m_inputQueue) == 0 && uart->int_ena.rxfifo_full == 0) {
477 uart->flow_conf.send_xon = 1;
479 uart->int_ena.rxfifo_full = 1;
488 uart_dev_t * uart = (
volatile uart_dev_t *) DR_REG_UART2_BASE;
490 bool initialSetup = !m_uart;
501 DPORT_SET_PERI_REG_MASK(DPORT_PERIP_CLK_EN_REG, DPORT_UART2_CLK_EN);
502 DPORT_CLEAR_PERI_REG_MASK(DPORT_PERIP_RST_EN_REG, DPORT_UART2_RST);
509 configureGPIO(int2gpio(rxPin), GPIO_MODE_INPUT);
510 configureGPIO(int2gpio(txPin), GPIO_MODE_OUTPUT);
513 uart->conf1.rxfifo_full_thrhd = 1;
514 uart->conf1.rx_tout_thrhd = 2;
515 uart->conf1.rx_tout_en = 0;
516 uart->int_ena.rxfifo_full = 1;
517 uart->int_ena.frm_err = 1;
518 uart->int_ena.rxfifo_tout = 0;
519 uart->int_ena.parity_err = 1;
520 uart->int_ena.rxfifo_ovf = 1;
521 uart->int_clr.val = 0xffffffff;
522 esp_intr_alloc(ETS_UART2_INTR_SOURCE, 0, uart_isr,
this,
nullptr);
525 uart->mem_conf.rx_size = 3;
526 uart->mem_conf.tx_size = 1;
535 uint32_t clk_div = (getApbFrequency() << 4) / baud;
536 uart->clk_div.div_int = clk_div >> 4;
537 uart->clk_div.div_frag = clk_div & 0xf;
540 uart->conf0.val = config;
541 if (uart->conf0.stop_bit_num == 0x3) {
542 uart->conf0.stop_bit_num = 1;
543 uart->rs485_conf.dl1_en = 1;
547 gpio_matrix_in(rxPin, U2RXD_IN_IDX, inverted);
548 gpio_matrix_out(txPin, U2TXD_OUT_IDX, inverted,
false);
551 uart->flow_conf.sw_flow_con_en = 0;
552 uart->flow_conf.xonoff_del = 0;
556 uart->swfc_conf.xon_threshold = 0;
557 uart->swfc_conf.xoff_threshold = 0;
558 uart->swfc_conf.xon_char = ASCII_XON;
559 uart->swfc_conf.xoff_char = ASCII_XOFF;
563 uart->flow_conf.send_xon = 1;
574 m_outputQueue = xQueueCreate(FABGLIB_TERMINAL_OUTPUT_QUEUE_SIZE,
sizeof(uint8_t));
583 vQueueDelete(m_outputQueue);
584 m_outputQueue =
nullptr;
588 void Terminal::logFmt(
const char * format, ...)
592 va_start(ap, format);
593 int size = vsnprintf(
nullptr, 0, format, ap) + 1;
596 va_start(ap, format);
598 vsnprintf(buf, size, format, ap);
599 m_logStream->write(buf);
606 void Terminal::log(
const char * txt)
609 m_logStream->write(txt);
613 void Terminal::log(
char c)
616 m_logStream->write(c);
620 void Terminal::freeFont()
622 #if FABGLIB_CACHE_FONT_IN_RAM 624 free((
void*) m_font.data);
625 m_font.data =
nullptr;
631 void Terminal::freeTabStops()
633 if (m_emuState.tabStop) {
634 free(m_emuState.tabStop);
635 m_emuState.tabStop =
nullptr;
640 void Terminal::freeGlyphsMap()
642 if (m_glyphsBuffer.map) {
643 free((
void*) m_glyphsBuffer.map);
644 m_glyphsBuffer.map =
nullptr;
646 if (m_alternateMap) {
647 free((
void*) m_alternateMap);
648 m_alternateMap =
nullptr;
653 void Terminal::reset()
655 #if FABGLIB_TERMINAL_DEBUG_REPORT_DESCS 659 xSemaphoreTake(m_mutex, portMAX_DELAY);
660 m_resetRequested =
false;
662 m_emuState.originMode =
false;
663 m_emuState.wraparound =
true;
664 m_emuState.insertMode =
false;
665 m_emuState.newLineMode =
false;
666 m_emuState.smoothScroll =
false;
667 m_emuState.keypadMode = KeypadMode::Numeric;
668 m_emuState.cursorKeysMode =
false;
669 m_emuState.keyAutorepeat =
true;
670 m_emuState.cursorBlinkingEnabled =
true;
671 m_emuState.cursorStyle = 0;
672 m_emuState.allow132ColumnMode =
false;
673 m_emuState.reverseWraparoundMode =
false;
674 m_emuState.backarrowKeyMode =
false;
675 m_emuState.ANSIMode =
true;
676 m_emuState.VT52GraphicsMode =
false;
677 m_emuState.allowFabGLSequences = 1;
678 m_emuState.characterSetIndex = 0;
679 for (
int i = 0; i < 4; ++i)
680 m_emuState.characterSet[i] = 1;
684 m_blinkingTextVisible =
false;
685 m_blinkingTextEnabled =
true;
687 m_cursorState =
false;
689 m_convMatchedCount = 0;
690 m_convMatchedItem =
nullptr;
693 setScrollingRegion(1, m_rows);
697 m_glyphOptions = (GlyphOptions) {{
700 .reduceLuminosity = 0,
712 m_paintOptions = PaintOptions();
716 int_setBackgroundColor(m_defaultBackgroundColor);
717 int_setForegroundColor(m_defaultForegroundColor);
719 clearSavedCursorStates();
723 xSemaphoreGive(m_mutex);
729 #if FABGLIB_TERMINAL_DEBUG_REPORT_DESCS 733 if (m_bitmappedDisplayController) {
740 #if FABGLIB_CACHE_FONT_IN_RAM 741 int size = m_font.height * 256 * ((m_font.width + 7) / 8);
742 m_font.data = (uint8_t
const*) malloc(size);
743 memcpy((
void*)m_font.data, font->data, size);
745 m_font.data = font->data;
748 m_columns = m_canvas->
getWidth() / m_font.width;
749 m_rows = m_canvas->
getHeight() / m_font.height;
751 m_glyphsBuffer.glyphsWidth = m_font.width;
752 m_glyphsBuffer.glyphsHeight = m_font.height;
753 m_glyphsBuffer.glyphsData = m_font.data;
765 if (m_maxColumns > 0 && m_maxColumns < m_columns)
766 m_columns = m_maxColumns;
767 if (m_maxRows > 0 && m_maxRows < m_rows)
771 m_emuState.tabStop = (uint8_t*) malloc(m_columns);
775 m_glyphsBuffer.columns = m_columns;
776 m_glyphsBuffer.rows = m_rows;
778 m_glyphsBuffer.map = (uint32_t*) heap_caps_malloc(
sizeof(uint32_t) * m_columns * m_rows, MALLOC_CAP_8BIT | MALLOC_CAP_INTERNAL);
779 if (m_glyphsBuffer.map)
785 m_alternateMap =
nullptr;
786 m_alternateScreenBuffer =
false;
788 if (!m_bitmappedDisplayController &&
isActive()) {
793 setScrollingRegion(1, m_rows);
800 #if FABGLIB_TERMINAL_DEBUG_REPORT_DESCS 803 if (m_bitmappedDisplayController &&
isActive()) {
804 while (uxQueueMessagesWaiting(m_inputQueue) > 0)
813 void Terminal::set132ColumnMode(
bool value)
815 #if FABGLIB_TERMINAL_DEBUG_REPORT_DESCS 816 log(
"set132ColumnMode()\n");
819 if (m_bitmappedDisplayController) {
834 m_defaultBackgroundColor = color;
839 void Terminal::int_setBackgroundColor(
Color color)
841 m_emuState.backgroundColor = color;
843 if (m_bitmappedDisplayController)
854 m_defaultForegroundColor = color;
859 void Terminal::int_setForegroundColor(
Color color)
861 m_emuState.foregroundColor = color;
863 if (m_bitmappedDisplayController)
871 void Terminal::reverseVideo(
bool value)
873 if (m_paintOptions.
swapFGBG != value) {
876 if (m_bitmappedDisplayController) {
893 void Terminal::int_clear()
895 #if FABGLIB_TERMINAL_DEBUG_REPORT_DESCS 896 log(
"int_clear()\n");
899 if (m_bitmappedDisplayController &&
isActive())
901 clearMap(m_glyphsBuffer.map);
905 void Terminal::clearMap(uint32_t * map)
907 uint32_t itemValue = GLYPHMAP_ITEM_MAKE(ASCII_SPC, m_emuState.backgroundColor, m_emuState.foregroundColor, m_glyphOptions);
908 uint32_t * mapItemPtr = map;
909 for (
int row = 0; row < m_rows; ++row)
910 for (
int col = 0; col < m_columns; ++col, ++mapItemPtr)
911 *mapItemPtr = itemValue;
916 bool Terminal::moveUp()
918 #if FABGLIB_TERMINAL_DEBUG_REPORT_DESCS 922 if (m_emuState.cursorY == m_emuState.scrollingRegionTop)
924 setCursorPos(m_emuState.cursorX, m_emuState.cursorY - 1);
930 bool Terminal::moveDown()
932 #if FABGLIB_TERMINAL_DEBUG_REPORT_DESCS 936 if (m_emuState.cursorY == m_emuState.scrollingRegionDown || m_emuState.cursorY == m_rows)
938 setCursorPos(m_emuState.cursorX, m_emuState.cursorY + 1);
944 void Terminal::move(
int offset)
946 int pos = m_emuState.cursorX - 1 + (m_emuState.cursorY - 1) * m_columns + offset;
947 int newY = pos / m_columns + 1;
948 int newX = pos % m_columns + 1;
949 if (newY < m_emuState.scrollingRegionTop) {
951 newY = m_emuState.scrollingRegionTop;
953 if (newY > m_emuState.scrollingRegionDown) {
955 newY = m_emuState.scrollingRegionDown;
957 setCursorPos(newX, newY);
961 void Terminal::setCursorPos(
int X,
int Y)
963 m_emuState.cursorX = tclamp(
X, 1, (
int)m_columns);
964 m_emuState.cursorY = tclamp(
Y, 1, (
int)m_rows);
965 m_emuState.cursorPastLastCol =
false;
967 if (!m_bitmappedDisplayController &&
isActive())
968 static_cast<TextualDisplayController*
>(m_displayController)->setCursorPos(m_emuState.cursorY - 1, m_emuState.cursorX - 1);
970 #if FABGLIB_TERMINAL_DEBUG_REPORT_DESCSALL 971 logFmt(
"setCursorPos(%d, %d) => set to (%d, %d)\n",
X,
Y, m_emuState.cursorX, m_emuState.cursorY);
976 int Terminal::getAbsoluteRow(
int Y)
978 #if FABGLIB_TERMINAL_DEBUG_REPORT_DESCS 979 logFmt(
"getAbsoluteRow(%d)\n",
Y);
982 if (m_emuState.originMode) {
983 Y += m_emuState.scrollingRegionTop - 1;
984 Y = tclamp(
Y, m_emuState.scrollingRegionTop, m_emuState.scrollingRegionDown);
996 bool Terminal::int_enableCursor(
bool value)
998 bool prev = m_emuState.cursorEnabled;
999 if (m_emuState.cursorEnabled != value) {
1000 m_emuState.cursorEnabled = value;
1001 if (m_bitmappedDisplayController) {
1003 if (m_emuState.cursorEnabled) {
1004 if (uxQueueMessagesWaiting(m_inputQueue) == 0) {
1009 if (m_cursorState) {
1016 static_cast<TextualDisplayController*
>(m_displayController)->
enableCursor(value);
1023 bool Terminal::enableBlinkingText(
bool value)
1025 bool prev = m_blinkingTextEnabled;
1026 m_blinkingTextEnabled = value;
1032 void Terminal::blinkTimerFunc(TimerHandle_t xTimer)
1034 Terminal * term = (Terminal*) pvTimerGetTimerID(xTimer);
1036 if (term->isActive() && xSemaphoreTake(term->m_mutex, 0) == pdTRUE) {
1038 if (term->m_emuState.cursorEnabled && term->m_emuState.cursorBlinkingEnabled)
1039 term->blinkCursor();
1042 if (term->m_blinkingTextEnabled)
1045 xSemaphoreGive(term->m_mutex);
1051 void Terminal::blinkCursor()
1053 if (m_bitmappedDisplayController &&
isActive()) {
1054 m_cursorState = !m_cursorState;
1055 int X = (m_emuState.cursorX - 1) * m_font.width;
1056 int Y = (m_emuState.cursorY - 1) * m_font.height;
1057 switch (m_emuState.cursorStyle) {
1064 m_canvas->
swapRectangle(
X,
Y + m_font.height - 2,
X + m_font.width - 1,
Y + m_font.height - 1);
1075 void Terminal::blinkText()
1078 m_blinkingTextVisible = !m_blinkingTextVisible;
1079 bool keepEnabled =
false;
1081 int cols = m_columns;
1082 if (m_bitmappedDisplayController)
1084 for (
int y = 0; y < rows; ++y) {
1085 uint32_t * itemPtr = m_glyphsBuffer.map + y * cols;
1086 for (
int x = 0; x < cols; ++x, ++itemPtr) {
1088 GlyphOptions glyphOptions = glyphMapItem_getOptions(itemPtr);
1089 if (glyphOptions.userOpt1) {
1090 glyphOptions.blank = !m_blinkingTextVisible;
1091 glyphMapItem_setOptions(itemPtr, glyphOptions);
1092 refresh(x + 1, y + 1);
1096 if (m_bitmappedDisplayController)
1099 if (m_bitmappedDisplayController)
1102 m_blinkingTextEnabled =
false;
1107 void Terminal::nextTabStop()
1109 int actualColumns = m_columns;
1112 if (getGlyphOptionsAt(1, m_emuState.cursorY).
doubleWidth)
1115 int x = m_emuState.cursorX;
1116 while (x < actualColumns) {
1118 if (m_emuState.tabStop[x - 1])
1121 setCursorPos(x, m_emuState.cursorY);
1126 void Terminal::resetTabStops()
1128 for (
int i = 0; i < m_columns; ++i)
1129 m_emuState.tabStop[i] = (i > 0 && (i % 8) == 0 ? 1 : 0);
1134 void Terminal::setTabStop(
int column,
bool set)
1136 #if FABGLIB_TERMINAL_DEBUG_REPORT_DESCS 1137 logFmt(
"setTabStop %d %d\n", column, (
int)
set);
1141 memset(m_emuState.tabStop, 0, m_columns);
1143 m_emuState.tabStop[column - 1] =
set ? 1 : 0;
1147 void Terminal::scrollDown()
1149 #if FABGLIB_TERMINAL_DEBUG_REPORT_DESCS 1150 log(
"scrollDown\n");
1153 if (m_bitmappedDisplayController &&
isActive()) {
1155 if (m_emuState.smoothScroll) {
1156 for (
int i = 0; i < m_font.height; ++i)
1159 m_canvas->
scroll(0, m_font.height);
1163 for (
int y = m_emuState.scrollingRegionDown - 1; y > m_emuState.scrollingRegionTop - 1; --y)
1164 memcpy(m_glyphsBuffer.map + y * m_columns, m_glyphsBuffer.map + (y - 1) * m_columns, m_columns *
sizeof(uint32_t));
1167 uint32_t itemValue = GLYPHMAP_ITEM_MAKE(ASCII_SPC, m_emuState.backgroundColor, m_emuState.foregroundColor, m_glyphOptions);
1168 uint32_t * itemPtr = m_glyphsBuffer.map + (m_emuState.scrollingRegionTop - 1) * m_columns;
1169 for (
int x = 0; x < m_columns; ++x, ++itemPtr)
1170 *itemPtr = itemValue;
1176 void Terminal::scrollDownAt(
int startingRow)
1178 int prevScrollingRegionTop = m_emuState.scrollingRegionTop;
1179 setScrollingRegion(startingRow, m_emuState.scrollingRegionDown,
false);
1183 setScrollingRegion(prevScrollingRegionTop, m_emuState.scrollingRegionDown,
false);
1187 void Terminal::scrollUp()
1189 #if FABGLIB_TERMINAL_DEBUG_REPORT_DESCS 1193 auto ldown = m_emuState.scrollingRegionDown;
1194 if (m_emuState.cursorY == m_rows) {
1196 m_emuState.scrollingRegionDown = m_rows;
1197 updateCanvasScrollingRegion();
1200 if (m_bitmappedDisplayController &&
isActive()) {
1202 if (m_emuState.smoothScroll) {
1203 for (
int i = 0; i < m_font.height; ++i)
1206 m_canvas->
scroll(0, -m_font.height);
1210 for (
int y = m_emuState.scrollingRegionTop - 1; y < m_emuState.scrollingRegionDown - 1; ++y)
1211 memcpy(m_glyphsBuffer.map + y * m_columns, m_glyphsBuffer.map + (y + 1) * m_columns, m_columns *
sizeof(uint32_t));
1214 uint32_t itemValue = GLYPHMAP_ITEM_MAKE(ASCII_SPC, m_emuState.backgroundColor, m_emuState.foregroundColor, m_glyphOptions);
1215 uint32_t * itemPtr = m_glyphsBuffer.map + (m_emuState.scrollingRegionDown - 1) * m_columns;
1216 for (
int x = 0; x < m_columns; ++x, ++itemPtr)
1217 *itemPtr = itemValue;
1219 if (ldown != m_emuState.scrollingRegionDown) {
1220 m_emuState.scrollingRegionDown = ldown;
1221 updateCanvasScrollingRegion();
1226 void Terminal::scrollUpAt(
int startingRow)
1228 int prevScrollingRegionTop = m_emuState.scrollingRegionTop;
1229 setScrollingRegion(startingRow, m_emuState.scrollingRegionDown,
false);
1233 setScrollingRegion(prevScrollingRegionTop, m_emuState.scrollingRegionDown,
false);
1237 void Terminal::setScrollingRegion(
int top,
int down,
bool resetCursorPos)
1239 m_emuState.scrollingRegionTop = tclamp(top, 1, (
int)m_rows);
1240 m_emuState.scrollingRegionDown = tclamp(down, 1, (
int)m_rows);
1241 updateCanvasScrollingRegion();
1244 setCursorPos(1, m_emuState.originMode ? m_emuState.scrollingRegionTop : 1);
1246 #if FABGLIB_TERMINAL_DEBUG_REPORT_DESCS 1247 logFmt(
"setScrollingRegion: %d %d => %d %d\n", top, down, m_emuState.scrollingRegionTop, m_emuState.scrollingRegionDown);
1252 void Terminal::updateCanvasScrollingRegion()
1254 if (m_bitmappedDisplayController &&
isActive())
1255 m_canvas->
setScrollingRegion(0, (m_emuState.scrollingRegionTop - 1) * m_font.height, m_canvas->
getWidth() - 1, m_emuState.scrollingRegionDown * m_font.height - 1);
1262 bool Terminal::multilineInsertChar(
int charsToMove)
1264 bool scrolled =
false;
1265 int col = m_emuState.cursorX;
1266 int row = m_emuState.cursorY;
1267 if (m_emuState.cursorPastLastCol) {
1271 uint32_t lastColItem = 0;
1272 while (charsToMove > 0) {
1273 uint32_t * rowPtr = m_glyphsBuffer.map + (row - 1) * m_columns;
1274 uint32_t lItem = rowPtr[m_columns - 1];
1275 insertAt(col, row, 1);
1276 if (row > m_emuState.cursorY) {
1277 rowPtr[0] = lastColItem;
1280 lastColItem = lItem;
1281 charsToMove -= m_columns - col;
1283 if (charsToMove > 0 && row == m_emuState.scrollingRegionDown) {
1286 setCursorPos(m_emuState.cursorX, m_emuState.cursorY - 1);
1290 if (m_bitmappedDisplayController &&
isActive())
1298 void Terminal::insertAt(
int column,
int row,
int count)
1300 #if FABGLIB_TERMINAL_DEBUG_REPORT_DESCS 1301 logFmt(
"insertAt(%d, %d, %d)\n", column, row, count);
1304 count = tmin((
int)m_columns, count);
1306 if (m_bitmappedDisplayController &&
isActive()) {
1308 int charWidth = getCharWidthAt(row);
1309 m_canvas->
setScrollingRegion((column - 1) * charWidth, (row - 1) * m_font.height, charWidth * getColumnsAt(row) - 1, row * m_font.height - 1);
1310 m_canvas->
scroll(count * charWidth, 0);
1311 updateCanvasScrollingRegion();
1315 uint32_t * rowPtr = m_glyphsBuffer.map + (row - 1) * m_columns;
1316 for (
int i = m_columns - 1; i >= column + count - 1; --i)
1317 rowPtr[i] = rowPtr[i - count];
1320 GlyphOptions glyphOptions = m_glyphOptions;
1321 glyphOptions.doubleWidth = glyphMapItem_getOptions(rowPtr).
doubleWidth;
1322 uint32_t itemValue = GLYPHMAP_ITEM_MAKE(ASCII_SPC, m_emuState.backgroundColor, m_emuState.foregroundColor, glyphOptions);
1323 for (
int i = 0; i < count; ++i)
1324 rowPtr[column + i - 1] = itemValue;
1328 void Terminal::multilineDeleteChar(
int charsToMove)
1330 int col = m_emuState.cursorX;
1331 int row = m_emuState.cursorY;
1332 if (m_emuState.cursorPastLastCol) {
1338 if (charsToMove == 0)
1339 deleteAt(col, row, 1);
1341 while (charsToMove > 0) {
1342 deleteAt(col, row, 1);
1343 charsToMove -= m_columns - col;
1344 if (charsToMove > 0) {
1345 if (m_bitmappedDisplayController &&
isActive())
1347 uint32_t * lastItem = m_glyphsBuffer.map + (row - 1) * m_columns + (m_columns - 1);
1348 lastItem[0] = lastItem[1];
1349 refresh(m_columns, row);
1353 if (m_bitmappedDisplayController &&
isActive())
1360 void Terminal::deleteAt(
int column,
int row,
int count)
1362 #if FABGLIB_TERMINAL_DEBUG_REPORT_DESCS 1363 logFmt(
"deleteAt(%d, %d, %d)\n", column, row, count);
1366 count = imin(m_columns - column + 1, count);
1368 if (m_bitmappedDisplayController &&
isActive()) {
1370 int charWidth = getCharWidthAt(row);
1371 m_canvas->
setScrollingRegion((column - 1) * charWidth, (row - 1) * m_font.height, charWidth * getColumnsAt(row) - 1, row * m_font.height - 1);
1372 m_canvas->
scroll(-count * charWidth, 0);
1373 updateCanvasScrollingRegion();
1377 uint32_t * rowPtr = m_glyphsBuffer.map + (row - 1) * m_columns;
1378 int itemsToMove = m_columns - column - count + 1;
1379 for (
int i = 0; i < itemsToMove; ++i)
1380 rowPtr[column - 1 + i] = rowPtr[column - 1 + i + count];
1383 GlyphOptions glyphOptions = m_glyphOptions;
1384 glyphOptions.doubleWidth = glyphMapItem_getOptions(rowPtr).
doubleWidth;
1385 uint32_t itemValue = GLYPHMAP_ITEM_MAKE(ASCII_SPC, m_emuState.backgroundColor, m_emuState.foregroundColor, glyphOptions);
1386 for (
int i = m_columns - count + 1 ; i <= m_columns; ++i)
1387 rowPtr[i - 1] = itemValue;
1394 void Terminal::erase(
int X1,
int Y1,
int X2,
int Y2, uint8_t c,
bool maintainDoubleWidth,
bool selective)
1396 #if FABGLIB_TERMINAL_DEBUG_REPORT_DESCS 1397 logFmt(
"erase(%d, %d, %d, %d, %d, %d)\n",
X1,
Y1,
X2,
Y2, (
int)c, (
int)maintainDoubleWidth);
1400 X1 = tclamp(
X1 - 1, 0, (
int)m_columns - 1);
1401 Y1 = tclamp(
Y1 - 1, 0, (
int)m_rows - 1);
1402 X2 = tclamp(
X2 - 1, 0, (
int)m_columns - 1);
1403 Y2 = tclamp(
Y2 - 1, 0, (
int)m_rows - 1);
1405 if (m_bitmappedDisplayController &&
isActive()) {
1406 if (c == ASCII_SPC && !selective) {
1407 int charWidth = getCharWidthAt(m_emuState.cursorY);
1408 m_canvas->
fillRectangle(
X1 * charWidth,
Y1 * m_font.height, (
X2 + 1) * charWidth - 1, (
Y2 + 1) * m_font.height - 1);
1412 GlyphOptions glyphOptions = {.value = 0};
1413 glyphOptions.fillBackground = 1;
1415 for (
int y =
Y1; y <=
Y2; ++y) {
1416 uint32_t * itemPtr = m_glyphsBuffer.map +
X1 + y * m_columns;
1417 for (
int x =
X1; x <=
X2; ++x, ++itemPtr) {
1418 if (selective && glyphMapItem_getOptions(itemPtr).
userOpt2)
1420 glyphOptions.doubleWidth = maintainDoubleWidth ? glyphMapItem_getOptions(itemPtr).
doubleWidth : 0;
1421 *itemPtr = GLYPHMAP_ITEM_MAKE(c, m_emuState.backgroundColor, m_emuState.foregroundColor, glyphOptions);
1424 if (c != ASCII_SPC || selective)
1425 refresh(
X1 + 1,
Y1 + 1,
X2 + 1,
Y2 + 1);
1429 void Terminal::enableFabGLSequences(
bool value)
1431 m_emuState.allowFabGLSequences += value ? 1 : -1;
1432 if (m_emuState.allowFabGLSequences < 0)
1433 m_emuState.allowFabGLSequences = 0;
1437 void Terminal::clearSavedCursorStates()
1439 #if FABGLIB_TERMINAL_DEBUG_REPORT_DESCS 1440 log(
"clearSavedCursorStates()\n");
1443 for (TerminalCursorState * curItem = m_savedCursorStateList, * next; curItem; curItem = next) {
1444 next = curItem->next;
1445 free(curItem->tabStop);
1448 m_savedCursorStateList =
nullptr;
1452 void Terminal::saveCursorState()
1454 #if FABGLIB_TERMINAL_DEBUG_REPORT_DESCS 1455 log(
"saveCursorState()\n");
1458 TerminalCursorState * s = (TerminalCursorState*) malloc(
sizeof(TerminalCursorState));
1461 *s = (TerminalCursorState) {
1462 .next = m_savedCursorStateList,
1463 .cursorX = (int16_t) m_emuState.cursorX,
1464 .cursorY = (int16_t) m_emuState.cursorY,
1465 .tabStop = (uint8_t*) malloc(m_columns),
1466 .cursorPastLastCol = m_emuState.cursorPastLastCol,
1467 .originMode = m_emuState.originMode,
1468 .glyphOptions = m_glyphOptions,
1469 .characterSetIndex = m_emuState.characterSetIndex,
1470 .characterSet = {m_emuState.characterSet[0], m_emuState.characterSet[1], m_emuState.characterSet[2], m_emuState.characterSet[3]},
1473 memcpy(s->tabStop, m_emuState.tabStop, m_columns);
1474 m_savedCursorStateList = s;
1476 #if FABGLIB_TERMINAL_DEBUG_REPORT_ERRORS 1477 log(
"ERROR: Unable to alloc TerminalCursorState\n");
1483 void Terminal::restoreCursorState()
1485 #if FABGLIB_TERMINAL_DEBUG_REPORT_DESCS 1486 log(
"restoreCursorState()\n");
1489 if (m_savedCursorStateList) {
1490 m_emuState.cursorX = m_savedCursorStateList->cursorX;
1491 m_emuState.cursorY = m_savedCursorStateList->cursorY;
1492 m_emuState.cursorPastLastCol = m_savedCursorStateList->cursorPastLastCol;
1493 m_emuState.originMode = m_savedCursorStateList->originMode;
1494 if (m_savedCursorStateList->tabStop)
1495 memcpy(m_emuState.tabStop, m_savedCursorStateList->tabStop, m_columns);
1496 m_glyphOptions = m_savedCursorStateList->glyphOptions;
1497 if (m_bitmappedDisplayController &&
isActive())
1499 m_emuState.characterSetIndex = m_savedCursorStateList->characterSetIndex;
1500 for (
int i = 0; i < 4; ++i)
1501 m_emuState.characterSet[i] = m_savedCursorStateList->characterSet[i];
1503 TerminalCursorState * next = m_savedCursorStateList->next;
1505 free(m_savedCursorStateList->tabStop);
1506 free(m_savedCursorStateList);
1507 m_savedCursorStateList = next;
1512 void Terminal::useAlternateScreenBuffer(
bool value)
1514 #if FABGLIB_TERMINAL_DEBUG_REPORT_DESCS 1515 logFmt(
"useAlternateScreenBuffer: %d\n", value);
1517 if (m_alternateScreenBuffer != value) {
1518 m_alternateScreenBuffer = value;
1519 if (!m_alternateMap) {
1521 m_alternateMap = (uint32_t*) heap_caps_malloc(
sizeof(uint32_t) * m_columns * m_rows, MALLOC_CAP_8BIT | MALLOC_CAP_INTERNAL);
1522 clearMap(m_alternateMap);
1523 m_alternateCursorX = 1;
1524 m_alternateCursorY = 1;
1525 m_alternateScrollingRegionTop = 1;
1526 m_alternateScrollingRegionDown = m_rows;
1527 m_alternateCursorBlinkingEnabled =
true;
1529 tswap(m_alternateMap, m_glyphsBuffer.map);
1530 tswap(m_emuState.cursorX, m_alternateCursorX);
1531 tswap(m_emuState.cursorY, m_alternateCursorY);
1532 tswap(m_emuState.scrollingRegionTop, m_alternateScrollingRegionTop);
1533 tswap(m_emuState.scrollingRegionDown, m_alternateScrollingRegionDown);
1534 tswap(m_emuState.cursorBlinkingEnabled, m_alternateCursorBlinkingEnabled);
1535 setScrollingRegion(m_emuState.scrollingRegionTop, m_emuState.scrollingRegionDown,
false);
1536 m_emuState.cursorPastLastCol =
false;
1545 xQueueSendToFront(m_outputQueue, &c, portMAX_DELAY);
1552 xQueueSendToBack(m_outputQueue, &c, portMAX_DELAY);
1558 if (m_outputQueue) {
1560 xQueueSendToBack(m_outputQueue, str, portMAX_DELAY);
1562 #if FABGLIB_TERMINAL_DEBUG_REPORT_OUT_CODES 1563 logFmt(
"=> %02X %s%c\n", (
int)*str, (*str <= ASCII_SPC ? CTRLCHAR_TO_STR[(
int)(*str)] :
""), (*str > ASCII_SPC ? *str : ASCII_SPC));
1574 return m_outputQueue ? uxQueueMessagesWaiting(m_outputQueue) : 0;
1586 if (m_outputQueue) {
1588 xQueueReceive(m_outputQueue, &c, msToTicks(timeOutMS));
1598 while (!timeout.expired(timeOutMS)) {
1599 int c =
read(timeOutMS);
1624 int avail = m_serialPort->available();
1629 if (avail < FABGLIB_TERMINAL_XON_THRESHOLD) {
1635 if (avail >= FABGLIB_TERMINAL_XOFF_THRESHOLD) {
1645 auto r = m_serialPort->read();
1646 if (m_uartRXEnabled)
1653 void IRAM_ATTR Terminal::uart_isr(
void *arg)
1656 uart_dev_t * uart = (
volatile uart_dev_t *)(DR_REG_UART2_BASE);
1659 if (uart->int_st.rxfifo_ovf || uart->int_st.frm_err || uart->int_st.parity_err) {
1663 uart->int_clr.rxfifo_ovf = 1;
1664 uart->int_clr.frm_err = 1;
1665 uart->int_clr.parity_err = 1;
1670 if (term->m_autoXONOFF) {
1672 int count = uartGetRXFIFOCount();
1673 if (count > 300 && !term->m_XOFF) {
1674 uart->flow_conf.send_xoff = 1;
1675 term->m_XOFF =
true;
1676 }
else if (count < 20 && term->m_XOFF) {
1677 uart->flow_conf.send_xon = 1;
1678 term->m_XOFF =
false;
1683 while (uartGetRXFIFOCount() != 0 || uart->mem_rx_status.wr_addr != uart->mem_rx_status.rd_addr) {
1685 if (term->m_autoXONOFF && xQueueIsQueueFullFromISR(term->m_inputQueue)) {
1686 if (!term->m_XOFF) {
1687 uart->flow_conf.send_xoff = 1;
1688 term->m_XOFF =
true;
1691 uart->int_ena.rxfifo_full = 0;
1695 auto r = uart->fifo.rw_byte;
1696 if (term->m_uartRXEnabled)
1697 term->
write(r,
true);
1701 uart->int_clr.rxfifo_full = 1;
1708 #if FABGLIB_TERMINAL_DEBUG_REPORT_OUT_CODES 1709 logFmt(
"=> %02X %s%c\n", (
int)c, (c <= ASCII_SPC ? CTRLCHAR_TO_STR[(
int)c] :
""), (c > ASCII_SPC ? c : ASCII_SPC));
1714 while (m_serialPort->availableForWrite() == 0)
1715 vTaskDelay(1 / portTICK_PERIOD_MS);
1716 m_serialPort->write(c);
1721 uart_dev_t * uart = (
volatile uart_dev_t *)(DR_REG_UART2_BASE);
1722 while (uart->status.txfifo_cnt == 0x7F)
1724 uart->fifo.rw_byte = c;
1737 while (m_serialPort->availableForWrite() == 0)
1738 vTaskDelay(1 / portTICK_PERIOD_MS);
1739 m_serialPort->write(*str);
1741 #if FABGLIB_TERMINAL_DEBUG_REPORT_OUT_CODES 1742 logFmt(
"=> %02X %s%c\n", (
int)*str, (*str <= ASCII_SPC ? CTRLCHAR_TO_STR[(
int)(*str)] :
""), (*str > ASCII_SPC ? *str : ASCII_SPC));
1751 uart_dev_t * uart = (
volatile uart_dev_t *)(DR_REG_UART2_BASE);
1753 while (uart->status.txfifo_cnt == 0x7F)
1755 uart->fifo.rw_byte = *str++;
1763 void Terminal::sendCSI()
1765 send(m_emuState.ctrlBits == 7 ? CSI_7BIT : CSI_8BIT);
1769 void Terminal::sendDCS()
1771 send(m_emuState.ctrlBits == 7 ? DCS_7BIT : DCS_8BIT);
1775 void Terminal::sendSS3()
1777 send(m_emuState.ctrlBits == 7 ? SS3_7BIT : SS3_8BIT);
1783 return uxQueueSpacesAvailable(m_inputQueue);
1787 bool Terminal::addToInputQueue(uint8_t c,
bool fromISR)
1790 return xQueueSendToBackFromISR(m_inputQueue, &c,
nullptr);
1792 return xQueueSendToBack(m_inputQueue, &c, portMAX_DELAY);
1796 bool Terminal::insertToInputQueue(uint8_t c,
bool fromISR)
1799 return xQueueSendToFrontFromISR(m_inputQueue, &c,
nullptr);
1801 return xQueueSendToFront(m_inputQueue, &c, portMAX_DELAY);
1807 if (m_termInfo ==
nullptr || m_writeDetectedFabGLSeq)
1808 addToInputQueue(c, fromISR);
1810 convHandleTranslation(c, fromISR);
1813 if (m_writeDetectedFabGLSeq) {
1814 if (c == FABGLEXT_ENDCODE)
1815 m_writeDetectedFabGLSeq =
false;
1816 }
else if (m_emuState.allowFabGLSequences && m_lastWrittenChar == ASCII_ESC && c == FABGLEXT_STARTCODE) {
1817 m_writeDetectedFabGLSeq =
true;
1820 m_lastWrittenChar = c;
1822 #if FABGLIB_TERMINAL_DEBUG_REPORT_IN_CODES 1823 logFmt(
"<= %02X %s%c\n", (
int)c, (c <= ASCII_SPC ? CTRLCHAR_TO_STR[(
int)c] :
""), (c > ASCII_SPC ? c : ASCII_SPC));
1837 for (
int i = 0; i < size; ++i)
1850 void Terminal::int_setTerminalType(TermInfo
const * value)
1853 m_emuState.ANSIMode =
true;
1854 m_emuState.conformanceLevel = 4;
1856 m_termInfo =
nullptr;
1858 if (value !=
nullptr) {
1860 auto s = value->initString;
1861 for (
int i = strlen(s) - 1; i >= 0; --i)
1862 insertToInputQueue(s[i],
false);
1869 void Terminal::int_setTerminalType(
TermType value)
1873 int_setTerminalType(
nullptr);
1876 int_setTerminalType(&term_ADM3A);
1879 int_setTerminalType(&term_ADM31);
1882 int_setTerminalType(&term_Hazeltine1500);
1885 int_setTerminalType(&term_Osborne);
1888 int_setTerminalType(&term_Kaypro);
1891 int_setTerminalType(&term_VT52);
1894 int_setTerminalType(&term_ANSILegacy);
1900 void Terminal::convHandleTranslation(uint8_t c,
bool fromISR)
1902 if (m_convMatchedCount > 0 || c < 32 || c == 0x7f || c ==
'~') {
1904 m_convMatchedChars[m_convMatchedCount] = c;
1906 if (m_convMatchedItem ==
nullptr)
1907 m_convMatchedItem = m_termInfo->videoCtrlSet;
1909 for (
auto item = m_convMatchedItem; item->termSeq; ++item) {
1910 if (item != m_convMatchedItem) {
1912 if (m_convMatchedCount == 0 || (item->termSeqLen > m_convMatchedCount && strncmp(item->termSeq, m_convMatchedItem->termSeq, m_convMatchedCount) == 0))
1913 m_convMatchedItem = item;
1918 if (item->termSeq[m_convMatchedCount] == 0xFF || item->termSeq[m_convMatchedCount] == c) {
1920 ++m_convMatchedCount;
1921 if (item->termSeqLen == m_convMatchedCount) {
1923 for (ConvCtrl
const * ctrl = item->convCtrl; *ctrl != ConvCtrl::END; ++ctrl)
1924 convSendCtrl(*ctrl, fromISR);
1931 convQueue(
nullptr, fromISR);
1933 addToInputQueue(c, fromISR);
1937 void Terminal::convSendCtrl(ConvCtrl ctrl,
bool fromISR)
1940 case ConvCtrl::CarriageReturn:
1941 convQueue(
"\x0d", fromISR);
1943 case ConvCtrl::LineFeed:
1944 convQueue(
"\x0a", fromISR);
1946 case ConvCtrl::CursorLeft:
1947 convQueue(
"\e[D", fromISR);
1950 convQueue(
"\e[A", fromISR);
1952 case ConvCtrl::CursorRight:
1953 convQueue(
"\e[C", fromISR);
1955 case ConvCtrl::EraseToEndOfScreen:
1956 convQueue(
"\e[J", fromISR);
1958 case ConvCtrl::EraseToEndOfLine:
1959 convQueue(
"\e[K", fromISR);
1961 case ConvCtrl::CursorHome:
1962 convQueue(
"\e[H", fromISR);
1964 case ConvCtrl::AttrNormal:
1965 convQueue(
"\e[0m", fromISR);
1967 case ConvCtrl::AttrBlank:
1968 convQueue(
"\e[8m", fromISR);
1970 case ConvCtrl::AttrBlink:
1971 convQueue(
"\e[5m", fromISR);
1973 case ConvCtrl::AttrBlinkOff:
1974 convQueue(
"\e[25m", fromISR);
1976 case ConvCtrl::AttrReverse:
1977 convQueue(
"\e[7m", fromISR);
1979 case ConvCtrl::AttrReverseOff:
1980 convQueue(
"\e[27m", fromISR);
1982 case ConvCtrl::AttrUnderline:
1983 convQueue(
"\e[4m", fromISR);
1985 case ConvCtrl::AttrUnderlineOff:
1986 convQueue(
"\e[24m", fromISR);
1988 case ConvCtrl::AttrReduce:
1989 convQueue(
"\e[2m", fromISR);
1991 case ConvCtrl::AttrReduceOff:
1992 convQueue(
"\e[22m", fromISR);
1994 case ConvCtrl::InsertLine:
1995 convQueue(
"\e[L", fromISR);
1997 case ConvCtrl::InsertChar:
1998 convQueue(
"\e[@", fromISR);
2000 case ConvCtrl::DeleteLine:
2001 convQueue(
"\e[M", fromISR);
2003 case ConvCtrl::DeleteCharacter:
2004 convQueue(
"\e[P", fromISR);
2006 case ConvCtrl::CursorOn:
2007 convQueue(
"\e[?25h", fromISR);
2009 case ConvCtrl::CursorOff:
2010 convQueue(
"\e[?25l", fromISR);
2012 case ConvCtrl::SaveCursor:
2013 convQueue(
"\e[?1048h", fromISR);
2015 case ConvCtrl::RestoreCursor:
2016 convQueue(
"\e[?1048l", fromISR);
2018 case ConvCtrl::CursorPos:
2019 case ConvCtrl::CursorPos2:
2022 int y = (ctrl == ConvCtrl::CursorPos ? m_convMatchedChars[2] - 31 : m_convMatchedChars[3] + 1);
2023 int x = (ctrl == ConvCtrl::CursorPos ? m_convMatchedChars[3] - 31 : m_convMatchedChars[2] + 1);
2024 sprintf(s,
"\e[%d;%dH", y, x);
2025 convQueue(s, fromISR);
2036 void Terminal::convQueue(
const char * str,
bool fromISR)
2040 addToInputQueue(*str, fromISR);
2042 for (
int i = 0; i <= m_convMatchedCount; ++i) {
2043 addToInputQueue(m_convMatchedChars[i], fromISR);
2046 m_convMatchedCount = 0;
2047 m_convMatchedItem =
nullptr;
2053 bool Terminal::setChar(uint8_t c)
2055 bool vscroll =
false;
2057 if (m_emuState.cursorPastLastCol) {
2058 if (m_emuState.wraparound) {
2059 setCursorPos(1, m_emuState.cursorY);
2067 if (m_emuState.insertMode)
2068 insertAt(m_emuState.cursorX, m_emuState.cursorY, 1);
2070 GlyphOptions glyphOptions = m_glyphOptions;
2073 uint32_t * mapItemPtr = m_glyphsBuffer.map + (m_emuState.cursorX - 1) + (m_emuState.cursorY - 1) * m_columns;
2074 glyphOptions.doubleWidth = glyphMapItem_getOptions(mapItemPtr).
doubleWidth;
2075 *mapItemPtr = GLYPHMAP_ITEM_MAKE(c, m_emuState.backgroundColor, m_emuState.foregroundColor, glyphOptions);
2077 if (m_bitmappedDisplayController &&
isActive()) {
2078 if (glyphOptions.value != m_glyphOptions.value)
2081 int x = (m_emuState.cursorX - 1) * m_font.width * (glyphOptions.doubleWidth ? 2 : 1);
2082 int y = (m_emuState.cursorY - 1) * m_font.height;
2083 m_canvas->
drawGlyph(x, y, m_font.width, m_font.height, m_font.data, c);
2085 if (glyphOptions.value != m_glyphOptions.value)
2091 m_prevBlinkingTextEnabled =
true;
2093 if (m_emuState.cursorX == m_columns) {
2094 m_emuState.cursorPastLastCol =
true;
2096 setCursorPos(m_emuState.cursorX + 1, m_emuState.cursorY);
2103 void Terminal::refresh()
2105 #if FABGLIB_TERMINAL_DEBUG_REPORT_DESCS 2109 refresh(1, 1, m_columns, m_rows);
2114 void Terminal::refresh(
int X,
int Y)
2116 #if FABGLIB_TERMINAL_DEBUG_REPORT_DESCS 2117 logFmt(
"refresh(%d, %d)\n",
X,
Y);
2120 if (m_bitmappedDisplayController &&
isActive())
2121 m_canvas->renderGlyphsBuffer(
X - 1,
Y - 1, &m_glyphsBuffer);
2125 void Terminal::refresh(
int X1,
int Y1,
int X2,
int Y2)
2127 #if FABGLIB_TERMINAL_DEBUG_REPORT_DESCS 2128 logFmt(
"refresh(%d, %d, %d, %d)\n",
X1,
Y1,
X2,
Y2);
2131 if (m_bitmappedDisplayController &&
isActive()) {
2132 for (
int y =
Y1 - 1; y <
Y2; ++y) {
2133 for (
int x =
X1 - 1; x <
X2; ++x)
2134 m_canvas->renderGlyphsBuffer(x, y, &m_glyphsBuffer);
2142 void Terminal::setLineDoubleWidth(
int row,
int value)
2144 #if FABGLIB_TERMINAL_DEBUG_REPORT_DESCS 2145 logFmt(
"setLineDoubleWidth(%d, %d)\n", row, value);
2148 uint32_t * mapItemPtr = m_glyphsBuffer.map + (row - 1) * m_columns;
2149 for (
int i = 0; i < m_columns; ++i, ++mapItemPtr) {
2150 GlyphOptions glyphOptions = glyphMapItem_getOptions(mapItemPtr);
2151 glyphOptions.doubleWidth = value;
2152 glyphMapItem_setOptions(mapItemPtr, glyphOptions);
2155 refresh(1, row, m_columns, row);
2159 int Terminal::getCharWidthAt(
int row)
2161 return glyphMapItem_getOptions(m_glyphsBuffer.map + (row - 1) * m_columns).
doubleWidth ? m_font.width * 2 : m_font.width;
2165 int Terminal::getColumnsAt(
int row)
2167 return glyphMapItem_getOptions(m_glyphsBuffer.map + (row - 1) * m_columns).
doubleWidth ? m_columns / 2 : m_columns;
2171 GlyphOptions Terminal::getGlyphOptionsAt(
int X,
int Y)
2173 return glyphMapItem_getOptions(m_glyphsBuffer.map + (
X - 1) + (
Y - 1) * m_columns);
2178 uint8_t Terminal::getNextCode(
bool processCtrlCodes)
2182 xQueueReceive(m_inputQueue, &c, portMAX_DELAY);
2184 #if FABGLIB_TERMINAL_DEBUG_REPORT_INQUEUE_CODES 2185 logFmt(
"<= %02X %s%c\n", (
int)c, (c <= ASCII_SPC ? CTRLCHAR_TO_STR[(
int)c] :
""), (c > ASCII_SPC ? c : ASCII_SPC));
2189 uartCheckInputQueueForFlowControl();
2192 if (processCtrlCodes && ISCTRLCHAR(c))
2200 void Terminal::charsConsumerTask(
void * pvParameters)
2202 Terminal * term = (Terminal*) pvParameters;
2205 term->consumeInputQueue();
2209 void Terminal::consumeInputQueue()
2211 uint8_t c = getNextCode(
false);
2213 xSemaphoreTake(m_mutex, portMAX_DELAY);
2215 m_prevCursorEnabled = int_enableCursor(
false);
2216 m_prevBlinkingTextEnabled = enableBlinkingText(
false);
2223 else if (ISCTRLCHAR(c))
2227 if (m_emuState.characterSet[m_emuState.characterSetIndex] == 0 || (!m_emuState.ANSIMode && m_emuState.VT52GraphicsMode))
2228 c = DECGRAPH_TO_CP437[(uint8_t)c];
2232 enableBlinkingText(m_prevBlinkingTextEnabled);
2233 int_enableCursor(m_prevCursorEnabled);
2235 xSemaphoreGive(m_mutex);
2237 if (m_resetRequested)
2242 void Terminal::execCtrlCode(uint8_t c)
2249 if (m_emuState.cursorX > 1)
2250 setCursorPos(m_emuState.cursorX - 1, m_emuState.cursorY);
2251 else if (m_emuState.reverseWraparoundMode) {
2252 int newX = m_columns;
2253 int newY = m_emuState.cursorY - 1;
2256 setCursorPos(newX, newY);
2269 if (!m_emuState.cursorPastLastCol) {
2270 if (m_emuState.newLineMode)
2271 setCursorPos(1, m_emuState.cursorY);
2289 setCursorPos(1, m_emuState.cursorY);
2295 m_emuState.characterSetIndex = 1;
2301 m_emuState.characterSetIndex = 0;
2315 void Terminal::consumeESC()
2318 if (!m_emuState.ANSIMode) {
2323 uint8_t c = getNextCode(
true);
2337 if (c == FABGLEXT_STARTCODE && m_emuState.allowFabGLSequences > 0) {
2349 #if FABGLIB_TERMINAL_DEBUG_REPORT_ESC 2350 logFmt(
"ESC%c\n", c);
2357 m_resetRequested =
true;
2368 setCursorPos(1, m_emuState.cursorY);
2375 setTabStop(m_emuState.cursorX,
true);
2398 restoreCursorState();
2403 c = getNextCode(
true);
2407 setLineDoubleWidth(m_emuState.cursorY, 2);
2411 setLineDoubleWidth(m_emuState.cursorY, 3);
2415 setLineDoubleWidth(m_emuState.cursorY, 0);
2419 setLineDoubleWidth(m_emuState.cursorY, 1);
2423 erase(1, 1, m_columns, m_rows,
'E',
false,
false);
2436 switch (getNextCode(
true)) {
2439 m_emuState.characterSet[c -
'('] = 0;
2442 m_emuState.characterSet[c -
'('] = 1;
2449 m_emuState.keypadMode = KeypadMode::Application;
2450 #if FABGLIB_TERMINAL_DEBUG_REPORT_DESCSALL 2451 log(
"Keypad Application Mode\n");
2457 m_emuState.keypadMode = KeypadMode::Numeric;
2458 #if FABGLIB_TERMINAL_DEBUG_REPORT_DESCSALL 2459 log(
"Keypad Numeric Mode\n");
2464 switch (getNextCode(
true)) {
2468 m_emuState.ctrlBits = 7;
2473 if (m_emuState.conformanceLevel >= 2 && m_emuState.ANSIMode)
2474 m_emuState.ctrlBits = 8;
2481 #if FABGLIB_TERMINAL_DEBUG_REPORT_UNSUPPORT 2482 logFmt(
"Unknown ESC %c\n", c);
2492 uint8_t Terminal::consumeParamsAndGetCode(
int * params,
int * paramsCount,
bool * questionMarkFound)
2496 *questionMarkFound =
false;
2500 uint8_t c = getNextCode(
true);
2502 #if FABGLIB_TERMINAL_DEBUG_REPORT_ESC 2507 *questionMarkFound =
true;
2512 if (!isdigit(c) && c !=
';') {
2514 #if FABGLIB_TERMINAL_DEBUG_REPORT_ESC 2519 while (p < params + FABGLIB_MAX_CSI_PARAMS)
2525 if (p < params + FABGLIB_MAX_CSI_PARAMS) {
2532 *p = *p * 10 + (c -
'0');
2540 void Terminal::consumeCSI()
2542 #if FABGLIB_TERMINAL_DEBUG_REPORT_ESC 2546 bool questionMarkFound;
2547 int params[FABGLIB_MAX_CSI_PARAMS];
2549 uint8_t c = consumeParamsAndGetCode(params, ¶msCount, &questionMarkFound);
2553 if (questionMarkFound && (c ==
'h' || c ==
'l')) {
2554 consumeDECPrivateModes(params, paramsCount, c);
2559 if (c == ASCII_SPC) {
2560 consumeCSISPC(params, paramsCount);
2566 consumeCSIQUOT(params, paramsCount);
2577 setCursorPos(params[1], getAbsoluteRow(params[0]));
2582 switch (params[0]) {
2584 setTabStop(m_emuState.cursorX,
false);
2587 setTabStop(0,
false);
2594 setCursorPos(m_emuState.cursorX + tmax(1, params[0]), m_emuState.cursorY);
2599 deleteAt(m_emuState.cursorX, m_emuState.cursorY, tmax(1, params[0]));
2604 setCursorPos(m_emuState.cursorX, getAbsoluteRow(m_emuState.cursorY - tmax(1, params[0])));
2614 switch (params[0]) {
2616 erase(m_emuState.cursorX, m_emuState.cursorY, m_columns, m_emuState.cursorY, ASCII_SPC,
false, questionMarkFound);
2617 erase(1, m_emuState.cursorY + 1, m_columns, m_rows, ASCII_SPC,
false, questionMarkFound);
2620 erase(1, 1, m_columns, m_emuState.cursorY - 1, ASCII_SPC,
false, questionMarkFound);
2621 erase(1, m_emuState.cursorY, m_emuState.cursorX, m_emuState.cursorY, ASCII_SPC,
false, questionMarkFound);
2624 erase(1, 1, m_columns, m_rows, ASCII_SPC,
false, questionMarkFound);
2636 switch (params[0]) {
2638 erase(m_emuState.cursorX, m_emuState.cursorY, m_columns, m_emuState.cursorY, ASCII_SPC,
true, questionMarkFound);
2641 erase(1, m_emuState.cursorY, m_emuState.cursorX, m_emuState.cursorY, ASCII_SPC,
true, questionMarkFound);
2644 erase(1, m_emuState.cursorY, m_columns, m_emuState.cursorY, ASCII_SPC,
true, questionMarkFound);
2652 erase(m_emuState.cursorX, m_emuState.cursorY, tmin((
int)m_columns, m_emuState.cursorX + tmax(1, params[0]) - 1), m_emuState.cursorY, ASCII_SPC,
true,
false);
2657 setScrollingRegion(tmax(params[0], 1), (params[1] < 1 ? m_rows : params[1]));
2662 setCursorPos(m_emuState.cursorX, params[0]);
2667 setCursorPos(params[0], m_emuState.cursorY);
2672 for (
int i = tmax(1, params[0]); i > 0; --i)
2678 for (
int i = tmax(1, params[0]); i > 0; --i)
2685 int newX = m_emuState.cursorX - tmax(1, params[0]);
2686 if (m_emuState.reverseWraparoundMode && newX < 1) {
2688 int newY = m_emuState.cursorY - newX / m_columns - 1;
2690 newY = m_rows + newY;
2691 newX = m_columns - (newX % m_columns);
2692 setCursorPos(newX, newY);
2694 setCursorPos(tmax(1, newX), m_emuState.cursorY);
2700 setCursorPos(m_emuState.cursorX, getAbsoluteRow(m_emuState.cursorY + tmax(1, params[0])));
2705 execSGRParameters(params, paramsCount);
2710 for (
int i = tmax(1, params[0]); i > 0; --i)
2711 scrollDownAt(m_emuState.cursorY);
2716 for (
int i = tmax(1, params[0]); i > 0; --i)
2717 scrollUpAt(m_emuState.cursorY);
2724 switch (params[0]) {
2728 m_emuState.insertMode = (c ==
'h');
2733 m_emuState.newLineMode = (c ==
'h');
2737 #if FABGLIB_TERMINAL_DEBUG_REPORT_UNSUPPORT 2738 logFmt(
"Unknown: ESC [ %d %c\n", params[0], c);
2746 insertAt(m_emuState.cursorX, m_emuState.cursorY, tmax(1, params[0]));
2751 if (params[0] == 0) {
2759 paramsCount = tmax(1, paramsCount);
2760 for (
int i = 0; i < paramsCount; ++i) {
2761 bool numLock, capsLock, scrollLock;
2762 m_keyboard->
getLEDs(&numLock, &capsLock, &scrollLock);
2763 switch (params[i]) {
2765 numLock = capsLock = scrollLock =
false;
2786 m_keyboard->
setLEDs(numLock, capsLock, scrollLock);
2792 switch (params[0]) {
2803 send(itoa(m_emuState.originMode ? m_emuState.cursorY - m_emuState.scrollingRegionTop + 1 : m_emuState.cursorY, s, 10));
2805 send(itoa(m_emuState.cursorX, s, 10));
2813 #if FABGLIB_TERMINAL_DEBUG_REPORT_UNSUPPORT 2814 log(
"Unknown: ESC [ ");
2815 if (questionMarkFound)
2817 for (
int i = 0; i < paramsCount; ++i)
2818 logFmt(
"%d %c ", params[i], i < paramsCount - 1 ?
';' : c);
2827 void Terminal::consumeCSIQUOT(
int * params,
int paramsCount)
2829 uint8_t c = getNextCode(
true);
2835 m_emuState.conformanceLevel = params[0] - 60;
2836 if (params[0] == 61 || (paramsCount == 2 && params[1] == 1))
2837 m_emuState.ctrlBits = 7;
2839 m_emuState.ctrlBits = 8;
2844 m_glyphOptions.
userOpt2 = (params[0] == 1 ? 1 : 0);
2852 void Terminal::consumeCSISPC(
int * params,
int paramsCount)
2854 uint8_t c = getNextCode(
true);
2861 m_emuState.cursorStyle = params[0];
2862 m_emuState.cursorBlinkingEnabled = (params[0] == 0) || (params[0] & 1);
2866 #if FABGLIB_TERMINAL_DEBUG_REPORT_UNSUPPORT 2867 log(
"Unknown: ESC [ ");
2868 for (
int i = 0; i < paramsCount; ++i)
2869 logFmt(
"%d %c ", params[i], i < paramsCount - 1 ?
';' : ASCII_SPC);
2881 void Terminal::consumeDECPrivateModes(
int const * params,
int paramsCount, uint8_t c)
2883 bool set = (c ==
'h');
2884 switch (params[0]) {
2890 m_emuState.cursorKeysMode =
set;
2897 m_emuState.ANSIMode =
set;
2904 if (m_emuState.allow132ColumnMode) {
2905 set132ColumnMode(
set);
2914 m_emuState.smoothScroll =
set;
2929 m_emuState.originMode =
set;
2931 setCursorPos(m_emuState.cursorX, m_emuState.scrollingRegionTop);
2938 m_emuState.wraparound =
set;
2945 m_emuState.keyAutorepeat =
set;
2952 m_emuState.cursorBlinkingEnabled =
set;
2959 m_prevCursorEnabled =
set;
2966 m_emuState.allow132ColumnMode =
set;
2973 m_emuState.reverseWraparoundMode =
set;
2983 useAlternateScreenBuffer(
set);
2990 m_emuState.backarrowKeyMode =
set;
2999 restoreCursorState();
3008 useAlternateScreenBuffer(
true);
3010 useAlternateScreenBuffer(
false);
3011 restoreCursorState();
3020 enableFabGLSequences(
set);
3024 #if FABGLIB_TERMINAL_DEBUG_REPORT_UNSUPPORT 3025 logFmt(
"Unknown DECSET/DECRST: %d %c\n", params[0], c);
3034 void Terminal::execSGRParameters(
int const * params,
int paramsCount)
3036 for (; paramsCount; ++params, --paramsCount) {
3041 m_glyphOptions.
bold = 0;
3043 m_glyphOptions.
italic = 0;
3046 m_glyphOptions.
blank = 0;
3047 m_glyphOptions.
invert = 0;
3048 int_setForegroundColor(m_defaultForegroundColor);
3049 int_setBackgroundColor(m_defaultBackgroundColor);
3054 m_glyphOptions.
bold = 1;
3064 m_emuState.characterSetIndex = 0;
3074 m_glyphOptions.
italic = 1;
3079 m_glyphOptions.
italic = 0;
3104 m_glyphOptions.
invert = 1;
3109 m_glyphOptions.
invert = 0;
3114 m_glyphOptions.
blank = 1;
3119 m_glyphOptions.
blank = 0;
3124 int_setForegroundColor( (
Color) (*params - 30) );
3129 int_setForegroundColor(m_defaultForegroundColor);
3134 int_setBackgroundColor( (
Color) (*params - 40) );
3139 int_setBackgroundColor(m_defaultBackgroundColor);
3144 int_setForegroundColor( (
Color) (8 + *params - 90) );
3149 int_setBackgroundColor( (
Color) (8 + *params - 100) );
3153 #if FABGLIB_TERMINAL_DEBUG_REPORT_UNSUPPORT 3154 logFmt(
"Unknown: ESC [ %d m\n", *params);
3160 if (m_bitmappedDisplayController &&
isActive())
3167 void Terminal::consumeDCS()
3169 #if FABGLIB_TERMINAL_DEBUG_REPORT_ESC 3174 bool questionMarkFound;
3175 int params[FABGLIB_MAX_CSI_PARAMS];
3177 uint8_t c = consumeParamsAndGetCode(params, ¶msCount, &questionMarkFound);
3180 uint8_t content[FABGLIB_MAX_DCS_CONTENT];
3181 int contentLength = 0;
3182 content[contentLength++] = c;
3184 uint8_t c = getNextCode(
false);
3185 if (c == ASCII_ESC) {
3186 if (getNextCode(
false) ==
'\\')
3189 #if FABGLIB_TERMINAL_DEBUG_REPORT_UNSUPPORT 3190 log(
"DCS failed, expected ST\n");
3194 }
else if (contentLength == FABGLIB_MAX_DCS_CONTENT) {
3195 #if FABGLIB_TERMINAL_DEBUG_REPORT_UNSUPPORT 3196 log(
"DCS failed, content too long\n");
3200 content[contentLength++] = c;
3204 if (m_emuState.conformanceLevel >= 3 && contentLength > 2 && content[0] ==
'$' && content[1] ==
'q') {
3209 if (contentLength == 4 && content[2] ==
'\"' && content[3] ==
'p') {
3212 send(
'0' + m_emuState.conformanceLevel);
3214 send(m_emuState.ctrlBits == 7 ?
'1' :
'0');
3221 #if FABGLIB_TERMINAL_DEBUG_REPORT_UNSUPPORT 3222 log(
"Unknown: ESC P ");
3223 for (
int i = 0; i < paramsCount; ++i)
3224 logFmt(
"%d %c ", params[i], i < paramsCount - 1 ?
';' : ASCII_SPC);
3225 logFmt(
"%.*s ESC \\\n", contentLength, content);
3230 void Terminal::consumeESCVT52()
3232 uint8_t c = getNextCode(
false);
3234 #if FABGLIB_TERMINAL_DEBUG_REPORT_ESC 3235 logFmt(
"ESC%c\n", c);
3239 if (c == FABGLEXT_STARTCODE && m_emuState.allowFabGLSequences > 0) {
3249 m_emuState.ANSIMode =
true;
3250 m_emuState.conformanceLevel = 1;
3255 setCursorPos(m_emuState.cursorX, m_emuState.cursorY - 1);
3260 setCursorPos(m_emuState.cursorX, m_emuState.cursorY + 1);
3265 setCursorPos(m_emuState.cursorX + 1, m_emuState.cursorY);
3270 setCursorPos(m_emuState.cursorX -1, m_emuState.cursorY);
3286 erase(m_emuState.cursorX, m_emuState.cursorY, m_columns, m_emuState.cursorY, ASCII_SPC,
false,
false);
3287 erase(1, m_emuState.cursorY + 1, m_columns, m_rows, ASCII_SPC,
false,
false);
3292 erase(m_emuState.cursorX, m_emuState.cursorY, m_columns, m_emuState.cursorY, ASCII_SPC,
true,
false);
3298 int row = getNextCode(
false) - 31;
3299 int col = getNextCode(
false) - 31;
3300 setCursorPos(col, row);
3311 m_emuState.keypadMode = KeypadMode::Application;
3312 #if FABGLIB_TERMINAL_DEBUG_REPORT_DESCSALL 3313 log(
"Enter Alternate Keypad Mode\n");
3319 m_emuState.keypadMode = KeypadMode::Numeric;
3320 #if FABGLIB_TERMINAL_DEBUG_REPORT_DESCSALL 3321 log(
"Exit Alternate Keypad Mode\n");
3327 m_emuState.VT52GraphicsMode =
true;
3332 m_emuState.VT52GraphicsMode =
false;
3337 #if FABGLIB_TERMINAL_DEBUG_REPORT_UNSUPPORT 3338 logFmt(
"Unknown ESC %c\n", c);
3348 void Terminal::consumeOSC()
3350 #if FABGLIB_TERMINAL_DEBUG_REPORT_ESC 3356 uint8_t c = getNextCode(
false);
3357 #if FABGLIB_TERMINAL_DEBUG_REPORT_OSC_CONTENT 3358 logFmt(
"OSC: %02X %s%c\n", (
int)c, (c <= ASCII_SPC ? CTRLCHAR_TO_STR[(
int)(c)] :
""), (c > ASCII_SPC ? c : ASCII_SPC));
3360 if (c == ASCII_BEL || (c ==
'\\' && prevChar == ASCII_ESC))
3367 void Terminal::sound(
int waveform,
int frequency,
int duration,
int volume)
3369 if (!m_soundGenerator)
3370 m_soundGenerator =
new SoundGenerator;
3373 m_soundGenerator->
playSound(SineWaveformGenerator(), frequency, duration, volume);
3376 m_soundGenerator->
playSound(SquareWaveformGenerator(), frequency, duration, volume);
3379 m_soundGenerator->
playSound(TriangleWaveformGenerator(), frequency, duration, volume);
3382 m_soundGenerator->
playSound(SawtoothWaveformGenerator(), frequency, duration, volume);
3385 m_soundGenerator->
playSound(NoiseWaveformGenerator(), frequency, duration, volume);
3388 m_soundGenerator->
playSound(VICNoiseGenerator(), frequency, duration, volume);
3395 uint8_t Terminal::extGetByteParam()
3397 if (m_extNextCode > -1) {
3398 auto r = m_extNextCode;
3402 return getNextCode(
false);
3408 int Terminal::extGetIntParam()
3413 uint8_t c = extGetByteParam();
3420 }
else if (c ==
'+' || c ==
' ') {
3428 }
else if (sign == -2)
3430 val = val * 10 + c -
'0';
3438 void Terminal::extGetCmdParam(
char * cmd)
3441 for (; len < FABGLEXT_MAXSUBCMDLEN - 1; ++len) {
3442 uint8_t c = extGetByteParam();
3455 void Terminal::consumeFabGLSeq()
3457 #if FABGLIB_TERMINAL_DEBUG_REPORT_ESC 3458 log(
"ESC FABGLEXT_STARTCODE");
3463 uint8_t c = extGetByteParam();
3473 case FABGLEXTX_CLEAR:
3475 syncDisplayController();
3476 erase(1, 1, m_columns, m_rows, ASCII_SPC,
false,
false);
3484 case FABGLEXTX_ENABLECURSOR:
3485 m_prevCursorEnabled = (extGetByteParam() ==
'1');
3497 case FABGLEXTB_GETCURSORCOL:
3499 send(FABGLEXT_REPLYCODE);
3500 send(m_emuState.cursorX);
3511 case FABGLEXTB_GETCURSORROW:
3513 send(FABGLEXT_REPLYCODE);
3514 send(m_emuState.cursorY);
3526 case FABGLEXTB_GETCURSORPOS:
3528 send(FABGLEXT_REPLYCODE);
3529 send(m_emuState.cursorX);
3530 send(m_emuState.cursorY);
3539 case FABGLEXTB_SETCURSORPOS:
3541 uint8_t col = extGetByteParam();
3542 uint8_t row = extGetByteParam();
3544 setCursorPos(col, getAbsoluteRow(row));
3554 case FABGLEXTX_SETCURSORPOS:
3556 uint8_t col = extGetIntParam();
3558 uint8_t row = extGetIntParam();
3560 setCursorPos(col, getAbsoluteRow(row));
3574 case FABGLEXTB_INSERTSPACE:
3576 uint8_t charsToMove_L = extGetByteParam();
3577 uint8_t charsToMove_H = extGetByteParam();
3579 bool scroll = multilineInsertChar(charsToMove_L | charsToMove_H << 8);
3580 send(FABGLEXT_REPLYCODE);
3591 case FABGLEXTB_DELETECHAR:
3593 uint8_t charsToMove_L = extGetByteParam();
3594 uint8_t charsToMove_H = extGetByteParam();
3596 multilineDeleteChar(charsToMove_L | charsToMove_H << 8);
3605 case FABGLEXTB_CURSORLEFT:
3607 uint8_t count_L = extGetByteParam();
3608 uint8_t count_H = extGetByteParam();
3610 move(-(count_L | count_H << 8));
3619 case FABGLEXTB_CURSORRIGHT:
3621 uint8_t count_L = extGetByteParam();
3622 uint8_t count_H = extGetByteParam();
3624 move(count_L | count_H << 8);
3637 case FABGLEXTB_SETCHAR:
3639 bool scroll = setChar(extGetByteParam());
3641 send(FABGLEXT_REPLYCODE);
3654 case FABGLEXTB_ISVKDOWN:
3658 send(FABGLEXT_REPLYCODE);
3666 case FABGLEXTB_DISABLEFABSEQ:
3668 enableFabGLSequences(
false);
3676 case FABGLEXTB_SETTERMTYPE:
3678 auto termType = (
TermType) extGetByteParam();
3680 int_setTerminalType(termType);
3689 case FABGLEXTB_SETFGCOLOR:
3690 int_setForegroundColor((
Color) extGetByteParam());
3699 case FABGLEXTB_SETBGCOLOR:
3700 int_setBackgroundColor((
Color) extGetByteParam());
3710 case FABGLEXTB_SETCHARSTYLE:
3712 int idx = extGetByteParam();
3713 int val = extGetByteParam();
3717 m_glyphOptions.
bold = val;
3723 m_glyphOptions.
italic = val;
3732 m_glyphOptions.
blank = val;
3735 m_glyphOptions.
invert = val;
3738 if (m_bitmappedDisplayController &&
isActive())
3755 case FABGLEXTX_SETUPGPIO:
3757 auto mode = GPIO_MODE_DISABLE;
3758 switch (extGetByteParam()) {
3760 mode = GPIO_MODE_INPUT;
3763 mode = GPIO_MODE_OUTPUT;
3766 mode = GPIO_MODE_OUTPUT_OD;
3769 mode = GPIO_MODE_INPUT_OUTPUT_OD;
3772 mode = GPIO_MODE_INPUT_OUTPUT;
3775 auto gpio = (gpio_num_t) extGetIntParam();
3777 configureGPIO(gpio, mode);
3787 case FABGLEXTX_SETGPIO:
3789 auto l = extGetByteParam();
3790 auto level = (l == 1 || l ==
'1' || l ==
'H') ? 1 : 0;
3791 auto gpio = (gpio_num_t) extGetIntParam();
3793 gpio_set_level(gpio, level);
3805 case FABGLEXTX_GETGPIO:
3807 auto gpio = (gpio_num_t) extGetIntParam();
3809 send(FABGLEXT_REPLYCODE);
3810 send(gpio_get_level(gpio) ?
'1' :
'0');
3825 case FABGLEXTX_SETUPADC:
3827 auto width = (adc_bits_width_t) (extGetIntParam() - 9);
3829 auto atten = (adc_atten_t) extGetIntParam();
3831 auto channel = ADC1_GPIO2Channel((gpio_num_t)extGetIntParam());
3833 adc1_config_width(
width);
3834 adc1_config_channel_atten(channel, atten);
3853 case FABGLEXTX_READADC:
3855 auto val = adc1_get_raw(ADC1_GPIO2Channel((gpio_num_t)extGetIntParam()));
3857 send(FABGLEXT_REPLYCODE);
3858 send(toupper(digit2hex((val & 0xF00) >> 8)));
3859 send(toupper(digit2hex((val & 0x0F0) >> 4)));
3860 send(toupper(digit2hex(val & 0x00F)));
3872 case FABGLEXTX_SOUND:
3874 char waveform = extGetByteParam();
3876 uint16_t frequency = extGetIntParam();
3878 uint16_t duration = extGetIntParam();
3880 uint8_t volume = extGetIntParam() & 0x7f;
3882 sound(waveform, frequency, duration, volume);
3889 case FABGLEXTX_GRAPHICSCMD:
3890 consumeFabGLGraphicsSeq();
3898 case FABGLEXTX_SHOWMOUSE:
3900 bool value = (extGetByteParam() ==
'1');
3901 if (m_bitmappedDisplayController) {
3902 auto dispctrl =
static_cast<BitmappedDisplayController*
>(m_displayController);
3904 if (mouse && mouse->isMouseAvailable()) {
3909 dispctrl->setMouseCursor(
nullptr);
3910 mouse->terminateAbsolutePositioner();
3932 case FABGLEXTX_GETMOUSEPOS:
3935 if (m_bitmappedDisplayController) {
3937 auto x = mouse->status().X;
3938 auto y = mouse->status().Y;
3939 send(FABGLEXT_REPLYCODE);
3941 send(toupper(digit2hex((x & 0xF00) >> 8)));
3942 send(toupper(digit2hex((x & 0x0F0) >> 4)));
3943 send(toupper(digit2hex((x & 0x00F) )));
3946 send(toupper(digit2hex((y & 0xF00) >> 8)));
3947 send(toupper(digit2hex((y & 0x0F0) >> 4)));
3948 send(toupper(digit2hex((y & 0x00F) )));
3951 send(toupper(digit2hex(mouse->status().wheelDelta & 0xf)));
3954 auto b = mouse->status().buttons;
3955 send(toupper(digit2hex( b.left | (b.middle << 1) | (b.right << 2) )));
3967 case FABGLEXTX_DELAY:
3969 auto value = extGetIntParam();
3971 vTaskDelay(value / portTICK_PERIOD_MS);
3972 send(FABGLEXT_REPLYCODE);
3981 case FABGLEXT_USERSEQ:
3983 char usrseq[FABGLEXT_MAXSUBCMDLEN];
3985 while (count < FABGLEXT_MAXSUBCMDLEN) {
3986 char c = extGetByteParam();
3987 if (c == FABGLEXT_ENDCODE)
3989 usrseq[count++] = c;
3997 #if FABGLIB_TERMINAL_DEBUG_REPORT_UNSUPPORT 3998 logFmt(
"Unknown: ESC FABGLEXT_STARTCODE %02x\n", c);
4005 void Terminal::freeSprites()
4007 for (
int i = 0; i < m_spritesCount; ++i) {
4008 for (
int j = 0; j < m_sprites[i].framesCount; ++j) {
4009 free(m_sprites[i].frames[j]->
data);
4010 delete m_sprites[i].frames[j];
4013 delete [] m_sprites;
4014 m_sprites =
nullptr;
4021 void Terminal::consumeFabGLGraphicsSeq()
4023 char cmd[FABGLEXT_MAXSUBCMDLEN];
4024 extGetCmdParam(cmd);
4026 if (strcmp(cmd, FABGLEXT_GCLEAR) == 0) {
4037 }
else if (strcmp(cmd, FABGLEXT_GSETBRUSHCOLOR) == 0) {
4046 int r = extGetIntParam();
4048 int g = extGetIntParam();
4050 int b = extGetIntParam();
4055 }
else if (strcmp(cmd, FABGLEXT_GSETPENCOLOR) == 0) {
4064 int r = extGetIntParam();
4066 int g = extGetIntParam();
4068 int b = extGetIntParam();
4073 }
else if (strcmp(cmd, FABGLEXT_GSETPIXEL) == 0) {
4081 int x = extGetIntParam();
4083 int y = extGetIntParam();
4088 }
else if (strcmp(cmd, FABGLEXT_GSCROLL) == 0) {
4096 int ox = extGetIntParam();
4098 int oy = extGetIntParam();
4101 m_canvas->
scroll(ox, oy);
4103 }
else if (strcmp(cmd, FABGLEXT_GPENWIDTH) == 0) {
4110 int w = extGetIntParam();
4115 }
else if (strcmp(cmd, FABGLEXT_GLINE) == 0) {
4125 int x1 = extGetIntParam();
4127 int y1 = extGetIntParam();
4129 int x2 = extGetIntParam();
4131 int y2 = extGetIntParam();
4134 m_canvas->
drawLine(x1, y1, x2, y2);
4136 }
else if (strcmp(cmd, FABGLEXT_GRECT) == 0) {
4146 int x1 = extGetIntParam();
4148 int y1 = extGetIntParam();
4150 int x2 = extGetIntParam();
4152 int y2 = extGetIntParam();
4157 }
else if (strcmp(cmd, FABGLEXT_GFILLRECT) == 0) {
4167 int x1 = extGetIntParam();
4169 int y1 = extGetIntParam();
4171 int x2 = extGetIntParam();
4173 int y2 = extGetIntParam();
4178 }
else if (strcmp(cmd, FABGLEXT_GELLIPSE) == 0) {
4188 int x = extGetIntParam();
4190 int y = extGetIntParam();
4192 int w = extGetIntParam();
4194 int h = extGetIntParam();
4199 }
else if (strcmp(cmd, FABGLEXT_GFILLELLIPSE) == 0) {
4209 int x = extGetIntParam();
4211 int y = extGetIntParam();
4213 int w = extGetIntParam();
4215 int h = extGetIntParam();
4220 }
else if (strcmp(cmd, FABGLEXT_GPATH) == 0) {
4230 constexpr
int MAXPOINTS = 32;
4231 Point pts[MAXPOINTS];
4233 while (count < MAXPOINTS) {
4234 pts[count].X = extGetIntParam();
4236 pts[count].Y = extGetIntParam();
4238 if (extGetByteParam() == FABGLEXT_ENDCODE)
4244 }
else if (strcmp(cmd, FABGLEXT_GFILLPATH) == 0) {
4254 constexpr
int MAXPOINTS = 32;
4255 Point pts[MAXPOINTS];
4257 while (count < MAXPOINTS) {
4258 pts[count].X = extGetIntParam();
4260 pts[count].Y = extGetIntParam();
4262 if (extGetByteParam() == FABGLEXT_ENDCODE)
4268 }
else if (strcmp(cmd, FABGLEXT_GSPRITECOUNT) == 0) {
4275 int count = extGetIntParam();
4277 if (m_bitmappedDisplayController) {
4278 static_cast<BitmappedDisplayController*
>(m_displayController)->setSprites<Sprite>(
nullptr, 0);
4281 m_spritesCount = count;
4282 m_sprites =
new Sprite[count];
4286 }
else if (strcmp(cmd, FABGLEXT_GSPRITEDEF) == 0) {
4300 int sprite = extGetIntParam();
4302 int width = extGetIntParam();
4304 int height = extGetIntParam();
4306 char cformat = extGetByteParam();
4308 int r = 0, g = 0, b = 0;
4313 r = extGetIntParam();
4315 g = extGetIntParam();
4317 b = extGetIntParam();
4331 auto data = (uint8_t*) malloc(bytes);
4332 for (
int i = 0; i < bytes + 1; ++i) {
4333 auto c = extGetByteParam();
4334 if (c == FABGLEXT_ENDCODE)
4336 data[i] = hex2digit(tolower(c)) << 4;
4337 c = extGetByteParam();
4338 if (c == FABGLEXT_ENDCODE)
4340 data[i] |= hex2digit(tolower(c));
4342 if (m_bitmappedDisplayController && sprite < m_spritesCount) {
4343 auto bitmap =
new Bitmap(
width,
height,
data, format, RGB888(r, g, b),
false);
4344 m_sprites[sprite].addBitmap(bitmap);
4345 static_cast<BitmappedDisplayController*
>(m_displayController)->setSprites(m_sprites, m_spritesCount);
4351 }
else if (strcmp(cmd, FABGLEXT_GSPRITESET) == 0) {
4362 int sprite = extGetIntParam();
4364 char visible = extGetByteParam();
4366 int frame = extGetIntParam();
4368 int posx = extGetIntParam();
4370 int posy = extGetIntParam();
4372 if (m_bitmappedDisplayController && sprite < m_spritesCount) {
4373 m_sprites[sprite].visible = (visible ==
'V');
4374 m_sprites[sprite].setFrame(frame);
4375 m_sprites[sprite].x = posx;
4376 m_sprites[sprite].y = posy;
4377 static_cast<BitmappedDisplayController*
>(m_displayController)->refreshSprites();
4381 #if FABGLIB_TERMINAL_DEBUG_REPORT_UNSUPPORT 4382 logFmt(
"Unknown: ESC FABGLEXT_STARTCODE FABGLEXTX_GRAPHICSCMD %s\n", cmd);
4388 void Terminal::keyboardReaderTask(
void * pvParameters)
4390 Terminal * term = (Terminal*) pvParameters;
4394 if (!term->isActive())
4398 VirtualKey vk = term->m_keyboard->getNextVirtualKey(&keyDown);
4400 if (term->isActive()) {
4402 term->onVirtualKey(&vk, keyDown);
4406 if (!term->m_emuState.keyAutorepeat && term->m_lastPressedKey == vk)
4408 term->m_lastPressedKey = vk;
4410 xSemaphoreTake(term->m_mutex, portMAX_DELAY);
4412 if (term->m_termInfo ==
nullptr) {
4413 if (term->m_emuState.ANSIMode)
4414 term->ANSIDecodeVirtualKey(vk);
4416 term->VT52DecodeVirtualKey(vk);
4418 term->TermDecodeVirtualKey(vk);
4420 xSemaphoreGive(term->m_mutex);
4424 term->m_lastPressedKey =
VK_NONE;
4429 term->m_keyboard->injectVirtualKey(vk, keyDown,
true);
4436 void Terminal::sendCursorKeyCode(uint8_t c)
4438 if (m_emuState.cursorKeysMode)
4446 void Terminal::sendKeypadCursorKeyCode(uint8_t applicationCode,
const char * numericCode)
4448 if (m_emuState.keypadMode == KeypadMode::Application) {
4450 send(applicationCode);
4458 void Terminal::ANSIDecodeVirtualKey(
VirtualKey vk)
4465 sendCursorKeyCode(
'A');
4469 sendCursorKeyCode(
'B');
4473 sendCursorKeyCode(
'C');
4477 sendCursorKeyCode(
'D');
4483 sendKeypadCursorKeyCode(
'x',
"A");
4487 sendKeypadCursorKeyCode(
'r',
"B");
4491 sendKeypadCursorKeyCode(
'v',
"C");
4495 sendKeypadCursorKeyCode(
't',
"D");
4533 sendKeypadCursorKeyCode(
'y',
"5~");
4537 sendKeypadCursorKeyCode(
's',
"6~");
4541 sendKeypadCursorKeyCode(
'p',
"2~");
4545 sendKeypadCursorKeyCode(
'w',
"1~");
4549 sendKeypadCursorKeyCode(
'n',
"3~");
4553 sendKeypadCursorKeyCode(
'q',
"4~");
4559 send(m_emuState.backarrowKeyMode ? ASCII_BS : ASCII_DEL);
4633 if (m_emuState.newLineMode)
4651 void Terminal::VT52DecodeVirtualKey(
VirtualKey vk)
4677 send(m_emuState.keypadMode == KeypadMode::Application ?
"\e?p" :
"0");
4682 send(m_emuState.keypadMode == KeypadMode::Application ?
"\e?q" :
"1");
4687 send(m_emuState.keypadMode == KeypadMode::Application ?
"\e?r" :
"2");
4692 send(m_emuState.keypadMode == KeypadMode::Application ?
"\e?s" :
"3");
4697 send(m_emuState.keypadMode == KeypadMode::Application ?
"\e?t" :
"4");
4702 send(m_emuState.keypadMode == KeypadMode::Application ?
"\e?u" :
"5");
4707 send(m_emuState.keypadMode == KeypadMode::Application ?
"\e?v" :
"6");
4712 send(m_emuState.keypadMode == KeypadMode::Application ?
"\e?w" :
"7");
4717 send(m_emuState.keypadMode == KeypadMode::Application ?
"\e?x" :
"8");
4722 send(m_emuState.keypadMode == KeypadMode::Application ?
"\e?y" :
"9");
4727 send(m_emuState.keypadMode == KeypadMode::Application ?
"\e?n" :
".");
4731 send(m_emuState.keypadMode == KeypadMode::Application ?
"\e?M" :
"\r");
4749 void Terminal::TermDecodeVirtualKey(
VirtualKey vk)
4751 for (
auto item = m_termInfo->kbdCtrlSet; item->vk !=
VK_NONE; ++item) {
4752 if (item->vk == vk) {
4753 send(item->ANSICtrlCode);
4772 : m_terminal(terminal)
4777 TerminalController::~TerminalController()
4784 m_terminal = terminal;
4788 void TerminalController::write(uint8_t c)
4791 m_terminal->
write(c);
4797 void TerminalController::write(
char const * str)
4804 int TerminalController::read()
4807 return m_terminal->
read(-1);
4816 void TerminalController::waitFor(
int value)
4819 if (read() == value)
4826 write(FABGLEXT_CMD);
4827 write(FABGLEXTX_CLEAR);
4828 write(FABGLEXT_ENDCODE);
4834 write(FABGLEXT_CMD);
4835 write(FABGLEXTX_ENABLECURSOR);
4836 write(value ?
'1' :
'0');
4837 write(FABGLEXT_ENDCODE);
4843 write(FABGLEXT_CMD);
4844 write(FABGLEXTB_SETCURSORPOS);
4847 write(FABGLEXT_ENDCODE);
4853 write(FABGLEXT_CMD);
4854 write(FABGLEXTB_CURSORLEFT);
4855 write(count & 0xff);
4857 write(FABGLEXT_ENDCODE);
4863 write(FABGLEXT_CMD);
4864 write(FABGLEXTB_CURSORRIGHT);
4865 write(count & 0xff);
4867 write(FABGLEXT_ENDCODE);
4873 write(FABGLEXT_CMD);
4874 write(FABGLEXTB_GETCURSORPOS);
4875 write(FABGLEXT_ENDCODE);
4876 waitFor(FABGLEXT_REPLYCODE);
4884 write(FABGLEXT_CMD);
4885 write(FABGLEXTB_GETCURSORCOL);
4886 write(FABGLEXT_ENDCODE);
4887 waitFor(FABGLEXT_REPLYCODE);
4894 write(FABGLEXT_CMD);
4895 write(FABGLEXTB_GETCURSORROW);
4896 write(FABGLEXT_ENDCODE);
4897 waitFor(FABGLEXT_REPLYCODE);
4904 write(FABGLEXT_CMD);
4905 write(FABGLEXTB_INSERTSPACE);
4906 write(charsToMove & 0xff);
4907 write(charsToMove >> 8);
4908 write(FABGLEXT_ENDCODE);
4909 waitFor(FABGLEXT_REPLYCODE);
4916 write(FABGLEXT_CMD);
4917 write(FABGLEXTB_DELETECHAR);
4918 write(charsToMove & 0xff);
4919 write(charsToMove >> 8);
4920 write(FABGLEXT_ENDCODE);
4926 write(FABGLEXT_CMD);
4927 write(FABGLEXTB_SETCHAR);
4929 write(FABGLEXT_ENDCODE);
4930 waitFor(FABGLEXT_REPLYCODE);
4937 write(FABGLEXT_CMD);
4938 write(FABGLEXTB_ISVKDOWN);
4940 write(FABGLEXT_ENDCODE);
4941 waitFor(FABGLEXT_REPLYCODE);
4942 return read() ==
'1';
4948 write(FABGLEXT_CMD);
4949 write(FABGLEXTB_DISABLEFABSEQ);
4950 write(FABGLEXT_ENDCODE);
4956 write(FABGLEXT_CMD);
4957 write(FABGLEXTB_SETTERMTYPE);
4959 write(FABGLEXT_ENDCODE);
4965 write(FABGLEXT_CMD);
4966 write(FABGLEXTB_SETFGCOLOR);
4968 write(FABGLEXT_ENDCODE);
4974 write(FABGLEXT_CMD);
4975 write(FABGLEXTB_SETBGCOLOR);
4977 write(FABGLEXT_ENDCODE);
4983 write(FABGLEXT_CMD);
4984 write(FABGLEXTB_SETCHARSTYLE);
4986 write(enabled ? 1 : 0);
4987 write(FABGLEXT_ENDCODE);
4998 : m_terminal(terminal),
4999 m_termctrl(terminal),
5005 m_typeText(nullptr),
5011 LineEditor::~LineEditor()
5019 void LineEditor::setLength(
int newLength)
5021 if (m_allocated < newLength || m_allocated == 0) {
5022 int allocated = imax(m_allocated * 2, newLength);
5023 m_text = (
char*) realloc(m_text, allocated + 1);
5024 memset(m_text + m_allocated, 0, allocated - m_allocated + 1);
5025 m_allocated = allocated;
5027 m_textLength = newLength;
5035 m_typeText = strdup(text);
5042 setText(text, strlen(text), moveCursor);
5051 for (
int i = 0; i < m_textLength; ++i)
5054 for (
int i = 0; i < length; ++i)
5055 m_homeRow -= m_termctrl.
setChar(text[i]);
5058 memcpy(m_text, text, length);
5060 m_inputPos = moveCursor ? length : 0;
5064 void LineEditor::write(uint8_t c)
5067 m_terminal->
write(c);
5073 int LineEditor::read()
5076 return m_terminal->
read(-1);
5085 void LineEditor::beginInput()
5087 if (m_terminal ==
nullptr) {
5096 for (
int i = 0, len = strlen(m_text); i < len; ++i)
5097 m_homeRow -= m_termctrl.
setChar(m_text[i]);
5098 if (m_inputPos == 0)
5107 void LineEditor::endInput()
5110 if (m_text ==
nullptr) {
5111 m_text = (
char*) malloc(1);
5117 void LineEditor::performCursorUp()
5123 void LineEditor::performCursorDown()
5129 void LineEditor::performCursorLeft()
5131 if (m_inputPos > 0) {
5135 while (m_inputPos - count > 0 && (m_text[m_inputPos - count] == ASCII_SPC || m_text[m_inputPos - count - 1] != ASCII_SPC))
5139 m_inputPos -= count;
5144 void LineEditor::performCursorRight()
5146 if (m_inputPos < m_textLength) {
5150 while (m_text[m_inputPos + count] && (m_text[m_inputPos + count] == ASCII_SPC || m_text[m_inputPos + count - 1] != ASCII_SPC))
5154 m_inputPos += count;
5159 void LineEditor::performCursorHome()
5166 void LineEditor::performCursorEnd()
5168 m_termctrl.
cursorRight(m_textLength - m_inputPos);
5169 m_inputPos = m_textLength;
5173 void LineEditor::performDeleteRight()
5175 if (m_inputPos < m_textLength) {
5176 memmove(m_text + m_inputPos, m_text + m_inputPos + 1, m_textLength - m_inputPos);
5183 void LineEditor::performDeleteLeft()
5185 if (m_inputPos > 0) {
5188 memmove(m_text + m_inputPos - 1, m_text + m_inputPos, m_textLength - m_inputPos + 1);
5207 c = m_typeText[m_typingIndex++];
5210 m_typeText =
nullptr;
5240 }
else if (m_state == 2) {
5248 performCursorHome();
5259 }
else if (m_state >= 31) {
5279 performCursorLeft();
5285 performCursorRight();
5301 performCursorHome();
5311 performDeleteRight();
5316 m_insertMode = !m_insertMode;
5348 performDeleteLeft();
5353 performDeleteRight();
5362 m_termctrl.
cursorRight(m_textLength - m_inputPos);
5380 performCursorDown();
5385 performCursorLeft();
5390 performCursorRight();
5397 if (maxLength == 0 || m_inputPos < maxLength) {
5399 if (m_insertMode || m_inputPos == m_textLength) {
5400 setLength(m_textLength + 1);
5401 memmove(m_text + m_inputPos + 1, m_text + m_inputPos, m_textLength - m_inputPos);
5403 m_text[m_inputPos++] = c;
5405 if (m_insertMode && m_inputPos < m_textLength) {
Delegate< int > onWrite
Write character delegate.
void flush()
Waits for all codes sent to the display has been processed.
Delegate< int * > onCarriageReturn
A delegate called whenever carriage return has been pressed.
void end()
Finalizes the terminal.
void disconnectLocally()
Avoids using of terminal locally.
A class with a set of drawing methods.
void activate(TerminalTransition transition=TerminalTransition::None)
Activates this terminal for input and output.
void setForegroundColor(Color value)
Sets foreground color.
TerminalController(Terminal *terminal=nullptr)
Object constructor.
bool isKeyboardAvailable()
Checks if keyboard has been detected and correctly initialized.
char const * edit(int maxLength=0)
Reads user input and return the inserted line.
void playSound(T const &waveform, int frequency, int durationMS, int volume=100)
Plays the specified waveform.
void connectLocally()
Permits using of terminal locally.
bool waitFor(int value, int timeOutMS=-1)
Wait for a specific code from keyboard, discarding all previous codes.
void setCursorPos(int col, int row)
Sets current cursor position.
Delegate< int * > onRead
Read character delegate.
void typeText(char const *text)
Simulates user typing.
int getHeight()
Determines the canvas height in pixels.
Keyboard * keyboard()
Returns the instance of Keyboard object automatically created by PS2Controller.
int getColumns()
Returns the number of columns.
int virtualKeyToASCII(VirtualKey virtualKey)
Converts virtual key to ASCII.
void reset()
Resets paint state and other display controller settings.
void scroll(int offsetX, int offsetY)
Scrolls pixels horizontally and/or vertically.
FlowControl
This enum defines various serial port flow control methods.
void setForegroundColor(Color color, bool setAsDefault=true)
Sets the foreground color.
static int inputQueueSize
Number of characters the terminal can "write" without pause (increase if you have loss of characters ...
bool multilineInsertChar(int charsToMove)
Inserts a blank character and move specified amount of characters to the right.
TerminalTransition
This enum defines terminal transition effect.
void enableCursor(bool value)
Enables or disables cursor.
uint16_t reduceLuminosity
Color
This enum defines named colors.
void getLEDs(bool *numLock, bool *capsLock, bool *scrollLock)
Gets keyboard LEDs status.
This file contains fabgl::Terminal definition.
The PS2 Keyboard controller class.
CharStyle
This enum defines a character style.
void getCursorPos(int *col, int *row)
Gets current cursor position.
int getWidth()
Determines the canvas width in pixels.
void setPaintOptions(PaintOptions options)
Sets paint options.
VirtualKey
Represents each possible real or derived (SHIFT + real) key.
This file contains fabgl::Mouse definition.
bool setLEDs(bool numLock, bool capsLock, bool scrollLock)
Sets keyboard LEDs status.
void pollSerialPort()
Pools the serial port for incoming data.
void fillEllipse(int X, int Y, int width, int height)
Fills an ellipse specifying center and size, using current brush color.
void swapRectangle(int X1, int Y1, int X2, int Y2)
Swaps pen and brush colors of the specified rectangle.
int getRows()
Returns the number of lines.
void setBrushColor(uint8_t red, uint8_t green, uint8_t blue)
Sets brush (background) color specifying color components.
void setCharStyle(CharStyle style, bool enabled)
Enables or disables specified character style.
size_t write(const uint8_t *buffer, size_t size)
Sends specified number of codes to the display.
PixelFormat
This enum defines a pixel format.
void waitCompletion(bool waitVSync=true)
Waits for drawing queue to become empty.
void endUpdate()
Resumes drawings after beginUpdate().
static int keyboardReaderTaskStackSize
Stack size of the task that reads keys from keyboard and send ANSI/VT codes to output stream in Termi...
This file contains some utility classes and functions.
static PS2Controller * instance()
Returns the singleton instance of PS2Controller class.
Delegate< char const * > onUserSequence
Delegate called whenever a new user sequence has been received.
void fillRectangle(int X1, int Y1, int X2, int Y2)
Fills a rectangle using the current brush color.
int read()
Reads codes from keyboard.
Delegate< int * > onRead
Read character delegate.
TerminalController allows direct controlling of the Terminal object without using escape sequences...
bool begin(BaseDisplayController *displayController, int maxColumns=-1, int maxRows=-1, Keyboard *keyboard=nullptr)
Initializes the terminal.
void drawGlyph(int X, int Y, int width, int height, uint8_t const *data, int index=0)
Draws a glyph at specified position.
virtual DisplayControllerType controllerType()=0
Determines the display controller type.
void multilineDeleteChar(int charsToMove)
Deletes a character moving specified amount of characters to the left.
int getCursorRow()
Gets current cursor row.
static int inputConsumerTaskStackSize
Stack size of the task that processes Terminal input stream.
int available()
Gets the number of codes available in the keyboard queue.
LineEditor(Terminal *terminal)
Object constructor.
int peek()
Reads a code from the keyboard without advancing to the next one.
void setBackgroundColor(Color value)
Sets background color.
bool setChar(uint8_t c)
Sets a raw character at current cursor position.
int availableForWrite()
Determines number of codes that the display input queue can still accept.
void setText(char const *text, bool moveCursor=true)
Sets initial text.
void cursorRight(int count)
Moves cursor to the right.
void setBackgroundColor(Color color, bool setAsDefault=true)
Sets the background color.
An ANSI-VT100 compatible display terminal.
void beginUpdate()
Suspends drawings.
void setTerminal(Terminal *terminal=nullptr)
Sets destination terminal.
void setScrollingRegion(int X1, int Y1, int X2, int Y2)
Defines the scrolling region.
void clear()
Fills the entire canvas with the brush color.
int getCursorCol()
Gets current cursor column.
void setGlyphOptions(GlyphOptions options)
Sets drawing options for the next glyphs.
void clear(bool moveCursor=true)
Clears the screen.
TermType
This enum defines supported terminals.
void drawLine(int X1, int Y1, int X2, int Y2)
Draws a line specifying initial and ending coordinates.
void setTerminalType(TermType value)
Sets the terminal type to emulate.
bool isVKDown(VirtualKey vk)
Checks if a virtual key is currently down.
void clear()
Clears screen.
void drawEllipse(int X, int Y, int width, int height)
Draws an ellipse specifying center and size, using current pen color.
void setPenColor(uint8_t red, uint8_t green, uint8_t blue)
Sets pen (foreground) color specifying color components.
void connectSerialPort(HardwareSerial &serialPort, bool autoXONXOFF=true)
Connects a remote host using the specified serial port.
Delegate< int > onWrite
Write character delegate.
void drawPath(Point const *points, int pointsCount)
Draws a sequence of lines.
void cursorLeft(int count)
Moves cursor to the left.
Represents the base abstract class for all display controllers.
Delegate< int * > onChar
A delegate called whenever a character has been received.
Represents the base abstract class for textual display controllers.
void setPixel(int X, int Y)
Fills a single pixel with the pen color.
void send(uint8_t c)
Like localWrite() but sends also to serial port if connected.
void disableFabGLSequences()
Disables FabGL specific sequences.
void setPenWidth(int value)
Sets pen width for lines, rectangles and paths.
void setTerminalType(TermType value)
Sets the terminal type to emulate.
void fillPath(Point const *points, int pointsCount)
Fills the polygon enclosed in a sequence of lines.
void loadFont(FontInfo const *font)
Sets the font to use.
void deactivate()
Deactivates this terminal.
void localInsert(uint8_t c)
Injects keys into the keyboard queue.
void setupAbsolutePositioner(int width, int height, bool createAbsolutePositionsQueue, BitmappedDisplayController *updateDisplayController=nullptr, uiApp *app=nullptr)
Initializes absolute position handler.
void setOrigin(int X, int Y)
Sets the axes origin.
Keyboard * keyboard()
Gets associated keyboard object.
Mouse * mouse()
Returns the instance of Mouse object automatically created by PS2Controller.
void drawRectangle(int X1, int Y1, int X2, int Y2)
Draws a rectangle using the current pen color.
Delegate< LineEditorSpecialChar > onSpecialChar
A delegate called whenever a special character has been pressed.
bool isActive()
Determines if this terminal is active or not.
void enableCursor(bool value)
Enables/disables cursor.
void localWrite(uint8_t c)
Injects keys into the keyboard queue.