30#include "freertos/FreeRTOS.h"
31#include "freertos/task.h"
32#include "freertos/timers.h"
35#if __has_include("esp32/rom/uart.h")
36 #include "esp32/rom/uart.h"
40#include "soc/uart_reg.h"
41#include "soc/uart_struct.h"
42#include "soc/io_mux_reg.h"
43#include "soc/gpio_sig_map.h"
44#include "soc/dport_reg.h"
46#include "esp_intr_alloc.h"
55#pragma GCC optimize ("O2")
66const char TERMID[] =
"?64;1;6;22c";
69const char CSI_7BIT[] =
"\e[";
70const char CSI_8BIT[] =
"\x9B";
71const char DCS_7BIT[] =
"\eP";
72const char DCS_8BIT[] =
"\x90";
73const char SS2_7BIT[] =
"\eN";
74const char SS2_8BIT[] =
"\x8E";
75const char SS3_7BIT[] =
"\eO";
76const char SS3_8BIT[] =
"\x8F";
77const char ST_7BIT[] =
"\e\\";
78const char ST_8BIT[] =
"\x9C";
79const char OSC_7BIT[] =
"\e]";
80const char OSC_8BIT[] =
"\x9D";
84#define ISCTRLCHAR(c) ((c) <= ASCII_US || (c) == ASCII_DEL)
88static 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,
89 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49,
90 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 65, 66, 67, 68, 69, 70, 71, 72, 73,
91 74, 75, 76, 77, 78, 79, 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94,
94 63, 63, 217, 191, 218,
95 192, 197, 63, 63, 196,
96 63, 63, 195, 180, 193,
97 194, 179, 243, 242, 227, 63,
101const char * CTRLCHAR_TO_STR[] = {
"NUL",
"SOH",
"STX",
"ETX",
"EOT",
"ENQ",
"ACK",
"BELL",
"BS",
"HT",
"LF",
"VT",
"FF",
"CR",
"SO",
"SI",
"DLE",
"XON",
"DC2",
102 "XOFF",
"DC4",
"NAK",
"SYN",
"ETB",
"CAN",
"EM",
"SUB",
"ESC",
"FS",
"GS",
"RS",
"US",
"SPC"};
107#define FABGLEXT_STARTCODE '_'
108#define FABGLEXT_CMD "\e_"
109#define FABGLEXT_ENDCODE '$'
110#define FABGLEXT_REPLYCODE '$'
114#define FABGLEXT_USERSEQ '#'
116#define FABGLEXTB_GETCURSORPOS 'a'
117#define FABGLEXTB_GETCURSORCOL 'b'
118#define FABGLEXTB_GETCURSORROW 'c'
119#define FABGLEXTB_SETCURSORPOS 'd'
120#define FABGLEXTB_INSERTSPACE 'e'
121#define FABGLEXTB_DELETECHAR 'f'
122#define FABGLEXTB_CURSORLEFT 'g'
123#define FABGLEXTB_CURSORRIGHT 'h'
124#define FABGLEXTB_SETCHAR 'i'
125#define FABGLEXTB_DISABLEFABSEQ 'j'
126#define FABGLEXTB_SETTERMTYPE 'k'
127#define FABGLEXTB_ISVKDOWN 'K'
128#define FABGLEXTB_SETFGCOLOR 'l'
129#define FABGLEXTB_SETBGCOLOR 'm'
130#define FABGLEXTB_SETCHARSTYLE 'n'
132#define FABGLEXTX_SETUPADC 'A'
133#define FABGLEXTX_CLEAR 'B'
134#define FABGLEXTX_READADC 'C'
135#define FABGLEXTX_SETUPGPIO 'D'
136#define FABGLEXTX_ENABLECURSOR 'E'
137#define FABGLEXTX_SETCURSORPOS 'F'
138#define FABGLEXTX_GRAPHICSCMD 'G'
139#define FABGLEXTX_SHOWMOUSE 'H'
140#define FABGLEXTX_GETMOUSEPOS 'M'
141#define FABGLEXTX_GETGPIO 'R'
142#define FABGLEXTX_SOUND 'S'
143#define FABGLEXTX_SETGPIO 'W'
144#define FABGLEXTX_DELAY 'Y'
147#define FABGLEXT_MAXSUBCMDLEN 16
150#define FABGLEXT_GCLEAR "CLEAR"
151#define FABGLEXT_GSETBRUSHCOLOR "BRUSH"
152#define FABGLEXT_GSETPENCOLOR "PEN"
153#define FABGLEXT_GSETPIXEL "PIXEL"
154#define FABGLEXT_GSCROLL "SCROLL"
155#define FABGLEXT_GPENWIDTH "PENW"
156#define FABGLEXT_GLINE "LINE"
157#define FABGLEXT_GRECT "RECT"
158#define FABGLEXT_GFILLRECT "FILLRECT"
159#define FABGLEXT_GELLIPSE "ELLIPSE"
160#define FABGLEXT_GFILLELLIPSE "FILLELLIPSE"
161#define FABGLEXT_GPATH "PATH"
162#define FABGLEXT_GFILLPATH "FILLPATH"
163#define FABGLEXT_GSPRITECOUNT "SPRITECOUNT"
164#define FABGLEXT_GSPRITEDEF "SPRITEDEF"
165#define FABGLEXT_GSPRITESET "SPRITESET"
169#define COLORED_ATTRIBUTE_BOLD 0
170#define COLORED_ATTRIBUTE_FAINT 1
171#define COLORED_ATTRIBUTE_ITALIC 2
172#define COLORED_ATTRIBUTE_UNDERLINE 3
173#define COLORED_ATTRIBUTE_INVALID 4
178Terminal * Terminal::s_activeTerminal =
nullptr;
192 m_uartRXEnabled(true),
193 m_soundGenerator(nullptr),
197 if (s_activeTerminal ==
nullptr)
198 s_activeTerminal =
this;
208 if (m_soundGenerator)
209 delete m_soundGenerator;
217 xSemaphoreTake(m_mutex, portMAX_DELAY);
218 if (s_activeTerminal !=
this) {
220 if (s_activeTerminal && transition != TerminalTransition::None) {
221 if (m_bitmappedDisplayController) {
223 s_activeTerminal =
nullptr;
224 switch (transition) {
225 case TerminalTransition::LeftToRight:
226 for (
int x = 0; x < m_columns; ++x) {
227 m_canvas->scroll(m_font.width, 0);
228 m_canvas->setOrigin(-m_font.width * (m_columns - x - 1), 0);
229 for (
int y = 0; y < m_rows; ++y)
230 m_canvas->renderGlyphsBuffer(m_columns - x - 1, y, &m_glyphsBuffer);
231 m_canvas->waitCompletion(
false);
232 vTaskDelay(2 / portTICK_PERIOD_MS);
235 case TerminalTransition::RightToLeft:
236 for (
int x = 0; x < m_columns; ++x) {
237 m_canvas->scroll(-m_font.width, 0);
238 m_canvas->setOrigin(m_font.width * (m_columns - x - 1), 0);
239 for (
int y = 0; y < m_rows; ++y)
240 m_canvas->renderGlyphsBuffer(x, y, &m_glyphsBuffer);
241 m_canvas->waitCompletion(
false);
242 vTaskDelay(2 / portTICK_PERIOD_MS);
251 auto map = (uint32_t*) heap_caps_malloc(
sizeof(uint32_t) * m_columns * m_rows, MALLOC_CAP_8BIT | MALLOC_CAP_INTERNAL);
252 memcpy(map, s_activeTerminal->m_glyphsBuffer.map,
sizeof(uint32_t) * m_columns * m_rows);
253 txtCtrl->enableCursor(
false);
254 txtCtrl->setTextMap(map, m_rows);
255 switch (transition) {
256 case TerminalTransition::LeftToRight:
257 for (
int x = 0; x < m_columns; ++x) {
258 for (
int y = 0; y < m_rows; ++y) {
259 memmove(map + y * m_columns + 1, map + y * m_columns,
sizeof(uint32_t) * (m_columns - 1));
260 map[y * m_columns] = m_glyphsBuffer.map[y * m_columns + m_columns - x - 1];
265 case TerminalTransition::RightToLeft:
266 for (
int x = 0; x < m_columns; ++x) {
267 for (
int y = 0; y < m_rows; ++y) {
268 memmove(map + y * m_columns, map + y * m_columns + 1,
sizeof(uint32_t) * (m_columns - 1));
269 map[y * m_columns + m_columns - 1] = m_glyphsBuffer.map[y * m_columns + x];
277 txtCtrl->setTextMap(m_glyphsBuffer.map, m_rows);
282 s_activeTerminal =
this;
283 vTaskResume(m_keyboardReaderTaskHandle);
284 syncDisplayController();
286 xSemaphoreGive(m_mutex);
290void Terminal::deactivate()
292 xSemaphoreTake(m_mutex, portMAX_DELAY);
293 if (s_activeTerminal ==
this) {
294 s_activeTerminal =
nullptr;
296 xSemaphoreGive(m_mutex);
301void Terminal::syncDisplayController()
303 if (m_bitmappedDisplayController) {
306 m_canvas->setGlyphOptions(m_glyphOptions);
307 m_canvas->setBrushColor(m_emuState.backgroundColor);
308 m_canvas->setPenColor(m_emuState.foregroundColor);
311 auto txtCtrl =
static_cast<TextualDisplayController*
>(m_displayController);
312 txtCtrl->setTextMap(m_glyphsBuffer.map, m_rows);
313 txtCtrl->setCursorBackground(m_emuState.backgroundColor);
314 txtCtrl->setCursorForeground(m_emuState.foregroundColor);
315 txtCtrl->setCursorPos(m_emuState.cursorY - 1, m_emuState.cursorX - 1);
316 txtCtrl->enableCursor(m_emuState.cursorEnabled);
318 updateCanvasScrollingRegion();
325 m_displayController = displayController;
326 m_bitmappedDisplayController = (m_displayController->controllerType() == DisplayControllerType::Bitmapped);
328 m_maxColumns = maxColumns;
331 if (m_bitmappedDisplayController) {
338 m_keyboard = keyboard;
339 if (m_keyboard ==
nullptr && PS2Controller::initialized()) {
341 m_keyboard = PS2Controller::keyboard();
344 m_logStream =
nullptr;
346 m_glyphsBuffer = (GlyphsBuffer){0, 0,
nullptr, 0, 0,
nullptr};
348 m_emuState.tabStop =
nullptr;
349 m_font.data =
nullptr;
351 m_savedCursorStateList =
nullptr;
353 m_alternateScreenBuffer =
false;
354 m_alternateMap =
nullptr;
356 m_flowControl = FlowControl::None;
360 m_lastWrittenChar = 0;
362 m_writeDetectedFabGLSeq =
false;
365 m_emuState.conformanceLevel = 4;
366 m_emuState.ctrlBits = 7;
369 m_cursorState =
false;
370 m_emuState.cursorEnabled =
false;
373 m_coloredAttributesMaintainStyle =
true;
374 m_coloredAttributesMask = 0;
376 m_mutex = xSemaphoreCreateMutex();
378 set132ColumnMode(
false);
382 xTimerStart(m_blinkTimer, portMAX_DELAY);
385 m_inputQueue = xQueueCreate(Terminal::inputQueueSize,
sizeof(uint8_t));
392 m_serialPort =
nullptr;
395 m_keyboardReaderTaskHandle =
nullptr;
398 m_outputQueue =
nullptr;
400 m_termInfo =
nullptr;
402 bool success = (m_glyphsBuffer.map !=
nullptr);
413 if (m_keyboardReaderTaskHandle)
414 vTaskDelete(m_keyboardReaderTaskHandle);
416 xTimerDelete(m_blinkTimer, portMAX_DELAY);
418 clearSavedCursorStates();
420 vTaskDelete(m_charsConsumerTaskHandle);
421 vQueueDelete(m_inputQueue);
424 vQueueDelete(m_outputQueue);
430 vSemaphoreDelete(m_mutex);
436 s_activeTerminal =
nullptr;
441void Terminal::connectSerialPort(HardwareSerial & serialPort,
bool autoXONXOFF)
444 vTaskDelete(m_keyboardReaderTaskHandle);
445 m_serialPort = &serialPort;
446 m_flowControl = autoXONXOFF ? FlowControl::Software : FlowControl::None;
448 m_serialPort->setRxBufferSize(Terminal::inputQueueSize);
450 if (!m_keyboardReaderTaskHandle && m_keyboard->isKeyboardAvailable())
461inline int uartGetRXFIFOCount()
463 auto uart = (
volatile uart_dev_t *)(DR_REG_UART2_BASE);
464 return uart->status.rxfifo_cnt | ((int)(uart->mem_cnt_status.rx_cnt) << 8);
469static void uartFlushTXFIFO()
471 auto uart = (
volatile uart_dev_t *)(DR_REG_UART2_BASE);
472 while (uart->status.txfifo_cnt || uart->status.st_utx_out)
478static void uartFlushRXFIFO()
480 auto uart = (
volatile uart_dev_t *)(DR_REG_UART2_BASE);
481 while (uartGetRXFIFOCount() != 0 || uart->mem_rx_status.wr_addr != uart->mem_rx_status.rd_addr)
487void Terminal::uartCheckInputQueueForFlowControl()
489 if (m_flowControl != FlowControl::None) {
490 auto uart = (
volatile uart_dev_t *)(DR_REG_UART2_BASE);
491 if (uxQueueMessagesWaiting(m_inputQueue) == 0 && uart->int_ena.rxfifo_full == 0) {
495 SET_PERI_REG_MASK(UART_INT_ENA_REG(2), UART_RXFIFO_FULL_INT_ENA_M);
501void Terminal::setRTSStatus(
bool value)
503 if (m_rtsPin != GPIO_UNUSED) {
505 gpio_set_level(m_rtsPin, !value);
511void Terminal::flowControl(
bool enableRX)
514 if (m_flowControl == FlowControl::Software || m_flowControl == FlowControl::Hardsoft)
515 SET_PERI_REG_MASK(UART_FLOW_CONF_REG(2), UART_SEND_XON_M);
516 if (m_flowControl == FlowControl::Hardware || m_flowControl == FlowControl::Hardsoft)
520 if (m_flowControl == FlowControl::Software || m_flowControl == FlowControl::Hardsoft)
521 SET_PERI_REG_MASK(UART_FLOW_CONF_REG(2), UART_SEND_XOFF_M);
522 if (m_flowControl == FlowControl::Hardware || m_flowControl == FlowControl::Hardsoft)
530bool Terminal::flowControl()
533 if ((m_flowControl == FlowControl::Software || m_flowControl == FlowControl::Hardsoft) && m_recvXOFF)
535 if ((m_flowControl == FlowControl::Hardware || m_flowControl == FlowControl::Hardsoft) && CTSStatus() ==
false)
542void Terminal::connectSerialPort(uint32_t baud,
int dataLength,
char parity,
float stopBits,
int rxPin,
int txPin,
FlowControl flowControl,
bool inverted,
int rtsPin,
int ctsPin)
544 auto uart = (
volatile uart_dev_t *) DR_REG_UART2_BASE;
546 bool initialSetup = !m_uart;
557 DPORT_SET_PERI_REG_MASK(DPORT_PERIP_CLK_EN_REG, DPORT_UART2_CLK_EN);
558 DPORT_CLEAR_PERI_REG_MASK(DPORT_PERIP_RST_EN_REG, DPORT_UART2_RST);
565 configureGPIO(int2gpio(rxPin), GPIO_MODE_INPUT);
566 configureGPIO(int2gpio(txPin), GPIO_MODE_OUTPUT);
569 m_rtsPin = int2gpio(rtsPin);
570 if (m_rtsPin != GPIO_UNUSED) {
571 configureGPIO(m_rtsPin, GPIO_MODE_OUTPUT);
576 m_ctsPin = int2gpio(ctsPin);
577 if (m_ctsPin != GPIO_UNUSED) {
578 configureGPIO(m_ctsPin, GPIO_MODE_INPUT);
582 WRITE_PERI_REG(UART_CONF1_REG(2), (1 << UART_RXFIFO_FULL_THRHD_S) |
583 (2 << UART_RX_TOUT_THRHD_S) |
584 (0 << UART_RX_TOUT_EN_S));
585 WRITE_PERI_REG(UART_INT_ENA_REG(2), (1 << UART_RXFIFO_FULL_INT_ENA_S) |
586 (1 << UART_FRM_ERR_INT_ENA_S) |
587 (0 << UART_RXFIFO_TOUT_INT_ENA_S) |
588 (1 << UART_PARITY_ERR_INT_ENA_S) |
589 (1 << UART_RXFIFO_OVF_INT_ENA_S));
590 WRITE_PERI_REG(UART_INT_CLR_REG(2), 0xffffffff);
591 esp_intr_alloc_pinnedToCore(ETS_UART2_INTR_SOURCE, 0, uart_isr,
this,
nullptr, CoreUsage::quietCore());
594 WRITE_PERI_REG(UART_MEM_CONF_REG(2), (3 << UART_RX_SIZE_S) |
595 (1 << UART_TX_SIZE_S));
597 if (!m_keyboardReaderTaskHandle && m_keyboard->isKeyboardAvailable())
601 m_flowControl = flowControl;
604 uint32_t clk_div = (getApbFrequency() << 4) / baud;
605 WRITE_PERI_REG(UART_CLKDIV_REG(2), ((clk_div >> 4) << UART_CLKDIV_S) |
606 ((clk_div & 0xf) << UART_CLKDIV_FRAG_S));
609 uint32_t config0 = (1 << UART_TICK_REF_ALWAYS_ON_S) | ((dataLength - 5) << UART_BIT_NUM_S);
611 config0 |= (1 << UART_PARITY_EN_S);
612 else if (parity ==
'O')
613 config0 |= (1 << UART_PARITY_EN_S) | (1 << UART_PARITY_S);
615 config0 |= 1 << UART_STOP_BIT_NUM_S;
616 else if (stopBits == 1.5)
617 config0 |= 2 << UART_STOP_BIT_NUM_S;
618 else if (stopBits >= 2.0) {
619 config0 |= 1 << UART_STOP_BIT_NUM_S;
620 SET_PERI_REG_BITS(UART_RS485_CONF_REG(2), UART_DL1_EN_V, 1, UART_DL1_EN_S);
622 SET_PERI_REG_BITS(UART_RS485_CONF_REG(2), UART_DL0_EN_V, 1, UART_DL1_EN_S);
624 WRITE_PERI_REG(UART_CONF0_REG(2), config0);
627 gpio_matrix_in(rxPin, U2RXD_IN_IDX, inverted);
628 gpio_matrix_out(txPin, U2TXD_OUT_IDX, inverted,
false);
631 WRITE_PERI_REG(UART_FLOW_CONF_REG(2), 0);
632 if (flowControl != FlowControl::None) {
635 WRITE_PERI_REG(UART_SWFC_CONF_REG(2), (0 << UART_XON_THRESHOLD_S) |
636 (0 << UART_XOFF_THRESHOLD_S) |
637 (ASCII_XON << UART_XON_CHAR_S) |
638 (ASCII_XOFF << UART_XOFF_CHAR_S));
642 SET_PERI_REG_MASK(UART_FLOW_CONF_REG(2), UART_SEND_XON_M);
651void Terminal::connectLocally()
654 if (!m_keyboardReaderTaskHandle && m_keyboard->isKeyboardAvailable())
659void Terminal::disconnectLocally()
662 vQueueDelete(m_outputQueue);
663 m_outputQueue =
nullptr;
667void Terminal::logFmt(
const char * format, ...)
671 va_start(ap, format);
672 int size = vsnprintf(
nullptr, 0, format, ap) + 1;
675 va_start(ap, format);
677 vsnprintf(buf, size, format, ap);
678 m_logStream->write(buf);
685void Terminal::log(
const char * txt)
688 m_logStream->write(txt);
692void Terminal::log(
char c)
695 m_logStream->write(c);
699void Terminal::freeFont()
701 #if FABGLIB_CACHE_FONT_IN_RAM
703 free((
void*) m_font.data);
704 m_font.data =
nullptr;
710void Terminal::freeTabStops()
712 if (m_emuState.tabStop) {
713 free(m_emuState.tabStop);
714 m_emuState.tabStop =
nullptr;
719void Terminal::freeGlyphsMap()
721 if (m_glyphsBuffer.map) {
722 free((
void*) m_glyphsBuffer.map);
723 m_glyphsBuffer.map =
nullptr;
725 if (m_alternateMap) {
726 free((
void*) m_alternateMap);
727 m_alternateMap =
nullptr;
732void Terminal::reset()
734 #if FABGLIB_TERMINAL_DEBUG_REPORT_DESCS
738 xSemaphoreTake(m_mutex, portMAX_DELAY);
739 m_resetRequested =
false;
741 m_emuState.originMode =
false;
742 m_emuState.wraparound =
true;
743 m_emuState.insertMode =
false;
744 m_emuState.newLineMode =
false;
745 m_emuState.smoothScroll =
false;
746 m_emuState.keypadMode = KeypadMode::Numeric;
747 m_emuState.cursorKeysMode =
false;
748 m_emuState.keyAutorepeat =
true;
749 m_emuState.cursorBlinkingEnabled =
true;
750 m_emuState.cursorStyle = 0;
751 m_emuState.allow132ColumnMode =
false;
752 m_emuState.reverseWraparoundMode =
false;
753 m_emuState.backarrowKeyMode =
false;
754 m_emuState.ANSIMode =
true;
755 m_emuState.VT52GraphicsMode =
false;
756 m_emuState.allowFabGLSequences = 1;
757 m_emuState.characterSetIndex = 0;
758 for (
int i = 0; i < 4; ++i)
759 m_emuState.characterSet[i] = 1;
763 m_blinkingTextVisible =
false;
764 m_blinkingTextEnabled =
true;
766 m_cursorState =
false;
768 m_convMatchedCount = 0;
769 m_convMatchedItem =
nullptr;
772 setScrollingRegion(1, m_rows);
776 m_glyphOptions = (GlyphOptions) {{
779 .reduceLuminosity = 0,
789 m_canvas->setGlyphOptions(m_glyphOptions);
791 m_paintOptions = PaintOptions();
795 int_setBackgroundColor(m_defaultBackgroundColor);
796 int_setForegroundColor(m_defaultForegroundColor);
798 clearSavedCursorStates();
802 xSemaphoreGive(m_mutex);
806void Terminal::loadFont(FontInfo
const * font)
808 #if FABGLIB_TERMINAL_DEBUG_REPORT_DESCS
814 if (m_bitmappedDisplayController) {
821 #if FABGLIB_CACHE_FONT_IN_RAM
822 int size = m_font.height * 256 * ((m_font.width + 7) / 8);
823 m_font.data = (uint8_t
const*) malloc(size);
824 memcpy((
void*)m_font.data, font->data, size);
826 m_font.data = font->data;
829 m_columns = m_canvas->getWidth() / m_font.width;
830 m_rows = m_canvas->getHeight() / m_font.height;
832 m_glyphsBuffer.glyphsWidth = m_font.width;
833 m_glyphsBuffer.glyphsHeight = m_font.height;
834 m_glyphsBuffer.glyphsData = m_font.data;
836 codepage = m_font.codepage;
844 m_columns = txtctrl->getColumns();
845 m_rows = txtctrl->getRows();
846 codepage = txtctrl->getFont()->codepage;
851 m_keyboard->setCodePage(CodePages::get(codepage));
854 if (m_maxColumns > 0 && m_maxColumns < m_columns)
855 m_columns = m_maxColumns;
856 if (m_maxRows > 0 && m_maxRows < m_rows)
860 m_emuState.tabStop = (uint8_t*) malloc(m_columns);
865 m_glyphsBuffer.map = (uint32_t*) heap_caps_malloc(
sizeof(uint32_t) * m_columns * m_rows, MALLOC_CAP_8BIT | MALLOC_CAP_INTERNAL);
866 if (m_glyphsBuffer.map)
871 m_glyphsBuffer.columns = m_columns;
872 m_glyphsBuffer.rows = m_rows;
874 m_alternateMap =
nullptr;
875 m_alternateScreenBuffer =
false;
877 if (!m_bitmappedDisplayController && isActive()) {
882 setScrollingRegion(1, m_rows);
887void Terminal::flush(
bool waitVSync)
889 #if FABGLIB_TERMINAL_DEBUG_REPORT_DESCS
892 if (m_bitmappedDisplayController && isActive()) {
893 while (uxQueueMessagesWaiting(m_inputQueue) > 0)
895 m_canvas->waitCompletion(waitVSync);
902void Terminal::set132ColumnMode(
bool value)
904 #if FABGLIB_TERMINAL_DEBUG_REPORT_DESCS
905 log(
"set132ColumnMode()\n");
908 if (m_bitmappedDisplayController) {
914 loadFont(getPresetFontInfo(m_canvas->getWidth(), m_canvas->getHeight(), (value ? 132 : 80), 25));
920void Terminal::setBackgroundColor(
Color color,
bool setAsDefault)
923 m_defaultBackgroundColor = color;
928void Terminal::int_setBackgroundColor(
Color color)
930 m_emuState.backgroundColor = color;
932 if (m_bitmappedDisplayController)
933 m_canvas->setBrushColor(color);
940void Terminal::setForegroundColor(
Color color,
bool setAsDefault)
943 m_defaultForegroundColor = color;
948void Terminal::int_setForegroundColor(
Color color)
950 m_emuState.foregroundColor = color;
952 if (m_bitmappedDisplayController)
953 m_canvas->setPenColor(color);
960int CharStyleToColorAttributeIndex(
CharStyle attribute)
964 return COLORED_ATTRIBUTE_BOLD;
966 return COLORED_ATTRIBUTE_FAINT;
968 return COLORED_ATTRIBUTE_ITALIC;
970 return COLORED_ATTRIBUTE_UNDERLINE;
972 return COLORED_ATTRIBUTE_INVALID;
977void Terminal::setColorForAttribute(
CharStyle attribute,
Color color,
bool maintainStyle)
979 int cindex = CharStyleToColorAttributeIndex(attribute);
980 if (cindex != COLORED_ATTRIBUTE_INVALID) {
981 m_coloredAttributesMask |= 1 << cindex;
982 m_coloredAttributesColor[cindex] = color;
983 m_coloredAttributesMaintainStyle = maintainStyle;
990 int cindex = CharStyleToColorAttributeIndex(attribute);
991 if (cindex != COLORED_ATTRIBUTE_INVALID)
992 m_coloredAttributesMask &= ~(1 << cindex);
996void Terminal::reverseVideo(
bool value)
998 if (m_paintOptions.swapFGBG != value) {
999 m_paintOptions.swapFGBG = value;
1001 if (m_bitmappedDisplayController) {
1002 m_canvas->setPaintOptions(m_paintOptions);
1003 m_canvas->swapRectangle(0, 0, m_canvas->getWidth() - 1, m_canvas->getHeight() - 1);
1010void Terminal::clear(
bool moveCursor)
1018void Terminal::int_clear()
1020 #if FABGLIB_TERMINAL_DEBUG_REPORT_DESCS
1021 log(
"int_clear()\n");
1024 if (m_bitmappedDisplayController && isActive())
1026 clearMap(m_glyphsBuffer.map);
1030void Terminal::clearMap(uint32_t * map)
1032 uint32_t itemValue = GLYPHMAP_ITEM_MAKE(ASCII_SPC, m_emuState.backgroundColor, m_emuState.foregroundColor, m_glyphOptions);
1033 uint32_t * mapItemPtr = map;
1034 for (
int row = 0; row < m_rows; ++row)
1035 for (
int col = 0; col < m_columns; ++col, ++mapItemPtr)
1036 *mapItemPtr = itemValue;
1041bool Terminal::moveUp()
1043 #if FABGLIB_TERMINAL_DEBUG_REPORT_DESCS
1047 if (m_emuState.cursorY == m_emuState.scrollingRegionTop)
1049 setCursorPos(m_emuState.cursorX, m_emuState.cursorY - 1);
1055bool Terminal::moveDown()
1057 #if FABGLIB_TERMINAL_DEBUG_REPORT_DESCS
1058 log(
"moveDown()\n");
1061 if (m_emuState.cursorY == m_emuState.scrollingRegionDown || m_emuState.cursorY == m_rows)
1063 setCursorPos(m_emuState.cursorX, m_emuState.cursorY + 1);
1069void Terminal::move(
int offset)
1071 int pos = m_emuState.cursorX - 1 + (m_emuState.cursorY - 1) * m_columns + offset;
1072 int newY = pos / m_columns + 1;
1073 int newX = pos % m_columns + 1;
1074 if (newY < m_emuState.scrollingRegionTop) {
1076 newY = m_emuState.scrollingRegionTop;
1078 if (newY > m_emuState.scrollingRegionDown) {
1080 newY = m_emuState.scrollingRegionDown;
1082 setCursorPos(newX, newY);
1086void Terminal::setCursorPos(
int X,
int Y)
1088 m_emuState.cursorX = tclamp(
X, 1, (
int)m_columns);
1089 m_emuState.cursorY = tclamp(
Y, 1, (
int)m_rows);
1090 m_emuState.cursorPastLastCol =
false;
1092 if (!m_bitmappedDisplayController && isActive())
1093 static_cast<TextualDisplayController*
>(m_displayController)->setCursorPos(m_emuState.cursorY - 1, m_emuState.cursorX - 1);
1095 #if FABGLIB_TERMINAL_DEBUG_REPORT_DESCSALL
1096 logFmt(
"setCursorPos(%d, %d) => set to (%d, %d)\n",
X,
Y, m_emuState.cursorX, m_emuState.cursorY);
1101int Terminal::getAbsoluteRow(
int Y)
1103 #if FABGLIB_TERMINAL_DEBUG_REPORT_DESCS
1104 logFmt(
"getAbsoluteRow(%d)\n",
Y);
1107 if (m_emuState.originMode) {
1108 Y += m_emuState.scrollingRegionTop - 1;
1109 Y = tclamp(
Y, m_emuState.scrollingRegionTop, m_emuState.scrollingRegionDown);
1115void Terminal::enableCursor(
bool value)
1121bool Terminal::int_enableCursor(
bool value)
1123 bool prev = m_emuState.cursorEnabled;
1124 if (m_emuState.cursorEnabled != value) {
1125 m_emuState.cursorEnabled = value;
1126 if (m_bitmappedDisplayController) {
1128 if (m_emuState.cursorEnabled) {
1129 if (uxQueueMessagesWaiting(m_inputQueue) == 0) {
1134 if (m_cursorState) {
1139 }
else if (isActive()) {
1141 static_cast<TextualDisplayController*
>(m_displayController)->enableCursor(value);
1148bool Terminal::enableBlinkingText(
bool value)
1150 bool prev = m_blinkingTextEnabled;
1151 m_blinkingTextEnabled = value;
1157void Terminal::blinkTimerFunc(TimerHandle_t xTimer)
1159 Terminal * term = (Terminal*) pvTimerGetTimerID(xTimer);
1161 if (term->isActive() && xSemaphoreTake(term->m_mutex, 0) == pdTRUE) {
1163 if (term->m_emuState.cursorEnabled && term->m_emuState.cursorBlinkingEnabled)
1164 term->blinkCursor();
1167 if (term->m_blinkingTextEnabled)
1170 xSemaphoreGive(term->m_mutex);
1176void Terminal::blinkCursor()
1178 if (m_bitmappedDisplayController && isActive()) {
1179 m_cursorState = !m_cursorState;
1180 int X = (m_emuState.cursorX - 1) * m_font.width;
1181 int Y = (m_emuState.cursorY - 1) * m_font.height;
1182 switch (m_emuState.cursorStyle) {
1185 m_canvas->swapRectangle(
X,
Y,
X + m_font.width - 1,
Y + m_font.height - 1);
1189 m_canvas->swapRectangle(
X,
Y + m_font.height - 2,
X + m_font.width - 1,
Y + m_font.height - 1);
1193 m_canvas->swapRectangle(
X,
Y,
X + 1,
Y + m_font.height - 1);
1200void Terminal::blinkText()
1203 m_blinkingTextVisible = !m_blinkingTextVisible;
1204 bool keepEnabled =
false;
1206 int cols = m_columns;
1207 if (m_bitmappedDisplayController)
1208 m_canvas->beginUpdate();
1209 for (
int y = 0; y < rows; ++y) {
1210 uint32_t * itemPtr = m_glyphsBuffer.map + y * cols;
1211 for (
int x = 0; x < cols; ++x, ++itemPtr) {
1213 GlyphOptions glyphOptions = glyphMapItem_getOptions(itemPtr);
1214 if (glyphOptions.userOpt1) {
1215 glyphOptions.blank = !m_blinkingTextVisible;
1216 glyphMapItem_setOptions(itemPtr, glyphOptions);
1217 refresh(x + 1, y + 1);
1221 if (m_bitmappedDisplayController)
1222 m_canvas->waitCompletion(
false);
1224 if (m_bitmappedDisplayController)
1225 m_canvas->endUpdate();
1227 m_blinkingTextEnabled =
false;
1232void Terminal::nextTabStop()
1234 int actualColumns = m_columns;
1237 if (getGlyphOptionsAt(1, m_emuState.cursorY).doubleWidth)
1240 int x = m_emuState.cursorX;
1241 while (x < actualColumns) {
1243 if (m_emuState.tabStop[x - 1])
1246 setCursorPos(x, m_emuState.cursorY);
1251void Terminal::resetTabStops()
1253 for (
int i = 0; i < m_columns; ++i)
1254 m_emuState.tabStop[i] = (i > 0 && (i % 8) == 0 ? 1 : 0);
1259void Terminal::setTabStop(
int column,
bool set)
1261 #if FABGLIB_TERMINAL_DEBUG_REPORT_DESCS
1262 logFmt(
"setTabStop %d %d\n", column, (
int)set);
1266 memset(m_emuState.tabStop, 0, m_columns);
1268 m_emuState.tabStop[column - 1] = set ? 1 : 0;
1272void Terminal::scrollDown()
1274 #if FABGLIB_TERMINAL_DEBUG_REPORT_DESCS
1275 log(
"scrollDown\n");
1278 if (m_bitmappedDisplayController && isActive()) {
1280 if (m_emuState.smoothScroll) {
1281 for (
int i = 0; i < m_font.height; ++i)
1282 m_canvas->scroll(0, 1);
1284 m_canvas->scroll(0, m_font.height);
1288 for (
int y = m_emuState.scrollingRegionDown - 1; y > m_emuState.scrollingRegionTop - 1; --y)
1289 memcpy(m_glyphsBuffer.map + y * m_columns, m_glyphsBuffer.map + (y - 1) * m_columns, m_columns *
sizeof(uint32_t));
1292 uint32_t itemValue = GLYPHMAP_ITEM_MAKE(ASCII_SPC, m_emuState.backgroundColor, m_emuState.foregroundColor, m_glyphOptions);
1293 uint32_t * itemPtr = m_glyphsBuffer.map + (m_emuState.scrollingRegionTop - 1) * m_columns;
1294 for (
int x = 0; x < m_columns; ++x, ++itemPtr)
1295 *itemPtr = itemValue;
1301void Terminal::scrollDownAt(
int startingRow)
1303 int prevScrollingRegionTop = m_emuState.scrollingRegionTop;
1304 setScrollingRegion(startingRow, m_emuState.scrollingRegionDown,
false);
1308 setScrollingRegion(prevScrollingRegionTop, m_emuState.scrollingRegionDown,
false);
1312void Terminal::scrollUp()
1314 #if FABGLIB_TERMINAL_DEBUG_REPORT_DESCS
1318 auto ldown = m_emuState.scrollingRegionDown;
1319 if (m_emuState.cursorY == m_rows) {
1321 m_emuState.scrollingRegionDown = m_rows;
1322 updateCanvasScrollingRegion();
1325 if (m_bitmappedDisplayController && isActive()) {
1327 if (m_emuState.smoothScroll) {
1328 for (
int i = 0; i < m_font.height; ++i)
1329 m_canvas->scroll(0, -1);
1331 m_canvas->scroll(0, -m_font.height);
1335 for (
int y = m_emuState.scrollingRegionTop - 1; y < m_emuState.scrollingRegionDown - 1; ++y)
1336 memcpy(m_glyphsBuffer.map + y * m_columns, m_glyphsBuffer.map + (y + 1) * m_columns, m_columns *
sizeof(uint32_t));
1339 uint32_t itemValue = GLYPHMAP_ITEM_MAKE(ASCII_SPC, m_emuState.backgroundColor, m_emuState.foregroundColor, m_glyphOptions);
1340 uint32_t * itemPtr = m_glyphsBuffer.map + (m_emuState.scrollingRegionDown - 1) * m_columns;
1341 for (
int x = 0; x < m_columns; ++x, ++itemPtr)
1342 *itemPtr = itemValue;
1344 if (ldown != m_emuState.scrollingRegionDown) {
1345 m_emuState.scrollingRegionDown = ldown;
1346 updateCanvasScrollingRegion();
1351void Terminal::scrollUpAt(
int startingRow)
1353 int prevScrollingRegionTop = m_emuState.scrollingRegionTop;
1354 setScrollingRegion(startingRow, m_emuState.scrollingRegionDown,
false);
1358 setScrollingRegion(prevScrollingRegionTop, m_emuState.scrollingRegionDown,
false);
1362void Terminal::setScrollingRegion(
int top,
int down,
bool resetCursorPos)
1364 m_emuState.scrollingRegionTop = tclamp(top, 1, (
int)m_rows);
1365 m_emuState.scrollingRegionDown = tclamp(down, 1, (
int)m_rows);
1366 updateCanvasScrollingRegion();
1369 setCursorPos(1, m_emuState.originMode ? m_emuState.scrollingRegionTop : 1);
1371 #if FABGLIB_TERMINAL_DEBUG_REPORT_DESCS
1372 logFmt(
"setScrollingRegion: %d %d => %d %d\n", top, down, m_emuState.scrollingRegionTop, m_emuState.scrollingRegionDown);
1377void Terminal::updateCanvasScrollingRegion()
1379 if (m_bitmappedDisplayController && isActive())
1380 m_canvas->setScrollingRegion(0, (m_emuState.scrollingRegionTop - 1) * m_font.height, m_canvas->getWidth() - 1, m_emuState.scrollingRegionDown * m_font.height - 1);
1387bool Terminal::multilineInsertChar(
int charsToMove)
1389 bool scrolled =
false;
1390 int col = m_emuState.cursorX;
1391 int row = m_emuState.cursorY;
1392 if (m_emuState.cursorPastLastCol) {
1396 uint32_t lastColItem = 0;
1397 while (charsToMove > 0) {
1398 uint32_t * rowPtr = m_glyphsBuffer.map + (row - 1) * m_columns;
1399 uint32_t lItem = rowPtr[m_columns - 1];
1400 insertAt(col, row, 1);
1401 if (row > m_emuState.cursorY) {
1402 rowPtr[0] = lastColItem;
1405 lastColItem = lItem;
1406 charsToMove -= m_columns - col;
1408 if (charsToMove > 0 && row == m_emuState.scrollingRegionDown) {
1411 setCursorPos(m_emuState.cursorX, m_emuState.cursorY - 1);
1415 if (m_bitmappedDisplayController && isActive())
1416 m_canvas->waitCompletion(
false);
1423void Terminal::insertAt(
int column,
int row,
int count)
1425 #if FABGLIB_TERMINAL_DEBUG_REPORT_DESCS
1426 logFmt(
"insertAt(%d, %d, %d)\n", column, row, count);
1429 count = tmin((
int)m_columns, count);
1431 if (m_bitmappedDisplayController && isActive()) {
1433 int charWidth = getCharWidthAt(row);
1434 m_canvas->setScrollingRegion((column - 1) * charWidth, (row - 1) * m_font.height, charWidth * getColumnsAt(row) - 1, row * m_font.height - 1);
1435 m_canvas->scroll(count * charWidth, 0);
1436 updateCanvasScrollingRegion();
1440 uint32_t * rowPtr = m_glyphsBuffer.map + (row - 1) * m_columns;
1441 for (
int i = m_columns - 1; i >= column + count - 1; --i)
1442 rowPtr[i] = rowPtr[i - count];
1445 GlyphOptions glyphOptions = m_glyphOptions;
1446 glyphOptions.doubleWidth = glyphMapItem_getOptions(rowPtr).
doubleWidth;
1447 uint32_t itemValue = GLYPHMAP_ITEM_MAKE(ASCII_SPC, m_emuState.backgroundColor, m_emuState.foregroundColor, glyphOptions);
1448 for (
int i = 0; i < count; ++i)
1449 rowPtr[column + i - 1] = itemValue;
1453void Terminal::multilineDeleteChar(
int charsToMove)
1455 int col = m_emuState.cursorX;
1456 int row = m_emuState.cursorY;
1457 if (m_emuState.cursorPastLastCol) {
1463 if (charsToMove == 0)
1464 deleteAt(col, row, 1);
1466 while (charsToMove > 0) {
1467 deleteAt(col, row, 1);
1468 charsToMove -= m_columns - col;
1469 if (charsToMove > 0) {
1470 if (m_bitmappedDisplayController && isActive())
1471 m_canvas->waitCompletion(
false);
1472 uint32_t * lastItem = m_glyphsBuffer.map + (row - 1) * m_columns + (m_columns - 1);
1473 lastItem[0] = lastItem[1];
1474 refresh(m_columns, row);
1478 if (m_bitmappedDisplayController && isActive())
1479 m_canvas->waitCompletion(
false);
1485void Terminal::deleteAt(
int column,
int row,
int count)
1487 #if FABGLIB_TERMINAL_DEBUG_REPORT_DESCS
1488 logFmt(
"deleteAt(%d, %d, %d)\n", column, row, count);
1491 count = imin(m_columns - column + 1, count);
1493 if (m_bitmappedDisplayController && isActive()) {
1495 int charWidth = getCharWidthAt(row);
1496 m_canvas->setScrollingRegion((column - 1) * charWidth, (row - 1) * m_font.height, charWidth * getColumnsAt(row) - 1, row * m_font.height - 1);
1497 m_canvas->scroll(-count * charWidth, 0);
1498 updateCanvasScrollingRegion();
1502 uint32_t * rowPtr = m_glyphsBuffer.map + (row - 1) * m_columns;
1503 int itemsToMove = m_columns - column - count + 1;
1504 for (
int i = 0; i < itemsToMove; ++i)
1505 rowPtr[column - 1 + i] = rowPtr[column - 1 + i + count];
1508 GlyphOptions glyphOptions = m_glyphOptions;
1509 glyphOptions.doubleWidth = glyphMapItem_getOptions(rowPtr).
doubleWidth;
1510 uint32_t itemValue = GLYPHMAP_ITEM_MAKE(ASCII_SPC, m_emuState.backgroundColor, m_emuState.foregroundColor, glyphOptions);
1511 for (
int i = m_columns - count + 1 ; i <= m_columns; ++i)
1512 rowPtr[i - 1] = itemValue;
1519void Terminal::erase(
int X1,
int Y1,
int X2,
int Y2, uint8_t c,
bool maintainDoubleWidth,
bool selective)
1521 #if FABGLIB_TERMINAL_DEBUG_REPORT_DESCS
1522 logFmt(
"erase(%d, %d, %d, %d, %d, %d)\n",
X1,
Y1,
X2,
Y2, (
int)c, (
int)maintainDoubleWidth);
1525 X1 = tclamp(
X1 - 1, 0, (
int)m_columns - 1);
1526 Y1 = tclamp(
Y1 - 1, 0, (
int)m_rows - 1);
1527 X2 = tclamp(
X2 - 1, 0, (
int)m_columns - 1);
1528 Y2 = tclamp(
Y2 - 1, 0, (
int)m_rows - 1);
1530 if (m_bitmappedDisplayController && isActive()) {
1531 if (c == ASCII_SPC && !selective) {
1532 int charWidth = getCharWidthAt(m_emuState.cursorY);
1533 m_canvas->fillRectangle(
X1 * charWidth,
Y1 * m_font.height, (
X2 + 1) * charWidth - 1, (
Y2 + 1) * m_font.height - 1);
1537 GlyphOptions glyphOptions = {.value = 0};
1538 glyphOptions.fillBackground = 1;
1540 for (
int y =
Y1; y <=
Y2; ++y) {
1541 uint32_t * itemPtr = m_glyphsBuffer.map +
X1 + y * m_columns;
1542 for (
int x =
X1; x <=
X2; ++x, ++itemPtr) {
1543 if (selective && glyphMapItem_getOptions(itemPtr).
userOpt2)
1545 glyphOptions.doubleWidth = maintainDoubleWidth ? glyphMapItem_getOptions(itemPtr).
doubleWidth : 0;
1546 *itemPtr = GLYPHMAP_ITEM_MAKE(c, m_emuState.backgroundColor, m_emuState.foregroundColor, glyphOptions);
1549 if (c != ASCII_SPC || selective)
1550 refresh(
X1 + 1,
Y1 + 1,
X2 + 1,
Y2 + 1);
1554void Terminal::enableFabGLSequences(
bool value)
1556 m_emuState.allowFabGLSequences += value ? 1 : -1;
1557 if (m_emuState.allowFabGLSequences < 0)
1558 m_emuState.allowFabGLSequences = 0;
1562void Terminal::clearSavedCursorStates()
1564 #if FABGLIB_TERMINAL_DEBUG_REPORT_DESCS
1565 log(
"clearSavedCursorStates()\n");
1568 for (TerminalCursorState * curItem = m_savedCursorStateList, * next; curItem; curItem = next) {
1569 next = curItem->next;
1570 free(curItem->tabStop);
1573 m_savedCursorStateList =
nullptr;
1577void Terminal::saveCursorState()
1579 #if FABGLIB_TERMINAL_DEBUG_REPORT_DESCS
1580 log(
"saveCursorState()\n");
1583 TerminalCursorState * s = (TerminalCursorState*) malloc(
sizeof(TerminalCursorState));
1586 *s = (TerminalCursorState) {
1587 .next = m_savedCursorStateList,
1588 .cursorX = (int16_t) m_emuState.cursorX,
1589 .cursorY = (int16_t) m_emuState.cursorY,
1590 .tabStop = (uint8_t*) malloc(m_columns),
1591 .cursorPastLastCol = m_emuState.cursorPastLastCol,
1592 .originMode = m_emuState.originMode,
1593 .glyphOptions = m_glyphOptions,
1594 .characterSetIndex = m_emuState.characterSetIndex,
1595 .characterSet = {m_emuState.characterSet[0], m_emuState.characterSet[1], m_emuState.characterSet[2], m_emuState.characterSet[3]},
1598 memcpy(s->tabStop, m_emuState.tabStop, m_columns);
1599 m_savedCursorStateList = s;
1601 #if FABGLIB_TERMINAL_DEBUG_REPORT_ERRORS
1602 log(
"ERROR: Unable to alloc TerminalCursorState\n");
1608void Terminal::restoreCursorState()
1610 #if FABGLIB_TERMINAL_DEBUG_REPORT_DESCS
1611 log(
"restoreCursorState()\n");
1614 if (m_savedCursorStateList) {
1615 m_emuState.cursorX = m_savedCursorStateList->cursorX;
1616 m_emuState.cursorY = m_savedCursorStateList->cursorY;
1617 m_emuState.cursorPastLastCol = m_savedCursorStateList->cursorPastLastCol;
1618 m_emuState.originMode = m_savedCursorStateList->originMode;
1619 if (m_savedCursorStateList->tabStop)
1620 memcpy(m_emuState.tabStop, m_savedCursorStateList->tabStop, m_columns);
1621 m_glyphOptions = m_savedCursorStateList->glyphOptions;
1622 if (m_bitmappedDisplayController && isActive())
1623 m_canvas->setGlyphOptions(m_glyphOptions);
1624 m_emuState.characterSetIndex = m_savedCursorStateList->characterSetIndex;
1625 for (
int i = 0; i < 4; ++i)
1626 m_emuState.characterSet[i] = m_savedCursorStateList->characterSet[i];
1628 TerminalCursorState * next = m_savedCursorStateList->next;
1630 free(m_savedCursorStateList->tabStop);
1631 free(m_savedCursorStateList);
1632 m_savedCursorStateList = next;
1637void Terminal::useAlternateScreenBuffer(
bool value)
1639 #if FABGLIB_TERMINAL_DEBUG_REPORT_DESCS
1640 logFmt(
"useAlternateScreenBuffer: %d\n", value);
1642 if (m_alternateScreenBuffer != value) {
1643 m_alternateScreenBuffer = value;
1644 if (!m_alternateMap) {
1646 m_alternateMap = (uint32_t*) heap_caps_malloc(
sizeof(uint32_t) * m_columns * m_rows, MALLOC_CAP_8BIT | MALLOC_CAP_INTERNAL);
1647 clearMap(m_alternateMap);
1648 m_alternateCursorX = 1;
1649 m_alternateCursorY = 1;
1650 m_alternateScrollingRegionTop = 1;
1651 m_alternateScrollingRegionDown = m_rows;
1652 m_alternateCursorBlinkingEnabled =
true;
1654 tswap(m_alternateMap, m_glyphsBuffer.map);
1655 tswap(m_emuState.cursorX, m_alternateCursorX);
1656 tswap(m_emuState.cursorY, m_alternateCursorY);
1657 tswap(m_emuState.scrollingRegionTop, m_alternateScrollingRegionTop);
1658 tswap(m_emuState.scrollingRegionDown, m_alternateScrollingRegionDown);
1659 tswap(m_emuState.cursorBlinkingEnabled, m_alternateCursorBlinkingEnabled);
1660 setScrollingRegion(m_emuState.scrollingRegionTop, m_emuState.scrollingRegionDown,
false);
1661 m_emuState.cursorPastLastCol =
false;
1667void Terminal::localInsert(uint8_t c)
1670 xQueueSendToFront(m_outputQueue, &c, portMAX_DELAY);
1674void Terminal::localWrite(uint8_t c)
1677 xQueueSendToBack(m_outputQueue, &c, portMAX_DELAY);
1681void Terminal::localWrite(
char const * str)
1683 if (m_outputQueue) {
1685 xQueueSendToBack(m_outputQueue, str, portMAX_DELAY);
1687 #if FABGLIB_TERMINAL_DEBUG_REPORT_OUT_CODES
1688 logFmt(
"=> %02X %s%c\n", (
int)*str, (*str <= ASCII_SPC ? CTRLCHAR_TO_STR[(
int)(*str)] :
""), (*str > ASCII_SPC ? *str : ASCII_SPC));
1697int Terminal::available()
1699 return m_outputQueue ? uxQueueMessagesWaiting(m_outputQueue) : 0;
1709int Terminal::read(
int timeOutMS)
1711 if (m_outputQueue) {
1713 xQueueReceive(m_outputQueue, &c, msToTicks(timeOutMS));
1720bool Terminal::waitFor(
int value,
int timeOutMS)
1723 while (!timeout.expired(timeOutMS)) {
1724 int c = read(timeOutMS);
1739void Terminal::flush()
1746void Terminal::pollSerialPort()
1749 int avail = m_serialPort->available();
1751 if (m_flowControl == FlowControl::Software) {
1754 if (avail < FABGLIB_TERMINAL_XON_THRESHOLD) {
1760 if (avail >= FABGLIB_TERMINAL_XOFF_THRESHOLD) {
1770 auto r = m_serialPort->read();
1771 if (m_uartRXEnabled)
1778void IRAM_ATTR Terminal::uart_isr(
void *arg)
1781 auto uart = (
volatile uart_dev_t *)(DR_REG_UART2_BASE);
1784 if (uart->int_st.rxfifo_ovf || uart->int_st.frm_err || uart->int_st.parity_err) {
1788 SET_PERI_REG_MASK(UART_INT_CLR_REG(2), UART_RXFIFO_OVF_INT_CLR_M | UART_FRM_ERR_INT_CLR_M | UART_PARITY_ERR_INT_CLR_M);
1793 if (term->m_flowControl != FlowControl::None) {
1795 int count = uartGetRXFIFOCount();
1796 if (count > FABGLIB_TERMINAL_FLOWCONTROL_RXFIFO_MAX_THRESHOLD && !term->m_sentXOFF)
1798 else if (count < FABGLIB_TERMINAL_FLOWCONTROL_RXFIFO_MIN_THRESHOLD && term->m_sentXOFF)
1803 while (uartGetRXFIFOCount() != 0 || uart->mem_rx_status.wr_addr != uart->mem_rx_status.rd_addr) {
1805 if (term->m_flowControl != FlowControl::None && xQueueIsQueueFullFromISR(term->m_inputQueue)) {
1806 if (!term->m_sentXOFF)
1809 CLEAR_PERI_REG_MASK(UART_INT_ENA_REG(2), UART_RXFIFO_FULL_INT_ENA_M);
1813 uint8_t r = READ_PERI_REG(UART_FIFO_REG(2));
1814 if (term->m_uartRXEnabled)
1815 term->
write(r,
true);
1819 SET_PERI_REG_MASK(UART_INT_CLR_REG(2), UART_RXFIFO_FULL_INT_CLR_M);
1824void Terminal::send(uint8_t c)
1826 #if FABGLIB_TERMINAL_DEBUG_REPORT_OUT_CODES
1827 logFmt(
"=> %02X %s%c\n", (
int)c, (c <= ASCII_SPC ? CTRLCHAR_TO_STR[(
int)c] :
""), (c > ASCII_SPC ? c : ASCII_SPC));
1832 while (m_serialPort->availableForWrite() == 0)
1833 vTaskDelay(1 / portTICK_PERIOD_MS);
1834 m_serialPort->write(c);
1841 auto uart = (
volatile uart_dev_t *)(DR_REG_UART2_BASE);
1842 while (uart->status.txfifo_cnt == 0x7F)
1844 WRITE_PERI_REG(UART_FIFO_AHB_REG(2), c);
1852void Terminal::send(
char const * str)
1857 while (m_serialPort->availableForWrite() == 0)
1858 vTaskDelay(1 / portTICK_PERIOD_MS);
1859 m_serialPort->write(*str);
1861 #if FABGLIB_TERMINAL_DEBUG_REPORT_OUT_CODES
1862 logFmt(
"=> %02X %s%c\n", (
int)*str, (*str <= ASCII_SPC ? CTRLCHAR_TO_STR[(
int)(*str)] :
""), (*str > ASCII_SPC ? *str : ASCII_SPC));
1871 auto uart = (
volatile uart_dev_t *)(DR_REG_UART2_BASE);
1875 while (uart->status.txfifo_cnt == 0x7F)
1877 WRITE_PERI_REG(UART_FIFO_AHB_REG(2), (*str++));
1885void Terminal::sendCSI()
1887 send(m_emuState.ctrlBits == 7 ? CSI_7BIT : CSI_8BIT);
1891void Terminal::sendDCS()
1893 send(m_emuState.ctrlBits == 7 ? DCS_7BIT : DCS_8BIT);
1897void Terminal::sendSS3()
1899 send(m_emuState.ctrlBits == 7 ? SS3_7BIT : SS3_8BIT);
1903int Terminal::availableForWrite()
1905 return uxQueueSpacesAvailable(m_inputQueue);
1909bool Terminal::addToInputQueue(uint8_t c,
bool fromISR)
1912 return xQueueSendToBackFromISR(m_inputQueue, &c,
nullptr);
1914 return xQueueSendToBack(m_inputQueue, &c, portMAX_DELAY);
1918bool Terminal::insertToInputQueue(uint8_t c,
bool fromISR)
1921 return xQueueSendToFrontFromISR(m_inputQueue, &c,
nullptr);
1923 return xQueueSendToFront(m_inputQueue, &c, portMAX_DELAY);
1927void Terminal::write(uint8_t c,
bool fromISR)
1929 if (m_termInfo ==
nullptr || m_writeDetectedFabGLSeq)
1930 addToInputQueue(c, fromISR);
1932 convHandleTranslation(c, fromISR);
1935 if (m_writeDetectedFabGLSeq) {
1936 if (c == FABGLEXT_ENDCODE)
1937 m_writeDetectedFabGLSeq =
false;
1938 }
else if (m_emuState.allowFabGLSequences && m_lastWrittenChar == ASCII_ESC && c == FABGLEXT_STARTCODE) {
1939 m_writeDetectedFabGLSeq =
true;
1942 m_lastWrittenChar = c;
1944 #if FABGLIB_TERMINAL_DEBUG_REPORT_IN_CODES
1946 logFmt(
"<= %02X %s%c\n", (
int)c, (c <= ASCII_SPC ? CTRLCHAR_TO_STR[(
int)c] :
""), (c > ASCII_SPC ? c : ASCII_SPC));
1951size_t Terminal::write(uint8_t c)
1958size_t Terminal::write(
const uint8_t * buffer,
size_t size)
1960 for (
int i = 0; i < size; ++i)
1973void Terminal::int_setTerminalType(TermInfo
const * value)
1976 m_emuState.ANSIMode =
true;
1977 m_emuState.conformanceLevel = 4;
1979 m_termInfo =
nullptr;
1981 if (value !=
nullptr) {
1983 auto s = value->initString;
1984 for (
int i = strlen(s) - 1; i >= 0; --i)
1985 insertToInputQueue(s[i],
false);
1992void Terminal::int_setTerminalType(
TermType value)
1996 int_setTerminalType(
nullptr);
1999 int_setTerminalType(&term_ADM3A);
2002 int_setTerminalType(&term_ADM31);
2005 int_setTerminalType(&term_Hazeltine1500);
2008 int_setTerminalType(&term_Osborne);
2011 int_setTerminalType(&term_Kaypro);
2014 int_setTerminalType(&term_VT52);
2017 int_setTerminalType(&term_ANSILegacy);
2023void Terminal::convHandleTranslation(uint8_t c,
bool fromISR)
2025 if (m_convMatchedCount > 0 || c < 32 || c == 0x7f || c ==
'~') {
2027 m_convMatchedChars[m_convMatchedCount] = c;
2029 if (m_convMatchedItem ==
nullptr)
2030 m_convMatchedItem = m_termInfo->videoCtrlSet;
2032 for (
auto item = m_convMatchedItem; item->termSeq; ++item) {
2033 if (item != m_convMatchedItem) {
2035 if (m_convMatchedCount == 0 || (item->termSeqLen > m_convMatchedCount && strncmp(item->termSeq, m_convMatchedItem->termSeq, m_convMatchedCount) == 0))
2036 m_convMatchedItem = item;
2041 if (item->termSeq[m_convMatchedCount] == 0xFF || item->termSeq[m_convMatchedCount] == c) {
2043 ++m_convMatchedCount;
2044 if (item->termSeqLen == m_convMatchedCount) {
2046 for (ConvCtrl
const * ctrl = item->convCtrl; *ctrl != ConvCtrl::END; ++ctrl)
2047 convSendCtrl(*ctrl, fromISR);
2054 convQueue(
nullptr, fromISR);
2056 addToInputQueue(c, fromISR);
2060void Terminal::convSendCtrl(ConvCtrl ctrl,
bool fromISR)
2063 case ConvCtrl::CarriageReturn:
2064 convQueue(
"\x0d", fromISR);
2066 case ConvCtrl::LineFeed:
2067 convQueue(
"\x0a", fromISR);
2069 case ConvCtrl::CursorLeft:
2070 convQueue(
"\e[D", fromISR);
2072 case ConvCtrl::CursorUp:
2073 convQueue(
"\e[A", fromISR);
2075 case ConvCtrl::CursorRight:
2076 convQueue(
"\e[C", fromISR);
2078 case ConvCtrl::EraseToEndOfScreen:
2079 convQueue(
"\e[J", fromISR);
2081 case ConvCtrl::EraseToEndOfLine:
2082 convQueue(
"\e[K", fromISR);
2084 case ConvCtrl::CursorHome:
2085 convQueue(
"\e[H", fromISR);
2087 case ConvCtrl::AttrNormal:
2088 convQueue(
"\e[0m", fromISR);
2090 case ConvCtrl::AttrBlank:
2091 convQueue(
"\e[8m", fromISR);
2093 case ConvCtrl::AttrBlink:
2094 convQueue(
"\e[5m", fromISR);
2096 case ConvCtrl::AttrBlinkOff:
2097 convQueue(
"\e[25m", fromISR);
2099 case ConvCtrl::AttrReverse:
2100 convQueue(
"\e[7m", fromISR);
2102 case ConvCtrl::AttrReverseOff:
2103 convQueue(
"\e[27m", fromISR);
2105 case ConvCtrl::AttrUnderline:
2106 convQueue(
"\e[4m", fromISR);
2108 case ConvCtrl::AttrUnderlineOff:
2109 convQueue(
"\e[24m", fromISR);
2111 case ConvCtrl::AttrReduce:
2112 convQueue(
"\e[2m", fromISR);
2114 case ConvCtrl::AttrReduceOff:
2115 convQueue(
"\e[22m", fromISR);
2117 case ConvCtrl::InsertLine:
2118 convQueue(
"\e[L", fromISR);
2120 case ConvCtrl::InsertChar:
2121 convQueue(
"\e[@", fromISR);
2123 case ConvCtrl::DeleteLine:
2124 convQueue(
"\e[M", fromISR);
2126 case ConvCtrl::DeleteCharacter:
2127 convQueue(
"\e[P", fromISR);
2129 case ConvCtrl::CursorOn:
2130 convQueue(
"\e[?25h", fromISR);
2132 case ConvCtrl::CursorOff:
2133 convQueue(
"\e[?25l", fromISR);
2135 case ConvCtrl::SaveCursor:
2136 convQueue(
"\e[?1048h", fromISR);
2138 case ConvCtrl::RestoreCursor:
2139 convQueue(
"\e[?1048l", fromISR);
2141 case ConvCtrl::CursorPos:
2142 case ConvCtrl::CursorPos2:
2145 int y = (ctrl == ConvCtrl::CursorPos ? m_convMatchedChars[2] - 31 : m_convMatchedChars[3] + 1);
2146 int x = (ctrl == ConvCtrl::CursorPos ? m_convMatchedChars[3] - 31 : m_convMatchedChars[2] + 1);
2147 sprintf(s,
"\e[%d;%dH", y, x);
2148 convQueue(s, fromISR);
2159void Terminal::convQueue(
const char * str,
bool fromISR)
2163 addToInputQueue(*str, fromISR);
2165 for (
int i = 0; i <= m_convMatchedCount; ++i) {
2166 addToInputQueue(m_convMatchedChars[i], fromISR);
2169 m_convMatchedCount = 0;
2170 m_convMatchedItem =
nullptr;
2174uint32_t Terminal::makeGlyphItem(uint8_t c, GlyphOptions * glyphOptions,
Color * newForegroundColor)
2176 *newForegroundColor = m_emuState.foregroundColor;
2178 if (glyphOptions->bold && (m_coloredAttributesMask & (1 << COLORED_ATTRIBUTE_BOLD))) {
2179 *newForegroundColor = m_coloredAttributesColor[COLORED_ATTRIBUTE_BOLD];
2180 if (!m_coloredAttributesMaintainStyle)
2181 glyphOptions->bold = 0;
2184 if (glyphOptions->reduceLuminosity && (m_coloredAttributesMask & (1 << COLORED_ATTRIBUTE_FAINT))) {
2185 *newForegroundColor = m_coloredAttributesColor[COLORED_ATTRIBUTE_FAINT];
2186 if (!m_coloredAttributesMaintainStyle)
2187 glyphOptions->reduceLuminosity = 0;
2190 if (glyphOptions->italic && (m_coloredAttributesMask & (1 << COLORED_ATTRIBUTE_ITALIC))) {
2191 *newForegroundColor = m_coloredAttributesColor[COLORED_ATTRIBUTE_ITALIC];
2192 if (!m_coloredAttributesMaintainStyle)
2193 glyphOptions->italic = 0;
2196 if (glyphOptions->underline && (m_coloredAttributesMask & (1 << COLORED_ATTRIBUTE_UNDERLINE))) {
2197 *newForegroundColor = m_coloredAttributesColor[COLORED_ATTRIBUTE_UNDERLINE];
2198 if (!m_coloredAttributesMaintainStyle)
2199 glyphOptions->underline = 0;
2202 return GLYPHMAP_ITEM_MAKE(c, m_emuState.backgroundColor, *newForegroundColor, *glyphOptions);
2208bool Terminal::setChar(uint8_t c)
2210 bool vscroll =
false;
2212 if (m_emuState.cursorPastLastCol) {
2213 if (m_emuState.wraparound) {
2214 setCursorPos(1, m_emuState.cursorY);
2222 if (m_emuState.insertMode)
2223 insertAt(m_emuState.cursorX, m_emuState.cursorY, 1);
2225 GlyphOptions glyphOptions = m_glyphOptions;
2228 uint32_t * mapItemPtr = m_glyphsBuffer.map + (m_emuState.cursorX - 1) + (m_emuState.cursorY - 1) * m_columns;
2229 glyphOptions.doubleWidth = glyphMapItem_getOptions(mapItemPtr).
doubleWidth;
2230 Color newForegroundColor;
2231 *mapItemPtr = makeGlyphItem(c, &glyphOptions, &newForegroundColor);
2233 if (m_bitmappedDisplayController && isActive()) {
2239 if (glyphOptions.value != m_glyphOptions.value)
2240 m_canvas->setGlyphOptions(glyphOptions);
2241 if (newForegroundColor != m_emuState.foregroundColor)
2242 m_canvas->setPenColor(newForegroundColor);
2244 int x = (m_emuState.cursorX - 1) * m_font.width * (glyphOptions.doubleWidth ? 2 : 1);
2245 int y = (m_emuState.cursorY - 1) * m_font.height;
2246 m_canvas->drawGlyph(x, y, m_font.width, m_font.height, m_font.data, c);
2248 if (newForegroundColor != m_emuState.foregroundColor)
2249 m_canvas->setPenColor(m_emuState.foregroundColor);
2250 if (glyphOptions.value != m_glyphOptions.value)
2251 m_canvas->setGlyphOptions(m_glyphOptions);
2255 if (m_glyphOptions.userOpt1)
2256 m_prevBlinkingTextEnabled =
true;
2258 if (m_emuState.cursorX == m_columns) {
2259 m_emuState.cursorPastLastCol =
true;
2261 setCursorPos(m_emuState.cursorX + 1, m_emuState.cursorY);
2268void Terminal::refresh()
2270 #if FABGLIB_TERMINAL_DEBUG_REPORT_DESCS
2274 refresh(1, 1, m_columns, m_rows);
2279void Terminal::refresh(
int X,
int Y)
2281 #if FABGLIB_TERMINAL_DEBUG_REPORT_DESCS
2282 logFmt(
"refresh(%d, %d)\n",
X,
Y);
2285 if (m_bitmappedDisplayController && isActive())
2286 m_canvas->renderGlyphsBuffer(
X - 1,
Y - 1, &m_glyphsBuffer);
2290void Terminal::refresh(
int X1,
int Y1,
int X2,
int Y2)
2292 #if FABGLIB_TERMINAL_DEBUG_REPORT_DESCS
2293 logFmt(
"refresh(%d, %d, %d, %d)\n",
X1,
Y1,
X2,
Y2);
2296 if (m_bitmappedDisplayController && isActive()) {
2297 for (
int y =
Y1 - 1; y <
Y2; ++y) {
2298 for (
int x =
X1 - 1; x <
X2; ++x)
2299 m_canvas->renderGlyphsBuffer(x, y, &m_glyphsBuffer);
2300 m_canvas->waitCompletion(
false);
2307void Terminal::setLineDoubleWidth(
int row,
int value)
2309 #if FABGLIB_TERMINAL_DEBUG_REPORT_DESCS
2310 logFmt(
"setLineDoubleWidth(%d, %d)\n", row, value);
2313 uint32_t * mapItemPtr = m_glyphsBuffer.map + (row - 1) * m_columns;
2314 for (
int i = 0; i < m_columns; ++i, ++mapItemPtr) {
2315 GlyphOptions glyphOptions = glyphMapItem_getOptions(mapItemPtr);
2316 glyphOptions.doubleWidth = value;
2317 glyphMapItem_setOptions(mapItemPtr, glyphOptions);
2320 refresh(1, row, m_columns, row);
2324int Terminal::getCharWidthAt(
int row)
2326 return glyphMapItem_getOptions(m_glyphsBuffer.map + (row - 1) * m_columns).
doubleWidth ? m_font.width * 2 : m_font.width;
2330int Terminal::getColumnsAt(
int row)
2332 return glyphMapItem_getOptions(m_glyphsBuffer.map + (row - 1) * m_columns).
doubleWidth ? m_columns / 2 : m_columns;
2336GlyphOptions Terminal::getGlyphOptionsAt(
int X,
int Y)
2338 return glyphMapItem_getOptions(m_glyphsBuffer.map + (
X - 1) + (
Y - 1) * m_columns);
2343uint8_t Terminal::getNextCode(
bool processCtrlCodes)
2347 xQueueReceive(m_inputQueue, &c, portMAX_DELAY);
2349 #if FABGLIB_TERMINAL_DEBUG_REPORT_INQUEUE_CODES
2350 logFmt(
"<= %02X %s%c\n", (
int)c, (c <= ASCII_SPC ? CTRLCHAR_TO_STR[(
int)c] :
""), (c > ASCII_SPC ? c : ASCII_SPC));
2354 uartCheckInputQueueForFlowControl();
2357 if (processCtrlCodes && ISCTRLCHAR(c))
2365void Terminal::charsConsumerTask(
void * pvParameters)
2367 Terminal * term = (Terminal*) pvParameters;
2370 term->consumeInputQueue();
2374void Terminal::consumeInputQueue()
2376 uint8_t c = getNextCode(
false);
2378 xSemaphoreTake(m_mutex, portMAX_DELAY);
2380 m_prevCursorEnabled = int_enableCursor(
false);
2381 m_prevBlinkingTextEnabled = enableBlinkingText(
false);
2388 else if (ISCTRLCHAR(c))
2392 if (m_emuState.characterSet[m_emuState.characterSetIndex] == 0 || (!m_emuState.ANSIMode && m_emuState.VT52GraphicsMode))
2393 c = DECGRAPH_TO_CP437[(uint8_t)c];
2397 enableBlinkingText(m_prevBlinkingTextEnabled);
2398 int_enableCursor(m_prevCursorEnabled);
2400 xSemaphoreGive(m_mutex);
2402 if (m_resetRequested)
2407void Terminal::execCtrlCode(uint8_t c)
2414 if (m_emuState.cursorX > 1)
2415 setCursorPos(m_emuState.cursorX - 1, m_emuState.cursorY);
2416 else if (m_emuState.reverseWraparoundMode) {
2417 int newX = m_columns;
2418 int newY = m_emuState.cursorY - 1;
2421 setCursorPos(newX, newY);
2434 if (!m_emuState.cursorPastLastCol) {
2435 if (m_emuState.newLineMode)
2436 setCursorPos(1, m_emuState.cursorY);
2454 setCursorPos(1, m_emuState.cursorY);
2460 m_emuState.characterSetIndex = 1;
2466 m_emuState.characterSetIndex = 0;
2475 sound(
'1', 800, 250, 100);
2497void Terminal::consumeESC()
2500 if (!m_emuState.ANSIMode) {
2505 uint8_t c = getNextCode(
true);
2519 if (c == FABGLEXT_STARTCODE && m_emuState.allowFabGLSequences > 0) {
2531 #if FABGLIB_TERMINAL_DEBUG_REPORT_ESC
2532 logFmt(
"ESC%c\n", c);
2539 m_resetRequested =
true;
2550 setCursorPos(1, m_emuState.cursorY);
2557 setTabStop(m_emuState.cursorX,
true);
2580 restoreCursorState();
2585 c = getNextCode(
true);
2589 setLineDoubleWidth(m_emuState.cursorY, 2);
2593 setLineDoubleWidth(m_emuState.cursorY, 3);
2597 setLineDoubleWidth(m_emuState.cursorY, 0);
2601 setLineDoubleWidth(m_emuState.cursorY, 1);
2605 erase(1, 1, m_columns, m_rows,
'E',
false,
false);
2618 switch (getNextCode(
true)) {
2621 m_emuState.characterSet[c -
'('] = 0;
2624 m_emuState.characterSet[c -
'('] = 1;
2631 m_emuState.keypadMode = KeypadMode::Application;
2632 #if FABGLIB_TERMINAL_DEBUG_REPORT_DESCSALL
2633 log(
"Keypad Application Mode\n");
2639 m_emuState.keypadMode = KeypadMode::Numeric;
2640 #if FABGLIB_TERMINAL_DEBUG_REPORT_DESCSALL
2641 log(
"Keypad Numeric Mode\n");
2646 switch (getNextCode(
true)) {
2650 m_emuState.ctrlBits = 7;
2655 if (m_emuState.conformanceLevel >= 2 && m_emuState.ANSIMode)
2656 m_emuState.ctrlBits = 8;
2663 #if FABGLIB_TERMINAL_DEBUG_REPORT_UNSUPPORT
2664 logFmt(
"Unknown ESC %c\n", c);
2674uint8_t Terminal::consumeParamsAndGetCode(
int * params,
int * paramsCount,
bool * questionMarkFound)
2678 *questionMarkFound =
false;
2682 uint8_t c = getNextCode(
true);
2684 #if FABGLIB_TERMINAL_DEBUG_REPORT_ESC
2689 *questionMarkFound =
true;
2694 if (!isdigit(c) && c !=
';') {
2696 #if FABGLIB_TERMINAL_DEBUG_REPORT_ESC
2714 *p = *p * 10 + (c -
'0');
2722void Terminal::consumeCSI()
2724 #if FABGLIB_TERMINAL_DEBUG_REPORT_ESC
2728 bool questionMarkFound;
2731 uint8_t c = consumeParamsAndGetCode(params, ¶msCount, &questionMarkFound);
2735 if (questionMarkFound && (c ==
'h' || c ==
'l')) {
2736 consumeDECPrivateModes(params, paramsCount, c);
2741 if (c == ASCII_SPC) {
2742 consumeCSISPC(params, paramsCount);
2748 consumeCSIQUOT(params, paramsCount);
2759 setCursorPos(params[1], getAbsoluteRow(params[0]));
2764 switch (params[0]) {
2766 setTabStop(m_emuState.cursorX,
false);
2769 setTabStop(0,
false);
2776 setCursorPos(m_emuState.cursorX + tmax(1, params[0]), m_emuState.cursorY);
2781 deleteAt(m_emuState.cursorX, m_emuState.cursorY, tmax(1, params[0]));
2786 setCursorPos(m_emuState.cursorX, getAbsoluteRow(m_emuState.cursorY - tmax(1, params[0])));
2796 switch (params[0]) {
2798 erase(m_emuState.cursorX, m_emuState.cursorY, m_columns, m_emuState.cursorY, ASCII_SPC,
false, questionMarkFound);
2799 erase(1, m_emuState.cursorY + 1, m_columns, m_rows, ASCII_SPC,
false, questionMarkFound);
2802 erase(1, 1, m_columns, m_emuState.cursorY - 1, ASCII_SPC,
false, questionMarkFound);
2803 erase(1, m_emuState.cursorY, m_emuState.cursorX, m_emuState.cursorY, ASCII_SPC,
false, questionMarkFound);
2806 erase(1, 1, m_columns, m_rows, ASCII_SPC,
false, questionMarkFound);
2818 switch (params[0]) {
2820 erase(m_emuState.cursorX, m_emuState.cursorY, m_columns, m_emuState.cursorY, ASCII_SPC,
true, questionMarkFound);
2823 erase(1, m_emuState.cursorY, m_emuState.cursorX, m_emuState.cursorY, ASCII_SPC,
true, questionMarkFound);
2826 erase(1, m_emuState.cursorY, m_columns, m_emuState.cursorY, ASCII_SPC,
true, questionMarkFound);
2834 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);
2839 setScrollingRegion(tmax(params[0], 1), (params[1] < 1 ? m_rows : params[1]));
2844 setCursorPos(m_emuState.cursorX, params[0]);
2849 setCursorPos(params[0], m_emuState.cursorY);
2854 for (
int i = tmax(1, params[0]); i > 0; --i)
2860 for (
int i = tmax(1, params[0]); i > 0; --i)
2867 int newX = m_emuState.cursorX - tmax(1, params[0]);
2868 if (m_emuState.reverseWraparoundMode && newX < 1) {
2870 int newY = m_emuState.cursorY - newX / m_columns - 1;
2872 newY = m_rows + newY;
2873 newX = m_columns - (newX % m_columns);
2874 setCursorPos(newX, newY);
2876 setCursorPos(tmax(1, newX), m_emuState.cursorY);
2882 setCursorPos(m_emuState.cursorX, getAbsoluteRow(m_emuState.cursorY + tmax(1, params[0])));
2887 execSGRParameters(params, paramsCount);
2892 for (
int i = tmax(1, params[0]); i > 0; --i)
2893 scrollDownAt(m_emuState.cursorY);
2898 for (
int i = tmax(1, params[0]); i > 0; --i)
2899 scrollUpAt(m_emuState.cursorY);
2906 switch (params[0]) {
2910 m_emuState.insertMode = (c ==
'h');
2915 m_emuState.newLineMode = (c ==
'h');
2919 #if FABGLIB_TERMINAL_DEBUG_REPORT_UNSUPPORT
2920 logFmt(
"Unknown: ESC [ %d %c\n", params[0], c);
2928 insertAt(m_emuState.cursorX, m_emuState.cursorY, tmax(1, params[0]));
2933 if (params[0] == 0) {
2941 paramsCount = tmax(1, paramsCount);
2942 for (
int i = 0; i < paramsCount; ++i) {
2943 bool numLock, capsLock, scrollLock;
2944 m_keyboard->getLEDs(&numLock, &capsLock, &scrollLock);
2945 switch (params[i]) {
2947 numLock = capsLock = scrollLock =
false;
2968 m_keyboard->setLEDs(numLock, capsLock, scrollLock);
2974 switch (params[0]) {
2985 send(itoa(m_emuState.originMode ? m_emuState.cursorY - m_emuState.scrollingRegionTop + 1 : m_emuState.cursorY, s, 10));
2987 send(itoa(m_emuState.cursorX, s, 10));
2995 #if FABGLIB_TERMINAL_DEBUG_REPORT_UNSUPPORT
2996 log(
"Unknown: ESC [ ");
2997 if (questionMarkFound)
2999 for (
int i = 0; i < paramsCount; ++i)
3000 logFmt(
"%d %c ", params[i], i < paramsCount - 1 ?
';' : c);
3009void Terminal::consumeCSIQUOT(
int * params,
int paramsCount)
3011 uint8_t c = getNextCode(
true);
3017 m_emuState.conformanceLevel = params[0] - 60;
3018 if (params[0] == 61 || (paramsCount == 2 && params[1] == 1))
3019 m_emuState.ctrlBits = 7;
3021 m_emuState.ctrlBits = 8;
3026 m_glyphOptions.userOpt2 = (params[0] == 1 ? 1 : 0);
3034void Terminal::consumeCSISPC(
int * params,
int paramsCount)
3036 uint8_t c = getNextCode(
true);
3043 m_emuState.cursorStyle = params[0];
3044 m_emuState.cursorBlinkingEnabled = (params[0] == 0) || (params[0] & 1);
3048 #if FABGLIB_TERMINAL_DEBUG_REPORT_UNSUPPORT
3049 log(
"Unknown: ESC [ ");
3050 for (
int i = 0; i < paramsCount; ++i)
3051 logFmt(
"%d %c ", params[i], i < paramsCount - 1 ?
';' : ASCII_SPC);
3063void Terminal::consumeDECPrivateModes(
int const * params,
int paramsCount, uint8_t c)
3065 bool set = (c ==
'h');
3066 switch (params[0]) {
3072 m_emuState.cursorKeysMode = set;
3079 m_emuState.ANSIMode = set;
3086 if (m_emuState.allow132ColumnMode) {
3087 set132ColumnMode(set);
3096 m_emuState.smoothScroll = set;
3111 m_emuState.originMode = set;
3113 setCursorPos(m_emuState.cursorX, m_emuState.scrollingRegionTop);
3120 m_emuState.wraparound = set;
3127 m_emuState.keyAutorepeat = set;
3134 m_emuState.cursorBlinkingEnabled = set;
3141 m_prevCursorEnabled = set;
3148 m_emuState.allow132ColumnMode = set;
3155 m_emuState.reverseWraparoundMode = set;
3165 useAlternateScreenBuffer(set);
3172 m_emuState.backarrowKeyMode = set;
3181 restoreCursorState();
3190 useAlternateScreenBuffer(
true);
3192 useAlternateScreenBuffer(
false);
3193 restoreCursorState();
3202 enableFabGLSequences(set);
3206 #if FABGLIB_TERMINAL_DEBUG_REPORT_UNSUPPORT
3207 logFmt(
"Unknown DECSET/DECRST: %d %c\n", params[0], c);
3216void Terminal::execSGRParameters(
int const * params,
int paramsCount)
3218 for (; paramsCount; ++params, --paramsCount) {
3223 m_glyphOptions.bold = 0;
3224 m_glyphOptions.reduceLuminosity = 0;
3225 m_glyphOptions.italic = 0;
3226 m_glyphOptions.underline = 0;
3227 m_glyphOptions.userOpt1 = 0;
3228 m_glyphOptions.blank = 0;
3229 m_glyphOptions.invert = 0;
3230 int_setForegroundColor(m_defaultForegroundColor);
3231 int_setBackgroundColor(m_defaultBackgroundColor);
3236 m_glyphOptions.bold = 1;
3241 m_glyphOptions.reduceLuminosity = 1;
3246 m_emuState.characterSetIndex = 0;
3251 m_glyphOptions.bold = m_glyphOptions.reduceLuminosity = 0;
3256 m_glyphOptions.italic = 1;
3261 m_glyphOptions.italic = 0;
3266 m_glyphOptions.underline = 1;
3271 m_glyphOptions.underline = 0;
3276 m_glyphOptions.userOpt1 = 1;
3281 m_glyphOptions.userOpt1 = 0;
3286 m_glyphOptions.invert = 1;
3291 m_glyphOptions.invert = 0;
3296 m_glyphOptions.blank = 1;
3301 m_glyphOptions.blank = 0;
3306 int_setForegroundColor( (
Color) (*params - 30) );
3311 int_setForegroundColor(m_defaultForegroundColor);
3316 int_setBackgroundColor( (
Color) (*params - 40) );
3321 int_setBackgroundColor(m_defaultBackgroundColor);
3326 int_setForegroundColor( (
Color) (8 + *params - 90) );
3331 int_setBackgroundColor( (
Color) (8 + *params - 100) );
3335 #if FABGLIB_TERMINAL_DEBUG_REPORT_UNSUPPORT
3336 logFmt(
"Unknown: ESC [ %d m\n", *params);
3342 if (m_bitmappedDisplayController && isActive())
3343 m_canvas->setGlyphOptions(m_glyphOptions);
3349void Terminal::consumeDCS()
3351 #if FABGLIB_TERMINAL_DEBUG_REPORT_ESC
3356 bool questionMarkFound;
3359 uint8_t c = consumeParamsAndGetCode(params, ¶msCount, &questionMarkFound);
3363 int contentLength = 0;
3364 content[contentLength++] = c;
3366 uint8_t c = getNextCode(
false);
3367 if (c == ASCII_ESC) {
3368 if (getNextCode(
false) ==
'\\')
3371 #if FABGLIB_TERMINAL_DEBUG_REPORT_UNSUPPORT
3372 log(
"DCS failed, expected ST\n");
3377 #if FABGLIB_TERMINAL_DEBUG_REPORT_UNSUPPORT
3378 log(
"DCS failed, content too long\n");
3382 content[contentLength++] = c;
3386 if (m_emuState.conformanceLevel >= 3 && contentLength > 2 && content[0] ==
'$' && content[1] ==
'q') {
3391 if (contentLength == 4 && content[2] ==
'\"' && content[3] ==
'p') {
3394 send(
'0' + m_emuState.conformanceLevel);
3396 send(m_emuState.ctrlBits == 7 ?
'1' :
'0');
3403 #if FABGLIB_TERMINAL_DEBUG_REPORT_UNSUPPORT
3404 log(
"Unknown: ESC P ");
3405 for (
int i = 0; i < paramsCount; ++i)
3406 logFmt(
"%d %c ", params[i], i < paramsCount - 1 ?
';' : ASCII_SPC);
3407 logFmt(
"%.*s ESC \\\n", contentLength, content);
3412void Terminal::consumeESCVT52()
3414 uint8_t c = getNextCode(
false);
3416 #if FABGLIB_TERMINAL_DEBUG_REPORT_ESC
3417 logFmt(
"ESC%c\n", c);
3421 if (c == FABGLEXT_STARTCODE && m_emuState.allowFabGLSequences > 0) {
3431 m_emuState.ANSIMode =
true;
3432 m_emuState.conformanceLevel = 1;
3437 setCursorPos(m_emuState.cursorX, m_emuState.cursorY - 1);
3442 setCursorPos(m_emuState.cursorX, m_emuState.cursorY + 1);
3447 setCursorPos(m_emuState.cursorX + 1, m_emuState.cursorY);
3452 setCursorPos(m_emuState.cursorX -1, m_emuState.cursorY);
3468 erase(m_emuState.cursorX, m_emuState.cursorY, m_columns, m_emuState.cursorY, ASCII_SPC,
false,
false);
3469 erase(1, m_emuState.cursorY + 1, m_columns, m_rows, ASCII_SPC,
false,
false);
3474 erase(m_emuState.cursorX, m_emuState.cursorY, m_columns, m_emuState.cursorY, ASCII_SPC,
true,
false);
3480 int row = getNextCode(
false) - 31;
3481 int col = getNextCode(
false) - 31;
3482 setCursorPos(col, row);
3493 m_emuState.keypadMode = KeypadMode::Application;
3494 #if FABGLIB_TERMINAL_DEBUG_REPORT_DESCSALL
3495 log(
"Enter Alternate Keypad Mode\n");
3501 m_emuState.keypadMode = KeypadMode::Numeric;
3502 #if FABGLIB_TERMINAL_DEBUG_REPORT_DESCSALL
3503 log(
"Exit Alternate Keypad Mode\n");
3509 m_emuState.VT52GraphicsMode =
true;
3514 m_emuState.VT52GraphicsMode =
false;
3519 #if FABGLIB_TERMINAL_DEBUG_REPORT_UNSUPPORT
3520 logFmt(
"Unknown ESC %c\n", c);
3530void Terminal::consumeOSC()
3532 #if FABGLIB_TERMINAL_DEBUG_REPORT_ESC
3538 uint8_t c = getNextCode(
false);
3539 #if FABGLIB_TERMINAL_DEBUG_REPORT_OSC_CONTENT
3540 logFmt(
"OSC: %02X %s%c\n", (
int)c, (c <= ASCII_SPC ? CTRLCHAR_TO_STR[(
int)(c)] :
""), (c > ASCII_SPC ? c : ASCII_SPC));
3542 if (c == ASCII_BEL || (c ==
'\\' && prevChar == ASCII_ESC))
3551 if (!m_soundGenerator)
3553 return m_soundGenerator;
3557void Terminal::sound(
int waveform,
int frequency,
int duration,
int volume)
3559 auto sg = soundGenerator();
3584uint8_t Terminal::extGetByteParam()
3586 if (m_extNextCode > -1) {
3587 auto r = m_extNextCode;
3591 return getNextCode(
false);
3597int Terminal::extGetIntParam()
3602 uint8_t c = extGetByteParam();
3609 }
else if (c ==
'+' || c ==
' ') {
3617 }
else if (sign == -2)
3619 val = val * 10 + c -
'0';
3627void Terminal::extGetCmdParam(
char * cmd)
3630 for (; len < FABGLEXT_MAXSUBCMDLEN - 1; ++len) {
3631 uint8_t c = extGetByteParam();
3644void Terminal::consumeFabGLSeq()
3646 #if FABGLIB_TERMINAL_DEBUG_REPORT_ESC
3647 log(
"ESC FABGLEXT_STARTCODE");
3652 uint8_t c = extGetByteParam();
3662 case FABGLEXTX_CLEAR:
3664 syncDisplayController();
3665 erase(1, 1, m_columns, m_rows, ASCII_SPC,
false,
false);
3673 case FABGLEXTX_ENABLECURSOR:
3674 m_prevCursorEnabled = (extGetByteParam() ==
'1');
3686 case FABGLEXTB_GETCURSORCOL:
3688 send(FABGLEXT_REPLYCODE);
3689 send(m_emuState.cursorX);
3700 case FABGLEXTB_GETCURSORROW:
3702 send(FABGLEXT_REPLYCODE);
3703 send(m_emuState.cursorY);
3715 case FABGLEXTB_GETCURSORPOS:
3717 send(FABGLEXT_REPLYCODE);
3718 send(m_emuState.cursorX);
3719 send(m_emuState.cursorY);
3728 case FABGLEXTB_SETCURSORPOS:
3730 uint8_t col = extGetByteParam();
3731 uint8_t row = extGetByteParam();
3733 setCursorPos(col, getAbsoluteRow(row));
3743 case FABGLEXTX_SETCURSORPOS:
3745 uint8_t col = extGetIntParam();
3747 uint8_t row = extGetIntParam();
3749 setCursorPos(col, getAbsoluteRow(row));
3763 case FABGLEXTB_INSERTSPACE:
3765 uint8_t charsToMove_L = extGetByteParam();
3766 uint8_t charsToMove_H = extGetByteParam();
3768 bool scroll = multilineInsertChar(charsToMove_L | charsToMove_H << 8);
3769 send(FABGLEXT_REPLYCODE);
3780 case FABGLEXTB_DELETECHAR:
3782 uint8_t charsToMove_L = extGetByteParam();
3783 uint8_t charsToMove_H = extGetByteParam();
3785 multilineDeleteChar(charsToMove_L | charsToMove_H << 8);
3794 case FABGLEXTB_CURSORLEFT:
3796 uint8_t count_L = extGetByteParam();
3797 uint8_t count_H = extGetByteParam();
3799 move(-(count_L | count_H << 8));
3808 case FABGLEXTB_CURSORRIGHT:
3810 uint8_t count_L = extGetByteParam();
3811 uint8_t count_H = extGetByteParam();
3813 move(count_L | count_H << 8);
3826 case FABGLEXTB_SETCHAR:
3828 bool scroll = setChar(extGetByteParam());
3830 send(FABGLEXT_REPLYCODE);
3843 case FABGLEXTB_ISVKDOWN:
3847 send(FABGLEXT_REPLYCODE);
3848 send(keyboard()->isVKDown(vk) ?
'1' :
'0');
3855 case FABGLEXTB_DISABLEFABSEQ:
3857 enableFabGLSequences(
false);
3865 case FABGLEXTB_SETTERMTYPE:
3867 auto termType = (
TermType) extGetByteParam();
3869 int_setTerminalType(termType);
3878 case FABGLEXTB_SETFGCOLOR:
3879 int_setForegroundColor((
Color) extGetByteParam());
3888 case FABGLEXTB_SETBGCOLOR:
3889 int_setBackgroundColor((
Color) extGetByteParam());
3899 case FABGLEXTB_SETCHARSTYLE:
3901 int idx = extGetByteParam();
3902 int val = extGetByteParam();
3906 m_glyphOptions.bold = val;
3909 m_glyphOptions.reduceLuminosity = val;
3912 m_glyphOptions.italic = val;
3915 m_glyphOptions.underline = val;
3918 m_glyphOptions.userOpt1 = val;
3921 m_glyphOptions.blank = val;
3924 m_glyphOptions.invert = val;
3927 if (m_bitmappedDisplayController && isActive())
3928 m_canvas->setGlyphOptions(m_glyphOptions);
3944 case FABGLEXTX_SETUPGPIO:
3946 auto mode = GPIO_MODE_DISABLE;
3947 switch (extGetByteParam()) {
3949 mode = GPIO_MODE_INPUT;
3952 mode = GPIO_MODE_OUTPUT;
3955 mode = GPIO_MODE_OUTPUT_OD;
3958 mode = GPIO_MODE_INPUT_OUTPUT_OD;
3961 mode = GPIO_MODE_INPUT_OUTPUT;
3964 auto gpio = (gpio_num_t) extGetIntParam();
3966 configureGPIO(gpio, mode);
3976 case FABGLEXTX_SETGPIO:
3978 auto l = extGetByteParam();
3979 auto level = (l == 1 || l ==
'1' || l ==
'H') ? 1 : 0;
3980 auto gpio = (gpio_num_t) extGetIntParam();
3982 gpio_set_level(gpio, level);
3994 case FABGLEXTX_GETGPIO:
3996 auto gpio = (gpio_num_t) extGetIntParam();
3998 send(FABGLEXT_REPLYCODE);
3999 send(gpio_get_level(gpio) ?
'1' :
'0');
4014 case FABGLEXTX_SETUPADC:
4016 auto width = (adc_bits_width_t) (extGetIntParam() - 9);
4018 auto atten = (adc_atten_t) extGetIntParam();
4020 auto channel = ADC1_GPIO2Channel((gpio_num_t)extGetIntParam());
4022 adc1_config_width(
width);
4023 adc1_config_channel_atten(channel, atten);
4042 case FABGLEXTX_READADC:
4044 auto val = adc1_get_raw(ADC1_GPIO2Channel((gpio_num_t)extGetIntParam()));
4046 send(FABGLEXT_REPLYCODE);
4047 send(toupper(digit2hex((val & 0xF00) >> 8)));
4048 send(toupper(digit2hex((val & 0x0F0) >> 4)));
4049 send(toupper(digit2hex(val & 0x00F)));
4061 case FABGLEXTX_SOUND:
4063 char waveform = extGetByteParam();
4065 uint16_t frequency = extGetIntParam();
4067 uint16_t duration = extGetIntParam();
4069 uint8_t volume = extGetIntParam() & 0x7f;
4071 sound(waveform, frequency, duration, volume);
4078 case FABGLEXTX_GRAPHICSCMD:
4079 consumeFabGLGraphicsSeq();
4087 case FABGLEXTX_SHOWMOUSE:
4089 bool value = (extGetByteParam() ==
'1');
4090 if (m_bitmappedDisplayController) {
4091 auto dispctrl =
static_cast<BitmappedDisplayController*
>(m_displayController);
4092 auto mouse = PS2Controller::mouse();
4093 if (mouse && mouse->isMouseAvailable()) {
4095 mouse->setupAbsolutePositioner(m_canvas->getWidth(), m_canvas->getHeight(),
false, dispctrl);
4098 dispctrl->setMouseCursor(
nullptr);
4099 mouse->terminateAbsolutePositioner();
4121 case FABGLEXTX_GETMOUSEPOS:
4124 if (m_bitmappedDisplayController) {
4125 auto mouse = PS2Controller::mouse();
4126 auto x = mouse->status().X;
4127 auto y = mouse->status().Y;
4128 send(FABGLEXT_REPLYCODE);
4130 send(toupper(digit2hex((x & 0xF00) >> 8)));
4131 send(toupper(digit2hex((x & 0x0F0) >> 4)));
4132 send(toupper(digit2hex((x & 0x00F) )));
4135 send(toupper(digit2hex((y & 0xF00) >> 8)));
4136 send(toupper(digit2hex((y & 0x0F0) >> 4)));
4137 send(toupper(digit2hex((y & 0x00F) )));
4140 send(toupper(digit2hex(mouse->status().wheelDelta & 0xf)));
4143 auto b = mouse->status().buttons;
4144 send(toupper(digit2hex( b.left | (b.middle << 1) | (b.right << 2) )));
4156 case FABGLEXTX_DELAY:
4158 auto value = extGetIntParam();
4160 vTaskDelay(value / portTICK_PERIOD_MS);
4161 send(FABGLEXT_REPLYCODE);
4170 case FABGLEXT_USERSEQ:
4172 char usrseq[FABGLEXT_MAXSUBCMDLEN];
4174 while (count < FABGLEXT_MAXSUBCMDLEN) {
4175 char c = extGetByteParam();
4176 if (c == FABGLEXT_ENDCODE)
4178 usrseq[count++] = c;
4181 onUserSequence(usrseq);
4186 #if FABGLIB_TERMINAL_DEBUG_REPORT_UNSUPPORT
4187 logFmt(
"Unknown: ESC FABGLEXT_STARTCODE %02x\n", c);
4194void Terminal::freeSprites()
4196 for (
int i = 0; i < m_spritesCount; ++i) {
4197 for (
int j = 0; j < m_sprites[i].framesCount; ++j) {
4198 free(m_sprites[i].frames[j]->
data);
4199 delete m_sprites[i].frames[j];
4202 delete [] m_sprites;
4203 m_sprites =
nullptr;
4210void Terminal::consumeFabGLGraphicsSeq()
4212 char cmd[FABGLEXT_MAXSUBCMDLEN];
4213 extGetCmdParam(cmd);
4215 if (strcmp(cmd, FABGLEXT_GCLEAR) == 0) {
4226 }
else if (strcmp(cmd, FABGLEXT_GSETBRUSHCOLOR) == 0) {
4235 int r = extGetIntParam();
4237 int g = extGetIntParam();
4239 int b = extGetIntParam();
4242 m_canvas->setBrushColor(r, g, b);
4244 }
else if (strcmp(cmd, FABGLEXT_GSETPENCOLOR) == 0) {
4253 int r = extGetIntParam();
4255 int g = extGetIntParam();
4257 int b = extGetIntParam();
4260 m_canvas->setPenColor(r, g, b);
4262 }
else if (strcmp(cmd, FABGLEXT_GSETPIXEL) == 0) {
4270 int x = extGetIntParam();
4272 int y = extGetIntParam();
4275 m_canvas->setPixel(x, y);
4277 }
else if (strcmp(cmd, FABGLEXT_GSCROLL) == 0) {
4285 int ox = extGetIntParam();
4287 int oy = extGetIntParam();
4290 m_canvas->scroll(ox, oy);
4292 }
else if (strcmp(cmd, FABGLEXT_GPENWIDTH) == 0) {
4299 int w = extGetIntParam();
4302 m_canvas->setPenWidth(w);
4304 }
else if (strcmp(cmd, FABGLEXT_GLINE) == 0) {
4314 int x1 = extGetIntParam();
4316 int y1 = extGetIntParam();
4318 int x2 = extGetIntParam();
4320 int y2 = extGetIntParam();
4323 m_canvas->drawLine(x1, y1, x2, y2);
4325 }
else if (strcmp(cmd, FABGLEXT_GRECT) == 0) {
4335 int x1 = extGetIntParam();
4337 int y1 = extGetIntParam();
4339 int x2 = extGetIntParam();
4341 int y2 = extGetIntParam();
4344 m_canvas->drawRectangle(x1, y1, x2, y2);
4346 }
else if (strcmp(cmd, FABGLEXT_GFILLRECT) == 0) {
4356 int x1 = extGetIntParam();
4358 int y1 = extGetIntParam();
4360 int x2 = extGetIntParam();
4362 int y2 = extGetIntParam();
4365 m_canvas->fillRectangle(x1, y1, x2, y2);
4367 }
else if (strcmp(cmd, FABGLEXT_GELLIPSE) == 0) {
4377 int x = extGetIntParam();
4379 int y = extGetIntParam();
4381 int w = extGetIntParam();
4383 int h = extGetIntParam();
4386 m_canvas->drawEllipse(x, y, w, h);
4388 }
else if (strcmp(cmd, FABGLEXT_GFILLELLIPSE) == 0) {
4398 int x = extGetIntParam();
4400 int y = extGetIntParam();
4402 int w = extGetIntParam();
4404 int h = extGetIntParam();
4407 m_canvas->fillEllipse(x, y, w, h);
4409 }
else if (strcmp(cmd, FABGLEXT_GPATH) == 0) {
4419 constexpr int MAXPOINTS = 32;
4420 Point pts[MAXPOINTS];
4422 while (count < MAXPOINTS) {
4423 pts[count].X = extGetIntParam();
4425 pts[count].Y = extGetIntParam();
4427 if (extGetByteParam() == FABGLEXT_ENDCODE)
4431 m_canvas->drawPath(pts, count);
4433 }
else if (strcmp(cmd, FABGLEXT_GFILLPATH) == 0) {
4443 constexpr int MAXPOINTS = 32;
4444 Point pts[MAXPOINTS];
4446 while (count < MAXPOINTS) {
4447 pts[count].X = extGetIntParam();
4449 pts[count].Y = extGetIntParam();
4451 if (extGetByteParam() == FABGLEXT_ENDCODE)
4455 m_canvas->fillPath(pts, count);
4457 }
else if (strcmp(cmd, FABGLEXT_GSPRITECOUNT) == 0) {
4464 int count = extGetIntParam();
4466 if (m_bitmappedDisplayController) {
4467 static_cast<BitmappedDisplayController*
>(m_displayController)->setSprites<Sprite>(
nullptr, 0);
4470 m_spritesCount = count;
4471 m_sprites =
new Sprite[count];
4475 }
else if (strcmp(cmd, FABGLEXT_GSPRITEDEF) == 0) {
4489 int sprite = extGetIntParam();
4491 int width = extGetIntParam();
4493 int height = extGetIntParam();
4495 char cformat = extGetByteParam();
4497 int r = 0, g = 0, b = 0;
4502 r = extGetIntParam();
4504 g = extGetIntParam();
4506 b = extGetIntParam();
4509 format = PixelFormat::Mask;
4513 format = PixelFormat::RGBA2222;
4517 format = PixelFormat::RGBA8888;
4520 auto data = (uint8_t*) malloc(bytes);
4521 for (
int i = 0; i < bytes + 1; ++i) {
4522 auto c = extGetByteParam();
4523 if (c == FABGLEXT_ENDCODE)
4525 data[i] = hex2digit(tolower(c)) << 4;
4526 c = extGetByteParam();
4527 if (c == FABGLEXT_ENDCODE)
4529 data[i] |= hex2digit(tolower(c));
4531 if (m_bitmappedDisplayController && sprite < m_spritesCount) {
4532 auto bitmap =
new Bitmap(
width,
height,
data, format, RGB888(r, g, b),
false);
4533 m_sprites[sprite].addBitmap(bitmap);
4534 static_cast<BitmappedDisplayController*
>(m_displayController)->setSprites(m_sprites, m_spritesCount);
4540 }
else if (strcmp(cmd, FABGLEXT_GSPRITESET) == 0) {
4551 int sprite = extGetIntParam();
4553 char visible = extGetByteParam();
4555 int frame = extGetIntParam();
4557 int posx = extGetIntParam();
4559 int posy = extGetIntParam();
4561 if (m_bitmappedDisplayController && sprite < m_spritesCount) {
4562 m_sprites[sprite].visible = (visible ==
'V');
4563 m_sprites[sprite].setFrame(frame);
4564 m_sprites[sprite].x = posx;
4565 m_sprites[sprite].y = posy;
4566 static_cast<BitmappedDisplayController*
>(m_displayController)->refreshSprites();
4570 #if FABGLIB_TERMINAL_DEBUG_REPORT_UNSUPPORT
4571 logFmt(
"Unknown: ESC FABGLEXT_STARTCODE FABGLEXTX_GRAPHICSCMD %s\n", cmd);
4577void Terminal::keyboardReaderTask(
void * pvParameters)
4579 Terminal * term = (Terminal*) pvParameters;
4583 if (!term->isActive())
4586 VirtualKeyItem item;
4587 if (term->m_keyboard->getNextVirtualKey(&item)) {
4589 if (term->isActive()) {
4591 term->onVirtualKey(&item.vk, item.down);
4592 term->onVirtualKeyItem(&item);
4594 if (term->flowControl()) {
4600 if (!term->m_emuState.keyAutorepeat && term->m_lastPressedKey == item.vk)
4602 term->m_lastPressedKey = item.vk;
4604 xSemaphoreTake(term->m_mutex, portMAX_DELAY);
4606 if (term->m_termInfo ==
nullptr) {
4607 if (term->m_emuState.ANSIMode)
4608 term->ANSIDecodeVirtualKey(item);
4610 term->VT52DecodeVirtualKey(item);
4612 term->TermDecodeVirtualKey(item);
4614 xSemaphoreGive(term->m_mutex);
4618 term->m_lastPressedKey =
VK_NONE;
4625 term->m_keyboard->injectVirtualKey(item,
true);
4634void Terminal::sendCursorKeyCode(uint8_t c)
4636 if (m_emuState.cursorKeysMode)
4644void Terminal::sendKeypadCursorKeyCode(uint8_t applicationCode,
const char * numericCode)
4646 if (m_emuState.keypadMode == KeypadMode::Application) {
4648 send(applicationCode);
4656void Terminal::ANSIDecodeVirtualKey(VirtualKeyItem
const & item)
4663 sendCursorKeyCode(
'A');
4667 sendCursorKeyCode(
'B');
4671 sendCursorKeyCode(
'C');
4675 sendCursorKeyCode(
'D');
4681 sendKeypadCursorKeyCode(
'x',
"A");
4685 sendKeypadCursorKeyCode(
'r',
"B");
4689 sendKeypadCursorKeyCode(
'v',
"C");
4693 sendKeypadCursorKeyCode(
't',
"D");
4731 sendKeypadCursorKeyCode(
'y',
"5~");
4735 sendKeypadCursorKeyCode(
's',
"6~");
4739 sendKeypadCursorKeyCode(
'p',
"2~");
4743 sendKeypadCursorKeyCode(
'w',
"1~");
4747 sendKeypadCursorKeyCode(
'n',
"3~");
4751 sendKeypadCursorKeyCode(
'q',
"4~");
4757 send(m_emuState.backarrowKeyMode ? ASCII_BS : ASCII_DEL);
4826 switch (item.ASCII) {
4830 if (m_emuState.newLineMode)
4848void Terminal::VT52DecodeVirtualKey(VirtualKeyItem
const & item)
4874 send(m_emuState.keypadMode == KeypadMode::Application ?
"\e?p" :
"0");
4879 send(m_emuState.keypadMode == KeypadMode::Application ?
"\e?q" :
"1");
4884 send(m_emuState.keypadMode == KeypadMode::Application ?
"\e?r" :
"2");
4889 send(m_emuState.keypadMode == KeypadMode::Application ?
"\e?s" :
"3");
4894 send(m_emuState.keypadMode == KeypadMode::Application ?
"\e?t" :
"4");
4899 send(m_emuState.keypadMode == KeypadMode::Application ?
"\e?u" :
"5");
4904 send(m_emuState.keypadMode == KeypadMode::Application ?
"\e?v" :
"6");
4909 send(m_emuState.keypadMode == KeypadMode::Application ?
"\e?w" :
"7");
4914 send(m_emuState.keypadMode == KeypadMode::Application ?
"\e?x" :
"8");
4919 send(m_emuState.keypadMode == KeypadMode::Application ?
"\e?y" :
"9");
4924 send(m_emuState.keypadMode == KeypadMode::Application ?
"\e?n" :
".");
4928 send(m_emuState.keypadMode == KeypadMode::Application ?
"\e?M" :
"\r");
4945void Terminal::TermDecodeVirtualKey(VirtualKeyItem
const & item)
4947 for (
auto i = m_termInfo->kbdCtrlSet; i->vk !=
VK_NONE; ++i) {
4948 if (i->vk == item.vk) {
4949 send(i->ANSICtrlCode);
4966TerminalController::TerminalController(
Terminal * terminal)
4967 : m_terminal(terminal)
4972TerminalController::~TerminalController()
4979 m_terminal = terminal;
4983void TerminalController::write(uint8_t c)
4986 m_terminal->
write(c);
4992void TerminalController::write(
char const * str)
4999int TerminalController::read()
5002 return m_terminal->
read(-1);
5011void TerminalController::waitFor(
int value)
5014 if (read() == value)
5021 write(FABGLEXT_CMD);
5022 write(FABGLEXTX_CLEAR);
5023 write(FABGLEXT_ENDCODE);
5029 write(FABGLEXT_CMD);
5030 write(FABGLEXTX_ENABLECURSOR);
5031 write(value ?
'1' :
'0');
5032 write(FABGLEXT_ENDCODE);
5038 write(FABGLEXT_CMD);
5039 write(FABGLEXTB_SETCURSORPOS);
5042 write(FABGLEXT_ENDCODE);
5048 write(FABGLEXT_CMD);
5049 write(FABGLEXTB_CURSORLEFT);
5050 write(count & 0xff);
5052 write(FABGLEXT_ENDCODE);
5058 write(FABGLEXT_CMD);
5059 write(FABGLEXTB_CURSORRIGHT);
5060 write(count & 0xff);
5062 write(FABGLEXT_ENDCODE);
5068 write(FABGLEXT_CMD);
5069 write(FABGLEXTB_GETCURSORPOS);
5070 write(FABGLEXT_ENDCODE);
5071 waitFor(FABGLEXT_REPLYCODE);
5079 write(FABGLEXT_CMD);
5080 write(FABGLEXTB_GETCURSORCOL);
5081 write(FABGLEXT_ENDCODE);
5082 waitFor(FABGLEXT_REPLYCODE);
5089 write(FABGLEXT_CMD);
5090 write(FABGLEXTB_GETCURSORROW);
5091 write(FABGLEXT_ENDCODE);
5092 waitFor(FABGLEXT_REPLYCODE);
5099 write(FABGLEXT_CMD);
5100 write(FABGLEXTB_INSERTSPACE);
5101 write(charsToMove & 0xff);
5102 write(charsToMove >> 8);
5103 write(FABGLEXT_ENDCODE);
5104 waitFor(FABGLEXT_REPLYCODE);
5111 write(FABGLEXT_CMD);
5112 write(FABGLEXTB_DELETECHAR);
5113 write(charsToMove & 0xff);
5114 write(charsToMove >> 8);
5115 write(FABGLEXT_ENDCODE);
5121 write(FABGLEXT_CMD);
5122 write(FABGLEXTB_SETCHAR);
5124 write(FABGLEXT_ENDCODE);
5125 waitFor(FABGLEXT_REPLYCODE);
5132 write(FABGLEXT_CMD);
5133 write(FABGLEXTB_ISVKDOWN);
5135 write(FABGLEXT_ENDCODE);
5136 waitFor(FABGLEXT_REPLYCODE);
5137 return read() ==
'1';
5143 write(FABGLEXT_CMD);
5144 write(FABGLEXTB_DISABLEFABSEQ);
5145 write(FABGLEXT_ENDCODE);
5151 write(FABGLEXT_CMD);
5152 write(FABGLEXTB_SETTERMTYPE);
5154 write(FABGLEXT_ENDCODE);
5160 write(FABGLEXT_CMD);
5161 write(FABGLEXTB_SETFGCOLOR);
5163 write(FABGLEXT_ENDCODE);
5169 write(FABGLEXT_CMD);
5170 write(FABGLEXTB_SETBGCOLOR);
5172 write(FABGLEXT_ENDCODE);
5178 write(FABGLEXT_CMD);
5179 write(FABGLEXTB_SETCHARSTYLE);
5181 write(enabled ? 1 : 0);
5182 write(FABGLEXT_ENDCODE);
5193 : m_terminal(terminal),
5194 m_termctrl(terminal),
5200 m_typeText(nullptr),
5206LineEditor::~LineEditor()
5214void LineEditor::setLength(
int newLength)
5216 if (m_allocated < newLength || m_allocated == 0) {
5217 int allocated = imax(m_allocated * 2, newLength);
5218 m_text = (
char*) realloc(m_text, allocated + 1);
5219 memset(m_text + m_allocated, 0, allocated - m_allocated + 1);
5220 m_allocated = allocated;
5222 m_textLength = newLength;
5230 m_typeText = strdup(text);
5237 setText(text, strlen(text), moveCursor);
5246 for (
int i = 0; i < m_textLength; ++i)
5249 for (
int i = 0; i < length; ++i)
5250 m_homeRow -= m_termctrl.
setChar(text[i]);
5253 memcpy(m_text, text, length);
5255 m_inputPos = moveCursor ? length : 0;
5259void LineEditor::write(uint8_t c)
5262 m_terminal->
write(c);
5268int LineEditor::read()
5271 return m_terminal->
read(-1);
5280void LineEditor::beginInput()
5282 if (m_terminal ==
nullptr) {
5291 for (
int i = 0, len = strlen(m_text); i < len; ++i)
5292 m_homeRow -= m_termctrl.
setChar(m_text[i]);
5293 if (m_inputPos == 0)
5302void LineEditor::endInput()
5305 if (m_text ==
nullptr) {
5306 m_text = (
char*) malloc(1);
5312void LineEditor::performCursorUp()
5318void LineEditor::performCursorDown()
5324void LineEditor::performCursorLeft()
5326 if (m_inputPos > 0) {
5330 while (m_inputPos - count > 0 && (m_text[m_inputPos - count] == ASCII_SPC || m_text[m_inputPos - count - 1] != ASCII_SPC))
5334 m_inputPos -= count;
5339void LineEditor::performCursorRight()
5341 if (m_inputPos < m_textLength) {
5345 while (m_text[m_inputPos + count] && (m_text[m_inputPos + count] == ASCII_SPC || m_text[m_inputPos + count - 1] != ASCII_SPC))
5349 m_inputPos += count;
5354void LineEditor::performCursorHome()
5361void LineEditor::performCursorEnd()
5363 m_termctrl.
cursorRight(m_textLength - m_inputPos);
5364 m_inputPos = m_textLength;
5368void LineEditor::performDeleteRight()
5370 if (m_inputPos < m_textLength) {
5371 memmove(m_text + m_inputPos, m_text + m_inputPos + 1, m_textLength - m_inputPos);
5378void LineEditor::performDeleteLeft()
5380 if (m_inputPos > 0) {
5383 memmove(m_text + m_inputPos - 1, m_text + m_inputPos, m_textLength - m_inputPos + 1);
5402 c = m_typeText[m_typingIndex++];
5405 m_typeText =
nullptr;
5435 }
else if (m_state == 2) {
5443 performCursorHome();
5454 }
else if (m_state >= 31) {
5474 performCursorLeft();
5480 performCursorRight();
5496 performCursorHome();
5506 performDeleteRight();
5511 m_insertMode = !m_insertMode;
5543 performDeleteLeft();
5548 performDeleteRight();
5557 m_termctrl.
cursorRight(m_textLength - m_inputPos);
5575 performCursorDown();
5580 performCursorLeft();
5585 performCursorRight();
5592 if (maxLength == 0 || m_inputPos < maxLength) {
5594 if (m_insertMode || m_inputPos == m_textLength) {
5595 setLength(m_textLength + 1);
5596 memmove(m_text + m_inputPos + 1, m_text + m_inputPos, m_textLength - m_inputPos);
5598 m_text[m_inputPos++] = c;
5600 if (m_insertMode && m_inputPos < m_textLength) {
Represents the base abstract class for all display controllers.
Represents the base abstract class for bitmapped display controllers.
A class with a set of drawing methods.
The PS2 Keyboard controller class.
char const * edit(int maxLength=0)
Reads user input and return the inserted line.
Delegate< LineEditorSpecialChar > onSpecialChar
A delegate called whenever a special character has been pressed.
void setText(char const *text, bool moveCursor=true)
Sets initial text.
LineEditor(Terminal *terminal)
Object constructor.
Delegate< int * > onChar
A delegate called whenever a character has been received.
Delegate< int * > onRead
Read character delegate.
void typeText(char const *text)
Simulates user typing.
Delegate< int > onWrite
Write character delegate.
Delegate< int * > onCarriageReturn
A delegate called whenever carriage return has been pressed.
SoundGenerator handles audio output.
static int keyboardReaderTaskStackSize
Stack size of the task that reads keys from keyboard and send ANSI/VT codes to output stream in Termi...
size_t write(const uint8_t *buffer, size_t size)
Sends specified number of codes to the display.
static int inputQueueSize
Number of characters the terminal can "write" without pause (increase if you have loss of characters ...
int read()
Reads codes from keyboard.
static int inputConsumerTaskStackSize
Stack size of the task that processes Terminal input stream.
void flowControl(bool enableRX)
Allows/disallows host to send data.
void cursorRight(int count)
Moves cursor to the right.
void disableFabGLSequences()
Disables FabGL specific sequences.
void cursorLeft(int count)
Moves cursor to the left.
void multilineDeleteChar(int charsToMove)
Deletes a character moving specified amount of characters to the left.
bool isVKDown(VirtualKey vk)
Checks if a virtual key is currently down.
bool multilineInsertChar(int charsToMove)
Inserts a blank character and move specified amount of characters to the right.
int getCursorCol()
Gets current cursor column.
bool setChar(uint8_t c)
Sets a raw character at current cursor position.
void setTerminal(Terminal *terminal=nullptr)
Sets destination terminal.
Delegate< int * > onRead
Read character delegate.
int getCursorRow()
Gets current cursor row.
Delegate< int > onWrite
Write character delegate.
void getCursorPos(int *col, int *row)
Gets current cursor position.
void enableCursor(bool value)
Enables/disables cursor.
void setBackgroundColor(Color value)
Sets background color.
void clear()
Clears screen.
void setCursorPos(int col, int row)
Sets current cursor position.
void setTerminalType(TermType value)
Sets the terminal type to emulate.
void setForegroundColor(Color value)
Sets foreground color.
void setCharStyle(CharStyle style, bool enabled)
Enables or disables specified character style.
TerminalController allows direct controlling of the Terminal object without using escape sequences.
An ANSI-VT100 compatible display terminal.
Represents the base abstract class for textual display controllers.
Emulates VIC6561 (VIC20) noise generator.
GlyphOptions & Underline(bool value)
Helper method to set or reset underlined.
GlyphOptions & Italic(bool value)
Helper method to set or reset italic.
GlyphOptions & Bold(bool value)
Helper method to set or reset bold.
#define FABGLIB_TERMINAL_OUTPUT_QUEUE_SIZE
#define FABGLIB_DEFAULT_TERMINAL_INPUT_CONSUMER_TASK_STACK_SIZE
#define FABGLIB_DEFAULT_TERMINAL_KEYBOARD_READER_TASK_STACK_SIZE
#define FABGLIB_DEFAULT_BLINK_PERIOD_MS
#define FABGLIB_MAX_DCS_CONTENT
#define FABGLIB_MAX_CSI_PARAMS
#define FABGLIB_KEYBOARD_READER_TASK_PRIORITY
#define FABGLIB_DEFAULT_TERMINAL_INPUT_QUEUE_SIZE
#define FABGLIB_CHARS_CONSUMER_TASK_PRIORITY
This file contains some utility classes and functions.
TerminalTransition
This enum defines terminal transition effect.
CharStyle
This enum defines a character style.
TermType
This enum defines supported terminals.
Color
This enum defines named colors.
@ CursorPointerSimpleReduced
VirtualKey
Represents each possible real or derived (SHIFT + real) key.
FlowControl
This enum defines various serial port flow control methods.
PixelFormat
This enum defines a pixel format.
This file contains fabgl::Mouse definition.
This file contains fabgl::Terminal definition.