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 uart_dev_t * 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 uart_dev_t * uart = (
volatile uart_dev_t *)(DR_REG_UART2_BASE);
472 while (uart->status.txfifo_cnt || uart->status.st_utx_out)
478static void uartFlushRXFIFO()
480 uart_dev_t * 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 uart_dev_t * uart = (
volatile uart_dev_t *)(DR_REG_UART2_BASE);
491 if (uxQueueMessagesWaiting(m_inputQueue) == 0 && uart->int_ena.rxfifo_full == 0) {
494 uart->int_ena.rxfifo_full = 1;
500void Terminal::setRTSStatus(
bool value)
502 if (m_rtsPin != GPIO_UNUSED) {
504 gpio_set_level(m_rtsPin, !value);
510void Terminal::flowControl(
bool enableRX)
513 uart_dev_t * uart = (
volatile uart_dev_t *) DR_REG_UART2_BASE;
515 if (m_flowControl == FlowControl::Software || m_flowControl == FlowControl::Hardsoft)
516 uart->flow_conf.send_xon = 1;
517 if (m_flowControl == FlowControl::Hardware || m_flowControl == FlowControl::Hardsoft)
521 if (m_flowControl == FlowControl::Software || m_flowControl == FlowControl::Hardsoft)
522 uart->flow_conf.send_xoff = 1;
523 if (m_flowControl == FlowControl::Hardware || m_flowControl == FlowControl::Hardsoft)
531bool Terminal::flowControl()
534 if ((m_flowControl == FlowControl::Software || m_flowControl == FlowControl::Hardsoft) && m_recvXOFF)
536 if ((m_flowControl == FlowControl::Hardware || m_flowControl == FlowControl::Hardsoft) && CTSStatus() ==
false)
543void Terminal::connectSerialPort(uint32_t baud, uint32_t config,
int rxPin,
int txPin,
FlowControl flowControl,
bool inverted,
int rtsPin,
int ctsPin)
545 uart_dev_t * uart = (
volatile uart_dev_t *) DR_REG_UART2_BASE;
547 bool initialSetup = !m_uart;
558 DPORT_SET_PERI_REG_MASK(DPORT_PERIP_CLK_EN_REG, DPORT_UART2_CLK_EN);
559 DPORT_CLEAR_PERI_REG_MASK(DPORT_PERIP_RST_EN_REG, DPORT_UART2_RST);
566 configureGPIO(int2gpio(rxPin), GPIO_MODE_INPUT);
567 configureGPIO(int2gpio(txPin), GPIO_MODE_OUTPUT);
570 m_rtsPin = int2gpio(rtsPin);
571 if (m_rtsPin != GPIO_UNUSED) {
572 configureGPIO(m_rtsPin, GPIO_MODE_OUTPUT);
577 m_ctsPin = int2gpio(ctsPin);
578 if (m_ctsPin != GPIO_UNUSED) {
579 configureGPIO(m_ctsPin, GPIO_MODE_INPUT);
583 uart->conf1.rxfifo_full_thrhd = 1;
584 uart->conf1.rx_tout_thrhd = 2;
585 uart->conf1.rx_tout_en = 0;
586 uart->int_ena.rxfifo_full = 1;
587 uart->int_ena.frm_err = 1;
588 uart->int_ena.rxfifo_tout = 0;
589 uart->int_ena.parity_err = 1;
590 uart->int_ena.rxfifo_ovf = 1;
591 uart->int_clr.val = 0xffffffff;
592 esp_intr_alloc(ETS_UART2_INTR_SOURCE, 0, uart_isr,
this,
nullptr);
595 uart->mem_conf.rx_size = 3;
596 uart->mem_conf.tx_size = 1;
598 if (!m_keyboardReaderTaskHandle && m_keyboard->isKeyboardAvailable())
602 m_flowControl = flowControl;
605 uint32_t clk_div = (getApbFrequency() << 4) / baud;
606 uart->clk_div.div_int = clk_div >> 4;
607 uart->clk_div.div_frag = clk_div & 0xf;
610 uart->conf0.val = config;
611 if (uart->conf0.stop_bit_num == 0x3) {
612 uart->conf0.stop_bit_num = 1;
613 uart->rs485_conf.dl1_en = 1;
617 gpio_matrix_in(rxPin, U2RXD_IN_IDX, inverted);
618 gpio_matrix_out(txPin, U2TXD_OUT_IDX, inverted,
false);
621 uart->flow_conf.sw_flow_con_en = 0;
622 uart->flow_conf.xonoff_del = 0;
623 if (flowControl != FlowControl::None) {
626 uart->swfc_conf.xon_threshold = 0;
627 uart->swfc_conf.xoff_threshold = 0;
628 uart->swfc_conf.xon_char = ASCII_XON;
629 uart->swfc_conf.xoff_char = ASCII_XOFF;
633 uart->flow_conf.send_xon = 1;
642void Terminal::connectLocally()
645 if (!m_keyboardReaderTaskHandle && m_keyboard->isKeyboardAvailable())
650void Terminal::disconnectLocally()
653 vQueueDelete(m_outputQueue);
654 m_outputQueue =
nullptr;
658void Terminal::logFmt(
const char * format, ...)
662 va_start(ap, format);
663 int size = vsnprintf(
nullptr, 0, format, ap) + 1;
666 va_start(ap, format);
668 vsnprintf(buf, size, format, ap);
669 m_logStream->write(buf);
676void Terminal::log(
const char * txt)
679 m_logStream->write(txt);
683void Terminal::log(
char c)
686 m_logStream->write(c);
690void Terminal::freeFont()
692 #if FABGLIB_CACHE_FONT_IN_RAM
694 free((
void*) m_font.data);
695 m_font.data =
nullptr;
701void Terminal::freeTabStops()
703 if (m_emuState.tabStop) {
704 free(m_emuState.tabStop);
705 m_emuState.tabStop =
nullptr;
710void Terminal::freeGlyphsMap()
712 if (m_glyphsBuffer.map) {
713 free((
void*) m_glyphsBuffer.map);
714 m_glyphsBuffer.map =
nullptr;
716 if (m_alternateMap) {
717 free((
void*) m_alternateMap);
718 m_alternateMap =
nullptr;
723void Terminal::reset()
725 #if FABGLIB_TERMINAL_DEBUG_REPORT_DESCS
729 xSemaphoreTake(m_mutex, portMAX_DELAY);
730 m_resetRequested =
false;
732 m_emuState.originMode =
false;
733 m_emuState.wraparound =
true;
734 m_emuState.insertMode =
false;
735 m_emuState.newLineMode =
false;
736 m_emuState.smoothScroll =
false;
737 m_emuState.keypadMode = KeypadMode::Numeric;
738 m_emuState.cursorKeysMode =
false;
739 m_emuState.keyAutorepeat =
true;
740 m_emuState.cursorBlinkingEnabled =
true;
741 m_emuState.cursorStyle = 0;
742 m_emuState.allow132ColumnMode =
false;
743 m_emuState.reverseWraparoundMode =
false;
744 m_emuState.backarrowKeyMode =
false;
745 m_emuState.ANSIMode =
true;
746 m_emuState.VT52GraphicsMode =
false;
747 m_emuState.allowFabGLSequences = 1;
748 m_emuState.characterSetIndex = 0;
749 for (
int i = 0; i < 4; ++i)
750 m_emuState.characterSet[i] = 1;
754 m_blinkingTextVisible =
false;
755 m_blinkingTextEnabled =
true;
757 m_cursorState =
false;
759 m_convMatchedCount = 0;
760 m_convMatchedItem =
nullptr;
763 setScrollingRegion(1, m_rows);
767 m_glyphOptions = (GlyphOptions) {{
770 .reduceLuminosity = 0,
780 m_canvas->setGlyphOptions(m_glyphOptions);
782 m_paintOptions = PaintOptions();
786 int_setBackgroundColor(m_defaultBackgroundColor);
787 int_setForegroundColor(m_defaultForegroundColor);
789 clearSavedCursorStates();
793 xSemaphoreGive(m_mutex);
797void Terminal::loadFont(FontInfo
const * font)
799 #if FABGLIB_TERMINAL_DEBUG_REPORT_DESCS
805 if (m_bitmappedDisplayController) {
812 #if FABGLIB_CACHE_FONT_IN_RAM
813 int size = m_font.height * 256 * ((m_font.width + 7) / 8);
814 m_font.data = (uint8_t
const*) malloc(size);
815 memcpy((
void*)m_font.data, font->data, size);
817 m_font.data = font->data;
820 m_columns = m_canvas->getWidth() / m_font.width;
821 m_rows = m_canvas->getHeight() / m_font.height;
823 m_glyphsBuffer.glyphsWidth = m_font.width;
824 m_glyphsBuffer.glyphsHeight = m_font.height;
825 m_glyphsBuffer.glyphsData = m_font.data;
827 codepage = m_font.codepage;
835 m_columns = txtctrl->getColumns();
836 m_rows = txtctrl->getRows();
837 codepage = txtctrl->getFont()->codepage;
842 m_keyboard->setCodePage(CodePages::get(codepage));
845 if (m_maxColumns > 0 && m_maxColumns < m_columns)
846 m_columns = m_maxColumns;
847 if (m_maxRows > 0 && m_maxRows < m_rows)
851 m_emuState.tabStop = (uint8_t*) malloc(m_columns);
855 m_glyphsBuffer.columns = m_columns;
856 m_glyphsBuffer.rows = m_rows;
858 m_glyphsBuffer.map = (uint32_t*) heap_caps_malloc(
sizeof(uint32_t) * m_columns * m_rows, MALLOC_CAP_8BIT | MALLOC_CAP_INTERNAL);
859 if (m_glyphsBuffer.map)
865 m_alternateMap =
nullptr;
866 m_alternateScreenBuffer =
false;
868 if (!m_bitmappedDisplayController && isActive()) {
873 setScrollingRegion(1, m_rows);
878void Terminal::flush(
bool waitVSync)
880 #if FABGLIB_TERMINAL_DEBUG_REPORT_DESCS
883 if (m_bitmappedDisplayController && isActive()) {
884 while (uxQueueMessagesWaiting(m_inputQueue) > 0)
886 m_canvas->waitCompletion(waitVSync);
893void Terminal::set132ColumnMode(
bool value)
895 #if FABGLIB_TERMINAL_DEBUG_REPORT_DESCS
896 log(
"set132ColumnMode()\n");
899 if (m_bitmappedDisplayController) {
905 loadFont(getPresetFontInfo(m_canvas->getWidth(), m_canvas->getHeight(), (value ? 132 : 80), 25));
911void Terminal::setBackgroundColor(
Color color,
bool setAsDefault)
914 m_defaultBackgroundColor = color;
919void Terminal::int_setBackgroundColor(
Color color)
921 m_emuState.backgroundColor = color;
923 if (m_bitmappedDisplayController)
924 m_canvas->setBrushColor(color);
931void Terminal::setForegroundColor(
Color color,
bool setAsDefault)
934 m_defaultForegroundColor = color;
939void Terminal::int_setForegroundColor(
Color color)
941 m_emuState.foregroundColor = color;
943 if (m_bitmappedDisplayController)
944 m_canvas->setPenColor(color);
951int CharStyleToColorAttributeIndex(
CharStyle attribute)
955 return COLORED_ATTRIBUTE_BOLD;
957 return COLORED_ATTRIBUTE_FAINT;
959 return COLORED_ATTRIBUTE_ITALIC;
961 return COLORED_ATTRIBUTE_UNDERLINE;
963 return COLORED_ATTRIBUTE_INVALID;
968void Terminal::setColorForAttribute(
CharStyle attribute,
Color color,
bool maintainStyle)
970 int cindex = CharStyleToColorAttributeIndex(attribute);
971 if (cindex != COLORED_ATTRIBUTE_INVALID) {
972 m_coloredAttributesMask |= 1 << cindex;
973 m_coloredAttributesColor[cindex] = color;
974 m_coloredAttributesMaintainStyle = maintainStyle;
981 int cindex = CharStyleToColorAttributeIndex(attribute);
982 if (cindex != COLORED_ATTRIBUTE_INVALID)
983 m_coloredAttributesMask &= ~(1 << cindex);
987void Terminal::reverseVideo(
bool value)
989 if (m_paintOptions.swapFGBG != value) {
990 m_paintOptions.swapFGBG = value;
992 if (m_bitmappedDisplayController) {
993 m_canvas->setPaintOptions(m_paintOptions);
994 m_canvas->swapRectangle(0, 0, m_canvas->getWidth() - 1, m_canvas->getHeight() - 1);
1001void Terminal::clear(
bool moveCursor)
1009void Terminal::int_clear()
1011 #if FABGLIB_TERMINAL_DEBUG_REPORT_DESCS
1012 log(
"int_clear()\n");
1015 if (m_bitmappedDisplayController && isActive())
1017 clearMap(m_glyphsBuffer.map);
1021void Terminal::clearMap(uint32_t * map)
1023 uint32_t itemValue = GLYPHMAP_ITEM_MAKE(ASCII_SPC, m_emuState.backgroundColor, m_emuState.foregroundColor, m_glyphOptions);
1024 uint32_t * mapItemPtr = map;
1025 for (
int row = 0; row < m_rows; ++row)
1026 for (
int col = 0; col < m_columns; ++col, ++mapItemPtr)
1027 *mapItemPtr = itemValue;
1032bool Terminal::moveUp()
1034 #if FABGLIB_TERMINAL_DEBUG_REPORT_DESCS
1038 if (m_emuState.cursorY == m_emuState.scrollingRegionTop)
1040 setCursorPos(m_emuState.cursorX, m_emuState.cursorY - 1);
1046bool Terminal::moveDown()
1048 #if FABGLIB_TERMINAL_DEBUG_REPORT_DESCS
1049 log(
"moveDown()\n");
1052 if (m_emuState.cursorY == m_emuState.scrollingRegionDown || m_emuState.cursorY == m_rows)
1054 setCursorPos(m_emuState.cursorX, m_emuState.cursorY + 1);
1060void Terminal::move(
int offset)
1062 int pos = m_emuState.cursorX - 1 + (m_emuState.cursorY - 1) * m_columns + offset;
1063 int newY = pos / m_columns + 1;
1064 int newX = pos % m_columns + 1;
1065 if (newY < m_emuState.scrollingRegionTop) {
1067 newY = m_emuState.scrollingRegionTop;
1069 if (newY > m_emuState.scrollingRegionDown) {
1071 newY = m_emuState.scrollingRegionDown;
1073 setCursorPos(newX, newY);
1077void Terminal::setCursorPos(
int X,
int Y)
1079 m_emuState.cursorX = tclamp(
X, 1, (
int)m_columns);
1080 m_emuState.cursorY = tclamp(
Y, 1, (
int)m_rows);
1081 m_emuState.cursorPastLastCol =
false;
1083 if (!m_bitmappedDisplayController && isActive())
1084 static_cast<TextualDisplayController*
>(m_displayController)->setCursorPos(m_emuState.cursorY - 1, m_emuState.cursorX - 1);
1086 #if FABGLIB_TERMINAL_DEBUG_REPORT_DESCSALL
1087 logFmt(
"setCursorPos(%d, %d) => set to (%d, %d)\n",
X,
Y, m_emuState.cursorX, m_emuState.cursorY);
1092int Terminal::getAbsoluteRow(
int Y)
1094 #if FABGLIB_TERMINAL_DEBUG_REPORT_DESCS
1095 logFmt(
"getAbsoluteRow(%d)\n",
Y);
1098 if (m_emuState.originMode) {
1099 Y += m_emuState.scrollingRegionTop - 1;
1100 Y = tclamp(
Y, m_emuState.scrollingRegionTop, m_emuState.scrollingRegionDown);
1106void Terminal::enableCursor(
bool value)
1112bool Terminal::int_enableCursor(
bool value)
1114 bool prev = m_emuState.cursorEnabled;
1115 if (m_emuState.cursorEnabled != value) {
1116 m_emuState.cursorEnabled = value;
1117 if (m_bitmappedDisplayController) {
1119 if (m_emuState.cursorEnabled) {
1120 if (uxQueueMessagesWaiting(m_inputQueue) == 0) {
1125 if (m_cursorState) {
1130 }
else if (isActive()) {
1132 static_cast<TextualDisplayController*
>(m_displayController)->enableCursor(value);
1139bool Terminal::enableBlinkingText(
bool value)
1141 bool prev = m_blinkingTextEnabled;
1142 m_blinkingTextEnabled = value;
1148void Terminal::blinkTimerFunc(TimerHandle_t xTimer)
1150 Terminal * term = (Terminal*) pvTimerGetTimerID(xTimer);
1152 if (term->isActive() && xSemaphoreTake(term->m_mutex, 0) == pdTRUE) {
1154 if (term->m_emuState.cursorEnabled && term->m_emuState.cursorBlinkingEnabled)
1155 term->blinkCursor();
1158 if (term->m_blinkingTextEnabled)
1161 xSemaphoreGive(term->m_mutex);
1167void Terminal::blinkCursor()
1169 if (m_bitmappedDisplayController && isActive()) {
1170 m_cursorState = !m_cursorState;
1171 int X = (m_emuState.cursorX - 1) * m_font.width;
1172 int Y = (m_emuState.cursorY - 1) * m_font.height;
1173 switch (m_emuState.cursorStyle) {
1176 m_canvas->swapRectangle(
X,
Y,
X + m_font.width - 1,
Y + m_font.height - 1);
1180 m_canvas->swapRectangle(
X,
Y + m_font.height - 2,
X + m_font.width - 1,
Y + m_font.height - 1);
1184 m_canvas->swapRectangle(
X,
Y,
X + 1,
Y + m_font.height - 1);
1191void Terminal::blinkText()
1194 m_blinkingTextVisible = !m_blinkingTextVisible;
1195 bool keepEnabled =
false;
1197 int cols = m_columns;
1198 if (m_bitmappedDisplayController)
1199 m_canvas->beginUpdate();
1200 for (
int y = 0; y < rows; ++y) {
1201 uint32_t * itemPtr = m_glyphsBuffer.map + y * cols;
1202 for (
int x = 0; x < cols; ++x, ++itemPtr) {
1204 GlyphOptions glyphOptions = glyphMapItem_getOptions(itemPtr);
1205 if (glyphOptions.userOpt1) {
1206 glyphOptions.blank = !m_blinkingTextVisible;
1207 glyphMapItem_setOptions(itemPtr, glyphOptions);
1208 refresh(x + 1, y + 1);
1212 if (m_bitmappedDisplayController)
1213 m_canvas->waitCompletion(
false);
1215 if (m_bitmappedDisplayController)
1216 m_canvas->endUpdate();
1218 m_blinkingTextEnabled =
false;
1223void Terminal::nextTabStop()
1225 int actualColumns = m_columns;
1228 if (getGlyphOptionsAt(1, m_emuState.cursorY).doubleWidth)
1231 int x = m_emuState.cursorX;
1232 while (x < actualColumns) {
1234 if (m_emuState.tabStop[x - 1])
1237 setCursorPos(x, m_emuState.cursorY);
1242void Terminal::resetTabStops()
1244 for (
int i = 0; i < m_columns; ++i)
1245 m_emuState.tabStop[i] = (i > 0 && (i % 8) == 0 ? 1 : 0);
1250void Terminal::setTabStop(
int column,
bool set)
1252 #if FABGLIB_TERMINAL_DEBUG_REPORT_DESCS
1253 logFmt(
"setTabStop %d %d\n", column, (
int)set);
1257 memset(m_emuState.tabStop, 0, m_columns);
1259 m_emuState.tabStop[column - 1] = set ? 1 : 0;
1263void Terminal::scrollDown()
1265 #if FABGLIB_TERMINAL_DEBUG_REPORT_DESCS
1266 log(
"scrollDown\n");
1269 if (m_bitmappedDisplayController && isActive()) {
1271 if (m_emuState.smoothScroll) {
1272 for (
int i = 0; i < m_font.height; ++i)
1273 m_canvas->scroll(0, 1);
1275 m_canvas->scroll(0, m_font.height);
1279 for (
int y = m_emuState.scrollingRegionDown - 1; y > m_emuState.scrollingRegionTop - 1; --y)
1280 memcpy(m_glyphsBuffer.map + y * m_columns, m_glyphsBuffer.map + (y - 1) * m_columns, m_columns *
sizeof(uint32_t));
1283 uint32_t itemValue = GLYPHMAP_ITEM_MAKE(ASCII_SPC, m_emuState.backgroundColor, m_emuState.foregroundColor, m_glyphOptions);
1284 uint32_t * itemPtr = m_glyphsBuffer.map + (m_emuState.scrollingRegionTop - 1) * m_columns;
1285 for (
int x = 0; x < m_columns; ++x, ++itemPtr)
1286 *itemPtr = itemValue;
1292void Terminal::scrollDownAt(
int startingRow)
1294 int prevScrollingRegionTop = m_emuState.scrollingRegionTop;
1295 setScrollingRegion(startingRow, m_emuState.scrollingRegionDown,
false);
1299 setScrollingRegion(prevScrollingRegionTop, m_emuState.scrollingRegionDown,
false);
1303void Terminal::scrollUp()
1305 #if FABGLIB_TERMINAL_DEBUG_REPORT_DESCS
1309 auto ldown = m_emuState.scrollingRegionDown;
1310 if (m_emuState.cursorY == m_rows) {
1312 m_emuState.scrollingRegionDown = m_rows;
1313 updateCanvasScrollingRegion();
1316 if (m_bitmappedDisplayController && isActive()) {
1318 if (m_emuState.smoothScroll) {
1319 for (
int i = 0; i < m_font.height; ++i)
1320 m_canvas->scroll(0, -1);
1322 m_canvas->scroll(0, -m_font.height);
1326 for (
int y = m_emuState.scrollingRegionTop - 1; y < m_emuState.scrollingRegionDown - 1; ++y)
1327 memcpy(m_glyphsBuffer.map + y * m_columns, m_glyphsBuffer.map + (y + 1) * m_columns, m_columns *
sizeof(uint32_t));
1330 uint32_t itemValue = GLYPHMAP_ITEM_MAKE(ASCII_SPC, m_emuState.backgroundColor, m_emuState.foregroundColor, m_glyphOptions);
1331 uint32_t * itemPtr = m_glyphsBuffer.map + (m_emuState.scrollingRegionDown - 1) * m_columns;
1332 for (
int x = 0; x < m_columns; ++x, ++itemPtr)
1333 *itemPtr = itemValue;
1335 if (ldown != m_emuState.scrollingRegionDown) {
1336 m_emuState.scrollingRegionDown = ldown;
1337 updateCanvasScrollingRegion();
1342void Terminal::scrollUpAt(
int startingRow)
1344 int prevScrollingRegionTop = m_emuState.scrollingRegionTop;
1345 setScrollingRegion(startingRow, m_emuState.scrollingRegionDown,
false);
1349 setScrollingRegion(prevScrollingRegionTop, m_emuState.scrollingRegionDown,
false);
1353void Terminal::setScrollingRegion(
int top,
int down,
bool resetCursorPos)
1355 m_emuState.scrollingRegionTop = tclamp(top, 1, (
int)m_rows);
1356 m_emuState.scrollingRegionDown = tclamp(down, 1, (
int)m_rows);
1357 updateCanvasScrollingRegion();
1360 setCursorPos(1, m_emuState.originMode ? m_emuState.scrollingRegionTop : 1);
1362 #if FABGLIB_TERMINAL_DEBUG_REPORT_DESCS
1363 logFmt(
"setScrollingRegion: %d %d => %d %d\n", top, down, m_emuState.scrollingRegionTop, m_emuState.scrollingRegionDown);
1368void Terminal::updateCanvasScrollingRegion()
1370 if (m_bitmappedDisplayController && isActive())
1371 m_canvas->setScrollingRegion(0, (m_emuState.scrollingRegionTop - 1) * m_font.height, m_canvas->getWidth() - 1, m_emuState.scrollingRegionDown * m_font.height - 1);
1378bool Terminal::multilineInsertChar(
int charsToMove)
1380 bool scrolled =
false;
1381 int col = m_emuState.cursorX;
1382 int row = m_emuState.cursorY;
1383 if (m_emuState.cursorPastLastCol) {
1387 uint32_t lastColItem = 0;
1388 while (charsToMove > 0) {
1389 uint32_t * rowPtr = m_glyphsBuffer.map + (row - 1) * m_columns;
1390 uint32_t lItem = rowPtr[m_columns - 1];
1391 insertAt(col, row, 1);
1392 if (row > m_emuState.cursorY) {
1393 rowPtr[0] = lastColItem;
1396 lastColItem = lItem;
1397 charsToMove -= m_columns - col;
1399 if (charsToMove > 0 && row == m_emuState.scrollingRegionDown) {
1402 setCursorPos(m_emuState.cursorX, m_emuState.cursorY - 1);
1406 if (m_bitmappedDisplayController && isActive())
1407 m_canvas->waitCompletion(
false);
1414void Terminal::insertAt(
int column,
int row,
int count)
1416 #if FABGLIB_TERMINAL_DEBUG_REPORT_DESCS
1417 logFmt(
"insertAt(%d, %d, %d)\n", column, row, count);
1420 count = tmin((
int)m_columns, count);
1422 if (m_bitmappedDisplayController && isActive()) {
1424 int charWidth = getCharWidthAt(row);
1425 m_canvas->setScrollingRegion((column - 1) * charWidth, (row - 1) * m_font.height, charWidth * getColumnsAt(row) - 1, row * m_font.height - 1);
1426 m_canvas->scroll(count * charWidth, 0);
1427 updateCanvasScrollingRegion();
1431 uint32_t * rowPtr = m_glyphsBuffer.map + (row - 1) * m_columns;
1432 for (
int i = m_columns - 1; i >= column + count - 1; --i)
1433 rowPtr[i] = rowPtr[i - count];
1436 GlyphOptions glyphOptions = m_glyphOptions;
1437 glyphOptions.doubleWidth = glyphMapItem_getOptions(rowPtr).
doubleWidth;
1438 uint32_t itemValue = GLYPHMAP_ITEM_MAKE(ASCII_SPC, m_emuState.backgroundColor, m_emuState.foregroundColor, glyphOptions);
1439 for (
int i = 0; i < count; ++i)
1440 rowPtr[column + i - 1] = itemValue;
1444void Terminal::multilineDeleteChar(
int charsToMove)
1446 int col = m_emuState.cursorX;
1447 int row = m_emuState.cursorY;
1448 if (m_emuState.cursorPastLastCol) {
1454 if (charsToMove == 0)
1455 deleteAt(col, row, 1);
1457 while (charsToMove > 0) {
1458 deleteAt(col, row, 1);
1459 charsToMove -= m_columns - col;
1460 if (charsToMove > 0) {
1461 if (m_bitmappedDisplayController && isActive())
1462 m_canvas->waitCompletion(
false);
1463 uint32_t * lastItem = m_glyphsBuffer.map + (row - 1) * m_columns + (m_columns - 1);
1464 lastItem[0] = lastItem[1];
1465 refresh(m_columns, row);
1469 if (m_bitmappedDisplayController && isActive())
1470 m_canvas->waitCompletion(
false);
1476void Terminal::deleteAt(
int column,
int row,
int count)
1478 #if FABGLIB_TERMINAL_DEBUG_REPORT_DESCS
1479 logFmt(
"deleteAt(%d, %d, %d)\n", column, row, count);
1482 count = imin(m_columns - column + 1, count);
1484 if (m_bitmappedDisplayController && isActive()) {
1486 int charWidth = getCharWidthAt(row);
1487 m_canvas->setScrollingRegion((column - 1) * charWidth, (row - 1) * m_font.height, charWidth * getColumnsAt(row) - 1, row * m_font.height - 1);
1488 m_canvas->scroll(-count * charWidth, 0);
1489 updateCanvasScrollingRegion();
1493 uint32_t * rowPtr = m_glyphsBuffer.map + (row - 1) * m_columns;
1494 int itemsToMove = m_columns - column - count + 1;
1495 for (
int i = 0; i < itemsToMove; ++i)
1496 rowPtr[column - 1 + i] = rowPtr[column - 1 + i + count];
1499 GlyphOptions glyphOptions = m_glyphOptions;
1500 glyphOptions.doubleWidth = glyphMapItem_getOptions(rowPtr).
doubleWidth;
1501 uint32_t itemValue = GLYPHMAP_ITEM_MAKE(ASCII_SPC, m_emuState.backgroundColor, m_emuState.foregroundColor, glyphOptions);
1502 for (
int i = m_columns - count + 1 ; i <= m_columns; ++i)
1503 rowPtr[i - 1] = itemValue;
1510void Terminal::erase(
int X1,
int Y1,
int X2,
int Y2, uint8_t c,
bool maintainDoubleWidth,
bool selective)
1512 #if FABGLIB_TERMINAL_DEBUG_REPORT_DESCS
1513 logFmt(
"erase(%d, %d, %d, %d, %d, %d)\n",
X1,
Y1,
X2,
Y2, (
int)c, (
int)maintainDoubleWidth);
1516 X1 = tclamp(
X1 - 1, 0, (
int)m_columns - 1);
1517 Y1 = tclamp(
Y1 - 1, 0, (
int)m_rows - 1);
1518 X2 = tclamp(
X2 - 1, 0, (
int)m_columns - 1);
1519 Y2 = tclamp(
Y2 - 1, 0, (
int)m_rows - 1);
1521 if (m_bitmappedDisplayController && isActive()) {
1522 if (c == ASCII_SPC && !selective) {
1523 int charWidth = getCharWidthAt(m_emuState.cursorY);
1524 m_canvas->fillRectangle(
X1 * charWidth,
Y1 * m_font.height, (
X2 + 1) * charWidth - 1, (
Y2 + 1) * m_font.height - 1);
1528 GlyphOptions glyphOptions = {.value = 0};
1529 glyphOptions.fillBackground = 1;
1531 for (
int y =
Y1; y <=
Y2; ++y) {
1532 uint32_t * itemPtr = m_glyphsBuffer.map +
X1 + y * m_columns;
1533 for (
int x =
X1; x <=
X2; ++x, ++itemPtr) {
1534 if (selective && glyphMapItem_getOptions(itemPtr).
userOpt2)
1536 glyphOptions.doubleWidth = maintainDoubleWidth ? glyphMapItem_getOptions(itemPtr).
doubleWidth : 0;
1537 *itemPtr = GLYPHMAP_ITEM_MAKE(c, m_emuState.backgroundColor, m_emuState.foregroundColor, glyphOptions);
1540 if (c != ASCII_SPC || selective)
1541 refresh(
X1 + 1,
Y1 + 1,
X2 + 1,
Y2 + 1);
1545void Terminal::enableFabGLSequences(
bool value)
1547 m_emuState.allowFabGLSequences += value ? 1 : -1;
1548 if (m_emuState.allowFabGLSequences < 0)
1549 m_emuState.allowFabGLSequences = 0;
1553void Terminal::clearSavedCursorStates()
1555 #if FABGLIB_TERMINAL_DEBUG_REPORT_DESCS
1556 log(
"clearSavedCursorStates()\n");
1559 for (TerminalCursorState * curItem = m_savedCursorStateList, * next; curItem; curItem = next) {
1560 next = curItem->next;
1561 free(curItem->tabStop);
1564 m_savedCursorStateList =
nullptr;
1568void Terminal::saveCursorState()
1570 #if FABGLIB_TERMINAL_DEBUG_REPORT_DESCS
1571 log(
"saveCursorState()\n");
1574 TerminalCursorState * s = (TerminalCursorState*) malloc(
sizeof(TerminalCursorState));
1577 *s = (TerminalCursorState) {
1578 .next = m_savedCursorStateList,
1579 .cursorX = (int16_t) m_emuState.cursorX,
1580 .cursorY = (int16_t) m_emuState.cursorY,
1581 .tabStop = (uint8_t*) malloc(m_columns),
1582 .cursorPastLastCol = m_emuState.cursorPastLastCol,
1583 .originMode = m_emuState.originMode,
1584 .glyphOptions = m_glyphOptions,
1585 .characterSetIndex = m_emuState.characterSetIndex,
1586 .characterSet = {m_emuState.characterSet[0], m_emuState.characterSet[1], m_emuState.characterSet[2], m_emuState.characterSet[3]},
1589 memcpy(s->tabStop, m_emuState.tabStop, m_columns);
1590 m_savedCursorStateList = s;
1592 #if FABGLIB_TERMINAL_DEBUG_REPORT_ERRORS
1593 log(
"ERROR: Unable to alloc TerminalCursorState\n");
1599void Terminal::restoreCursorState()
1601 #if FABGLIB_TERMINAL_DEBUG_REPORT_DESCS
1602 log(
"restoreCursorState()\n");
1605 if (m_savedCursorStateList) {
1606 m_emuState.cursorX = m_savedCursorStateList->cursorX;
1607 m_emuState.cursorY = m_savedCursorStateList->cursorY;
1608 m_emuState.cursorPastLastCol = m_savedCursorStateList->cursorPastLastCol;
1609 m_emuState.originMode = m_savedCursorStateList->originMode;
1610 if (m_savedCursorStateList->tabStop)
1611 memcpy(m_emuState.tabStop, m_savedCursorStateList->tabStop, m_columns);
1612 m_glyphOptions = m_savedCursorStateList->glyphOptions;
1613 if (m_bitmappedDisplayController && isActive())
1614 m_canvas->setGlyphOptions(m_glyphOptions);
1615 m_emuState.characterSetIndex = m_savedCursorStateList->characterSetIndex;
1616 for (
int i = 0; i < 4; ++i)
1617 m_emuState.characterSet[i] = m_savedCursorStateList->characterSet[i];
1619 TerminalCursorState * next = m_savedCursorStateList->next;
1621 free(m_savedCursorStateList->tabStop);
1622 free(m_savedCursorStateList);
1623 m_savedCursorStateList = next;
1628void Terminal::useAlternateScreenBuffer(
bool value)
1630 #if FABGLIB_TERMINAL_DEBUG_REPORT_DESCS
1631 logFmt(
"useAlternateScreenBuffer: %d\n", value);
1633 if (m_alternateScreenBuffer != value) {
1634 m_alternateScreenBuffer = value;
1635 if (!m_alternateMap) {
1637 m_alternateMap = (uint32_t*) heap_caps_malloc(
sizeof(uint32_t) * m_columns * m_rows, MALLOC_CAP_8BIT | MALLOC_CAP_INTERNAL);
1638 clearMap(m_alternateMap);
1639 m_alternateCursorX = 1;
1640 m_alternateCursorY = 1;
1641 m_alternateScrollingRegionTop = 1;
1642 m_alternateScrollingRegionDown = m_rows;
1643 m_alternateCursorBlinkingEnabled =
true;
1645 tswap(m_alternateMap, m_glyphsBuffer.map);
1646 tswap(m_emuState.cursorX, m_alternateCursorX);
1647 tswap(m_emuState.cursorY, m_alternateCursorY);
1648 tswap(m_emuState.scrollingRegionTop, m_alternateScrollingRegionTop);
1649 tswap(m_emuState.scrollingRegionDown, m_alternateScrollingRegionDown);
1650 tswap(m_emuState.cursorBlinkingEnabled, m_alternateCursorBlinkingEnabled);
1651 setScrollingRegion(m_emuState.scrollingRegionTop, m_emuState.scrollingRegionDown,
false);
1652 m_emuState.cursorPastLastCol =
false;
1658void Terminal::localInsert(uint8_t c)
1661 xQueueSendToFront(m_outputQueue, &c, portMAX_DELAY);
1665void Terminal::localWrite(uint8_t c)
1668 xQueueSendToBack(m_outputQueue, &c, portMAX_DELAY);
1672void Terminal::localWrite(
char const * str)
1674 if (m_outputQueue) {
1676 xQueueSendToBack(m_outputQueue, str, portMAX_DELAY);
1678 #if FABGLIB_TERMINAL_DEBUG_REPORT_OUT_CODES
1679 logFmt(
"=> %02X %s%c\n", (
int)*str, (*str <= ASCII_SPC ? CTRLCHAR_TO_STR[(
int)(*str)] :
""), (*str > ASCII_SPC ? *str : ASCII_SPC));
1688int Terminal::available()
1690 return m_outputQueue ? uxQueueMessagesWaiting(m_outputQueue) : 0;
1700int Terminal::read(
int timeOutMS)
1702 if (m_outputQueue) {
1704 xQueueReceive(m_outputQueue, &c, msToTicks(timeOutMS));
1711bool Terminal::waitFor(
int value,
int timeOutMS)
1714 while (!timeout.expired(timeOutMS)) {
1715 int c = read(timeOutMS);
1730void Terminal::flush()
1737void Terminal::pollSerialPort()
1740 int avail = m_serialPort->available();
1742 if (m_flowControl == FlowControl::Software) {
1745 if (avail < FABGLIB_TERMINAL_XON_THRESHOLD) {
1751 if (avail >= FABGLIB_TERMINAL_XOFF_THRESHOLD) {
1761 auto r = m_serialPort->read();
1762 if (m_uartRXEnabled)
1769void IRAM_ATTR Terminal::uart_isr(
void *arg)
1772 uart_dev_t * uart = (
volatile uart_dev_t *)(DR_REG_UART2_BASE);
1775 if (uart->int_st.rxfifo_ovf || uart->int_st.frm_err || uart->int_st.parity_err) {
1779 uart->int_clr.rxfifo_ovf = 1;
1780 uart->int_clr.frm_err = 1;
1781 uart->int_clr.parity_err = 1;
1786 if (term->m_flowControl != FlowControl::None) {
1788 int count = uartGetRXFIFOCount();
1789 if (count > FABGLIB_TERMINAL_FLOWCONTROL_RXFIFO_MAX_THRESHOLD && !term->m_sentXOFF)
1791 else if (count < FABGLIB_TERMINAL_FLOWCONTROL_RXFIFO_MIN_THRESHOLD && term->m_sentXOFF)
1796 while (uartGetRXFIFOCount() != 0 || uart->mem_rx_status.wr_addr != uart->mem_rx_status.rd_addr) {
1798 if (term->m_flowControl != FlowControl::None && xQueueIsQueueFullFromISR(term->m_inputQueue)) {
1799 if (!term->m_sentXOFF)
1802 uart->int_ena.rxfifo_full = 0;
1806 auto r = uart->fifo.rw_byte;
1807 if (term->m_uartRXEnabled)
1808 term->
write(r,
true);
1812 uart->int_clr.rxfifo_full = 1;
1817void Terminal::send(uint8_t c)
1819 #if FABGLIB_TERMINAL_DEBUG_REPORT_OUT_CODES
1820 logFmt(
"=> %02X %s%c\n", (
int)c, (c <= ASCII_SPC ? CTRLCHAR_TO_STR[(
int)c] :
""), (c > ASCII_SPC ? c : ASCII_SPC));
1825 while (m_serialPort->availableForWrite() == 0)
1826 vTaskDelay(1 / portTICK_PERIOD_MS);
1827 m_serialPort->write(c);
1834 uart_dev_t * uart = (
volatile uart_dev_t *)(DR_REG_UART2_BASE);
1835 while (uart->status.txfifo_cnt == 0x7F)
1837 uart->fifo.rw_byte = c;
1845void Terminal::send(
char const * str)
1850 while (m_serialPort->availableForWrite() == 0)
1851 vTaskDelay(1 / portTICK_PERIOD_MS);
1852 m_serialPort->write(*str);
1854 #if FABGLIB_TERMINAL_DEBUG_REPORT_OUT_CODES
1855 logFmt(
"=> %02X %s%c\n", (
int)*str, (*str <= ASCII_SPC ? CTRLCHAR_TO_STR[(
int)(*str)] :
""), (*str > ASCII_SPC ? *str : ASCII_SPC));
1864 uart_dev_t * uart = (
volatile uart_dev_t *)(DR_REG_UART2_BASE);
1868 while (uart->status.txfifo_cnt == 0x7F)
1870 uart->fifo.rw_byte = *str++;
1878void Terminal::sendCSI()
1880 send(m_emuState.ctrlBits == 7 ? CSI_7BIT : CSI_8BIT);
1884void Terminal::sendDCS()
1886 send(m_emuState.ctrlBits == 7 ? DCS_7BIT : DCS_8BIT);
1890void Terminal::sendSS3()
1892 send(m_emuState.ctrlBits == 7 ? SS3_7BIT : SS3_8BIT);
1896int Terminal::availableForWrite()
1898 return uxQueueSpacesAvailable(m_inputQueue);
1902bool Terminal::addToInputQueue(uint8_t c,
bool fromISR)
1905 return xQueueSendToBackFromISR(m_inputQueue, &c,
nullptr);
1907 return xQueueSendToBack(m_inputQueue, &c, portMAX_DELAY);
1911bool Terminal::insertToInputQueue(uint8_t c,
bool fromISR)
1914 return xQueueSendToFrontFromISR(m_inputQueue, &c,
nullptr);
1916 return xQueueSendToFront(m_inputQueue, &c, portMAX_DELAY);
1920void Terminal::write(uint8_t c,
bool fromISR)
1922 if (m_termInfo ==
nullptr || m_writeDetectedFabGLSeq)
1923 addToInputQueue(c, fromISR);
1925 convHandleTranslation(c, fromISR);
1928 if (m_writeDetectedFabGLSeq) {
1929 if (c == FABGLEXT_ENDCODE)
1930 m_writeDetectedFabGLSeq =
false;
1931 }
else if (m_emuState.allowFabGLSequences && m_lastWrittenChar == ASCII_ESC && c == FABGLEXT_STARTCODE) {
1932 m_writeDetectedFabGLSeq =
true;
1935 m_lastWrittenChar = c;
1937 #if FABGLIB_TERMINAL_DEBUG_REPORT_IN_CODES
1938 logFmt(
"<= %02X %s%c\n", (
int)c, (c <= ASCII_SPC ? CTRLCHAR_TO_STR[(
int)c] :
""), (c > ASCII_SPC ? c : ASCII_SPC));
1943size_t Terminal::write(uint8_t c)
1950size_t Terminal::write(
const uint8_t * buffer,
size_t size)
1952 for (
int i = 0; i < size; ++i)
1965void Terminal::int_setTerminalType(TermInfo
const * value)
1968 m_emuState.ANSIMode =
true;
1969 m_emuState.conformanceLevel = 4;
1971 m_termInfo =
nullptr;
1973 if (value !=
nullptr) {
1975 auto s = value->initString;
1976 for (
int i = strlen(s) - 1; i >= 0; --i)
1977 insertToInputQueue(s[i],
false);
1984void Terminal::int_setTerminalType(
TermType value)
1988 int_setTerminalType(
nullptr);
1991 int_setTerminalType(&term_ADM3A);
1994 int_setTerminalType(&term_ADM31);
1997 int_setTerminalType(&term_Hazeltine1500);
2000 int_setTerminalType(&term_Osborne);
2003 int_setTerminalType(&term_Kaypro);
2006 int_setTerminalType(&term_VT52);
2009 int_setTerminalType(&term_ANSILegacy);
2015void Terminal::convHandleTranslation(uint8_t c,
bool fromISR)
2017 if (m_convMatchedCount > 0 || c < 32 || c == 0x7f || c ==
'~') {
2019 m_convMatchedChars[m_convMatchedCount] = c;
2021 if (m_convMatchedItem ==
nullptr)
2022 m_convMatchedItem = m_termInfo->videoCtrlSet;
2024 for (
auto item = m_convMatchedItem; item->termSeq; ++item) {
2025 if (item != m_convMatchedItem) {
2027 if (m_convMatchedCount == 0 || (item->termSeqLen > m_convMatchedCount && strncmp(item->termSeq, m_convMatchedItem->termSeq, m_convMatchedCount) == 0))
2028 m_convMatchedItem = item;
2033 if (item->termSeq[m_convMatchedCount] == 0xFF || item->termSeq[m_convMatchedCount] == c) {
2035 ++m_convMatchedCount;
2036 if (item->termSeqLen == m_convMatchedCount) {
2038 for (ConvCtrl
const * ctrl = item->convCtrl; *ctrl != ConvCtrl::END; ++ctrl)
2039 convSendCtrl(*ctrl, fromISR);
2046 convQueue(
nullptr, fromISR);
2048 addToInputQueue(c, fromISR);
2052void Terminal::convSendCtrl(ConvCtrl ctrl,
bool fromISR)
2055 case ConvCtrl::CarriageReturn:
2056 convQueue(
"\x0d", fromISR);
2058 case ConvCtrl::LineFeed:
2059 convQueue(
"\x0a", fromISR);
2061 case ConvCtrl::CursorLeft:
2062 convQueue(
"\e[D", fromISR);
2064 case ConvCtrl::CursorUp:
2065 convQueue(
"\e[A", fromISR);
2067 case ConvCtrl::CursorRight:
2068 convQueue(
"\e[C", fromISR);
2070 case ConvCtrl::EraseToEndOfScreen:
2071 convQueue(
"\e[J", fromISR);
2073 case ConvCtrl::EraseToEndOfLine:
2074 convQueue(
"\e[K", fromISR);
2076 case ConvCtrl::CursorHome:
2077 convQueue(
"\e[H", fromISR);
2079 case ConvCtrl::AttrNormal:
2080 convQueue(
"\e[0m", fromISR);
2082 case ConvCtrl::AttrBlank:
2083 convQueue(
"\e[8m", fromISR);
2085 case ConvCtrl::AttrBlink:
2086 convQueue(
"\e[5m", fromISR);
2088 case ConvCtrl::AttrBlinkOff:
2089 convQueue(
"\e[25m", fromISR);
2091 case ConvCtrl::AttrReverse:
2092 convQueue(
"\e[7m", fromISR);
2094 case ConvCtrl::AttrReverseOff:
2095 convQueue(
"\e[27m", fromISR);
2097 case ConvCtrl::AttrUnderline:
2098 convQueue(
"\e[4m", fromISR);
2100 case ConvCtrl::AttrUnderlineOff:
2101 convQueue(
"\e[24m", fromISR);
2103 case ConvCtrl::AttrReduce:
2104 convQueue(
"\e[2m", fromISR);
2106 case ConvCtrl::AttrReduceOff:
2107 convQueue(
"\e[22m", fromISR);
2109 case ConvCtrl::InsertLine:
2110 convQueue(
"\e[L", fromISR);
2112 case ConvCtrl::InsertChar:
2113 convQueue(
"\e[@", fromISR);
2115 case ConvCtrl::DeleteLine:
2116 convQueue(
"\e[M", fromISR);
2118 case ConvCtrl::DeleteCharacter:
2119 convQueue(
"\e[P", fromISR);
2121 case ConvCtrl::CursorOn:
2122 convQueue(
"\e[?25h", fromISR);
2124 case ConvCtrl::CursorOff:
2125 convQueue(
"\e[?25l", fromISR);
2127 case ConvCtrl::SaveCursor:
2128 convQueue(
"\e[?1048h", fromISR);
2130 case ConvCtrl::RestoreCursor:
2131 convQueue(
"\e[?1048l", fromISR);
2133 case ConvCtrl::CursorPos:
2134 case ConvCtrl::CursorPos2:
2137 int y = (ctrl == ConvCtrl::CursorPos ? m_convMatchedChars[2] - 31 : m_convMatchedChars[3] + 1);
2138 int x = (ctrl == ConvCtrl::CursorPos ? m_convMatchedChars[3] - 31 : m_convMatchedChars[2] + 1);
2139 sprintf(s,
"\e[%d;%dH", y, x);
2140 convQueue(s, fromISR);
2151void Terminal::convQueue(
const char * str,
bool fromISR)
2155 addToInputQueue(*str, fromISR);
2157 for (
int i = 0; i <= m_convMatchedCount; ++i) {
2158 addToInputQueue(m_convMatchedChars[i], fromISR);
2161 m_convMatchedCount = 0;
2162 m_convMatchedItem =
nullptr;
2166uint32_t Terminal::makeGlyphItem(uint8_t c, GlyphOptions * glyphOptions,
Color * newForegroundColor)
2168 *newForegroundColor = m_emuState.foregroundColor;
2170 if (glyphOptions->bold && (m_coloredAttributesMask & (1 << COLORED_ATTRIBUTE_BOLD))) {
2171 *newForegroundColor = m_coloredAttributesColor[COLORED_ATTRIBUTE_BOLD];
2172 if (!m_coloredAttributesMaintainStyle)
2173 glyphOptions->bold = 0;
2176 if (glyphOptions->reduceLuminosity && (m_coloredAttributesMask & (1 << COLORED_ATTRIBUTE_FAINT))) {
2177 *newForegroundColor = m_coloredAttributesColor[COLORED_ATTRIBUTE_FAINT];
2178 if (!m_coloredAttributesMaintainStyle)
2179 glyphOptions->reduceLuminosity = 0;
2182 if (glyphOptions->italic && (m_coloredAttributesMask & (1 << COLORED_ATTRIBUTE_ITALIC))) {
2183 *newForegroundColor = m_coloredAttributesColor[COLORED_ATTRIBUTE_ITALIC];
2184 if (!m_coloredAttributesMaintainStyle)
2185 glyphOptions->italic = 0;
2188 if (glyphOptions->underline && (m_coloredAttributesMask & (1 << COLORED_ATTRIBUTE_UNDERLINE))) {
2189 *newForegroundColor = m_coloredAttributesColor[COLORED_ATTRIBUTE_UNDERLINE];
2190 if (!m_coloredAttributesMaintainStyle)
2191 glyphOptions->underline = 0;
2194 return GLYPHMAP_ITEM_MAKE(c, m_emuState.backgroundColor, *newForegroundColor, *glyphOptions);
2200bool Terminal::setChar(uint8_t c)
2202 bool vscroll =
false;
2204 if (m_emuState.cursorPastLastCol) {
2205 if (m_emuState.wraparound) {
2206 setCursorPos(1, m_emuState.cursorY);
2214 if (m_emuState.insertMode)
2215 insertAt(m_emuState.cursorX, m_emuState.cursorY, 1);
2217 GlyphOptions glyphOptions = m_glyphOptions;
2220 uint32_t * mapItemPtr = m_glyphsBuffer.map + (m_emuState.cursorX - 1) + (m_emuState.cursorY - 1) * m_columns;
2221 glyphOptions.doubleWidth = glyphMapItem_getOptions(mapItemPtr).
doubleWidth;
2222 Color newForegroundColor;
2223 *mapItemPtr = makeGlyphItem(c, &glyphOptions, &newForegroundColor);
2225 if (m_bitmappedDisplayController && isActive()) {
2231 if (glyphOptions.value != m_glyphOptions.value)
2232 m_canvas->setGlyphOptions(glyphOptions);
2233 if (newForegroundColor != m_emuState.foregroundColor)
2234 m_canvas->setPenColor(newForegroundColor);
2236 int x = (m_emuState.cursorX - 1) * m_font.width * (glyphOptions.doubleWidth ? 2 : 1);
2237 int y = (m_emuState.cursorY - 1) * m_font.height;
2238 m_canvas->drawGlyph(x, y, m_font.width, m_font.height, m_font.data, c);
2240 if (newForegroundColor != m_emuState.foregroundColor)
2241 m_canvas->setPenColor(m_emuState.foregroundColor);
2242 if (glyphOptions.value != m_glyphOptions.value)
2243 m_canvas->setGlyphOptions(m_glyphOptions);
2247 if (m_glyphOptions.userOpt1)
2248 m_prevBlinkingTextEnabled =
true;
2250 if (m_emuState.cursorX == m_columns) {
2251 m_emuState.cursorPastLastCol =
true;
2253 setCursorPos(m_emuState.cursorX + 1, m_emuState.cursorY);
2260void Terminal::refresh()
2262 #if FABGLIB_TERMINAL_DEBUG_REPORT_DESCS
2266 refresh(1, 1, m_columns, m_rows);
2271void Terminal::refresh(
int X,
int Y)
2273 #if FABGLIB_TERMINAL_DEBUG_REPORT_DESCS
2274 logFmt(
"refresh(%d, %d)\n",
X,
Y);
2277 if (m_bitmappedDisplayController && isActive())
2278 m_canvas->renderGlyphsBuffer(
X - 1,
Y - 1, &m_glyphsBuffer);
2282void Terminal::refresh(
int X1,
int Y1,
int X2,
int Y2)
2284 #if FABGLIB_TERMINAL_DEBUG_REPORT_DESCS
2285 logFmt(
"refresh(%d, %d, %d, %d)\n",
X1,
Y1,
X2,
Y2);
2288 if (m_bitmappedDisplayController && isActive()) {
2289 for (
int y =
Y1 - 1; y <
Y2; ++y) {
2290 for (
int x =
X1 - 1; x <
X2; ++x)
2291 m_canvas->renderGlyphsBuffer(x, y, &m_glyphsBuffer);
2292 m_canvas->waitCompletion(
false);
2299void Terminal::setLineDoubleWidth(
int row,
int value)
2301 #if FABGLIB_TERMINAL_DEBUG_REPORT_DESCS
2302 logFmt(
"setLineDoubleWidth(%d, %d)\n", row, value);
2305 uint32_t * mapItemPtr = m_glyphsBuffer.map + (row - 1) * m_columns;
2306 for (
int i = 0; i < m_columns; ++i, ++mapItemPtr) {
2307 GlyphOptions glyphOptions = glyphMapItem_getOptions(mapItemPtr);
2308 glyphOptions.doubleWidth = value;
2309 glyphMapItem_setOptions(mapItemPtr, glyphOptions);
2312 refresh(1, row, m_columns, row);
2316int Terminal::getCharWidthAt(
int row)
2318 return glyphMapItem_getOptions(m_glyphsBuffer.map + (row - 1) * m_columns).
doubleWidth ? m_font.width * 2 : m_font.width;
2322int Terminal::getColumnsAt(
int row)
2324 return glyphMapItem_getOptions(m_glyphsBuffer.map + (row - 1) * m_columns).
doubleWidth ? m_columns / 2 : m_columns;
2328GlyphOptions Terminal::getGlyphOptionsAt(
int X,
int Y)
2330 return glyphMapItem_getOptions(m_glyphsBuffer.map + (
X - 1) + (
Y - 1) * m_columns);
2335uint8_t Terminal::getNextCode(
bool processCtrlCodes)
2339 xQueueReceive(m_inputQueue, &c, portMAX_DELAY);
2341 #if FABGLIB_TERMINAL_DEBUG_REPORT_INQUEUE_CODES
2342 logFmt(
"<= %02X %s%c\n", (
int)c, (c <= ASCII_SPC ? CTRLCHAR_TO_STR[(
int)c] :
""), (c > ASCII_SPC ? c : ASCII_SPC));
2346 uartCheckInputQueueForFlowControl();
2349 if (processCtrlCodes && ISCTRLCHAR(c))
2357void Terminal::charsConsumerTask(
void * pvParameters)
2359 Terminal * term = (Terminal*) pvParameters;
2362 term->consumeInputQueue();
2366void Terminal::consumeInputQueue()
2368 uint8_t c = getNextCode(
false);
2370 xSemaphoreTake(m_mutex, portMAX_DELAY);
2372 m_prevCursorEnabled = int_enableCursor(
false);
2373 m_prevBlinkingTextEnabled = enableBlinkingText(
false);
2380 else if (ISCTRLCHAR(c))
2384 if (m_emuState.characterSet[m_emuState.characterSetIndex] == 0 || (!m_emuState.ANSIMode && m_emuState.VT52GraphicsMode))
2385 c = DECGRAPH_TO_CP437[(uint8_t)c];
2389 enableBlinkingText(m_prevBlinkingTextEnabled);
2390 int_enableCursor(m_prevCursorEnabled);
2392 xSemaphoreGive(m_mutex);
2394 if (m_resetRequested)
2399void Terminal::execCtrlCode(uint8_t c)
2406 if (m_emuState.cursorX > 1)
2407 setCursorPos(m_emuState.cursorX - 1, m_emuState.cursorY);
2408 else if (m_emuState.reverseWraparoundMode) {
2409 int newX = m_columns;
2410 int newY = m_emuState.cursorY - 1;
2413 setCursorPos(newX, newY);
2426 if (!m_emuState.cursorPastLastCol) {
2427 if (m_emuState.newLineMode)
2428 setCursorPos(1, m_emuState.cursorY);
2446 setCursorPos(1, m_emuState.cursorY);
2452 m_emuState.characterSetIndex = 1;
2458 m_emuState.characterSetIndex = 0;
2467 sound(
'1', 800, 250, 100);
2489void Terminal::consumeESC()
2492 if (!m_emuState.ANSIMode) {
2497 uint8_t c = getNextCode(
true);
2511 if (c == FABGLEXT_STARTCODE && m_emuState.allowFabGLSequences > 0) {
2523 #if FABGLIB_TERMINAL_DEBUG_REPORT_ESC
2524 logFmt(
"ESC%c\n", c);
2531 m_resetRequested =
true;
2542 setCursorPos(1, m_emuState.cursorY);
2549 setTabStop(m_emuState.cursorX,
true);
2572 restoreCursorState();
2577 c = getNextCode(
true);
2581 setLineDoubleWidth(m_emuState.cursorY, 2);
2585 setLineDoubleWidth(m_emuState.cursorY, 3);
2589 setLineDoubleWidth(m_emuState.cursorY, 0);
2593 setLineDoubleWidth(m_emuState.cursorY, 1);
2597 erase(1, 1, m_columns, m_rows,
'E',
false,
false);
2610 switch (getNextCode(
true)) {
2613 m_emuState.characterSet[c -
'('] = 0;
2616 m_emuState.characterSet[c -
'('] = 1;
2623 m_emuState.keypadMode = KeypadMode::Application;
2624 #if FABGLIB_TERMINAL_DEBUG_REPORT_DESCSALL
2625 log(
"Keypad Application Mode\n");
2631 m_emuState.keypadMode = KeypadMode::Numeric;
2632 #if FABGLIB_TERMINAL_DEBUG_REPORT_DESCSALL
2633 log(
"Keypad Numeric Mode\n");
2638 switch (getNextCode(
true)) {
2642 m_emuState.ctrlBits = 7;
2647 if (m_emuState.conformanceLevel >= 2 && m_emuState.ANSIMode)
2648 m_emuState.ctrlBits = 8;
2655 #if FABGLIB_TERMINAL_DEBUG_REPORT_UNSUPPORT
2656 logFmt(
"Unknown ESC %c\n", c);
2666uint8_t Terminal::consumeParamsAndGetCode(
int * params,
int * paramsCount,
bool * questionMarkFound)
2670 *questionMarkFound =
false;
2674 uint8_t c = getNextCode(
true);
2676 #if FABGLIB_TERMINAL_DEBUG_REPORT_ESC
2681 *questionMarkFound =
true;
2686 if (!isdigit(c) && c !=
';') {
2688 #if FABGLIB_TERMINAL_DEBUG_REPORT_ESC
2706 *p = *p * 10 + (c -
'0');
2714void Terminal::consumeCSI()
2716 #if FABGLIB_TERMINAL_DEBUG_REPORT_ESC
2720 bool questionMarkFound;
2723 uint8_t c = consumeParamsAndGetCode(params, ¶msCount, &questionMarkFound);
2727 if (questionMarkFound && (c ==
'h' || c ==
'l')) {
2728 consumeDECPrivateModes(params, paramsCount, c);
2733 if (c == ASCII_SPC) {
2734 consumeCSISPC(params, paramsCount);
2740 consumeCSIQUOT(params, paramsCount);
2751 setCursorPos(params[1], getAbsoluteRow(params[0]));
2756 switch (params[0]) {
2758 setTabStop(m_emuState.cursorX,
false);
2761 setTabStop(0,
false);
2768 setCursorPos(m_emuState.cursorX + tmax(1, params[0]), m_emuState.cursorY);
2773 deleteAt(m_emuState.cursorX, m_emuState.cursorY, tmax(1, params[0]));
2778 setCursorPos(m_emuState.cursorX, getAbsoluteRow(m_emuState.cursorY - tmax(1, params[0])));
2788 switch (params[0]) {
2790 erase(m_emuState.cursorX, m_emuState.cursorY, m_columns, m_emuState.cursorY, ASCII_SPC,
false, questionMarkFound);
2791 erase(1, m_emuState.cursorY + 1, m_columns, m_rows, ASCII_SPC,
false, questionMarkFound);
2794 erase(1, 1, m_columns, m_emuState.cursorY - 1, ASCII_SPC,
false, questionMarkFound);
2795 erase(1, m_emuState.cursorY, m_emuState.cursorX, m_emuState.cursorY, ASCII_SPC,
false, questionMarkFound);
2798 erase(1, 1, m_columns, m_rows, ASCII_SPC,
false, questionMarkFound);
2810 switch (params[0]) {
2812 erase(m_emuState.cursorX, m_emuState.cursorY, m_columns, m_emuState.cursorY, ASCII_SPC,
true, questionMarkFound);
2815 erase(1, m_emuState.cursorY, m_emuState.cursorX, m_emuState.cursorY, ASCII_SPC,
true, questionMarkFound);
2818 erase(1, m_emuState.cursorY, m_columns, m_emuState.cursorY, ASCII_SPC,
true, questionMarkFound);
2826 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);
2831 setScrollingRegion(tmax(params[0], 1), (params[1] < 1 ? m_rows : params[1]));
2836 setCursorPos(m_emuState.cursorX, params[0]);
2841 setCursorPos(params[0], m_emuState.cursorY);
2846 for (
int i = tmax(1, params[0]); i > 0; --i)
2852 for (
int i = tmax(1, params[0]); i > 0; --i)
2859 int newX = m_emuState.cursorX - tmax(1, params[0]);
2860 if (m_emuState.reverseWraparoundMode && newX < 1) {
2862 int newY = m_emuState.cursorY - newX / m_columns - 1;
2864 newY = m_rows + newY;
2865 newX = m_columns - (newX % m_columns);
2866 setCursorPos(newX, newY);
2868 setCursorPos(tmax(1, newX), m_emuState.cursorY);
2874 setCursorPos(m_emuState.cursorX, getAbsoluteRow(m_emuState.cursorY + tmax(1, params[0])));
2879 execSGRParameters(params, paramsCount);
2884 for (
int i = tmax(1, params[0]); i > 0; --i)
2885 scrollDownAt(m_emuState.cursorY);
2890 for (
int i = tmax(1, params[0]); i > 0; --i)
2891 scrollUpAt(m_emuState.cursorY);
2898 switch (params[0]) {
2902 m_emuState.insertMode = (c ==
'h');
2907 m_emuState.newLineMode = (c ==
'h');
2911 #if FABGLIB_TERMINAL_DEBUG_REPORT_UNSUPPORT
2912 logFmt(
"Unknown: ESC [ %d %c\n", params[0], c);
2920 insertAt(m_emuState.cursorX, m_emuState.cursorY, tmax(1, params[0]));
2925 if (params[0] == 0) {
2933 paramsCount = tmax(1, paramsCount);
2934 for (
int i = 0; i < paramsCount; ++i) {
2935 bool numLock, capsLock, scrollLock;
2936 m_keyboard->getLEDs(&numLock, &capsLock, &scrollLock);
2937 switch (params[i]) {
2939 numLock = capsLock = scrollLock =
false;
2960 m_keyboard->setLEDs(numLock, capsLock, scrollLock);
2966 switch (params[0]) {
2977 send(itoa(m_emuState.originMode ? m_emuState.cursorY - m_emuState.scrollingRegionTop + 1 : m_emuState.cursorY, s, 10));
2979 send(itoa(m_emuState.cursorX, s, 10));
2987 #if FABGLIB_TERMINAL_DEBUG_REPORT_UNSUPPORT
2988 log(
"Unknown: ESC [ ");
2989 if (questionMarkFound)
2991 for (
int i = 0; i < paramsCount; ++i)
2992 logFmt(
"%d %c ", params[i], i < paramsCount - 1 ?
';' : c);
3001void Terminal::consumeCSIQUOT(
int * params,
int paramsCount)
3003 uint8_t c = getNextCode(
true);
3009 m_emuState.conformanceLevel = params[0] - 60;
3010 if (params[0] == 61 || (paramsCount == 2 && params[1] == 1))
3011 m_emuState.ctrlBits = 7;
3013 m_emuState.ctrlBits = 8;
3018 m_glyphOptions.userOpt2 = (params[0] == 1 ? 1 : 0);
3026void Terminal::consumeCSISPC(
int * params,
int paramsCount)
3028 uint8_t c = getNextCode(
true);
3035 m_emuState.cursorStyle = params[0];
3036 m_emuState.cursorBlinkingEnabled = (params[0] == 0) || (params[0] & 1);
3040 #if FABGLIB_TERMINAL_DEBUG_REPORT_UNSUPPORT
3041 log(
"Unknown: ESC [ ");
3042 for (
int i = 0; i < paramsCount; ++i)
3043 logFmt(
"%d %c ", params[i], i < paramsCount - 1 ?
';' : ASCII_SPC);
3055void Terminal::consumeDECPrivateModes(
int const * params,
int paramsCount, uint8_t c)
3057 bool set = (c ==
'h');
3058 switch (params[0]) {
3064 m_emuState.cursorKeysMode = set;
3071 m_emuState.ANSIMode = set;
3078 if (m_emuState.allow132ColumnMode) {
3079 set132ColumnMode(set);
3088 m_emuState.smoothScroll = set;
3103 m_emuState.originMode = set;
3105 setCursorPos(m_emuState.cursorX, m_emuState.scrollingRegionTop);
3112 m_emuState.wraparound = set;
3119 m_emuState.keyAutorepeat = set;
3126 m_emuState.cursorBlinkingEnabled = set;
3133 m_prevCursorEnabled = set;
3140 m_emuState.allow132ColumnMode = set;
3147 m_emuState.reverseWraparoundMode = set;
3157 useAlternateScreenBuffer(set);
3164 m_emuState.backarrowKeyMode = set;
3173 restoreCursorState();
3182 useAlternateScreenBuffer(
true);
3184 useAlternateScreenBuffer(
false);
3185 restoreCursorState();
3194 enableFabGLSequences(set);
3198 #if FABGLIB_TERMINAL_DEBUG_REPORT_UNSUPPORT
3199 logFmt(
"Unknown DECSET/DECRST: %d %c\n", params[0], c);
3208void Terminal::execSGRParameters(
int const * params,
int paramsCount)
3210 for (; paramsCount; ++params, --paramsCount) {
3215 m_glyphOptions.bold = 0;
3216 m_glyphOptions.reduceLuminosity = 0;
3217 m_glyphOptions.italic = 0;
3218 m_glyphOptions.underline = 0;
3219 m_glyphOptions.userOpt1 = 0;
3220 m_glyphOptions.blank = 0;
3221 m_glyphOptions.invert = 0;
3222 int_setForegroundColor(m_defaultForegroundColor);
3223 int_setBackgroundColor(m_defaultBackgroundColor);
3228 m_glyphOptions.bold = 1;
3233 m_glyphOptions.reduceLuminosity = 1;
3238 m_emuState.characterSetIndex = 0;
3243 m_glyphOptions.bold = m_glyphOptions.reduceLuminosity = 0;
3248 m_glyphOptions.italic = 1;
3253 m_glyphOptions.italic = 0;
3258 m_glyphOptions.underline = 1;
3263 m_glyphOptions.underline = 0;
3268 m_glyphOptions.userOpt1 = 1;
3273 m_glyphOptions.userOpt1 = 0;
3278 m_glyphOptions.invert = 1;
3283 m_glyphOptions.invert = 0;
3288 m_glyphOptions.blank = 1;
3293 m_glyphOptions.blank = 0;
3298 int_setForegroundColor( (
Color) (*params - 30) );
3303 int_setForegroundColor(m_defaultForegroundColor);
3308 int_setBackgroundColor( (
Color) (*params - 40) );
3313 int_setBackgroundColor(m_defaultBackgroundColor);
3318 int_setForegroundColor( (
Color) (8 + *params - 90) );
3323 int_setBackgroundColor( (
Color) (8 + *params - 100) );
3327 #if FABGLIB_TERMINAL_DEBUG_REPORT_UNSUPPORT
3328 logFmt(
"Unknown: ESC [ %d m\n", *params);
3334 if (m_bitmappedDisplayController && isActive())
3335 m_canvas->setGlyphOptions(m_glyphOptions);
3341void Terminal::consumeDCS()
3343 #if FABGLIB_TERMINAL_DEBUG_REPORT_ESC
3348 bool questionMarkFound;
3351 uint8_t c = consumeParamsAndGetCode(params, ¶msCount, &questionMarkFound);
3355 int contentLength = 0;
3356 content[contentLength++] = c;
3358 uint8_t c = getNextCode(
false);
3359 if (c == ASCII_ESC) {
3360 if (getNextCode(
false) ==
'\\')
3363 #if FABGLIB_TERMINAL_DEBUG_REPORT_UNSUPPORT
3364 log(
"DCS failed, expected ST\n");
3369 #if FABGLIB_TERMINAL_DEBUG_REPORT_UNSUPPORT
3370 log(
"DCS failed, content too long\n");
3374 content[contentLength++] = c;
3378 if (m_emuState.conformanceLevel >= 3 && contentLength > 2 && content[0] ==
'$' && content[1] ==
'q') {
3383 if (contentLength == 4 && content[2] ==
'\"' && content[3] ==
'p') {
3386 send(
'0' + m_emuState.conformanceLevel);
3388 send(m_emuState.ctrlBits == 7 ?
'1' :
'0');
3395 #if FABGLIB_TERMINAL_DEBUG_REPORT_UNSUPPORT
3396 log(
"Unknown: ESC P ");
3397 for (
int i = 0; i < paramsCount; ++i)
3398 logFmt(
"%d %c ", params[i], i < paramsCount - 1 ?
';' : ASCII_SPC);
3399 logFmt(
"%.*s ESC \\\n", contentLength, content);
3404void Terminal::consumeESCVT52()
3406 uint8_t c = getNextCode(
false);
3408 #if FABGLIB_TERMINAL_DEBUG_REPORT_ESC
3409 logFmt(
"ESC%c\n", c);
3413 if (c == FABGLEXT_STARTCODE && m_emuState.allowFabGLSequences > 0) {
3423 m_emuState.ANSIMode =
true;
3424 m_emuState.conformanceLevel = 1;
3429 setCursorPos(m_emuState.cursorX, m_emuState.cursorY - 1);
3434 setCursorPos(m_emuState.cursorX, m_emuState.cursorY + 1);
3439 setCursorPos(m_emuState.cursorX + 1, m_emuState.cursorY);
3444 setCursorPos(m_emuState.cursorX -1, m_emuState.cursorY);
3460 erase(m_emuState.cursorX, m_emuState.cursorY, m_columns, m_emuState.cursorY, ASCII_SPC,
false,
false);
3461 erase(1, m_emuState.cursorY + 1, m_columns, m_rows, ASCII_SPC,
false,
false);
3466 erase(m_emuState.cursorX, m_emuState.cursorY, m_columns, m_emuState.cursorY, ASCII_SPC,
true,
false);
3472 int row = getNextCode(
false) - 31;
3473 int col = getNextCode(
false) - 31;
3474 setCursorPos(col, row);
3485 m_emuState.keypadMode = KeypadMode::Application;
3486 #if FABGLIB_TERMINAL_DEBUG_REPORT_DESCSALL
3487 log(
"Enter Alternate Keypad Mode\n");
3493 m_emuState.keypadMode = KeypadMode::Numeric;
3494 #if FABGLIB_TERMINAL_DEBUG_REPORT_DESCSALL
3495 log(
"Exit Alternate Keypad Mode\n");
3501 m_emuState.VT52GraphicsMode =
true;
3506 m_emuState.VT52GraphicsMode =
false;
3511 #if FABGLIB_TERMINAL_DEBUG_REPORT_UNSUPPORT
3512 logFmt(
"Unknown ESC %c\n", c);
3522void Terminal::consumeOSC()
3524 #if FABGLIB_TERMINAL_DEBUG_REPORT_ESC
3530 uint8_t c = getNextCode(
false);
3531 #if FABGLIB_TERMINAL_DEBUG_REPORT_OSC_CONTENT
3532 logFmt(
"OSC: %02X %s%c\n", (
int)c, (c <= ASCII_SPC ? CTRLCHAR_TO_STR[(
int)(c)] :
""), (c > ASCII_SPC ? c : ASCII_SPC));
3534 if (c == ASCII_BEL || (c ==
'\\' && prevChar == ASCII_ESC))
3543 if (!m_soundGenerator)
3545 return m_soundGenerator;
3549void Terminal::sound(
int waveform,
int frequency,
int duration,
int volume)
3551 auto sg = soundGenerator();
3576uint8_t Terminal::extGetByteParam()
3578 if (m_extNextCode > -1) {
3579 auto r = m_extNextCode;
3583 return getNextCode(
false);
3589int Terminal::extGetIntParam()
3594 uint8_t c = extGetByteParam();
3601 }
else if (c ==
'+' || c ==
' ') {
3609 }
else if (sign == -2)
3611 val = val * 10 + c -
'0';
3619void Terminal::extGetCmdParam(
char * cmd)
3622 for (; len < FABGLEXT_MAXSUBCMDLEN - 1; ++len) {
3623 uint8_t c = extGetByteParam();
3636void Terminal::consumeFabGLSeq()
3638 #if FABGLIB_TERMINAL_DEBUG_REPORT_ESC
3639 log(
"ESC FABGLEXT_STARTCODE");
3644 uint8_t c = extGetByteParam();
3654 case FABGLEXTX_CLEAR:
3656 syncDisplayController();
3657 erase(1, 1, m_columns, m_rows, ASCII_SPC,
false,
false);
3665 case FABGLEXTX_ENABLECURSOR:
3666 m_prevCursorEnabled = (extGetByteParam() ==
'1');
3678 case FABGLEXTB_GETCURSORCOL:
3680 send(FABGLEXT_REPLYCODE);
3681 send(m_emuState.cursorX);
3692 case FABGLEXTB_GETCURSORROW:
3694 send(FABGLEXT_REPLYCODE);
3695 send(m_emuState.cursorY);
3707 case FABGLEXTB_GETCURSORPOS:
3709 send(FABGLEXT_REPLYCODE);
3710 send(m_emuState.cursorX);
3711 send(m_emuState.cursorY);
3720 case FABGLEXTB_SETCURSORPOS:
3722 uint8_t col = extGetByteParam();
3723 uint8_t row = extGetByteParam();
3725 setCursorPos(col, getAbsoluteRow(row));
3735 case FABGLEXTX_SETCURSORPOS:
3737 uint8_t col = extGetIntParam();
3739 uint8_t row = extGetIntParam();
3741 setCursorPos(col, getAbsoluteRow(row));
3755 case FABGLEXTB_INSERTSPACE:
3757 uint8_t charsToMove_L = extGetByteParam();
3758 uint8_t charsToMove_H = extGetByteParam();
3760 bool scroll = multilineInsertChar(charsToMove_L | charsToMove_H << 8);
3761 send(FABGLEXT_REPLYCODE);
3772 case FABGLEXTB_DELETECHAR:
3774 uint8_t charsToMove_L = extGetByteParam();
3775 uint8_t charsToMove_H = extGetByteParam();
3777 multilineDeleteChar(charsToMove_L | charsToMove_H << 8);
3786 case FABGLEXTB_CURSORLEFT:
3788 uint8_t count_L = extGetByteParam();
3789 uint8_t count_H = extGetByteParam();
3791 move(-(count_L | count_H << 8));
3800 case FABGLEXTB_CURSORRIGHT:
3802 uint8_t count_L = extGetByteParam();
3803 uint8_t count_H = extGetByteParam();
3805 move(count_L | count_H << 8);
3818 case FABGLEXTB_SETCHAR:
3820 bool scroll = setChar(extGetByteParam());
3822 send(FABGLEXT_REPLYCODE);
3835 case FABGLEXTB_ISVKDOWN:
3839 send(FABGLEXT_REPLYCODE);
3840 send(keyboard()->isVKDown(vk) ?
'1' :
'0');
3847 case FABGLEXTB_DISABLEFABSEQ:
3849 enableFabGLSequences(
false);
3857 case FABGLEXTB_SETTERMTYPE:
3859 auto termType = (
TermType) extGetByteParam();
3861 int_setTerminalType(termType);
3870 case FABGLEXTB_SETFGCOLOR:
3871 int_setForegroundColor((
Color) extGetByteParam());
3880 case FABGLEXTB_SETBGCOLOR:
3881 int_setBackgroundColor((
Color) extGetByteParam());
3891 case FABGLEXTB_SETCHARSTYLE:
3893 int idx = extGetByteParam();
3894 int val = extGetByteParam();
3898 m_glyphOptions.bold = val;
3901 m_glyphOptions.reduceLuminosity = val;
3904 m_glyphOptions.italic = val;
3907 m_glyphOptions.underline = val;
3910 m_glyphOptions.userOpt1 = val;
3913 m_glyphOptions.blank = val;
3916 m_glyphOptions.invert = val;
3919 if (m_bitmappedDisplayController && isActive())
3920 m_canvas->setGlyphOptions(m_glyphOptions);
3936 case FABGLEXTX_SETUPGPIO:
3938 auto mode = GPIO_MODE_DISABLE;
3939 switch (extGetByteParam()) {
3941 mode = GPIO_MODE_INPUT;
3944 mode = GPIO_MODE_OUTPUT;
3947 mode = GPIO_MODE_OUTPUT_OD;
3950 mode = GPIO_MODE_INPUT_OUTPUT_OD;
3953 mode = GPIO_MODE_INPUT_OUTPUT;
3956 auto gpio = (gpio_num_t) extGetIntParam();
3958 configureGPIO(gpio, mode);
3968 case FABGLEXTX_SETGPIO:
3970 auto l = extGetByteParam();
3971 auto level = (l == 1 || l ==
'1' || l ==
'H') ? 1 : 0;
3972 auto gpio = (gpio_num_t) extGetIntParam();
3974 gpio_set_level(gpio, level);
3986 case FABGLEXTX_GETGPIO:
3988 auto gpio = (gpio_num_t) extGetIntParam();
3990 send(FABGLEXT_REPLYCODE);
3991 send(gpio_get_level(gpio) ?
'1' :
'0');
4006 case FABGLEXTX_SETUPADC:
4008 auto width = (adc_bits_width_t) (extGetIntParam() - 9);
4010 auto atten = (adc_atten_t) extGetIntParam();
4012 auto channel = ADC1_GPIO2Channel((gpio_num_t)extGetIntParam());
4014 adc1_config_width(
width);
4015 adc1_config_channel_atten(channel, atten);
4034 case FABGLEXTX_READADC:
4036 auto val = adc1_get_raw(ADC1_GPIO2Channel((gpio_num_t)extGetIntParam()));
4038 send(FABGLEXT_REPLYCODE);
4039 send(toupper(digit2hex((val & 0xF00) >> 8)));
4040 send(toupper(digit2hex((val & 0x0F0) >> 4)));
4041 send(toupper(digit2hex(val & 0x00F)));
4053 case FABGLEXTX_SOUND:
4055 char waveform = extGetByteParam();
4057 uint16_t frequency = extGetIntParam();
4059 uint16_t duration = extGetIntParam();
4061 uint8_t volume = extGetIntParam() & 0x7f;
4063 sound(waveform, frequency, duration, volume);
4070 case FABGLEXTX_GRAPHICSCMD:
4071 consumeFabGLGraphicsSeq();
4079 case FABGLEXTX_SHOWMOUSE:
4081 bool value = (extGetByteParam() ==
'1');
4082 if (m_bitmappedDisplayController) {
4083 auto dispctrl =
static_cast<BitmappedDisplayController*
>(m_displayController);
4084 auto mouse = PS2Controller::mouse();
4085 if (mouse && mouse->isMouseAvailable()) {
4087 mouse->setupAbsolutePositioner(m_canvas->getWidth(), m_canvas->getHeight(),
false, dispctrl);
4090 dispctrl->setMouseCursor(
nullptr);
4091 mouse->terminateAbsolutePositioner();
4113 case FABGLEXTX_GETMOUSEPOS:
4116 if (m_bitmappedDisplayController) {
4117 auto mouse = PS2Controller::mouse();
4118 auto x = mouse->status().X;
4119 auto y = mouse->status().Y;
4120 send(FABGLEXT_REPLYCODE);
4122 send(toupper(digit2hex((x & 0xF00) >> 8)));
4123 send(toupper(digit2hex((x & 0x0F0) >> 4)));
4124 send(toupper(digit2hex((x & 0x00F) )));
4127 send(toupper(digit2hex((y & 0xF00) >> 8)));
4128 send(toupper(digit2hex((y & 0x0F0) >> 4)));
4129 send(toupper(digit2hex((y & 0x00F) )));
4132 send(toupper(digit2hex(mouse->status().wheelDelta & 0xf)));
4135 auto b = mouse->status().buttons;
4136 send(toupper(digit2hex( b.left | (b.middle << 1) | (b.right << 2) )));
4148 case FABGLEXTX_DELAY:
4150 auto value = extGetIntParam();
4152 vTaskDelay(value / portTICK_PERIOD_MS);
4153 send(FABGLEXT_REPLYCODE);
4162 case FABGLEXT_USERSEQ:
4164 char usrseq[FABGLEXT_MAXSUBCMDLEN];
4166 while (count < FABGLEXT_MAXSUBCMDLEN) {
4167 char c = extGetByteParam();
4168 if (c == FABGLEXT_ENDCODE)
4170 usrseq[count++] = c;
4173 onUserSequence(usrseq);
4178 #if FABGLIB_TERMINAL_DEBUG_REPORT_UNSUPPORT
4179 logFmt(
"Unknown: ESC FABGLEXT_STARTCODE %02x\n", c);
4186void Terminal::freeSprites()
4188 for (
int i = 0; i < m_spritesCount; ++i) {
4189 for (
int j = 0; j < m_sprites[i].framesCount; ++j) {
4190 free(m_sprites[i].frames[j]->
data);
4191 delete m_sprites[i].frames[j];
4194 delete [] m_sprites;
4195 m_sprites =
nullptr;
4202void Terminal::consumeFabGLGraphicsSeq()
4204 char cmd[FABGLEXT_MAXSUBCMDLEN];
4205 extGetCmdParam(cmd);
4207 if (strcmp(cmd, FABGLEXT_GCLEAR) == 0) {
4218 }
else if (strcmp(cmd, FABGLEXT_GSETBRUSHCOLOR) == 0) {
4227 int r = extGetIntParam();
4229 int g = extGetIntParam();
4231 int b = extGetIntParam();
4234 m_canvas->setBrushColor(r, g, b);
4236 }
else if (strcmp(cmd, FABGLEXT_GSETPENCOLOR) == 0) {
4245 int r = extGetIntParam();
4247 int g = extGetIntParam();
4249 int b = extGetIntParam();
4252 m_canvas->setPenColor(r, g, b);
4254 }
else if (strcmp(cmd, FABGLEXT_GSETPIXEL) == 0) {
4262 int x = extGetIntParam();
4264 int y = extGetIntParam();
4267 m_canvas->setPixel(x, y);
4269 }
else if (strcmp(cmd, FABGLEXT_GSCROLL) == 0) {
4277 int ox = extGetIntParam();
4279 int oy = extGetIntParam();
4282 m_canvas->scroll(ox, oy);
4284 }
else if (strcmp(cmd, FABGLEXT_GPENWIDTH) == 0) {
4291 int w = extGetIntParam();
4294 m_canvas->setPenWidth(w);
4296 }
else if (strcmp(cmd, FABGLEXT_GLINE) == 0) {
4306 int x1 = extGetIntParam();
4308 int y1 = extGetIntParam();
4310 int x2 = extGetIntParam();
4312 int y2 = extGetIntParam();
4315 m_canvas->drawLine(x1, y1, x2, y2);
4317 }
else if (strcmp(cmd, FABGLEXT_GRECT) == 0) {
4327 int x1 = extGetIntParam();
4329 int y1 = extGetIntParam();
4331 int x2 = extGetIntParam();
4333 int y2 = extGetIntParam();
4336 m_canvas->drawRectangle(x1, y1, x2, y2);
4338 }
else if (strcmp(cmd, FABGLEXT_GFILLRECT) == 0) {
4348 int x1 = extGetIntParam();
4350 int y1 = extGetIntParam();
4352 int x2 = extGetIntParam();
4354 int y2 = extGetIntParam();
4357 m_canvas->fillRectangle(x1, y1, x2, y2);
4359 }
else if (strcmp(cmd, FABGLEXT_GELLIPSE) == 0) {
4369 int x = extGetIntParam();
4371 int y = extGetIntParam();
4373 int w = extGetIntParam();
4375 int h = extGetIntParam();
4378 m_canvas->drawEllipse(x, y, w, h);
4380 }
else if (strcmp(cmd, FABGLEXT_GFILLELLIPSE) == 0) {
4390 int x = extGetIntParam();
4392 int y = extGetIntParam();
4394 int w = extGetIntParam();
4396 int h = extGetIntParam();
4399 m_canvas->fillEllipse(x, y, w, h);
4401 }
else if (strcmp(cmd, FABGLEXT_GPATH) == 0) {
4411 constexpr int MAXPOINTS = 32;
4412 Point pts[MAXPOINTS];
4414 while (count < MAXPOINTS) {
4415 pts[count].X = extGetIntParam();
4417 pts[count].Y = extGetIntParam();
4419 if (extGetByteParam() == FABGLEXT_ENDCODE)
4423 m_canvas->drawPath(pts, count);
4425 }
else if (strcmp(cmd, FABGLEXT_GFILLPATH) == 0) {
4435 constexpr int MAXPOINTS = 32;
4436 Point pts[MAXPOINTS];
4438 while (count < MAXPOINTS) {
4439 pts[count].X = extGetIntParam();
4441 pts[count].Y = extGetIntParam();
4443 if (extGetByteParam() == FABGLEXT_ENDCODE)
4447 m_canvas->fillPath(pts, count);
4449 }
else if (strcmp(cmd, FABGLEXT_GSPRITECOUNT) == 0) {
4456 int count = extGetIntParam();
4458 if (m_bitmappedDisplayController) {
4459 static_cast<BitmappedDisplayController*
>(m_displayController)->setSprites<Sprite>(
nullptr, 0);
4462 m_spritesCount = count;
4463 m_sprites =
new Sprite[count];
4467 }
else if (strcmp(cmd, FABGLEXT_GSPRITEDEF) == 0) {
4481 int sprite = extGetIntParam();
4483 int width = extGetIntParam();
4485 int height = extGetIntParam();
4487 char cformat = extGetByteParam();
4489 int r = 0, g = 0, b = 0;
4494 r = extGetIntParam();
4496 g = extGetIntParam();
4498 b = extGetIntParam();
4501 format = PixelFormat::Mask;
4505 format = PixelFormat::RGBA2222;
4509 format = PixelFormat::RGBA8888;
4512 auto data = (uint8_t*) malloc(bytes);
4513 for (
int i = 0; i < bytes + 1; ++i) {
4514 auto c = extGetByteParam();
4515 if (c == FABGLEXT_ENDCODE)
4517 data[i] = hex2digit(tolower(c)) << 4;
4518 c = extGetByteParam();
4519 if (c == FABGLEXT_ENDCODE)
4521 data[i] |= hex2digit(tolower(c));
4523 if (m_bitmappedDisplayController && sprite < m_spritesCount) {
4524 auto bitmap =
new Bitmap(
width,
height,
data, format, RGB888(r, g, b),
false);
4525 m_sprites[sprite].addBitmap(bitmap);
4526 static_cast<BitmappedDisplayController*
>(m_displayController)->setSprites(m_sprites, m_spritesCount);
4532 }
else if (strcmp(cmd, FABGLEXT_GSPRITESET) == 0) {
4543 int sprite = extGetIntParam();
4545 char visible = extGetByteParam();
4547 int frame = extGetIntParam();
4549 int posx = extGetIntParam();
4551 int posy = extGetIntParam();
4553 if (m_bitmappedDisplayController && sprite < m_spritesCount) {
4554 m_sprites[sprite].visible = (visible ==
'V');
4555 m_sprites[sprite].setFrame(frame);
4556 m_sprites[sprite].x = posx;
4557 m_sprites[sprite].y = posy;
4558 static_cast<BitmappedDisplayController*
>(m_displayController)->refreshSprites();
4562 #if FABGLIB_TERMINAL_DEBUG_REPORT_UNSUPPORT
4563 logFmt(
"Unknown: ESC FABGLEXT_STARTCODE FABGLEXTX_GRAPHICSCMD %s\n", cmd);
4569void Terminal::keyboardReaderTask(
void * pvParameters)
4571 Terminal * term = (Terminal*) pvParameters;
4575 if (!term->isActive())
4578 VirtualKeyItem item;
4579 if (term->m_keyboard->getNextVirtualKey(&item)) {
4581 if (term->isActive() && term->flowControl()) {
4583 term->onVirtualKey(&item.vk, item.down);
4584 term->onVirtualKeyItem(&item);
4588 if (!term->m_emuState.keyAutorepeat && term->m_lastPressedKey == item.vk)
4590 term->m_lastPressedKey = item.vk;
4592 xSemaphoreTake(term->m_mutex, portMAX_DELAY);
4594 if (term->m_termInfo ==
nullptr) {
4595 if (term->m_emuState.ANSIMode)
4596 term->ANSIDecodeVirtualKey(item);
4598 term->VT52DecodeVirtualKey(item);
4600 term->TermDecodeVirtualKey(item);
4602 xSemaphoreGive(term->m_mutex);
4606 term->m_lastPressedKey =
VK_NONE;
4611 term->m_keyboard->injectVirtualKey(item,
true);
4620void Terminal::sendCursorKeyCode(uint8_t c)
4622 if (m_emuState.cursorKeysMode)
4630void Terminal::sendKeypadCursorKeyCode(uint8_t applicationCode,
const char * numericCode)
4632 if (m_emuState.keypadMode == KeypadMode::Application) {
4634 send(applicationCode);
4642void Terminal::ANSIDecodeVirtualKey(VirtualKeyItem
const & item)
4649 sendCursorKeyCode(
'A');
4653 sendCursorKeyCode(
'B');
4657 sendCursorKeyCode(
'C');
4661 sendCursorKeyCode(
'D');
4667 sendKeypadCursorKeyCode(
'x',
"A");
4671 sendKeypadCursorKeyCode(
'r',
"B");
4675 sendKeypadCursorKeyCode(
'v',
"C");
4679 sendKeypadCursorKeyCode(
't',
"D");
4717 sendKeypadCursorKeyCode(
'y',
"5~");
4721 sendKeypadCursorKeyCode(
's',
"6~");
4725 sendKeypadCursorKeyCode(
'p',
"2~");
4729 sendKeypadCursorKeyCode(
'w',
"1~");
4733 sendKeypadCursorKeyCode(
'n',
"3~");
4737 sendKeypadCursorKeyCode(
'q',
"4~");
4743 send(m_emuState.backarrowKeyMode ? ASCII_BS : ASCII_DEL);
4812 switch (item.ASCII) {
4816 if (m_emuState.newLineMode)
4834void Terminal::VT52DecodeVirtualKey(VirtualKeyItem
const & item)
4860 send(m_emuState.keypadMode == KeypadMode::Application ?
"\e?p" :
"0");
4865 send(m_emuState.keypadMode == KeypadMode::Application ?
"\e?q" :
"1");
4870 send(m_emuState.keypadMode == KeypadMode::Application ?
"\e?r" :
"2");
4875 send(m_emuState.keypadMode == KeypadMode::Application ?
"\e?s" :
"3");
4880 send(m_emuState.keypadMode == KeypadMode::Application ?
"\e?t" :
"4");
4885 send(m_emuState.keypadMode == KeypadMode::Application ?
"\e?u" :
"5");
4890 send(m_emuState.keypadMode == KeypadMode::Application ?
"\e?v" :
"6");
4895 send(m_emuState.keypadMode == KeypadMode::Application ?
"\e?w" :
"7");
4900 send(m_emuState.keypadMode == KeypadMode::Application ?
"\e?x" :
"8");
4905 send(m_emuState.keypadMode == KeypadMode::Application ?
"\e?y" :
"9");
4910 send(m_emuState.keypadMode == KeypadMode::Application ?
"\e?n" :
".");
4914 send(m_emuState.keypadMode == KeypadMode::Application ?
"\e?M" :
"\r");
4931void Terminal::TermDecodeVirtualKey(VirtualKeyItem
const & item)
4933 for (
auto i = m_termInfo->kbdCtrlSet; i->vk !=
VK_NONE; ++i) {
4934 if (i->vk == item.vk) {
4935 send(i->ANSICtrlCode);
4952TerminalController::TerminalController(
Terminal * terminal)
4953 : m_terminal(terminal)
4958TerminalController::~TerminalController()
4965 m_terminal = terminal;
4969void TerminalController::write(uint8_t c)
4972 m_terminal->
write(c);
4978void TerminalController::write(
char const * str)
4985int TerminalController::read()
4988 return m_terminal->
read(-1);
4997void TerminalController::waitFor(
int value)
5000 if (read() == value)
5007 write(FABGLEXT_CMD);
5008 write(FABGLEXTX_CLEAR);
5009 write(FABGLEXT_ENDCODE);
5015 write(FABGLEXT_CMD);
5016 write(FABGLEXTX_ENABLECURSOR);
5017 write(value ?
'1' :
'0');
5018 write(FABGLEXT_ENDCODE);
5024 write(FABGLEXT_CMD);
5025 write(FABGLEXTB_SETCURSORPOS);
5028 write(FABGLEXT_ENDCODE);
5034 write(FABGLEXT_CMD);
5035 write(FABGLEXTB_CURSORLEFT);
5036 write(count & 0xff);
5038 write(FABGLEXT_ENDCODE);
5044 write(FABGLEXT_CMD);
5045 write(FABGLEXTB_CURSORRIGHT);
5046 write(count & 0xff);
5048 write(FABGLEXT_ENDCODE);
5054 write(FABGLEXT_CMD);
5055 write(FABGLEXTB_GETCURSORPOS);
5056 write(FABGLEXT_ENDCODE);
5057 waitFor(FABGLEXT_REPLYCODE);
5065 write(FABGLEXT_CMD);
5066 write(FABGLEXTB_GETCURSORCOL);
5067 write(FABGLEXT_ENDCODE);
5068 waitFor(FABGLEXT_REPLYCODE);
5075 write(FABGLEXT_CMD);
5076 write(FABGLEXTB_GETCURSORROW);
5077 write(FABGLEXT_ENDCODE);
5078 waitFor(FABGLEXT_REPLYCODE);
5085 write(FABGLEXT_CMD);
5086 write(FABGLEXTB_INSERTSPACE);
5087 write(charsToMove & 0xff);
5088 write(charsToMove >> 8);
5089 write(FABGLEXT_ENDCODE);
5090 waitFor(FABGLEXT_REPLYCODE);
5097 write(FABGLEXT_CMD);
5098 write(FABGLEXTB_DELETECHAR);
5099 write(charsToMove & 0xff);
5100 write(charsToMove >> 8);
5101 write(FABGLEXT_ENDCODE);
5107 write(FABGLEXT_CMD);
5108 write(FABGLEXTB_SETCHAR);
5110 write(FABGLEXT_ENDCODE);
5111 waitFor(FABGLEXT_REPLYCODE);
5118 write(FABGLEXT_CMD);
5119 write(FABGLEXTB_ISVKDOWN);
5121 write(FABGLEXT_ENDCODE);
5122 waitFor(FABGLEXT_REPLYCODE);
5123 return read() ==
'1';
5129 write(FABGLEXT_CMD);
5130 write(FABGLEXTB_DISABLEFABSEQ);
5131 write(FABGLEXT_ENDCODE);
5137 write(FABGLEXT_CMD);
5138 write(FABGLEXTB_SETTERMTYPE);
5140 write(FABGLEXT_ENDCODE);
5146 write(FABGLEXT_CMD);
5147 write(FABGLEXTB_SETFGCOLOR);
5149 write(FABGLEXT_ENDCODE);
5155 write(FABGLEXT_CMD);
5156 write(FABGLEXTB_SETBGCOLOR);
5158 write(FABGLEXT_ENDCODE);
5164 write(FABGLEXT_CMD);
5165 write(FABGLEXTB_SETCHARSTYLE);
5167 write(enabled ? 1 : 0);
5168 write(FABGLEXT_ENDCODE);
5179 : m_terminal(terminal),
5180 m_termctrl(terminal),
5186 m_typeText(nullptr),
5192LineEditor::~LineEditor()
5200void LineEditor::setLength(
int newLength)
5202 if (m_allocated < newLength || m_allocated == 0) {
5203 int allocated = imax(m_allocated * 2, newLength);
5204 m_text = (
char*) realloc(m_text, allocated + 1);
5205 memset(m_text + m_allocated, 0, allocated - m_allocated + 1);
5206 m_allocated = allocated;
5208 m_textLength = newLength;
5216 m_typeText = strdup(text);
5223 setText(text, strlen(text), moveCursor);
5232 for (
int i = 0; i < m_textLength; ++i)
5235 for (
int i = 0; i < length; ++i)
5236 m_homeRow -= m_termctrl.
setChar(text[i]);
5239 memcpy(m_text, text, length);
5241 m_inputPos = moveCursor ? length : 0;
5245void LineEditor::write(uint8_t c)
5248 m_terminal->
write(c);
5254int LineEditor::read()
5257 return m_terminal->
read(-1);
5266void LineEditor::beginInput()
5268 if (m_terminal ==
nullptr) {
5277 for (
int i = 0, len = strlen(m_text); i < len; ++i)
5278 m_homeRow -= m_termctrl.
setChar(m_text[i]);
5279 if (m_inputPos == 0)
5288void LineEditor::endInput()
5291 if (m_text ==
nullptr) {
5292 m_text = (
char*) malloc(1);
5298void LineEditor::performCursorUp()
5304void LineEditor::performCursorDown()
5310void LineEditor::performCursorLeft()
5312 if (m_inputPos > 0) {
5316 while (m_inputPos - count > 0 && (m_text[m_inputPos - count] == ASCII_SPC || m_text[m_inputPos - count - 1] != ASCII_SPC))
5320 m_inputPos -= count;
5325void LineEditor::performCursorRight()
5327 if (m_inputPos < m_textLength) {
5331 while (m_text[m_inputPos + count] && (m_text[m_inputPos + count] == ASCII_SPC || m_text[m_inputPos + count - 1] != ASCII_SPC))
5335 m_inputPos += count;
5340void LineEditor::performCursorHome()
5347void LineEditor::performCursorEnd()
5349 m_termctrl.
cursorRight(m_textLength - m_inputPos);
5350 m_inputPos = m_textLength;
5354void LineEditor::performDeleteRight()
5356 if (m_inputPos < m_textLength) {
5357 memmove(m_text + m_inputPos, m_text + m_inputPos + 1, m_textLength - m_inputPos);
5364void LineEditor::performDeleteLeft()
5366 if (m_inputPos > 0) {
5369 memmove(m_text + m_inputPos - 1, m_text + m_inputPos, m_textLength - m_inputPos + 1);
5388 c = m_typeText[m_typingIndex++];
5391 m_typeText =
nullptr;
5421 }
else if (m_state == 2) {
5429 performCursorHome();
5440 }
else if (m_state >= 31) {
5460 performCursorLeft();
5466 performCursorRight();
5482 performCursorHome();
5492 performDeleteRight();
5497 m_insertMode = !m_insertMode;
5529 performDeleteLeft();
5534 performDeleteRight();
5543 m_termctrl.
cursorRight(m_textLength - m_inputPos);
5561 performCursorDown();
5566 performCursorLeft();
5571 performCursorRight();
5578 if (maxLength == 0 || m_inputPos < maxLength) {
5580 if (m_insertMode || m_inputPos == m_textLength) {
5581 setLength(m_textLength + 1);
5582 memmove(m_text + m_inputPos + 1, m_text + m_inputPos, m_textLength - m_inputPos);
5584 m_text[m_inputPos++] = c;
5586 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.