diff --git a/src/sbbs3/ansi_terminal.cpp b/src/sbbs3/ansi_terminal.cpp index 8ba33f793df2725d4d9994f030a029e1095d29df..d4a55f233fd3c6ca91052a8053a3431b54c491e6 100644 --- a/src/sbbs3/ansi_terminal.cpp +++ b/src/sbbs3/ansi_terminal.cpp @@ -65,6 +65,11 @@ const char *ANSI_Terminal::attrstr(unsigned atr) // Was ansi() and ansi_attr() char* ANSI_Terminal::attrstr(unsigned atr, unsigned curatr, char* str, size_t strsz) { + if (curatr & 0x100) { + if (curatr != ANSI_NORMAL) + sbbs->lprintf(LOG_WARNING, "Invalid current attribute %04x", curatr); + curatr = 0x07; + } bool color = supports(COLOR); size_t lastret; if (supports(ICE_COLOR)) { @@ -192,7 +197,7 @@ bool ANSI_Terminal::getdims() if (sbbs->sys_status & SS_USERON && (sbbs->useron.rows == TERM_ROWS_AUTO || sbbs->useron.cols == TERM_COLS_AUTO) && sbbs->online == ON_REMOTE) { /* Remote */ - sbbs->putcom("\x1b[s\x1b[255B\x1b[255C\x1b[6n\x1b[u"); + sbbs->term_out("\x1b[s\x1b[255B\x1b[255C\x1b[6n\x1b[u"); return sbbs->inkey(K_ANSI_CPR, TIMEOUT_ANSI_GETXY * 1000) == 0; } return false; @@ -210,7 +215,7 @@ bool ANSI_Terminal::getxy(unsigned* x, unsigned* y) if (y != NULL) *y = 0; - sbbs->putcom("\x1b[6n"); /* Request cursor position */ + sbbs->term_out("\x1b[6n"); /* Request cursor position */ time_t start = time(NULL); sbbs->sys_status &= ~SS_ABORT; @@ -250,7 +255,7 @@ bool ANSI_Terminal::getxy(unsigned* x, unsigned* y) #ifdef _DEBUG char dbg[128]; c_escape_str(str, dbg, sizeof(dbg), /* Ctrl-only? */ true); - lprintf(LOG_DEBUG, "Unexpected ansi_getxy response: '%s'", dbg); + sbbs->lprintf(LOG_DEBUG, "Unexpected ansi_getxy response: '%s'", dbg); #endif sbbs->ungetkeys(str, /* insert */ false); rsp = 0; @@ -258,7 +263,7 @@ bool ANSI_Terminal::getxy(unsigned* x, unsigned* y) } } if (time(NULL) - start > TIMEOUT_ANSI_GETXY) { - lprintf(LOG_NOTICE, "!TIMEOUT in ansi_getxy"); + sbbs->lprintf(LOG_NOTICE, "!TIMEOUT in ansi_getxy"); return false; } } @@ -280,58 +285,43 @@ bool ANSI_Terminal::gotoxy(unsigned x, unsigned y) x = 1; if (y == 0) y = 1; - sbbs->comprintf("\x1b[%d;%dH", y, x); - if (x > 0) - column = x - 1; - if (y > 0) - row = y - 1; - lncntr = 0; + sbbs->term_printf("\x1b[%d;%dH", y, x); return true; } // Was ansi_save bool ANSI_Terminal::save_cursor_pos() { - sbbs->putcom("\x1b[s"); + sbbs->term_out("\x1b[s"); return true; } // Was ansi_restore bool ANSI_Terminal::restore_cursor_pos() { - sbbs->putcom("\x1b[u"); + sbbs->term_out("\x1b[u"); return true; } void ANSI_Terminal::clearscreen() { clear_hotspots(); - sbbs->putcom("\x1b[2J\x1b[H"); /* clear screen, home cursor */ - row = 0; - column = 0; - lncntr = 0; - lbuflen = 0; - lastlinelen = 0; + sbbs->term_out("\x1b[2J\x1b[H"); /* clear screen, home cursor */ } void ANSI_Terminal::cleartoeos() { - sbbs->putcom("\x1b[J"); + sbbs->term_out("\x1b[J"); } void ANSI_Terminal::cleartoeol() { - sbbs->putcom("\x1b[K"); + sbbs->term_out("\x1b[K"); } void ANSI_Terminal::cursor_home() { - sbbs->putcom("\x1b[H"); - row = 0; - column = 0; - // TODO: Did not reset lncntr - lncntr = 0; - lastlinelen = 0; + sbbs->term_out("\x1b[H"); } void ANSI_Terminal::cursor_up(unsigned count = 1) @@ -339,17 +329,9 @@ void ANSI_Terminal::cursor_up(unsigned count = 1) if (count == 0) return; if (count > 1) - sbbs->comprintf("\x1b[%dA", count); + sbbs->term_printf("\x1b[%dA", count); else - sbbs->putcom("\x1b[A"); - // TODO: Old version didn't update row? - if (count > row) - count = row; - row -= count; - // TODO: Did not adjust lncntr - if (count > lncntr) - count = lncntr; - lncntr -= count; + sbbs->term_out("\x1b[A"); } void ANSI_Terminal::cursor_down(unsigned count = 1) @@ -357,13 +339,9 @@ void ANSI_Terminal::cursor_down(unsigned count = 1) if (count == 0) return; if (count > 1) - sbbs->comprintf("\x1b[%dB", count); + sbbs->term_printf("\x1b[%dB", count); else - sbbs->putcom("\x1b[B"); - // TODO: Old version assumes this can scroll - if (row + count > rows) - count = rows - row; - inc_row(count); + sbbs->term_out("\x1b[B"); } void ANSI_Terminal::cursor_right(unsigned count = 1) @@ -371,26 +349,18 @@ void ANSI_Terminal::cursor_right(unsigned count = 1) if (count == 0) return; if (count > 1) - sbbs->comprintf("\x1b[%dC", count); + sbbs->term_printf("\x1b[%dC", count); else - sbbs->putcom("\x1b[C"); - // TODO: Old version would move past cols - if (column + count > cols) - count = cols - column; - column += count; + sbbs->term_out("\x1b[C"); } void ANSI_Terminal::cursor_left(unsigned count = 1) { if (count == 0) return; if (count < 4) - sbbs->comprintf("%.*s", count, "\b\b\b"); - else - sbbs->comprintf("\x1b[%dD", count); - if (column > count) - column -= count; + sbbs->term_printf("%.*s", count, "\b\b\b"); else - column = 0; + sbbs->term_printf("\x1b[%dD", count); } void ANSI_Terminal::set_output_rate(enum output_rate speed) { @@ -413,7 +383,7 @@ void ANSI_Terminal::set_output_rate(enum output_rate speed) { val = 11; break; } - sbbs->comprintf("\x1b[;%u*r", val); + sbbs->term_printf("\x1b[;%u*r", val); cur_output_rate = speed; } @@ -425,7 +395,7 @@ static void ansi_mouse(sbbs_t *sbbs, enum ansi_mouse_mode mode, bool enable) { char str[32] = ""; SAFEPRINTF2(str, "\x1b[?%u%c", mode, enable ? 'h' : 'l'); - sbbs->putcom(str); + sbbs->term_out(str); } void ANSI_Terminal::set_mouse(unsigned flags) { @@ -463,38 +433,229 @@ void ANSI_Terminal::set_mouse(unsigned flags) { } } -bool ANSI_Terminal::parse_outchar(char ch) { - // TODO: Actually parse these so the various functions don't - // need to update row/column/etc. - if (ch == ESC && outchar_esc < ansiState_string) +static unsigned +get_pval(std::string params, unsigned pnum, unsigned dflt) +{ + try { + if (params == "") + return dflt; + unsigned p = 0; + std::string tp = params; + switch (tp.at(0)) { + case '<': + case '=': + case '>': + case '?': + tp.erase(0, 1); + break; + } + while (p < pnum) { + size_t sc = tp.find(";"); + if (sc == std::string::npos) + return dflt; + tp.erase(0, sc + 1); + p++; + } + size_t sc = tp.find(";"); + if (sc != std::string::npos) + tp.erase(sc); + sc = tp.find(":"); + if (sc != std::string::npos) + tp.erase(sc); + sc = tp.find("<"); + if (sc != std::string::npos) + tp.erase(sc); + sc = tp.find("="); + if (sc != std::string::npos) + tp.erase(sc); + sc = tp.find(">"); + if (sc != std::string::npos) + tp.erase(sc); + sc = tp.find("?"); + if (sc != std::string::npos) + tp.erase(sc); + if (tp == "") + return dflt; + return std::stoul(tp); + } + catch (...) { + return dflt; + } +} + +static unsigned +count_params(std::string params) +{ + std::string tp = params; + unsigned ret = 1; + + try { + for (;;) { + size_t sc = tp.find(";"); + if (sc == std::string::npos) + return ret; + ret++; + tp.erase(0, sc + 1); + } + } + catch (...) { + return 0; + } +} + +// TODO: Split this up into easily understandable chunks... +bool ANSI_Terminal::parse_outchar(char ich) { + unsigned char ch = static_cast<unsigned char>(ich); + if (flags & UTF8) { + // TODO: How many errors should be logged? + if (utf8_remain > 0) { + // First, check if this is overlong... + if (first_continuation + && ((utf8_remain == 1 && ch < 0xA0) + || (utf8_remain == 2 && ch < 0x90) + || (utf8_remain == 3 && ch >= 0x90))) { + sbbs->lprintf(LOG_WARNING, "Sending invalid UTF-8 codepoint"); + first_continuation = false; + utf8_remain = 0; + } + else if ((ch & 0xc0) != 0x80) { + first_continuation = false; + utf8_remain = 0; + sbbs->lprintf(LOG_WARNING, "Sending invalid UTF-8 codepoint"); + } + else { + first_continuation = false; + utf8_remain--; + if (utf8_remain) + return true; + } + } + else if ((ch & 0x80) != 0) { + if ((ch == 0xc0) || (ch == 0xc1) || (ch > 0xF5)) { + sbbs->lprintf(LOG_WARNING, "Sending invalid UTF-8 codepoint"); + } + if ((ch & 0xe0) == 0xc0) { + utf8_remain = 1; + if (ch == 0xE0) + first_continuation = true; + return true; + } + else if ((ch & 0xf0) == 0xe0) { + utf8_remain = 2; + if (ch == 0xF0) + first_continuation = true; + return true; + } + else if ((ch & 0xf8) == 0xf0) { + utf8_remain = 3; + if (ch == 0xF4) + first_continuation = true; + return true; + } + else + sbbs->lprintf(LOG_WARNING, "Sending invalid UTF-8 codepoint"); + } + if (utf8_remain) + return true; + } + + if (ch == ESC && outchar_esc < ansiState_string) { outchar_esc = ansiState_esc; + ansi_was_cc = false; + ansi_was_string = false; + ansi_params = ""; + ansi_ibs = ""; + ansi_sequence = ""; + ansi_final_byte = 0; + ansi_was_private = false; + } else if (outchar_esc == ansiState_esc) { - if (ch == '[') + ansi_sequence += ch; + if (ch == '[') { outchar_esc = ansiState_csi; - else if (ch == '_' || ch == 'P' || ch == '^' || ch == ']') + ansi_params = ""; + } + else if (ch == '_' || ch == 'P' || ch == '^' || ch == ']') { outchar_esc = ansiState_string; - else if (ch == 'X') + ansi_was_string = true; + } + else if (ch == 'X') { outchar_esc = ansiState_sos; - else if (ch >= '@' && ch <= '_') + ansi_was_string = true; + } + else if (ch >= ' ' && ch <= '/') { + ansi_ibs += ch; + outchar_esc = ansiState_intermediate; + } + else if (ch >= '0' && ch <= '~') { outchar_esc = ansiState_final; - else + ansi_was_cc = true; + ansi_final_byte = ch; + } + else { + // TODO: Broken sequence, position unknown + sbbs->lprintf(LOG_WARNING, "Sent broken ANSI sequence '%s' at %d", ansi_sequence.c_str(), __LINE__); outchar_esc = ansiState_none; + } } else if (outchar_esc == ansiState_csi) { - if (ch >= '@' && ch <= '~') + ansi_sequence += ch; + if (ch >= '0' && ch <= '?') { + if (ansi_params == "" && ch >= '<' && ch <= '?') + ansi_was_private = true; + ansi_params += ch; + } + else if (ch >= ' ' && ch <= '/') { + ansi_ibs += ch; + outchar_esc = ansiState_intermediate; + } + else if (ch >= '@' && ch <= '~') { outchar_esc = ansiState_final; + ansi_final_byte = ch; + } + else { + // TODO: Broken sequence, position unknown + sbbs->lprintf(LOG_WARNING, "Sent broken ANSI sequence '%s' at %d", ansi_sequence.c_str(), __LINE__); + outchar_esc = ansiState_none; + } + } + else if (outchar_esc == ansiState_intermediate) { + ansi_sequence += ch; + if (ch >= ' ' && ch <= '/') { + ansi_ibs += ch; + outchar_esc = ansiState_intermediate; + } + else if (ch >= '0' && ch <= '~') { + if (!ansi_was_cc) { + // TODO: Broken sequence, position unknown + sbbs->lprintf(LOG_WARNING, "Sent broken ANSI sequence '%s' at %d", ansi_sequence.c_str(), __LINE__); + outchar_esc = ansiState_none; + } + else { + outchar_esc = ansiState_final; + ansi_final_byte = ch; + } + } + else { + // TODO: Broken sequence, position unknown + sbbs->lprintf(LOG_WARNING, "Sent broken ANSI sequence '%s' at %d", ansi_sequence.c_str(), __LINE__); + outchar_esc = ansiState_none; + } } else if (outchar_esc == ansiState_string) { // APS, DCS, PM, or OSC + ansi_sequence += ch; if (ch == ESC) outchar_esc = ansiState_esc; if (!((ch >= '\b' && ch <= '\r') || (ch >= ' ' && ch <= '~'))) outchar_esc = ansiState_none; } else if (outchar_esc == ansiState_sos) { // SOS + ansi_sequence += ch; if (ch == ESC) outchar_esc = ansiState_sos_esc; } else if (outchar_esc == ansiState_sos_esc) { // ESC inside SOS + ansi_sequence += ch; if (ch == '\\') outchar_esc = ansiState_esc; else if (ch == 'X') @@ -506,24 +667,299 @@ bool ANSI_Terminal::parse_outchar(char ch) { outchar_esc = ansiState_none; if (outchar_esc != ansiState_none) { - if (outchar_esc == ansiState_final) + if (outchar_esc == ansiState_final) { + if (ansi_was_cc) { + if (ansi_ibs == "") { + switch (ansi_final_byte) { + case 'E': // NEL - Next Line + set_column(); + inc_row(); + break; + case 'M': // RI - Reverse Line Feed + // TODO: line counter etc. + if (row) + row--; + break; + case 'c': // RIS - Reset to Initial State.. homes + set_column(); + set_row(); + // TODO: line counter etc. + break; + } + } + } + else if (!ansi_was_string) { + unsigned cnt; + unsigned pval; + if (ansi_ibs == "") { + if (ansi_was_private) { + // TODO: Track things like origin mode, auto wrap, etc. + } + else { + switch (ansi_final_byte) { + case 'A': // Cursor up + case 'F': // Cursor Preceding Line + case 'k': // Line Position Backward + // Single parameter, default 1 + pval = get_pval(ansi_params, 0, 1); + if (pval > row) + pval = row; + dec_row(pval); + break; + case 'B': // Cursor Down + case 'E': // Cursor Next Line + case 'e': // Line position Forward + // Single parameter, default 1 + pval = get_pval(ansi_params, 0, 1); + if (pval >= (rows - row)) + pval = rows - row - 1; + inc_row(pval); + break; + case 'C': // Cursor Right + case 'a': // Cursor Position Forward + // Single parameter, default 1 + pval = get_pval(ansi_params, 0, 1); + if (pval >= (cols - column)) + pval = cols - column - 1; + inc_column(pval); + break; + case 'D': // Cursor Left + case 'j': // Character Position Backward + // Single parameter, default 1 + pval = get_pval(ansi_params, 0, 1); + if (pval > column) + pval = column; + dec_column(pval); + break; + case 'G': // Cursor Character Absolute + case '`': // Character Position Absolute + pval = get_pval(ansi_params, 0, 1); + if (pval > cols) + pval = cols; + if (pval == 0) + pval = 1; + set_column(pval - 1); + break; + case 'H': // Cursor Position + case 'f': // Character and Line Position + pval = get_pval(ansi_params, 0, 1); + if (pval > rows) + pval = rows; + if (pval == 0) + pval = 1; + set_row(pval - 1); + pval = get_pval(ansi_params, 1, 1); + if (pval > cols) + pval = cols; + if (pval == 0) + pval = 1; + set_column(pval - 1); + break; + case 'I': // Cursor Forward Tabulation + // TODO + break; + case 'J': // Clear screen + // TODO: ANSI does not move cursor, ANSI-BBS does. + set_row(); + set_column(); + break; + case 'Y': // Line tab + // TODO: Track tabs + break; + case 'Z': // Back tab + // TODO: Track tabs + break; + case 'b': // Repeat + // TODO: Can't repeat ESC + break; + case 'd': // Line Position Abolute + pval = get_pval(ansi_params, 0, 1); + if (pval > rows) + pval = rows; + if (pval == 0) + pval = 1; + set_row(pval - 1); + break; + case 'm': // Set Graphic Rendidtion + cnt = count_params(ansi_params); + for (unsigned i = 0; i < cnt; i++) { + pval = get_pval(ansi_params, i, 0); + switch (pval) { + case 0: + // Don't use ANSI_NORMAL, it's only for the const thing + curatr = 0x07; + break; + case 1: + curatr &= ~0x100; + curatr |= 0x08; + break; + case 2: + curatr &= ~0x108; + break; + case 5: + case 6: + curatr &= ~0x100; + if (flags & ICE_COLOR) + curatr |= BG_BRIGHT; + else + curatr |= BLINK; + break; + case 7: + curatr &= ~0x100; + if (!is_negative) { + curatr = (curatr & ~0x77) | ((curatr & 0x70) >> 4) | ((curatr & 0x07) << 4); + is_negative = true; + } + break; + case 8: + curatr &= ~0x100; + curatr = (curatr & ~0x07) | ((curatr & 0x70) >> 4); + break; + case 22: + curatr &= ~0x108; + break; + case 25: + curatr &= ~0x100; + if (flags & ICE_COLOR) + curatr &= ~BG_BRIGHT; + else + curatr &= ~BLINK; + break; + case 27: + if (is_negative) { + curatr = (curatr & ~0x77) | ((curatr & 0x70) >> 4) | ((curatr & 0x07) << 4); + is_negative = false; + } + break; + case 30: + curatr &= ~0x107; + curatr |= BLACK; + break; + case 31: + curatr &= ~0x107; + curatr |= RED; + break; + case 32: + curatr &= ~0x107; + curatr |= GREEN; + break; + case 33: + curatr &= ~0x107; + curatr |= BROWN; + break; + case 34: + curatr &= ~0x107; + curatr |= BLUE; + break; + case 35: + curatr &= ~0x107; + curatr |= MAGENTA; + break; + case 36: + curatr &= ~0x107; + curatr |= CYAN; + break; + case 37: + curatr &= ~0x107; + curatr |= LIGHTGRAY; + break; + case 40: + curatr &= ~0x370; + // Don't use BG_BLACK, it's only for the const thing. + //curatr |= BG_BLACK; + break; + case 41: + curatr &= ~0x370; + curatr |= BG_RED; + break; + case 42: + curatr &= ~0x370; + curatr |= BG_GREEN; + break; + case 43: + curatr &= ~0x370; + curatr |= BG_BROWN; + break; + case 44: + curatr &= ~0x370; + curatr |= BG_BLUE; + break; + case 45: + curatr &= ~0x370; + curatr |= BG_MAGENTA; + break; + case 46: + curatr &= ~0x370; + curatr |= BG_CYAN; + break; + case 47: + curatr &= ~0x370; + curatr |= BG_LIGHTGRAY; + break; + } + } + break; + case 'r': // Set Top and Bottom Margins + // TODO + break; + case 's': // Save Current Position (also set left/right margins) + saved_row = row; + saved_column = column; + break; + case 'u': // Restore Cursor Position + set_row(saved_row); + set_column(saved_column); + break; + case 'z': // Invoke Macro + // TODO + break; + case 'N': // "ANSI" Music + case '|': // SyncTERM Music + // TODO + break; + } + } + } + } outchar_esc = ansiState_none; - sbbs->outcom(ch); - return false; + } } - - if (!required_parse_outchar(ch)) - return false; - - /* Track cursor position locally */ - switch (ch) { - case '\a': // 7 - /* Non-printing, does not go into lbuf */ - break; - default: - // TODO: All kinds of CTRL charaters not handled properly - inc_column(1); - break; + else { + /* Track cursor position locally */ + switch (ch) { + case '\a': // 7 + /* Non-printing */ + break; + case 8: // BS + dec_column(); + break; + case 9: // TAB + // TODO: This makes the position unknown + if (column < (cols - 1)) { + inc_column(); + while ((column < (cols - 1)) && (column % 8)) + inc_column(); + } + break; + case 10: // LF + inc_row(); + break; + case 12: // FF + // TODO: Technically, this makes the position unknown + set_row(); + set_column(); + break; + case 13: // CR + if (sbbs->console & CON_CR_CLREOL) + cleartoeol(); + set_column(); + break; + default: + // TODO: We'll need to handle UTF-8 here now... + // TODO: All kinds of CTRL charaters not handled properly + inc_column(); + break; + } } return true; @@ -815,16 +1251,16 @@ void ANSI_Terminal::insert_indicator() { gotoxy(cols, 1); int tmpatr; if (sbbs->console & CON_INSERT) { - sbbs->putcom(attrstr(tmpatr = BLINK | BLACK | (LIGHTGRAY << 4), curatr, str, supports(COLOR))); - sbbs->outcom('I'); + sbbs->term_out(attrstr(tmpatr = BLINK | BLACK | (LIGHTGRAY << 4), curatr, str, supports(COLOR))); + sbbs->cp437_out('I'); } else { - sbbs->putcom(attrstr(tmpatr = ANSI_NORMAL)); - sbbs->outcom(' '); + sbbs->term_out(attrstr(tmpatr = ANSI_NORMAL)); + sbbs->cp437_out(' '); } - sbbs->putcom(attrstr(curatr, tmpatr, str, supports(COLOR))); + sbbs->term_out(attrstr(curatr, tmpatr, str, supports(COLOR))); restore_cursor_pos(); - column = col; - this->row = row; + set_column(col); + set_row(row); } struct mouse_hotspot* ANSI_Terminal::add_hotspot(struct mouse_hotspot* spot) {return nullptr;} diff --git a/src/sbbs3/ansi_terminal.h b/src/sbbs3/ansi_terminal.h index 58af192dfd0ddf58e5fb3ccbeddad622d572d719..6cbdbb699a6a3964d0ba3d8b070d54d62427dd4d 100644 --- a/src/sbbs3/ansi_terminal.h +++ b/src/sbbs3/ansi_terminal.h @@ -1,12 +1,14 @@ #ifndef ANSI_TERMINAL_H #define ANSI_TERMINAL_H +#include <string> #include "terminal.h" enum ansiState { ansiState_none // No sequence ,ansiState_esc // Escape ,ansiState_csi // CSI + ,ansiState_intermediate // Intermediate byte ,ansiState_final // Final byte ,ansiState_string // APS, DCS, PM, or OSC ,ansiState_sos // SOS @@ -53,6 +55,18 @@ public: private: enum ansiState outchar_esc{ansiState_none}; // track ANSI escape seq output + std::string ansi_params{""}; + std::string ansi_ibs{""}; + std::string ansi_sequence{""}; + bool ansi_was_cc{false}; + bool ansi_was_string{false}; + char ansi_final_byte{0}; + bool ansi_was_private{false}; + unsigned saved_row{0}; + unsigned saved_column{0}; + bool is_negative{0}; + uint8_t utf8_remain{0}; + bool first_continuation{false}; }; #endif diff --git a/src/sbbs3/answer.cpp b/src/sbbs3/answer.cpp index 23c9fae05687cfedb5c7d73bb1f5f286e54dfb2d..e9ab4b816098e4c8d9d3f69bd0fa05970ea330d0 100644 --- a/src/sbbs3/answer.cpp +++ b/src/sbbs3/answer.cpp @@ -554,7 +554,7 @@ bool sbbs_t::answer() outchar(FF); term->center(str); } else { /* ANSI+ terminal detection */ - putcom( "\r\n" /* locate cursor at column 1 */ + term_out( "\r\n" /* locate cursor at column 1 */ "\x1b[s" /* save cursor position (necessary for HyperTerm auto-ANSI) */ "\x1b[0c" /* Request CTerm version */ "\x1b[255B" /* locate cursor as far down as possible */ diff --git a/src/sbbs3/atcodes.cpp b/src/sbbs3/atcodes.cpp index 7b4bb421763e58a67b115c818d9ed9eb196b81ae..6b5efa31f858bad9b83683bff2a028a8c65e70b0 100644 --- a/src/sbbs3/atcodes.cpp +++ b/src/sbbs3/atcodes.cpp @@ -368,18 +368,18 @@ const char* sbbs_t::atcode(const char* sp, char* str, size_t maxlen, int* pmode, if (strncmp(sp, "U+", 2) == 0) { // UNICODE enum unicode_codepoint codepoint = (enum unicode_codepoint)strtoul(sp + 2, &tp, 16); if (tp == NULL || *tp == 0) - outchar(codepoint, unicode_to_cp437(codepoint)); + outcp(codepoint, unicode_to_cp437(codepoint)); else if (*tp == ':') - outchar(codepoint, tp + 1); + outcp(codepoint, tp + 1); else { char fallback = (char)strtoul(tp + 1, NULL, 16); if (*tp == ',') - outchar(codepoint, fallback); + outcp(codepoint, fallback); else if (*tp == '!') { char ch = unicode_to_cp437(codepoint); if (ch != 0) fallback = ch; - outchar(codepoint, fallback); + outcp(codepoint, fallback); } else return NULL; // Invalid @-code @@ -388,36 +388,36 @@ const char* sbbs_t::atcode(const char* sp, char* str, size_t maxlen, int* pmode, } if (strcmp(sp, "CHECKMARK") == 0) { - outchar(UNICODE_CHECK_MARK, CP437_CHECK_MARK); + outcp(UNICODE_CHECK_MARK, CP437_CHECK_MARK); return nulstr; } if (strcmp(sp, "ELLIPSIS") == 0) { - outchar(UNICODE_HORIZONTAL_ELLIPSIS, "..."); + outcp(UNICODE_HORIZONTAL_ELLIPSIS, "..."); return nulstr; } if (strcmp(sp, "COPY") == 0) { - outchar(UNICODE_COPYRIGHT_SIGN, "(C)"); + outcp(UNICODE_COPYRIGHT_SIGN, "(C)"); return nulstr; } if (strcmp(sp, "SOUNDCOPY") == 0) { - outchar(UNICODE_SOUND_RECORDING_COPYRIGHT, "(P)"); + outcp(UNICODE_SOUND_RECORDING_COPYRIGHT, "(P)"); return nulstr; } if (strcmp(sp, "REGISTERED") == 0) { - outchar(UNICODE_REGISTERED_SIGN, "(R)"); + outcp(UNICODE_REGISTERED_SIGN, "(R)"); return nulstr; } if (strcmp(sp, "TRADEMARK") == 0) { - outchar(UNICODE_TRADE_MARK_SIGN, "(TM)"); + outcp(UNICODE_TRADE_MARK_SIGN, "(TM)"); return nulstr; } if (strcmp(sp, "DEGREE_C") == 0) { - outchar(UNICODE_DEGREE_CELSIUS, "\xF8""C"); + outcp(UNICODE_DEGREE_CELSIUS, "\xF8""C"); return nulstr; } if (strcmp(sp, "DEGREE_F") == 0) { - outchar(UNICODE_DEGREE_FAHRENHEIT, "\xF8""F"); + outcp(UNICODE_DEGREE_FAHRENHEIT, "\xF8""F"); return nulstr; } @@ -653,7 +653,7 @@ const char* sbbs_t::atcode(const char* sp, char* str, size_t maxlen, int* pmode, if (strcmp(sp, "PETGRFX") == 0) { if (term->supports(PETSCII)) - outcom(PETSCII_UPPERGRFX); + term_out(PETSCII_UPPERGRFX); return nulstr; } diff --git a/src/sbbs3/con_hi.cpp b/src/sbbs3/con_hi.cpp index e4663043c57fd27b6e31ed6edd9e6594737a1052..d314d16beda9ec991f621a2320007e9db9ebe544 100644 --- a/src/sbbs3/con_hi.cpp +++ b/src/sbbs3/con_hi.cpp @@ -37,7 +37,7 @@ void sbbs_t::redrwstr(char *strin, int i, int l, int mode) if (mode) bprintf(mode, "%-*.*s", l, l, strin); else - term->column += rprintf("%-*.*s", l, l, strin); + rprintf("%-*.*s", l, l, strin); term->cleartoeol(); if (i < l) { auto_utf8(strin, mode); diff --git a/src/sbbs3/con_out.cpp b/src/sbbs3/con_out.cpp index 47cb24ea11dac6b31172fb8cf62af0002a39898b..6553c95c10bc0b8ea607d47ec3b05da0ef98e2da 100644 --- a/src/sbbs3/con_out.cpp +++ b/src/sbbs3/con_out.cpp @@ -118,12 +118,12 @@ int sbbs_t::bputs(const char *str, int mode) } if (mode & P_PETSCII) { if (term->flags & PETSCII) - outcom(str[l++]); + term_out(str[l++]); else petscii_to_ansibbs(str[l++]); } else if ((str[l] & 0x80) && (mode & P_UTF8)) { if (term->flags & UTF8) - outcom(str[l++]); + term_out(str[l++]); else l += print_utf8_as_cp437(str + l, len - l); } else @@ -333,44 +333,160 @@ size_t sbbs_t::print_utf8_as_cp437(const char* str, size_t len) /* Performs saveline buffering (for restoreline) */ /* DOES NOT expand ctrl-A codes, track columns, lines, auto-pause, etc. */ /****************************************************************************/ -int sbbs_t::rputs(const char *str, size_t len) +size_t sbbs_t::rputs(const char *str, size_t len) { - size_t l; + unsigned oldlc = term->lncntr; + size_t ret = cp437_out(str, len ? len : SIZE_MAX); + term->lncntr = oldlc; + return ret; +} + +/* + * Translates from CP437 to the current encoding and passes the result + * to term_out() + * Returns the number of bytes successfully consumed + */ +size_t sbbs_t::cp437_out(const char *str, size_t len) +{ + if (len == SIZE_MAX) + len = strlen(str); + for (size_t l = 0; l < len && online; l++) { + if (cp437_out(str[l]) == 0) + return l; + } + return len; +} +size_t sbbs_t::cp437_out(int ich) +{ + unsigned char ch {static_cast<unsigned char>(ich)}; if (console & CON_ECHO_OFF) return 0; - if (len == 0) - len = strlen(str); - char utf8[UTF8_MAX_LEN + 1] = ""; - for (l = 0; l < len && online; l++) { - uchar ch = str[l]; - utf8[0] = 0; - if (term->flags & PETSCII) { - ch = cp437_to_petscii(ch); - if (ch == PETSCII_SOLID) - outcom(PETSCII_REVERSE_ON); + if (!online) + return 0; + + // Many of the low CP437 characters will print on many terminals, + // so we may want to translate some of them. + // Synchronet specifically has definitions for the following: + // + // BEL (0x07) Make a beeping noise + // BS (0x08) Move left one character, but do not wrap to + // previous line + // LF (0x0A) Move down one line, scrolling if on bottom row + // CR (0x0D) Move to the first position in the current line + // + // Further, the following are expected to be translated + // earlier and should only arrive here via rputs() + // TAB (0x09) Expanded to spaces to the next tabstop (defined by + // sbbs::tabstop) + // FF (0x0C) Clear the screen + // + // It is unclear what "should" happen when these arrive here, + // but it should be consistent, likely they should be expanded. + switch (ch) { + case 0x08: // BS + term->cursor_left(); + return 1; + case 0x09: // TAB + if (term->column < (term->cols - 1)) { + outchar(' '); + while ((term->column < (term->cols - 1)) && (term->column % term->tabstop)) + outchar(' '); + } + return 1; + + case 0x0A: // LF + term->line_feed(); + return 1; + case 0x0D: // CR + term->carriage_return(); + return 1; + case 0x0C: // FF + term->clearscreen(); + return 1; + } + + // UTF-8 Note that CP437 0x7C maps to U+00A6 so we can't just do + // everything below 0x80 this way. + if (term->flags & UTF8) { + if (ch != 0x07 && ch != 0x08 && ch != 0x09 && ch != 0x0A && ch != 0x0C && ch != 0x13) { + char utf8[UTF8_MAX_LEN + 1]; + enum unicode_codepoint codepoint = cp437_unicode_tbl[ch]; + if (codepoint == 0) + codepoint = static_cast<enum unicode_codepoint>(ch); + int len = utf8_putc(utf8, sizeof(utf8), codepoint); + + if (len < 1) + return 0; + if (term_out(utf8, len) != len) + return 0; + return 1; } - else if ((term->flags & NO_EXASCII) && (ch & 0x80)) - ch = exascii_to_ascii_char(ch); /* seven bit table */ - else if (term->flags & UTF8) { - enum unicode_codepoint codepoint = cp437_unicode_tbl[(uchar)ch]; - if (codepoint != 0) - utf8_putc(utf8, sizeof(utf8) - 1, codepoint); + } + // PETSCII + else if (term->flags & PETSCII) { + ch = cp437_to_petscii(ch); + if (ch == PETSCII_SOLID) { + if (term_out(PETSCII_REVERSE_ON) != 1) + return 0; } - if (utf8[0]) - putcom(utf8); - else { - if (outcom(ch) != 0) - break; - if ((char)ch == (char)TELNET_IAC && !(telnet_mode & TELNET_MODE_OFF)) - outcom(TELNET_IAC); /* Must escape Telnet IAC char (255) */ - if ((term->flags & PETSCII) && ch == PETSCII_SOLID) - outcom(PETSCII_REVERSE_OFF); + if (term_out(ch) != 1) + return 0; + if (ch == PETSCII_SOLID) { + if (term_out(PETSCII_REVERSE_OFF) != 1) + return 0; } - if (ch == '\n') - term->lbuflen = 0; + return 1; } - return l; + // CP437 or US-ASCII + if ((term->flags & NO_EXASCII) && (ch & 0x80)) + ch = exascii_to_ascii_char(ch); /* seven bit table */ + if (term_out(ch) != 1) + return 0; + return 1; +} + +/* + * Update column, row, line buffer, and line counter + * Returns the number of bytes consumed + */ +size_t sbbs_t::term_out(const char *str, size_t len) +{ + if (len == SIZE_MAX) + len = strlen(str); + for (size_t l = 0; l < len && online; l++) { + uchar ch = str[l]; + if (!term_out(ch)) + return l; + } + return len; +} + +size_t sbbs_t::term_out(int ich) +{ + unsigned char ch{static_cast<unsigned char>(ich)}; + if (console & CON_ECHO_OFF) + return 0; + if (!online) + return 0; + if (!term->parse_outchar(ch)) + return 1; + if (term->lbuflen < LINE_BUFSIZE) { + if (term->lbuflen == 0) + term->latr = term->curatr; + // Historically, beeps don't go into lbuf + // hopefully nobody notices and cares, because this would mean + // that BEL *must* be part of any charset we support... and it's + // not part of C64 PETSCII. + term->lbuf[term->lbuflen++] = ch; + } + if (ch == TELNET_IAC && !(telnet_mode & TELNET_MODE_OFF)) { + if (outcom(TELNET_IAC)) + return 0; + } + if (outcom(ch)) + return 0; + return 1; } /****************************************************************************/ @@ -423,9 +539,9 @@ int sbbs_t::rprintf(const char *fmt, ...) } /****************************************************************************/ -/* Performs printf() using bbs putcom/outcom functions */ +/* Performs printf() using bbs term_out functions */ /****************************************************************************/ -int sbbs_t::comprintf(const char *fmt, ...) +int sbbs_t::term_printf(const char *fmt, ...) { va_list argptr; char sbuf[4096]; @@ -434,7 +550,7 @@ int sbbs_t::comprintf(const char *fmt, ...) vsnprintf(sbuf, sizeof(sbuf), fmt, argptr); sbuf[sizeof(sbuf) - 1] = 0; /* force termination */ va_end(argptr); - return putcom(sbuf); + return term_out(sbuf); } char* sbbs_t::term_rows(user_t* user, char* str, size_t size) @@ -599,14 +715,18 @@ bool sbbs_t::update_nodeterm(void) * rputs() and unify the conversion code. outchar() and rputs() appear * to be equivilent, but they're implemented differently. * + * putcom() is used not only for text, but also to send telnet commands. + * The use for text could be replaced, but the use for IAC needs to be + * unchanged. + * * bputs putmsg * \ / * outchar rputs * +---------| - * putcom cp437_out outcp - * +-----------+-----------+ - * term_out - * | + * cp437_out outcp + * +-----------+ + * putcom term_out + * +-----------+ * outcom * | * RingBufWrite @@ -619,20 +739,22 @@ bool sbbs_t::update_nodeterm(void) * * rputs() would effectively do nothing except call cp437_out() * - * putcom() would effectively do nothing except call term_out() - * * cp437_out() would translate from cp437 to the terminal charset * this would specifically include the control characters * BEL, BS, TAB, LF, FF, CR, and DEL * * outcp() would call term_out() if UTF-8 is supported, or bputs() if not * + * putcom() would remain a string wrapper around outcom() + * * term_out() would update column and row, and maintain the line buffer, * it would be available for a char, a uchar, a char*, and * a char* + size_t * * outcom() and RingBufWrite() would be unchanged * + * Open questions: + * - Should term_out() strip/convert ANSI sequences? Ugh, I hope not. */ /****************************************************************************/ @@ -649,9 +771,6 @@ int sbbs_t::outchar(char ch) if (console & CON_ECHO_OFF) return 0; - if (!term->parse_outchar(ch)) - return 0; - if (rainbow_index >= 0) { attr(rainbow[rainbow_index]); if (rainbow[rainbow_index + 1] == 0) { @@ -660,35 +779,25 @@ int sbbs_t::outchar(char ch) } else ++rainbow_index; } - char utf8[UTF8_MAX_LEN + 1] = ""; - if (!(term->flags & PETSCII)) { - if ((term->flags & NO_EXASCII) && (ch & 0x80)) - ch = exascii_to_ascii_char(ch); /* seven bit table */ - else if (term->flags & UTF8) { - enum unicode_codepoint codepoint = cp437_unicode_tbl[(uchar)ch]; - if (codepoint != 0) - utf8_putc(utf8, sizeof(utf8) - 1, codepoint); + if ((console & CON_R_ECHOX) && (uchar)ch >= ' ') + ch = *text[PasswordChar]; + + // TODO: We may want to move these into term_out() + // Otherwise they won't work with UTF-8 + if (ch == '\n' && line_delay) + SLEEP(line_delay); + + if (ch == FF && term->lncntr > 0 && term->row > 0) { + term->lncntr = 0; + term->newline(); + if (!(sys_status & SS_PAUSEOFF)) { + pause(); + while (term->lncntr && online && !(sys_status & SS_ABORT)) + pause(); } } - if ((console & CON_R_ECHOX) && (uchar)ch >= ' ') { - ch = *text[PasswordChar]; - } - if (ch == (char)TELNET_IAC && !(telnet_mode & TELNET_MODE_OFF)) - outcom(TELNET_IAC); /* Must escape Telnet IAC char (255) */ - if (term->flags & PETSCII) { - uchar pet = cp437_to_petscii(ch); - if (pet == PETSCII_SOLID) - outcom(PETSCII_REVERSE_ON); - outcom(pet); - if (pet == PETSCII_SOLID) - outcom(PETSCII_REVERSE_OFF); - } else { - if (utf8[0] != 0) - putcom(utf8); - else - outcom(ch); - } + cp437_out(ch); if (term->lncntr == term->rows - 1 && ((useron.misc & (UPAUSE ^ (console & CON_PAUSEOFF))) || sys_status & SS_PAUSEON) && !(sys_status & (SS_PAUSEOFF | SS_ABORT))) { @@ -698,14 +807,14 @@ int sbbs_t::outchar(char ch) return 0; } -int sbbs_t::outchar(enum unicode_codepoint codepoint, const char* cp437_fallback) +int sbbs_t::outcp(enum unicode_codepoint codepoint, const char* cp437_fallback) { if (term->supports(UTF8)) { char str[UTF8_MAX_LEN]; int len = utf8_putc(str, sizeof(str), codepoint); if (len < 1) return len; - putcom(str, len); + term_out(str, len); term->inc_column(unicode_width(codepoint, unicode_zerowidth)); return 0; } @@ -714,10 +823,10 @@ int sbbs_t::outchar(enum unicode_codepoint codepoint, const char* cp437_fallback return bputs(cp437_fallback); } -int sbbs_t::outchar(enum unicode_codepoint codepoint, char cp437_fallback) +int sbbs_t::outcp(enum unicode_codepoint codepoint, char cp437_fallback) { char str[2] = { cp437_fallback, '\0' }; - return outchar(codepoint, str); + return outcp(codepoint, str); } void sbbs_t::wide(const char* str) @@ -997,8 +1106,7 @@ int sbbs_t::attr(int atr) term->attrstr(atr, term->curatr, str, sizeof(str)); // TODO: This was rputs() // TODO: We may need a raw output that goes in there - putcom(str); - term->curatr = atr; + term_out(str); return 0; } diff --git a/src/sbbs3/exec.cpp b/src/sbbs3/exec.cpp index f481218bf25f1c61bf3af4f46a6f0c50bc7c9a90..196478abb84660193c4bdcbaf6ad8a53616a9c76 100644 --- a/src/sbbs3/exec.cpp +++ b/src/sbbs3/exec.cpp @@ -1334,7 +1334,7 @@ int sbbs_t::exec(csi_t *csi) lputs(LOG_INFO, cmdstr((char*)csi->ip, path, csi->str, (char*)buf)); break; case CS_PRINT_REMOTE: - putcom(cmdstr((char*)csi->ip, path, csi->str, (char*)buf)); + term_out(cmdstr((char*)csi->ip, path, csi->str, (char*)buf)); break; case CS_PRINTFILE: printfile(cmdstr((char*)csi->ip, path, csi->str, (char*)buf), P_SAVEATR); diff --git a/src/sbbs3/getstr.cpp b/src/sbbs3/getstr.cpp index 1d8f90b2c08b464216b68638106adc7f5124a8bc..265cb94d614d46464d8e6a7c821911affd708447 100644 --- a/src/sbbs3/getstr.cpp +++ b/src/sbbs3/getstr.cpp @@ -51,9 +51,8 @@ size_t sbbs_t::getstr(char *strout, size_t maxlen, int mode, const str_list_t hi if (mode & K_LINE && (term->can_highlight()) && !(mode & K_NOECHO)) { attr(cfg.color[clr_inputline]); for (i = 0; i < maxlen; i++) - outcom(' '); + term_out(' '); term->cursor_left(maxlen); - term->column = org_column; } if (wordwrap[0]) { SAFECOPY(str1, wordwrap); @@ -92,7 +91,7 @@ size_t sbbs_t::getstr(char *strout, size_t maxlen, int mode, const str_list_t hi else { for (i = 0; i < l; i++) outchar(BS); - term->column += bputs(str1, P_AUTO_UTF8); + bputs(str1, P_AUTO_UTF8); i = l; } if (ch != ' ' && ch != TAB) @@ -150,7 +149,7 @@ size_t sbbs_t::getstr(char *strout, size_t maxlen, int mode, const str_list_t hi l++; for (x = l; x > i; x--) str1[x] = str1[x - 1]; - term->column += rprintf("%.*s", (int)(l - i), str1 + i); + rprintf("%.*s", (int)(l - i), str1 + i); term->cursor_left(l - i); #if 0 if (i == maxlen - 1) @@ -581,7 +580,7 @@ size_t sbbs_t::getstr(char *strout, size_t maxlen, int mode, const str_list_t hi l++; for (x = l; x > i; x--) str1[x] = str1[x - 1]; - term->column += rprintf("%.*s", (int)(l - i), str1 + i); + rprintf("%.*s", (int)(l - i), str1 + i); term->cursor_left(l - i); #if 0 if (i == maxlen - 1) { @@ -596,6 +595,7 @@ size_t sbbs_t::getstr(char *strout, size_t maxlen, int mode, const str_list_t hi if (i > l) l = i; str1[l] = 0; + // TODO: Test this... if (utf8_str_is_valid(str1)) redrwstr(str1, term->column - org_column, l, P_UTF8); } else { diff --git a/src/sbbs3/main.cpp b/src/sbbs3/main.cpp index efb5d3ff8425fc7f084fd49b6ac2c564b9232d25..cd504262194c4653bd79b656a39be35567fea2bc 100644 --- a/src/sbbs3/main.cpp +++ b/src/sbbs3/main.cpp @@ -1071,7 +1071,7 @@ js_write_raw(JSContext *cx, uintN argc, jsval *arglist) if (len < 1) continue; rc = JS_SUSPENDREQUEST(cx); - sbbs->putcom(str, len); + sbbs->term_out(str, len); JS_RESUMEREQUEST(cx, rc); } if (str != NULL) @@ -4099,15 +4099,6 @@ int sbbs_t::_outcom(uchar ch) return TXBOF; if (!RingBufWrite(&outbuf, &ch, 1)) return TXBOF; - if (term->lbuflen < LINE_BUFSIZE) { - if (term->lbuflen == 0) - term->latr = term->curatr; - // Historically, beeps don't go into lbuf - // hopefully nobody notices and cares, because this would mean - // that BEL *must* be part of any charset we support... and it's - // not part of C64 PETSCII. - term->lbuf[term->lbuflen++] = ch; - } return 0; } @@ -5585,7 +5576,7 @@ NO_SSH: if (inet_addrport(&local_addr) == startup->pet40_port || inet_addrport(&local_addr) == startup->pet80_port) { sbbs->autoterm = PETSCII; sbbs->term->cols = inet_addrport(&local_addr) == startup->pet40_port ? 40 : 80; - sbbs->outcom(PETSCII_UPPERLOWER); + sbbs->term_out(PETSCII_UPPERLOWER); } update_terminal(sbbs); // TODO: Plain text output in SSH socket @@ -5670,7 +5661,7 @@ NO_SSH: if (!(startup->options & BBS_OPT_NO_HOST_LOOKUP)) { sbbs->bprintf("Resolving hostname..."); getnameinfo(&client_addr.addr, client_addr_len, host_name, sizeof(host_name), NULL, 0, NI_NAMEREQD); - sbbs->putcom(crlf); + sbbs->cp437_out(crlf); lprintf(LOG_INFO, "%04d %s [%s] Hostname: %s", client_socket, client.protocol, host_ip, host_name); } @@ -5697,7 +5688,7 @@ NO_SSH: lprintf(LOG_INFO, "%04d %s [%s] Identity: %s", client_socket, client.protocol, host_ip, identity); } } - sbbs->putcom(crlf); + sbbs->cp437_out(crlf); } /* Initialize client display */ client.size = sizeof(client); @@ -5759,8 +5750,8 @@ NO_SSH: if (fexist(str)) sbbs->printfile(str, P_NOABORT); else { - sbbs->putcom("\r\nSorry, all terminal nodes are in use or otherwise unavailable.\r\n"); - sbbs->putcom("Please try again later.\r\n"); + sbbs->cp437_out("\r\nSorry, all terminal nodes are in use or otherwise unavailable.\r\n"); + sbbs->cp437_out("Please try again later.\r\n"); } sbbs->flush_output(3000); client_off(client_socket); @@ -5835,7 +5826,7 @@ NO_SSH: if (fexist(str)) sbbs->printfile(str, P_NOABORT); else - sbbs->putcom("\r\nSorry, initialization failed. Try again later.\r\n"); + sbbs->cp437_out("\r\nSorry, initialization failed. Try again later.\r\n"); sbbs->flush_output(3000); if (sbbs->getnodedat(new_node->cfg.node_num, &node, true)) { node.status = NODE_WFC; diff --git a/src/sbbs3/newuser.cpp b/src/sbbs3/newuser.cpp index 7775414cbce12091cdd0ec6a671a3907227456c2..9fc56bfd379768413c2fbb9a99975b636b02c7ce 100644 --- a/src/sbbs3/newuser.cpp +++ b/src/sbbs3/newuser.cpp @@ -151,7 +151,7 @@ bool sbbs_t::newuser() if (useron.misc & PETSCII) { autoterm |= PETSCII; - outcom(PETSCII_UPPERLOWER); + term_out(PETSCII_UPPERLOWER); bputs(text[PetTerminalDetected]); } else { if (!yesno(text[ExAsciiTerminalQ])) diff --git a/src/sbbs3/petscii_term.cpp b/src/sbbs3/petscii_term.cpp index e46958ae5fe219a715feb008df590ea3ef35b086..94ad9fbd4a6c7bad3904c1a8418c54ff77759801 100644 --- a/src/sbbs3/petscii_term.cpp +++ b/src/sbbs3/petscii_term.cpp @@ -162,14 +162,11 @@ char* PETSCII_Terminal::attrstr(unsigned atr, unsigned curatr, char* str, size_t bool PETSCII_Terminal::gotoxy(unsigned x, unsigned y) { - sbbs->outcom(PETSCII_HOME); - row = 0; - column = 0; - lncntr = 0; - lastlinelen = 0; - lbuflen = 0; - cursor_down(y - 1); - cursor_right(x - 1); + sbbs->term_out(PETSCII_HOME); + while (row < (y - 1)) + sbbs->term_out(PETSCII_DOWN); + while (column < (x - 1)) + sbbs->term_out(PETSCII_RIGHT); return true; } @@ -196,38 +193,24 @@ void PETSCII_Terminal::carriage_return() void PETSCII_Terminal::line_feed(unsigned count) { // Like cursor_down() but scrolls... - for (unsigned i = 0; i < count; i++) { - sbbs->outcom(PETSCII_DOWN); - inc_row(); - lastlinelen = column; - } + for (unsigned i = 0; i < count; i++) + sbbs->term_out(PETSCII_DOWN); } void PETSCII_Terminal::backspace(unsigned int count) { - sbbs->outcom(PETSCII_DELETE); + sbbs->term_out(PETSCII_DELETE); } void PETSCII_Terminal::newline(unsigned count) { - char str[128]; - - sbbs->outcom('\r'); - unsigned prevatr = curatr; - sbbs->putcom(attrstr(prevatr, (curatr >> 4) & 0x07, str, sizeof(str))); - inc_row(); - column = 0; + sbbs->term_out('\r'); } void PETSCII_Terminal::clearscreen() { clear_hotspots(); - sbbs->outcom('\x93'); - row = 0; - column = 0; - lncntr = 0; - lastlinelen = 0; - lbuflen = 0; + sbbs->term_out('\x93'); } void PETSCII_Terminal::cleartoeos() @@ -247,10 +230,8 @@ void PETSCII_Terminal::cleartoeol() { unsigned s; s = column; - while (++s <= cols) { - sbbs->outcom(' '); - sbbs->outcom('\x14'); - } + while (++s <= cols) + sbbs->term_out(" \x14"); } void PETSCII_Terminal::clearline() @@ -263,25 +244,13 @@ void PETSCII_Terminal::clearline() void PETSCII_Terminal::cursor_home() { - sbbs->outcom(PETSCII_HOME); - row = 0; - column = 0; - lncntr = 0; - lastlinelen = 0; - lbuflen = 0; + sbbs->term_out(PETSCII_HOME); } void PETSCII_Terminal::cursor_up(unsigned count) { - for (unsigned i = 0; i < count; i++) { - sbbs->outcom('\x91'); - if (row > 0) - row--; - if (lncntr > 0) - lncntr--; - lastlinelen = column; - lbuflen = 0; - } + for (unsigned i = 0; i < count; i++) + sbbs->term_out('\x91'); } void PETSCII_Terminal::cursor_down(unsigned count) @@ -289,9 +258,7 @@ void PETSCII_Terminal::cursor_down(unsigned count) for (unsigned i = 0; i < count; i++) { if (row >= (rows - 1)) break; - sbbs->outcom(PETSCII_DOWN); - inc_row(); - lastlinelen = column; + sbbs->term_out(PETSCII_DOWN); } } @@ -300,17 +267,16 @@ void PETSCII_Terminal::cursor_right(unsigned count) for (unsigned i = 0; i < count; i++) { if (column >= (cols - 1)) break; - sbbs->outcom(PETSCII_RIGHT); - inc_column(); + sbbs->term_out(PETSCII_RIGHT); } } void PETSCII_Terminal::cursor_left(unsigned count) { for (unsigned i = 0; i < count; i++) { - sbbs->outcom('\x9d'); - if (column > 0) - column--; + sbbs->term_out('\x9d'); + if (column == 0) + break; } } @@ -321,8 +287,6 @@ const char* PETSCII_Terminal::type() bool PETSCII_Terminal::parse_outchar(char ch) { - if (!required_parse_outchar(ch)) - return false; switch (ch) { // Zero-width characters we likely shouldn't send case 0: @@ -332,12 +296,7 @@ bool PETSCII_Terminal::parse_outchar(char ch) case 4: case 6: case 7: - //case 8: // Translated as Backspace - //case 9: // Transpated as Tab - //case 10: // Translated as Linefeed case 11: - //case 12: // Translated as Form Feed - //case 13: // Translated as Carriage Return case 14: case 15: case 16: @@ -363,35 +322,216 @@ bool PETSCII_Terminal::parse_outchar(char ch) case '\x8F': return false; - // Zero-width characters we want to pass through - case 5: // White + // Specials that affect cursor position + //case 9: // TODO: Tab or unlock case... + //case 10: // TODO: Linefeed or nothing + case '\x8D': // Shift-return + case 13: // Translated as Carriage Return + inc_row(); + set_column(0); + if (curatr & 0xf0) + curatr = (curatr & ~0xff) | ((curatr & 0xf0) >> 4); + return true; case 17: // Cursor down - case 18: // Reverse on + inc_row(); + return true; case 19: // Home + set_row(); + set_column(); + return true; case 20: // Delete - case 28: // Red + if (column == 0) { + dec_row(); + set_column(cols - 1); + } + else + dec_column(); + return true; case 29: // Cursor right + inc_column(); + return true; + case '\x91': // Cursor up + dec_row(); + return true; + case '\x9D': // Cursor Left + dec_column(); + return true; + + // TODO: Parse attributes + // Zero-width characters we want to pass through + case 18: // Reverse on + if (!reverse_on) { + reverse_on = true; + curatr = ((curatr & 0xf0) >> 4) | ((curatr & 0x0f) << 4); + } + return true; + case '\x92': // Reverse off + if (reverse_on) { + reverse_on = false; + curatr = ((curatr & 0xf0) >> 4) | ((curatr & 0x0f) << 4); + } + return true; + case 5: // White + if (reverse_on) { + curatr &= ~0xF0; + curatr |= (WHITE << 4); + } + else { + curatr &= ~0x0F; + curatr |= WHITE; + } + return true; + case 28: // Red + if (reverse_on) { + curatr &= ~0xF0; + curatr |= (RED << 4); + } + else { + curatr &= ~0x0F; + curatr |= RED; + } + return true; case 30: // Green + if (reverse_on) { + curatr &= ~0xF0; + curatr |= (GREEN << 4); + } + else { + curatr &= ~0x0F; + curatr |= GREEN; + } + return true; case 31: // Blue + if (reverse_on) { + curatr &= ~0xF0; + curatr |= (BLUE << 4); + } + else { + curatr &= ~0x0F; + curatr |= BLUE; + } + return true; case '\x81': // Orange - case '\x8D': // Shift-return - case '\x8E': // Upper case + if (reverse_on) { + curatr &= ~0xF0; + curatr |= (MAGENTA << 4); + } + else { + curatr &= ~0x0F; + curatr |= MAGENTA; + } + return true; case '\x90': // Black - case '\x91': // Cursor up - case '\x92': // Reverse off - case '\x93': // Clear - case '\x94': // Insert + if (reverse_on) { + curatr &= ~0xF0; + } + else { + curatr &= ~0x0F; + } + return true; case '\x95': // Brown + if (reverse_on) { + curatr &= ~0xF0; + curatr |= (BROWN << 4); + } + else { + curatr &= ~0x0F; + curatr |= BROWN; + } + return true; case '\x96': // Pink + if (reverse_on) { + curatr &= ~0xF0; + curatr |= (LIGHTRED << 4); + } + else { + curatr &= ~0x0F; + curatr |= LIGHTRED; + } + return true; case '\x97': // Dark gray + if (reverse_on) { + curatr &= ~0xF0; + curatr |= (DARKGRAY << 4); + } + else { + curatr &= ~0x0F; + curatr |= DARKGRAY; + } + return true; case '\x98': // Gray + if (reverse_on) { + curatr &= ~0xF0; + curatr |= (CYAN << 4); + } + else { + curatr &= ~0x0F; + curatr |= CYAN; + } + return true; case '\x99': // Light Green + if (reverse_on) { + curatr &= ~0xF0; + curatr |= (LIGHTGREEN << 4); + } + else { + curatr &= ~0x0F; + curatr |= LIGHTGREEN; + } + return true; case '\x9A': // Light Blue + if (reverse_on) { + curatr &= ~0xF0; + curatr |= (LIGHTBLUE << 4); + } + else { + curatr &= ~0x0F; + curatr |= LIGHTBLUE; + } + return true; case '\x9B': // Light Gray + if (reverse_on) { + curatr &= ~0xF0; + curatr |= (LIGHTGRAY << 4); + } + else { + curatr &= ~0x0F; + curatr |= LIGHTGRAY; + } + return true; case '\x9C': // Purple - case '\x9D': // Cursor Left + if (reverse_on) { + curatr &= ~0xF0; + curatr |= (LIGHTMAGENTA << 4); + } + else { + curatr &= ~0x0F; + curatr |= LIGHTMAGENTA; + } + return true; case '\x9E': // Yellow + if (reverse_on) { + curatr &= ~0xF0; + curatr |= (YELLOW << 4); + } + else { + curatr &= ~0x0F; + curatr |= YELLOW; + } + return true; case '\x9F': // Cyan + if (reverse_on) { + curatr &= ~0xF0; + curatr |= (LIGHTCYAN << 4); + } + else { + curatr &= ~0x0F; + curatr |= LIGHTCYAN; + } + return true; + case '\x8E': // Upper case + case '\x93': // Clear + case '\x94': // Insert return true; // Everything else is assumed one byte wide @@ -468,10 +608,10 @@ void PETSCII_Terminal::insert_indicator() gotoxy(cols, 1); if (sbbs->console & CON_INSERT) { sbbs->attr(BLINK | BLACK | (LIGHTGRAY << 4)); - sbbs->outchar('I'); + sbbs->term_out('I'); } else { sbbs->attr(ANSI_NORMAL); - sbbs->outchar(' '); + sbbs->term_out(' '); } sbbs->attr(oldatr); gotoxy(x, y); diff --git a/src/sbbs3/petscii_term.h b/src/sbbs3/petscii_term.h index e3bc664f5f7d7f8a8e78f0fe926c56f53b20931c..b1c0eea7c66bee820607de6dfcaf78a173bd68c4 100644 --- a/src/sbbs3/petscii_term.h +++ b/src/sbbs3/petscii_term.h @@ -7,6 +7,7 @@ class PETSCII_Terminal : public Terminal { private: unsigned saved_x{0}; unsigned saved_y{0}; + bool reverse_on{false}; public: diff --git a/src/sbbs3/prntfile.cpp b/src/sbbs3/prntfile.cpp index 0c4a479ef30e19410de86c06e3351cf373c79792..94d6f743e8a8be36fcb202cbf00f053b2499eb2a 100644 --- a/src/sbbs3/prntfile.cpp +++ b/src/sbbs3/prntfile.cpp @@ -150,7 +150,7 @@ bool sbbs_t::printfile(const char* fname, int mode, int org_cols, JSObject* obj) if (!(mode & P_NOATCODES) && term->cur_output_rate != output_rate) term->set_output_rate(output_rate); if (mode & P_PETSCII) - outcom(PETSCII_UPPERLOWER); + term_out(PETSCII_UPPERLOWER); attr_sp = 0; /* clear any saved attributes */ /* Restore original settings of Forced Pause On/Off */ diff --git a/src/sbbs3/putmsg.cpp b/src/sbbs3/putmsg.cpp index d9ab734dfc10ce8b83bfc4d8b7fe2a5fd5e42614..92feebc61f518f917a22af3aafff9eb473c8a5e4 100644 --- a/src/sbbs3/putmsg.cpp +++ b/src/sbbs3/putmsg.cpp @@ -62,7 +62,7 @@ char sbbs_t::putmsg(const char *buf, int mode, int org_cols, JSObject* obj) term->set_output_rate(output_rate); if (mode & P_PETSCII) - outcom(PETSCII_UPPERLOWER); + term_out(PETSCII_UPPERLOWER); attr_sp = 0; /* clear any saved attributes */ @@ -499,57 +499,12 @@ char sbbs_t::putmsgfrag(const char* buf, int& mode, unsigned org_cols, JSObject* size_t skip = sizeof(char); if (mode & P_PETSCII) { if (term->flags & PETSCII) { - outcom(str[l]); - // TODO: Do this in outcom() - // Splitting it out here is bad. - // It's very similar to what parse_outchar() does. - switch ((uchar)str[l]) { - case '\r': // PETSCII "Return" / new-line - term->column = 0; - /* fall-through */ - case PETSCII_DOWN: - term->lncntr++; - term->inc_row(); - break; - case PETSCII_CLEAR: - case PETSCII_HOME: - term->row = 0; - term->column = 0; - term->lncntr = 0; - break; - case PETSCII_BLACK: - case PETSCII_WHITE: - case PETSCII_RED: - case PETSCII_GREEN: - case PETSCII_BLUE: - case PETSCII_ORANGE: - case PETSCII_BROWN: - case PETSCII_YELLOW: - case PETSCII_CYAN: - case PETSCII_LIGHTRED: - case PETSCII_DARKGRAY: - case PETSCII_MEDIUMGRAY: - case PETSCII_LIGHTGREEN: - case PETSCII_LIGHTBLUE: - case PETSCII_LIGHTGRAY: - case PETSCII_PURPLE: - case PETSCII_UPPERLOWER: - case PETSCII_UPPERGRFX: - case PETSCII_FLASH_ON: - case PETSCII_FLASH_OFF: - case PETSCII_REVERSE_ON: - case PETSCII_REVERSE_OFF: - // No cursor movement - break; - default: - term->inc_column(); - break; - } + term_out(str[l]); } else petscii_to_ansibbs(str[l]); } else if ((str[l] & 0x80) && (mode & P_UTF8)) { if (term->flags & UTF8) - outcom(str[l]); + term_out(str[l]); else skip = print_utf8_as_cp437(str + l, len - l); } else { diff --git a/src/sbbs3/sbbs.h b/src/sbbs3/sbbs.h index 435ab96bcdd31865909930aefe3bdec0766acd0b..2725db4c02340f6a014eb2ef88eea22c77a67550 100644 --- a/src/sbbs3/sbbs.h +++ b/src/sbbs3/sbbs.h @@ -592,7 +592,7 @@ public: char prev_key(void) { return toupper(*text[Previous]); } char dszlog[127]{}; /* DSZLOG environment variable */ - int keybuftop=0, keybufbot=0; /* Keyboard input buffer pointers (for ungetkey) */ + int keybuftop=0, keybufbot=0; /* Keyboard input buffer pointers (for ungetkey) */ char keybuf[KEY_BUFSIZE]{}; /* Keyboard input buffer */ size_t keybuf_space(void); size_t keybuf_level(void); @@ -875,7 +875,7 @@ public: /* con_out.cpp */ int bputs(const char *str, int mode = 0); /* BBS puts function */ int bputs(int mode, const char* str) { return bputs(str, mode); } - int rputs(const char *str, size_t len=0); /* BBS raw puts function */ + size_t rputs(const char *str, size_t len=0); /* BBS raw puts function */ int rputs(int mode, const char* str) { return rputs(str, mode); } int bprintf(const char *fmt, ...) /* BBS printf function */ #if defined(__GNUC__) // Catch printf-format errors @@ -892,14 +892,14 @@ public: __attribute__ ((format (printf, 2, 3))); // 1 is 'this' #endif ; - int comprintf(const char *fmt, ...) /* BBS direct-comm printf function */ + int term_printf(const char *fmt, ...) /* BBS direct-comm printf function */ #if defined(__GNUC__) // Catch printf-format errors __attribute__ ((format (printf, 2, 3))); // 1 is 'this' #endif ; int outchar(char ch); /* Output a char - check echo and emu. */ - int outchar(enum unicode_codepoint, char cp437_fallback); - int outchar(enum unicode_codepoint, const char* cp437_fallback = NULL); + int outcp(enum unicode_codepoint, char cp437_fallback); + int outcp(enum unicode_codepoint, const char* cp437_fallback = NULL); void wide(const char*); // TODO: There appear to describe the USER, not the terminal... char* term_rows(user_t*, char* str, size_t); @@ -918,6 +918,10 @@ public: char* auto_utf8(const char*, int& mode); // TODO: Not To ANSI_Terminal void getdimensions(); + size_t term_out(const char *str, size_t len = SIZE_MAX); + size_t term_out(int ch); + size_t cp437_out(const char *str, size_t len = SIZE_MAX); + size_t cp437_out(int ch); /* getstr.cpp */ size_t getstr_offset = 0; diff --git a/src/sbbs3/terminal.cpp b/src/sbbs3/terminal.cpp index 5f4ae560e3ee2d619faf2c4f45340d7c9649004a..1f4b7dc9b0df35609de57e7a49a6b20308b74bfe 100644 --- a/src/sbbs3/terminal.cpp +++ b/src/sbbs3/terminal.cpp @@ -3,47 +3,6 @@ #include "petscii_term.h" #include "link_list.h" -/* - * Returns true if the caller should send the char, false if - * this function handled it (ie: via outcom(), or stripping it) - */ -bool Terminal::required_parse_outchar(char ch) { - switch (ch) { - // Special values - case 8: // BS - cursor_left(); - return false; - case 9: - // TODO: Original would wrap, this one (hopefully) doesn't. - // Further, use outchar() instead of outcom() to get - // the spaces into the line buffer instead of tabs - if (column < (cols - 1)) { - sbbs->outchar(' '); - while ((column < (cols - 1)) && (column % tabstop)) - sbbs->outchar(' '); - } - return false; - case 10: // LF - // Terminates lbuf - if (sbbs->line_delay) - SLEEP(sbbs->line_delay); - line_feed(); - return false; - case 12: // FF - // Does not go into lbuf - check_clear_pause(); - clearscreen(); - return false; - case 13: // CR - if (sbbs->console & CON_CR_CLREOL) - cleartoeol(); - carriage_return(); - return false; - // Everything else is assumed one byte wide - } - return true; -} - void Terminal::clear_hotspots(void) { if (!(flags & MOUSE)) @@ -51,7 +10,7 @@ void Terminal::clear_hotspots(void) int spots = listCountNodes(mouse_hotspots); if (spots) { #if 0 //def _DEBUG - lprintf(LOG_DEBUG, "Clearing %ld mouse hot spots", spots); + sbbs->lprintf(LOG_DEBUG, "Clearing %ld mouse hot spots", spots); #endif listFreeNodes(mouse_hotspots); if (!(sbbs->console & CON_MOUSE_SCROLL)) @@ -74,7 +33,7 @@ void Terminal::scroll_hotspots(unsigned count) } #ifdef _DEBUG if (spots) - lprintf(LOG_DEBUG, "Scrolled %u mouse hot-spots %u rows (%u remain)", spots, count, remain); + sbbs->lprintf(LOG_DEBUG, "Scrolled %u mouse hot-spots %u rows (%u remain)", spots, count, remain); #endif if (remain < 1) clear_hotspots(); @@ -198,6 +157,50 @@ void Terminal::inc_column(unsigned count) { } } +void Terminal::dec_row(unsigned count) { + if (column) + lastlinelen = column; + // TODO: Never allow dec_row to scroll up + if (count > row) + count = row; +#if 0 + // TODO: If we do allow scrolling up, scroll_hotspots needs to get signed + if (count > row) { + scroll_hotspots(row - count); + } +#endif + row -= count; + if (count > lncntr) + count = lncntr; + lncntr -= count; + lbuflen = 0; +} + +void Terminal::dec_column(unsigned count) { + // TODO: Never allow dec_column() to wrap + if (count > column) + count = column; + column -= count; + if (column == 0) + lbuflen = 0; +} + +void Terminal::set_row(unsigned val) { + if (val >= rows) + val = rows - 1; + if (column) + lastlinelen = column; + row = val; + lncntr = 0; + lbuflen = 0; +} + +void Terminal::set_column(unsigned val) { + if (val >= cols) + val = cols - 1; + column = val; +} + void Terminal::cond_newline() { if (column > 0) newline(); @@ -245,19 +248,6 @@ list_node_t *Terminal::find_hotspot(unsigned x, unsigned y) return node; } -void Terminal::check_clear_pause() -{ - if (lncntr > 0 && row > 0) { - lncntr = 0; - newline(); - if (!(sbbs->sys_status & SS_PAUSEOFF)) { - sbbs->pause(); - while (lncntr && sbbs->online && !(sbbs->sys_status & SS_ABORT)) - sbbs->pause(); - } - } -} - static void flags_fixup(uint32_t& flags) { diff --git a/src/sbbs3/terminal.h b/src/sbbs3/terminal.h index d5d5bd50e8b8ba81ffcf52ffc630d4309b8beb28..c954abf8b36807619455fad364ac44c118c03414 100644 --- a/src/sbbs3/terminal.h +++ b/src/sbbs3/terminal.h @@ -181,34 +181,28 @@ public: } virtual void carriage_return() { - lastlinelen = column; - sbbs->outcom('\r'); - column = 0; + sbbs->term_out('\r'); + set_column(); } virtual void line_feed(unsigned count = 1) { for (unsigned i = 0; i < count; i++) - sbbs->outcom('\n'); + sbbs->term_out('\n'); inc_row(count); - lbuflen = 0; } /* * Destructive backspace. * TODO: This seems to be the only one of this family that * checks CON_ECHO_OFF itself. Figure out why and either - * remove it, or add it to all the rest that call outcom() + * remove it, or add it to all the rest that call term_out() */ virtual void backspace(unsigned int count = 1) { if (sbbs->console & CON_ECHO_OFF) return; for (unsigned i = 0; i < count; i++) { - if (column > 0) { + if (column > 0) sbbs->putcom("\b \b"); - column--; - if (lbuflen) - lbuflen--; - } else break; } @@ -224,14 +218,10 @@ public: } virtual void clearscreen() { - check_clear_pause(); clear_hotspots(); - sbbs->outcom(FF); - row = 0; - column = 0; - lncntr = 0; - lbuflen = 0; - lastlinelen = 0; + sbbs->term_out(FF); + set_row(); + set_column(); } virtual void cleartoeos() {} @@ -250,7 +240,7 @@ public: virtual void cursor_right(unsigned count = 1) { for (unsigned i = 0; i < count; i++) { if (column < (cols - 1)) - sbbs->outcom(' '); + sbbs->term_out(' '); else break; } @@ -258,10 +248,8 @@ public: virtual void cursor_left(unsigned count = 1) { for (unsigned i = 0; i < count; i++) { - if (column > 0) { - sbbs->outcom('\b'); - column--; - } + if (column > 0) + sbbs->term_out('\b'); else break; } @@ -324,14 +312,9 @@ public: /* * Returns true if the caller should send the char, false if - * this function handled it (ie: via outcom(), or stripping it) + * this function handled it (ie: via term_out(), or stripping it) */ virtual bool parse_outchar(char ch) { - if (!lbuflen) - latr = curatr; - if (!required_parse_outchar(ch)) - return false; - switch (ch) { // Zero-width characters we likely shouldn't send case 0: // NUL @@ -355,7 +338,6 @@ public: case 24: // CAN case 25: // EM case 26: // SUB - case 27: // ESC - This one is especially troubling case 28: // FS case 29: // GS case 30: // RS @@ -363,14 +345,40 @@ public: return false; // Zero-width characters we want to pass through + case 27: // ESC - This one is especially troubling, but we need to pass it for ANSI detection case 7: // BEL // Does not go into lbuf... return true; + // Specials + case 8: // BS + if (column) + dec_column(); + return true; + case 9: // TAB + // TODO: This makes the position unknown + if (column < (cols - 1)) { + inc_column(); + while ((column < (cols - 1)) && (column % 8)) + inc_column(); + } + return true; + case 10: // LF + inc_row(); + return true; + case 12: // FF + // TODO: This makes the position unknown + set_row(); + set_column(); + return true; + case 13: // CR + if (sbbs->console & CON_CR_CLREOL) + cleartoeol(); + set_column(); + return true; + // Everything else is assumed one byte wide default: - if (lbuflen < LINE_BUFSIZE) - lbuf[lbuflen++] = ch; inc_column(); return true; } @@ -411,15 +419,17 @@ public: if (line == NULL) return false; lbuflen = 0; + // Moved insert_indicator() to first to avoid messing + // up the line buffer with it. + // Now behaves differently on line 1 (where we should + // never actually see it) + insert_indicator(); sbbs->attr(line->beg_attr); - // Switch from rputs to outcom() loop + // Switch from rputs to term_out() // This way we don't need to re-encode - for (unsigned u = 0; line->buf[u]; u++) - sbbs->outcom(line->buf[u]); + sbbs->term_out(line->buf); curatr = line->end_attr; - column = line->column; free(line); - insert_indicator(); return true; } @@ -439,8 +449,6 @@ public: return true; } - bool required_parse_outchar(char ch); - void clear_hotspots(void); void scroll_hotspots(unsigned count); @@ -452,12 +460,15 @@ public: bool add_pause_hotspot(char cmd); void inc_row(unsigned count = 1); void inc_column(unsigned count = 1); + void dec_row(unsigned count = 1); + void dec_column(unsigned count = 1); + void set_row(unsigned val = 0); + void set_column(unsigned val = 0); void cond_newline(); void cond_blankline(); void cond_contline(); bool supports(unsigned cmp_flags); list_node_t *find_hotspot(unsigned x, unsigned y); - void check_clear_pause(); }; void update_terminal(sbbs_t *sbbsptr); diff --git a/src/sbbs3/useredit.cpp b/src/sbbs3/useredit.cpp index b9dc08aa5f1412eec2b5d016bf72dfbeb78475ed..f51964c22175e8deaef38ce8171952aadc6114a5 100644 --- a/src/sbbs3/useredit.cpp +++ b/src/sbbs3/useredit.cpp @@ -946,7 +946,7 @@ void sbbs_t::user_config(user_t* user) else if (key == PETSCII_DELETE) { autoterm |= PETSCII; user->misc |= PETSCII; - outcom(PETSCII_UPPERLOWER); + term_out(PETSCII_UPPERLOWER); bputs(text[PetTerminalDetected]); } else