diff --git a/exec/load/cga_defs.js b/exec/load/cga_defs.js index 2a59c873760f85cb6d7a40f4a04db85a1f546fdf..7abe627e00e4efd11e5947f4bc821e9ead6d70f8 100644 --- a/exec/load/cga_defs.js +++ b/exec/load/cga_defs.js @@ -44,10 +44,15 @@ var colors = [ 'YELLOW', 'WHITE' ]; - /* background colors */ -var ANSI_NORMAL =0x100; /* special value for ansi() */ +var FG_UNKNOWN =0x100; var BG_BLACK =0x200; /* special value for ansi() */ var BG_HIGH =0x400; /* not an ANSI.SYS compatible attribute */ +var REVERSED =0x800; +var UNDERLINE =0x1000; +var CONCEALED =0x2000; +var BG_UNKNOWN =0x4000; +var ANSI_NORMAL =(FG_UNKNOWN | BG_UNKNOWN); + /* background colors */ var BG_BLUE =(BLUE<<4); var BG_GREEN =(GREEN<<4); var BG_CYAN =(CYAN<<4); diff --git a/exec/xbimage.js b/exec/xbimage.js index 5f0b5df64c0cf7c3dba86be12576093f981c053c..4cc6f97646e3970733e463ba6e65c9514abd6635 100644 --- a/exec/xbimage.js +++ b/exec/xbimage.js @@ -67,7 +67,7 @@ function convert_from_bmp(filename, charheight, fg_color, bg_color, palette, inv function show(filename, xpos, ypos, fg_color, bg_color, palette, delay, cleanup) { - if((console.term_supports()&(USER_ANSI|USER_NO_EXASCII|USER_UTF8|USER_ICE_COLOR)) + if((console.term_supports()&(USER_ANSI|USER_NO_EXASCII|USER_UTF8)) != USER_ANSI) return false; diff --git a/src/sbbs3/ansi_parser.cpp b/src/sbbs3/ansi_parser.cpp new file mode 100644 index 0000000000000000000000000000000000000000..42ed0eebd0b5f0e654c781db4b6efd6d7d419604 --- /dev/null +++ b/src/sbbs3/ansi_parser.cpp @@ -0,0 +1,193 @@ +#include "ansi_parser.h" + +#include <stdio.h> +enum ansiState +ANSI_Parser::parse(unsigned char ch) +{ + switch (state) { + case ansiState_none: + if (ch == '\x1b') { + state = ansiState_esc; + ansi_sequence += ch; + } + break; + case ansiState_esc: + ansi_sequence += ch; + if (ch == '[') { + state = ansiState_csi; + ansi_params = ""; + } + else if (ch == '_' || ch == 'P' || ch == '^' || ch == ']') { + state = ansiState_string; + ansi_was_string = true; + } + else if (ch == 'X') { + state = ansiState_sos; + ansi_was_string = true; + } + else if (ch >= ' ' && ch <= '/') { + ansi_ibs += ch; + state = ansiState_intermediate; + } + else if (ch >= '0' && ch <= '~') { + state = ansiState_final; + ansi_was_cc = true; + ansi_final_byte = ch; + } + else { + state = ansiState_broken; + } + break; + case ansiState_csi: + 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; + state = ansiState_intermediate; + } + else if (ch >= '@' && ch <= '~') { + state = ansiState_final; + ansi_final_byte = ch; + } + else { + state = ansiState_broken; + } + break; + case ansiState_intermediate: + ansi_sequence += ch; + if (ch >= ' ' && ch <= '/') { + ansi_ibs += ch; + state = ansiState_intermediate; + } + else if (ch >= '@' && ch <= '~') { + state = ansiState_final; + ansi_final_byte = ch; + } + else { + state = ansiState_broken; + } + break; + case ansiState_string: // APS, DCS, PM, or OSC + ansi_sequence += ch; + if (ch == '\x1b') + state = ansiState_esc; + else if (!((ch >= '\b' && ch <= '\r') || (ch >= ' ' && ch <= '~'))) + state = ansiState_broken; + break; + case ansiState_sos: // SOS + ansi_sequence += ch; + if (ch == '\x1b') + state = ansiState_sos_esc; + break; + case ansiState_sos_esc: // ESC inside SOS + ansi_sequence += ch; + if (ch == '\\') + state = ansiState_esc; + else if (ch == 'X') + state = ansiState_broken; + else + state = ansiState_sos; + break; + case ansiState_broken: + // Stay in broken state. + break; + case ansiState_final: + // Stay in final state. + break; + } + return state; +} + +enum ansiState +ANSI_Parser::current_state() +{ + return state; +} + +void +ANSI_Parser::reset() +{ + ansi_params.clear(); + ansi_ibs.clear(); + ansi_sequence.clear(); + state = ansiState_none; + ansi_final_byte = 0; + ansi_was_cc = false; + ansi_was_string = false; + ansi_was_private = false; +} + +unsigned +ANSI_Parser::count_params() +{ + std::string tp = ansi_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; + } +} + +unsigned +ANSI_Parser::get_pval(unsigned pnum, unsigned dflt) +{ + try { + if (ansi_params == "") + return dflt; + unsigned p = 0; + std::string tp = ansi_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; + } +} + diff --git a/src/sbbs3/ansi_parser.h b/src/sbbs3/ansi_parser.h new file mode 100644 index 0000000000000000000000000000000000000000..a038c9b99206f9ab835e82cd6e0ee62f624a9fb3 --- /dev/null +++ b/src/sbbs3/ansi_parser.h @@ -0,0 +1,38 @@ +#ifndef ANSI_PARSE_H +#define ANSI_PARSE_H + +#include <string> + +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 + ,ansiState_sos_esc // ESC inside SOS + ,ansiState_broken // Invalid ANSI +}; + +class ANSI_Parser { +public: + enum ansiState parse(unsigned char ch); + enum ansiState current_state(); + void reset(); + unsigned count_params(); + unsigned get_pval(unsigned pnum, unsigned dflt); + + std::string ansi_sequence{""}; + std::string ansi_params{""}; + std::string ansi_ibs{""}; + char ansi_final_byte{0}; + bool ansi_was_cc{false}; + bool ansi_was_string{false}; + bool ansi_was_private{false}; + +private: + enum ansiState state{ansiState_none}; // track ANSI escape seq output +}; + +#endif diff --git a/src/sbbs3/ansi_terminal.cpp b/src/sbbs3/ansi_terminal.cpp new file mode 100644 index 0000000000000000000000000000000000000000..ba151d8383e0e2dd9d20edd12f48457935608e5d --- /dev/null +++ b/src/sbbs3/ansi_terminal.cpp @@ -0,0 +1,1168 @@ +#include "ansi_terminal.h" + +enum ansi_mouse_mode { + ANSI_MOUSE_X10 = 9, + ANSI_MOUSE_NORM = 1000, + ANSI_MOUSE_BTN = 1002, + ANSI_MOUSE_ANY = 1003, + ANSI_MOUSE_EXT = 1006 +}; + +// Was ansi() +const char *ANSI_Terminal::attrstr(unsigned atr) +{ + switch (atr) { + + /* Special case */ + case ANSI_NORMAL: + return "\x1b[0m"; + case BLINK: + case BG_BRIGHT: + return "\x1b[5m"; + + /* Foreground */ + case HIGH: + return "\x1b[1m"; + case BLACK: + return "\x1b[30m"; + case RED: + return "\x1b[31m"; + case GREEN: + return "\x1b[32m"; + case BROWN: + return "\x1b[33m"; + case BLUE: + return "\x1b[34m"; + case MAGENTA: + return "\x1b[35m"; + case CYAN: + return "\x1b[36m"; + case LIGHTGRAY: + return "\x1b[37m"; + + /* Background */ + case BG_BLACK: + return "\x1b[40m"; + case BG_RED: + return "\x1b[41m"; + case BG_GREEN: + return "\x1b[42m"; + case BG_BROWN: + return "\x1b[43m"; + case BG_BLUE: + return "\x1b[44m"; + case BG_MAGENTA: + return "\x1b[45m"; + case BG_CYAN: + return "\x1b[46m"; + case BG_LIGHTGRAY: + return "\x1b[47m"; + } + + return "-Invalid use of ansi()-"; +} + +static uint32_t +popcnt(const uint32_t val) +{ + uint32_t i = val; + + // Clang optimizes this to popcnt on my system. + i = i - ((i >> 1) & 0x55555555); + i = (i & 0x33333333) + ((i >> 2) & 0x33333333); + i = (i + (i >> 4)) & 0x0F0F0F0F; + i *= 0x01010101; + return i >> 24; +} + +// Was ansi() and ansi_attr() +char* ANSI_Terminal::attrstr(unsigned atr, unsigned curatr, char* str, size_t strsz) +{ + if (!supports(COLOR)) { /* eliminate colors if terminal doesn't support them */ + if (atr & LIGHTGRAY) /* if any foreground bits set, set all */ + atr |= LIGHTGRAY; + if (atr & BG_LIGHTGRAY) /* if any background bits set, set all */ + atr |= BG_LIGHTGRAY; + if ((atr & LIGHTGRAY) && (atr & BG_LIGHTGRAY)) + atr &= ~LIGHTGRAY; /* if background is solid, foreground is black */ + if (!atr) + atr |= LIGHTGRAY; /* don't allow black on black */ + } + + if (atr & FG_UNKNOWN) + atr &= ~0x07; + if (atr & BG_UNKNOWN) + atr &= ~0x70; + if (curatr & FG_UNKNOWN) + curatr &= ~0x07; + if (curatr & BG_UNKNOWN) + curatr &= ~0x70; + + size_t lastret; + if (supports(ICE_COLOR)) { + switch (atr & (BG_BRIGHT | BLINK)) { + case BG_BRIGHT: + case BLINK: + atr ^= BLINK; + break; + } + switch (curatr & (BG_BRIGHT | BLINK)) { + case BG_BRIGHT: + case BLINK: + curatr ^= BLINK; + break; + } + } + + if (curatr == atr) { /* text hasn't changed. no sequence needed */ + *str = 0; + return str; + } + + lastret = strlcpy(str, "\033[", strsz); + uint32_t changed_mask = (curatr ^ atr) & (HIGH | BLINK | REVERSED | UNDERLINE | CONCEALED); + // TODO: CSI 0 m does *NOT* set + if (changed_mask) { + uint32_t set = popcnt(changed_mask & atr); + uint32_t clear = popcnt(changed_mask & ~atr); + uint32_t set_only_weight = set * 2 + 8; + uint32_t set_clear_weight = set * 2 + clear * 3; + + if (atr & 0x70) + set_only_weight += 3; + if (!(atr & FG_UNKNOWN)) + set_only_weight += 3; + if (!(atr & BG_UNKNOWN)) + set_only_weight += 3; + + if ((atr & (BG_UNKNOWN | 0x70)) != (curatr & (BG_UNKNOWN |0x70))) + set_clear_weight += 3; + if ((atr & (FG_UNKNOWN | 0x07)) != (curatr & (FG_UNKNOWN | 0x07))) + set_clear_weight += 3; + + if (set_only_weight < set_clear_weight) { + lastret = strlcat(str, "0;", strsz); + curatr &= ~0x77; + curatr |= ANSI_NORMAL; + } + } + if (atr & HIGH) { /* special attributes */ + if (!(curatr & HIGH)) + lastret = strlcat(str, "1;", strsz); + } + else { + if (curatr & HIGH) + lastret = strlcat(str, "22;", strsz); + } + if (atr & BLINK) { + if (!(curatr & BLINK)) + lastret = strlcat(str, "5;", strsz); + } + else { + if (curatr & BLINK) + lastret = strlcat(str, "25;", strsz); + } + if (atr & REVERSED) { + if (!(curatr & REVERSED)) + lastret = strlcat(str, "7;", strsz); + } + else { + if (curatr & REVERSED) + lastret = strlcat(str, "27;", strsz); + } + if (atr & UNDERLINE) { + if (!(curatr & UNDERLINE)) + lastret = strlcat(str, "4;", strsz); + } + else { + if (curatr & UNDERLINE) + lastret = strlcat(str, "24;", strsz); + } + if (atr & CONCEALED) { + if (!(curatr & CONCEALED)) + lastret = strlcat(str, "8;", strsz); + } + else { + if (curatr & CONCEALED) + lastret = strlcat(str, "28;", strsz); + } + if ((atr & FG_UNKNOWN) && !(curatr & FG_UNKNOWN)) { + lastret = strlcat(str, "39;", strsz); + curatr &= ~0x07; + curatr |= FG_UNKNOWN; + } + if ((atr & (FG_UNKNOWN | 0x07)) != (curatr & (FG_UNKNOWN | 0x07))) { + switch (atr & 0x07) { + case BLACK: + lastret = strlcat(str, "30;", strsz); + break; + case RED: + lastret = strlcat(str, "31;", strsz); + break; + case GREEN: + lastret = strlcat(str, "32;", strsz); + break; + case BROWN: + lastret = strlcat(str, "33;", strsz); + break; + case BLUE: + lastret = strlcat(str, "34;", strsz); + break; + case MAGENTA: + lastret = strlcat(str, "35;", strsz); + break; + case CYAN: + lastret = strlcat(str, "36;", strsz); + break; + case LIGHTGRAY: + lastret = strlcat(str, "37;", strsz); + break; + } + } + if ((atr & BG_UNKNOWN) && !(curatr & BG_UNKNOWN)) { + lastret = strlcat(str, "39;", strsz); + curatr &= ~0x70; + curatr |= BG_UNKNOWN; + } + if ((atr & (BG_UNKNOWN | 0x70)) != (curatr & (BG_UNKNOWN | 0x70))) { + switch (atr & 0x70) { + /* The BG_BLACK macro is 0x200, so isn't in the mask */ + case 0 /* BG_BLACK */: + lastret = strlcat(str, "40;", strsz); + break; + case BG_RED: + lastret = strlcat(str, "41;", strsz); + break; + case BG_GREEN: + lastret = strlcat(str, "42;", strsz); + break; + case BG_BROWN: + lastret = strlcat(str, "43;", strsz); + break; + case BG_BLUE: + lastret = strlcat(str, "44;", strsz); + break; + case BG_MAGENTA: + lastret = strlcat(str, "45;", strsz); + break; + case BG_CYAN: + lastret = strlcat(str, "46;", strsz); + break; + case BG_LIGHTGRAY: + lastret = strlcat(str, "47;", strsz); + break; + } + } + if (lastret == 2) { /* Convert <ESC>[ to blank */ + lastret = 0; + if (strsz > 0) { + *str = 0; + } + } + else { + // Replace ; with m + if (strsz > (lastret)) { + str[lastret - 1] = 'm'; + str[lastret] = 0; + } + lastret++; + } + + if (lastret >= strsz) { + sbbs->lprintf(LOG_ERR, "ANSI sequence attr %02X to %02X, strsz %zu too small", curatr, atr, strsz); + if (strsz) + str[0] = 0; + } + + return str; +} + +#define TIMEOUT_ANSI_GETXY 5 // Seconds +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->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; +} + +bool ANSI_Terminal::getxy(unsigned* x, unsigned* y) +{ + size_t rsp = 0; + int ch; + char str[128]; + enum { state_escape, state_open, state_y, state_x } state = state_escape; + + if (x != NULL) + *x = 0; + if (y != NULL) + *y = 0; + + sbbs->term_out("\x1b[6n"); /* Request cursor position */ + + time_t start = time(NULL); + sbbs->sys_status &= ~SS_ABORT; + while (sbbs->online && !(sbbs->sys_status & SS_ABORT) && rsp < sizeof(str) - 1) { + if ((ch = sbbs->incom(1000)) != NOINP) { + str[rsp++] = ch; + if (ch == ESC && state == state_escape) { + state = state_open; + start = time(NULL); + } + else if (ch == '[' && state == state_open) { + state = state_y; + start = time(NULL); + } + else if (IS_DIGIT(ch) && state == state_y) { + if (y != NULL) { + (*y) *= 10; + (*y) += (ch & 0xf); + } + start = time(NULL); + } + else if (ch == ';' && state == state_y) { + state = state_x; + start = time(NULL); + } + else if (IS_DIGIT(ch) && state == state_x) { + if (x != NULL) { + (*x) *= 10; + (*x) += (ch & 0xf); + } + start = time(NULL); + } + else if (ch == 'R' && state == state_x) + break; + else { + str[rsp] = '\0'; +#ifdef _DEBUG + char dbg[128]; + c_escape_str(str, dbg, sizeof(dbg), /* Ctrl-only? */ true); + sbbs->lprintf(LOG_DEBUG, "Unexpected ansi_getxy response: '%s'", dbg); +#endif + sbbs->ungetkeys(str, /* insert */ false); + rsp = 0; + state = state_escape; + } + } + if (time(NULL) - start > TIMEOUT_ANSI_GETXY) { + sbbs->lprintf(LOG_NOTICE, "!TIMEOUT in ansi_getxy"); + return false; + } + } + + return true; +} + +bool ANSI_Terminal::gotoxy(unsigned x, unsigned y) +{ + if (x == 0) + x = 1; + if (y == 0) + y = 1; + sbbs->term_printf("\x1b[%d;%dH", y, x); + return true; +} + +// Was ansi_save +bool ANSI_Terminal::save_cursor_pos() +{ + sbbs->term_out("\x1b[s"); + return true; +} + +// Was ansi_restore +bool ANSI_Terminal::restore_cursor_pos() +{ + sbbs->term_out("\x1b[u"); + return true; +} + +void ANSI_Terminal::clearscreen() +{ + clear_hotspots(); + sbbs->term_out("\x1b[2J\x1b[H"); /* clear screen, home cursor */ + lastcrcol = 0; +} + +void ANSI_Terminal::cleartoeos() +{ + sbbs->term_out("\x1b[J"); +} + +void ANSI_Terminal::cleartoeol() +{ + sbbs->term_out("\x1b[K"); +} + +void ANSI_Terminal::cursor_home() +{ + sbbs->term_out("\x1b[H"); +} + +void ANSI_Terminal::cursor_up(unsigned count = 1) +{ + if (count == 0) + return; + if (count > 1) + sbbs->term_printf("\x1b[%dA", count); + else + sbbs->term_out("\x1b[A"); +} + +void ANSI_Terminal::cursor_down(unsigned count = 1) +{ + if (count == 0) + return; + if (count > 1) + sbbs->term_printf("\x1b[%dB", count); + else + sbbs->term_out("\x1b[B"); +} + +void ANSI_Terminal::cursor_right(unsigned count = 1) +{ + if (count == 0) + return; + if (count > 1) + sbbs->term_printf("\x1b[%dC", count); + else + sbbs->term_out("\x1b[C"); +} + +void ANSI_Terminal::cursor_left(unsigned count = 1) { + if (count == 0) + return; + if (count < 4) + sbbs->term_printf("%.*s", count, "\b\b\b"); + else + sbbs->term_printf("\x1b[%dD", count); +} + +void ANSI_Terminal::set_output_rate(enum output_rate speed) { + unsigned int val = speed; + switch (val) { + case 0: val = 0; break; + case 600: val = 2; break; + case 1200: val = 3; break; + case 2400: val = 4; break; + case 4800: val = 5; break; + case 9600: val = 6; break; + case 19200: val = 7; break; + case 38400: val = 8; break; + case 57600: val = 9; break; + case 76800: val = 10; break; + default: + if (val <= 300) + val = 1; + else if (val > 76800) + val = 11; + break; + } + sbbs->term_printf("\x1b[;%u*r", val); + cur_output_rate = speed; +} + +const char* ANSI_Terminal::type() {return "ANSI";} + +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->term_out(str); +} + +void ANSI_Terminal::set_mouse(unsigned flags) { + if ((!supports(MOUSE)) && (mouse_mode != MOUSE_MODE_OFF)) + flags = MOUSE_MODE_OFF; + if (supports(MOUSE) || flags == MOUSE_MODE_OFF) { + unsigned mode = mouse_mode & ~flags; + if (mode & MOUSE_MODE_X10) + ansi_mouse(sbbs, ANSI_MOUSE_X10, false); + if (mode & MOUSE_MODE_NORM) + ansi_mouse(sbbs, ANSI_MOUSE_NORM, false); + if (mode & MOUSE_MODE_BTN) + ansi_mouse(sbbs, ANSI_MOUSE_BTN, false); + if (mode & MOUSE_MODE_ANY) + ansi_mouse(sbbs, ANSI_MOUSE_ANY, false); + if (mode & MOUSE_MODE_EXT) + ansi_mouse(sbbs, ANSI_MOUSE_EXT, false); + + mode = flags & ~mouse_mode; + if (mode & MOUSE_MODE_X10) + ansi_mouse(sbbs, ANSI_MOUSE_X10, true); + if (mode & MOUSE_MODE_NORM) + ansi_mouse(sbbs, ANSI_MOUSE_NORM, true); + if (mode & MOUSE_MODE_BTN) + ansi_mouse(sbbs, ANSI_MOUSE_BTN, true); + if (mode & MOUSE_MODE_ANY) + ansi_mouse(sbbs, ANSI_MOUSE_ANY, true); + if (mode & MOUSE_MODE_EXT) + ansi_mouse(sbbs, ANSI_MOUSE_EXT, true); + + if (mouse_mode != flags) { + mouse_mode = flags; + } + } +} + +void ANSI_Terminal::handle_control_code() { + if (ansiParser.ansi_ibs == "") { + switch (ansiParser.ansi_final_byte) { + case 'E': // NEL - Next Line + set_column(); + inc_row(); + break; + case 'M': // RI - Reverse Line Feed + dec_row(); + break; + case 'c': // RIS - Reset to Initial State.. homes + set_column(); + set_row(); + break; + } + } +} + +void ANSI_Terminal::set_color(int c, bool bg) +{ + if (curatr & REVERSED) + bg = !bg; + if (bg) { + curatr &= ~(BG_BLACK | BG_UNKNOWN | 0x70); + if (c == FG_UNKNOWN) + curatr |= BG_UNKNOWN; + else + curatr |= c << 4; + } + else { + curatr &= ~(FG_UNKNOWN | 0x07); + curatr |= c; + } +} + +void ANSI_Terminal::handle_SGR_sequence() { + unsigned cnt = ansiParser.count_params(); + unsigned pval; + + for (unsigned i = 0; i < cnt; i++) { + pval = ansiParser.get_pval(i, 0); + switch (pval) { + case 0: + curatr = ANSI_NORMAL; + break; + case 1: + curatr |= HIGH; + break; + case 2: + curatr &= ~HIGH; + break; + case 4: + curatr |= UNDERLINE; + break; + case 5: + case 6: + if (flags_ & ICE_COLOR) + curatr |= BG_BRIGHT; + else + curatr |= BLINK; + break; + case 7: + if (!(curatr & REVERSED)) + curatr = REVERSED | (curatr & ~0x77) | ((curatr & 0x70) >> 4) | ((curatr & 0x07) << 4); + break; + case 8: + curatr |= CONCEALED; + break; + case 22: + curatr &= ~HIGH; + break; + case 24: + curatr &= ~UNDERLINE; + break; + case 25: + // Turn both off... + curatr &= ~(BG_BRIGHT|BLINK); + break; + case 27: + if (curatr & REVERSED) + curatr = (curatr & ~(REVERSED | 0x77)) | ((curatr & 0x70) >> 4) | ((curatr & 0x07) << 4); + break; + case 28: + curatr &= ~CONCEALED; + break; + case 30: + set_color(BLACK, false); + break; + case 31: + set_color(RED, false); + break; + case 32: + set_color(GREEN, false); + break; + case 33: + set_color(BROWN, false); + break; + case 34: + set_color(BLUE, false); + break; + case 35: + set_color(MAGENTA, false); + break; + case 36: + set_color(CYAN, false); + break; + case 37: + set_color(LIGHTGRAY, false); + break; + case 39: + set_color(FG_UNKNOWN, false); + break; + case 40: + set_color(BLACK, true); + break; + case 41: + set_color(RED, true); + break; + case 42: + set_color(GREEN, true); + break; + case 43: + set_color(BROWN, true); + break; + case 44: + set_color(BLUE, true); + break; + case 45: + set_color(MAGENTA, true); + break; + case 46: + set_color(CYAN, true); + break; + case 47: + set_color(LIGHTGRAY, true); + break; + case 49: + set_color(FG_UNKNOWN, true); + break; + } + } +} + +void ANSI_Terminal::handle_control_sequence() { + unsigned pval; + + if (ansiParser.ansi_was_private) { + // TODO: Track things like origin mode, auto wrap, etc. + } + else { + if (ansiParser.ansi_ibs == "") { + switch (ansiParser.ansi_final_byte) { + case 'A': // Cursor up + case 'F': // Cursor Preceding Line + case 'k': // Line Position Backward + // Single parameter, default 1 + pval = ansiParser.get_pval(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 = ansiParser.get_pval(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 = ansiParser.get_pval(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 = ansiParser.get_pval(0, 1); + if (pval > column) + pval = column; + dec_column(pval); + break; + case 'G': // Cursor Character Absolute + case '`': // Character Position Absolute + pval = ansiParser.get_pval(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 = ansiParser.get_pval(0, 1); + if (pval > rows) + pval = rows; + if (pval == 0) + pval = 1; + set_row(pval - 1); + pval = ansiParser.get_pval(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 = ansiParser.get_pval(0, 1); + if (pval > rows) + pval = rows; + if (pval == 0) + pval = 1; + set_row(pval - 1); + break; + case 'm': // Set Graphic Rendidtion + handle_SGR_sequence(); + 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; + } + } + } +} + +bool ANSI_Terminal::parse_output(char ich) { + unsigned char ch = static_cast<unsigned char>(ich); + + if (utf8_increment(ch)) + return true; + + switch (ansiParser.parse(ch)) { + case ansiState_final: + if (ansiParser.ansi_was_cc) + handle_control_code(); + else if (!ansiParser.ansi_was_string) + handle_control_sequence(); + ansiParser.reset(); + return true; + case ansiState_broken: + sbbs->lprintf(LOG_WARNING, "Sent broken ANSI sequence '%s'", ansiParser.ansi_sequence.c_str()); + ansiParser.reset(); + return true; + case ansiState_none: + break; + default: + return true; + } + + /* Track cursor position locally */ + switch (ch) { + case '\a': // 7 + /* Non-printing */ + break; + case 8: // BS + dec_column(); + break; + case 9: // TAB + if (column < (cols - 1)) { + inc_column(); + while ((column < (cols - 1)) && (column % 8)) + inc_column(); + } + break; + case 10: // LF + inc_row(); + break; + case 12: // FF + set_row(); + set_column(); + break; + case 13: // CR + lastcrcol = column; + if (sbbs->console & CON_CR_CLREOL) + cleartoeol(); + set_column(); + break; + default: + if ((charset() == CHARSET_UTF8) && (ch < 32)) { + // Assume all other UTF-8 control characters do nothing. + // TODO: Some might (ie: VT) but we can't really know + // what they'll do. + break; + } + // Assume other CP427 control characters print a glyph. + inc_column(); + break; + } + + return true; +} + +bool ANSI_Terminal::stuff_unhandled(char &ch, ANSI_Parser& ansi) +{ + if (ansi.ansi_sequence == "") + return false; + ch = ESC; + std::string remain = ansi.ansi_sequence.substr(1); + for (auto uch = remain.crbegin(); uch != remain.crend(); uch++) { + sbbs->ungetkey(*uch, true); + } + return true; +} + +bool ANSI_Terminal::stuff_str(char& ch, const char *str, bool skipctlcheck) +{ + const char *end = str; + bool ret = false; + if (!skipctlcheck) { + if (str[0] < 32) { + ret = true; + end = &str[1]; + } + } + for (const char *p = strchr(str, 0) - 1; p >= end; p--) { + sbbs->ungetkey(*p, true); + } + return ret; +} + +bool ANSI_Terminal::handle_left_press(unsigned x, unsigned y, char& ch, bool& retval) +{ + list_node_t* node = find_hotspot(x, y); + if (node != NULL) { + struct mouse_hotspot* spot = (struct mouse_hotspot*)node->data; +#ifdef _DEBUG + { + char dbg[128]; + c_escape_str(spot->cmd, dbg, sizeof(dbg), /* Ctrl-only? */ true); + sbbs->lprintf(LOG_DEBUG, "Stuffing hot spot command into keybuf: '%s'", dbg); + } +#endif + if (sbbs->pause_inside && !pause_hotspot) { + // Abort the pause then send command + ch = TERM_KEY_ABORT; + stuff_str(ch, spot->cmd); + retval = true; + return true; + } + else { + retval = stuff_str(ch, spot->cmd); + return retval; + } + } + if (sbbs->pause_inside && y == rows - 1) { + ch = '\r'; + retval = true; + return true; + } + return false; +} + +bool ANSI_Terminal::handle_non_SGR_mouse_sequence(char& ch, ANSI_Parser& ansi) +{ + int button = sbbs->kbincom(100); + if (button == NOINP) { + sbbs->lprintf(LOG_DEBUG, "Timeout waiting for mouse button value"); + return false; + } + if (button < ' ') { + sbbs->lprintf(LOG_DEBUG, "Unexpected mouse-button (0x%02X) tracking char: 0x%02X < ' '" + , button, ch); + return false; + } + button -= ' '; + bool move{false}; + bool release{false}; + if (button == 3) { + release = true; + button &= ~0x20; + } + if (button & 0x20) { + move = true; + button &= ~0x20; + } + if (button >= 128) + button -= 121; + else if (button >= 64) + button -= 61; + if (button >= 11) { + sbbs->lprintf(LOG_DEBUG, "Unexpected mouse-button (0x%02X) decoded", button); + return false; + } + int inch = sbbs->kbincom(100); + if (inch == NOINP) { + sbbs->lprintf(LOG_DEBUG, "Timeout waiting for mouse X value"); + return false; + } + if (inch < '!') { + sbbs->lprintf(LOG_DEBUG, "Unexpected mouse-button (0x%02X) tracking char: 0x%02X < '!'" + , button, ch); + return false; + } + int x = ch - '!'; + inch = sbbs->kbincom(100); + if (inch == NOINP) { + sbbs->lprintf(LOG_DEBUG, "Timeout waiting for mouse Y value"); + return false; + } + if (ch < '!') { + sbbs->lprintf(LOG_DEBUG, "Unexpected mouse-button (0x%02X) tracking char: 0x%02X < '!'" + , button, ch); + return false; + } + int y = ch - '!'; + sbbs->lprintf(LOG_DEBUG, "X10 Mouse button-%s (0x%02X) reported at: %u x %u", release ? "release" : (move ? "move" : "press"), button, x, y); + if (button == 0 && release == false && move == false) { + bool retval{false}; + if (handle_left_press(x, y, ch, retval)) + return retval; + } + else if (button == 3 && release == false && move == false) { + ch = TERM_KEY_UP; + return true; + } + else if (button == 4 && release == false && move == false) { + ch = TERM_KEY_DOWN; + return true; + } + if ((sbbs->console & CON_MOUSE_CLK_PASSTHRU) && (!release)) + return stuff_unhandled(ch, ansi); + if ((sbbs->console & CON_MOUSE_REL_PASSTHRU) && release) + return stuff_unhandled(ch, ansi); + if (button == 2) { + ch = TERM_KEY_ABORT; + return true; + } + return false; +} + +bool ANSI_Terminal::handle_SGR_mouse_sequence(char& ch, ANSI_Parser& ansi, bool release) +{ + if (ansi.count_params() != 3) { + sbbs->lprintf(LOG_DEBUG, "Invalid SGR mouse report sequence: '%s'", ansi.ansi_params.c_str()); + return false; + } + unsigned button = ansi.get_pval(0, 256); + bool move{false}; + if (button & 0x20) { + move = true; + button &= ~0x20; + } + if (button >= 128) + button -= 121; + else if (button >= 64) + button -= 61; + if (button >= 11) { + sbbs->lprintf(LOG_DEBUG, "Unexpected SGR mouse-button (0x%02X) decoded", button); + return false; + } + unsigned x = ansi.get_pval(1, 0); + unsigned y = ansi.get_pval(2, 0); + if (x < 1 || y < 1) { + sbbs->lprintf(LOG_DEBUG, "Invalid SGR mouse report position: '%s'", ansi.ansi_params.c_str()); + return false; + } + x--; + y--; + if (button == 0 && release == true && move == false) { + bool retval{false}; + if (handle_left_press(x, y, ch, retval)) + return retval; + } + else if (button == 3 && release == false && move == false) { + ch = TERM_KEY_UP; + return true; + } + else if (button == 4 && release == false && move == false) { + ch = TERM_KEY_DOWN; + return true; + } + if ((sbbs->console & CON_MOUSE_CLK_PASSTHRU) && (!release)) + return stuff_unhandled(ch, ansi); + if ((sbbs->console & CON_MOUSE_REL_PASSTHRU) && release) + return stuff_unhandled(ch, ansi); + if (button == 2) { + ch = TERM_KEY_ABORT; + return true; + } + return false; +} + +bool ANSI_Terminal::parse_input_sequence(char& ch, int mode) { + if (ch == ESC) { + ANSI_Parser ansi{}; + ansi.parse(ESC); + bool done = false; + unsigned timeouts = 0; + while (!done) { + int rc = sbbs->kbincom(100); + if (rc == NOINP) { // Timed out + if (++timeouts >= 30) + break; + } + switch(ansi.parse(rc)) { + case ansiState_final: + done = 1; + break; + case ansiState_broken: + sbbs->lprintf(LOG_DEBUG, "Invalid ANSI sequence: '\\e%s'", &(ansi.ansi_sequence.c_str()[1])); + done = 1; + break; + default: + break; + } + } + switch (ansi.current_state()) { + case ansiState_final: + if ((!ansi.ansi_was_private) + && ansi.ansi_ibs == "" + && (!ansi.ansi_was_cc)) { + if (ansi.ansi_params == "") { + switch (ansi.ansi_final_byte) { + case 'A': + ch = TERM_KEY_UP; + return true; + case 'B': + ch = TERM_KEY_DOWN; + return true; + case 'C': + ch = TERM_KEY_RIGHT; + return true; + case 'D': + ch = TERM_KEY_LEFT; + return true; + case 'H': + ch = TERM_KEY_HOME; + return true; + case 'V': + ch = TERM_KEY_PAGEUP; + return true; + case 'U': + ch = TERM_KEY_PAGEDN; + return true; + case 'F': + ch = TERM_KEY_END; + return true; + case 'K': + ch = TERM_KEY_END; + return true; + case 'M': + return handle_non_SGR_mouse_sequence(ch, ansi); + case '@': + ch = TERM_KEY_INSERT; + return true; + } + } + else if (ansi.ansi_final_byte == '~' && ansi.count_params() == 1) { + switch (ansi.get_pval(0, 0)) { + case 1: + ch = TERM_KEY_HOME; + return true; + case 2: + ch = TERM_KEY_INSERT; + return true; + case 3: + ch = TERM_KEY_DELETE; + return true; + case 4: + ch = TERM_KEY_END; + return true; + case 5: + ch = TERM_KEY_PAGEUP; + return true; + case 6: + ch = TERM_KEY_PAGEDN; + return true; + } + } + else if (ansi.ansi_final_byte == 'R') { + if (mode & K_ANSI_CPR) { /* auto-detect rows */ + unsigned x = ansi.get_pval(1, 1); + unsigned y = ansi.get_pval(0, 1); + sbbs->lprintf(LOG_DEBUG, "received ANSI cursor position report: %ux%u" + , x, y); + /* Sanity check the coordinates in the response: */ + bool update{false}; + if (sbbs->useron.cols == TERM_COLS_AUTO && x >= TERM_COLS_MIN && x <= TERM_COLS_MAX) { + cols = x; + update = true; + } + if (sbbs->useron.rows == TERM_ROWS_AUTO && y >= TERM_ROWS_MIN && y <= TERM_ROWS_MAX) { + rows = y; + update = true; + } + if (update) + sbbs->update_nodeterm(); + } + // TODO: This was suppressed before the terminal-abstration + // branch. I can't think of a good reason to keep doing + // that though, and feel it should be more like the rest. + ch = 0; + return true; + } + } + else if (ansi.ansi_was_private + && ansi.ansi_ibs == "" + && (!ansi.ansi_was_cc)) { + if (ansi.ansi_sequence[2] == '<') { + switch (ansi.ansi_final_byte) { + case 'm': + return handle_SGR_mouse_sequence(ch, ansi, true); + case 'M': + return handle_SGR_mouse_sequence(ch, ansi, false); + } + } + } + // Fall through + default: + return stuff_unhandled(ch, ansi); + } + } + return false; +} + +struct mouse_hotspot* ANSI_Terminal::add_hotspot(struct mouse_hotspot* spot) {return nullptr;} + +bool ANSI_Terminal::can_highlight() { return true; } +bool ANSI_Terminal::can_move() { return true; } +bool ANSI_Terminal::can_mouse() { return flags_ & MOUSE; } +bool ANSI_Terminal::is_monochrome() { return !(flags_ & COLOR); } diff --git a/src/sbbs3/ansi_terminal.h b/src/sbbs3/ansi_terminal.h new file mode 100644 index 0000000000000000000000000000000000000000..fb215b37a590385dea61c2ad54a90b3267da395d --- /dev/null +++ b/src/sbbs3/ansi_terminal.h @@ -0,0 +1,60 @@ +#ifndef ANSI_TERMINAL_H +#define ANSI_TERMINAL_H + +#include <string> +#include "ansi_parser.h" +#include "terminal.h" + +class ANSI_Terminal : public Terminal { +public: + ANSI_Terminal() = delete; + using Terminal::Terminal; + + // Was ansi() + virtual const char *attrstr(unsigned atr); + // Was ansi() and ansi_attr() + virtual char* attrstr(unsigned atr, unsigned curatr, char* str, size_t strsz); + virtual bool getdims(); + virtual bool getxy(unsigned* x, unsigned* y); + virtual bool gotoxy(unsigned x, unsigned y); + // Was ansi_save + virtual bool save_cursor_pos(); + // Was ansi_restore + virtual bool restore_cursor_pos(); + virtual void clearscreen(); + virtual void cleartoeos(); + virtual void cleartoeol(); + virtual void cursor_home(); + virtual void cursor_up(unsigned count); + virtual void cursor_down(unsigned count); + virtual void cursor_right(unsigned count); + virtual void cursor_left(unsigned count); + virtual void set_output_rate(enum output_rate speed); + virtual const char* type(); + virtual void set_mouse(unsigned mode); + virtual bool parse_output(char ch); + // Needs to handle C0 and C1 + virtual bool parse_input_sequence(char& ch, int mode); + virtual struct mouse_hotspot* add_hotspot(struct mouse_hotspot* spot); + virtual bool can_highlight(); + virtual bool can_move(); + virtual bool can_mouse(); + virtual bool is_monochrome(); + +private: + void handle_control_code(); + void handle_control_sequence(); + void handle_SGR_sequence(); + bool stuff_unhandled(char &ch, ANSI_Parser& ansi); + bool stuff_str(char& ch, const char *str, bool skipctlcheck = false); + bool handle_non_SGR_mouse_sequence(char& ch, ANSI_Parser& ansi); + bool handle_SGR_mouse_sequence(char& ch, ANSI_Parser& ansi, bool release); + bool handle_left_press(unsigned x, unsigned y, char& ch, bool& retval); + void set_color(int c, bool bg); + + ANSI_Parser ansiParser{}; + unsigned saved_row{0}; + unsigned saved_column{0}; +}; + +#endif diff --git a/src/sbbs3/ansiterm.cpp b/src/sbbs3/ansiterm.cpp deleted file mode 100644 index a61d71e9163e88266361409adf5f4206a18ca706..0000000000000000000000000000000000000000 --- a/src/sbbs3/ansiterm.cpp +++ /dev/null @@ -1,311 +0,0 @@ -/* Synchronet ANSI terminal functions */ - -/**************************************************************************** - * @format.tab-size 4 (Plain Text/Source Code File Header) * - * @format.use-tabs true (see http://www.synchro.net/ptsc_hdr.html) * - * * - * Copyright Rob Swindell - http://www.synchro.net/copyright.html * - * * - * This program is free software; you can redistribute it and/or * - * modify it under the terms of the GNU General Public License * - * as published by the Free Software Foundation; either version 2 * - * of the License, or (at your option) any later version. * - * See the GNU General Public License for more details: gpl.txt or * - * http://www.fsf.org/copyleft/gpl.html * - * * - * For Synchronet coding style and modification guidelines, see * - * http://www.synchro.net/source.html * - * * - * Note: If this box doesn't appear square, then you need to fix your tabs. * - ****************************************************************************/ - -#include "sbbs.h" - -#define TIMEOUT_ANSI_GETXY 5 // Seconds - -/****************************************************************************/ -/* Returns the ANSI code to obtain the value of atr. Mixed attributes */ -/* high intensity colors, or background/foreground combinations don't work. */ -/* A call to attr() is more appropriate, being it is intelligent */ -/****************************************************************************/ -const char *sbbs_t::ansi(int atr) -{ - - switch (atr) { - - /* Special case */ - case ANSI_NORMAL: - return "\x1b[0m"; - case BLINK: - case BG_BRIGHT: - return "\x1b[5m"; - - /* Foreground */ - case HIGH: - return "\x1b[1m"; - case BLACK: - return "\x1b[30m"; - case RED: - return "\x1b[31m"; - case GREEN: - return "\x1b[32m"; - case BROWN: - return "\x1b[33m"; - case BLUE: - return "\x1b[34m"; - case MAGENTA: - return "\x1b[35m"; - case CYAN: - return "\x1b[36m"; - case LIGHTGRAY: - return "\x1b[37m"; - - /* Background */ - case BG_BLACK: - return "\x1b[40m"; - case BG_RED: - return "\x1b[41m"; - case BG_GREEN: - return "\x1b[42m"; - case BG_BROWN: - return "\x1b[43m"; - case BG_BLUE: - return "\x1b[44m"; - case BG_MAGENTA: - return "\x1b[45m"; - case BG_CYAN: - return "\x1b[46m"; - case BG_LIGHTGRAY: - return "\x1b[47m"; - } - - return "-Invalid use of ansi()-"; -} - -/* insure str is at least 14 bytes in size! */ -extern "C" char* ansi_attr(int atr, int curatr, char* str, bool color) -{ - if (!color) { /* eliminate colors if terminal doesn't support them */ - if (atr & LIGHTGRAY) /* if any foreground bits set, set all */ - atr |= LIGHTGRAY; - if (atr & BG_LIGHTGRAY) /* if any background bits set, set all */ - atr |= BG_LIGHTGRAY; - if ((atr & LIGHTGRAY) && (atr & BG_LIGHTGRAY)) - atr &= ~LIGHTGRAY; /* if background is solid, foreground is black */ - if (!atr) - atr |= LIGHTGRAY; /* don't allow black on black */ - } - if (curatr == atr) { /* text hasn't changed. no sequence needed */ - *str = 0; - return str; - } - - strcpy(str, "\033["); - if ((!(atr & HIGH) && curatr & HIGH) || (!(atr & BLINK) && curatr & BLINK) - || atr == LIGHTGRAY) { - strcat(str, "0;"); - curatr = LIGHTGRAY; - } - if (atr & BLINK) { /* special attributes */ - if (!(curatr & BLINK)) - strcat(str, "5;"); - } - if (atr & HIGH) { - if (!(curatr & HIGH)) - strcat(str, "1;"); - } - if ((atr & 0x07) != (curatr & 0x07)) { - switch (atr & 0x07) { - case BLACK: - strcat(str, "30;"); - break; - case RED: - strcat(str, "31;"); - break; - case GREEN: - strcat(str, "32;"); - break; - case BROWN: - strcat(str, "33;"); - break; - case BLUE: - strcat(str, "34;"); - break; - case MAGENTA: - strcat(str, "35;"); - break; - case CYAN: - strcat(str, "36;"); - break; - case LIGHTGRAY: - strcat(str, "37;"); - break; - } - } - if ((atr & 0x70) != (curatr & 0x70)) { - switch (atr & 0x70) { - /* The BG_BLACK macro is 0x200, so isn't in the mask */ - case 0 /* BG_BLACK */: - strcat(str, "40;"); - break; - case BG_RED: - strcat(str, "41;"); - break; - case BG_GREEN: - strcat(str, "42;"); - break; - case BG_BROWN: - strcat(str, "43;"); - break; - case BG_BLUE: - strcat(str, "44;"); - break; - case BG_MAGENTA: - strcat(str, "45;"); - break; - case BG_CYAN: - strcat(str, "46;"); - break; - case BG_LIGHTGRAY: - strcat(str, "47;"); - break; - } - } - if (strlen(str) == 2) /* Convert <ESC>[ to blank */ - *str = 0; - else - str[strlen(str) - 1] = 'm'; - return str; -} - -char* sbbs_t::ansi(int atr, int curatr, char* str) -{ - long term = term_supports(); - if (term & ICE_COLOR) { - switch (atr & (BG_BRIGHT | BLINK)) { - case BG_BRIGHT: - case BLINK: - atr ^= BLINK; - break; - } - } - return ::ansi_attr(atr, curatr, str, (term & COLOR) ? TRUE:FALSE); -} - -bool sbbs_t::ansi_getdims() -{ - if (sys_status & SS_USERON && useron.misc & ANSI - && (useron.rows == TERM_ROWS_AUTO || useron.cols == TERM_COLS_AUTO) - && online == ON_REMOTE) { /* Remote */ - putcom("\x1b[s\x1b[255B\x1b[255C\x1b[6n\x1b[u"); - return inkey(K_ANSI_CPR, TIMEOUT_ANSI_GETXY * 1000) == 0; - } - return false; -} - -bool sbbs_t::ansi_getxy(int* x, int* y) -{ - size_t rsp = 0; - int ch; - char str[128]; - enum { state_escape, state_open, state_y, state_x } state = state_escape; - - if (x != NULL) - *x = 0; - if (y != NULL) - *y = 0; - - putcom("\x1b[6n"); /* Request cursor position */ - - time_t start = time(NULL); - sys_status &= ~SS_ABORT; - while (online && !(sys_status & SS_ABORT) && rsp < sizeof(str) - 1) { - if ((ch = incom(1000)) != NOINP) { - str[rsp++] = ch; - if (ch == ESC && state == state_escape) { - state = state_open; - start = time(NULL); - } - else if (ch == '[' && state == state_open) { - state = state_y; - start = time(NULL); - } - else if (IS_DIGIT(ch) && state == state_y) { - if (y != NULL) { - (*y) *= 10; - (*y) += (ch & 0xf); - } - start = time(NULL); - } - else if (ch == ';' && state == state_y) { - state = state_x; - start = time(NULL); - } - else if (IS_DIGIT(ch) && state == state_x) { - if (x != NULL) { - (*x) *= 10; - (*x) += (ch & 0xf); - } - start = time(NULL); - } - else if (ch == 'R' && state == state_x) - break; - else { - str[rsp] = '\0'; -#ifdef _DEBUG - char dbg[128]; - c_escape_str(str, dbg, sizeof(dbg), /* Ctrl-only? */ true); - lprintf(LOG_DEBUG, "Unexpected ansi_getxy response: '%s'", dbg); -#endif - ungetkeys(str, /* insert */ false); - rsp = 0; - state = state_escape; - } - } - if (time(NULL) - start > TIMEOUT_ANSI_GETXY) { - lprintf(LOG_NOTICE, "!TIMEOUT in ansi_getxy"); - return false; - } - } - - return true; -} - -bool sbbs_t::ansi_gotoxy(int x, int y) -{ - if (term_supports(ANSI)) { - comprintf("\x1b[%d;%dH", y, x); - if (x > 0) - column = x - 1; - if (y > 0) - row = y - 1; - lncntr = 0; - return true; - } - return false; -} - -bool sbbs_t::ansi_save(void) -{ - if (term_supports(ANSI)) { - putcom("\x1b[s"); - return true; - } - return false; -} - -bool sbbs_t::ansi_restore(void) -{ - if (term_supports(ANSI)) { - putcom("\x1b[u"); - return true; - } - return false; -} - -int sbbs_t::ansi_mouse(enum ansi_mouse_mode mode, bool enable) -{ - char str[32] = ""; - SAFEPRINTF2(str, "\x1b[?%u%c", mode, enable ? 'h' : 'l'); - return putcom(str); -} diff --git a/src/sbbs3/answer.cpp b/src/sbbs3/answer.cpp index 2c0e2921b3ffc0d37c23498349bd229259306fea..939e529c8ab8a10f16fda22fb989a1eb9db60832 100644 --- a/src/sbbs3/answer.cpp +++ b/src/sbbs3/answer.cpp @@ -429,8 +429,8 @@ bool sbbs_t::answer() if (((startup->options & (BBS_OPT_ALLOW_SFTP | BBS_OPT_SSH_ANYAUTH)) == BBS_OPT_ALLOW_SFTP) && tnamelen == 4 && strncmp(tname, "sftp", 4) == 0) { if (useron.number) { activate_ssh = init_sftp(cid); - cols = 0; - rows = 0; + term->cols = 0; + term->rows = 0; SAFECOPY(terminal, "sftp"); mouse_mode = MOUSE_MODE_OFF; autoterm = 0; @@ -509,12 +509,12 @@ bool sbbs_t::answer() } if (cryptStatusOK(cryptGetAttribute(ssh_session, CRYPT_SESSINFO_SSH_CHANNEL_WIDTH, &l)) && l > 0) { - cols = l; - lprintf(LOG_DEBUG, "%04d SSH [%s] height %d", client_socket, client.addr, cols); + term->cols = l; + lprintf(LOG_DEBUG, "%04d SSH [%s] height %d", client_socket, client.addr, term->cols); } if (cryptStatusOK(cryptGetAttribute(ssh_session, CRYPT_SESSINFO_SSH_CHANNEL_HEIGHT, &l)) && l > 0) { - rows = l; - lprintf(LOG_DEBUG, "%04d SSH [%s] height %d", client_socket, client.addr, rows); + term->rows = l; + lprintf(LOG_DEBUG, "%04d SSH [%s] height %d", client_socket, client.addr, term->rows); } l = 0; if (cryptStatusOK(cryptGetAttributeString(ssh_session, CRYPT_SESSINFO_SSH_CHANNEL_TERMINAL, terminal, &l)) && l > 0) { @@ -546,15 +546,57 @@ bool sbbs_t::answer() /* Detect terminal type */ if (!term_output_disabled) { - mswait(200); // Allow some time for Telnet negotiation + // Grab telnet terminal if negotiated already + if (!(telnet_mode & TELNET_MODE_OFF)) { + if (autoterm == 0) { + unsigned loops = 0; + // Wait up to 2s more for telnet term type + // TODO: Any way to detect if the remote send a zero-length type? + lprintf(LOG_DEBUG, "Waiting for telnet terminal negotiation"); + while (telnet_remote_option[TELNET_TERM_TYPE] == TELNET_WILL + || telnet_remote_option[TELNET_TERM_TYPE] == 0) { + /* Stop the input thread from writing to the telnet_* vars */ + pthread_mutex_lock(&input_thread_mutex); + if (telnet_remote_option[TELNET_NEGOTIATE_WINDOW_SIZE] == TELNET_WILL) { + if (telnet_cols >= TERM_COLS_MIN && telnet_cols <= TERM_COLS_MAX) + term->cols = telnet_cols; + if (telnet_rows >= TERM_ROWS_MIN && telnet_rows <= TERM_ROWS_MAX) + term->rows = telnet_rows; + } + if (telnet_terminal[0]) { + pthread_mutex_unlock(&input_thread_mutex); + SAFECOPY(terminal, telnet_terminal); + break; + } + pthread_mutex_unlock(&input_thread_mutex); + if (++loops >= 30) + break; + if (telnet_remote_option[TELNET_TERM_TYPE] == 0 && loops >= 10) + break; + mswait(100); // Allow some time for Telnet negotiation + } + lprintf(LOG_DEBUG, "Telnet terminal negotiation wait complete (%u loops)", loops); + } + } rioctl(IOFI); /* flush input buffer */ safe_snprintf(str, sizeof(str), "%s %s", VERSION_NOTICE, COPYRIGHT_NOTICE); + if (strcmp(terminal, "PETSCII") == 0) { + autoterm |= PETSCII; + update_terminal(this); + } if (autoterm & PETSCII) { SAFECOPY(terminal, "PETSCII"); outchar(FF); - center(str); + term->center(str); + term_out("\r\n"); } else { /* ANSI+ terminal detection */ - putcom( "\r\n" /* locate cursor at column 1 */ + /* + * TODO: Once this merges, it would be good to split the "ANSI detection" + * out from feature detection (rows/cols/UTF8/CTerm/RIP/etc) so the + * ANSI_Terminal class can be responsible for collecting the + * details of the ANSI implementation. + */ + 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 */ @@ -576,9 +618,9 @@ bool sbbs_t::answer() "\r" /* Move cursor left (in case previous char printed) */ ); i = l = 0; - row = 0; - lncntr = 0; - center(str); + term->row = 0; + term->lncntr = 0; + term->center(str); while (i++ < 50 && l < (int)sizeof(str) - 1) { /* wait up to 5 seconds for response */ c = incom(100) & 0x7f; @@ -634,9 +676,9 @@ bool sbbs_t::answer() if (cursor_pos_report == 1) { /* Sanity check the coordinates in the response: */ if (x >= TERM_COLS_MIN && x <= TERM_COLS_MAX) - cols = x; + term->cols = x; if (y >= TERM_ROWS_MIN && y <= TERM_ROWS_MAX) - rows = y; + term->rows = y; } else { // second report if (x < 3) { // ZWNBSP didn't move cursor (more than one column) autoterm |= UTF8; @@ -645,7 +687,7 @@ bool sbbs_t::answer() } } else if (sscanf(p, "[=67;84;101;114;109;%u;%u", &x, &y) == 2 && *lastchar(p) == 'c') { lprintf(LOG_INFO, "received CTerm version report: %u.%u", x, y); - cterm_version = (x * 1000) + y; + term->cterm_version = (x * 1000) + y; } p = strtok_r(NULL, "\x1b", &tokenizer); } @@ -661,7 +703,7 @@ bool sbbs_t::answer() } } if (terminal[0]) - lprintf(LOG_DEBUG, "auto-detected terminal type: %ux%u %s", cols, rows, terminal); + lprintf(LOG_DEBUG, "auto-detected terminal type: %ux%u %s", term->cols, term->rows, terminal); else SAFECOPY(terminal, "DUMB"); } @@ -733,9 +775,9 @@ bool sbbs_t::answer() if (telnet_terminal[0]) SAFECOPY(terminal, telnet_terminal); if (telnet_cols >= TERM_COLS_MIN && telnet_cols <= TERM_COLS_MAX) - cols = telnet_cols; + term->cols = telnet_cols; if (telnet_rows >= TERM_ROWS_MIN && telnet_rows <= TERM_ROWS_MAX) - rows = telnet_rows; + term->rows = telnet_rows; } else { lprintf(LOG_NOTICE, "no Telnet commands received, reverting to Raw TCP mode"); telnet_mode |= TELNET_MODE_OFF; @@ -746,7 +788,7 @@ bool sbbs_t::answer() } pthread_mutex_unlock(&input_thread_mutex); } - lprintf(LOG_INFO, "terminal type: %ux%u %s %s", cols, rows, term_charset(autoterm), terminal); + lprintf(LOG_INFO, "terminal type: %ux%u %s %s", term->cols, term->rows, term_charset(autoterm), terminal); SAFECOPY(client_ipaddr, cid); /* Over-ride IP address with Caller-ID info */ SAFECOPY(useron.comp, client_name); } diff --git a/src/sbbs3/atcodes.cpp b/src/sbbs3/atcodes.cpp index 3d2f3e08db6da1e3f701bacaf966a19436417eb8..b155a5faef20b2aa0e0a153ca9329d2c74ffcdc8 100644 --- a/src/sbbs3/atcodes.cpp +++ b/src/sbbs3/atcodes.cpp @@ -143,7 +143,7 @@ int sbbs_t::show_atcode(const char *instr, JSObject* obj) tp++; } c_unescape_str(tp); - add_hotspot(tp, /* hungry: */ true, column, column + strlen(sp) - 1, row); + term->add_hotspot(tp, /* hungry: */ true, term->column, term->column + strlen(sp) - 1, term->row); bputs(sp); return len; } @@ -158,7 +158,7 @@ int sbbs_t::show_atcode(const char *instr, JSObject* obj) tp++; } c_unescape_str(tp); - add_hotspot(tp, /* hungry: */ false, column, column + strlen(sp) - 1, row); + term->add_hotspot(tp, /* hungry: */ false, term->column, term->column + strlen(sp) - 1, term->row); bputs(sp); return len; } @@ -194,15 +194,15 @@ int sbbs_t::show_atcode(const char *instr, JSObject* obj) fmt.align = fmt.left; if (fmt.truncated && strchr(cp, '\n') == NULL) { - if (column + fmt.disp_len > cols - 1) { - if (column >= cols - 1) + if (term->column + fmt.disp_len > term->cols - 1) { + if (term->column >= term->cols - 1) fmt.disp_len = 0; else - fmt.disp_len = (cols - 1) - column; + fmt.disp_len = (term->cols - 1) - term->column; } } if (pmode & P_UTF8) { - if (term_supports(UTF8)) + if (term->charset() == CHARSET_UTF8) fmt.disp_len += strlen(cp) - utf8_str_total_width(cp, unicode_zerowidth); else fmt.disp_len += strlen(cp) - utf8_str_count_width(cp, /* min: */ 1, /* max: */ 2, unicode_zerowidth); @@ -344,7 +344,7 @@ const char* sbbs_t::atcode(const char* sp, char* str, size_t maxlen, int* pmode, return nulstr; } if (strcmp(sp, "CLEAR_HOT") == 0) { - clear_hotspots(); + term->clear_hotspots(); return nulstr; } @@ -371,18 +371,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 @@ -391,36 +391,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; } @@ -525,7 +525,7 @@ const char* sbbs_t::atcode(const char* sp, char* str, size_t maxlen, int* pmode, return cfg.sys_name; if (!strcmp(sp, "BAUD") || !strcmp(sp, "BPS")) { - safe_snprintf(str, maxlen, "%u", cur_output_rate ? cur_output_rate : cur_rate); + safe_snprintf(str, maxlen, "%u", term->cur_output_rate ? term->cur_output_rate : cur_rate); return str; } @@ -535,18 +535,18 @@ const char* sbbs_t::atcode(const char* sp, char* str, size_t maxlen, int* pmode, } if (!strcmp(sp, "COLS")) { - safe_snprintf(str, maxlen, "%u", cols); + safe_snprintf(str, maxlen, "%u", term->cols); return str; } if (!strcmp(sp, "ROWS")) { - safe_snprintf(str, maxlen, "%u", rows); + safe_snprintf(str, maxlen, "%u", term->rows); return str; } if (strcmp(sp, "TERM") == 0) return term_type(); if (strcmp(sp, "CHARSET") == 0) - return term_charset(); + return term->charset_str(); if (!strcmp(sp, "CONN")) return connection; @@ -625,7 +625,7 @@ const char* sbbs_t::atcode(const char* sp, char* str, size_t maxlen, int* pmode, return useron.netmail; if (strcmp(sp, "TERMTYPE") == 0) - return term_type(&useron, term_supports(), str, maxlen); + return term_type(&useron, term->flags(), str, maxlen); if (strcmp(sp, "TERMROWS") == 0) return term_rows(&useron, str, maxlen); @@ -655,8 +655,8 @@ const char* sbbs_t::atcode(const char* sp, char* str, size_t maxlen, int* pmode, return (useron.misc & PETSCII) ? text[On] : text[Off]; if (strcmp(sp, "PETGRFX") == 0) { - if (term_supports(PETSCII)) - outcom(PETSCII_UPPERGRFX); + if (term->charset() == CHARSET_PETSCII) + term_out(PETSCII_UPPERGRFX); return nulstr; } @@ -956,7 +956,7 @@ const char* sbbs_t::atcode(const char* sp, char* str, size_t maxlen, int* pmode, } if (!strcmp(sp, "RESETPAUSE")) { - lncntr = 0; + term->lncntr = 0; return nulstr; } @@ -972,11 +972,11 @@ const char* sbbs_t::atcode(const char* sp, char* str, size_t maxlen, int* pmode, if (strncmp(sp, "FILL:", 5) == 0) { SAFECOPY(tmp, sp + 5); - int margin = centered ? column : 1; + int margin = centered ? term->column : 1; if (margin < 1) margin = 1; c_unescape_str(tmp); - while (*tmp && online && column < cols - margin) + while (*tmp && online && term->column < term->cols - margin) bputs(tmp, P_TRUNCATE); return nulstr; } @@ -985,7 +985,7 @@ const char* sbbs_t::atcode(const char* sp, char* str, size_t maxlen, int* pmode, i = atoi(sp + 4); if (i >= 1) // Convert to 0-based i--; - for (l = i - column; l > 0; l--) + for (l = i - term->column; l > 0; l--) outchar(' '); return nulstr; } @@ -1011,7 +1011,7 @@ const char* sbbs_t::atcode(const char* sp, char* str, size_t maxlen, int* pmode, } if (strncmp(sp, "BPS:", 4) == 0) { - set_output_rate((enum output_rate)atoi(sp + 4)); + term->set_output_rate((enum output_rate)atoi(sp + 4)); return nulstr; } @@ -1638,52 +1638,52 @@ const char* sbbs_t::atcode(const char* sp, char* str, size_t maxlen, int* pmode, return "\r\n"; if (!strcmp(sp, "PUSHXY")) { - ansi_save(); + term->save_cursor_pos(); return nulstr; } if (!strcmp(sp, "POPXY")) { - ansi_restore(); + term->restore_cursor_pos(); return nulstr; } if (!strcmp(sp, "HOME")) { - cursor_home(); + term->cursor_home(); return nulstr; } if (!strcmp(sp, "CLRLINE")) { - clearline(); + term->clearline(); return nulstr; } if (!strcmp(sp, "CLR2EOL") || !strcmp(sp, "CLREOL")) { - cleartoeol(); + term->cleartoeol(); return nulstr; } if (!strcmp(sp, "CLR2EOS")) { - cleartoeos(); + term->cleartoeos(); return nulstr; } if (!strncmp(sp, "UP:", 3)) { - cursor_up(atoi(sp + 3)); + term->cursor_up(atoi(sp + 3)); return str; } if (!strncmp(sp, "DOWN:", 5)) { - cursor_down(atoi(sp + 5)); + term->cursor_down(atoi(sp + 5)); return str; } if (!strncmp(sp, "LEFT:", 5)) { - cursor_left(atoi(sp + 5)); + term->cursor_left(atoi(sp + 5)); return str; } if (!strncmp(sp, "RIGHT:", 6)) { - cursor_right(atoi(sp + 6)); + term->cursor_right(atoi(sp + 6)); return str; } @@ -1691,7 +1691,7 @@ const char* sbbs_t::atcode(const char* sp, char* str, size_t maxlen, int* pmode, const char* cp = strchr(sp, ','); if (cp != NULL) { cp++; - cursor_xy(atoi(sp + 7), atoi(cp)); + term->gotoxy(atoi(sp + 7), atoi(cp)); } return nulstr; } diff --git a/src/sbbs3/bat_xfer.cpp b/src/sbbs3/bat_xfer.cpp index f8c9a6b56ee78720f6abae5a204544aaf4886405..23f0b139021e42937f59f60bd6fb75f7d032746c 100644 --- a/src/sbbs3/bat_xfer.cpp +++ b/src/sbbs3/bat_xfer.cpp @@ -49,14 +49,14 @@ void sbbs_t::batchmenu() } if (useron.misc & (RIP) && !(useron.misc & EXPERT)) menu("batchxfr"); - lncntr = 0; + term->lncntr = 0; while (online && (cfg.upload_dir != INVALID_DIR || batdn_total() || batup_total())) { if (!(useron.misc & (EXPERT | RIP))) { sys_status &= ~SS_ABORT; - if (lncntr) { + if (term->lncntr) { sync(); CRLF; - if (lncntr) /* CRLF or SYNC can cause pause */ + if (term->lncntr) /* CRLF or SYNC can cause pause */ pause(); } menu("batchxfr"); @@ -68,7 +68,7 @@ void sbbs_t::batchmenu() if (ch > ' ') logch(ch, 0); if (ch == quit_key() || ch == '\r') { /* Quit */ - lncntr = 0; + term->lncntr = 0; break; } switch (ch) { @@ -368,7 +368,7 @@ bool sbbs_t::start_batch_download() curdirnum = batdn_dir[i]; /* for ARS */ unpadfname(batdn_name[i], fname); if (cfg.dir[batdn_dir[i]]->seqdev) { - lncntr = 0; + term->lncntr = 0; SAFEPRINTF2(path, "%s%s", cfg.temp_dir, fname); if (!fexistcase(path)) { seqwait(cfg.dir[batdn_dir[i]]->seqdev); @@ -548,7 +548,7 @@ bool sbbs_t::process_batch_upload_queue() const char* filename = filenames[i]; int dir = batch_file_dir(&cfg, ini, filename); curdirnum = dir; /* for ARS */ - lncntr = 0; /* defeat pause */ + term->lncntr = 0; /* defeat pause */ SAFEPRINTF2(src, "%s%s", cfg.temp_dir, filename); SAFEPRINTF2(dest, "%s%s", cfg.dir[dir]->path, filename); @@ -639,7 +639,7 @@ void sbbs_t::batch_download(int xfrprot) for (size_t i = 0; filenames[i] != NULL; ++i) { char* filename = filenames[i]; - lncntr = 0; /* defeat pause */ + term->lncntr = 0; /* defeat pause */ if (xfrprot == -1 || checkprotresult(cfg.prot[xfrprot], 0, filename)) { file_t f = {{}}; if (!batch_file_load(&cfg, ini, filename, &f)) { @@ -680,7 +680,7 @@ void sbbs_t::batch_add_list(char *list) if (!fgets(str, sizeof(str) - 1, stream)) break; truncnl(str); - lncntr = 0; + term->lncntr = 0; for (i = j = k = 0; i < usrlibs; i++) { for (j = 0; j < usrdirs[i]; j++, k++) { outchar('.'); diff --git a/src/sbbs3/bulkmail.cpp b/src/sbbs3/bulkmail.cpp index d5cd78eeb37ac118fab1c852eaa4a70fb7bb39eb..57b705bc6ed3fd96f45d6fefe1304feaa43627e7 100644 --- a/src/sbbs3/bulkmail.cpp +++ b/src/sbbs3/bulkmail.cpp @@ -193,7 +193,7 @@ int sbbs_t::bulkmailhdr(smb_t* smb, smbmsg_t* msg, uint usernum) if (j != SMB_SUCCESS) return j; - lncntr = 0; + term->lncntr = 0; bprintf(text[Emailing], user.alias, usernum); SAFEPRINTF2(str, "bulk-mailed %s #%d" , user.alias, usernum); diff --git a/src/sbbs3/chat.cpp b/src/sbbs3/chat.cpp index b9069fbd0dc668ce6041de77abf7538fb1782714..6eb629bf227eac749e9e0f6fb3fd7d4560d5ff6b 100644 --- a/src/sbbs3/chat.cpp +++ b/src/sbbs3/chat.cpp @@ -410,7 +410,7 @@ void sbbs_t::multinodechat(int channel) ? text[AnonUserChatHandle] : useron.handle , cfg.node_num, ':', nulstr); - snprintf(tmp, sizeof tmp, "%*s", (int)bstrlen(str), nulstr); + snprintf(tmp, sizeof tmp, "%*s", (int)term->bstrlen(str), nulstr); SAFECAT(pgraph, tmp); } SAFECAT(pgraph, line); @@ -528,7 +528,7 @@ void sbbs_t::multinodechat(int channel) if (sys_status & SS_ABORT) break; } - lncntr = 0; + term->lncntr = 0; if (gurubuf != NULL) free(gurubuf); } @@ -681,17 +681,19 @@ bool sbbs_t::chan_access(int cnum) void sbbs_t::privchat(bool forced, int node_num) { char str[128], c, *p, localbuf[5][81], remotebuf[5][81] - , localline = 0, remoteline = 0, localchar = 0, remotechar = 0 + , localchar = 0, remotechar = 0 , *sep = text[PrivateChatSeparator] , *local_sep = text[SysopChatSeparator] ; + unsigned localline = 0, remoteline = 0; char tmp[512]; char outpath[MAX_PATH + 1]; char inpath[MAX_PATH + 1]; uchar ch; int wr; - int in, out, i, n, echo = 1, x, y, activity, remote_activity; - int local_y = 1, remote_y = 1; + int in, out, i, n, echo = 1, activity, remote_activity; + unsigned x, y; + unsigned local_y = 1, remote_y = 1; node_t node; time_t last_nodechk = 0; @@ -785,7 +787,7 @@ void sbbs_t::privchat(bool forced, int node_num) } if (((sys_status & SS_USERON && useron.chat & CHAT_SPLITP) || !(sys_status & SS_USERON)) - && term_supports(ANSI) && rows >= 24 && cols >= 80) + && term->can_move() && term->rows >= 24 && term->cols >= 80) sys_status |= SS_SPLITP; else sys_status &= ~SS_SPLITP; @@ -865,23 +867,23 @@ void sbbs_t::privchat(bool forced, int node_num) sync(); if (sys_status & SS_SPLITP) { - lncntr = 0; + term->lncntr = 0; CLS; - ansi_save(); - ansi_gotoxy(1, 13); + term->save_cursor_pos(); + term->gotoxy(1, 13); remote_y = 1; bprintf(forced ? local_sep : sep , thisnode.misc & NODE_MSGW ? 'T':' ' , sectostr(timeleft, tmp) , thisnode.misc & NODE_NMSG ? 'M':' '); - ansi_gotoxy(1, 14); + term->gotoxy(1, 14); local_y = 14; } while (online && (forced || !(sys_status & SS_ABORT))) { - lncntr = 0; + term->lncntr = 0; if (sys_status & SS_SPLITP) - lbuflen = 0; + term->lbuflen = 0; action = NODE_PCHT; activity = 0; remote_activity = 0; @@ -892,7 +894,7 @@ void sbbs_t::privchat(bool forced, int node_num) if (ch == BS || ch == DEL) { if (localchar) { if (echo) - backspace(); + term->backspace(); localchar--; localbuf[localline][localchar] = 0; } @@ -913,25 +915,25 @@ void sbbs_t::privchat(bool forced, int node_num) CLS; attr(cfg.color[clr_chatremote]); remotebuf[remoteline][remotechar] = 0; - for (i = 0; i <= remoteline; i++) { - bputs(remotebuf[i]); - if (i != remoteline) + for (unsigned u = 0; u <= remoteline; u++) { + bputs(remotebuf[u]); + if (u != remoteline) bputs(crlf); } remote_y = 1 + remoteline; bputs("\1i_\1n"); /* Fake cursor */ - ansi_save(); - ansi_gotoxy(1, 13); + term->save_cursor_pos(); + term->gotoxy(1, 13); bprintf(forced ? local_sep : sep , thisnode.misc & NODE_MSGW ? 'T':' ' , sectostr(timeleft, tmp) , thisnode.misc & NODE_NMSG ? 'M':' '); - ansi_gotoxy(1, 14); + term->gotoxy(1, 14); attr(cfg.color[clr_chatlocal]); localbuf[localline][localchar] = 0; - for (i = 0; i <= localline; i++) { - bputs(localbuf[i]); - if (i != localline) + for (unsigned u = 0; u <= localline; u++) { + bputs(localbuf[u]); + if (u != localline) bputs(crlf); } local_y = 15 + localline; @@ -951,19 +953,20 @@ void sbbs_t::privchat(bool forced, int node_num) localbuf[localline][localchar] = 0; localchar = 0; - if (sys_status & SS_SPLITP && local_y >= rows) { - ansi_gotoxy(1, 13); + if (sys_status & SS_SPLITP && local_y >= term->rows) { + term->gotoxy(1, 13); bprintf(forced ? local_sep : sep , thisnode.misc & NODE_MSGW ? 'T':' ' , sectostr(timeleft, tmp) , thisnode.misc & NODE_NMSG ? 'M':' '); attr(cfg.color[clr_chatlocal]); - for (x = 13, y = 0; x < rows; x++, y++) { - comprintf("\x1b[%d;1H\x1b[K", x + 1); + for (x = 13, y = 0; x < term->rows; x++, y++) { + term->gotoxy(1, x + 1); + term->cleartoeol(); if (y <= localline) bprintf("%s\r\n", localbuf[y]); } - ansi_gotoxy(1, local_y = (15 + localline)); + term->gotoxy(1, local_y = (15 + localline)); localline = 0; } else { @@ -976,7 +979,7 @@ void sbbs_t::privchat(bool forced, int node_num) CRLF; local_y++; if (sys_status & SS_SPLITP) - cleartoeol(); + term->cleartoeol(); } } // sync(); @@ -1032,16 +1035,16 @@ void sbbs_t::privchat(bool forced, int node_num) lprintf(LOG_DEBUG, "read character '%c' from %s", ch, inpath); activity = 1; if (sys_status & SS_SPLITP && !remote_activity) { - ansi_getxy(&x, &y); - ansi_restore(); + term->getxy(&x, &y); + term->restore_cursor_pos(); } attr(cfg.color[clr_chatremote]); if (sys_status & SS_SPLITP && !remote_activity) - backspace(); /* Delete fake cursor */ + term->backspace(); /* Delete fake cursor */ remote_activity = 1; if (ch == BS || ch == DEL) { if (remotechar) { - backspace(); + term->backspace(); remotechar--; remotebuf[remoteline][remotechar] = 0; } @@ -1073,13 +1076,14 @@ void sbbs_t::privchat(bool forced, int node_num) , sectostr(timeleft, tmp) , thisnode.misc & NODE_NMSG ? 'M':' '); attr(cfg.color[clr_chatremote]); - for (i = 0; i < 12; i++) { - bprintf("\x1b[%d;1H\x1b[K", i + 1); - if (i <= remoteline) - bprintf("%s\r\n", remotebuf[i]); + for (unsigned u = 0; u < 12; u++) { + term->gotoxy(1, u + 1); + term->cleartoeol(); + if (u <= remoteline) + bprintf("%s\r\n", remotebuf[u]); } remoteline = 0; - ansi_gotoxy(1, remote_y = 6); + term->gotoxy(1, remote_y = 6); } else { if (remoteline >= 4) @@ -1091,7 +1095,7 @@ void sbbs_t::privchat(bool forced, int node_num) CRLF; remote_y++; if (sys_status & SS_SPLITP) - cleartoeol(); + term->cleartoeol(); } } } @@ -1106,8 +1110,8 @@ void sbbs_t::privchat(bool forced, int node_num) if (sys_status & SS_SPLITP && remote_activity) { bputs("\1i_\1n"); /* Fake cursor */ - ansi_save(); - ansi_gotoxy(x, y); + term->save_cursor_pos(); + term->gotoxy(x, y); } now = time(NULL); @@ -1314,15 +1318,15 @@ void sbbs_t::nodemsg() break; if (getnodedat(cfg.node_num, &thisnode, false)) { if (thisnode.misc & (NODE_MSGW | NODE_NMSG)) { - lncntr = 0; /* prevent pause prompt */ - saveline(); + term->lncntr = 0; /* prevent pause prompt */ + term->saveline(); CRLF; if (thisnode.misc & NODE_NMSG) getnmsg(); if (thisnode.misc & NODE_MSGW) getsmsg(useron.number); CRLF; - restoreline(); + term->restoreline(); } else nodesync(); @@ -1726,7 +1730,7 @@ void sbbs_t::guruchat(char* line, char* gurubuf, int gurunum, char* last_answer) if (sbbs_random(100)) { mswait(100 + sbbs_random(300)); while (c) { - backspace(); + term->backspace(); mswait(50 + sbbs_random(50)); c--; } @@ -1922,7 +1926,6 @@ void sbbs_t::localguru(char *gurubuf, int gurunum) return; sys_status |= SS_GURUCHAT; console &= ~(CON_L_ECHOX | CON_R_ECHOX); /* turn off X's */ - console |= (CON_L_ECHO | CON_R_ECHO); /* make sure echo is on */ if (action == NODE_CHAT) { /* only page if from chat section */ bprintf(text[PagingGuru], cfg.guru[gurunum]->name); ch = sbbs_random(25) + 25; diff --git a/src/sbbs3/chk_ar.cpp b/src/sbbs3/chk_ar.cpp index c73ee739f66232412fb32d6d60267d93be4fff2b..f850a6f10d1fe81f1e3e8c98ddf5b775bed9a5ec 100644 --- a/src/sbbs3/chk_ar.cpp +++ b/src/sbbs3/chk_ar.cpp @@ -109,7 +109,7 @@ bool sbbs_t::ar_exp(const uchar **ptrptr, user_t* user, client_t* client) } break; case AR_ANSI: - if (!term_supports(ANSI)) + if (!term->supports(ANSI)) result = _not; else result = !_not; @@ -119,37 +119,37 @@ bool sbbs_t::ar_exp(const uchar **ptrptr, user_t* user, client_t* client) } break; case AR_PETSCII: - if ((term_supports() & CHARSET_FLAGS) != CHARSET_PETSCII) + if (term->charset() != CHARSET_PETSCII) result = _not; else result = !_not; if (!result) { noaccess_str = text[NoAccessTerminal]; - noaccess_val = PETSCII; + noaccess_val = CHARSET_PETSCII; } break; case AR_ASCII: - if ((term_supports() & CHARSET_FLAGS) != CHARSET_ASCII) + if (term->charset() != CHARSET_ASCII) result = _not; else result = !_not; if (!result) { noaccess_str = text[NoAccessTerminal]; - noaccess_val = NO_EXASCII; + noaccess_val = CHARSET_ASCII; } break; case AR_UTF8: - if ((term_supports() & CHARSET_FLAGS) != CHARSET_UTF8) + if (term->charset() != CHARSET_UTF8) result = _not; else result = !_not; if (!result) { noaccess_str = text[NoAccessTerminal]; - noaccess_val = UTF8; + noaccess_val = CHARSET_UTF8; } break; case AR_CP437: - if ((term_supports() & CHARSET_FLAGS) != CHARSET_CP437) + if (term->charset() != CHARSET_CP437) result = _not; else result = !_not; @@ -159,7 +159,7 @@ bool sbbs_t::ar_exp(const uchar **ptrptr, user_t* user, client_t* client) } break; case AR_RIP: - if (!term_supports(RIP)) + if (!term->supports(RIP)) result = _not; else result = !_not; @@ -707,7 +707,7 @@ bool sbbs_t::ar_exp(const uchar **ptrptr, user_t* user, client_t* client) } break; case AR_COLS: - if ((equal && cols != (long)n) || (!equal && cols < (long)n)) + if ((equal && term->cols != n) || (!equal && term->cols < n)) result = _not; else result = !_not; @@ -717,7 +717,7 @@ bool sbbs_t::ar_exp(const uchar **ptrptr, user_t* user, client_t* client) } break; case AR_ROWS: - if ((equal && rows != (long)n) || (!equal && rows < (long)n)) + if ((equal && term->rows != n) || (!equal && term->rows < n)) result = _not; else result = !_not; diff --git a/src/sbbs3/con_hi.cpp b/src/sbbs3/con_hi.cpp index 06efbdaf156865b80e80c825b67e44cf921a4a33..d314d16beda9ec991f621a2320007e9db9ebe544 100644 --- a/src/sbbs3/con_hi.cpp +++ b/src/sbbs3/con_hi.cpp @@ -31,19 +31,19 @@ void sbbs_t::redrwstr(char *strin, int i, int l, int mode) if (i <= 0) i = 0; else - cursor_left(i); + term->cursor_left(i); if (l < 0) l = 0; if (mode) bprintf(mode, "%-*.*s", l, l, strin); else - column += rprintf("%-*.*s", l, l, strin); - cleartoeol(); + rprintf("%-*.*s", l, l, strin); + term->cleartoeol(); if (i < l) { auto_utf8(strin, mode); if (mode & P_UTF8) l = utf8_str_total_width(strin, unicode_zerowidth); - cursor_left(l - i); + term->cursor_left(l - i); } } @@ -62,7 +62,7 @@ int sbbs_t::uselect(bool add, uint n, const char *title, const char *item, const if (!uselect_total) bprintf(text[SelectItemHdr], title); uselect_num[uselect_total++] = n; - add_hotspot(uselect_total); + term->add_hotspot(uselect_total); bprintf(text[SelectItemFmt], uselect_total, item); return 0; } @@ -80,7 +80,7 @@ int sbbs_t::uselect(bool add, uint n, const char *title, const char *item, const i = getnum(uselect_total); t = uselect_total; uselect_total = 0; - clear_hotspots(); + term->clear_hotspots(); if (i < 0) return -1; if (!i) { /* User hit ENTER, use default */ @@ -159,7 +159,7 @@ bool sbbs_t::chksyspass(const char* sys_pw) bputs(text[SystemPassword]); getstr(str, sizeof(cfg.sys_pass) - 1, K_UPPER | K_NOECHO); CRLF; - lncntr = 0; + term->lncntr = 0; } if (stricmp(cfg.sys_pass, str)) { if (cfg.sys_misc & SM_ECHO_PW) diff --git a/src/sbbs3/con_out.cpp b/src/sbbs3/con_out.cpp index a219d293a3c353299bb4bfd4c35b5fd9291fff32..b1e416363d6f22797d90c1d4155fe113c69ee98b 100644 --- a/src/sbbs3/con_out.cpp +++ b/src/sbbs3/con_out.cpp @@ -53,12 +53,11 @@ int sbbs_t::bputs(const char *str, int mode) { int i; size_t l = 0; - int term = term_supports(); if ((mode & P_REMOTE) && online != ON_REMOTE) return 0; - if (online == ON_LOCAL && console & CON_L_ECHO) /* script running as event */ + if (online == ON_LOCAL) /* script running as event */ return lputs(LOG_INFO, str); str = auto_utf8(str, mode); @@ -72,7 +71,7 @@ int sbbs_t::bputs(const char *str, int mode) case CTRL_A: break; default: // printing char - if ((mode & P_TRUNCATE) && column >= (cols - 1)) { + if ((mode & P_TRUNCATE) && term->column >= (term->cols - 1)) { l++; continue; } @@ -84,14 +83,14 @@ int sbbs_t::bputs(const char *str, int mode) if (str[l] == '~') { // Mouse hot-spot (hungry) l++; if (str[l] >= ' ') - add_hotspot(str[l], /* hungry */ true); + term->add_hotspot(str[l], /* hungry */ true); else - add_hotspot('\r', /* hungry */ true); + term->add_hotspot('\r', /* hungry */ true); continue; } if (str[l] == '`' && str[l + 1] >= ' ') { // Mouse hot-spot (strict) l++; - add_hotspot(str[l], /* hungry */ false); + term->add_hotspot(str[l], /* hungry */ false); continue; } ctrl_a(str[l++]); @@ -117,13 +116,16 @@ int sbbs_t::bputs(const char *str, int mode) } } if (mode & P_PETSCII) { - if (term & PETSCII) - outcom(str[l++]); + if (term->charset() == CHARSET_PETSCII) + term_out(str[l++]); else petscii_to_ansibbs(str[l++]); + } else if (str[l] == '\r' && str[l + 1] == '\n') { + term->newline(); + l += 2; } else if ((str[l] & 0x80) && (mode & P_UTF8)) { - if (term & UTF8) - outcom(str[l++]); + if (term->charset() == CHARSET_UTF8) + term_out(str[l++]); else l += print_utf8_as_cp437(str + l, len - l); } else @@ -132,38 +134,6 @@ int sbbs_t::bputs(const char *str, int mode) return l; } -/****************************************************************************/ -/* Returns the printed columns from 'str' accounting for Ctrl-A codes */ -/****************************************************************************/ -size_t sbbs_t::bstrlen(const char *str, int mode) -{ - str = auto_utf8(str, mode); - size_t count = 0; - const char* end = str + strlen(str); - while (str < end) { - int len = 1; - if (*str == CTRL_A) { - str++; - if (*str == 0 || *str == 'Z') // EOF - break; - if (*str == '[') // CR - count = 0; - else if (*str == '<' && count) // ND-Backspace - count--; - } else if (((*str) & 0x80) && (mode & P_UTF8)) { - enum unicode_codepoint codepoint = UNICODE_UNDEFINED; - len = utf8_getc(str, end - str, &codepoint); - if (len < 1) - break; - count += unicode_width(codepoint, unicode_zerowidth); - } else - count++; - str += len; - } - return count; -} - - /* Perform PETSCII terminal output translation (from ASCII/CP437) */ unsigned char cp437_to_petscii(unsigned char ch) { @@ -252,14 +222,14 @@ int sbbs_t::petscii_to_ansibbs(unsigned char ch) if (IS_ALPHA(ch)) return outchar(ch ^ 0x20); /* swap upper/lower case */ switch (ch) { - case '\r': newline(); break; - case PETSCII_HOME: cursor_home(); break; + case '\r': term->newline(); break; + case PETSCII_HOME: term->cursor_home(); break; case PETSCII_CLEAR: return CLS; - case PETSCII_DELETE: backspace(); break; - case PETSCII_LEFT: cursor_left(); break; - case PETSCII_RIGHT: cursor_right(); break; - case PETSCII_UP: cursor_up(); break; - case PETSCII_DOWN: cursor_down(); break; + case PETSCII_DELETE: term->backspace(); break; + case PETSCII_LEFT: term->cursor_left(); break; + case PETSCII_RIGHT: term->cursor_right(); break; + case PETSCII_UP: term->cursor_up(); break; + case PETSCII_DOWN: term->cursor_down(); break; case PETSCII_BRITPOUND: return outchar((char)156); case PETSCII_CHECKMARK: return outchar((char)251); @@ -365,50 +335,164 @@ 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); - int term = term_supports(); - char utf8[UTF8_MAX_LEN + 1] = ""; - for (l = 0; l < len && online; l++) { - uchar ch = str[l]; - utf8[0] = 0; - if (term & PETSCII) { - ch = cp437_to_petscii(ch); - if (ch == PETSCII_SOLID) - outcom(PETSCII_REVERSE_ON); - } - else if ((term & NO_EXASCII) && (ch & 0x80)) - ch = exascii_to_ascii_char(ch); /* seven bit table */ - else if (term & UTF8) { - enum unicode_codepoint codepoint = cp437_unicode_tbl[(uchar)ch]; - if (codepoint != 0) - utf8_putc(utf8, sizeof(utf8) - 1, codepoint); + 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 '\b': // BS + term->cursor_left(); + return 1; + case '\t': // TAB + if (term->column < (term->cols - 1)) { + outchar(' '); + while ((term->column < (term->cols - 1)) && (term->column % term->tabstop)) + outchar(' '); + } + return 1; + + case '\n': // LF + term->line_feed(); + return 1; + case '\r': // CR + term->carriage_return(); + return 1; + case FF: // 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->charset() == CHARSET_UTF8) { + if (ch != '\a') { + 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) != (size_t)len) + return 0; + return 1; } - 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 & PETSCII) && ch == PETSCII_SOLID) - outcom(PETSCII_REVERSE_OFF); + } + // PETSCII + else if (term->charset() == CHARSET_PETSCII) { + ch = cp437_to_petscii(ch); + // TODO: This needs to be aware of the current state of reverse... + // It could cast sbbs->term to PETSCII_Terminal (ugh) + if (ch == PETSCII_SOLID) { + if (term_out(PETSCII_REVERSE_ON) != 1) + return 0; } - if (ch == '\n') - lbuflen = 0; - else if (lbuflen < LINE_BUFSIZE) { - if (lbuflen == 0) - latr = curatr; - lbuf[lbuflen++] = str[l]; // save non-translated char to line buffer + if (term_out(ch) != 1) + return 0; + if (ch == PETSCII_SOLID) { + if (term_out(PETSCII_REVERSE_OFF) != 1) + return 0; } + return 1; } - return l; + // CP437 or US-ASCII + if ((term->charset() == CHARSET_ASCII) && (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; + // We do this before parse_output() so parse_output() can + // prevent \n from ending up at the start of the line buffer. + if (term->lbuflen < LINE_BUFSIZE && !term->suspend_lbuf) { + if (term->lbuflen == 0) + term->latr = 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 (!term->parse_output(ch)) + return 1; + if (ch == TELNET_IAC && !(telnet_mode & TELNET_MODE_OFF)) { + if (outcom(TELNET_IAC)) + return 0; + } + if (outcom(ch)) + return 0; + return 1; } /****************************************************************************/ @@ -461,9 +545,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]; @@ -472,61 +556,22 @@ 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); -} - -/****************************************************************************/ -/* Outputs destructive backspace */ -/****************************************************************************/ -void sbbs_t::backspace(int count) -{ - if (count < 1) - return; - if (!(console & CON_ECHO_OFF)) { - for (int i = 0; i < count; i++) { - if (term_supports(PETSCII)) - outcom(PETSCII_DELETE); - else { - outcom('\b'); - outcom(' '); - outcom('\b'); - } - if (column > 0) - column--; - if (lbuflen > 0) - lbuflen--; - } - } -} - -/****************************************************************************/ -/* Returns true if the user (or the yet-to-be-logged-in client) supports */ -/* all of the specified terminal 'cmp_flags' (e.g. ANSI, COLOR, RIP). */ -/* If no flags specified, returns all terminal flag bits supported */ -/****************************************************************************/ -int sbbs_t::term_supports(int cmp_flags) -{ - int flags = ((sys_status & (SS_USERON | SS_NEWUSER)) && !(useron.misc & AUTOTERM)) ? useron.misc : autoterm; - - if ((sys_status & (SS_USERON | SS_NEWUSER)) && (useron.misc & AUTOTERM)) - flags |= useron.misc & (NO_EXASCII | SWAP_DELETE | COLOR | ICE_COLOR | MOUSE); - - return cmp_flags ? ((flags & cmp_flags) == cmp_flags) : (flags & TERM_FLAGS); + return term_out(sbuf); } char* sbbs_t::term_rows(user_t* user, char* str, size_t size) { if (user->rows >= TERM_ROWS_MIN && user->rows <= TERM_ROWS_MAX) - rows = user->rows; - safe_snprintf(str, size, "%s%d %s", user->rows ? nulstr:text[TerminalAutoDetect], rows, text[TerminalRows]); + term->rows = user->rows; + safe_snprintf(str, size, "%s%d %s", user->rows ? nulstr:text[TerminalAutoDetect], term->rows, text[TerminalRows]); return str; } char* sbbs_t::term_cols(user_t* user, char* str, size_t size) { if (user->cols >= TERM_COLS_MIN && user->cols <= TERM_COLS_MAX) - cols = user->cols; - safe_snprintf(str, size, "%s%d %s", user->cols ? nulstr:text[TerminalAutoDetect], cols, text[TerminalColumns]); + term->cols = user->cols; + safe_snprintf(str, size, "%s%d %s", user->cols ? nulstr:text[TerminalAutoDetect], term->cols, text[TerminalColumns]); return str; } @@ -556,7 +601,7 @@ char* sbbs_t::term_type(user_t* user, int term, char* str, size_t size) const char* sbbs_t::term_type(int term) { if (term == -1) - term = term_supports(); + term = this->term->flags(); if (term & PETSCII) return "PETSCII"; if (term & RIP) @@ -572,7 +617,7 @@ const char* sbbs_t::term_type(int term) const char* sbbs_t::term_charset(int term) { if (term == -1) - term = term_supports(); + return this->term->charset_str(); if (term & PETSCII) return "CBM-ASCII"; if (term & UTF8) @@ -587,13 +632,14 @@ const char* sbbs_t::term_charset(int term) /****************************************************************************/ bool sbbs_t::update_nodeterm(void) { + update_terminal(this); str_list_t ini = strListInit(); - iniSetInteger(&ini, ROOT_SECTION, "cols", cols, NULL); - iniSetInteger(&ini, ROOT_SECTION, "rows", rows, NULL); + iniSetInteger(&ini, ROOT_SECTION, "cols", term->cols, NULL); + iniSetInteger(&ini, ROOT_SECTION, "rows", term->rows, NULL); iniSetString(&ini, ROOT_SECTION, "desc", terminal, NULL); iniSetString(&ini, ROOT_SECTION, "type", term_type(), NULL); - iniSetString(&ini, ROOT_SECTION, "chars", term_charset(), NULL); - iniSetHexInt(&ini, ROOT_SECTION, "flags", term_supports(), NULL); + iniSetString(&ini, ROOT_SECTION, "chars", term->charset_str(), NULL); + iniSetHexInt(&ini, ROOT_SECTION, "flags", term->flags(), NULL); iniSetHexInt(&ini, ROOT_SECTION, "mouse", mouse_mode, NULL); iniSetHexInt(&ini, ROOT_SECTION, "console", console, NULL); @@ -612,12 +658,12 @@ bool sbbs_t::update_nodeterm(void) char topic[128]; SAFEPRINTF(topic, "node/%u/terminal", cfg.node_num); snprintf(str, sizeof(str), "%u\t%u\t%s\t%s\t%s\t%x\t%x\t%x" - , cols - , rows + , term->cols + , term->rows , terminal , term_type() - , term_charset() - , term_supports() + , term->charset_str() + , term->flags() , mouse_mode , console ); @@ -626,9 +672,48 @@ bool sbbs_t::update_nodeterm(void) return result; } +/* + * bputs putmsg + * \ / + * outchar rputs + * +---------| + * cp437_out outcp + * +-----------+ + * putcom term_out + * +-----------+ + * outcom + * | + * RingBufWrite + * + * In this model: + * bputs() and putmsg() call term_out to bypass charset translations + * (ie: PETSCII and UTF-8 output) + * + * outchar() does tab, FF, rainbow, and auto-pause + * + * rputs() effectively just calls cp437_out() and keeps the line counter + * unchanged. It's used by console.print(), so keeping the + * behaviour unchanged may be important. + * + * cp437_out() translates from cp437 to the terminal charset this + * specifically includes the control characters + * BEL, BS, TAB, LF, FF, CR, and DEL + * + * outcp() calls term_out() if UTF-8 is supported, or bputs() if not + * + * 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. + * + * term_out() updates column and row, and maintains the line buffer + * + * outcom() and RingBufWrite() are post-IAC expansion + */ + /****************************************************************************/ /* Outputs character */ -/* Performs terminal translations (e.g. EXASCII-to-ASCII, FF->ESC[2J) */ +/* Performs charset translations (e.g. EXASCII-to-ASCII, CP437-to-PETSCII) */ +/* Performs terminal expansions and state parsing (e.g. FF to ESC[2JESC[H) */ /* Performs Telnet IAC escaping */ /* Performs tab expansion */ /* Performs column counting, line counting, and auto-pausing */ @@ -638,46 +723,8 @@ int sbbs_t::outchar(char ch) { if (console & CON_ECHO_OFF) return 0; - if (ch == ESC && outchar_esc < ansiState_string) - outchar_esc = ansiState_esc; - else if (outchar_esc == ansiState_esc) { - if (ch == '[') - outchar_esc = ansiState_csi; - else if (ch == '_' || ch == 'P' || ch == '^' || ch == ']') - outchar_esc = ansiState_string; - else if (ch == 'X') - outchar_esc = ansiState_sos; - else if (ch >= '@' && ch <= '_') - outchar_esc = ansiState_final; - else - outchar_esc = ansiState_none; - } - else if (outchar_esc == ansiState_csi) { - if (ch >= '@' && ch <= '~') - outchar_esc = ansiState_final; - } - else if (outchar_esc == ansiState_string) { // APS, DCS, PM, or OSC - 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 - if (ch == ESC) - outchar_esc = ansiState_sos_esc; - } - else if (outchar_esc == ansiState_sos_esc) { // ESC inside SOS - if (ch == '\\') - outchar_esc = ansiState_esc; - else if (ch == 'X') - outchar_esc = ansiState_none; - else - outchar_esc = ansiState_sos; - } - else - outchar_esc = ansiState_none; - if (outchar_esc == ansiState_none && rainbow_index >= 0) { + if (rainbow_index >= 0) { attr(rainbow[rainbow_index]); if (rainbow[rainbow_index + 1] == 0) { if (rainbow_repeat) @@ -685,127 +732,60 @@ int sbbs_t::outchar(char ch) } else ++rainbow_index; } - int term = term_supports(); - char utf8[UTF8_MAX_LEN + 1] = ""; - if (!(term & PETSCII)) { - if ((term & NO_EXASCII) && (ch & 0x80)) - ch = exascii_to_ascii_char(ch); /* seven bit table */ - else if (term & 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]; + + if (ch == '\n' && line_delay) + SLEEP(line_delay); + + /* + * When line counter overflows, pause on the next pause-eligable line + * and log a debug message + */ + if (term->lncntr >= term->rows - 1 && term->column == 0) { + unsigned lost = term->lncntr - (term->rows - 1); + term->lncntr = term->rows -1; + if (check_pause()) { + if (lost) + lprintf(LOG_DEBUG, "line counter overflowed, %u lines scrolled", lost); } } - if (ch == FF && lncntr > 0 && row > 0) { - lncntr = 0; - newline(); + if (ch == FF && term->lncntr > 0 && term->row > 0) { + term->lncntr = 0; + term->newline(); if (!(sys_status & SS_PAUSEOFF)) { pause(); - while (lncntr && online && !(sys_status & SS_ABORT)) + while (term->lncntr && online && !(sys_status & SS_ABORT)) pause(); } } - if (!(console & CON_R_ECHO)) - return 0; + cp437_out(ch); - if ((console & CON_R_ECHOX) && (uchar)ch >= ' ' && outchar_esc == ansiState_none) { - ch = *text[PasswordChar]; - } - if (ch == FF) - clearscreen(term); - else if (ch == '\t') { - outcom(' '); - column++; - while (column % tabstop) { - outcom(' '); - column++; - } - } - else { - if (ch == (char)TELNET_IAC && !(telnet_mode & TELNET_MODE_OFF)) - outcom(TELNET_IAC); /* Must escape Telnet IAC char (255) */ - if (ch == '\r' && (console & CON_CR_CLREOL)) - cleartoeol(); - if (ch == '\n' && line_delay) - SLEEP(line_delay); - if (term & 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); - if (ch == '\r' && (curatr & 0xf0) != 0) // reverse video is disabled upon CR - curatr >>= 4; - } else { - if (utf8[0] != 0) - putcom(utf8); - else - outcom(ch); - } - } - if (outchar_esc == ansiState_none) { - /* Track cursor position locally */ - switch (ch) { - case '\a': // 7 - case '\t': // 9 - /* Non-printing or handled elsewhere */ - break; - case '\b': // 8 - if (column > 0) - column--; - if (lbuflen < LINE_BUFSIZE) { - if (lbuflen == 0) - latr = curatr; - lbuf[lbuflen++] = ch; - } - break; - case '\n': // 10 - inc_row(1); - if (lncntr || lastlinelen) - lncntr++; - lbuflen = 0; - break; - case FF: // 12 - lncntr = 0; - lbuflen = 0; - row = 0; - column = 0; - case '\r': // 13 - lastlinelen = column; - column = 0; - break; - default: - inc_column(1); - if (!lbuflen) - latr = curatr; - if (lbuflen < LINE_BUFSIZE) - lbuf[lbuflen++] = ch; - break; - } - } - if (outchar_esc == ansiState_final) - outchar_esc = ansiState_none; + check_pause(); + return 0; +} - if (lncntr == rows - 1 && ((useron.misc & (UPAUSE ^ (console & CON_PAUSEOFF))) || sys_status & SS_PAUSEON) +bool sbbs_t::check_pause() { + if (term->lncntr == term->rows - 1 && ((useron.misc & (UPAUSE ^ (console & CON_PAUSEOFF))) || sys_status & SS_PAUSEON) && !(sys_status & (SS_PAUSEOFF | SS_ABORT))) { - lncntr = 0; + term->lncntr = 0; pause(); + return true; } - return 0; + return false; } -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)) { + if (term->charset() == CHARSET_UTF8) { char str[UTF8_MAX_LEN]; int len = utf8_putc(str, sizeof(str), codepoint); if (len < 1) return len; - putcom(str, len); - inc_column(unicode_width(codepoint, unicode_zerowidth)); + term_out(str, len); + term->inc_column(unicode_width(codepoint, unicode_zerowidth)); return 0; } if (cp437_fallback == NULL) @@ -813,60 +793,17 @@ 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); -} - -void sbbs_t::inc_column(int count) -{ - column += count; - if (column >= cols) { // assume terminal has/will auto-line-wrap - lncntr++; - lbuflen = 0; - lastlinelen = column; - column = 0; - inc_row(1); - } -} - -void sbbs_t::inc_row(int count) -{ - row += count; - if (row >= rows) { - scroll_hotspots((row - rows) + 1); - row = rows - 1; - } -} - -void sbbs_t::center(const char *instr, bool msg, unsigned int columns) -{ - char str[256]; - size_t len; - - if (columns < 1) - columns = cols; - - SAFECOPY(str, instr); - truncsp(str); - len = bstrlen(str); - carriage_return(); - if (len < columns) - cursor_right((columns - len) / 2); - if (msg) - putmsg(str, P_NONE); - else - bputs(str); - newline(); + return outcp(codepoint, str); } void sbbs_t::wide(const char* str) { - int term = term_supports(); while (*str != '\0') { - if ((term & UTF8) && *str >= '!' && *str <= '~') - outchar((enum unicode_codepoint)(UNICODE_FULLWIDTH_EXCLAMATION_MARK + (*str - '!'))); + if ((term->charset() == CHARSET_UTF8) && *str >= '!' && *str <= '~') + outcp((enum unicode_codepoint)(UNICODE_FULLWIDTH_EXCLAMATION_MARK + (*str - '!'))); else { outchar(*str); outchar(' '); @@ -875,244 +812,17 @@ void sbbs_t::wide(const char* str) } } - -// Send a bare carriage return, hopefully moving the cursor to the far left, current row -void sbbs_t::carriage_return(int count) -{ - if (count < 1) - return; - for (int i = 0; i < count; i++) { - if (term_supports(PETSCII)) - cursor_left(column); - else - outcom('\r'); - column = 0; - } -} - -// Send a bare line_feed, hopefully moving the cursor down one row, current column -void sbbs_t::line_feed(int count) -{ - if (count < 1) - return; - for (int i = 0; i < count; i++) { - if (term_supports(PETSCII)) - outcom(PETSCII_DOWN); - else - outcom('\n'); - } - inc_row(count); -} - -void sbbs_t::newline(int count) -{ - if (count < 1) - return; - for (int i = 0; i < count; i++) { - outchar('\r'); - outchar('\n'); - } -} - -void sbbs_t::clearscreen(int term) -{ - clear_hotspots(); - if (term & ANSI) - putcom("\x1b[2J\x1b[H"); /* clear screen, home cursor */ - else if (term & PETSCII) - outcom(PETSCII_CLEAR); - else - outcom(FF); - row = 0; - column = 0; - lncntr = 0; -} - -void sbbs_t::clearline(void) -{ - carriage_return(); - cleartoeol(); -} - -void sbbs_t::cursor_home(void) -{ - int term = term_supports(); - if (term & ANSI) - putcom("\x1b[H"); - else if (term & PETSCII) - outcom(PETSCII_HOME); - else - outchar(FF); /* this will clear some terminals, do nothing with others */ - row = 0; - column = 0; -} - -void sbbs_t::cursor_up(int count) -{ - if (count < 1) - return; - int term = term_supports(); - if (term & ANSI) { - if (count > 1) - comprintf("\x1b[%dA", count); - else - putcom("\x1b[A"); - } else { - if (term & PETSCII) { - for (int i = 0; i < count; i++) - outcom(PETSCII_UP); - } - } -} - -void sbbs_t::cursor_down(int count) -{ - if (count < 1) - return; - if (term_supports(ANSI)) { - if (count > 1) - comprintf("\x1b[%dB", count); - else - putcom("\x1b[B"); - inc_row(count); - } else { - for (int i = 0; i < count; i++) - line_feed(); - } -} - -void sbbs_t::cursor_right(int count) -{ - if (count < 1) - return; - int term = term_supports(); - if (term & ANSI) { - if (count > 1) - comprintf("\x1b[%dC", count); - else - putcom("\x1b[C"); - } else { - for (int i = 0; i < count; i++) { - if (term & PETSCII) - outcom(PETSCII_RIGHT); - else - outcom(' '); - } - } - column += count; -} - -void sbbs_t::cursor_left(int count) -{ - if (count < 1) - return; - int term = term_supports(); - if (term & ANSI) { - if (count > 1) - comprintf("\x1b[%dD", count); - else - putcom("\x1b[D"); - } else { - for (int i = 0; i < count; i++) { - if (term & PETSCII) - outcom(PETSCII_LEFT); - else - outcom('\b'); - } - } - if (column > count) - column -= count; - else - column = 0; -} - -bool sbbs_t::cursor_xy(int x, int y) -{ - int term = term_supports(); - if (term & ANSI) - return ansi_gotoxy(x, y); - if (term & PETSCII) { - outcom(PETSCII_HOME); - cursor_down(y - 1); - cursor_right(x - 1); - return true; - } - return false; -} - -bool sbbs_t::cursor_getxy(int* x, int* y) -{ - if (term_supports(ANSI)) - return ansi_getxy(x, y); - *x = column + 1; - *y = row + 1; - return true; -} - -void sbbs_t::cleartoeol(void) -{ - int i, j; - - int term = term_supports(); - if (term & ANSI) - putcom("\x1b[K"); - else { - i = j = column; - while (++i <= cols) - outcom(' '); - while (++j <= cols) { - if (term & PETSCII) - outcom(PETSCII_LEFT); - else - outcom('\b'); - } - } -} - -void sbbs_t::cleartoeos(void) -{ - if (term_supports(ANSI)) - putcom("\x1b[J"); -} - -void sbbs_t::set_output_rate(enum output_rate speed) -{ - if (term_supports(ANSI)) { - unsigned int val = speed; - switch (val) { - case 0: val = 0; break; - case 600: val = 2; break; - case 1200: val = 3; break; - case 2400: val = 4; break; - case 4800: val = 5; break; - case 9600: val = 6; break; - case 19200: val = 7; break; - case 38400: val = 8; break; - case 57600: val = 9; break; - case 76800: val = 10; break; - default: - if (val <= 300) - val = 1; - else if (val > 76800) - val = 11; - break; - } - comprintf("\x1b[;%u*r", val); - cur_output_rate = speed; - } -} - /****************************************************************************/ /* Get the dimensions of the current user console, place into row and cols */ /****************************************************************************/ void sbbs_t::getdimensions() { if (sys_status & SS_USERON) { - ansi_getdims(); + term->getdims(); if (useron.rows >= TERM_ROWS_MIN && useron.rows <= TERM_ROWS_MAX) - rows = useron.rows; + term->rows = useron.rows; if (useron.cols >= TERM_COLS_MIN && useron.cols <= TERM_COLS_MAX) - cols = useron.cols; + term->cols = useron.cols; } } @@ -1174,7 +884,7 @@ void sbbs_t::ctrl_a(char x) return; if ((uchar)x > 0x7f) { - cursor_right((uchar)x - 0x7f); + term->cursor_right((uchar)x - 0x7f); return; } if (valid_ctrl_a_attr(x)) @@ -1202,7 +912,7 @@ void sbbs_t::ctrl_a(char x) pause(); break; case 'Q': /* Pause reset */ - lncntr = 0; + term->lncntr = 0; break; case 'T': /* Time */ now = time(NULL); @@ -1233,35 +943,35 @@ void sbbs_t::ctrl_a(char x) sync(); break; case 'J': /* clear to end-of-screen */ - cleartoeos(); + term->cleartoeos(); break; case 'L': /* CLS (form feed) */ CLS; break; case '\'': /* Home cursor */ case '`': // usurped by strict hot-spot - cursor_home(); + term->cursor_home(); break; case '>': /* CLREOL */ - cleartoeol(); + term->cleartoeol(); break; case '<': /* Non-destructive backspace */ - cursor_left(); + term->cursor_left(); break; case '/': /* Conditional new-line */ - cond_newline(); + term->cond_newline(); break; case '\\': /* Conditional New-line / Continuation prefix (if cols < 80) */ - cond_contline(); + term->cond_contline(); break; case '?': /* Conditional blank-line */ - cond_blankline(); + term->cond_blankline(); break; case '[': /* Carriage return */ - carriage_return(); + term->carriage_return(); break; case ']': /* Line feed */ - line_feed(); + term->line_feed(); break; case 'A': /* Ctrl-A */ outchar(CTRL_A); @@ -1274,12 +984,10 @@ void sbbs_t::ctrl_a(char x) attr(atr); break; case 'I': - if ((term_supports() & (ICE_COLOR | PETSCII)) != ICE_COLOR) - attr(atr | BLINK); + attr(atr | BLINK); break; case 'E': /* Bright Background */ - if (term_supports() & (ICE_COLOR | PETSCII)) - attr(atr | BG_BRIGHT); + attr(atr | BG_BRIGHT); break; case 'F': /* Blink, only if alt Blink Font is loaded */ if (((atr & HIGH) && (console & CON_HBLINK_FONT)) || (!(atr & HIGH) && (console & CON_BLINK_FONT))) @@ -1359,78 +1067,11 @@ void sbbs_t::ctrl_a(char x) /****************************************************************************/ int sbbs_t::attr(int atr) { - char str[16]; - int newatr = atr; + char str[128]; - int term = term_supports(); - if (term & PETSCII) { - if (atr & (0x70 | BG_BRIGHT)) { // background color (reverse video for PETSCII) - if (atr & BG_BRIGHT) - atr |= HIGH; - else - atr &= ~HIGH; - atr = (atr & (BLINK | HIGH)) | ((atr & 0x70) >> 4); - outcom(PETSCII_REVERSE_ON); - } else - outcom(PETSCII_REVERSE_OFF); - if (atr & BLINK) - outcom(PETSCII_FLASH_ON); - else - outcom(PETSCII_FLASH_OFF); - switch (atr & 0x0f) { - case BLACK: - outcom(PETSCII_BLACK); - break; - case WHITE: - outcom(PETSCII_WHITE); - break; - case DARKGRAY: - outcom(PETSCII_DARKGRAY); - break; - case LIGHTGRAY: - outcom(PETSCII_LIGHTGRAY); - break; - case BLUE: - outcom(PETSCII_BLUE); - break; - case LIGHTBLUE: - outcom(PETSCII_LIGHTBLUE); - break; - case CYAN: - outcom(PETSCII_MEDIUMGRAY); - break; - case LIGHTCYAN: - outcom(PETSCII_CYAN); - break; - case YELLOW: - outcom(PETSCII_YELLOW); - break; - case BROWN: - outcom(PETSCII_BROWN); - break; - case RED: - outcom(PETSCII_RED); - break; - case LIGHTRED: - outcom(PETSCII_LIGHTRED); - break; - case GREEN: - outcom(PETSCII_GREEN); - break; - case LIGHTGREEN: - outcom(PETSCII_LIGHTGREEN); - break; - case MAGENTA: - outcom(PETSCII_ORANGE); - break; - case LIGHTMAGENTA: - outcom(PETSCII_PURPLE); - break; - } - } - else if (term & ANSI) - rputs(ansi(newatr, curatr, str)); - curatr = newatr; + term->attrstr(atr, str, sizeof(str)); + term_out(str); + curatr = atr; return 0; } @@ -1463,7 +1104,7 @@ int sbbs_t::backfill(const char* instr, float pct, int full_attr, int empty_attr char* str = strip_ctrl(instr, NULL); len = strlen(str); - if (!(term_supports() & (ANSI | PETSCII))) + if (!(term->can_highlight())) bputs(str, P_REMOTE); else { for (int i = 0; i < len; i++) { @@ -1493,51 +1134,8 @@ void sbbs_t::progress(const char* text, int count, int total, int interval) if (text == NULL) text = ""; float pct = total ? ((float)count / total) * 100.0F : 100.0F; - SAFEPRINTF2(str, "[ %-8s %4.1f%% ]", text, pct); - cond_newline(); - cursor_left(backfill(str, pct, cfg.color[clr_progress_full], cfg.color[clr_progress_empty])); + SAFEPRINTF2(str, "[ %-8s %5.1f%% ]", text, pct); + term->cond_newline(); + term->cursor_left(backfill(str, pct, cfg.color[clr_progress_full], cfg.color[clr_progress_empty])); last_progress = now; } - -struct savedline { - char buf[LINE_BUFSIZE + 1]; /* Line buffer (i.e. ANSI-encoded) */ - uint beg_attr; /* Starting attribute of each line */ - uint end_attr; /* Ending attribute of each line */ - int column; /* Current column number */ -}; - -bool sbbs_t::saveline(void) -{ - struct savedline line; -#ifdef _DEBUG - lprintf(LOG_DEBUG, "Saving %d chars, cursor at col %d: '%.*s'", lbuflen, column, lbuflen, lbuf); -#endif - line.beg_attr = latr; - line.end_attr = curatr; - line.column = column; - snprintf(line.buf, sizeof(line.buf), "%.*s", lbuflen, lbuf); - TERMINATE(line.buf); - lbuflen = 0; - return listPushNodeData(&savedlines, &line, sizeof(line)) != NULL; -} - -bool sbbs_t::restoreline(void) -{ - struct savedline* line = (struct savedline*)listPopNode(&savedlines); - if (line == NULL) - return false; -#ifdef _DEBUG - lprintf(LOG_DEBUG, "Restoring %d chars, cursor at col %d: '%s'", (int)strlen(line->buf), line->column, line->buf); -#endif - lbuflen = 0; - attr(line->beg_attr); - rputs(line->buf); - if (term_supports(PETSCII)) - column = strlen(line->buf); - curatr = line->end_attr; - carriage_return(); - cursor_right(line->column); - free(line); - insert_indicator(); - return true; -} diff --git a/src/sbbs3/data.cpp b/src/sbbs3/data.cpp index 4fe3115bd0c5ff8f64c46e007b8ce4abf7bf6a50..f2c70bd427ad63be59094560d51ca3116e6dd580 100644 --- a/src/sbbs3/data.cpp +++ b/src/sbbs3/data.cpp @@ -222,7 +222,7 @@ uint sbbs_t::gettimeleft(bool handle_out_of_time) if (!timeleft && !SYSOP && !(sys_status & SS_LCHAT)) { logline(LOG_NOTICE, nulstr, "Ran out of time"); - saveline(); + term->saveline(); if (sys_status & SS_EVENT) bprintf(text[ReducedTime], timestr(event_time)); bputs(text[TimesUp]); @@ -237,7 +237,7 @@ uint sbbs_t::gettimeleft(bool handle_out_of_time) logline("$-", str); SAFEPRINTF(str, "Minute Adjustment: %u", cfg.cdt_min_value); logline("*+", str); - restoreline(); + term->restoreline(); gettimeleft(); gettimeleft_inside = 0; return timeleft; @@ -287,7 +287,7 @@ uint sbbs_t::gettimeleft(bool handle_out_of_time) putuserflags(useron.number, USER_REST, useron.rest); if (cfg.expire_mod[0]) exec_bin(cfg.expire_mod, &main_csi); - restoreline(); + term->restoreline(); gettimeleft(); gettimeleft_inside = 0; return timeleft; diff --git a/src/sbbs3/download.cpp b/src/sbbs3/download.cpp index d8a437d31a8bf2dbb1a61b426b55503874af0aa9..39addebc002ea9877304b595e1a62cbd0908d485 100644 --- a/src/sbbs3/download.cpp +++ b/src/sbbs3/download.cpp @@ -211,7 +211,7 @@ void sbbs_t::autohangup() rioctl(IOFI); if (!autohang) return; - lncntr = 0; + term->lncntr = 0; bputs(text[Disconnecting]); attr(GREEN); outchar('['); diff --git a/src/sbbs3/exec.cpp b/src/sbbs3/exec.cpp index 2a13fe8e2259f6315660a71e4ef990981f9ccaca..ba555119b3647d50c7f8b5772c5dec5157420330 100644 --- a/src/sbbs3/exec.cpp +++ b/src/sbbs3/exec.cpp @@ -229,11 +229,11 @@ int32_t * sbbs_t::getintvar(csi_t *bin, uint32_t name) case 0x1c4455ee: return (int32_t *)&dte_rate; case 0x7fbf958e: - return (int32_t *)&lncntr; + return (int32_t *)&term->lncntr; // case 0x5c1c1500: // return((int32_t *)&tos); case 0x613b690e: - return (int32_t *)&rows; + return (int32_t *)&term->rows; case 0x205ace36: return (int32_t *)&autoterm; case 0x7d0ed0d1: @@ -1328,24 +1328,18 @@ 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); break; case CS_PRINTFILE_REMOTE: - if (online != ON_REMOTE || !(console & CON_R_ECHO)) + if (online != ON_REMOTE) break; - console &= ~CON_L_ECHO; printfile(cmdstr((char*)csi->ip, path, csi->str, (char*)buf), P_SAVEATR); - console |= CON_L_ECHO; break; case CS_PRINTFILE_LOCAL: - if (!(console & CON_L_ECHO)) - break; - console &= ~CON_R_ECHO; - printfile(cmdstr((char*)csi->ip, path, csi->str, (char*)buf), P_SAVEATR); - console |= CON_R_ECHO; + lprintf(LOG_WARNING, "PRINTFILE_LOCAL is no longer functional"); break; case CS_CHKFILE: csi->logic = !fexistcase(cmdstr((char*)csi->ip, path, csi->str, (char*)buf)); @@ -1829,7 +1823,7 @@ int sbbs_t::exec(csi_t *csi) pause(); return 0; case CS_PAUSE_RESET: - lncntr = 0; + term->lncntr = 0; return 0; case CS_GETLINES: getdimensions(); @@ -1902,10 +1896,10 @@ int sbbs_t::exec(csi_t *csi) csi->logic = LOGIC_FALSE; return 0; case CS_SAVELINE: - saveline(); + term->saveline(); return 0; case CS_RESTORELINE: - restoreline(); + term->restoreline(); return 0; case CS_SELECT_SHELL: csi->logic = select_shell() ? LOGIC_TRUE:LOGIC_FALSE; diff --git a/src/sbbs3/execfile.cpp b/src/sbbs3/execfile.cpp index 0eaa0012b1f3a74188042b2076e650952a6414ef..fb3bc26eaac10b019b3b65de05f892fb45e76d0e 100644 --- a/src/sbbs3/execfile.cpp +++ b/src/sbbs3/execfile.cpp @@ -50,7 +50,7 @@ int sbbs_t::exec_file(csi_t *csi) outchar(' '); if (i < 99) outchar(' '); - add_hotspot(i + 1); + term->add_hotspot(i + 1); bprintf(text[CfgLibLstFmt] , i + 1, cfg.lib[usrlib[i]]->lname); } @@ -58,7 +58,7 @@ int sbbs_t::exec_file(csi_t *csi) snprintf(str, sizeof str, text[JoinWhichLib], curlib + 1); mnemonics(str); j = getnum(usrlibs); - clear_hotspots(); + term->clear_hotspots(); if ((int)j == -1) return 0; if (!j) @@ -82,14 +82,14 @@ int sbbs_t::exec_file(csi_t *csi) outchar(' '); if (i < 99) outchar(' '); - add_hotspot(i + 1); + term->add_hotspot(i + 1); bputs(str); } } snprintf(str, sizeof str, text[JoinWhichDir], curdir[j] + 1); mnemonics(str); i = getnum(usrdirs[j]); - clear_hotspots(); + term->clear_hotspots(); if ((int)i == -1) { if (usrlibs == 1) return 0; @@ -210,7 +210,7 @@ int sbbs_t::exec_file(csi_t *csi) outchar(' '); if (i < 9) outchar(' '); - add_hotspot(i + 1); + term->add_hotspot(i + 1); bprintf(text[LibLstFmt], i + 1 , cfg.lib[usrlib[i]]->lname, nulstr, usrdirs[i]); } @@ -237,7 +237,7 @@ int sbbs_t::exec_file(csi_t *csi) outchar(' '); if (i < 99) outchar(' '); - add_hotspot(i + 1); + term->add_hotspot(i + 1); bputs(str); } return 0; diff --git a/src/sbbs3/execmsg.cpp b/src/sbbs3/execmsg.cpp index a0b836c578945c1a3f54fd1711cb767236b4639f..94db3e742b05f52ffffdc6e757386fb8a33883fd 100644 --- a/src/sbbs3/execmsg.cpp +++ b/src/sbbs3/execmsg.cpp @@ -47,7 +47,7 @@ int sbbs_t::exec_msg(csi_t *csi) outchar(' '); if (i < 99) outchar(' '); - add_hotspot(i + 1); + term->add_hotspot(i + 1); bprintf(text[CfgGrpLstFmt] , i + 1, cfg.grp[usrgrp[i]]->lname); } @@ -55,7 +55,7 @@ int sbbs_t::exec_msg(csi_t *csi) snprintf(str, sizeof str, text[JoinWhichGrp], curgrp + 1); mnemonics(str); j = getnum(usrgrps); - clear_hotspots(); + term->clear_hotspots(); if ((int)j == -1) return 0; if (!j) @@ -79,14 +79,14 @@ int sbbs_t::exec_msg(csi_t *csi) outchar(' '); if (i < 99) outchar(' '); - add_hotspot(i + 1); + term->add_hotspot(i + 1); bputs(str); } } snprintf(str, sizeof str, text[JoinWhichSub], cursub[j] + 1); mnemonics(str); i = getnum(usrsubs[j]); - clear_hotspots(); + term->clear_hotspots(); if ((int)i == -1) { if (usrgrps == 1) return 0; @@ -218,7 +218,7 @@ int sbbs_t::exec_msg(csi_t *csi) outchar(' '); if (i < 9) outchar(' '); - add_hotspot(i + 1); + term->add_hotspot(i + 1); bprintf(text[GrpLstFmt], i + 1 , cfg.grp[usrgrp[i]]->lname, nulstr, usrsubs[i]); } @@ -245,7 +245,7 @@ int sbbs_t::exec_msg(csi_t *csi) outchar(' '); if (i < 99) outchar(' '); - add_hotspot(i + 1); + term->add_hotspot(i + 1); bputs(str); } return 0; diff --git a/src/sbbs3/extdeps.mk b/src/sbbs3/extdeps.mk index 8d16bc80934cd7508947a968a73f7c12081e4996..ea4abb7fb18c279fc7443d0f4ec66cba551fe9eb 100644 --- a/src/sbbs3/extdeps.mk +++ b/src/sbbs3/extdeps.mk @@ -38,7 +38,6 @@ $(MTOBJODIR)/ssl$(OFILE): $(JS_LIB) $(CRYPT_LIB) $(MTOBJODIR)/websrvr$(OFILE): $(JS_LIB) $(CRYPT_LIB) # C++ -$(MTOBJODIR)/ansiterm$(OFILE): $(JS_LIB) $(CRYPT_LIB) $(MTOBJODIR)/answer$(OFILE): $(JS_LIB) $(CRYPT_LIB) $(MTOBJODIR)/atcodes$(OFILE): $(JS_LIB) $(CRYPT_LIB) $(MTOBJODIR)/bat_xfer$(OFILE): $(JS_LIB) $(CRYPT_LIB) diff --git a/src/sbbs3/file.cpp b/src/sbbs3/file.cpp index 19bc9555f4c18fa6307226b7436a16b481749133..d871ccee7ca4f3135693e771d1a4e0ad933c270a 100644 --- a/src/sbbs3/file.cpp +++ b/src/sbbs3/file.cpp @@ -103,7 +103,7 @@ void sbbs_t::showfileinfo(file_t* f, bool show_extdesc) SKIP_CRLF(p); truncsp(p); putmsg(p, P_NOATCODES | P_CPM_EOF | P_AUTO_UTF8); - newline(); + term->newline(); } if (f->size == -1) { bprintf(text[FileIsNotOnline], f->name); diff --git a/src/sbbs3/getkey.cpp b/src/sbbs3/getkey.cpp index ce7c855394c70a60f8d00a43775d284c19d67df8..e07d2931e8bfd659cc05cc5cad4f48b1cb3022c1 100644 --- a/src/sbbs3/getkey.cpp +++ b/src/sbbs3/getkey.cpp @@ -45,7 +45,7 @@ char sbbs_t::getkey(int mode) sys_status &= ~SS_ABORT; if ((sys_status & SS_USERON || action == NODE_DFLT) && !(mode & (K_GETSTR | K_NOSPIN))) mode |= (useron.misc & SPIN); - lncntr = 0; + term->lncntr = 0; getkey_last_activity = time(NULL); #if !defined SPINNING_CURSOR_OVER_HARDWARE_CURSOR if (mode & K_SPIN) @@ -57,7 +57,7 @@ char sbbs_t::getkey(int mode) #if defined SPINNING_CURSOR_OVER_HARDWARE_CURSOR bputs(" \b"); #else - backspace(); + term->backspace(); #endif } return 0; @@ -87,7 +87,7 @@ char sbbs_t::getkey(int mode) #if defined SPINNING_CURSOR_OVER_HARDWARE_CURSOR bputs(" \b"); #else - backspace(); + term->backspace(); #endif } if (mode & K_COLD && ch > ' ' && useron.misc & COLDKEYS) { @@ -97,7 +97,7 @@ char sbbs_t::getkey(int mode) outchar(ch); while ((coldkey = inkey(mode, 1000)) == 0 && online && !(sys_status & SS_ABORT)) ; - backspace(); + term->backspace(); if (coldkey == BS || coldkey == DEL) continue; if (coldkey > ' ') @@ -111,18 +111,17 @@ char sbbs_t::getkey(int mode) gettimeleft(); else if (online && now - answertime > SEC_LOGON && !(sys_status & SS_LCHAT)) { console &= ~(CON_R_ECHOX | CON_L_ECHOX); - console |= (CON_R_ECHO | CON_L_ECHO); bputs(text[TakenTooLongToLogon]); hangup(); } if (sys_status & SS_USERON && online && (timeleft / 60) < (5 - timeleft_warn) && !SYSOP && !(sys_status & SS_LCHAT)) { timeleft_warn = 5 - (timeleft / 60); - saveline(); + term->saveline(); attr(LIGHTGRAY); bprintf(text[OnlyXminutesLeft] , ((ushort)timeleft / 60) + 1, (timeleft / 60) ? "s" : nulstr); - restoreline(); + term->restoreline(); } if (!(startup->options & BBS_OPT_NO_TELNET_GA) @@ -138,7 +137,7 @@ char sbbs_t::getkey(int mode) && ((cfg.inactivity_warn && inactive >= cfg.max_getkey_inactivity * (cfg.inactivity_warn / 100.0)) || inactive >= cfg.max_getkey_inactivity)) { if ((sys_status & SS_USERON) && inactive < cfg.max_getkey_inactivity && *text[AreYouThere] != '\0') { - saveline(); + term->saveline(); bputs(text[AreYouThere]); } else @@ -148,7 +147,6 @@ char sbbs_t::getkey(int mode) } if (now - getkey_last_activity >= cfg.max_getkey_inactivity) { if (online == ON_REMOTE) { - console |= CON_R_ECHO; console &= ~CON_R_ECHOX; } bputs(text[CallBackWhenYoureThere]); @@ -158,9 +156,9 @@ char sbbs_t::getkey(int mode) } if ((sys_status & SS_USERON) && *text[AreYouThere] != '\0') { attr(LIGHTGRAY); - carriage_return(); - cleartoeol(); - restoreline(); + term->carriage_return(); + term->cleartoeol(); + term->restoreline(); } getkey_last_activity = now; } @@ -200,38 +198,37 @@ void sbbs_t::mnemonics(const char *instr) char str[256]; expand_atcodes(instr, str, sizeof str); l = 0L; - int term = term_supports(); attr(mneattr_low); while (str[l]) { if (str[l] == '~' && str[l + 1] < ' ') { - add_hotspot('\r', /* hungry: */ true); + term->add_hotspot('\r', /* hungry: */ true); l += 2; } else if (str[l] == '~') { - if (!(term & (ANSI | PETSCII))) + if (!(term->can_highlight())) outchar('('); l++; if (!ctrl_a_codes) attr(mneattr_high); - add_hotspot(str[l], /* hungry: */ true); + term->add_hotspot(str[l], /* hungry: */ true); outchar(str[l]); l++; - if (!(term & (ANSI | PETSCII))) + if (!(term->can_highlight())) outchar(')'); if (!ctrl_a_codes) attr(mneattr_low); } else if (str[l] == '`' && str[l + 1] != 0) { - if (!(term & (ANSI | PETSCII))) + if (!(term->can_highlight())) outchar('['); l++; if (!ctrl_a_codes) attr(mneattr_high); - add_hotspot(str[l], /* hungry: */ false); + term->add_hotspot(str[l], /* hungry: */ false); outchar(str[l]); l++; - if (!(term & (ANSI | PETSCII))) + if (!(term->can_highlight())) outchar(']'); if (!ctrl_a_codes) attr(mneattr_low); @@ -275,7 +272,7 @@ bool sbbs_t::yesno(const char *str, int mode) CRLF; if (!(mode & P_SAVEATR)) attr(LIGHTGRAY); - lncntr = 0; + term->lncntr = 0; return true; } if (ch == no_key()) { @@ -283,7 +280,7 @@ bool sbbs_t::yesno(const char *str, int mode) CRLF; if (!(mode & P_SAVEATR)) attr(LIGHTGRAY); - lncntr = 0; + term->lncntr = 0; return false; } } @@ -313,7 +310,7 @@ bool sbbs_t::noyes(const char *str, int mode) CRLF; if (!(mode & P_SAVEATR)) attr(LIGHTGRAY); - lncntr = 0; + term->lncntr = 0; return true; } if (ch == yes_key()) { @@ -321,7 +318,7 @@ bool sbbs_t::noyes(const char *str, int mode) CRLF; if (!(mode & P_SAVEATR)) attr(LIGHTGRAY); - lncntr = 0; + term->lncntr = 0; return false; } } @@ -353,7 +350,7 @@ int sbbs_t::getkeys(const char *keys, uint max, int mode) attr(LIGHTGRAY); CRLF; } - lncntr = 0; + term->lncntr = 0; return -1; } if (ch && !n && ((keys == NULL && !IS_DIGIT(ch)) || (strchr(str, ch)))) { /* return character if in string */ @@ -374,7 +371,7 @@ int sbbs_t::getkeys(const char *keys, uint max, int mode) } if (c == BS || c == DEL) { if (!(mode & K_NOECHO)) - backspace(); + term->backspace(); continue; } } @@ -382,7 +379,7 @@ int sbbs_t::getkeys(const char *keys, uint max, int mode) attr(LIGHTGRAY); CRLF; } - lncntr = 0; + term->lncntr = 0; } return ch; } @@ -391,14 +388,14 @@ int sbbs_t::getkeys(const char *keys, uint max, int mode) attr(LIGHTGRAY); CRLF; } - lncntr = 0; + term->lncntr = 0; if (n) return i | 0x80000000L; /* return number plus high bit */ return 0; } if ((ch == BS || ch == DEL) && n) { if (!(mode & K_NOECHO)) - backspace(); + term->backspace(); i /= 10; n--; } @@ -413,7 +410,7 @@ int sbbs_t::getkeys(const char *keys, uint max, int mode) attr(LIGHTGRAY); CRLF; } - lncntr = 0; + term->lncntr = 0; return i | 0x80000000L; } } @@ -428,38 +425,37 @@ int sbbs_t::getkeys(const char *keys, uint max, int mode) bool sbbs_t::pause(bool set_abort) { char ch; - uint tempattrs = curatr; /* was lclatr(-1) */ + uint tempattrs = term->curatr; /* was lclatr(-1) */ int l = K_UPPER; size_t len; if ((sys_status & SS_ABORT) || pause_inside) return false; pause_inside = true; - lncntr = 0; + term->lncntr = 0; if (online == ON_REMOTE) rioctl(IOFI); - if (mouse_hotspots.first == NULL) - pause_hotspot = add_hotspot('\r'); + term->pause_hotspot = term->add_pause_hotspot('\r'); bputs(text[Pause]); - len = bstrlen(text[Pause]); + len = term->bstrlen(text[Pause]); if (sys_status & SS_USERON && !(useron.misc & (NOPAUSESPIN)) && cfg.spinning_pause_prompt) l |= K_SPIN; ch = getkey(l); - if (pause_hotspot) { - clear_hotspots(); - pause_hotspot = NULL; + if (term->pause_hotspot) { + term->clear_hotspots(); + term->pause_hotspot = false; } bool aborted = (ch == no_key() || ch == quit_key() || (sys_status & SS_ABORT)); if (set_abort && aborted) sys_status |= SS_ABORT; if (text[Pause][0] != '@') - backspace(len); + term->backspace(len); getnodedat(cfg.node_num, &thisnode); nodesync(); attr(tempattrs); if (ch == TERM_KEY_DOWN) // down arrow == display one more line - lncntr = rows - 2; + term->lncntr = term->rows - 2; pause_inside = false; return !aborted; } diff --git a/src/sbbs3/getmsg.cpp b/src/sbbs3/getmsg.cpp index b2fe8c56ea09a1db1380be1e0d7aaf4df5ba764c..06930d68ae348c523293d8b48aaa33e50c8692e6 100644 --- a/src/sbbs3/getmsg.cpp +++ b/src/sbbs3/getmsg.cpp @@ -189,13 +189,13 @@ void sbbs_t::show_msghdr(smb_t* smb, const smbmsg_t* msg, const char* subject, c current_msg_to = to; attr(LIGHTGRAY); - if (row != 0) { + if (term->row != 0) { if (useron.misc & CLRSCRN) outchar(FF); else CRLF; } - msghdr_tos = (row == 0); + msghdr_tos = (term->row == 0); if (!menu("msghdr", P_NOERROR)) { bprintf(pmode, msghdr_text(msg, MsgSubj), current_msg_subj); if (msg->tags && *msg->tags) @@ -269,14 +269,14 @@ bool sbbs_t::show_msg(smb_t* smb, smbmsg_t* msg, int p_mode, post_t* post) CRLF; if (msg->hdr.type == SMB_MSG_TYPE_POLL && post != NULL && is_sub) { - char* answer; - int longest_answer = 0; + char* answer; + unsigned longest_answer = 0; for (int i = 0; i < msg->total_hfields; i++) { if (msg->hfield[i].type != SMB_POLL_ANSWER) continue; answer = (char*)msg->hfield_dat[i]; - int len = strlen(answer); + size_t len = strlen(answer); if (len > longest_answer) longest_answer = len; } @@ -287,11 +287,11 @@ bool sbbs_t::show_msg(smb_t* smb, smbmsg_t* msg, int p_mode, post_t* post) answer = (char*)msg->hfield_dat[i]; float pct = post->total_votes ? ((float)post->votes[answers] / post->total_votes) * 100.0F : 0.0F; char str[128]; - int width = longest_answer; - if (width < cols / 3) - width = cols / 3; - else if (width > cols - 20) - width = cols - 20; + unsigned width = longest_answer; + if (width < term->cols / 3) + width = term->cols / 3; + else if (width > term->cols - 20) + width = term->cols - 20; bprintf(text[PollAnswerNumber], answers + 1); bool results_visible = false; if ((msg->hdr.auxattr & POLL_RESULTS_MASK) == POLL_RESULTS_OPEN) @@ -337,7 +337,7 @@ bool sbbs_t::show_msg(smb_t* smb, smbmsg_t* msg, int p_mode, post_t* post) truncsp(p); SKIP_CRLF(p); if (smb_msg_is_utf8(msg)) { - if (!term_supports(UTF8)) + if (!(term->charset() == CHARSET_UTF8)) utf8_normalize_str(txt); p_mode |= P_UTF8; } @@ -349,7 +349,7 @@ bool sbbs_t::show_msg(smb_t* smb, smbmsg_t* msg, int p_mode, post_t* post) p_mode = P_NOATCODES; putmsg(p, p_mode, msg->columns); smb_freemsgtxt(txt); - if (column) + if (term->column) CRLF; if ((txt = smb_getmsgtxt(smb, msg, GETMSGTXT_TAIL_ONLY)) == NULL) return false; diff --git a/src/sbbs3/getnode.cpp b/src/sbbs3/getnode.cpp index cf90bc5a9518a1c0f82ab8b37a1b71a040a224c6..2e5193deb61a1d58a95d23c28a0c549f5c9b5a4c 100644 --- a/src/sbbs3/getnode.cpp +++ b/src/sbbs3/getnode.cpp @@ -175,19 +175,19 @@ void sbbs_t::nodesync(bool clearline) } if (thisnode.misc & NODE_LCHAT) { // pulled into local chat with sysop - saveline(); + term->saveline(); privchat(true); - restoreline(); + term->restoreline(); } if (thisnode.misc & NODE_FCHAT) { // forced into private chat int n = getpagingnode(&cfg); if (n) { uint save_action = action; - saveline(); + term->saveline(); privchat(true, n); action = save_action; - restoreline(); + term->restoreline(); } if (getnodedat(cfg.node_num, &thisnode, true)) { thisnode.action = action; @@ -260,8 +260,8 @@ bool sbbs_t::getnmsg(bool clearline) buf[length] = 0; if (clearline) - this->clearline(); - else if (column) + term->clearline(); + else if (term->column) CRLF; putmsg(buf, P_NOATCODES); free(buf); @@ -343,9 +343,9 @@ bool sbbs_t::getsmsg(int usernumber, bool clearline) return false; getnodedat(cfg.node_num, &thisnode); if (clearline) - this->clearline(); + term->clearline(); else - if (column) + if (term->column) CRLF; putmsg(buf, P_NOATCODES); free(buf); diff --git a/src/sbbs3/getstr.cpp b/src/sbbs3/getstr.cpp index fd0d10f7936088ad51e003be2c5ccbf9c397221d..0df5afce0c05ef3d3cb2550ebfa4a670deadde94 100644 --- a/src/sbbs3/getstr.cpp +++ b/src/sbbs3/getstr.cpp @@ -38,23 +38,21 @@ size_t sbbs_t::getstr(char *strout, size_t maxlen, int mode, const str_list_t hi uchar ch; uint atr; int hidx = -1; - int org_column = column; - int org_lbuflen = lbuflen; + int org_column = term->column; + int org_lbuflen = term->lbuflen; - int term = term_supports(); console &= ~(CON_UPARROW | CON_DOWNARROW | CON_LEFTARROW | CON_RIGHTARROW | CON_BACKSPACE | CON_DELETELINE); if (!(mode & K_WORDWRAP)) console &= ~CON_INSERT; sys_status &= ~SS_ABORT; - if (!(mode & K_LINEWRAP) && cols >= TERM_COLS_MIN && !(mode & K_NOECHO) && !(console & CON_R_ECHOX) - && column + (int)maxlen >= cols) /* Don't allow the terminal to auto line-wrap */ - maxlen = cols - column - 1; - if (mode & K_LINE && (term & (ANSI | PETSCII)) && !(mode & K_NOECHO)) { + if (!(mode & K_LINEWRAP) && term->cols >= TERM_COLS_MIN && !(mode & K_NOECHO) && !(console & CON_R_ECHOX) + && term->column + (int)maxlen >= term->cols) /* Don't allow the terminal to auto line-wrap */ + maxlen = term->cols - term->column - 1; + if (mode & K_LINE && (term->can_highlight()) && !(mode & K_NOECHO)) { attr(cfg.color[clr_inputline]); for (i = 0; i < maxlen; i++) - outcom(' '); - cursor_left(maxlen); - column = org_column; + term_out(' '); + term->cursor_left(maxlen); } if (wordwrap[0]) { SAFECOPY(str1, wordwrap); @@ -71,29 +69,29 @@ size_t sbbs_t::getstr(char *strout, size_t maxlen, int mode, const str_list_t hi atr = curatr; if (!(mode & K_NOECHO)) { if (mode & K_AUTODEL && str1[0]) { - i = (cfg.color[clr_inputline] & 0x77) << 4; - i |= (cfg.color[clr_inputline] & 0x77) >> 4; + i = (cfg.color[clr_inputline] & 0x07) << 4; + i |= (cfg.color[clr_inputline] & 0x70) >> 4; attr(i); } bputs(str1, P_AUTO_UTF8); if (mode & K_EDIT && !(mode & (K_LINE | K_AUTODEL))) - cleartoeol(); /* destroy to eol */ + term->cleartoeol(); /* destroy to eol */ } SAFECOPY(undo, str1); - i = l = bstrlen(str1, P_AUTO_UTF8); + i = l = term->bstrlen(str1, P_AUTO_UTF8); if (mode & K_AUTODEL && str1[0] && !(mode & K_NOECHO)) { ch = getkey(mode | K_GETSTR); attr(atr); if (IS_PRINTABLE(ch) || ch == DEL) { for (i = 0; i < l; i++) - backspace(); + term->backspace(); i = l = 0; } else { for (i = 0; i < l; i++) outchar(BS); - column += bputs(str1, P_AUTO_UTF8); + bputs(str1, P_AUTO_UTF8); i = l; } if (ch != ' ' && ch != TAB) @@ -105,12 +103,12 @@ size_t sbbs_t::getstr(char *strout, size_t maxlen, int mode, const str_list_t hi if (i > l) i = l; if (l - i) { - cursor_left(l - i); + term->cursor_left(l - i); } } if (console & CON_INSERT && !(mode & K_NOECHO)) - insert_indicator(); + term->insert_indicator(); while (!(sys_status & SS_ABORT) && online && input_thread_running) { if (mode & K_LEFTEXIT @@ -151,8 +149,8 @@ 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]; - column += rprintf("%.*s", (int)(l - i), str1 + i); - cursor_left(l - i); + rprintf("%.*s", (int)(l - i), str1 + i); + term->cursor_left(l - i); #if 0 if (i == maxlen - 1) console &= ~CON_INSERT; @@ -162,7 +160,7 @@ size_t sbbs_t::getstr(char *strout, size_t maxlen, int mode, const str_list_t hi break; case TERM_KEY_HOME: /* Ctrl-B Beginning of Line */ if (i && !(mode & K_NOECHO)) { - cursor_left(i); + term->cursor_left(i); i = 0; } break; @@ -177,7 +175,7 @@ size_t sbbs_t::getstr(char *strout, size_t maxlen, int mode, const str_list_t hi outchar(' '); x++; } - cursor_left(x - i); /* move cursor back */ + term->cursor_left(x - i); /* move cursor back */ z = i; while (z < l - (x - i)) { /* move chars in string */ outchar(str1[z] = str1[z + (x - i)]); @@ -187,19 +185,19 @@ size_t sbbs_t::getstr(char *strout, size_t maxlen, int mode, const str_list_t hi outchar(' '); z++; } - cursor_left(z - i); + term->cursor_left(z - i); l -= x - i; /* l=new length */ } break; case TERM_KEY_END: /* Ctrl-E End of line */ - if (term & (ANSI | PETSCII) && i < l) { - cursor_right(l - i); /* move cursor to eol */ + if (term->can_move() && i < l) { + term->cursor_right(l - i); /* move cursor to eol */ i = l; } break; case TERM_KEY_RIGHT: /* Ctrl-F move cursor forward */ - if (i < l && term & (ANSI | PETSCII)) { - cursor_right(); /* move cursor right one */ + if (i < l && term->can_move()) { + term->cursor_right(); /* move cursor right one */ i++; } break; @@ -255,7 +253,7 @@ size_t sbbs_t::getstr(char *strout, size_t maxlen, int mode, const str_list_t hi do { i--; l--; - } while ((term & UTF8) && (i > 0) && (str1[i] & 0x80) && (str1[i - 1] & 0x80)); + } while ((term->charset() == CHARSET_UTF8) && (i > 0) && (str1[i] & 0x80) && (str1[i - 1] & 0x80)); if (i != l) { /* Deleting char in middle of line */ outchar(BS); z = i; @@ -264,10 +262,10 @@ size_t sbbs_t::getstr(char *strout, size_t maxlen, int mode, const str_list_t hi z++; } outchar(' '); /* write over the last char */ - cursor_left((l - i) + 1); + term->cursor_left((l - i) + 1); } else if (!(mode & K_NOECHO)) - backspace(); + term->backspace(); break; case CTRL_I: /* Ctrl-I/TAB */ if (history != NULL) { @@ -279,10 +277,10 @@ size_t sbbs_t::getstr(char *strout, size_t maxlen, int mode, const str_list_t hi hidx = hi; SAFECOPY(str1, history[hi]); while (i--) - backspace(); + term->backspace(); i = l = strlen(str1); rputs(str1); - cleartoeol(); + term->cleartoeol(); break; } break; @@ -323,7 +321,7 @@ size_t sbbs_t::getstr(char *strout, size_t maxlen, int mode, const str_list_t hi case CTRL_L: /* Ctrl-L Center line (used to be Ctrl-V) */ str1[l] = 0; - l = bstrlen(str1); + l = term->bstrlen(str1); if (!l) break; for (x = 0; x < (maxlen - l) / 2; x++) @@ -348,13 +346,13 @@ size_t sbbs_t::getstr(char *strout, size_t maxlen, int mode, const str_list_t hi return l; case CTRL_N: /* Ctrl-N Next word */ - if (i < l && term & (ANSI | PETSCII)) { + if (i < l && term->can_move()) { x = i; while (str1[i] != ' ' && i < l) i++; while (str1[i] == ' ' && i < l) i++; - cursor_right(i - x); + term->cursor_right(i - x); } break; case CTRL_R: /* Ctrl-R Redraw Line */ @@ -365,7 +363,7 @@ size_t sbbs_t::getstr(char *strout, size_t maxlen, int mode, const str_list_t hi if (mode & K_NOECHO) break; console ^= CON_INSERT; - insert_indicator(); + term->insert_indicator(); break; case CTRL_W: /* Ctrl-W Delete word left */ if (i < l) { @@ -387,27 +385,27 @@ size_t sbbs_t::getstr(char *strout, size_t maxlen, int mode, const str_list_t hi outchar(' '); z++; } - cursor_left(z - i); /* back to new x corridnant */ + term->cursor_left(z - i); /* back to new x corridnant */ l -= x - i; /* l=new length */ } else { while (i && str1[i - 1] == ' ') { i--; l--; if (!(mode & K_NOECHO)) - backspace(); + term->backspace(); } while (i && str1[i - 1] != ' ') { i--; l--; if (!(mode & K_NOECHO)) - backspace(); + term->backspace(); } } break; case CTRL_Y: /* Ctrl-Y Delete to end of line */ if (i != l) { /* if not at EOL */ if (!(mode & K_NOECHO)) - cleartoeol(); + term->cleartoeol(); l = i; break; } @@ -416,10 +414,10 @@ size_t sbbs_t::getstr(char *strout, size_t maxlen, int mode, const str_list_t hi if (mode & K_NOECHO) l = 0; else { - cursor_left(i); - cleartoeol(); + term->cursor_left(i); + term->cleartoeol(); l = 0; - lbuflen = org_lbuflen; + term->lbuflen = org_lbuflen; } i = 0; console |= CON_DELETELINE; @@ -427,12 +425,12 @@ size_t sbbs_t::getstr(char *strout, size_t maxlen, int mode, const str_list_t hi case CTRL_Z: /* Undo */ if (!(mode & K_NOECHO)) { while (i--) - backspace(); + term->backspace(); } SAFECOPY(str1, undo); i = l = strlen(str1); rputs(str1); - cleartoeol(); + term->cleartoeol(); break; case CTRL_BACKSLASH: /* Ctrl-\ Previous word */ if (i && !(mode & K_NOECHO)) { @@ -441,7 +439,7 @@ size_t sbbs_t::getstr(char *strout, size_t maxlen, int mode, const str_list_t hi i--; while (str1[i - 1] != ' ' && i) i--; - cursor_left(x - i); + term->cursor_left(x - i); } break; case TERM_KEY_LEFT: /* Ctrl-]/Left Arrow Reverse Cursor Movement */ @@ -451,7 +449,7 @@ size_t sbbs_t::getstr(char *strout, size_t maxlen, int mode, const str_list_t hi break; } if (!(mode & K_NOECHO)) { - cursor_left(); /* move cursor left one */ + term->cursor_left(); /* move cursor left one */ i--; } break; @@ -467,10 +465,10 @@ size_t sbbs_t::getstr(char *strout, size_t maxlen, int mode, const str_list_t hi else SAFECOPY(str1, history[hidx]); while (i--) - backspace(); + term->backspace(); i = l = strlen(str1); rputs(str1); - cleartoeol(); + term->cleartoeol(); break; } break; @@ -482,11 +480,11 @@ size_t sbbs_t::getstr(char *strout, size_t maxlen, int mode, const str_list_t hi } hidx++; while (i--) - backspace(); + term->backspace(); SAFECOPY(str1, history[hidx]); i = l = strlen(str1); rputs(str1); - cleartoeol(); + term->cleartoeol(); break; } if (!(mode & K_EDIT)) @@ -513,8 +511,8 @@ size_t sbbs_t::getstr(char *strout, size_t maxlen, int mode, const str_list_t hi i--; l--; if (!(mode & K_NOECHO)) - backspace(); - } while ((term & UTF8) && (i > 0) && (str1[i] & 0x80) && (str1[i - 1] & 0x80)); + term->backspace(); + } while ((term->charset() == CHARSET_UTF8) && (i > 0) && (str1[i] & 0x80) && (str1[i - 1] & 0x80)); } break; } @@ -525,7 +523,7 @@ size_t sbbs_t::getstr(char *strout, size_t maxlen, int mode, const str_list_t hi z++; } outchar(' '); /* write over the last char */ - cursor_left((l - i) + 1); + term->cursor_left((l - i) + 1); break; default: if (mode & K_WORDWRAP && i == maxlen && ch >= ' ' && !(console & CON_INSERT)) { @@ -555,7 +553,7 @@ size_t sbbs_t::getstr(char *strout, size_t maxlen, int mode, const str_list_t hi wordwrap[z] = 0; if (!(mode & K_NOECHO)) while (z--) { - backspace(); + term->backspace(); i--; } strrev(wordwrap); @@ -582,8 +580,8 @@ 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]; - column += rprintf("%.*s", (int)(l - i), str1 + i); - cursor_left(l - i); + rprintf("%.*s", (int)(l - i), str1 + i); + term->cursor_left(l - i); #if 0 if (i == maxlen - 1) { bputs(" \b\b"); @@ -593,12 +591,12 @@ size_t sbbs_t::getstr(char *strout, size_t maxlen, int mode, const str_list_t hi } str1[i++] = ch; if (!(mode & K_NOECHO)) { - if ((term & UTF8) && (ch & 0x80)) { + if ((term->charset() == CHARSET_UTF8) && (ch & 0x80)) { if (i > l) l = i; str1[l] = 0; if (utf8_str_is_valid(str1)) - redrwstr(str1, column - org_column, l, P_UTF8); + redrwstr(str1, term->column - org_column, l, P_UTF8); } else { outchar(ch); } @@ -632,8 +630,8 @@ size_t sbbs_t::getstr(char *strout, size_t maxlen, int mode, const str_list_t hi if (!(mode & K_MSG && sys_status & SS_ABORT)) { CRLF; } else - carriage_return(); - lncntr = 0; + term->carriage_return(); + term->lncntr = 0; } return l; } @@ -657,27 +655,27 @@ int sbbs_t::getnum(uint max, uint dflt) if (useron.misc & COLDKEYS) ch = getkey(K_UPPER); if (ch == BS || ch == DEL) { - backspace(); + term->backspace(); continue; } CRLF; - lncntr = 0; + term->lncntr = 0; return -1; } else if (sys_status & SS_ABORT) { CRLF; - lncntr = 0; + term->lncntr = 0; return -1; } else if (ch == CR) { CRLF; - lncntr = 0; + term->lncntr = 0; if (!n) return dflt; return i; } else if ((ch == BS || ch == DEL) && n) { - backspace(); + term->backspace(); i /= 10; n--; } @@ -688,33 +686,10 @@ int sbbs_t::getnum(uint max, uint dflt) outchar(ch); if (i * 10UL > max && !(useron.misc & COLDKEYS) && keybuf_level() < 1) { CRLF; - lncntr = 0; + term->lncntr = 0; return i; } } } return 0; } - -void sbbs_t::insert_indicator(void) -{ - if (term_supports(ANSI)) { - char str[32]; - int col = column; - auto row = this->row; - ansi_save(); - ansi_gotoxy(cols, 1); - int tmpatr; - if (console & CON_INSERT) { - putcom(ansi_attr(tmpatr = BLINK | BLACK | (LIGHTGRAY << 4), curatr, str, term_supports(COLOR))); - outcom('I'); - } else { - putcom(ansi(tmpatr = ANSI_NORMAL)); - outcom(' '); - } - putcom(ansi_attr(curatr, tmpatr, str, term_supports(COLOR))); - ansi_restore(); - column = col; - this->row = row; - } -} diff --git a/src/sbbs3/inkey.cpp b/src/sbbs3/inkey.cpp index 8fea4699f56ca4f977b423a313579e38ec34552c..807fc657fe8582e0694189d72194b2a0f44d182b 100644 --- a/src/sbbs3/inkey.cpp +++ b/src/sbbs3/inkey.cpp @@ -47,8 +47,7 @@ int sbbs_t::kbincom(unsigned int timeout) int sbbs_t::translate_input(int ch) { - int term = term_supports(); - if (term & PETSCII) { + if (term->charset() == CHARSET_PETSCII) { switch (ch) { case PETSCII_HOME: return TERM_KEY_HOME; @@ -72,14 +71,21 @@ int sbbs_t::translate_input(int ch) if (IS_ALPHA(ch)) ch ^= 0x20; /* Swap upper/lower case */ } - else if (term & SWAP_DELETE) { - switch (ch) { - case TERM_KEY_DELETE: - ch = '\b'; - break; - case '\b': - ch = TERM_KEY_DELETE; - break; + else { + bool lwe = last_inkey_was_esc; + if (ch == ESC) + last_inkey_was_esc = true; + if (lwe && ch == '[') + autoterm |= ANSI; // A CSI means ANSI. + if (term->supports(SWAP_DELETE)) { + switch (ch) { + case TERM_KEY_DELETE: + ch = '\b'; + break; + case '\b': + ch = TERM_KEY_DELETE; + break; + } } } @@ -109,7 +115,7 @@ int sbbs_t::inkey(int mode, unsigned int timeout) if (ch == NOINP) return no_input; - if (term_supports(NO_EXASCII)) + if (term->charset() == CHARSET_ASCII) ch &= 0x7f; // e.g. strip parity bit getkey_last_activity = time(NULL); @@ -123,7 +129,7 @@ int sbbs_t::inkey(int mode, unsigned int timeout) /* Translate (not control character) input into CP437 */ if (!(mode & K_UTF8)) { - if ((ch & 0x80) && term_supports(UTF8)) { + if ((ch & 0x80) && (term->charset() == CHARSET_UTF8)) { char utf8[UTF8_MAX_LEN] = { (char)ch }; size_t len = utf8_decode_firstbyte(ch); if (len < 2 || len > sizeof(utf8)) @@ -154,16 +160,16 @@ int sbbs_t::inkey(int mode, unsigned int timeout) char sbbs_t::handle_ctrlkey(char ch, int mode) { - char str[512]; char tmp[512]; - int i, j; + int i; if (ch == TERM_KEY_ABORT) { /* Ctrl-C Abort */ sys_status |= SS_ABORT; if (mode & K_SPIN) /* back space once if on spinning cursor */ - backspace(); + term->backspace(); return 0; } + if (ch == CTRL_Z && !(mode & (K_MSG | K_GETSTR)) && action != NODE_PCHT) { /* Ctrl-Z toggle raw input mode */ if (hotkey_inside & (1 << ch)) @@ -171,7 +177,7 @@ char sbbs_t::handle_ctrlkey(char ch, int mode) hotkey_inside |= (1 << ch); if (mode & K_SPIN) bputs("\b "); - saveline(); + term->saveline(); attr(LIGHTGRAY); CRLF; bputs(text[RawMsgInputModeIsNow]); @@ -182,8 +188,8 @@ char sbbs_t::handle_ctrlkey(char ch, int mode) console ^= CON_RAW_IN; CRLF; CRLF; - restoreline(); - lncntr = 0; + term->restoreline(); + term->lncntr = 0; hotkey_inside &= ~(1 << ch); if (action != NODE_MAIN && action != NODE_XFER) return CTRL_Z; @@ -193,11 +199,6 @@ char sbbs_t::handle_ctrlkey(char ch, int mode) if (console & CON_RAW_IN) /* ignore ctrl-key commands if in raw mode */ return ch; -#if 0 /* experimental removal to fix Tracker1's pause module problem with down-arrow */ - if (ch == LF) /* ignore LF's if not in raw mode */ - return 0; -#endif - /* Global hot key event */ if (sys_status & SS_USERON) { for (i = 0; i < cfg.total_hotkeys; i++) @@ -210,7 +211,7 @@ char sbbs_t::handle_ctrlkey(char ch, int mode) if (mode & K_SPIN) bputs("\b "); if (!(sys_status & SS_SPLITP)) { - saveline(); + term->saveline(); attr(LIGHTGRAY); CRLF; } @@ -224,14 +225,17 @@ char sbbs_t::handle_ctrlkey(char ch, int mode) external(cmdstr(cfg.hotkey[i]->cmd, nulstr, nulstr, tmp), 0); if (!(sys_status & SS_SPLITP)) { CRLF; - restoreline(); + term->restoreline(); } - lncntr = 0; + term->lncntr = 0; hotkey_inside &= ~(1 << ch); return 0; } } + if (term->parse_input_sequence(ch, mode)) + return ch; + switch (ch) { case CTRL_O: /* Ctrl-O toggles pause temporarily */ console ^= CON_PAUSEOFF; @@ -245,7 +249,7 @@ char sbbs_t::handle_ctrlkey(char ch, int mode) if (mode & K_SPIN) bputs("\b "); if (!(sys_status & SS_SPLITP)) { - saveline(); + term->saveline(); attr(LIGHTGRAY); CRLF; } @@ -254,9 +258,9 @@ char sbbs_t::handle_ctrlkey(char ch, int mode) sync(); if (!(sys_status & SS_SPLITP)) { CRLF; - restoreline(); + term->restoreline(); } - lncntr = 0; + term->lncntr = 0; hotkey_inside &= ~(1 << ch); return 0; @@ -269,7 +273,7 @@ char sbbs_t::handle_ctrlkey(char ch, int mode) if (mode & K_SPIN) bputs("\b "); if (!(sys_status & SS_SPLITP)) { - saveline(); + term->saveline(); attr(LIGHTGRAY); CRLF; } @@ -277,9 +281,9 @@ char sbbs_t::handle_ctrlkey(char ch, int mode) sync(); if (!(sys_status & SS_SPLITP)) { CRLF; - restoreline(); + term->restoreline(); } - lncntr = 0; + term->lncntr = 0; hotkey_inside &= ~(1 << ch); return 0; case CTRL_T: /* Ctrl-T Time information */ @@ -292,7 +296,7 @@ char sbbs_t::handle_ctrlkey(char ch, int mode) hotkey_inside |= (1 << ch); if (mode & K_SPIN) bputs("\b "); - saveline(); + term->saveline(); attr(LIGHTGRAY); now = time(NULL); bprintf(text[TiLogon], timestr(logontime)); @@ -304,8 +308,8 @@ char sbbs_t::handle_ctrlkey(char ch, int mode) if (sys_status & SS_EVENT) bprintf(text[ReducedTime], timestr(event_time)); sync(); - restoreline(); - lncntr = 0; + term->restoreline(); + term->lncntr = 0; hotkey_inside &= ~(1 << ch); return 0; case CTRL_K: /* Ctrl-K Control key menu */ @@ -318,405 +322,18 @@ char sbbs_t::handle_ctrlkey(char ch, int mode) hotkey_inside |= (1 << ch); if (mode & K_SPIN) bputs("\b "); - saveline(); + term->saveline(); attr(LIGHTGRAY); - lncntr = 0; + term->lncntr = 0; if (mode & K_GETSTR) bputs(text[GetStrMenu]); else bputs(text[ControlKeyMenu]); sync(); - restoreline(); - lncntr = 0; + term->restoreline(); + term->lncntr = 0; hotkey_inside &= ~(1 << ch); return 0; - case ESC: - i = kbincom((mode & K_GETSTR) ? 3000:1000); - if (i == NOINP) // timed-out waiting for '[' - return ESC; - ch = i; - if (ch != '[') { - ungetkey(ch, /* insert: */ true); - return ESC; - } - i = j = 0; - autoterm |= ANSI; /* <ESC>[x means they have ANSI */ -#if 0 // this seems like a "bad idea" {tm} - if (sys_status & SS_USERON && useron.misc & AUTOTERM && !(useron.misc & ANSI) - && useron.number) { - useron.misc |= ANSI; - putuserrec(&cfg, useron.number, U_MISC, 8, ultoa(useron.misc, str, 16)); - } -#endif - while (i < 10 && j < 30) { /* up to 3 seconds */ - ch = kbincom(100); - if (ch == (NOINP & 0xff)) { - j++; - continue; - } - if (i == 0 && ch == 'M' && mouse_mode != MOUSE_MODE_OFF) { - str[i++] = ch; - int button = kbincom(100); - if (button == NOINP) { - lprintf(LOG_DEBUG, "Timeout waiting for mouse button value"); - continue; - } - str[i++] = button; - ch = kbincom(100); - if (ch < '!') { - lprintf(LOG_DEBUG, "Unexpected mouse-button (0x%02X) tracking char: 0x%02X < '!'" - , button, ch); - continue; - } - str[i++] = ch; - int x = ch - '!'; - ch = kbincom(100); - if (ch < '!') { - lprintf(LOG_DEBUG, "Unexpected mouse-button (0x%02X) tracking char: 0x%02X < '!'" - , button, ch); - continue; - } - str[i++] = ch; - int y = ch - '!'; - lprintf(LOG_DEBUG, "X10 Mouse button-click (0x%02X) reported at: %u x %u", button, x, y); - if (button == 0x20) { // Left-click - list_node_t* node; - for (node = mouse_hotspots.first; node != NULL; node = node->next) { - struct mouse_hotspot* spot = (struct mouse_hotspot*)node->data; - if (spot->y == y && x >= spot->minx && x <= spot->maxx) - break; - } - if (node == NULL) { - for (node = mouse_hotspots.first; node != NULL; node = node->next) { - struct mouse_hotspot* spot = (struct mouse_hotspot*)node->data; - if (spot->hungry && spot->y == y && x >= spot->minx) - break; - } - } - if (node == NULL) { - for (node = mouse_hotspots.last; node != NULL; node = node->prev) { - struct mouse_hotspot* spot = (struct mouse_hotspot*)node->data; - if (spot->hungry && spot->y == y && x <= spot->minx) - break; - } - } - if (node != NULL) { - struct mouse_hotspot* spot = (struct mouse_hotspot*)node->data; - #ifdef _DEBUG - { - char dbg[128]; - c_escape_str(spot->cmd, dbg, sizeof(dbg), /* Ctrl-only? */ true); - lprintf(LOG_DEBUG, "Stuffing hot spot command into keybuf: '%s'", dbg); - } - #endif - ungetkeys(spot->cmd); - if (pause_inside && pause_hotspot == NULL) - return handle_ctrlkey(TERM_KEY_ABORT, mode); - return 0; - } - if (pause_inside && y == rows - 1) - return '\r'; - } else if (button == '`' && console & CON_MOUSE_SCROLL) { - return TERM_KEY_UP; - } else if (button == 'a' && console & CON_MOUSE_SCROLL) { - return TERM_KEY_DOWN; - } - if ((button != 0x23 && console & CON_MOUSE_CLK_PASSTHRU) - || (button == 0x23 && console & CON_MOUSE_REL_PASSTHRU)) { - for (j = i; j > 0; j--) - ungetkey(str[j - 1], /* insert: */ true); - ungetkey('[', /* insert: */ true); - return ESC; - } - if (button == 0x22) // Right-click - return handle_ctrlkey(TERM_KEY_ABORT, mode); - return 0; - } - if (i == 0 && ch == '<' && mouse_mode != MOUSE_MODE_OFF) { - while (i < (int)sizeof(str) - 1) { - int byte = kbincom(100); - if (byte == NOINP) { - lprintf(LOG_DEBUG, "Timeout waiting for mouse report character (%d)", i); - return 0; - } - str[i++] = byte; - if (IS_ALPHA(byte)) - break; - } - str[i] = 0; - int button = -1, x = 0, y = 0; - if (sscanf(str, "%d;%d;%d%c", &button, &x, &y, &ch) != 4 - || button < 0 || x < 1 || y < 1 || toupper(ch) != 'M') { - lprintf(LOG_DEBUG, "Invalid SGR mouse report sequence: '%s'", str); - return 0; - } - --x; - --y; - lprintf(LOG_DEBUG, "SGR Mouse button (0x%02X) %s reported at: %u x %u" - , button, ch == 'M' ? "PRESS" : "RELEASE", x, y); - if (button == 0 && ch == 'm') { // Left-button release - list_node_t* node; - for (node = mouse_hotspots.first; node != NULL; node = node->next) { - struct mouse_hotspot* spot = (struct mouse_hotspot*)node->data; - if (spot->y == y && x >= spot->minx && x <= spot->maxx) - break; - } - if (node == NULL) { - for (node = mouse_hotspots.first; node != NULL; node = node->next) { - struct mouse_hotspot* spot = (struct mouse_hotspot*)node->data; - if (spot->hungry && spot->y == y && x >= spot->minx) - break; - } - } - if (node == NULL) { - for (node = mouse_hotspots.last; node != NULL; node = node->prev) { - struct mouse_hotspot* spot = (struct mouse_hotspot*)node->data; - if (spot->hungry && spot->y == y && x <= spot->minx) - break; - } - } - if (node != NULL) { - struct mouse_hotspot* spot = (struct mouse_hotspot*)node->data; - #ifdef _DEBUG - { - char dbg[128]; - c_escape_str(spot->cmd, dbg, sizeof(dbg), /* Ctrl-only? */ true); - lprintf(LOG_DEBUG, "Stuffing hot spot command into keybuf: '%s'", dbg); - } - #endif - ungetkeys(spot->cmd); - if (pause_inside && pause_hotspot == NULL) - return handle_ctrlkey(TERM_KEY_ABORT, mode); - return 0; - } - if (pause_inside && y == rows - 1) - return '\r'; - } else if (button == 0x40 && console & CON_MOUSE_SCROLL) { - return TERM_KEY_UP; - } else if (button == 0x41 && console & CON_MOUSE_SCROLL) { - return TERM_KEY_DOWN; - } - if ((ch == 'M' && console & CON_MOUSE_CLK_PASSTHRU) - || (ch == 'm' && console & CON_MOUSE_REL_PASSTHRU)) { - lprintf(LOG_DEBUG, "Passing-through SGR mouse report: 'ESC[<%s'", str); - for (j = i; j > 0; j--) - ungetkey(str[j - 1], /* insert: */ true); - ungetkey('<', /* insert: */ true); - ungetkey('[', /* insert: */ true); - return ESC; - } - if (ch == 'M' && button == 2) // Right-click - return handle_ctrlkey(TERM_KEY_ABORT, mode); - #ifdef _DEBUG - lprintf(LOG_DEBUG, "Eating SGR mouse report: 'ESC[<%s'", str); - #endif - return 0; - } - if (ch != ';' && !IS_DIGIT(ch) && ch != 'R') { /* other ANSI */ - str[i] = 0; - switch (ch) { - case 'A': - return TERM_KEY_UP; - case 'B': - return TERM_KEY_DOWN; - case 'C': - return TERM_KEY_RIGHT; - case 'D': - return TERM_KEY_LEFT; - case 'H': /* ANSI: home cursor */ - return TERM_KEY_HOME; - case 'V': - return TERM_KEY_PAGEUP; - case 'U': - return TERM_KEY_PAGEDN; - case 'F': /* Xterm: cursor preceding line */ - case 'K': /* ANSI: clear-to-end-of-line */ - return TERM_KEY_END; - case '@': /* ANSI/ECMA-048 INSERT */ - return TERM_KEY_INSERT; - case '~': /* VT-220 (XP telnet.exe) */ - switch (atoi(str)) { - case 1: - return TERM_KEY_HOME; - case 2: - return TERM_KEY_INSERT; - case 3: - return TERM_KEY_DELETE; - case 4: - return TERM_KEY_END; - case 5: - return TERM_KEY_PAGEUP; - case 6: - return TERM_KEY_PAGEDN; - } - break; - } - ungetkey(ch, /* insert: */ true); - for (j = i; j > 0; j--) - ungetkey(str[j - 1], /* insert: */ true); - ungetkey('[', /* insert: */ true); - return ESC; - } - if (ch == 'R') { /* cursor position report */ - if (mode & K_ANSI_CPR && i) { /* auto-detect rows */ - int x, y; - str[i] = 0; - if (sscanf(str, "%u;%u", &y, &x) == 2) { - lprintf(LOG_DEBUG, "received ANSI cursor position report: %ux%u" - , x, y); - /* Sanity check the coordinates in the response: */ - if (useron.cols == TERM_COLS_AUTO && x >= TERM_COLS_MIN && x <= TERM_COLS_MAX) - cols = x; - if (useron.rows == TERM_ROWS_AUTO && y >= TERM_ROWS_MIN && y <= TERM_ROWS_MAX) - rows = y; - if (useron.cols == TERM_COLS_AUTO || useron.rows == TERM_ROWS_AUTO) - update_nodeterm(); - } - } - return 0; - } - str[i++] = ch; - } - - for (j = i; j > 0; j--) - ungetkey(str[j - 1], /* insert: */ true); - ungetkey('[', /* insert: */ true); - return ESC; } return ch; } - -void sbbs_t::set_mouse(int flags) -{ - int term = term_supports(); - if ((term & ANSI) && ((term & MOUSE) || flags == MOUSE_MODE_OFF)) { - int mode = mouse_mode & ~flags; - if (mode & MOUSE_MODE_X10) - ansi_mouse(ANSI_MOUSE_X10, false); - if (mode & MOUSE_MODE_NORM) - ansi_mouse(ANSI_MOUSE_NORM, false); - if (mode & MOUSE_MODE_BTN) - ansi_mouse(ANSI_MOUSE_BTN, false); - if (mode & MOUSE_MODE_ANY) - ansi_mouse(ANSI_MOUSE_ANY, false); - if (mode & MOUSE_MODE_EXT) - ansi_mouse(ANSI_MOUSE_EXT, false); - - mode = flags & ~mouse_mode; - if (mode & MOUSE_MODE_X10) - ansi_mouse(ANSI_MOUSE_X10, true); - if (mode & MOUSE_MODE_NORM) - ansi_mouse(ANSI_MOUSE_NORM, true); - if (mode & MOUSE_MODE_BTN) - ansi_mouse(ANSI_MOUSE_BTN, true); - if (mode & MOUSE_MODE_ANY) - ansi_mouse(ANSI_MOUSE_ANY, true); - if (mode & MOUSE_MODE_EXT) - ansi_mouse(ANSI_MOUSE_EXT, true); - - if (mouse_mode != flags) { -#if 0 - lprintf(LOG_DEBUG, "New mouse mode: %X (was: %X)", flags, mouse_mode); -#endif - mouse_mode = flags; - } - } -} - -struct mouse_hotspot* sbbs_t::add_hotspot(struct mouse_hotspot* spot) -{ - if (!(cfg.sys_misc & SM_MOUSE_HOT) || !term_supports(MOUSE)) - return NULL; - if (spot->y < 0) - spot->y = row; - if (spot->minx < 0) - spot->minx = column; - if (spot->maxx < 0) - spot->maxx = cols - 1; -#if 0 //def _DEBUG - char dbg[128]; - lprintf(LOG_DEBUG, "Adding mouse hot spot %ld-%ld x %ld = '%s'" - , spot->minx, spot->maxx, spot->y, c_escape_str(spot->cmd, dbg, sizeof(dbg), /* Ctrl-only? */ true)); -#endif - list_node_t* node = listInsertNodeData(&mouse_hotspots, spot, sizeof(*spot)); - if (node == NULL) - return NULL; - set_mouse(MOUSE_MODE_ON); - return (struct mouse_hotspot*)node->data; -} - -void sbbs_t::clear_hotspots(void) -{ - int spots = listCountNodes(&mouse_hotspots); - if (spots) { -#if 0 //def _DEBUG - lprintf(LOG_DEBUG, "Clearing %ld mouse hot spots", spots); -#endif - listFreeNodes(&mouse_hotspots); - if (!(console & CON_MOUSE_SCROLL)) - set_mouse(MOUSE_MODE_OFF); - } -} - -void sbbs_t::scroll_hotspots(int count) -{ - int spots = 0; - int remain = 0; - for (list_node_t* node = mouse_hotspots.first; node != NULL; node = node->next) { - struct mouse_hotspot* spot = (struct mouse_hotspot*)node->data; - spot->y -= count; - spots++; - if (spot->y >= 0) - remain++; - } -#ifdef _DEBUG - if (spots) - lprintf(LOG_DEBUG, "Scrolled %d mouse hot-spots %d rows (%d remain)", spots, count, remain); -#endif - if (remain < 1) - clear_hotspots(); -} - -struct mouse_hotspot* sbbs_t::add_hotspot(char cmd, bool hungry, int minx, int maxx, int y) -{ - struct mouse_hotspot spot = {}; - spot.cmd[0] = cmd; - spot.minx = minx < 0 ? column : minx; - spot.maxx = maxx < 0 ? column : maxx; - spot.y = y; - spot.hungry = hungry; - return add_hotspot(&spot); -} - -struct mouse_hotspot* sbbs_t::add_hotspot(int num, bool hungry, int minx, int maxx, int y) -{ - struct mouse_hotspot spot = {}; - SAFEPRINTF(spot.cmd, "%d\r", num); - spot.minx = minx; - spot.maxx = maxx; - spot.y = y; - spot.hungry = hungry; - return add_hotspot(&spot); -} - -struct mouse_hotspot* sbbs_t::add_hotspot(uint num, bool hungry, int minx, int maxx, int y) -{ - struct mouse_hotspot spot = {}; - SAFEPRINTF(spot.cmd, "%u\r", num); - spot.minx = minx; - spot.maxx = maxx; - spot.y = y; - spot.hungry = hungry; - return add_hotspot(&spot); -} - -struct mouse_hotspot* sbbs_t::add_hotspot(const char* cmd, bool hungry, int minx, int maxx, int y) -{ - struct mouse_hotspot spot = {}; - SAFECOPY(spot.cmd, cmd); - spot.minx = minx; - spot.maxx = maxx; - spot.y = y; - spot.hungry = hungry; - return add_hotspot(&spot); -} diff --git a/src/sbbs3/js_console.cpp b/src/sbbs3/js_console.cpp index e54b5e37caf612f909a4c4930a8e3f77fba861cb..ff2f4d684925640fede98fff2cc37e6381dad28c 100644 --- a/src/sbbs3/js_console.cpp +++ b/src/sbbs3/js_console.cpp @@ -99,13 +99,13 @@ static JSBool js_console_get(JSContext *cx, JSObject *obj, jsid id, jsval *vp) val = sbbs->mouse_mode; break; case CON_PROP_LNCNTR: - val = sbbs->lncntr; + val = sbbs->term->lncntr; break; case CON_PROP_COLUMN: - val = sbbs->column; + val = sbbs->term->column; break; case CON_PROP_LASTLINELEN: - val = sbbs->lastlinelen; + val = sbbs->term->lastcrcol; break; case CON_PROP_LINE_DELAY: val = sbbs->line_delay; @@ -114,19 +114,19 @@ static JSBool js_console_get(JSContext *cx, JSObject *obj, jsid id, jsval *vp) val = sbbs->curatr; break; case CON_PROP_TOS: - val = sbbs->row == 0; + val = sbbs->term->row == 0; break; case CON_PROP_ROW: - val = sbbs->row; + val = sbbs->term->row; break; case CON_PROP_ROWS: - val = sbbs->rows; + val = sbbs->term->rows; break; case CON_PROP_COLUMNS: - val = sbbs->cols; + val = sbbs->term->cols; break; case CON_PROP_TABSTOP: - val = sbbs->tabstop; + val = sbbs->term->tabstop; break; case CON_PROP_AUTOTERM: val = sbbs->autoterm; @@ -140,14 +140,14 @@ static JSBool js_console_get(JSContext *cx, JSObject *obj, jsid id, jsval *vp) return JS_FALSE; break; case CON_PROP_CHARSET: - if ((js_str = JS_NewStringCopyZ(cx, sbbs->term_charset())) == NULL) + if ((js_str = JS_NewStringCopyZ(cx, sbbs->term->charset_str())) == NULL) return JS_FALSE; break; case CON_PROP_UNICODE_ZEROWIDTH: val = sbbs->unicode_zerowidth; break; case CON_PROP_CTERM_VERSION: - val = sbbs->cterm_version; + val = sbbs->term->cterm_version; break; case CON_PROP_MAX_GETKEY_INACTIVITY: val = sbbs->cfg.max_getkey_inactivity; @@ -200,7 +200,7 @@ static JSBool js_console_get(JSContext *cx, JSObject *obj, jsid id, jsval *vp) val = RingBufFree(&sbbs->outbuf); break; case CON_PROP_OUTPUT_RATE: - val = sbbs->cur_output_rate; + val = sbbs->term->cur_output_rate; break; case CON_PROP_KEYBUF_LEVEL: val = sbbs->keybuf_level(); @@ -278,16 +278,16 @@ static JSBool js_console_set(JSContext *cx, JSObject *obj, jsid id, JSBool stric case CON_PROP_MOUSE_MODE: if (*vp == JSVAL_TRUE) val = MOUSE_MODE_ON; - sbbs->set_mouse(val); + sbbs->term->set_mouse(val); break; case CON_PROP_LNCNTR: - sbbs->lncntr = val; + sbbs->term->lncntr = val; break; case CON_PROP_COLUMN: - sbbs->column = val; + sbbs->term->column = val; break; case CON_PROP_LASTLINELEN: - sbbs->lastlinelen = val; + sbbs->term->lastcrcol = val; break; case CON_PROP_LINE_DELAY: sbbs->line_delay = val; @@ -306,18 +306,18 @@ static JSBool js_console_set(JSContext *cx, JSObject *obj, jsid id, JSBool stric break; case CON_PROP_ROW: if (val >= 0 && val < TERM_ROWS_MAX) - sbbs->row = val; + sbbs->term->row = val; break; case CON_PROP_ROWS: if (val >= TERM_ROWS_MIN && val <= TERM_ROWS_MAX) - sbbs->rows = val; + sbbs->term->rows = val; break; case CON_PROP_COLUMNS: if (val >= TERM_COLS_MIN && val <= TERM_COLS_MAX) - sbbs->cols = val; + sbbs->term->cols = val; break; case CON_PROP_TABSTOP: - sbbs->tabstop = val; + sbbs->term->tabstop = val; break; case CON_PROP_AUTOTERM: sbbs->autoterm = val; @@ -333,7 +333,7 @@ static JSBool js_console_set(JSContext *cx, JSObject *obj, jsid id, JSBool stric free(sval); break; case CON_PROP_CTERM_VERSION: - sbbs->cterm_version = val; + sbbs->term->cterm_version = val; break; case CON_PROP_MAX_GETKEY_INACTIVITY: sbbs->cfg.max_getkey_inactivity = (uint16_t)val; @@ -385,7 +385,7 @@ static JSBool js_console_set(JSContext *cx, JSObject *obj, jsid id, JSBool stric sbbs->cfg.ctrlkey_passthru = val; break; case CON_PROP_OUTPUT_RATE: - sbbs->set_output_rate((enum sbbs_t::output_rate)val); + sbbs->term->set_output_rate((enum output_rate)val); break; default: @@ -423,7 +423,7 @@ static jsSyncPropertySpec js_console_properties[] = { { "getkey_inactivity_warning", CON_PROP_GETKEY_INACTIVITY_WARN, JSPROP_ENUMERATE | JSPROP_READONLY, 32002}, { "inactivity_warning", CON_PROP_GETKEY_INACTIVITY_WARN, 0, 32002}, { "last_getkey_activity", CON_PROP_LAST_GETKEY_ACTIVITY, CON_PROP_FLAGS, 320}, - { "timeout", CON_PROP_LAST_GETKEY_ACTIVITY, 0, 310}, // alias + { "timeout", CON_PROP_LAST_GETKEY_ACTIVITY, 0, 310}, // alias { "max_socket_inactivity", CON_PROP_MAX_SOCKET_INACTIVITY, CON_PROP_FLAGS, 320}, { "timeleft_warning", CON_PROP_TIMELEFT_WARN, CON_PROP_FLAGS, 310}, { "aborted", CON_PROP_ABORTED, CON_PROP_FLAGS, 310}, @@ -458,7 +458,8 @@ static const char* con_prop_desc[] = { , "Current 0-based line counter (used for automatic screen pause)" , "Current 0-based row counter" , "Current 0-based column counter (used to auto-increment <i>line_counter</i> when screen wraps)" - , "Length of last line sent to terminal (before a carriage-return or line-wrap)" + , "Column the cursor was on when last CR was sent to terminal or the line wrapped" + , "Obsolete alias for last_cr_column" , "Duration of delay (in milliseconds) before each line-feed character is sent to the terminal" , "Current display attributes (set with number or string value)" , "<tt>true</tt> if the terminal cursor is already at the top of the screen - <small>READ ONLY</small>" @@ -679,7 +680,7 @@ js_add_hotspot(JSContext *cx, uintN argc, jsval *arglist) JSSTRING_TO_MSTRING(cx, js_str, p, NULL); if (p == NULL) return JS_FALSE; - sbbs->add_hotspot(p, hungry, min_x, max_x, y); + sbbs->term->add_hotspot(p, hungry, min_x, max_x, y); free(p); return JS_TRUE; } @@ -697,7 +698,7 @@ static JSBool js_scroll_hotspots(JSContext *cx, uintN argc, jsval *arglist) int32 rows = 1; if (argc > 0 && !JS_ValueToInt32(cx, argv[0], &rows)) return JS_FALSE; - sbbs->scroll_hotspots(rows); + sbbs->term->scroll_hotspots(rows); return JS_TRUE; } @@ -710,7 +711,7 @@ static JSBool js_clear_hotspots(JSContext *cx, uintN argc, jsval *arglist) if ((sbbs = (sbbs_t*)js_GetClassPrivate(cx, JS_THIS_OBJECT(cx, arglist), &js_console_class)) == NULL) return JS_FALSE; - sbbs->clear_hotspots(); + sbbs->term->clear_hotspots(); return JS_TRUE; } @@ -1185,7 +1186,7 @@ js_clear(JSContext *cx, uintN argc, jsval *arglist) if (autopause) sbbs->CLS; else - sbbs->clearscreen(sbbs->term_supports()); + sbbs->term->clearscreen(); JS_RESUMEREQUEST(cx, rc); return JS_TRUE; } @@ -1208,7 +1209,7 @@ js_clearline(JSContext *cx, uintN argc, jsval *arglist) } rc = JS_SUSPENDREQUEST(cx); - sbbs->clearline(); + sbbs->term->clearline(); JS_RESUMEREQUEST(cx, rc); return JS_TRUE; } @@ -1231,7 +1232,7 @@ js_cleartoeol(JSContext *cx, uintN argc, jsval *arglist) } rc = JS_SUSPENDREQUEST(cx); - sbbs->cleartoeol(); + sbbs->term->cleartoeol(); JS_RESUMEREQUEST(cx, rc); return JS_TRUE; } @@ -1254,7 +1255,7 @@ js_cleartoeos(JSContext *cx, uintN argc, jsval *arglist) } rc = JS_SUSPENDREQUEST(cx); - sbbs->cleartoeos(); + sbbs->term->cleartoeos(); JS_RESUMEREQUEST(cx, rc); return JS_TRUE; } @@ -1278,7 +1279,7 @@ js_newline(JSContext *cx, uintN argc, jsval *arglist) } rc = JS_SUSPENDREQUEST(cx); - sbbs->newline(count); + sbbs->term->newline(count); JS_RESUMEREQUEST(cx, rc); return JS_TRUE; } @@ -1295,7 +1296,7 @@ js_cond_newline(JSContext *cx, uintN argc, jsval *arglist) JS_SET_RVAL(cx, arglist, JSVAL_VOID); rc = JS_SUSPENDREQUEST(cx); - sbbs->cond_newline(); + sbbs->term->cond_newline(); JS_RESUMEREQUEST(cx, rc); return JS_TRUE; } @@ -1312,7 +1313,7 @@ js_cond_blankline(JSContext *cx, uintN argc, jsval *arglist) JS_SET_RVAL(cx, arglist, JSVAL_VOID); rc = JS_SUSPENDREQUEST(cx); - sbbs->cond_blankline(); + sbbs->term->cond_blankline(); JS_RESUMEREQUEST(cx, rc); return JS_TRUE; } @@ -1329,7 +1330,7 @@ js_cond_contline(JSContext *cx, uintN argc, jsval *arglist) JS_SET_RVAL(cx, arglist, JSVAL_VOID); rc = JS_SUSPENDREQUEST(cx); - sbbs->cond_contline(); + sbbs->term->cond_contline(); JS_RESUMEREQUEST(cx, rc); return JS_TRUE; } @@ -1451,7 +1452,7 @@ js_strlen(JSContext *cx, uintN argc, jsval *arglist) if (cstr == NULL) return JS_FALSE; rc = JS_SUSPENDREQUEST(cx); - JS_SET_RVAL(cx, arglist, INT_TO_JSVAL(sbbs->bstrlen(cstr, pmode))); + JS_SET_RVAL(cx, arglist, INT_TO_JSVAL(sbbs->term->bstrlen(cstr, pmode))); free(cstr); JS_RESUMEREQUEST(cx, rc); return JS_TRUE; @@ -1841,7 +1842,7 @@ js_center(JSContext *cx, uintN argc, jsval *arglist) if (cstr == NULL) return JS_FALSE; rc = JS_SUSPENDREQUEST(cx); - sbbs->center(cstr, cols); + sbbs->term->center(cstr, cols); free(cstr); JS_RESUMEREQUEST(cx, rc); return JS_TRUE; @@ -1883,7 +1884,7 @@ js_saveline(JSContext *cx, uintN argc, jsval *arglist) if ((sbbs = (sbbs_t*)js_GetClassPrivate(cx, JS_THIS_OBJECT(cx, arglist), &js_console_class)) == NULL) return JS_FALSE; - JS_SET_RVAL(cx, arglist, BOOLEAN_TO_JSVAL(sbbs->saveline())); + JS_SET_RVAL(cx, arglist, BOOLEAN_TO_JSVAL(sbbs->term->saveline())); return JS_TRUE; } @@ -1897,7 +1898,7 @@ js_restoreline(JSContext *cx, uintN argc, jsval *arglist) return JS_FALSE; rc = JS_SUSPENDREQUEST(cx); - JS_SET_RVAL(cx, arglist, BOOLEAN_TO_JSVAL(sbbs->restoreline())); + JS_SET_RVAL(cx, arglist, BOOLEAN_TO_JSVAL(sbbs->term->restoreline())); JS_RESUMEREQUEST(cx, rc); return JS_TRUE; } @@ -1921,14 +1922,15 @@ js_ansi(JSContext *cx, uintN argc, jsval *arglist) } if (argc > 1) { int32 curattr = 0; - char buf[16]; + char buf[128]; if (!JS_ValueToInt32(cx, argv[1], &curattr)) return JS_FALSE; - if ((js_str = JS_NewStringCopyZ(cx, sbbs->ansi(attr, curattr, buf))) == NULL) + // TODO: A way to use term->curattr here... + if ((js_str = JS_NewStringCopyZ(cx, sbbs->term->attrstr(attr, curattr, buf, sizeof(buf)))) == NULL) return JS_FALSE; } else { - if ((js_str = JS_NewStringCopyZ(cx, sbbs->ansi(attr))) == NULL) + if ((js_str = JS_NewStringCopyZ(cx, sbbs->term->attrstr(attr))) == NULL) return JS_FALSE; } @@ -1946,7 +1948,7 @@ js_pushxy(JSContext *cx, uintN argc, jsval *arglist) return JS_FALSE; rc = JS_SUSPENDREQUEST(cx); - JS_SET_RVAL(cx, arglist, BOOLEAN_TO_JSVAL(sbbs->ansi_save())); + JS_SET_RVAL(cx, arglist, BOOLEAN_TO_JSVAL(sbbs->term->save_cursor_pos())); JS_RESUMEREQUEST(cx, rc); return JS_TRUE; } @@ -1961,7 +1963,7 @@ js_popxy(JSContext *cx, uintN argc, jsval *arglist) return JS_FALSE; rc = JS_SUSPENDREQUEST(cx); - JS_SET_RVAL(cx, arglist, BOOLEAN_TO_JSVAL(sbbs->ansi_restore())); + JS_SET_RVAL(cx, arglist, BOOLEAN_TO_JSVAL(sbbs->term->restore_cursor_pos())); JS_RESUMEREQUEST(cx, rc); return JS_TRUE; } @@ -1999,7 +2001,7 @@ js_gotoxy(JSContext *cx, uintN argc, jsval *arglist) } rc = JS_SUSPENDREQUEST(cx); - JS_SET_RVAL(cx, arglist, BOOLEAN_TO_JSVAL(sbbs->cursor_xy(x, y))); + JS_SET_RVAL(cx, arglist, BOOLEAN_TO_JSVAL(sbbs->term->gotoxy(x, y))); JS_RESUMEREQUEST(cx, rc); return JS_TRUE; } @@ -2010,7 +2012,7 @@ js_getxy(JSContext *cx, uintN argc, jsval *arglist) { JSObject * obj = JS_THIS_OBJECT(cx, arglist); sbbs_t* sbbs; - int x, y; + unsigned x, y; JSObject* screen; jsrefcount rc; @@ -2020,7 +2022,7 @@ js_getxy(JSContext *cx, uintN argc, jsval *arglist) JS_SET_RVAL(cx, arglist, JSVAL_FALSE); rc = JS_SUSPENDREQUEST(cx); - bool result = sbbs->cursor_getxy(&x, &y); + bool result = sbbs->term->getxy(&x, &y); JS_RESUMEREQUEST(cx, rc); if (result == true) { @@ -2049,7 +2051,7 @@ js_cursor_home(JSContext *cx, uintN argc, jsval *arglist) JS_SET_RVAL(cx, arglist, JSVAL_VOID); rc = JS_SUSPENDREQUEST(cx); - sbbs->cursor_home(); + sbbs->term->cursor_home(); JS_RESUMEREQUEST(cx, rc); return JS_TRUE; } @@ -2072,7 +2074,7 @@ js_cursor_up(JSContext *cx, uintN argc, jsval *arglist) return JS_FALSE; } rc = JS_SUSPENDREQUEST(cx); - sbbs->cursor_up(val); + sbbs->term->cursor_up(val); JS_RESUMEREQUEST(cx, rc); return JS_TRUE; } @@ -2095,7 +2097,7 @@ js_cursor_down(JSContext *cx, uintN argc, jsval *arglist) return JS_FALSE; } rc = JS_SUSPENDREQUEST(cx); - sbbs->cursor_down(val); + sbbs->term->cursor_down(val); JS_RESUMEREQUEST(cx, rc); return JS_TRUE; } @@ -2118,7 +2120,7 @@ js_cursor_right(JSContext *cx, uintN argc, jsval *arglist) return JS_FALSE; } rc = JS_SUSPENDREQUEST(cx); - sbbs->cursor_right(val); + sbbs->term->cursor_right(val); JS_RESUMEREQUEST(cx, rc); return JS_TRUE; } @@ -2141,7 +2143,7 @@ js_cursor_left(JSContext *cx, uintN argc, jsval *arglist) return JS_FALSE; } rc = JS_SUSPENDREQUEST(cx); - sbbs->cursor_left(val); + sbbs->term->cursor_left(val); JS_RESUMEREQUEST(cx, rc); return JS_TRUE; } @@ -2163,7 +2165,7 @@ js_backspace(JSContext *cx, uintN argc, jsval *arglist) return JS_FALSE; } rc = JS_SUSPENDREQUEST(cx); - sbbs->backspace(val); + sbbs->term->backspace(val); JS_RESUMEREQUEST(cx, rc); return JS_TRUE; } @@ -2171,21 +2173,15 @@ js_backspace(JSContext *cx, uintN argc, jsval *arglist) static JSBool js_creturn(JSContext *cx, uintN argc, jsval *arglist) { - jsval * argv = JS_ARGV(cx, arglist); sbbs_t* sbbs; jsrefcount rc; - int32 val = 1; if ((sbbs = (sbbs_t*)js_GetClassPrivate(cx, JS_THIS_OBJECT(cx, arglist), &js_console_class)) == NULL) return JS_FALSE; JS_SET_RVAL(cx, arglist, JSVAL_VOID); - if (argc) { - if (!JS_ValueToInt32(cx, argv[0], &val)) - return JS_FALSE; - } rc = JS_SUSPENDREQUEST(cx); - sbbs->carriage_return(val); + sbbs->term->carriage_return(); JS_RESUMEREQUEST(cx, rc); return JS_TRUE; } @@ -2207,7 +2203,7 @@ js_linefeed(JSContext *cx, uintN argc, jsval *arglist) return JS_FALSE; } rc = JS_SUSPENDREQUEST(cx); - sbbs->line_feed(val); + sbbs->term->line_feed(val); JS_RESUMEREQUEST(cx, rc); return JS_TRUE; } @@ -2237,7 +2233,7 @@ js_ansi_getdims(JSContext *cx, uintN argc, jsval *arglist) return JS_FALSE; rc = JS_SUSPENDREQUEST(cx); - JS_SET_RVAL(cx, arglist, BOOLEAN_TO_JSVAL(sbbs->ansi_getdims())); + JS_SET_RVAL(cx, arglist, BOOLEAN_TO_JSVAL(sbbs->term->getdims())); JS_RESUMEREQUEST(cx, rc); return JS_TRUE; } @@ -2357,11 +2353,11 @@ js_term_supports(JSContext *cx, uintN argc, jsval *arglist) if (!JS_ValueToInt32(cx, argv[0], &flags)) return JS_FALSE; rc = JS_SUSPENDREQUEST(cx); - JS_SET_RVAL(cx, arglist, BOOLEAN_TO_JSVAL(sbbs->term_supports(flags))); + JS_SET_RVAL(cx, arglist, BOOLEAN_TO_JSVAL(sbbs->term->supports(flags))); JS_RESUMEREQUEST(cx, rc); } else { rc = JS_SUSPENDREQUEST(cx); - flags = sbbs->term_supports(); + flags = sbbs->term->flags(); JS_RESUMEREQUEST(cx, rc); JS_SET_RVAL(cx, arglist, INT_TO_JSVAL(flags)); } @@ -2662,7 +2658,7 @@ static jsSyncMethodSpec js_console_functions[] = { }, {"crlf", js_newline, 0, JSTYPE_ALIAS }, {"newline", js_newline, 0, JSTYPE_VOID, JSDOCSTR("[count=1]") - , JSDOCSTR("Output <i>count</i> number of new-line sequences (e.g. carriage-return/line-feed pairs), AKA <tt>crlf()</tt>") + , JSDOCSTR("Output <i>count</i> number of new-line sequences (e.g. carriage-return/line-feed pairs), AKA <tt>crlf() does perform pause</tt>") , 310 }, {"cond_newline", js_cond_newline, 0, JSTYPE_VOID, JSDOCSTR("") @@ -2840,12 +2836,12 @@ static jsSyncMethodSpec js_console_functions[] = { , JSDOCSTR("Send a destructive backspace sequence") , 315 }, - {"creturn", js_creturn, 0, JSTYPE_VOID, JSDOCSTR("[count=1]") + {"creturn", js_creturn, 0, JSTYPE_VOID, JSDOCSTR("") , JSDOCSTR("Send carriage-return (or equivalent) character(s) - moving the cursor to the left-most screen column") , 31700 }, {"linefeed", js_linefeed, 0, JSTYPE_VOID, JSDOCSTR("[count=1]") - , JSDOCSTR("Send line-feed (or equivalent) character(s) - moving the cursor down one or more screen rows") + , JSDOCSTR("Send line-feed (or equivalent) character(s) - moving the cursor down one or more screen rows, does not cause a pause") , 320 }, {"clearkeybuffer", js_clearkeybuf, 0, JSTYPE_VOID, JSDOCSTR("") diff --git a/src/sbbs3/js_user.c b/src/sbbs3/js_user.c index e9bff784967a5e581fe4b141027244ace12863e0..b08ec185af8bc7313654de9d9a816177c20ebcd1 100644 --- a/src/sbbs3/js_user.c +++ b/src/sbbs3/js_user.c @@ -22,6 +22,7 @@ #include "sbbs.h" #include "filedat.h" #include "js_request.h" +#include "terminal.h" #ifdef JAVASCRIPT @@ -468,6 +469,7 @@ static JSBool js_user_set(JSContext *cx, JSObject *obj, jsid id, JSBool strict, int32 usernumber; jsrefcount rc; scfg_t* scfg; + void* ptr; scfg = JS_GetRuntimePrivate(JS_GetRuntime(cx)); @@ -612,8 +614,10 @@ static JSBool js_user_set(JSContext *cx, JSObject *obj, jsid id, JSBool strict, free(str); return JS_FALSE; } - putusermisc(scfg, p->user->number, p->user->misc = val); + ptr = JS_GetContextPrivate(cx); rc = JS_SUSPENDREQUEST(cx); + putusermisc(scfg, p->user->number, p->user->misc = val); + update_terminal(ptr, p->user); break; case USER_PROP_QWK: JS_RESUMEREQUEST(cx, rc); diff --git a/src/sbbs3/listfile.cpp b/src/sbbs3/listfile.cpp index 3be3c3b341fcb768e4afc4c2eded9d4401e9e981..a0eb7f2746efa0fc4a6dbea2efa809018cb2c94d 100644 --- a/src/sbbs3/listfile.cpp +++ b/src/sbbs3/listfile.cpp @@ -110,7 +110,7 @@ int sbbs_t::listfiles(const int dirnum, const char *filespec, FILE* tofile, cons if (useron.misc & BATCHFLAG && !tofile && found && found != lastbat && !(mode & (FL_EXT | FL_VIEW))) { flagprompt = 0; - lncntr = 0; + term->lncntr = 0; if ((i = batchflagprompt(&smb, bf, file_row, letter - 'A', file_count)) == 2) { m = anchor; found -= letter - 'A'; @@ -202,19 +202,19 @@ int sbbs_t::listfiles(const int dirnum, const char *filespec, FILE* tofile, cons bputs("\xbb\r\n\xba "); snprintf(hdr, sizeof hdr, text[BoxHdrLib], i + 1, cfg.lib[usrlib[i]]->lname); bputs(hdr); - for (c = bstrlen(hdr); c < d; c++) + for (c = term->bstrlen(hdr); c < d; c++) outchar(' '); attr(cfg.color[clr_filelsthdrbox]); bputs("\xba\r\n\xba "); snprintf(hdr, sizeof hdr, text[BoxHdrDir], j + 1, cfg.dir[dirnum]->lname); bputs(hdr); - for (c = bstrlen(hdr); c < d; c++) + for (c = term->bstrlen(hdr); c < d; c++) outchar(' '); attr(cfg.color[clr_filelsthdrbox]); bputs("\xba\r\n\xba "); snprintf(hdr, sizeof hdr, text[BoxHdrFiles], file_count); bputs(hdr); - for (c = bstrlen(hdr); c < d; c++) + for (c = term->bstrlen(hdr); c < d; c++) outchar(' '); attr(cfg.color[clr_filelsthdrbox]); bputs("\xba\r\n\xc8\xcd"); @@ -232,7 +232,7 @@ int sbbs_t::listfiles(const int dirnum, const char *filespec, FILE* tofile, cons snprintf(hdr, sizeof hdr, text[ShortHdrLib], i + 1, cfg.lib[usrlib[i]]->sname); bputs("\1[\1>\r\n"); bputs(hdr); - c = bstrlen(hdr); + c = term->bstrlen(hdr); } if (tofile) { c += fprintf(tofile, "(%u) %s", j + 1, cfg.dir[dirnum]->lname); @@ -240,7 +240,7 @@ int sbbs_t::listfiles(const int dirnum, const char *filespec, FILE* tofile, cons else { snprintf(hdr, sizeof hdr, text[ShortHdrDir], j + 1, cfg.dir[dirnum]->lname); bputs(hdr); - c += bstrlen(hdr); + c += term->bstrlen(hdr); } if (tofile) { fprintf(tofile, "\r\n%.*s\r\n", c, "----------------------------------------------------------------"); @@ -254,7 +254,7 @@ int sbbs_t::listfiles(const int dirnum, const char *filespec, FILE* tofile, cons } } } - int currow = row; + int currow = term->row; next = m; disp = 1; if (mode & (FL_EXT | FL_VIEW)) { @@ -295,10 +295,10 @@ int sbbs_t::listfiles(const int dirnum, const char *filespec, FILE* tofile, cons if (flagprompt || letter == 'Z' || !disp || (filespec[0] && !strchr(filespec, '*') && !strchr(filespec, '?') && !(mode & FL_FIND)) - || (useron.misc & BATCHFLAG && !tofile && lncntr >= rows - 2) + || (useron.misc & BATCHFLAG && !tofile && term->lncntr >= term->rows - 2) ) { flagprompt = 0; - lncntr = 0; + term->lncntr = 0; lastbat = found; if ((int)(i = batchflagprompt(&smb, bf, file_row, letter - 'A' + 1, file_count)) < 1) { if ((int)i == -1) @@ -327,8 +327,8 @@ int sbbs_t::listfiles(const int dirnum, const char *filespec, FILE* tofile, cons letter++; } if (useron.misc & BATCHFLAG && !tofile - && lncntr >= rows - 2) { - lncntr = 0; /* defeat pause() */ + && term->lncntr >= term->rows - 2) { + term->lncntr = 0; /* defeat pause() */ flagprompt = 1; } m = next; @@ -365,16 +365,16 @@ bool sbbs_t::listfile(file_t* f, const int dirnum, const char *search, const cha FREE_AND_NULL(ext); if (ch != '\0') { ext = f->extdesc; - if ((useron.misc & BATCHFLAG) && lncntr + extdesclines(ext) >= rows - 2 && letter != 'A') + if ((useron.misc & BATCHFLAG) && term->lncntr + extdesclines(ext) >= term->rows - 2 && letter != 'A') return false; } } } - cond_newline(); + term->cond_newline(); attr(cfg.color[(f->hdr.attr & MSG_DELETE) ? clr_err : clr_filename]); char fname[SMB_FILEIDX_NAMELEN + 1]; - if (namelen < 12 || cols < 132) + if (namelen < 12 || term->cols < 132) namelen = 12; else if (namelen > sizeof(fname) - 1) namelen = sizeof(fname) - 1; @@ -459,6 +459,22 @@ bool sbbs_t::listfile(file_t* f, const int dirnum, const char *search, const cha return true; } +static int +mouse_list(sbbs_t *sbbs, uint *row, const int total, char *str) +{ + int d; + link_list_t tmp_hotspots {}; + link_list_t *saved_hotspots = sbbs->term->mouse_hotspots; + sbbs->term->mouse_hotspots = &tmp_hotspots; + for (int i = 0; i < total; i++) + sbbs->term->add_hotspot((char)('A' + i), /* hungry: */ true, HOTSPOT_CURRENT_X, HOTSPOT_CURRENT_X, row[i]); + sbbs->bputs(sbbs->text[BatchDlFlags]); + d = sbbs->getstr(str, BF_MAX, K_NOCRLF); + sbbs->term->clear_hotspots(); + sbbs->term->mouse_hotspots = saved_hotspots; + return d; +} + /****************************************************************************/ /* Batch flagging prompt for download, extended info, and archive viewing */ /* Returns -1 if 'Q' or Ctrl-C, 0 if skip, 1 if [Enter], 2 otherwise */ @@ -483,7 +499,7 @@ int sbbs_t::batchflagprompt(smb_t* smb, file_t** bf, uint* row, const int total if (usrdir[ulib][udir] == smb->dirnum) break; - cond_blankline(); + term->cond_blankline(); while (online) { bprintf(text[BatchFlagPrompt] , ulib + 1 @@ -492,10 +508,10 @@ int sbbs_t::batchflagprompt(smb_t* smb, file_t** bf, uint* row, const int total , cfg.dir[smb->dirnum]->sname , total, totalfiles); ch = getkey(K_UPPER); - clearline(); + term->clearline(); if (ch == '?') { menu("batflag"); - if (lncntr) + if (term->lncntr) pause(); return 2; } @@ -522,21 +538,14 @@ int sbbs_t::batchflagprompt(smb_t* smb, file_t** bf, uint* row, const int total CRLF; return 2; } - link_list_t saved_hotspots = mouse_hotspots; - ZERO_VAR(mouse_hotspots); - for (i = 0; i < total; i++) - add_hotspot((char)('A' + i), /* hungry: */ true, -1, -1, row[i]); - bputs(text[BatchDlFlags]); - d = getstr(str, BF_MAX, K_NOCRLF); - clear_hotspots(); - mouse_hotspots = saved_hotspots; - lncntr = 0; + d = mouse_list(this, row, total, str); + term->lncntr = 0; if (sys_status & SS_ABORT) return -1; if (d > 0) { /* d is string length */ strupr(str); CRLF; - lncntr = 0; + term->lncntr = 0; for (c = 0; c < d; c++) { if (batdn_total() >= cfg.max_batdn) { bprintf(text[BatchDlQueueIsFull], str + c); @@ -570,7 +579,7 @@ int sbbs_t::batchflagprompt(smb_t* smb, file_t** bf, uint* row, const int total CRLF; return 2; } - clearline(); + term->clearline(); continue; } @@ -580,21 +589,14 @@ int sbbs_t::batchflagprompt(smb_t* smb, file_t** bf, uint* row, const int total return -1; return 2; } - link_list_t saved_hotspots = mouse_hotspots; - ZERO_VAR(mouse_hotspots); - for (i = 0; i < total; i++) - add_hotspot((char)('A' + i), /* hungry: */ true, -1, -1, row[i]); - bputs(text[BatchDlFlags]); - d = getstr(str, BF_MAX, K_NOCRLF); - clear_hotspots(); - mouse_hotspots = saved_hotspots; - lncntr = 0; + d = mouse_list(this, row, total, str); + term->lncntr = 0; if (sys_status & SS_ABORT) return -1; if (d > 0) { /* d is string length */ strupr(str); CRLF; - lncntr = 0; + term->lncntr = 0; for (c = 0; c < d; c++) { if (str[c] == '*' || strchr(str + c, '.')) { /* filename or spec given */ // f.dir=dirnum; @@ -617,10 +619,10 @@ int sbbs_t::batchflagprompt(smb_t* smb, file_t** bf, uint* row, const int total return -1; } } - cond_newline(); + term->cond_newline(); return 2; } - clearline(); + term->clearline(); continue; } @@ -632,22 +634,15 @@ int sbbs_t::batchflagprompt(smb_t* smb, file_t** bf, uint* row, const int total d = 1; } else { - link_list_t saved_hotspots = mouse_hotspots; - ZERO_VAR(mouse_hotspots); - for (i = 0; i < total; i++) - add_hotspot((char)('A' + i), /* hungry: */ true, -1, -1, row[i]); - bputs(text[BatchDlFlags]); - d = getstr(str, BF_MAX, K_NOCRLF); - clear_hotspots(); - mouse_hotspots = saved_hotspots; + d = mouse_list(this, row, total, str); } - lncntr = 0; + term->lncntr = 0; if (sys_status & SS_ABORT) return -1; if (d > 0) { /* d is string length */ strupr(str); if (total > 1) - newline(); + term->newline(); if (ch == 'R') { if (noyes(text[RemoveFileQ])) return 2; @@ -684,7 +679,7 @@ int sbbs_t::batchflagprompt(smb_t* smb, file_t** bf, uint* row, const int total md--; CRLF; } - lncntr = 0; + term->lncntr = 0; for (c = 0; c < d; c++) { if (str[c] == '*' || strchr(str + c, '.')) { /* filename or spec given */ // f.dir=dirnum; @@ -732,7 +727,7 @@ int sbbs_t::batchflagprompt(smb_t* smb, file_t** bf, uint* row, const int total } return 2; } - clearline(); + term->clearline(); continue; } @@ -839,7 +834,7 @@ int sbbs_t::listfileinfo(const int dirnum, const char *filespec, const int mode) } else { showfileinfo(f, /* show_extdesc: */ mode != FI_DOWNLOAD); -// newline(); +// term->newline(); } if (mode == FI_REMOVE || mode == FI_OLD || mode == FI_OLDUL || mode == FI_OFFLINE) { @@ -1057,7 +1052,7 @@ int sbbs_t::listfileinfo(const int dirnum, const char *filespec, const int mode) if (i < cfg.total_prots) { delfiles(cfg.temp_dir, ALLFILES); if (cfg.dir[f->dir]->seqdev) { - lncntr = 0; + term->lncntr = 0; seqwait(cfg.dir[f->dir]->seqdev); bprintf(text[RetrievingFile], f->name); getfilepath(&cfg, f, str); @@ -1075,7 +1070,7 @@ int sbbs_t::listfileinfo(const int dirnum, const char *filespec, const int mode) bputs(cfg.dlevent[j]->workstr); external(cmdstr(cfg.dlevent[j]->cmd, path, nulstr, NULL, cfg.dlevent[j]->ex_mode) , cfg.dlevent[j]->ex_mode); - clearline(); + term->clearline(); } } putnode_downloading(getfilesize(&cfg, f)); diff --git a/src/sbbs3/logon.cpp b/src/sbbs3/logon.cpp index 8a361e963c11abcbda01f9667b2a9f9b45c79bef..e8670a74d5a133de49e279fa78c8eea432a63906 100644 --- a/src/sbbs3/logon.cpp +++ b/src/sbbs3/logon.cpp @@ -215,9 +215,9 @@ bool sbbs_t::logon() bputs(text[LoggingOn]); if (useron.rows != TERM_ROWS_AUTO) - rows = useron.rows; + term->rows = useron.rows; if (useron.cols != TERM_COLS_AUTO) - cols = useron.cols; + term->cols = useron.cols; update_nodeterm(); if (tm.tm_mon + 1 == getbirthmonth(&cfg, useron.birth) && tm.tm_mday == getbirthday(&cfg, useron.birth) && !(useron.rest & FLAG('Q'))) { @@ -456,6 +456,8 @@ bool sbbs_t::logon() putuserdat(&useron); getmsgptrs(); sys_status |= SS_USERON; /* moved from further down */ + // Needs to be called after SS_USERON is set + update_nodeterm(); mqtt_user_login(mqtt, &client); @@ -520,7 +522,7 @@ bool sbbs_t::logon() bprintf(text[LiMailWaiting], mailw, mailw - mailr); bprintf(text[LiSysopIs] , text[sysop_available(&cfg) ? LiSysopAvailable : LiSysopNotAvailable]); - newline(); + term->newline(); } if (sys_status & SS_EVENT) diff --git a/src/sbbs3/main.cpp b/src/sbbs3/main.cpp index 9d80a56e3319f1e24e1b75cf73fa188052d7e5a2..e5c8e95d04d52f1d34bbdae9b55a289fddefddbb 100644 --- a/src/sbbs3/main.cpp +++ b/src/sbbs3/main.cpp @@ -1086,7 +1086,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) @@ -2551,8 +2551,6 @@ void output_thread(void* arg) lprintf(LOG_DEBUG, "%s output thread started", node); #endif - sbbs->console |= CON_R_ECHO; - #ifdef TCP_MAXSEG /* * Auto-tune the highwater mark to be the negotiated MSS for the @@ -2982,12 +2980,10 @@ void event_thread(void* arg) continue; } sbbs->online = ON_LOCAL; - sbbs->console |= CON_L_ECHO; sbbs->getusrsubs(); bool success = sbbs->unpack_rep(fname); sbbs->delfiles(sbbs->cfg.temp_dir, ALLFILES); /* clean-up temp_dir after unpacking */ sbbs->online = false; - sbbs->console &= ~CON_L_ECHO; /* putuserdat? */ if (success) { @@ -3050,7 +3046,6 @@ void event_thread(void* arg) if (!(sbbs->useron.misc & (DELETED | INACTIVE))) { sbbs->lprintf(LOG_INFO, "Packing QWK Message Packet"); sbbs->online = ON_LOCAL; - sbbs->console |= CON_L_ECHO; sbbs->getmsgptrs(); sbbs->getusrsubs(); @@ -3066,7 +3061,6 @@ void event_thread(void* arg) } else sbbs->lputs(LOG_INFO, "No packet created (no new messages)"); sbbs->delfiles(sbbs->cfg.temp_dir, ALLFILES); - sbbs->console &= ~CON_L_ECHO; sbbs->online = false; } sbbs->fremove(WHERE, fname); @@ -3105,12 +3099,10 @@ void event_thread(void* arg) sbbs->lprintf(LOG_INFO, "Running node %d daily event", i); sbbs->online = ON_LOCAL; - sbbs->console |= CON_L_ECHO; sbbs->logentry("!:", "Run node daily event"); const char* cmd = sbbs->cmdstr(sbbs->cfg.node_daily.cmd, nulstr, nulstr, NULL, sbbs->cfg.node_daily.misc); int result = sbbs->external(cmd, EX_OFFLINE | sbbs->cfg.node_daily.misc); sbbs->lprintf(result ? LOG_ERR : LOG_INFO, "Node daily event: '%s' returned %d", cmd, result); - sbbs->console &= ~CON_L_ECHO; sbbs->online = false; } if (sbbs->getnodedat(i, &node, true)) { @@ -3177,7 +3169,6 @@ void event_thread(void* arg) if (flength(str) > 0) { /* silently ignore 0-byte QWK packets */ sbbs->lprintf(LOG_DEBUG, "Inbound QWK Packet detected: %s", str); sbbs->online = ON_LOCAL; - sbbs->console |= CON_L_ECHO; if (sbbs->unpack_qwk(str, i) == false) { char newname[MAX_PATH + 1]; SAFEPRINTF2(newname, "%s.%x.bad", str, (int)now); @@ -3191,7 +3182,6 @@ void event_thread(void* arg) sbbs->delfiles(sbbs->cfg.data_dir, newname, /* keep: */ 10); } sbbs->delfiles(sbbs->cfg.temp_dir, ALLFILES); - sbbs->console &= ~CON_L_ECHO; sbbs->online = false; if (fexist(str)) sbbs->fremove(WHERE, str, /* log-all-errors: */ true); @@ -3220,9 +3210,7 @@ void event_thread(void* arg) } if (file != -1) close(file); - sbbs->console |= CON_L_ECHO; packed_rep = sbbs->pack_rep(i); - sbbs->console &= ~CON_L_ECHO; if (packed_rep) { if ((file = sbbs->nopen(str, O_WRONLY | O_CREAT)) == -1) sbbs->errormsg(WHERE, ERR_OPEN, str, O_WRONLY | O_CREAT); @@ -3270,14 +3258,12 @@ void event_thread(void* arg) SAFECOPY(sbbs->cfg.node_dir, sbbs->cfg.node_path[sbbs->cfg.node_num - 1]); sbbs->lprintf(LOG_INFO, "Call-out: %s", sbbs->cfg.qhub[i]->id); sbbs->online = ON_LOCAL; - sbbs->console |= CON_L_ECHO; int ex_mode = EX_OFFLINE | EX_SH; /* sh for Unix perl scripts */ if (sbbs->cfg.qhub[i]->misc & QHUB_NATIVE) ex_mode |= EX_NATIVE; const char* cmd = sbbs->cmdstr(sbbs->cfg.qhub[i]->call, sbbs->cfg.qhub[i]->id, sbbs->cfg.qhub[i]->id, NULL, ex_mode); int result = sbbs->external(cmd, ex_mode); sbbs->lprintf(result ? LOG_ERR : LOG_INFO, "Call-out to: %s (%s) returned %d", sbbs->cfg.qhub[i]->id, cmd, result); - sbbs->console &= ~CON_L_ECHO; sbbs->online = false; } } @@ -3455,7 +3441,6 @@ void event_thread(void* arg) ex_mode |= EX_SH; ex_mode |= (sbbs->cfg.event[i]->misc & EX_NATIVE); sbbs->online = ON_LOCAL; - sbbs->console |= CON_L_ECHO; cmd = sbbs->cmdstr(cmd, nulstr, sbbs->cfg.event[i]->dir, NULL, ex_mode); sbbs->lprintf(LOG_INFO, "Running %s%stimed event: %s" , native_executable(&sbbs->cfg, cmd, ex_mode) ? "native ":"16-bit DOS " @@ -3468,7 +3453,6 @@ void event_thread(void* arg) else sbbs->lprintf(LOG_DEBUG, "Background timed event spawned: %s", cmd); } - sbbs->console &= ~CON_L_ECHO; sbbs->online = false; sbbs->cfg.event[i]->last = time32(NULL); SAFEPRINTF(str, "%stime.ini", sbbs->cfg.ctrl_dir); @@ -3570,9 +3554,7 @@ sbbs_t::sbbs_t(ushort node_num, union xp_sockaddr *addr, size_t addr_len, const SAFECOPY(connection, "Telnet"); telnet_ack_event = CreateEvent(NULL, /* Manual Reset: */ false, /* InitialState */ false, NULL); - listInit(&savedlines, /* flags: */ 0); listInit(&smb_list, /* flags: */ 0); - listInit(&mouse_hotspots, /* flags: */ 0); pthread_mutex_init(&nodefile_mutex, NULL); for (i = 0; i < TOTAL_TEXT; i++) @@ -3823,6 +3805,7 @@ bool sbbs_t::init() pthread_mutex_init(&input_thread_mutex, NULL); input_thread_mutex_created = true; + update_terminal(this); reset_logon_vars(); online = ON_REMOTE; @@ -3930,9 +3913,7 @@ sbbs_t::~sbbs_t() FREE_AND_NULL(qwknode); total_qwknodes = 0; - listFree(&savedlines); listFree(&smb_list); - listFree(&mouse_hotspots); #ifdef USE_CRYPTLIB while (ssh_mutex_created && pthread_mutex_destroy(&ssh_mutex) == EBUSY) @@ -3952,6 +3933,11 @@ sbbs_t::~sbbs_t() lprintf(LOG_ERR, "!MEMORY ERRORS REPORTED IN DATA/DEBUG.LOG!"); #endif + if (term) { + delete term; + term = nullptr; + } + #ifdef _DEBUG lprintf(LOG_DEBUG, "destructor end"); #endif @@ -4077,7 +4063,7 @@ int sbbs_t::mv(const char* path, const char* dest, bool copy) void sbbs_t::hangup(void) { if (online) { - clear_hotspots(); + term->clear_hotspots(); lprintf(LOG_DEBUG, "disconnecting client"); online = false; // moved from the bottom of this function on Jan-25-2009 } @@ -4266,13 +4252,16 @@ void sbbs_t::reset_logon_vars(void) cid[0] = 0; wordwrap[0] = 0; question[0] = 0; - row = 0; - rows = startup->default_term_height; - cols = startup->default_term_width; - lncntr = 0; + if (term) { + term->row = 0; + term->rows = startup->default_term_height; + term->cols = startup->default_term_width; + term->lncntr = 0; + term->cterm_version = 0; + term->lbuflen = 0; + term->cur_output_rate = output_rate_unlimited; + } autoterm = 0; - cterm_version = 0; - lbuflen = 0; timeleft_warn = 0; keybufbot = keybuftop = 0; usrgrps = usrlibs = 0; @@ -4283,7 +4272,6 @@ void sbbs_t::reset_logon_vars(void) cursub[i] = 0; cur_rate = 30000; dte_rate = 38400; - cur_output_rate = output_rate_unlimited; main_cmds = xfer_cmds = posts_read = 0; lastnodemsg = 0; lastnodemsguser[0] = 0; @@ -5608,6 +5596,13 @@ NO_SSH: close_socket(client_socket); continue; } + 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->term_out(PETSCII_UPPERLOWER); + } + update_terminal(sbbs); + // TODO: Plain text output in SSH socket struct trash trash; if (sbbs->trashcan(host_ip, "ip", &trash)) { char details[128]; @@ -5680,12 +5675,7 @@ NO_SSH: sbbs->outcom(0); /* acknowledge RLogin per RFC 1282 */ sbbs->autoterm = 0; - sbbs->cols = startup->default_term_width; - if (inet_addrport(&local_addr) == startup->pet40_port || inet_addrport(&local_addr) == startup->pet80_port) { - sbbs->autoterm = PETSCII; - sbbs->cols = inet_addrport(&local_addr) == startup->pet40_port ? 40 : 80; - sbbs->outcom(PETSCII_UPPERLOWER); - } + sbbs->term->cols = startup->default_term_width; sbbs->bprintf("\r\n%s\r\n", VERSION_NOTICE); sbbs->bprintf("%s connection from: %s\r\n", client.protocol, host_ip); @@ -5694,7 +5684,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); } @@ -5721,7 +5711,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); @@ -5783,8 +5773,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); @@ -5859,7 +5849,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; @@ -5996,10 +5986,11 @@ NO_PASSTHRU: uint32_t client_count = protected_uint32_adjust(&node_threads_running, 1); new_node->input_thread_running = true; + new_node->autoterm = sbbs->autoterm; + update_terminal(new_node, sbbs->term); + new_node->term->cols = sbbs->term->cols; new_node->input_thread = (HANDLE)_beginthread(input_thread, 0, new_node); new_node->output_thread_running = true; - new_node->autoterm = sbbs->autoterm; - new_node->cols = sbbs->cols; _beginthread(output_thread, 0, new_node); _beginthread(node_thread, 0, new_node); served++; diff --git a/src/sbbs3/msgtoqwk.cpp b/src/sbbs3/msgtoqwk.cpp index 0e0c7534d82952482f39ed1152d12561e1fbbc91..85b7723da20cfc6b7bc7ce6c29891bd3bfab820b 100644 --- a/src/sbbs3/msgtoqwk.cpp +++ b/src/sbbs3/msgtoqwk.cpp @@ -20,6 +20,7 @@ ****************************************************************************/ #include "sbbs.h" +#include "ansi_terminal.h" #include "qwk.h" #include "utf8.h" #include "cp437defs.h" @@ -258,7 +259,7 @@ int sbbs_t::msgtoqwk(smbmsg_t* msg, FILE *qwk_fp, int mode, smb_t* smb } if (mode & QM_WORDWRAP) { int org_cols = msg->columns ? msg->columns : 80; - int new_cols = useron.cols ? useron.cols : cols ? cols : 80; + int new_cols = useron.cols ? useron.cols : term->cols ? term->cols : 80; char* wrapped = ::wordwrap(buf, new_cols - 1, org_cols - 1, /* handle_quotes */ true, is_utf8, /* pipe_codes: */false); if (wrapped != NULL) { free(buf); @@ -419,65 +420,66 @@ int sbbs_t::msgtoqwk(smbmsg_t* msg, FILE *qwk_fp, int mode, smb_t* smb break; if (mode & QM_EXPCTLA) { str[0] = 0; + ANSI_Terminal ansi(this); switch (toupper(ch)) { case 'W': - SAFECOPY(str, ansi(LIGHTGRAY)); + SAFECOPY(str, ansi.attrstr(LIGHTGRAY)); break; case 'K': - SAFECOPY(str, ansi(BLACK)); + SAFECOPY(str, ansi.attrstr(BLACK)); break; case 'H': - SAFECOPY(str, ansi(HIGH)); + SAFECOPY(str, ansi.attrstr(HIGH)); break; case 'I': - SAFECOPY(str, ansi(BLINK)); + SAFECOPY(str, ansi.attrstr(BLINK)); break; case '-': case '_': case 'N': /* Normal */ - SAFECOPY(str, ansi(ANSI_NORMAL)); + SAFECOPY(str, ansi.attrstr(ANSI_NORMAL)); break; case 'R': - SAFECOPY(str, ansi(RED)); + SAFECOPY(str, ansi.attrstr(RED)); break; case 'G': - SAFECOPY(str, ansi(GREEN)); + SAFECOPY(str, ansi.attrstr(GREEN)); break; case 'B': - SAFECOPY(str, ansi(BLUE)); + SAFECOPY(str, ansi.attrstr(BLUE)); break; case 'C': - SAFECOPY(str, ansi(CYAN)); + SAFECOPY(str, ansi.attrstr(CYAN)); break; case 'M': - SAFECOPY(str, ansi(MAGENTA)); + SAFECOPY(str, ansi.attrstr(MAGENTA)); break; case 'Y': /* Yellow */ - SAFECOPY(str, ansi(BROWN)); + SAFECOPY(str, ansi.attrstr(BROWN)); break; case '0': - SAFECOPY(str, ansi(BG_BLACK)); + SAFECOPY(str, ansi.attrstr(BG_BLACK)); break; case '1': - SAFECOPY(str, ansi(BG_RED)); + SAFECOPY(str, ansi.attrstr(BG_RED)); break; case '2': - SAFECOPY(str, ansi(BG_GREEN)); + SAFECOPY(str, ansi.attrstr(BG_GREEN)); break; case '3': - SAFECOPY(str, ansi(BG_BROWN)); + SAFECOPY(str, ansi.attrstr(BG_BROWN)); break; case '4': - SAFECOPY(str, ansi(BG_BLUE)); + SAFECOPY(str, ansi.attrstr(BG_BLUE)); break; case '5': - SAFECOPY(str, ansi(BG_MAGENTA)); + SAFECOPY(str, ansi.attrstr(BG_MAGENTA)); break; case '6': - SAFECOPY(str, ansi(BG_CYAN)); + SAFECOPY(str, ansi.attrstr(BG_CYAN)); break; case '7': - SAFECOPY(str, ansi(BG_LIGHTGRAY)); + SAFECOPY(str, ansi.attrstr(BG_LIGHTGRAY)); break; } if (str[0]) 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/objects.mk b/src/sbbs3/objects.mk index d31a5fda0704a65583766ebffd9e9b857aa28b58..b08cd3be726eb891ec0a2b07ccbd9e3835141b8f 100644 --- a/src/sbbs3/objects.mk +++ b/src/sbbs3/objects.mk @@ -3,7 +3,8 @@ # [MT]OBJODIR and OFILE must be pre-defined OBJS = $(LOAD_CFG_OBJS) \ - $(MTOBJODIR)/ansiterm$(OFILE) \ + $(MTOBJODIR)/ansi_parser$(OFILE) \ + $(MTOBJODIR)/ansi_terminal$(OFILE) \ $(MTOBJODIR)/answer$(OFILE)\ $(MTOBJODIR)/atcodes$(OFILE)\ $(MTOBJODIR)/bat_xfer$(OFILE)\ @@ -77,6 +78,7 @@ OBJS = $(LOAD_CFG_OBJS) \ $(MTOBJODIR)/newuser$(OFILE)\ $(MTOBJODIR)/pack_qwk$(OFILE)\ $(MTOBJODIR)/pack_rep$(OFILE)\ + $(MTOBJODIR)/petscii_term$(OFILE)\ $(MTOBJODIR)/postmsg$(OFILE)\ $(MTOBJODIR)/prntfile$(OFILE)\ $(MTOBJODIR)/putmsg$(OFILE)\ @@ -95,6 +97,7 @@ OBJS = $(LOAD_CFG_OBJS) \ $(MTOBJODIR)/str$(OFILE)\ $(MTOBJODIR)/telgate$(OFILE)\ $(MTOBJODIR)/telnet$(OFILE)\ + $(MTOBJODIR)/terminal$(OFILE)\ $(MTOBJODIR)/text_sec$(OFILE)\ $(MTOBJODIR)/tmp_xfer$(OFILE)\ $(MTOBJODIR)/trash$(OFILE)\ diff --git a/src/sbbs3/pack_qwk.cpp b/src/sbbs3/pack_qwk.cpp index 108375f545875e69c2308c1e4c0893a8c2897229..d2c9d987fc69a7fe91d5da06b3eeee42ffe70380 100644 --- a/src/sbbs3/pack_qwk.cpp +++ b/src/sbbs3/pack_qwk.cpp @@ -435,7 +435,7 @@ bool sbbs_t::pack_qwk(char *packet, uint *msgcnt, bool prepack) && cfg.sub[usrsub[i][j]]->misc & SUB_FORCED)) { if (!chk_ar(cfg.sub[usrsub[i][j]]->read_ar, &useron, &client)) continue; - lncntr = 0; /* defeat pause */ + term->lncntr = 0; /* defeat pause */ if (useron.rest & FLAG('Q') && !(cfg.sub[usrsub[i][j]]->misc & SUB_QNET)) continue; /* QWK Net Node and not QWK networked, so skip */ @@ -641,7 +641,7 @@ bool sbbs_t::pack_qwk(char *packet, uint *msgcnt, bool prepack) if (isdir(str)) continue; SAFEPRINTF2(path, "%s%s", cfg.temp_dir, dirent->d_name); - lncntr = 0; /* Defeat pause */ + term->lncntr = 0; /* Defeat pause */ lprintf(LOG_INFO, "Including %s in packet", str); bprintf(text[RetrievingFile], str); if (!mv(str, path, /* copy: */ TRUE)) @@ -670,7 +670,7 @@ bool sbbs_t::pack_qwk(char *packet, uint *msgcnt, bool prepack) } totalcdt += f.cost; } - lncntr = 0; + term->lncntr = 0; SAFEPRINTF2(tmp, "%s%s", cfg.temp_dir, filename); if (!fexistcase(tmp)) { seqwait(cfg.dir[f.dir]->seqdev); diff --git a/src/sbbs3/pack_rep.cpp b/src/sbbs3/pack_rep.cpp index c393d5e99ff1160451387a7f065a9189fa266483..e1289ca88904a23d6a506220440957c7f7726b4d 100644 --- a/src/sbbs3/pack_rep.cpp +++ b/src/sbbs3/pack_rep.cpp @@ -174,7 +174,7 @@ bool sbbs_t::pack_rep(uint hubnum) for (i = 0; i < cfg.qhub[hubnum]->subs; i++) { j = cfg.qhub[hubnum]->sub[i]->subnum; /* j now equals the real sub num */ msgs = getlastmsg(j, &last, 0); - lncntr = 0; /* defeat pause */ + term->lncntr = 0; /* defeat pause */ if (!msgs || last <= subscan[j].ptr) { if (subscan[j].ptr > last) { subscan[j].ptr = last; diff --git a/src/sbbs3/petscii_term.cpp b/src/sbbs3/petscii_term.cpp new file mode 100644 index 0000000000000000000000000000000000000000..786de3897f2d0fd7ecc97bfbccbd557ac77fbf88 --- /dev/null +++ b/src/sbbs3/petscii_term.cpp @@ -0,0 +1,550 @@ +#include "petscii_term.h" +#include "petdefs.h" + +// Initial work is only C64 and C128 only, not C65, X16, C116, C16, Plus/4, or VIC-20, and certainly not a PET + +const char *PETSCII_Terminal::attrstr(unsigned atr) +{ + switch (atr) { + + /* Special case */ + case ANSI_NORMAL: + if (subset == PETSCII_C128_80) + return "\x0E\x92\x8F\x9b"; // Upper/Lower, Reverse Off, Flash Off (C128), Light Gray + return "\x0E\x92\x9b"; // Upper/Lower, Reverse Off, Light Gray + case BLINK: + case BG_BRIGHT: + if (subset == PETSCII_C128_80) + return "\x0F"; // Flash (C128 only) + return ""; + + /* Foreground */ + case HIGH: + return "\x05"; // Literally "White" + case BLACK: + return "\x90"; + case RED: + return "\x1C"; + case GREEN: + return "\x1E"; + case BROWN: + return "\x95"; + case BLUE: + return "\x1F"; + case MAGENTA: + return "\x9C"; + case CYAN: + return "\x9F"; + case LIGHTGRAY: + return "\x9B"; + + /* Background */ + case BG_BLACK: + return "\x92"; // Reverse off + case BG_RED: + return "\x1C\x12"; + case BG_GREEN: + return "\x1E\x12"; + case BG_BROWN: + return "\x95\x12"; + case BG_BLUE: + return "\x1F\x12"; + case BG_MAGENTA: + return "\x9C\x12"; + case BG_CYAN: + return "\x9F\x12"; + case BG_LIGHTGRAY: + return "\x9B\x12"; + } + + return "-Invalid use of ansi()-"; +} + +static unsigned +unreverse_attr(unsigned atr) +{ + // This drops all background bits and REVERSED + return (atr & BLINK) // Blink unchanged + | ((atr & BG_BRIGHT) ? HIGH : 0) // Put BG_BRIGHT bit into HIGH + | ((atr & 0x70) >> 4); // Background colour to Foreground +} + +static unsigned +reverse_attr(unsigned atr) +{ + // This drops all foreground bits and sets REVERSED + return REVERSED + | ((atr & HIGH) ? BG_BRIGHT : 0) // Put HIGH bit into BG_BRIGHT + | (atr & BLINK) // Blink unchanged + | ((atr & 0x07) << 4); // Foreground colour to Background +} + +/* + * This deals with the reverse "stuff" + * Basically, if the background is not black, the background colour is + * set as the foreground colour, and REVERSED is set + */ +unsigned +PETSCII_Terminal::xlat_atr(unsigned atr) +{ + if (atr == ANSI_NORMAL) { + // But convert to "normal" atr + atr = LIGHTGRAY; + } + if (atr == BG_BLACK) { + // But convert to "normal" atr + atr &= ~(BG_BLACK | 0x70 | BG_BRIGHT); + } + // If this is already reversed, clear background + if (atr & REVERSED) { + atr &= ~0x70; + } + else { + // If there is a background colour, translate to reversed with black + if (atr & (0x70 | BG_BRIGHT)) { + atr = REVERSED | unreverse_attr(atr); + } + } + if (subset == PETSCII_C64) + atr &= ~(BLINK | UNDERLINE); + return atr; +} + +char* PETSCII_Terminal::attrstr(unsigned atr, unsigned curatr, char* str, size_t strsz) +{ + unsigned newatr = xlat_atr(atr); + unsigned oldatr = xlat_atr(curatr); + + size_t sp = 0; + + if (strsz < 2) { + if (strsz > 0) + str[0] = 0; + return str; + } + + if (newatr & REVERSED) { + if (!(oldatr & REVERSED)) { + str[sp++] = PETSCII_REVERSE_ON; + oldatr = reverse_attr(oldatr); + } + } + else { + if (oldatr & REVERSED) { + str[sp++] = '\x92'; + oldatr = unreverse_attr(oldatr); + } + } + if (sp >= (strsz - 1)) { + str[sp] = 0; + return str; + } + if (newatr & BLINK) { + if (!(oldatr & BLINK)) + str[sp++] = '\x0F'; // C128 + } + else { + if (oldatr & BLINK) + str[sp++] = '\x8F'; // C128 + } + if (newatr & UNDERLINE) { + if (!(oldatr & UNDERLINE)) + str[sp++] = '\x02'; // C128 + } + else { + if (oldatr & UNDERLINE) + str[sp++] = '\x82'; // C128 + } + if (sp >= (strsz - 1)) { + str[sp] = 0; + return str; + } + if ((newatr & 0x0f) != (oldatr & 0x0f)) { + switch (newatr & 0x0f) { + case BLACK: + str[sp++] = '\x90'; + break; + case WHITE: + str[sp++] = PETSCII_WHITE; + break; + case DARKGRAY: + if (subset == PETSCII_C128_80) + str[sp++] = '\x98'; + else + str[sp++] = '\x97'; + break; + case LIGHTGRAY: + str[sp++] = '\x9b'; + break; + case BLUE: + str[sp++] = PETSCII_BLUE; + break; + case LIGHTBLUE: + str[sp++] = '\x9a'; + break; + case CYAN: + if (subset == PETSCII_C128_80) + str[sp++] = '\x97'; + else + str[sp++] = '\x98'; + break; + case LIGHTCYAN: + str[sp++] = '\x9f'; + break; + case YELLOW: + str[sp++] = '\x9e'; + break; + case BROWN: + str[sp++] = '\x95'; + break; + case RED: + str[sp++] = PETSCII_RED; + break; + case LIGHTRED: + str[sp++] = '\x96'; + break; + case GREEN: + str[sp++] = PETSCII_GREEN; + break; + case LIGHTGREEN: + str[sp++] = '\x99'; + break; + case MAGENTA: + str[sp++] = '\x81'; + break; + case LIGHTMAGENTA: + str[sp++] = '\x9c'; + break; + } + } + str[sp] = 0; + return str; +} + +bool PETSCII_Terminal::gotoxy(unsigned x, unsigned y) +{ + sbbs->term_out(PETSCII_HOME); + if (y > rows) + y = rows; + if (x > cols) + x = cols; + while (row < (y - 1) && sbbs->online) + sbbs->term_out(PETSCII_DOWN); + while (column < (x - 1) && sbbs->online) + sbbs->term_out(PETSCII_RIGHT); + lncntr = 0; + return true; +} + +// Was ansi_save +bool PETSCII_Terminal::save_cursor_pos() { + saved_x = column + 1; + saved_y = row + 1; + return true; +} + +// Was ansi_restore +bool PETSCII_Terminal::restore_cursor_pos() { + if (saved_x && saved_y) { + return gotoxy(saved_x, saved_y); + } + return false; +} + +void PETSCII_Terminal::carriage_return() +{ + cursor_left(column); +} + +void PETSCII_Terminal::line_feed(unsigned count) +{ + // Like cursor_down() but scrolls... + for (unsigned i = 0; i < count; i++) + sbbs->term_out(PETSCII_DOWN); +} + +void PETSCII_Terminal::backspace(unsigned int count) +{ + for (unsigned i = 0; i < count; i++) + sbbs->term_out(PETSCII_DELETE); +} + +void PETSCII_Terminal::newline(unsigned count) +{ + sbbs->term_out('\r'); + sbbs->check_pause(); +} + +void PETSCII_Terminal::clearscreen() +{ + clear_hotspots(); + sbbs->term_out(PETSCII_CLEAR); + lastcrcol = 0; +} + +void PETSCII_Terminal::cleartoeos() +{ + int x = row + 1; + int y = column + 1; + + cleartoeol(); + while (row < rows - 1 && sbbs->online) { + cursor_down(); + clearline(); + } + gotoxy(x, y); +} + +void PETSCII_Terminal::cleartoeol() +{ + unsigned s; + s = column; + while (++s <= cols && sbbs->online) + sbbs->term_out(" \x14"); +} + +void PETSCII_Terminal::clearline() +{ + int c = column; + carriage_return(); + cleartoeol(); + cursor_right(c); +} + +void PETSCII_Terminal::cursor_home() +{ + sbbs->term_out(PETSCII_HOME); +} + +void PETSCII_Terminal::cursor_up(unsigned count) +{ + for (unsigned i = 0; i < count; i++) + sbbs->term_out(PETSCII_UP); +} + +void PETSCII_Terminal::cursor_down(unsigned count) +{ + for (unsigned i = 0; i < count; i++) { + if (row >= (rows - 1)) + break; + sbbs->term_out(PETSCII_DOWN); + } +} + +void PETSCII_Terminal::cursor_right(unsigned count) +{ + for (unsigned i = 0; i < count; i++) { + if (column >= (cols - 1)) + break; + sbbs->term_out(PETSCII_RIGHT); + } +} + +void PETSCII_Terminal::cursor_left(unsigned count) +{ + for (unsigned i = 0; i < count; i++) { + sbbs->term_out(PETSCII_LEFT); + if (column == 0) + break; + } +} + +const char* PETSCII_Terminal::type() +{ + return "PETSCII"; +} + +void PETSCII_Terminal::set_color(int c) +{ + if (curatr & REVERSED) { + curatr &= ~(BG_BRIGHT | 0x70); + curatr |= ((c & 0x07) << 4); + if (c & HIGH) + curatr |= BG_BRIGHT; + } + else { + curatr &= ~0x0F; + curatr |= c; + } +} + +bool PETSCII_Terminal::parse_output(char ch) +{ + switch (ch) { + // Zero-width characters we likely shouldn't send + case 0: + case 1: + case 3: // Stop + case 4: + case 6: + case 7: + case 9: + case 10: + case 11: + case 16: + case 21: + case 22: + case 23: + case 24: + case 25: + case 26: + case 27: // ESC - This one is especially troubling + case '\x80': + case '\x83': // Run + case '\x84': + case '\x85': // F1 + case '\x86': // F3 + case '\x87': // F5 + case '\x88': // F7 + case '\x89': // F2 + case '\x8A': // F4 + case '\x8B': // F6 + case '\x8C': // F8 + return false; + + // Specials that affect cursor position + case '\x8D': // Shift-return + case 13: // Translated as Carriage Return + lastcrcol = column; + inc_row(); + set_column(); + if (curatr & 0xf0) + curatr = (curatr & ~0xff) | ((curatr & 0xf0) >> 4); + return true; + case 14: // Lower-case + return true; + case 15: // Flash on (C128 only) + if (subset == PETSCII_C128_80) { + curatr |= BLINK; + return true; + } + return false; + case 17: // Cursor down + inc_row(); + return true; + case 19: // Home + case '\x93': // Clear + set_row(); + set_column(); + return true; + case 20: // Delete + 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; + + // Zero-width characters we want to pass through + case 2: + if (subset == PETSCII_C128_80) { + curatr |= UNDERLINE; + return true; + } + return false; + case 18: // Reverse on + if (!(curatr & REVERSED)) + curatr = reverse_attr(curatr); + return true; + case '\x92': // Reverse off + if (curatr & REVERSED) + curatr = unreverse_attr(curatr); + return true; + case 5: // White + set_color(WHITE); + return true; + case 28: // Red + set_color(RED); + return true; + case 30: // Green + set_color(GREEN); + return true; + case 31: // Blue + set_color(BLUE); + return true; + case '\x81': // Orange + set_color(MAGENTA); + return true; + case '\x82': // Underline off + if (subset == PETSCII_C128_80) { + curatr &= ~UNDERLINE; + return true; + } + return false; + case '\x8F': // Flash off (C128 only) + if (subset == PETSCII_C128_80) { + curatr &= ~BLINK; + return true; + } + return false; + case '\x90': // Black + set_color(BLACK); + return true; + case '\x95': // Brown + set_color(BROWN); + return true; + case '\x96': // Pink + set_color(LIGHTRED); + return true; + case '\x97': // Dark gray or Cyan + if (subset == PETSCII_C128_80) + set_color(CYAN); + else + set_color(DARKGRAY); + return true; + case '\x98': // Cyan or Gray + if (subset == PETSCII_C128_80) + set_color(DARKGRAY); + else + set_color(CYAN); + return true; + case '\x99': // Light Green + set_color(LIGHTGREEN); + return true; + case '\x9A': // Light Blue + set_color(LIGHTBLUE); + return true; + case '\x9B': // Light Gray + set_color(LIGHTGRAY); + return true; + case '\x9C': // Purple + set_color(LIGHTMAGENTA); + return true; + case '\x9E': // Yellow + set_color(YELLOW); + return true; + case '\x9F': // Cyan + set_color(LIGHTCYAN); + return true; + case '\x8E': // Upper case + case '\x94': // Insert + return true; + + // Everything else is assumed one byte wide + default: + inc_column(); + return true; + } + return false; +} + +bool PETSCII_Terminal::can_highlight() { return true; } +bool PETSCII_Terminal::can_move() { return true; } +bool PETSCII_Terminal::is_monochrome() { return false; } + +void PETSCII_Terminal::updated() { + if (cols == 80) + subset = PETSCII_C128_80; + else + subset = PETSCII_C64; +} diff --git a/src/sbbs3/petscii_term.h b/src/sbbs3/petscii_term.h new file mode 100644 index 0000000000000000000000000000000000000000..4b6933e8ed0200001be5c068be197084e3b25deb --- /dev/null +++ b/src/sbbs3/petscii_term.h @@ -0,0 +1,50 @@ +#ifndef PETSCII_TERMINAL_H +#define PETSCII_TERMINAL_H + +#include "sbbs.h" + +class PETSCII_Terminal : public Terminal { +private: + void set_color(int c); + unsigned xlat_atr(unsigned atr); + + unsigned saved_x{0}; + unsigned saved_y{0}; + // https://www.pagetable.com/c64ref/charset/ + enum { + PETSCII_C64, + PETSCII_C128_80 + } subset{PETSCII_C64}; + +public: + + PETSCII_Terminal() = delete; + using Terminal::Terminal; + + virtual const char *attrstr(unsigned atr); + virtual char* attrstr(unsigned atr, unsigned curatr, char* str, size_t strsz); + virtual bool gotoxy(unsigned x, unsigned y); + virtual bool save_cursor_pos(); + virtual bool restore_cursor_pos(); + virtual void carriage_return(); + virtual void line_feed(unsigned count = 1); + virtual void backspace(unsigned int count = 1); + virtual void newline(unsigned count = 1); + virtual void clearscreen(); + virtual void cleartoeos(); + virtual void cleartoeol(); + virtual void clearline(); + virtual void cursor_home(); + virtual void cursor_up(unsigned count = 1); + virtual void cursor_down(unsigned count = 1); + virtual void cursor_right(unsigned count = 1); + virtual void cursor_left(unsigned count = 1); + virtual const char* type(); + virtual bool parse_output(char ch); + virtual bool can_highlight(); + virtual bool can_move(); + virtual bool is_monochrome(); + virtual void updated(); +}; + +#endif diff --git a/src/sbbs3/postmsg.cpp b/src/sbbs3/postmsg.cpp index 4e8845dd2393b5fd829d12d8565a23af66a6ce60..23624007bb940a299a0c2e241c3a247475ea47b7 100644 --- a/src/sbbs3/postmsg.cpp +++ b/src/sbbs3/postmsg.cpp @@ -93,17 +93,17 @@ bool sbbs_t::postmsg(int subnum, int wm_mode, smb_t* resmb, smbmsg_t* remsg) } if (remsg) { - SAFECOPY_UTF8(title, msghdr_field(remsg, remsg->subj, NULL, term_supports(UTF8))); + SAFECOPY_UTF8(title, msghdr_field(remsg, remsg->subj, NULL, term->charset() == CHARSET_UTF8)); SAFECOPY(org_title, title); if (remsg->hdr.attr & MSG_ANONYMOUS) SAFECOPY(from, text[Anonymous]); else - SAFECOPY_UTF8(from, msghdr_field(remsg, remsg->from, NULL, term_supports(UTF8))); + SAFECOPY_UTF8(from, msghdr_field(remsg, remsg->from, NULL, term->charset() == CHARSET_UTF8)); // If user posted this message, reply to the original recipient again if (remsg->to != NULL && ((remsg->from_ext != NULL && atoi(remsg->from_ext) == useron.number) || stricmp(useron.alias, remsg->from) == 0 || stricmp(useron.name, remsg->from) == 0)) - SAFECOPY_UTF8(touser, msghdr_field(remsg, remsg->to, NULL, term_supports(UTF8))); + SAFECOPY_UTF8(touser, msghdr_field(remsg, remsg->to, NULL, term->charset() == CHARSET_UTF8)); else SAFECOPY_UTF8(touser, from); if (remsg->to != NULL) @@ -112,7 +112,7 @@ bool sbbs_t::postmsg(int subnum, int wm_mode, smb_t* resmb, smbmsg_t* remsg) SAFECOPY(top, format_text(RegardingByToOn, remsg , title , from - , msghdr_field(remsg, remsg->to, NULL, term_supports(UTF8)) + , msghdr_field(remsg, remsg->to, NULL, term->charset() == CHARSET_UTF8) , timestr(smb_time(remsg->hdr.when_written)) , smb_zonestr(remsg->hdr.when_written.zone, NULL))); if (remsg->tags != NULL) diff --git a/src/sbbs3/prntfile.cpp b/src/sbbs3/prntfile.cpp index caf470b4d592e6bde3d03d8168c089600c02f968..0b123d871624d34f10a79b0e9f892a65e6f58e8d 100644 --- a/src/sbbs3/prntfile.cpp +++ b/src/sbbs3/prntfile.cpp @@ -60,7 +60,7 @@ bool sbbs_t::printfile(const char* fname, int mode, int org_cols, JSObject* obj) } if (mode & P_NOABORT || rip) { - if (online == ON_REMOTE && console & CON_R_ECHO) { + if (online == ON_REMOTE) { rioctl(IOCM | ABORT); rioctl(IOCS | ABORT); } @@ -90,8 +90,8 @@ bool sbbs_t::printfile(const char* fname, int mode, int org_cols, JSObject* obj) } lprintf(LOG_DEBUG, "Printing file: %s", fpath); - if (!(mode & P_NOCRLF) && row > 0 && !rip) { - newline(); + if (!(mode & P_NOCRLF) && term->row > 0 && !rip) { + term->newline(); } if ((mode & P_OPENCLOSE) && length <= PRINTFILE_MAX_FILE_LEN) { @@ -106,14 +106,14 @@ bool sbbs_t::printfile(const char* fname, int mode, int org_cols, JSObject* obj) errormsg(WHERE, ERR_READ, fpath, length); else { buf[l] = 0; - if ((mode & P_UTF8) && !term_supports(UTF8)) + if ((mode & P_UTF8) && (term->charset() != CHARSET_UTF8)) utf8_normalize_str(buf); putmsg(buf, mode, org_cols, obj); } free(buf); } else { // Line-at-a-time mode uint sys_status_sav = sys_status; - enum output_rate output_rate = cur_output_rate; + enum output_rate output_rate = term->cur_output_rate; uint org_line_delay = line_delay; uint tmpatr = curatr; uint orgcon = console; @@ -132,14 +132,17 @@ bool sbbs_t::printfile(const char* fname, int mode, int org_cols, JSObject* obj) uint rainbow_sav[LEN_RAINBOW + 1]; memcpy(rainbow_sav, rainbow, sizeof rainbow_sav); + ansiParser.reset(); while (!feof(stream) && !msgabort()) { if (fgets(buf, length + 1, stream) == NULL) break; - if ((mode & P_UTF8) && !term_supports(UTF8)) + if ((mode & P_UTF8) && (term->charset() != CHARSET_UTF8)) utf8_normalize_str(buf); if (putmsgfrag(buf, mode, org_cols, obj) != '\0') // early-EOF? break; } + if (ansiParser.current_state() != ansiState_none) + lprintf(LOG_DEBUG, "Incomplete ANSI stripped from end"); memcpy(rainbow, rainbow_sav, sizeof rainbow); free(buf); fclose(stream); @@ -147,10 +150,10 @@ bool sbbs_t::printfile(const char* fname, int mode, int org_cols, JSObject* obj) console = orgcon; attr(tmpatr); } - if (!(mode & P_NOATCODES) && cur_output_rate != output_rate) - set_output_rate(output_rate); + 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 */ @@ -165,7 +168,7 @@ bool sbbs_t::printfile(const char* fname, int mode, int org_cols, JSObject* obj) rioctl(IOSM | ABORT); } if (rip) - ansi_getdims(); + term->getdims(); console = savcon; return true; @@ -208,8 +211,8 @@ bool sbbs_t::printtail(const char* fname, int lines, int mode, int org_cols, JSO } lprintf(LOG_DEBUG, "Printing tail: %s", fpath); - if (!(mode & P_NOCRLF) && row > 0) { - newline(); + if (!(mode & P_NOCRLF) && term->row > 0) { + term->newline(); } if (length > lines * PRINTFILE_MAX_LINE_LEN) { @@ -273,17 +276,16 @@ bool sbbs_t::menu(const char *code, int mode, JSObject* obj) if (menu_file[0]) SAFECOPY(path, menu_file); else { - int term = term_supports(); do { - if ((term & RIP) && menu_exists(code, "rip", path)) + if ((term->supports(RIP)) && menu_exists(code, "rip", path)) break; - if ((term & (ANSI | COLOR)) == ANSI && menu_exists(code, "mon", path)) + if ((term->supports(ANSI) && (!term->supports(COLOR))) && menu_exists(code, "mon", path)) break; - if ((term & ANSI) && menu_exists(code, "ans", path)) + if ((term->supports(ANSI)) && menu_exists(code, "ans", path)) break; - if ((term & PETSCII) && menu_exists(code, "seq", path)) + if ((term->charset() == CHARSET_PETSCII) && menu_exists(code, "seq", path)) break; - if (term & NO_EXASCII) { + if (term->charset() == CHARSET_ASCII) { next = "asc"; last = "msg"; } @@ -298,7 +300,7 @@ bool sbbs_t::menu(const char *code, int mode, JSObject* obj) } mode |= P_OPENCLOSE | P_CPM_EOF; - if (column == 0) + if (term->column == 0) mode |= P_NOCRLF; return printfile(path, mode, /* org_cols: */ 0, obj); } @@ -334,7 +336,7 @@ bool sbbs_t::menu_exists(const char *code, const char* ext, char* path) SAFECOPY(prefix, path); } // Display specified EXACT width file - safe_snprintf(path, MAX_PATH, "%s.%ucol.%s", prefix, cols, ext); + safe_snprintf(path, MAX_PATH, "%s.%ucol.%s", prefix, term->cols, ext); if (fexistcase(path)) return true; // Display specified MINIMUM width file @@ -345,12 +347,12 @@ bool sbbs_t::menu_exists(const char *code, const char* ext, char* path) char term[MAX_PATH + 1]; safe_snprintf(term, sizeof(term), ".%s", ext); size_t skip = safe_snprintf(path, MAX_PATH, "%s.c", prefix); - int max = 0; + unsigned max = 0; for (size_t i = 0; i < g.gl_pathc; i++) { - int c = strtol(g.gl_pathv[i] + skip, &p, 10); + unsigned long c = strtoul(g.gl_pathv[i] + skip, &p, 10); if (stricmp(p, term) != 0) // Some other weird pattern ending in c*.<ext> continue; - if (c <= cols && c > max) { + if (c <= this->term->cols && c > max) { max = c; safe_snprintf(path, MAX_PATH, "%s", g.gl_pathv[i]); } diff --git a/src/sbbs3/putmsg.cpp b/src/sbbs3/putmsg.cpp index bdbb7dcd2a42c52ff7c49e9629cd1a49cc75344a..d36318d124b42c2285a4b63ebc32e45b4e550411 100644 --- a/src/sbbs3/putmsg.cpp +++ b/src/sbbs3/putmsg.cpp @@ -42,7 +42,7 @@ char sbbs_t::putmsg(const char *buf, int mode, int org_cols, JSObject* obj) uint orgcon = console; uint sys_status_sav = sys_status; uint rainbow_sav[LEN_RAINBOW + 1]; - enum output_rate output_rate = cur_output_rate; + enum output_rate output_rate = term->cur_output_rate; attr_sp = 0; /* clear any saved attributes */ tmpatr = curatr; /* was lclatr(-1) */ @@ -52,17 +52,20 @@ char sbbs_t::putmsg(const char *buf, int mode, int org_cols, JSObject* obj) sys_status |= SS_PAUSEOFF; memcpy(rainbow_sav, rainbow, sizeof rainbow_sav); + ansiParser.reset(); char ret = putmsgfrag(buf, mode, org_cols, obj); + if (ansiParser.current_state() != ansiState_none) + lprintf(LOG_DEBUG, "Incomplete ANSI stripped from end"); memcpy(rainbow, rainbow_sav, sizeof rainbow); if (!(mode & P_SAVEATR)) { console = orgcon; attr(tmpatr); } - if (!(mode & P_NOATCODES) && cur_output_rate != output_rate) - set_output_rate(output_rate); + 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 */ @@ -76,7 +79,7 @@ char sbbs_t::putmsg(const char *buf, int mode, int org_cols, JSObject* obj) } // Print a message fragment, doesn't save/restore any console states (e.g. attributes, auto-pause) -char sbbs_t::putmsgfrag(const char* buf, int& mode, int org_cols, JSObject* obj) +char sbbs_t::putmsgfrag(const char* buf, int& mode, unsigned org_cols, JSObject* obj) { char tmp[256]; char tmp2[256]; @@ -85,17 +88,17 @@ char sbbs_t::putmsgfrag(const char* buf, int& mode, int org_cols, JSObject* obj) uchar exatr = 0; char mark = '\0'; int i; - int col = column; + unsigned col = term->column; uint l = 0; uint lines_printed = 0; struct mouse_hotspot hot_spot = {}; + bool lfisnl; hot_attr = 0; hungry_hotspots = true; str = auto_utf8(str, mode); size_t len = strlen(str); - int term = term_supports(); if (!(mode & P_NOATCODES) && memcmp(str, "@WRAPOFF@", 9) == 0) { mode &= ~P_WORDWRAP; l += 9; @@ -110,7 +113,7 @@ char sbbs_t::putmsgfrag(const char* buf, int& mode, int org_cols, JSObject* obj) char *wrapped; if (org_cols < TERM_COLS_MIN) org_cols = TERM_COLS_DEFAULT; - if ((wrapped = ::wordwrap((char*)str + l, cols - 1, org_cols - 1, /* handle_quotes: */ TRUE + if ((wrapped = ::wordwrap((char*)str + l, term->cols - 1, org_cols - 1, /* handle_quotes: */ TRUE , /* is_utf8: */ INT_TO_BOOL(mode & P_UTF8) , /* pipe_codes: */(cfg.sys_misc & SM_RENEGADE) && !INT_TO_BOOL(mode & P_NOXATTRS))) == NULL) errormsg(WHERE, ERR_ALLOC, "wordwrap buffer", 0); @@ -139,18 +142,18 @@ char sbbs_t::putmsgfrag(const char* buf, int& mode, int org_cols, JSObject* obj) } // fallthrough default: // printing char - if ((mode & P_INDENT) && column < col) - cursor_right(col - column); - else if ((mode & P_TRUNCATE) && column >= (cols - 1)) { + if ((mode & P_INDENT) && term->column < col) + term->cursor_right(col - term->column); + else if ((mode & P_TRUNCATE) && term->column >= (term->cols - 1)) { l++; continue; } else if (mode & P_WRAP) { if (org_cols) { - if (column > (org_cols - 1)) { + if (term->column > (org_cols - 1)) { CRLF; } } else { - if (column >= (cols - 1)) { + if (term->column >= (term->cols - 1)) { CRLF; } } @@ -220,18 +223,18 @@ char sbbs_t::putmsgfrag(const char* buf, int& mode, int org_cols, JSObject* obj) else if (str[l + 1] == '~') { l += 2; if (str[l] >= ' ') - add_hotspot(str[l], /* hungry: */ true); + term->add_hotspot(str[l], /* hungry: */ true); else - add_hotspot('\r', /* hungry: */ true); + term->add_hotspot('\r', /* hungry: */ true); } else if (str[l + 1] == '`' && str[l + 2] >= ' ') { - add_hotspot(str[l + 2], /* hungry: */ false); + term->add_hotspot(str[l + 2], /* hungry: */ false); l += 2; } else { - bool was_tos = (row == 0); + bool was_tos = (term->row == 0); ctrl_a(str[l + 1]); - if (row == 0 && !was_tos && (sys_status & SS_ABORT) && !lines_printed) /* Aborted at (auto) pause prompt (e.g. due to CLS)? */ + if (term->row == 0 && !was_tos && (sys_status & SS_ABORT) && !lines_printed) /* Aborted at (auto) pause prompt (e.g. due to CLS)? */ sys_status &= ~SS_ABORT; /* Clear the abort flag (keep displaying the msg/file) */ l += 2; } @@ -375,26 +378,78 @@ char sbbs_t::putmsgfrag(const char* buf, int& mode, int org_cols, JSObject* obj) l += 2; } else { + lfisnl = false; if (!(mode & P_PETSCII) && str[l] == '\n') { if (exatr) /* clear at newline for extra attr codes */ attr(LIGHTGRAY); if (l == 0 || str[l - 1] != '\r') /* expand sole LF to CR/LF */ - outchar('\r'); + lfisnl = true; lines_printed++; } - /* ansi escape sequence */ - if (outchar_esc >= ansiState_csi) { - if (str[l] == 'A' || str[l] == 'B' || str[l] == 'H' || str[l] == 'J' - || str[l] == 'f' || str[l] == 'u') /* ANSI anim */ - lncntr = 0; /* so defeat pause */ - if (str[l] == '"' || str[l] == 'c') { - l++; /* don't pass on keyboard reassignment or Device Attributes (DA) requests */ - continue; + /* + * ansi escape sequence: + * Strip broken sequences + * Strip "dangerous" sequences + * Reset line counter on sequences that may change the row + * (The last is done that way for backward compatibility) + */ + if (term->supports(ANSI)) { + switch (ansiParser.parse(str[l])) { + case ansiState_broken: + // TODO: Maybe just strip the CSI or something? + lprintf(LOG_DEBUG, "Stripping broken ANSI sequence \"%s\"", ansiParser.ansi_sequence.c_str()); + ansiParser.reset(); + // break here prints the first non-valid character + break; + case ansiState_none: + break; + case ansiState_final: + if ((!ansiParser.ansi_was_private) && ansiParser.ansi_final_byte == 'p') + lprintf(LOG_DEBUG, "Stripping SKR sequence"); + else if (ansiParser.ansi_was_private && ansiParser.ansi_params[0] == '?' && ansiParser.ansi_final_byte == 'S') + lprintf(LOG_DEBUG, "Stripping XTSRGA sequence"); + else if (ansiParser.ansi_final_byte == 'n') + lprintf(LOG_DEBUG, "Stripping DSR sequence"); + else if (ansiParser.ansi_final_byte == 'c') + lprintf(LOG_DEBUG, "Stripping DA sequence"); + else if (ansiParser.ansi_ibs == "," && ansiParser.ansi_final_byte == 'q') + lprintf(LOG_DEBUG, "Stripping DECTID sequence"); + else if (ansiParser.ansi_ibs == "&" && ansiParser.ansi_final_byte == 'u') + lprintf(LOG_DEBUG, "Stripping DECRQUPSS sequence"); + else if (ansiParser.ansi_ibs == "+" && ansiParser.ansi_final_byte == 'x') + lprintf(LOG_DEBUG, "Stripping DECRQPKFM sequence"); + else if (ansiParser.ansi_ibs == "$" && ansiParser.ansi_final_byte == 'p') + lprintf(LOG_DEBUG, "Stripping DECRQM sequence"); + else if (ansiParser.ansi_ibs == "$" && ansiParser.ansi_final_byte == 'u') + lprintf(LOG_DEBUG, "Stripping DECRQTSR sequence"); + else if (ansiParser.ansi_ibs == "$" && ansiParser.ansi_final_byte == 'w') + lprintf(LOG_DEBUG, "Stripping DECRQPSR sequence"); + else if (ansiParser.ansi_ibs == "*" && ansiParser.ansi_final_byte == 'y') + lprintf(LOG_DEBUG, "Stripping DECRQCRA sequence"); + else if (ansiParser.ansi_sequence.substr(0, 4) == "\x1bP$q") + lprintf(LOG_DEBUG, "Stripping DECRQSS sequence"); + else if (ansiParser.ansi_sequence.substr(0, 14) == "\x1b_SyncTERM:C;L") + lprintf(LOG_DEBUG, "Stripping CTSFI sequence"); + else if (ansiParser.ansi_sequence.substr(0, 16) == "\x1b_SyncTERM:Q;JXL") + lprintf(LOG_DEBUG, "Stripping CTQJS sequence"); + else { + if ((!ansiParser.ansi_was_private) && ansiParser.ansi_ibs == "") { + if (strchr("AFkBEeHfJdu", ansiParser.ansi_final_byte) != nullptr) /* ANSI anim */ + term->lncntr = 0; /* so defeat pause */ + } + term_out(ansiParser.ansi_sequence.c_str(), ansiParser.ansi_sequence.length()); + } + ansiParser.reset(); + l++; + continue; + default: + l++; + continue; } } if (str[l] == '!' && str[l + 1] == '|' && useron.misc & RIP) /* RIP */ - lncntr = 0; /* so defeat pause */ + term->lncntr = 0; /* so defeat pause */ if (str[l] == '@' && !(mode & P_NOATCODES)) { if (memcmp(str + l, "@EOF@", 5) == 0) break; @@ -413,7 +468,7 @@ char sbbs_t::putmsgfrag(const char* buf, int& mode, int org_cols, JSObject* obj) tmp[i++] = str[l++]; tmp[i] = 0; truncsp(tmp); - center(expand_atcodes(tmp, tmp2, sizeof tmp2)); + term->center(expand_atcodes(tmp, tmp2, sizeof tmp2)); if (str[l] == '\r') l++; if (str[l] == '\n') @@ -468,10 +523,10 @@ char sbbs_t::putmsgfrag(const char* buf, int& mode, int org_cols, JSObject* obj) continue; } } - bool was_tos = (row == 0); + bool was_tos = (term->row == 0); i = show_atcode((char *)str + l, obj); /* returns 0 if not valid @ code */ l += i; /* i is length of code string */ - if (row > 0 && !was_tos && (sys_status & SS_ABORT) && !lines_printed) /* Aborted at (auto) pause prompt (e.g. due to CLS)? */ + if (term->row > 0 && !was_tos && (sys_status & SS_ABORT) && !lines_printed) /* Aborted at (auto) pause prompt (e.g. due to CLS)? */ sys_status &= ~SS_ABORT; /* Clear the abort flag (keep displaying the msg/file) */ if (i) /* if valid string, go to top */ continue; @@ -480,69 +535,33 @@ char sbbs_t::putmsgfrag(const char* buf, int& mode, int org_cols, JSObject* obj) break; if (hot_attr) { if (curatr == hot_attr && str[l] > ' ') { - hot_spot.y = row; + hot_spot.y = term->row; if (!hot_spot.minx) - hot_spot.minx = column; - hot_spot.maxx = column; + hot_spot.minx = term->column; + hot_spot.maxx = term->column; hot_spot.cmd[strlen(hot_spot.cmd)] = str[l]; } else if (hot_spot.cmd[0]) { hot_spot.hungry = hungry_hotspots; - add_hotspot(&hot_spot); + term->add_hotspot(&hot_spot); memset(&hot_spot, 0, sizeof(hot_spot)); } } size_t skip = sizeof(char); if (mode & P_PETSCII) { - if (term & PETSCII) { - outcom(str[l]); - switch ((uchar)str[l]) { - case '\r': // PETSCII "Return" / new-line - column = 0; - /* fall-through */ - case PETSCII_DOWN: - lncntr++; - break; - case PETSCII_CLEAR: - case PETSCII_HOME: - row = 0; - column = 0; - 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: - inc_column(1); - break; - } + if (term->charset() == CHARSET_PETSCII) { + term_out(str[l]); } else petscii_to_ansibbs(str[l]); } else if ((str[l] & 0x80) && (mode & P_UTF8)) { - if (term & UTF8) - outcom(str[l]); + if (term->charset() == CHARSET_UTF8) + term_out(str[l]); else skip = print_utf8_as_cp437(str + l, len - l); + } else if (str[l] == '\r' && str[l + 1] == '\n') { + term->newline(); + skip++; + } else if (str[l] == '\n' && lfisnl) { + term->newline(); } else { uint atr = curatr; outchar(str[l]); diff --git a/src/sbbs3/qwk.cpp b/src/sbbs3/qwk.cpp index e9941739be0de0fdced1cb73ff961e959a6a8552..0428e6962f277fd5b9da889f2c9601480601b53f 100644 --- a/src/sbbs3/qwk.cpp +++ b/src/sbbs3/qwk.cpp @@ -436,63 +436,63 @@ void sbbs_t::qwk_sec() while (online) { CLS; bprintf(text[QWKSettingsHdr], useron.alias, useron.number); - add_hotspot('A'); + term->add_hotspot('A'); bprintf(text[QWKSettingsCtrlA] , useron.qwk & QWK_EXPCTLA ? "Expand to ANSI" : useron.qwk & QWK_RETCTLA ? "Leave in" : "Strip"); - add_hotspot('T'); + term->add_hotspot('T'); bprintf(text[QWKSettingsArchive], useron.tmpext); - add_hotspot('E'); + term->add_hotspot('E'); bprintf(text[QWKSettingsEmail] , useron.qwk & QWK_EMAIL ? "Un-read Only" : useron.qwk & QWK_ALLMAIL ? text[Yes] : text[No]); if (useron.qwk & (QWK_ALLMAIL | QWK_EMAIL)) { - add_hotspot('I'); + term->add_hotspot('I'); bprintf(text[QWKSettingsAttach] , useron.qwk & QWK_ATTACH ? text[Yes] : text[No]); - add_hotspot('D'); + term->add_hotspot('D'); bprintf(text[QWKSettingsDeleteEmail] , useron.qwk & QWK_DELMAIL ? text[Yes]:text[No]); } - add_hotspot('F'); + term->add_hotspot('F'); bprintf(text[QWKSettingsNewFilesList] , useron.qwk & QWK_FILES ? text[Yes]:text[No]); - add_hotspot('N'); + term->add_hotspot('N'); bprintf(text[QWKSettingsIndex] , useron.qwk & QWK_NOINDEX ? text[No]:text[Yes]); - add_hotspot('C'); + term->add_hotspot('C'); bprintf(text[QWKSettingsControl] , useron.qwk & QWK_NOCTRL ? text[No]:text[Yes]); - add_hotspot('V'); + term->add_hotspot('V'); bprintf(text[QWKSettingsVoting] , useron.qwk & QWK_VOTING ? text[Yes]:text[No]); - add_hotspot('H'); + term->add_hotspot('H'); bprintf(text[QWKSettingsHeaders] , useron.qwk & QWK_HEADERS ? text[Yes]:text[No]); - add_hotspot('Y'); + term->add_hotspot('Y'); bprintf(text[QWKSettingsBySelf] , useron.qwk & QWK_BYSELF ? text[Yes]:text[No]); - add_hotspot('Z'); + term->add_hotspot('Z'); bprintf(text[QWKSettingsTimeZone] , useron.qwk & QWK_TZ ? text[Yes]:text[No]); - add_hotspot('P'); + term->add_hotspot('P'); bprintf(text[QWKSettingsVIA] , useron.qwk & QWK_VIA ? text[Yes]:text[No]); - add_hotspot('M'); + term->add_hotspot('M'); bprintf(text[QWKSettingsMsgID] , useron.qwk & QWK_MSGID ? text[Yes]:text[No]); - add_hotspot('U'); + term->add_hotspot('U'); bprintf(text[QWKSettingsUtf8] , useron.qwk & QWK_UTF8 ? text[Yes]:text[No]); - add_hotspot('W'); + term->add_hotspot('W'); bprintf(text[QWKSettingsWrapText] , useron.qwk & QWK_WORDWRAP ? text[Yes]:text[No]); - add_hotspot('X'); + term->add_hotspot('X'); bprintf(text[QWKSettingsExtended] , useron.qwk & QWK_EXT ? text[Yes]:text[No]); bputs(text[QWKSettingsWhich]); - add_hotspot('Q'); + term->add_hotspot('Q'); ch = (char)getkeys("AEDFHIOPQTUYMNCXZVW", 0); if (sys_status & SS_ABORT || !ch || ch == 'Q' || !online) break; @@ -581,7 +581,7 @@ void sbbs_t::qwk_sec() putuserqwk(useron.number, useron.qwk); } delfiles(cfg.temp_dir, ALLFILES); - clear_hotspots(); + term->clear_hotspots(); continue; } diff --git a/src/sbbs3/readmail.cpp b/src/sbbs3/readmail.cpp index dadd798aaed058e6d310c60ab3b31064a8dbcf53..925a95ee13c7f6998c615b8657bf35af084d5245 100644 --- a/src/sbbs3/readmail.cpp +++ b/src/sbbs3/readmail.cpp @@ -653,14 +653,14 @@ int sbbs_t::readmail(uint usernumber, int which, int lm_mode) break; case TERM_KEY_HOME: smb.curmsg = 0; - newline(); + term->newline(); break; case TERM_KEY_END: smb.curmsg = smb.msgs - 1; - newline(); + term->newline(); break; case TERM_KEY_RIGHT: - newline(); + term->newline(); // fall-through case 0: case '+': @@ -670,7 +670,7 @@ int sbbs_t::readmail(uint usernumber, int which, int lm_mode) done = 1; break; case TERM_KEY_LEFT: - newline(); + term->newline(); // fall-through case '-': if (smb.curmsg > 0) diff --git a/src/sbbs3/readmsgs.cpp b/src/sbbs3/readmsgs.cpp index d725251b016d31e52de7d69affc4377c9b811545..edb530058308048cf3c588d2da6e33ceb4f766b9 100644 --- a/src/sbbs3/readmsgs.cpp +++ b/src/sbbs3/readmsgs.cpp @@ -97,7 +97,7 @@ int sbbs_t::listmsgs(int subnum, int mode, post_t *post, int start, int posts, b void sbbs_t::dump_msghdr(smbmsg_t* msg) { - newline(); + term->newline(); str_list_t list = smb_msghdr_str_list(msg); if (list != NULL) { for (int i = 0; list[i] != NULL && !msgabort(); i++) { @@ -379,8 +379,8 @@ void sbbs_t::show_thread(uint32_t msgnum, post_t* post, unsigned curmsg, int thr // ,msg.hdr.number , (unsigned)i == curmsg ? '>' : ':'); bprintf("\1w%-*.*s\1g%c\1g%c \1w%s\r\n" - , (int)(cols - column - 12) - , (int)(cols - column - 12) + , (int)(term->cols - term->column - 12) + , (int)(term->cols - term->column - 12) , msg.hdr.attr & MSG_ANONYMOUS && !sub_op(smb.subnum) ? text[Anonymous] : msghdr_field(&msg, msg.from) , (unsigned)i == curmsg ? '<' : ' ' @@ -447,7 +447,7 @@ int sbbs_t::scanposts(int subnum, int mode, const char *find) } ZERO_VAR(msg); /* init to NULL, specify not-allocated */ if (!(mode & SCAN_CONT)) - lncntr = 0; + term->lncntr = 0; if ((msgs = getlastmsg(subnum, &last, 0)) == 0) { if (mode & (SCAN_NEW | SCAN_TOYOU)) bprintf(text[NScanStatusFmt] @@ -496,7 +496,7 @@ int sbbs_t::scanposts(int subnum, int mode, const char *find) for (smb.curmsg = 0; smb.curmsg < smb.msgs; smb.curmsg++) if (subscan[subnum].ptr < post[smb.curmsg].idx.number) break; - lncntr = 0; + term->lncntr = 0; bprintf(text[NScanStatusFmt] , cfg.grp[cfg.sub[subnum]->grp]->sname, cfg.sub[subnum]->lname, smb.msgs - smb.curmsg, msgs); if (!smb.msgs) { /* no messages at all */ @@ -516,8 +516,8 @@ int sbbs_t::scanposts(int subnum, int mode, const char *find) } } else { - cleartoeol(); - lncntr = 0; + term->cleartoeol(); + term->lncntr = 0; if (mode & SCAN_TOYOU) bprintf(text[NScanStatusFmt] , cfg.grp[cfg.sub[subnum]->grp]->sname, cfg.sub[subnum]->lname, smb.msgs, msgs); @@ -649,7 +649,7 @@ int sbbs_t::scanposts(int subnum, int mode, const char *find) break; } bprintf("\1n\1l\1h\1bThread\1n\1b: \1h\1c"); - bprintf("%-.*s\r\n", (int)(cols - (column + 1)), msghdr_field(&msg, msg.subj)); + bprintf("%-.*s\r\n", (int)(term->cols - (term->column + 1)), msghdr_field(&msg, msg.subj)); show_thread(first, post, smb.curmsg); subscan[subnum].last = post[smb.curmsg].idx.number; } @@ -783,8 +783,8 @@ int sbbs_t::scanposts(int subnum, int mode, const char *find) sync(); if (unvalidated < smb.curmsg) bprintf(text[UnvalidatedWarning], unvalidated + 1); - if (lncntr >= rows - 2) - lncntr--; + if (term->lncntr >= term->rows - 2) + term->lncntr--; bprintf(text[ReadingSub], ugrp, cfg.grp[cfg.sub[subnum]->grp]->sname , usub, cfg.sub[subnum]->sname, smb.curmsg + 1, smb.msgs); snprintf(str, sizeof str, "ABCDEFHILMNPQRTUVY?*<>[]{}-+()\b%c%c%c%c" @@ -924,7 +924,7 @@ int sbbs_t::scanposts(int subnum, int mode, const char *find) SAFEPRINTF2(str, "removed post from %s %s" , cfg.grp[cfg.sub[subnum]->grp]->sname, cfg.sub[subnum]->lname); logline("P-", str); - center(text[Deleted]); + term->center(text[Deleted]); if (!stricmp(cfg.sub[subnum]->misc & SUB_NAME ? useron.name : useron.alias, msg.from)) useron.posts = (ushort)adjustuserval(&cfg, useron.number, USER_POSTS, -1); @@ -1387,7 +1387,7 @@ int sbbs_t::scanposts(int subnum, int mode, const char *find) { if (!thread_mode) { smb.curmsg = 0; - newline(); + term->newline(); break; } uint32_t first = smb_first_in_thread(&smb, &msg, NULL); @@ -1405,7 +1405,7 @@ int sbbs_t::scanposts(int subnum, int mode, const char *find) { if (!thread_mode) { smb.curmsg = smb.msgs - 1; - newline(); + term->newline(); break; } uint32_t last = smb_last_in_thread(&smb, &msg); @@ -1449,7 +1449,7 @@ int sbbs_t::scanposts(int subnum, int mode, const char *find) smb.curmsg++; else done = 1; - newline(); + term->newline(); break; } l = msg.hdr.thread_first; @@ -1475,7 +1475,7 @@ int sbbs_t::scanposts(int subnum, int mode, const char *find) if (!thread_mode) { if (smb.curmsg > 0) smb.curmsg--; - newline(); + term->newline(); break; } case '(': /* Thread backwards */ diff --git a/src/sbbs3/sbbs.h b/src/sbbs3/sbbs.h index d2d60895d9964657f8765fd6243da44af0918f44..c94b08a2e41294d05e81eede4c581f49af719e74 100644 --- a/src/sbbs3/sbbs.h +++ b/src/sbbs3/sbbs.h @@ -276,6 +276,7 @@ extern int thread_suid_broken; /* NPTL is no longer broken */ #include "startup.h" #ifdef __cplusplus #include "threadwrap.h" /* pthread_mutex_t */ + #include "ansi_parser.h" #endif #include "smblib.h" @@ -403,13 +404,7 @@ typedef struct js_callback { #include <string> #include <unordered_map> -struct mouse_hotspot { // Mouse hot-spot - char cmd[128]; - int y; - int minx; - int maxx; - bool hungry; -}; +class Terminal; enum sftp_dir_tree { SFTP_DTREE_FULL, @@ -478,23 +473,14 @@ public: std::atomic<bool> ssh_mode{false}; bool term_output_disabled{}; - SOCKET passthru_socket=INVALID_SOCKET; - bool passthru_socket_active = false; - void passthru_socket_activate(bool); - bool passthru_thread_running = false; + SOCKET passthru_socket=INVALID_SOCKET; + bool passthru_socket_active = false; + void passthru_socket_activate(bool); + bool passthru_thread_running = false; scfg_t cfg{}; struct mqtt* mqtt = nullptr; - - enum ansiState { - ansiState_none // No sequence - ,ansiState_esc // Escape - ,ansiState_csi // CSI - ,ansiState_final // Final byte - ,ansiState_string // APS, DCS, PM, or OSC - ,ansiState_sos // SOS - ,ansiState_sos_esc // ESC inside SOS - } outchar_esc = ansiState_none; // track ANSI escape seq output + Terminal *term{nullptr}; int rioctl(ushort action); // remote i/o control bool rio_abortable = false; @@ -606,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); @@ -621,7 +607,7 @@ public: uint socket_inactive=0; // Socket inactivity counter (watchdog), in seconds, incremented by input_thread() uint max_socket_inactivity=0; // Socket inactivity limit (in seconds), enforced by input_thread() bool socket_inactivity_warning_sent=false; - uint curatr = LIGHTGRAY; /* Current Text Attributes Always */ + uint curatr = LIGHTGRAY; /* Last Attributes requested by attr() */ uint attr_stack[64]{}; /* Saved attributes (stack) */ int attr_sp = 0; /* Attribute stack pointer */ uint mneattr_low = LIGHTGRAY; @@ -630,22 +616,10 @@ public: uint rainbow[LEN_RAINBOW + 1]{}; bool rainbow_repeat = false; int rainbow_index = -1; - int lncntr = 0; /* Line Counter - for PAUSE */ bool msghdr_tos = false; /* Message header was displayed at Top of Screen */ - int row=0; /* Current row */ - int rows=0; /* Current number of Rows for User */ - int cols=0; /* Current number of Columns for User */ - int column = 0; /* Current column counter (for line counter) */ - int tabstop = 8; /* Current symmetric-tabstop (size) */ - int lastlinelen = 0; /* The previously displayed line length */ int autoterm=0; /* Auto-detected terminal type */ size_t unicode_zerowidth=0; char terminal[TELNET_TERM_MAXLEN+1]{}; // <- answer() writes to this - int cterm_version=0;/* (MajorVer*1000) + MinorVer */ - link_list_t savedlines{}; - char lbuf[LINE_BUFSIZE+1]{};/* Temp storage for each line output */ - int lbuflen = 0; /* Number of characters in line buffer */ - uint latr=0; /* Starting attribute of line buffer */ uint line_delay=0; /* Delay duration (ms) after each line sent */ uint console = 0; /* Defines current Console settings */ char wordwrap[TERM_COLS_MAX + 1]{}; /* Word wrap buffer */ @@ -722,23 +696,6 @@ public: long sysvar_l[MAX_SYSVARS]{}; uint sysvar_li = 0; - /* ansi_term.cpp */ - const char* ansi(int atr); /* Returns ansi escape sequence for atr */ - char* ansi(int atr, int curatr, char* str); - bool ansi_gotoxy(int x, int y); - bool ansi_getxy(int* x, int* y); - bool ansi_save(void); - bool ansi_restore(void); - bool ansi_getdims(void); - enum ansi_mouse_mode { - ANSI_MOUSE_X10 = 9, - ANSI_MOUSE_NORM = 1000, - ANSI_MOUSE_BTN = 1002, - ANSI_MOUSE_ANY = 1003, - ANSI_MOUSE_EXT = 1006 - }; - int ansi_mouse(enum ansi_mouse_mode, bool enable); - /* Command Shell Methods */ int exec(csi_t *csi); int exec_function(csi_t *csi); @@ -853,9 +810,10 @@ public: /* putmsg.cpp */ char putmsg(const char *str, int mode, int org_cols = 0, JSObject* obj = NULL); - char putmsgfrag(const char* str, int& mode, int org_cols = 0, JSObject* obj = NULL); + char putmsgfrag(const char* str, int& mode, unsigned org_cols = 0, JSObject* obj = NULL); bool putnmsg(int node_num, const char*); bool putsmsg(int user_num, const char*); + ANSI_Parser ansiParser{}; /* writemsg.cpp */ void automsg(void); @@ -917,10 +875,9 @@ public: int bulkmailhdr(smb_t*, smbmsg_t*, uint usernum); /* con_out.cpp */ - size_t bstrlen(const char *str, int mode = 0); 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 @@ -937,37 +894,17 @@ 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 ; - void backspace(int count=1); /* Output destructive backspace(s) via outchar */ 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); - void inc_row(int count); - void inc_column(int count); - void center(const char *str, bool msg = false, unsigned int columns = 0); + bool check_pause(); /* Check lncntr to and pause() if appropriate */ + int outcp(enum unicode_codepoint, char cp437_fallback); + int outcp(enum unicode_codepoint, const char* cp437_fallback = NULL); void wide(const char*); - void clearscreen(int term); - void clearline(void); - void cleartoeol(void); - void cleartoeos(void); - void cursor_home(void); - void cursor_up(int count=1); - void cursor_down(int count=1); - void cursor_left(int count=1); - void cursor_right(int count=1); - bool cursor_xy(int x, int y); - bool cursor_getxy(int* x, int* y); - void carriage_return(int count=1); - void line_feed(int count=1); - void newline(int count=1); - void cond_newline() { if(column > 0) newline(); } - void cond_blankline() { if(column > 0) newline(); if(lastlinelen) newline(); } - void cond_contline() { if(column > 0 && cols < TERM_COLS_DEFAULT) bputs(text[LongLineContinuationPrefix]); } - int term_supports(int cmp_flags=0); + // These are user settings, not terminal properties. char* term_rows(user_t*, char* str, size_t); char* term_cols(user_t*, char* str, size_t); char* term_type(user_t*, int term, char* str, size_t); @@ -977,35 +914,21 @@ public: int backfill(const char* str, float pct, int full_attr, int empty_attr); void progress(const char* str, int count, int total, int interval = 500); double last_progress = 0; - bool saveline(void); - bool restoreline(void); int petscii_to_ansibbs(unsigned char); size_t print_utf8_as_cp437(const char*, size_t); int attr(int); /* Change text color/attributes */ void ctrl_a(char); /* Performs Ctrl-Ax attribute changes */ char* auto_utf8(const char*, int& mode); - enum output_rate { - output_rate_unlimited, - output_rate_300 = 300, - output_rate_600 = 600, - output_rate_1200 = 1200, - output_rate_2400 = 2400, - output_rate_4800 = 4800, - output_rate_9600 = 9600, - output_rate_19200 = 19200, - output_rate_38400 = 38400, - output_rate_57600 = 57600, - output_rate_76800 = 76800, - output_rate_115200 = 115200, - } cur_output_rate = output_rate_unlimited; - void set_output_rate(enum output_rate); 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; size_t getstr(char *str, size_t length, int mode, const str_list_t history = NULL); int getnum(uint max, uint dflt=0); - void insert_indicator(void); /* getkey.cpp */ char getkey(int mode = K_NONE); @@ -1021,6 +944,7 @@ public: void mnemonics(const char *str); /* inkey.cpp */ + bool last_inkey_was_esc{false}; // Used by auto-ANSI detection int inkey(int mode = K_NONE, unsigned int timeout=0); char handle_ctrlkey(char ch, int mode=0); @@ -1036,16 +960,6 @@ public: int mouse_mode = MOUSE_MODE_OFF; // Mouse reporting mode flags uint hot_attr = 0; // Auto-Mouse hot-spot attribute (when non-zero) bool hungry_hotspots = true; - link_list_t mouse_hotspots{}; // Mouse hot-spots - struct mouse_hotspot* pause_hotspot = nullptr; - struct mouse_hotspot* add_hotspot(struct mouse_hotspot*); - struct mouse_hotspot* add_hotspot(char cmd, bool hungry = true, int minx = -1, int maxx = -1, int y = -1); - struct mouse_hotspot* add_hotspot(int num, bool hungry = true, int minx = -1, int maxx = -1, int y = -1); - struct mouse_hotspot* add_hotspot(uint num, bool hungry = true, int minx = -1, int maxx = -1, int y = -1); - struct mouse_hotspot* add_hotspot(const char* cmd, bool hungry = true, int minx = -1, int maxx = -1, int y = -1); - void clear_hotspots(void); - void scroll_hotspots(int count); - void set_mouse(int mode); // Thread-safe std/socket errno description getters char strerror_buf[256]{}; @@ -1361,6 +1275,8 @@ public: }; +#include "terminal.h" + #endif /* __cplusplus */ #ifdef DLLEXPORT @@ -1383,8 +1299,6 @@ public: #ifdef __cplusplus extern "C" { #endif - /* ansiterm.cpp */ - DLLEXPORT char* ansi_attr(int attr, int curattr, char* str, bool color); /* main.cpp */ extern const char* nulstr; diff --git a/src/sbbs3/sbbs.jsdocs.vcxproj b/src/sbbs3/sbbs.jsdocs.vcxproj index 1291236adc1a70438e0fb7bbb206e04dc87c4509..38fbfc21794e0c1ad459d8d1579d07ca7016f7bc 100644 --- a/src/sbbs3/sbbs.jsdocs.vcxproj +++ b/src/sbbs3/sbbs.jsdocs.vcxproj @@ -187,12 +187,6 @@ <ClCompile Include="..\encode\utf8.c" /> <ClCompile Include="..\encode\uucode.c" /> <ClCompile Include="..\encode\yenc.c" /> - <ClCompile Include="ansiterm.cpp"> - <AdditionalIncludeDirectories Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories> - <PreprocessorDefinitions Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">%(PreprocessorDefinitions)</PreprocessorDefinitions> - <AdditionalIncludeDirectories Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories> - <PreprocessorDefinitions Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">%(PreprocessorDefinitions)</PreprocessorDefinitions> - </ClCompile> <ClCompile Include="answer.cpp"> <AdditionalIncludeDirectories Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories> <PreprocessorDefinitions Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">%(PreprocessorDefinitions)</PreprocessorDefinitions> @@ -833,4 +827,4 @@ <Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" /> <ImportGroup Label="ExtensionTargets"> </ImportGroup> -</Project> \ No newline at end of file +</Project> diff --git a/src/sbbs3/sbbs.vcxproj b/src/sbbs3/sbbs.vcxproj index de2979125cb562825c35bb8c4cb4570bab36affa..a328b73f2bfcef365d4aa60b735aa7a1594526d2 100644 --- a/src/sbbs3/sbbs.vcxproj +++ b/src/sbbs3/sbbs.vcxproj @@ -185,12 +185,8 @@ <ClCompile Include="..\encode\utf8.c" /> <ClCompile Include="..\encode\uucode.c" /> <ClCompile Include="..\encode\yenc.c" /> - <ClCompile Include="ansiterm.cpp"> - <AdditionalIncludeDirectories Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories> - <PreprocessorDefinitions Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">%(PreprocessorDefinitions)</PreprocessorDefinitions> - <AdditionalIncludeDirectories Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories> - <PreprocessorDefinitions Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">%(PreprocessorDefinitions)</PreprocessorDefinitions> - </ClCompile> + <ClCompile Include="ansi_parser.cpp" /> + <ClCompile Include="ansi_terminal.cpp" /> <ClCompile Include="answer.cpp"> <AdditionalIncludeDirectories Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories> <PreprocessorDefinitions Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">%(PreprocessorDefinitions)</PreprocessorDefinitions> @@ -596,6 +592,7 @@ <AdditionalIncludeDirectories Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories> <PreprocessorDefinitions Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">%(PreprocessorDefinitions)</PreprocessorDefinitions> </ClCompile> + <ClCompile Include="petscii_term.cpp" /> <ClCompile Include="postmsg.cpp"> <AdditionalIncludeDirectories Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories> <PreprocessorDefinitions Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">%(PreprocessorDefinitions)</PreprocessorDefinitions> @@ -719,6 +716,7 @@ <AdditionalIncludeDirectories Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories> <PreprocessorDefinitions Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">%(PreprocessorDefinitions)</PreprocessorDefinitions> </ClCompile> + <ClCompile Include="terminal.cpp" /> <ClCompile Include="text_defaults.c"> <AdditionalIncludeDirectories Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories> <PreprocessorDefinitions Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">%(PreprocessorDefinitions)</PreprocessorDefinitions> @@ -823,4 +821,4 @@ <Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" /> <ImportGroup Label="ExtensionTargets"> </ImportGroup> -</Project> \ No newline at end of file +</Project> diff --git a/src/sbbs3/sbbsdefs.h b/src/sbbs3/sbbsdefs.h index 9b78f21f3df11b5d0984ccdbd6d89b40e15fa27e..78e8a5caf608c5df74e14477285ca153b824fa7a 100644 --- a/src/sbbs3/sbbsdefs.h +++ b/src/sbbs3/sbbsdefs.h @@ -476,11 +476,11 @@ typedef enum { /* Values for xtrn_t.event */ #define EDIT_TABSIZE 4 /* Tab size for internal message/line editor */ /* Console I/O Bits (console) */ -#define CON_R_ECHO (1 << 0) /* Echo remotely */ -#define CON_R_ECHOX (1 << 1) /* Echo X's to remote user */ +#define CON_R_ECHO 0 /* Echo remotely - Unused */ +#define CON_R_ECHOX (1 << 1) /* Echo X's to remote user */ #define CON_L_ECHOX 0 // Unused #define CON_R_INPUT (1 << 2) /* Accept input remotely */ -#define CON_L_ECHO (1 << 3) /* Echo locally */ +#define CON_L_ECHO 0 /* Echo locally */ #define CON_PAUSEOFF (1 << 4) // Temporary pause over-ride (same as UPAUSE) #define CON_L_INPUT (1 << 5) /* Accept input locally */ #define CON_RAW_IN (1 << 8) /* Raw input mode - no editing capabilities */ @@ -578,7 +578,9 @@ typedef enum { /* Values for xtrn_t.event */ #define UTF8 (1 << 29) /* UTF-8 terminal */ #define MOUSE (1U << 31) /* Mouse supported terminal */ +// TODO: Really, NO_EXASCII and UTF8 are not terminal flags. #define TERM_FLAGS (ANSI | COLOR | RIP | SWAP_DELETE | ICE_COLOR | MOUSE | CHARSET_FLAGS) +// TODO: Picking these out gets tricky, PETSCII is both terminal and charset #define CHARSET_FLAGS (NO_EXASCII | PETSCII | UTF8) #define CHARSET_ASCII NO_EXASCII // US-ASCII #define CHARSET_PETSCII PETSCII // CBM-ASCII @@ -847,7 +849,7 @@ enum { /* Values of mode for userlist function */ /* Macros */ /**********/ -#define CRLF newline() +#define CRLF term->newline() #define SYSOP_LEVEL 90 #define SYSOP (useron.level >= SYSOP_LEVEL || sys_status & SS_TMPSYSOP) #define REALSYSOP (useron.level >= SYSOP_LEVEL) @@ -891,9 +893,15 @@ enum COLORS { #endif /* __COLORS */ -#define ANSI_NORMAL 0x100 +#define FG_UNKNOWN 0x100 #define BG_BLACK 0x200 #define BG_BRIGHT 0x400 // Not an IBM-CGA/ANSI.SYS compatible attribute +#define REVERSED 0x800 +#define UNDERLINE 0x1000 +#define CONCEALED 0x2000 +#define BG_UNKNOWN 0x4000 +// TODO: Do we need to keep this value compatible? +#define ANSI_NORMAL (FG_UNKNOWN | BG_UNKNOWN) #define BG_BLUE (BLUE << 4) #define BG_GREEN (GREEN << 4) #define BG_CYAN (CYAN << 4) diff --git a/src/sbbs3/scandirs.cpp b/src/sbbs3/scandirs.cpp index 0e69e8f902aa177d116a76a96735cce5b003b220..24c2b2cd77c3181f60f958d22deb6a8e07175d32 100644 --- a/src/sbbs3/scandirs.cpp +++ b/src/sbbs3/scandirs.cpp @@ -47,7 +47,7 @@ void sbbs_t::scandirs(int mode) SAFEPRINTF2(keys, "%s%c\r", text[DirLibKeys], all_key()); ch = (char)getkeys(keys, 0); if (sys_status & SS_ABORT || ch == CR) { - lncntr = 0; + term->lncntr = 0; return; } if (ch != all_key()) { @@ -62,12 +62,12 @@ void sbbs_t::scandirs(int mode) if (text[DisplayExtendedFileInfoQ][0] && !noyes(text[DisplayExtendedFileInfoQ])) mode |= FL_EXT; if (sys_status & SS_ABORT) { - lncntr = 0; + term->lncntr = 0; return; } bputs(text[SearchStringPrompt]); if (!getstr(str, 40, K_LINE | K_UPPER)) { - lncntr = 0; + term->lncntr = 0; return; } } @@ -140,12 +140,12 @@ void sbbs_t::scanalldirs(int mode) if (text[DisplayExtendedFileInfoQ][0] && !noyes(text[DisplayExtendedFileInfoQ])) mode |= FL_EXT; if (sys_status & SS_ABORT) { - lncntr = 0; + term->lncntr = 0; return; } bputs(text[SearchStringPrompt]); if (!getstr(str, 40, K_LINE | K_UPPER)) { - lncntr = 0; + term->lncntr = 0; return; } } diff --git a/src/sbbs3/scansubs.cpp b/src/sbbs3/scansubs.cpp index 3746876070de67359c11e01465b251a00b81a1c9..0d50fd4a27648b316d3220302fd49fb7aba95d56 100644 --- a/src/sbbs3/scansubs.cpp +++ b/src/sbbs3/scansubs.cpp @@ -122,7 +122,7 @@ void sbbs_t::scansubs(int mode) } if (mode & SCAN_POLLS) { progress(text[Done], subs_scanned, usrsubs[curgrp]); - cleartoeol(); + term->cleartoeol(); } bputs(text[MessageScan]); if (i == usrsubs[curgrp]) @@ -229,7 +229,7 @@ void sbbs_t::scanallsubs(int mode) free(sub); if (mode & SCAN_POLLS) { progress(text[Done], subs_scanned, total_subs); - cleartoeol(); + term->cleartoeol(); } bputs(text[MessageScan]); if (subs_scanned < total_subs) { @@ -336,7 +336,7 @@ void sbbs_t::new_scan_ptr_cfg() snprintf(keys, sizeof keys, "%c%c", all_key(), quit_key()); s = getkeys(keys, usrsubs[i]); if (sys_status & SS_ABORT) { - lncntr = 0; + term->lncntr = 0; return; } if (s == -1 || !s || s == quit_key()) @@ -460,7 +460,7 @@ void sbbs_t::new_scan_cfg(uint misc) snprintf(keys, sizeof keys, "%c%c", all_key(), quit_key()); s = getkeys(keys, usrsubs[i]); if (sys_status & SS_ABORT) { - lncntr = 0; + term->lncntr = 0; return; } if (!s || s == -1 || s == quit_key()) diff --git a/src/sbbs3/str.cpp b/src/sbbs3/str.cpp index 6f540aadefa5881f4ceee31a01b975f2a272a9de..bb445d6d332acc3d4d2a5b6ca6f730b535f3d555 100644 --- a/src/sbbs3/str.cpp +++ b/src/sbbs3/str.cpp @@ -119,7 +119,7 @@ bool sbbs_t::load_user_text(void) char path[MAX_PATH + 1]; char charset[16]; - SAFECOPY(charset, term_charset()); + SAFECOPY(charset, term->charset_str()); strlwr(charset); revert_text(); snprintf(path, sizeof path, "%s%s/text.ini", cfg.ctrl_dir, charset); @@ -340,7 +340,7 @@ void sbbs_t::sif(char *fname, char *answers, int len) m++; } if ((buf[m + 1] & 0xdf) == 'L') { /* Draw line */ - if (term_supports(COLOR)) + if (term->supports(COLOR)) attr(cfg.color[clr_inputline]); else attr(BLACK | BG_LIGHTGRAY); @@ -508,7 +508,7 @@ void sbbs_t::sof(char *fname, char *answers, int len) else if ((buf[m + 1] & 0xdf) == 'N') /* Numbers only */ m++; if ((buf[m + 1] & 0xdf) == 'L') { /* Draw line */ - if (term_supports(COLOR)) + if (term->supports(COLOR)) attr(cfg.color[clr_inputline]); else attr(BLACK | BG_LIGHTGRAY); @@ -533,7 +533,7 @@ void sbbs_t::sof(char *fname, char *answers, int len) else if ((buf[m + 1] & 0xdf) == 'N') /* Numbers only */ m++; if ((buf[m + 1] & 0xdf) == 'L') { - if (term_supports(COLOR)) + if (term->supports(COLOR)) attr(cfg.color[clr_inputline]); else attr(BLACK | BG_LIGHTGRAY); @@ -646,22 +646,22 @@ size_t sbbs_t::gettmplt(char *strout, const char *templt, int mode) sys_status &= ~SS_ABORT; SAFECOPY(tmplt, templt); strupr(tmplt); - if (term_supports(ANSI)) { - if (mode & K_LINE) { - if (term_supports(COLOR)) - attr(cfg.color[clr_inputline]); - else - attr(BLACK | BG_LIGHTGRAY); - } - while (c < t) { - if (tmplt[c] == 'N' || tmplt[c] == 'A' || tmplt[c] == '!') - outchar(' '); - else - outchar(tmplt[c]); - c++; - } - cursor_left(t); - } + // MODE7: This was ANSI-only, added support for PETSCII, + // but we may not want it for Mode7. + if (mode & K_LINE) { + if (term->supports(COLOR)) + attr(cfg.color[clr_inputline]); + else + attr(BLACK | BG_LIGHTGRAY); + } + while (c < t) { + if (tmplt[c] == 'N' || tmplt[c] == 'A' || tmplt[c] == '!') + outchar(' '); + else + outchar(tmplt[c]); + c++; + } + term->cursor_left(t); c = 0; if (mode & K_EDIT) { SAFECOPY(str, strout); @@ -675,7 +675,7 @@ size_t sbbs_t::gettmplt(char *strout, const char *templt, int mode) for (ch = 1, c--; c; c--, ch++) if (tmplt[c] == 'N' || tmplt[c] == 'A' || tmplt[c] == '!') break; - cursor_left(ch); + term->cursor_left(ch); bputs(" \b"); continue; } @@ -1120,7 +1120,7 @@ char* sbbs_t::xfer_prot_menu(enum XFER_TYPE type, user_t* user, char* keys, size bool menu_used = menu(prot_menu_file[type], P_NOERROR); if (user == nullptr) user = &useron; - cond_blankline(); + term->cond_blankline(); int printed = 0; for (int i = 0; i < cfg.total_prots; i++) { if (!chk_ar(cfg.prot[i]->ar, user, &client)) @@ -1137,7 +1137,7 @@ char* sbbs_t::xfer_prot_menu(enum XFER_TYPE type, user_t* user, char* keys, size keys[count++] = cfg.prot[i]->mnemonic; if (menu_used) continue; - if (printed && (cols < 80 || (printed % 2) == 0)) + if (printed && (term->cols < 80 || (printed % 2) == 0)) CRLF; bprintf(text[TransferProtLstFmt], cfg.prot[i]->mnemonic, cfg.prot[i]->name); printed++; @@ -1145,7 +1145,7 @@ char* sbbs_t::xfer_prot_menu(enum XFER_TYPE type, user_t* user, char* keys, size if (keys != nullptr) keys[count] = '\0'; if (!menu_used) - newline(); + term->newline(); return keys; } @@ -1298,7 +1298,7 @@ bool sbbs_t::spy(uint i /* node_num */) continue; } if (ch < ' ') { - lncntr = 0; /* defeat pause */ + term->lncntr = 0; /* defeat pause */ spy_socket[i - 1] = INVALID_SOCKET; /* disable spy output */ ch = handle_ctrlkey(ch, K_NONE); spy_socket[i - 1] = passthru_thread_running ? client_socket_dup : client_socket; /* enable spy output */ diff --git a/src/sbbs3/telgate.cpp b/src/sbbs3/telgate.cpp index 2d3481b7d042c7764c200679f1068cabadd94db3..9671e5c336f3a52b7ef625386175eb02c7742eb2 100644 --- a/src/sbbs3/telgate.cpp +++ b/src/sbbs3/telgate.cpp @@ -173,14 +173,14 @@ struct TelnetProxy buf[0] = TELNET_IAC; buf[1] = TELNET_SB; buf[2] = TELNET_NEGOTIATE_WINDOW_SIZE; - buf[3] = (sbbs->cols >> 8) & 0xff; - buf[4] = sbbs->cols & 0xff; - buf[5] = (sbbs->rows >> 8) & 0xff; - buf[6] = sbbs->rows & 0xff; + buf[3] = (sbbs->term->cols >> 8) & 0xff; + buf[4] = sbbs->term->cols & 0xff; + buf[5] = (sbbs->term->rows >> 8) & 0xff; + buf[6] = sbbs->term->rows & 0xff; buf[7] = TELNET_IAC; buf[8] = TELNET_SE; if (sbbs->startup->options & BBS_OPT_DEBUG_TELNET) - sbbs->lprintf(LOG_DEBUG, "%s: Window Size is %u x %u", __FUNCTION__, sbbs->cols, sbbs->rows); + sbbs->lprintf(LOG_DEBUG, "%s: Window Size is %u x %u", __FUNCTION__, sbbs->term->cols, sbbs->term->rows); if (::sendsocket(sock, (char *)buf, 9) != 9) lprintf(LOG_WARNING, "%s: Failed to send Window Size command", __FUNCTION__); } diff --git a/src/sbbs3/terminal.cpp b/src/sbbs3/terminal.cpp new file mode 100644 index 0000000000000000000000000000000000000000..3f24e6c5f2e253437d84acc117378b8c3fd06a0c --- /dev/null +++ b/src/sbbs3/terminal.cpp @@ -0,0 +1,467 @@ +#include "terminal.h" +#include "ansi_terminal.h" +#include "petscii_term.h" +#include "link_list.h" + +void Terminal::clear_hotspots(void) +{ + if (!(flags_ & MOUSE)) + return; + int spots = listCountNodes(mouse_hotspots); + if (spots) { +#if 0 //def _DEBUG + sbbs->lprintf(LOG_DEBUG, "Clearing %ld mouse hot spots", spots); +#endif + listFreeNodes(mouse_hotspots); + if (!(sbbs->console & CON_MOUSE_SCROLL)) + set_mouse(MOUSE_MODE_OFF); + } +} + +void Terminal::scroll_hotspots(unsigned count) +{ + if (!(flags_ & MOUSE)) + return; + unsigned spots = 0; + unsigned remain = 0; + for (list_node_t* node = mouse_hotspots->first; node != NULL; node = node->next) { + struct mouse_hotspot* spot = (struct mouse_hotspot*)node->data; + spot->y -= count; + spots++; + if (spot->y >= 0) + remain++; + } +#ifdef _DEBUG + if (spots) + sbbs->lprintf(LOG_DEBUG, "Scrolled %u mouse hot-spots %u rows (%u remain)", spots, count, remain); +#endif + if (remain < 1) + clear_hotspots(); +} + +struct mouse_hotspot* Terminal::add_hotspot(struct mouse_hotspot* spot) +{ + if (!(sbbs->cfg.sys_misc & SM_MOUSE_HOT) || !supports(MOUSE)) + return nullptr; + if (spot->y == HOTSPOT_CURRENT_Y) + spot->y = row; + if (spot->minx == HOTSPOT_CURRENT_X) + spot->minx = column; + if (spot->maxx == HOTSPOT_CURRENT_X) + spot->maxx = cols - 1; +#if 0 //def _DEBUG + char dbg[128]; + sbbs->lprintf(LOG_DEBUG, "Adding mouse hot spot %ld-%ld x %ld = '%s'" + , spot->minx, spot->maxx, spot->y, c_escape_str(spot->cmd, dbg, sizeof(dbg), /* Ctrl-only? */ true)); +#endif + list_node_t* node = listInsertNodeData(mouse_hotspots, spot, sizeof(*spot)); + if (node == nullptr) + return nullptr; + set_mouse(MOUSE_MODE_ON); + return (struct mouse_hotspot*)node->data; +} + +struct mouse_hotspot* Terminal::add_hotspot(char cmd, bool hungry, unsigned minx, unsigned maxx, unsigned y) +{ + if (!(flags_ & MOUSE)) + return nullptr; + struct mouse_hotspot spot = {}; + spot.cmd[0] = cmd; + spot.minx = minx; + spot.maxx = maxx; + spot.y = y; + spot.hungry = hungry; + return add_hotspot(&spot); +} + +bool Terminal::add_pause_hotspot(char cmd) +{ + if (!(flags_ & MOUSE)) + return false; + if (mouse_hotspots->first != nullptr) + return false; + struct mouse_hotspot spot = {}; + spot.cmd[0] = cmd; + spot.minx = column; + spot.maxx = column; + spot.y = HOTSPOT_CURRENT_Y; + spot.hungry = true; + if (add_hotspot(&spot) != nullptr) + return true; + return false; +} + +struct mouse_hotspot* Terminal::add_hotspot(int num, bool hungry, unsigned minx, unsigned maxx, unsigned y) +{ + if (!(flags_ & MOUSE)) + return nullptr; + struct mouse_hotspot spot = {}; + SAFEPRINTF(spot.cmd, "%d\r", num); + spot.minx = minx; + spot.maxx = maxx; + spot.y = y; + spot.hungry = hungry; + return add_hotspot(&spot); +} + +struct mouse_hotspot* Terminal::add_hotspot(uint num, bool hungry, unsigned minx, unsigned maxx, unsigned y) +{ + if (!(flags_ & MOUSE)) + return nullptr; + struct mouse_hotspot spot = {}; + SAFEPRINTF(spot.cmd, "%u\r", num); + spot.minx = minx; + spot.maxx = maxx; + spot.y = y; + spot.hungry = hungry; + return add_hotspot(&spot); +} + +struct mouse_hotspot* Terminal::add_hotspot(const char* cmd, bool hungry, unsigned minx, unsigned maxx, unsigned y) +{ + if (!(flags_ & MOUSE)) + return nullptr; + struct mouse_hotspot spot = {}; + SAFECOPY(spot.cmd, cmd); + spot.minx = minx; + spot.maxx = maxx; + spot.y = y; + spot.hungry = hungry; + return add_hotspot(&spot); +} + +void Terminal::inc_row(unsigned count) { + row += count; + if (row >= rows) { + scroll_hotspots((row - rows) + 1); + row = rows - 1; + } + if (lncntr || lastcrcol) + lncntr += count; + if (!suspend_lbuf) + lbuflen = 0; +} + +// TODO: ANSI does *not* specify what happens at the end of a line, and +// there are at least three common behaviours (in decresing order +// of popularity): +// 1) Do the DEC Last Column Flag thing where it hangs out in the +// last column until a printing character is received, then +// decides if it should wrap or not +// 2) Wrap immediately when printed to the last column +// 3) Stay in the last column and replace the character +// +// We assume that #2 happens here, but most terminals do #1... which +// usually looks like #2, but is slightly different. Generally, any +// terminal that does #1 can be switched to do #3, and most terminals +// that do #2 can also do #3. +// +// It's fairly simple in ANSI to detect which happens, but is not +// possible for dumb terminals (though I don't think I've ever seen +// anything but #2 there). For things like VT52, PETSCII, and Mode7, +// the behaviour is well established. +// +// We can easily emulate #2 if we know if #1 or #3 is currently +// occuring. It's possible to emulate #1 with #2 as long as +// insert character is available, though it's trickier. +// +// The problem with emulating #2 is that it scrolls the screen when +// the bottom-right cell is written to, which can have undesired +// consequences. It also requires the suppression of CRLF after +// column 80 is written, which breaks the "a line is a line" +// abstraction. +// +// The best would be to switch to #3 when possible, and emulate #1 +// The behaviour would then match the most common implementations +// and be well controlled. If only #1 is available, we can still +// always work as expected (modulo some variations between exact +// flag clearing conditions), and be able to avoid the scroll. +// Only terminals that implement #2 (rare) and don't allow switching +// to #3 (I'm not aware of any) would have problems. +// +// This would resolve the long-standing issue of printing to column +// 80 which almost every sysop that customizes their BBS has ran +// across in the past. +// +// The sticky wicket here is what to do about doors. The vast +// majority of them assume #2, but are tested with #1 they also +// generally assume 80x24. It would be interesting to have a mode +// that (optionally) centres the door output in the current +// terminal. +void Terminal::inc_column(unsigned count) { + column += count; + if (column >= cols) + lastcrcol = cols; + while (column >= cols) { + if (!suspend_lbuf) + lbuflen = 0; + column -= cols; + inc_row(); + } +} + +void Terminal::dec_row(unsigned count) { + // Never allow dec_row to scroll up + if (count > row) + count = row; +#if 0 + // NOTE: 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; + if (!suspend_lbuf) + lbuflen = 0; +} + +void Terminal::dec_column(unsigned count) { + // Never allow dec_column() to wrap + if (count > column) + count = column; + column -= count; + if (column == 0) { + if (!suspend_lbuf) + lbuflen = 0; + } +} + +void Terminal::set_row(unsigned val) { + if (val >= rows) + val = rows - 1; + row = val; + lncntr = 0; + if (!suspend_lbuf) + lbuflen = 0; +} + +void Terminal::set_column(unsigned val) { + if (val >= cols) + val = cols - 1; + column = val; +} + +void Terminal::cond_newline() { + if (column > 0) + newline(); +} + +void Terminal::cond_blankline() { + cond_newline(); + if (lastcrcol) + newline(); +} + +void Terminal::cond_contline() { + if (column > 0 && cols < TERM_COLS_DEFAULT) + sbbs->bputs(sbbs->text[LongLineContinuationPrefix]); +} + +bool Terminal::supports(unsigned cmp_flags) { + return (flags_ & cmp_flags) == cmp_flags; +} + +bool Terminal::supports_any(unsigned cmp_flags) { + return (flags_ & cmp_flags); +} + +uint32_t Terminal::charset() { + return (flags_ & CHARSET_FLAGS); +} + +const char * Terminal::charset_str() +{ + switch(charset()) { + case CHARSET_PETSCII: + return "CBM-ASCII"; + case CHARSET_UTF8: + return "UTF-8"; + case CHARSET_ASCII: + return "US-ASCII"; + default: + return "CP437"; + } +} + +list_node_t *Terminal::find_hotspot(unsigned x, unsigned y) +{ + list_node_t *node; + + for (node = mouse_hotspots->first; node != nullptr; node = node->next) { + struct mouse_hotspot* spot = (struct mouse_hotspot*)node->data; + if (spot->y == y && x >= spot->minx && x <= spot->maxx) + break; + } + if (node == nullptr) { + for (node = mouse_hotspots->first; node != nullptr; node = node->next) { + struct mouse_hotspot* spot = (struct mouse_hotspot*)node->data; + if (spot->hungry && spot->y == y && x >= spot->minx) + break; + } + } + if (node == NULL) { + for (node = mouse_hotspots->last; node != nullptr; node = node->prev) { + struct mouse_hotspot* spot = (struct mouse_hotspot*)node->data; + if (spot->hungry && spot->y == y && x <= spot->minx) + break; + } + } + + return node; +} + +uint32_t Terminal::flags(bool raw) +{ + if (!raw) { + uint32_t newflags = get_flags(sbbs); + if (newflags != flags_) + update_terminal(sbbs); + } + // We have potentially destructed ourselves now... + return sbbs->term->flags_; +} + +void Terminal::insert_indicator() { + // Defeat line buffer + suspend_lbuf = true; + if (save_cursor_pos() && gotoxy(cols, 1)) { + const unsigned orig_atr{sbbs->curatr}; + if (sbbs->console & CON_INSERT) { + sbbs->attr(BLINK | BLACK | (LIGHTGRAY << 4)); + sbbs->cp437_out('I'); + } else { + sbbs->attr(LIGHTGRAY); + sbbs->cp437_out(' '); + } + restore_cursor_pos(); + sbbs->attr(orig_atr); + } + suspend_lbuf = false; +} + +char *Terminal::attrstr(unsigned newattr, char *str, size_t strsz) +{ + return attrstr(newattr, curatr, str, strsz); +} + +/* + * Increments columns appropriately for UTF-8 codepoint + * Parses the codepoint from successive uchars + */ +bool Terminal::utf8_increment(unsigned char ch) +{ + 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--; + codepoint <<= 6; + codepoint |= (ch & 0x3f); + if (utf8_remain) + return true; + inc_column(unicode_width(static_cast<enum unicode_codepoint>(codepoint), 0)); + codepoint = 0; + 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; + codepoint = ch & 0x1F; + return true; + } + else if ((ch & 0xf0) == 0xe0) { + utf8_remain = 2; + if (ch == 0xF0) + first_continuation = true; + codepoint = ch & 0x0F; + return true; + } + else if ((ch & 0xf8) == 0xf0) { + utf8_remain = 3; + if (ch == 0xF4) + first_continuation = true; + codepoint = ch & 0x07; + return true; + } + else + sbbs->lprintf(LOG_WARNING, "Sending invalid UTF-8 codepoint"); + } + if (utf8_remain) + return true; + } + return false; +} + +void update_terminal(sbbs_t *sbbsptr) +{ + uint32_t flags = Terminal::get_flags(sbbsptr); + if (sbbsptr->term == nullptr) { + if (flags & PETSCII) + sbbsptr->term = new PETSCII_Terminal(sbbsptr); + else if (flags & (ANSI)) + sbbsptr->term = new ANSI_Terminal(sbbsptr); + else + sbbsptr->term = new Terminal(sbbsptr); + } + else { + Terminal *newTerm; + if (flags & PETSCII) + newTerm = new PETSCII_Terminal(sbbsptr->term); + else if (flags & (ANSI)) + newTerm = new ANSI_Terminal(sbbsptr->term); + else + newTerm = new Terminal(sbbsptr->term); + delete sbbsptr->term; + sbbsptr->term = newTerm; + } + sbbsptr->term->updated(); +} + +void update_terminal(sbbs_t *sbbsptr, Terminal *term) +{ + uint32_t flags = term->flags(true); + Terminal *newTerm; + if (flags & PETSCII) + newTerm = new PETSCII_Terminal(sbbsptr, term); + else if (flags & (ANSI)) + newTerm = new ANSI_Terminal(sbbsptr, term); + else + newTerm = new Terminal(sbbsptr, term); + if (sbbsptr->term) + delete sbbsptr->term; + sbbsptr->term = newTerm; + sbbsptr->term->updated(); +} + +extern "C" void update_terminal(void *sbbsptr, user_t *userptr) +{ + if (sbbsptr == nullptr || userptr == nullptr) + return; + sbbs_t *sbbs = static_cast<sbbs_t *>(sbbsptr); + if (sbbs->useron.number == userptr->number) + update_terminal(sbbs); +} diff --git a/src/sbbs3/terminal.h b/src/sbbs3/terminal.h new file mode 100644 index 0000000000000000000000000000000000000000..69805ed302878a8d3f1760134fc52125fee1bb76 --- /dev/null +++ b/src/sbbs3/terminal.h @@ -0,0 +1,538 @@ +#ifndef TERMINAL_H +#define TERMINAL_H + +#include "sbbs.h" +#include "utf8.h" +#include "unicode.h" + +#ifdef __cplusplus + +struct mouse_hotspot { // Mouse hot-spot + char cmd[128]; + unsigned y; + unsigned minx; + unsigned maxx; + bool hungry; +}; + +struct savedline { + char buf[LINE_BUFSIZE + 1]; /* Line buffer (i.e. ANSI-encoded) */ + uint beg_attr; /* Starting attribute of each line */ + uint end_attr; /* Ending attribute of each line */ + int column; /* Current column number */ +}; + +enum output_rate { + output_rate_unlimited, + output_rate_300 = 300, + output_rate_600 = 600, + output_rate_1200 = 1200, + output_rate_2400 = 2400, + output_rate_4800 = 4800, + output_rate_9600 = 9600, + output_rate_19200 = 19200, + output_rate_38400 = 38400, + output_rate_57600 = 57600, + output_rate_76800 = 76800, + output_rate_115200 = 115200, +}; + +// Terminal mouse reporting mode (mouse_mode) +#define MOUSE_MODE_OFF 0 // No terminal mouse reporting enabled/expected +#define MOUSE_MODE_X10 (1<<0) // X10 compatible mouse reporting enabled +#define MOUSE_MODE_NORM (1<<1) // Normal tracking mode mouse reporting +#define MOUSE_MODE_BTN (1<<2) // Button-event tracking mode mouse reporting +#define MOUSE_MODE_ANY (1<<3) // Any-event tracking mode mouse reporting +#define MOUSE_MODE_EXT (1<<4) // SGR-encoded extended coordinate mouse reporting +#define MOUSE_MODE_ON (MOUSE_MODE_NORM | MOUSE_MODE_EXT) // Default mouse "enabled" mode flags + +#define HOTSPOT_CURRENT_X UINT_MAX +#define HOTSPOT_CURRENT_Y UINT_MAX + +class Terminal { +public: + unsigned row{0}; /* Current row */ + unsigned column{80}; /* Current column counter (for line counter) */ + unsigned rows{24}; /* Current number of Rows for User */ + unsigned cols{0}; /* Current number of Columns for User */ + unsigned tabstop{8}; /* Current symmetric-tabstop (size) */ + unsigned lastcrcol{0}; /* Column when last CR occured (previously lastlinelen) */ + unsigned cterm_version{0}; /* (MajorVer*1000) + MinorVer */ + unsigned lncntr{0}; /* Line Counter - for PAUSE */ + unsigned latr{ANSI_NORMAL}; /* Starting attribute of line buffer */ + uint32_t curatr{ANSI_NORMAL}; /* Current Text Attributes Always */ + unsigned lbuflen{0}; /* Number of characters in line buffer */ + char lbuf[LINE_BUFSIZE + 1]{}; /* Temp storage for each line output */ + enum output_rate cur_output_rate{output_rate_unlimited}; + unsigned mouse_mode{MOUSE_MODE_OFF}; // Mouse reporting mode flags + bool pause_hotspot{false}; + bool suspend_lbuf{0}; + link_list_t *mouse_hotspots{nullptr}; + +protected: + sbbs_t* sbbs; + uint32_t flags_{0}; /* user.misc flags that impact the terminal */ + +private: + link_list_t *savedlines{nullptr}; + uint8_t utf8_remain{0}; + bool first_continuation{false}; + uint32_t codepoint{0}; + +public: + + static uint32_t flags_fixup(uint32_t flags) + { + if (flags & UTF8) { + // These bits are *never* available in UTF8 mode + // Note that RIP is not inherently incompatible with UTF8 + flags &= ~(NO_EXASCII | PETSCII); + } + + if (flags & RIP) { + // ANSI is always available when RIP is + flags |= ANSI; + } + + if (!(flags & ANSI)) { + // These bits are *only* available in ANSI mode + // NOTE: COLOR is forced in PETSCII mode later + flags &= ~(COLOR | RIP | ICE_COLOR | MOUSE); + } + else { + // These bits are *never* available in ANSI mode + flags &= ~(PETSCII); + } + + if (flags & PETSCII) { + // These bits are *never* available in PETSCII mode + flags &= ~(RIP | ICE_COLOR | MOUSE | NO_EXASCII | UTF8); + // These bits are *always* availabe in PETSCII mode + flags |= COLOR; + } + return flags; + } + + static uint32_t get_flags(sbbs_t *sbbsptr) { + uint32_t flags; + + if (sbbsptr->sys_status & (SS_USERON | SS_NEWUSER)) { + if (sbbsptr->useron.misc & AUTOTERM) { + flags = sbbsptr->autoterm; + flags |= sbbsptr->useron.misc & (NO_EXASCII | SWAP_DELETE | COLOR | ICE_COLOR | MOUSE); + } + else + flags = sbbsptr->useron.misc; + } + else + flags = sbbsptr->autoterm; + flags &= TERM_FLAGS; + // TODO: Get rows and cols + return flags_fixup(flags); + } + + static link_list_t *listPtrInit(int flags) { + link_list_t *ret = new link_list_t; + listInit(ret, flags); + return ret; + } + + static void listPtrFree(link_list_t *ll) { + listFree(ll); + delete ll; + } + + Terminal() = delete; + // Create from sbbs_t*, ie: "Create new" + Terminal(sbbs_t *sbbsptr) : mouse_hotspots{listPtrInit(0)}, sbbs{sbbsptr}, + flags_{get_flags(sbbsptr)}, savedlines{listPtrInit(0)} {} + + // Create from Terminal*, ie: Update + Terminal(Terminal *t) : row{t->row}, column{t->column}, + rows{t->rows}, cols{t->cols}, tabstop{t->tabstop}, lastcrcol{t->lastcrcol}, + cterm_version{t->cterm_version}, lncntr{t->lncntr}, latr{t->latr}, curatr{t->curatr}, + lbuflen{t->lbuflen}, mouse_mode{t->mouse_mode}, pause_hotspot{t->pause_hotspot}, + suspend_lbuf{t->suspend_lbuf}, + mouse_hotspots{t->mouse_hotspots}, sbbs{t->sbbs}, flags_{get_flags(t->sbbs)}, + savedlines{t->savedlines} { + // Take ownership of lists so they're not destroyed + t->mouse_hotspots = nullptr; + t->savedlines = nullptr; + // Copy line buffer + memcpy(lbuf, t->lbuf, sizeof(lbuf)); + // TODO: This is pretty hacky... + // Calls the old set_mouse() with the current flags + // and mode. We can't call the new one because it's + // virtual and we aren't constructed yet. + // Ideally this would disable the mouse if !supports(MOUSE) + t->flags_ = flags_; + t->set_mouse(mouse_mode); + } + + // Create from sbbsptr* and Terminal*, ie: Create a copy + Terminal(sbbs_t *sbbsptr, Terminal *t) : row{t->row}, column{t->column}, + rows{t->rows}, cols{t->cols}, tabstop{t->tabstop}, lastcrcol{t->lastcrcol}, + cterm_version{t->cterm_version}, lncntr{t->lncntr}, latr{t->latr}, curatr{t->curatr}, + lbuflen{t->lbuflen}, mouse_mode{t->mouse_mode}, pause_hotspot{t->pause_hotspot}, + suspend_lbuf{t->suspend_lbuf}, + mouse_hotspots{listPtrInit(0)}, sbbs{sbbsptr}, flags_{get_flags(t->sbbs)}, + savedlines{listPtrInit(0)} {} + + virtual ~Terminal() + { + listPtrFree(mouse_hotspots); + listPtrFree(savedlines); + } + + // Was ansi() + virtual const char *attrstr(unsigned atr) { + return ""; + } + + // Was ansi() and ansi_attr() + virtual char* attrstr(unsigned atr, unsigned curatr, char* str, size_t strsz) { + if (strsz > 0) + str[0] = 0; + return str; + } + + virtual bool getdims() { + return false; + } + + virtual bool getxy(unsigned* x, unsigned* y) { + if (x) + *x = column + 1; + if (y) + *y = row + 1; + return true; + } + + virtual bool gotoxy(unsigned x, unsigned y) { + return false; + } + + // Was ansi_save + virtual bool save_cursor_pos() { + return false; + } + + // Was ansi_restore + virtual bool restore_cursor_pos() { + return false; + } + + virtual void carriage_return() { + sbbs->term_out('\r'); + } + + virtual void line_feed(unsigned count = 1) { + for (unsigned i = 0; i < count; i++) + sbbs->term_out('\n'); + } + + /* + * Destructive backspace. + */ + virtual void backspace(unsigned int count = 1) { + for (unsigned i = 0; i < count; i++) { + if (column > 0) + sbbs->term_out("\b \b"); + else + break; + } + } + + virtual void newline(unsigned count = 1) { + // TODO: Original version did not increment row or lncntr + // It recursed through outchar() + for (unsigned i = 0; i < count; i++) { + carriage_return(); + line_feed(); + sbbs->check_pause(); + } + } + + virtual void clearscreen() { + clear_hotspots(); + sbbs->term_out(FF); + lastcrcol = 0; + } + + virtual void cleartoeos() {} + virtual void cleartoeol() {} + virtual void clearline() { + carriage_return(); + cleartoeol(); + } + + virtual void cursor_home() {} + virtual void cursor_up(unsigned count = 1) {} + virtual void cursor_down(unsigned count = 1) { + line_feed(count); + } + + virtual void cursor_right(unsigned count = 1) { + for (unsigned i = 0; i < count; i++) { + if (column < (cols - 1)) + sbbs->term_out(' '); + else + break; + } + } + + virtual void cursor_left(unsigned count = 1) { + for (unsigned i = 0; i < count; i++) { + if (column > 0) + sbbs->term_out('\b'); + else + break; + } + } + virtual void set_output_rate(enum output_rate speed) {} + virtual void center(const char *instr, bool msg = false, unsigned columns = 0) { + if (columns == 0) + columns = cols; + char *str = strdup(instr); + truncsp(str); + size_t len = bstrlen(str); + carriage_return(); + if (len < columns) + cursor_right((columns - len) / 2); + if (msg) + sbbs->putmsg(str, P_NONE); + else + sbbs->bputs(str); + free(str); + newline(); + } + + /****************************************************************************/ + /* Returns the printed columns from 'str' accounting for Ctrl-A codes */ + /****************************************************************************/ + virtual size_t bstrlen(const char *str, int mode = 0) + { + str = sbbs->auto_utf8(str, mode); + size_t count = 0; + const char* end = str + strlen(str); + while (str < end) { + int len = 1; + if (*str == CTRL_A) { + str++; + if (*str == 0 || *str == 'Z') // EOF + break; + if (*str == '[') // CR + count = 0; + else if (*str == '<' && count) // ND-Backspace + count--; + } else if (((*str) & 0x80) && (mode & P_UTF8)) { + enum unicode_codepoint codepoint = UNICODE_UNDEFINED; + len = utf8_getc(str, end - str, &codepoint); + if (len < 1) + break; + count += unicode_width(codepoint, sbbs->unicode_zerowidth); + } else + count++; + str += len; + } + return count; + } + + // TODO: backfill? + virtual const char* type() { + return "DUMB"; + } + + virtual void set_mouse(unsigned flags) {} + + /* + * Returns true if the caller should send the char, false if + * this function handled it (ie: via term_out(), or stripping it) + */ + virtual bool parse_output(char ch) { + if (utf8_increment(ch)) + return true; + + switch (ch) { + // Zero-width characters we likely shouldn't send + case 0: // NUL + case 1: // SOH + case 2: // STX + case 3: // ETX + case 4: // EOT + case 5: // ENQ + case 6: // ACK + case 11: // VT - May be supported... TODO + case 14: // SO (7-bit) or LS1 (8-bit) + case 15: // SI (7-bit) or LS0 (8-bit) + case 16: // DLE + case 17: // DC1 / XON + case 18: // DC2 + case 19: // DC3 / XOFF + case 20: // DC4 + case 21: // NAK + case 22: // SYN + case 23: // STB + case 24: // CAN + case 25: // EM + case 26: // SUB + case 28: // FS + case 29: // GS + case 30: // RS + case 31: // US + 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 + return true; + 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 + lastcrcol = column; + if (sbbs->console & CON_CR_CLREOL) + cleartoeol(); + set_column(); + return true; + + // Everything else is assumed one byte wide + default: + inc_column(); + return true; + } + return false; + } + + /* + * Returns true if inkey() should return the value of ch + * Returns false if inkey() should parse as a ctrl character. + * + * If ch is a sequence introducer, this should parse the whole + * sequence. If the whole sequence can be replaced with a single + * control character, ch should be updated to that character, and + * the function should return true. + * + * This can add additional characters by using ungetkeys? with + * insert as true. However, to avoid infinite loops, when the + * first key this translates to is itself a control character, + * ch should be update to that one, and this should return true. + * + * Will replace ch with a TERM_KEY_* or CTRL_*, value if it + * returns true. + * + * ch is the control character that was received. + * mode is the mode passed to the inkey() call that received ch + */ + virtual bool parse_input_sequence(char& ch, int mode) { return false; } + + virtual bool saveline() { + struct savedline line; + line.beg_attr = latr; + line.end_attr = curatr; + line.column = column; + snprintf(line.buf, sizeof(line.buf), "%.*s", lbuflen, lbuf); + lbuflen = 0; + return listPushNodeData(savedlines, &line, sizeof(line)) != NULL; + } + + virtual bool restoreline() { + struct savedline* line = (struct savedline*)listPopNode(savedlines); + if (line == NULL) + return false; + // 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 term_out() + // This way we don't need to re-encode + lbuflen = 0; + sbbs->term_out(line->buf); + curatr = line->end_attr; + free(line); + return true; + } + + virtual bool can_highlight() { + return false; + } + + virtual bool can_move() { + return false; + } + + virtual bool can_mouse() { + return false; + } + + virtual bool is_monochrome() { + return true; + } + + virtual void updated() {} + + void clear_hotspots(void); + void scroll_hotspots(unsigned count); + + struct mouse_hotspot* add_hotspot(struct mouse_hotspot* spot); + struct mouse_hotspot* add_hotspot(char cmd, bool hungry = true, unsigned minx = HOTSPOT_CURRENT_X, unsigned maxx = HOTSPOT_CURRENT_X, unsigned y = HOTSPOT_CURRENT_Y); + struct mouse_hotspot* add_hotspot(int num, bool hungry = true, unsigned minx = HOTSPOT_CURRENT_X, unsigned maxx = HOTSPOT_CURRENT_X, unsigned y = HOTSPOT_CURRENT_Y); + struct mouse_hotspot* add_hotspot(uint num, bool hungry = true, unsigned minx = HOTSPOT_CURRENT_X, unsigned maxx = HOTSPOT_CURRENT_X, unsigned y = HOTSPOT_CURRENT_Y); + struct mouse_hotspot* add_hotspot(const char* cmd, bool hungry = true, unsigned minx = HOTSPOT_CURRENT_X, unsigned maxx = HOTSPOT_CURRENT_X, unsigned y = HOTSPOT_CURRENT_Y); + 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); + bool supports_any(unsigned cmp_flags); + uint32_t charset(); + const char *charset_str(); + list_node_t *find_hotspot(unsigned x, unsigned y); + uint32_t flags(bool raw = false); + void insert_indicator(); + char *attrstr(unsigned newattr, char *str, size_t strsz); + bool utf8_increment(unsigned char ch); +}; + +void update_terminal(sbbs_t *sbbsptr, Terminal *term); +void update_terminal(sbbs_t *sbbsptr); + +extern "C" { +#endif + +void update_terminal(void *sbbsptr, user_t *userptr); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/src/sbbs3/un_rep.cpp b/src/sbbs3/un_rep.cpp index 2d6759f0464f1d5d8e5789ca7c26f1dfce6fcda1..39ab25007afb0aec462fb5500354377820c0e222 100644 --- a/src/sbbs3/un_rep.cpp +++ b/src/sbbs3/un_rep.cpp @@ -193,7 +193,7 @@ bool sbbs_t::unpack_rep(char* repfile) break; } - lncntr = 0; /* defeat pause */ + term->lncntr = 0; /* defeat pause */ if (fseek(rep, l, SEEK_SET) != 0) { errormsg(WHERE, ERR_SEEK, msg_fname, l); errors++; diff --git a/src/sbbs3/upload.cpp b/src/sbbs3/upload.cpp index 0a30133a10928dbe78a4b41f27cd9ce60ce9c4a8..99e8d4ba8cde0e6eac09b3548642f548535c98b1 100644 --- a/src/sbbs3/upload.cpp +++ b/src/sbbs3/upload.cpp @@ -73,7 +73,7 @@ bool sbbs_t::uploadfile(file_t* f) } // Note: str (%s) is path/to/sbbsfile.des (used to be the description itself) int result = external(cmdstr(cfg.ftest[i]->cmd, path, str, NULL, cfg.ftest[i]->ex_mode), cfg.ftest[i]->ex_mode | EX_OFFLINE); - clearline(); + term->clearline(); if (result != 0) { safe_snprintf(str, sizeof(str), "attempted to upload %s to %s %s (%s error code %d)" , f->name diff --git a/src/sbbs3/useredit.cpp b/src/sbbs3/useredit.cpp index eddc505f0c8ae7079745aea518b8998185ddcecb..1413aebf2003047462534e2063bc1aa4e1b3a94d 100644 --- a/src/sbbs3/useredit.cpp +++ b/src/sbbs3/useredit.cpp @@ -78,7 +78,7 @@ void sbbs_t::useredit(int usernumber) } char user_pass[LEN_PASS + 1]; SAFECOPY(user_pass, user.pass); - size_t max_len = cols < 60 ? 8 : cols - 60; + size_t max_len = term->cols < 60 ? 8 : term->cols - 60; if (strlen(user_pass) > max_len - 2) SAFEPRINTF2(user_pass, "%.*s..", (int)(max_len - 2), user.pass); bprintf(text[UeditAliasPassword] @@ -142,18 +142,18 @@ void sbbs_t::useredit(int usernumber) bprintf(text[UeditFlags], u32toaf(user.flags1, tmp), u32toaf(user.flags3, tmp2) , u32toaf(user.flags2, tmp3), u32toaf(user.flags4, str)); bprintf(text[UeditExempts], u32toaf(user.exempt, tmp), u32toaf(user.rest, tmp2)); - if (lncntr >= rows - 2) - lncntr = 0; + if (term->lncntr >= term->rows - 2) + term->lncntr = 0; if (user.misc & DELETED) { if(user.deldate) datestr(user.deldate, tmp); else datestr(user.laston, tmp); snprintf(str, sizeof str, text[DeletedUser], tmp); - center(str); + term->center(str); } else if (user.misc & INACTIVE) - center(text[InactiveUser]); + term->center(text[InactiveUser]); else CRLF; l = lastuser(&cfg); @@ -168,7 +168,7 @@ void sbbs_t::useredit(int usernumber) continue; } if (IS_ALPHA(l)) - newline(); + term->newline(); switch (l) { case 'A': bputs(text[EnterYourAlias]); @@ -336,7 +336,7 @@ void sbbs_t::useredit(int usernumber) editfile(str); break; case 'I': - lncntr = 0; + term->lncntr = 0; user_config(&user); break; case 'J': /* Edit Minutes */ @@ -425,7 +425,7 @@ void sbbs_t::useredit(int usernumber) putuserstr(user.number, USER_PHONE, user.phone); break; case 'Q': - lncntr = 0; + term->lncntr = 0; CLS; free(ar); /* assertion here */ return; @@ -754,127 +754,127 @@ void sbbs_t::user_config(user_t* user) load_user_text(); } SAFECOPY(keys, "Q\r"); - long term = (user == &useron) ? term_supports() : user->misc; + long termf = (user == &useron) ? term->flags() : user->misc; if (*text[UserDefaultsTerminal]) { - add_hotspot('T'); + term->add_hotspot('T'); SAFECAT(keys, "T"); - bprintf(text[UserDefaultsTerminal], term_type(user, term, str, sizeof str)); + bprintf(text[UserDefaultsTerminal], term_type(user, termf, str, sizeof str)); } if (*text[UserDefaultsRows]) { - add_hotspot('L'); + term->add_hotspot('L'); SAFECAT(keys, "L"); bprintf(text[UserDefaultsRows], term_cols(user, str, sizeof str), term_rows(user, tmp, sizeof tmp)); } if (*text[UserDefaultsCommandSet] && cfg.total_shells > 1) { - add_hotspot('K'); + term->add_hotspot('K'); SAFECAT(keys, "K"); bprintf(text[UserDefaultsCommandSet], cfg.shell[user->shell]->name); } if (*text[UserDefaultsLanguage] && get_lang_count(&cfg) > 1) { - add_hotspot('I'); + term->add_hotspot('I'); SAFECAT(keys, "I"); bprintf(text[UserDefaultsLanguage], text[Language], text[LANG]); } if (*text[UserDefaultsXeditor] && cfg.total_xedits) { - add_hotspot('E'); + term->add_hotspot('E'); SAFECAT(keys, "E"); bprintf(text[UserDefaultsXeditor] , user->xedit ? cfg.xedit[user->xedit - 1]->name : text[None]); } if (*text[UserDefaultsArcType]) { - add_hotspot('A'); + term->add_hotspot('A'); SAFECAT(keys, "A"); bprintf(text[UserDefaultsArcType] , user->tmpext); } if (*text[UserDefaultsMenuMode]) { - add_hotspot('X'); + term->add_hotspot('X'); SAFECAT(keys, "X"); bprintf(text[UserDefaultsMenuMode] , user->misc & EXPERT ? text[On] : text[Off]); } if (*text[UserDefaultsPause]) { - add_hotspot('P'); + term->add_hotspot('P'); SAFECAT(keys, "P"); bprintf(text[UserDefaultsPause] , user->misc & UPAUSE ? text[On] : text[Off]); } if (*text[UserDefaultsHotKey]) { - add_hotspot('H'); + term->add_hotspot('H'); SAFECAT(keys, "H"); bprintf(text[UserDefaultsHotKey] , user->misc & COLDKEYS ? text[Off] : text[On]); } if (*text[UserDefaultsCursor]) { - add_hotspot('S'); + term->add_hotspot('S'); SAFECAT(keys, "S"); bprintf(text[UserDefaultsCursor] , user->misc & SPIN ? text[On] : user->misc & NOPAUSESPIN ? text[Off] : "Pause Prompt Only"); } if (*text[UserDefaultsCLS]) { - add_hotspot('C'); + term->add_hotspot('C'); SAFECAT(keys, "C"); bprintf(text[UserDefaultsCLS] , user->misc & CLRSCRN ? text[On] : text[Off]); } if (*text[UserDefaultsAskNScan]) { - add_hotspot('N'); + term->add_hotspot('N'); SAFECAT(keys, "N"); bprintf(text[UserDefaultsAskNScan] , user->misc & ASK_NSCAN ? text[On] : text[Off]); } if (*text[UserDefaultsAskSScan]) { - add_hotspot('Y'); + term->add_hotspot('Y'); SAFECAT(keys, "Y"); bprintf(text[UserDefaultsAskSScan] , user->misc & ASK_SSCAN ? text[On] : text[Off]); } if (*text[UserDefaultsANFS]) { - add_hotspot('F'); + term->add_hotspot('F'); SAFECAT(keys, "F"); bprintf(text[UserDefaultsANFS] , user->misc & ANFSCAN ? text[On] : text[Off]); } if (*text[UserDefaultsRemember]) { - add_hotspot('R'); + term->add_hotspot('R'); SAFECAT(keys, "R"); bprintf(text[UserDefaultsRemember] , user->misc & CURSUB ? text[On] : text[Off]); } if (*text[UserDefaultsBatFlag]) { - add_hotspot('B'); + term->add_hotspot('B'); SAFECAT(keys, "B"); bprintf(text[UserDefaultsBatFlag] , user->misc & BATCHFLAG ? text[On] : text[Off]); } if (*text[UserDefaultsNetMail] && (cfg.sys_misc & SM_FWDTONET)) { - add_hotspot('M'); + term->add_hotspot('M'); SAFECAT(keys, "M"); bprintf(text[UserDefaultsNetMail] , user->misc & NETMAIL ? text[On] : text[Off] , user->netmail); } if (*text[UserDefaultsQuiet] && (user->exempt & FLAG('Q') || user->misc & QUIET)) { - add_hotspot('D'); + term->add_hotspot('D'); SAFECAT(keys, "D"); bprintf(text[UserDefaultsQuiet] , user->misc & QUIET ? text[On] : text[Off]); } if (*text[UserDefaultsProtocol]) { - add_hotspot('Z'); + term->add_hotspot('Z'); SAFECAT(keys, "Z"); bprintf(text[UserDefaultsProtocol], protname(user->prot, XFER_DOWNLOAD) , user->misc & AUTOHANG ? "(Auto-Hangup)" : nulstr); } if (*text[UserDefaultsPassword] && (cfg.sys_misc & SM_PWEDIT) && !(user->rest & FLAG('G'))) { - add_hotspot('W'); + term->add_hotspot('W'); SAFECAT(keys, "W"); bputs(text[UserDefaultsPassword]); } sync(); bputs(text[UserDefaultsWhich]); - add_hotspot('Q'); + term->add_hotspot('Q'); ch = (char)getkeys(keys, 0); switch (ch) { case 'T': @@ -906,8 +906,8 @@ void sbbs_t::user_config(user_t* user) } if (sys_status & SS_ABORT) break; - term = (user == &useron) ? term_supports() : user->misc; - if (term & (AUTOTERM | ANSI) && !(term & PETSCII)) { + termf = (user == &useron) ? term->flags() : user->misc; + if (termf & (AUTOTERM | ANSI) && !(termf & PETSCII)) { user->misc |= COLOR; user->misc &= ~ICE_COLOR; if ((user->misc & AUTOTERM) || yesno(text[ColorTerminalQ])) { @@ -919,7 +919,7 @@ void sbbs_t::user_config(user_t* user) } if (sys_status & SS_ABORT) break; - if (term & ANSI) { + if (termf & ANSI) { if (text[MouseTerminalQ][0] && yesno(text[MouseTerminalQ])) user->misc |= MOUSE; else @@ -927,8 +927,8 @@ void sbbs_t::user_config(user_t* user) } if (sys_status & SS_ABORT) break; - if (!(term & PETSCII)) { - if (!(term & UTF8) && !yesno(text[ExAsciiTerminalQ])) + if (!(termf & PETSCII)) { + if (!(termf & UTF8) && !yesno(text[ExAsciiTerminalQ])) user->misc |= NO_EXASCII; else user->misc &= ~NO_EXASCII; @@ -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 @@ -955,7 +955,7 @@ void sbbs_t::user_config(user_t* user) } if (sys_status & SS_ABORT) break; - if (!(user->misc & AUTOTERM) && (term & (ANSI | NO_EXASCII)) == ANSI) { + if (!(user->misc & AUTOTERM) && (termf & (ANSI | NO_EXASCII)) == ANSI) { if (!noyes(text[RipTerminalQ])) user->misc |= RIP; else @@ -1166,7 +1166,7 @@ void sbbs_t::user_config(user_t* user) putusermisc(user->number, user->misc); break; default: - clear_hotspots(); + term->clear_hotspots(); return; } } diff --git a/src/sbbs3/ver.cpp b/src/sbbs3/ver.cpp index b137fbdb514488f40a50c8c2118a413a5b5a92b2..ca73fdaa875af29118d1208b8dc41791f8311ea0 100644 --- a/src/sbbs3/ver.cpp +++ b/src/sbbs3/ver.cpp @@ -76,14 +76,16 @@ char* socklib_version(char* str, size_t size, char* winsock_ver) void sbbs_t::ver() { char str[128], compiler[32], os[128], cpu[128]; +#ifdef USE_MOSQUITTO char tmp[128]; +#endif CRLF; strcpy(str, VERSION_NOTICE); #if defined(_DEBUG) strcat(str, " Debug"); #endif - center(str); + term->center(str); CRLF; DESCRIBE_COMPILER(compiler); @@ -95,19 +97,19 @@ void sbbs_t::ver() , git_date , smb_lib_ver(), compiler); - center(str); + term->center(str); CRLF; - center("https://gitlab.synchro.net - " GIT_BRANCH "/" GIT_HASH); + term->center("https://gitlab.synchro.net - " GIT_BRANCH "/" GIT_HASH); CRLF; snprintf(str, sizeof str, "%s - http://synchro.net", COPYRIGHT_NOTICE); - center(str); + term->center(str); CRLF; #ifdef JAVASCRIPT if (!(startup->options & BBS_OPT_NO_JAVASCRIPT)) { - center((char *)JS_GetImplementationVersion()); + term->center((char *)JS_GetImplementationVersion()); CRLF; } #endif @@ -121,24 +123,24 @@ void sbbs_t::ver() result = cryptGetAttribute(CRYPT_UNUSED, CRYPT_OPTION_INFO_STEPPING, &cl_step); (void)result; safe_snprintf(str, sizeof(str), "cryptlib %u.%u.%u (%u)", cl_major, cl_minor, cl_step, CRYPTLIB_VERSION); - center(str); + term->center(str); CRLF; } #endif safe_snprintf(str, sizeof str, "%s (%u)", archive_version_string(), ARCHIVE_VERSION_NUMBER); - center(str); + term->center(str); CRLF; #ifdef USE_MOSQUITTO SAFECOPY(str, mqtt_libver(tmp, sizeof tmp)); safe_snprintf(tmp, sizeof tmp, " (%u)", LIBMOSQUITTO_VERSION_NUMBER); SAFECAT(str, tmp); - center(str); + term->center(str); CRLF; #endif safe_snprintf(str, sizeof(str), "%s %s", os_version(os, sizeof(os)), os_cpuarch(cpu, sizeof(cpu))); - center(str); + term->center(str); } #endif diff --git a/src/sbbs3/writemsg.cpp b/src/sbbs3/writemsg.cpp index c3edb51a921c16acb1a0393873074a6b4097f4ab..3f9f45449e8ba0313fa89952973e31548b2286d1 100644 --- a/src/sbbs3/writemsg.cpp +++ b/src/sbbs3/writemsg.cpp @@ -25,7 +25,7 @@ #include "git_branch.h" #include "git_hash.h" -#define MAX_LINE_LEN ((cols - 1) + 2) +#define MAX_LINE_LEN ((term->cols - 1) + 2) const char *quote_fmt = " > %.*s\r\n"; void quotestr(char *str); @@ -97,7 +97,7 @@ bool sbbs_t::quotemsg(smb_t* smb, smbmsg_t* msg, bool tails) BOOL is_utf8 = FALSE; if (!str_is_ascii(buf)) { if (smb_msg_is_utf8(msg)) { - if (term_supports(UTF8) + if ((term->charset() == CHARSET_UTF8) && (!useron_xedit || (cfg.xedit[useron_xedit - 1]->misc & XTRN_UTF8))) is_utf8 = TRUE; else { @@ -105,7 +105,7 @@ bool sbbs_t::quotemsg(smb_t* smb, smbmsg_t* msg, bool tails) } } else { // CP437 char* orgtxt; - if (term_supports(UTF8) + if ((term->charset() == CHARSET_UTF8) && (!useron_xedit || (cfg.xedit[useron_xedit - 1]->misc & XTRN_UTF8)) && (orgtxt = strdup(buf)) != NULL) { is_utf8 = TRUE; @@ -124,7 +124,7 @@ bool sbbs_t::quotemsg(smb_t* smb, smbmsg_t* msg, bool tails) if (useron_xedit > 0) wrap_cols = cfg.xedit[useron_xedit - 1]->quotewrap_cols; if (wrap_cols == 0) - wrap_cols = cols - 1; + wrap_cols = term->cols - 1; wrapped = ::wordwrap(buf, wrap_cols, org_cols - 1, /* handle_quotes: */ TRUE, is_utf8, /* pipe_codes: */false); } if (wrapped != NULL) { @@ -291,8 +291,8 @@ bool sbbs_t::writemsg(const char *fname, const char *top, char *subj, int mode, unsigned lines; ushort useron_xedit = useron.xedit; - if (cols < TERM_COLS_MIN) { - errormsg(WHERE, ERR_CHK, "columns (too narrow)", cols); + if (term->cols < TERM_COLS_MIN) { + errormsg(WHERE, ERR_CHK, "columns (too narrow)", term->cols); return false; } @@ -366,7 +366,7 @@ bool sbbs_t::writemsg(const char *fname, const char *top, char *subj, int mode, if (!fgets(str, sizeof(str), stream)) break; quotestr(str); - SAFEPRINTF2(tmp, quote_fmt, cols - 4, str); + SAFEPRINTF2(tmp, quote_fmt, term->cols - 4, str); if (write(file, tmp, strlen(tmp)) > 0) linesquoted++; } @@ -422,7 +422,7 @@ bool sbbs_t::writemsg(const char *fname, const char *top, char *subj, int mode, if (!fgets(str, sizeof(str), stream)) break; quotestr(str); - SAFEPRINTF2(tmp, quote_fmt, cols - 4, str); + SAFEPRINTF2(tmp, quote_fmt, term->cols - 4, str); if (write(file, tmp, strlen(tmp)) > 0) linesquoted++; } @@ -437,7 +437,7 @@ bool sbbs_t::writemsg(const char *fname, const char *top, char *subj, int mode, if (!fgets(str, sizeof(str), stream)) break; quotestr(str); - bprintf(P_AUTO_UTF8, "%4d: %.*s\r\n", i, (int)cols - 7, str); + bprintf(P_AUTO_UTF8, "%4d: %.*s\r\n", i, (int)term->cols - 7, str); i++; } continue; @@ -466,7 +466,7 @@ bool sbbs_t::writemsg(const char *fname, const char *top, char *subj, int mode, if (!fgets(str, sizeof(str), stream)) break; quotestr(str); - SAFEPRINTF2(tmp, quote_fmt, cols - 4, str); + SAFEPRINTF2(tmp, quote_fmt, term->cols - 4, str); if (write(file, tmp, strlen(tmp)) > 0) linesquoted++; j++; @@ -475,7 +475,7 @@ bool sbbs_t::writemsg(const char *fname, const char *top, char *subj, int mode, else { /* one line */ if (fgets(str, sizeof(str), stream)) { quotestr(str); - SAFEPRINTF2(tmp, quote_fmt, cols - 4, str); + SAFEPRINTF2(tmp, quote_fmt, term->cols - 4, str); if (write(file, tmp, strlen(tmp)) > 0) linesquoted++; } @@ -508,7 +508,7 @@ bool sbbs_t::writemsg(const char *fname, const char *top, char *subj, int mode, else { bputs(text[SubjectPrompt]); } - max_title_len = cols - column - 1; + max_title_len = term->cols - term->column - 1; if (max_title_len > LEN_TITLE) max_title_len = LEN_TITLE; if (draft_restored) @@ -584,11 +584,11 @@ bool sbbs_t::writemsg(const char *fname, const char *top, char *subj, int mode, *editor = cfg.xedit[useron_xedit - 1]->name; if (!str_is_ascii(subj)) { if (utf8_str_is_valid(subj)) { - if (!term_supports(UTF8) || !(cfg.xedit[useron_xedit - 1]->misc & XTRN_UTF8)) { + if ((term->charset() != CHARSET_UTF8) || !(cfg.xedit[useron_xedit - 1]->misc & XTRN_UTF8)) { utf8_to_cp437_inplace(subj); } } else { // CP437 - if (term_supports(UTF8) && (cfg.xedit[useron_xedit - 1]->misc & XTRN_UTF8)) { + if ((term->charset() == CHARSET_UTF8) && (cfg.xedit[useron_xedit - 1]->misc & XTRN_UTF8)) { cp437_to_utf8_str(subj, str, sizeof(str) - 1, /* minval: */ '\x80'); safe_snprintf(subj, LEN_TITLE + 1, "%s", str); } @@ -704,7 +704,7 @@ bool sbbs_t::writemsg(const char *fname, const char *top, char *subj, int mode, if (linesquoted || draft_restored) { if ((file = nopen(msgtmp, O_RDONLY)) != -1) { length = (long)filelength(file); - l = length > (cfg.level_linespermsg[useron_level] * MAX_LINE_LEN) - 1 + l = length > (int)(cfg.level_linespermsg[useron_level] * MAX_LINE_LEN) - 1 ? (cfg.level_linespermsg[useron_level] * MAX_LINE_LEN) - 1 : length; if (read(file, buf, l) != l) l = 0; @@ -813,7 +813,7 @@ void sbbs_t::editor_info_to_msg(smbmsg_t* msg, const char* editor, const char* c useron_xedit = 0; if (editor == NULL || useron_xedit == 0 || (cfg.xedit[useron_xedit - 1]->misc & SAVECOLUMNS)) - smb_hfield_bin(msg, SMB_COLUMNS, cols); + smb_hfield_bin(msg, SMB_COLUMNS, term->cols); if (!str_is_ascii(msg->subj) && utf8_str_is_valid(msg->subj)) msg->hdr.auxattr |= MSG_HFIELDS_UTF8; @@ -958,8 +958,8 @@ uint sbbs_t::msgeditor(char *buf, const char *top, char *title, uint maxlines, u str_list_t str; long pmode = P_SAVEATR | P_NOATCODES | P_AUTO_UTF8; - if (cols < TERM_COLS_MIN) { - errormsg(WHERE, ERR_CHK, "columns (too narrow)", cols); + if (term->cols < TERM_COLS_MIN) { + errormsg(WHERE, ERR_CHK, "columns (too narrow)", term->cols); return 0; } @@ -980,8 +980,8 @@ uint sbbs_t::msgeditor(char *buf, const char *top, char *title, uint maxlines, u bprintf(text[EnterMsgNow], maxlines); if (!menu("msgtabs", P_NOERROR)) { - for (i = 0; i < (cols - 1); i++) { - if (i % EDIT_TABSIZE || !i) + for (unsigned u = 0; u < (term->cols - 1); u++) { + if (u % EDIT_TABSIZE || !u) outchar('-'); else outchar('+'); @@ -991,7 +991,7 @@ uint sbbs_t::msgeditor(char *buf, const char *top, char *title, uint maxlines, u putmsg(top, pmode); for (line = 0; line < lines && !msgabort(); line++) { /* display lines in buf */ putmsg(str[line], pmode); - cleartoeol(); /* delete to end of line */ + term->cleartoeol(); /* delete to end of line */ CRLF; } sync(); @@ -1012,14 +1012,14 @@ uint sbbs_t::msgeditor(char *buf, const char *top, char *title, uint maxlines, u else strin[0] = 0; if (line < 1) - carriage_return(); + term->carriage_return(); ulong prev_con = console; int kmode = K_WORDWRAP | K_MSG | K_EDIT | K_NOCRLF | K_USEOFFSET; if (line) kmode |= K_LEFTEXIT; if (str[line] != NULL) kmode |= K_RIGHTEXIT; - getstr(strin, cols - 1, kmode); + getstr(strin, term->cols - 1, kmode); if ((prev_con & CON_DELETELINE) /* Ctrl-X/ZDLE */ && strncmp(strin, "B00", 3) == 0) { strin[0] = 0; prot = 'Z'; @@ -1035,13 +1035,13 @@ uint sbbs_t::msgeditor(char *buf, const char *top, char *title, uint maxlines, u strListRemove(&str, line); for (i = line; str[i]; i++) { putmsg(str[i], pmode); - cleartoeol(); - newline(); + term->cleartoeol(); + term->newline(); } - clearline(); + term->clearline(); if (line) --line; - cursor_up(i - line); + term->cursor_up(i - line); continue; } else if (str[line] == NULL) { if (strin[0] != 0) @@ -1050,9 +1050,9 @@ uint sbbs_t::msgeditor(char *buf, const char *top, char *title, uint maxlines, u strListReplace(str, line, strin); if (line < 1) continue; - carriage_return(); - cursor_up(); - cleartoeol(); + term->carriage_return(); + term->cursor_up(); + term->cleartoeol(); line--; continue; } @@ -1060,7 +1060,7 @@ uint sbbs_t::msgeditor(char *buf, const char *top, char *title, uint maxlines, u strListDelete(&str, line); continue; } - newline(); + term->newline(); if (console & (CON_DOWNARROW | CON_RIGHTARROW)) { if (str[line] != NULL) { strListReplace(str, line, strin); @@ -1070,7 +1070,7 @@ uint sbbs_t::msgeditor(char *buf, const char *top, char *title, uint maxlines, u } if (strin[0] == '/' && strlen(strin) < 16) { if (!stricmp(strin, "/DEBUG") && SYSOP) { - bprintf("\r\nline=%d lines=%d (%d), rows=%d\r\n", line, lines, (int)strListCount(str), rows); + bprintf("\r\nline=%d lines=%d (%d), rows=%d\r\n", line, lines, (int)strListCount(str), term->rows); continue; } else if (!stricmp(strin, "/ABT")) { @@ -1137,7 +1137,7 @@ uint sbbs_t::msgeditor(char *buf, const char *top, char *title, uint maxlines, u bputs(text[InvalidLineNumber]); else { SAFECOPY(strin, str[i]); - getstr(strin, cols - 1, j); + getstr(strin, term->cols - 1, j); strListReplace(str, i, strin); } continue; @@ -1169,12 +1169,12 @@ uint sbbs_t::msgeditor(char *buf, const char *top, char *title, uint maxlines, u int digits = DEC_DIGITS(lines); while (str[j] != NULL && !msgabort()) { if (linenums) { /* line numbers */ - snprintf(tmp, sizeof tmp, "%*d: %-.*s", digits, j + 1, (int)(cols - (digits + 3)), str[j]); + snprintf(tmp, sizeof tmp, "%*d: %-.*s", digits, j + 1, (int)(term->cols - (digits + 3)), str[j]); putmsg(tmp, pmode); } else putmsg(str[j], pmode); - cleartoeol(); /* delete to end of line */ + term->cleartoeol(); /* delete to end of line */ CRLF; j++; } @@ -1238,11 +1238,11 @@ upload: strListInsert(&str, "", line); for (i = line; str[i]; i++) { putmsg(str[i], pmode); - cleartoeol(); - newline(); + term->cleartoeol(); + term->newline(); } - clearline(); - cursor_up(i - line); + term->clearline(); + term->cursor_up(i - line); continue; } @@ -1283,8 +1283,8 @@ bool sbbs_t::editfile(char *fname, uint maxlines, const char* to, const char* fr unsigned lines; ushort useron_xedit = useron.xedit; - if (cols < TERM_COLS_MIN) { - errormsg(WHERE, ERR_CHK, "columns (too narrow)", cols); + if (term->cols < TERM_COLS_MIN) { + errormsg(WHERE, ERR_CHK, "columns (too narrow)", term->cols); return false; } diff --git a/src/sbbs3/xtrn.cpp b/src/sbbs3/xtrn.cpp index 5587286b18c33085e1ae564108eda13a13a657d2..8b19107d5cf635c537170510b8813d23e7dafe3f 100644 --- a/src/sbbs3/xtrn.cpp +++ b/src/sbbs3/xtrn.cpp @@ -385,7 +385,7 @@ int sbbs_t::external(const char* cmdline, int mode, const char* startup_dir) return -1; } - clear_hotspots(); + term->clear_hotspots(); XTRN_LOADABLE_MODULE(cmdline, startup_dir); XTRN_LOADABLE_JS_MODULE(cmdline, mode, startup_dir); @@ -1146,7 +1146,7 @@ int sbbs_t::external(const char* cmdline, int mode, const char* startup_dir) xtrn_mode = mode; lprintf(LOG_DEBUG, "Executing external: %s", cmdline); - clear_hotspots(); + term->clear_hotspots(); if (startup_dir == NULL) startup_dir = nulstr; @@ -1617,17 +1617,17 @@ int sbbs_t::external(const char* cmdline, int mode, const char* startup_dir) if ((mode & EX_STDIO) == EX_STDIO) { struct winsize winsize; - struct termios term; - memset(&term, 0, sizeof(term)); - cfsetispeed(&term, B19200); - cfsetospeed(&term, B19200); + struct termios termio; + memset(&termio, 0, sizeof(term)); + cfsetispeed(&termio, B19200); + cfsetospeed(&termio, B19200); if (mode & EX_BIN) - cfmakeraw(&term); + cfmakeraw(&termio); else { - term.c_iflag = TTYDEF_IFLAG; - term.c_oflag = TTYDEF_OFLAG; - term.c_lflag = TTYDEF_LFLAG; - term.c_cflag = TTYDEF_CFLAG; + termio.c_iflag = TTYDEF_IFLAG; + termio.c_oflag = TTYDEF_OFLAG; + termio.c_lflag = TTYDEF_LFLAG; + termio.c_cflag = TTYDEF_CFLAG; /* * On Linux, ttydefchars is in the wrong order, so * it's completely useless for anything. @@ -1635,89 +1635,89 @@ int sbbs_t::external(const char* cmdline, int mode, const char* startup_dir) * to a value we may have made up. * TODO: We can set stuff from the user term here... */ - for (unsigned noti = 0; noti < (sizeof(term.c_cc) / sizeof(term.c_cc[0])); noti++) - term.c_cc[noti] = _POSIX_VDISABLE; + for (unsigned noti = 0; noti < (sizeof(termio.c_cc) / sizeof(termio.c_cc[0])); noti++) + termio.c_cc[noti] = _POSIX_VDISABLE; #ifdef VEOF - term.c_cc[VEOF] = CEOF; + termio.c_cc[VEOF] = CEOF; #endif #ifdef VEOL - term.c_cc[VEOL] = CEOL; + termio.c_cc[VEOL] = CEOL; #endif #ifdef VEOL2 #ifdef CEOL2 - term.c_cc[VEOL2] = CEOL2; + termio.c_cc[VEOL2] = CEOL2; #else - term.c_cc[VEOL2] = CEOL; + termio.c_cc[VEOL2] = CEOL; #endif #endif #ifdef VERASE - term.c_cc[VERASE] = CERASE; + termio.c_cc[VERASE] = CERASE; #endif #ifdef VKILL - term.c_cc[VKILL] = CKILL; + termio.c_cc[VKILL] = CKILL; #endif #ifdef VREPRINT - term.c_cc[VREPRINT] = CREPRINT; + termio.c_cc[VREPRINT] = CREPRINT; #endif #ifdef VINTR - term.c_cc[VINTR] = CINTR; + termio.c_cc[VINTR] = CINTR; #endif #ifdef VERASE2 #ifdef CERASE2 - term.c_cc[VERASE2] = CERASE2; + termio.c_cc[VERASE2] = CERASE2; #else - term.c_cc[VERASE2] = CERASE; + termio.c_cc[VERASE2] = CERASE; #endif #endif #ifdef VQUIT - term.c_cc[VQUIT] = CQUIT; + termio.c_cc[VQUIT] = CQUIT; #endif #ifdef VSUSP - term.c_cc[VSUSP] = CSUSP; + termio.c_cc[VSUSP] = CSUSP; #endif #ifdef VDSUSP - term.c_cc[VDSUSP] = CDSUSP; + termio.c_cc[VDSUSP] = CDSUSP; #endif #ifdef VSTART - term.c_cc[VSTART] = CSTART; + termio.c_cc[VSTART] = CSTART; #endif #ifdef VSTOP - term.c_cc[VSTOP] = CSTOP; + termio.c_cc[VSTOP] = CSTOP; #endif #ifdef VLNEXT - term.c_cc[VLNEXT] = CLNEXT; + termio.c_cc[VLNEXT] = CLNEXT; #endif #ifdef VDISCARD - term.c_cc[VDISCARD] = CDISCARD; + termio.c_cc[VDISCARD] = CDISCARD; #endif #ifdef VMIN - term.c_cc[VMIN] = CMIN; + termio.c_cc[VMIN] = CMIN; #endif #ifdef VTIME - term.c_cc[VTIME] = CTIME; + termio.c_cc[VTIME] = CTIME; #endif #ifdef VSTATUS - term.c_cc[VSTATUS] = CSTATUS; + termio.c_cc[VSTATUS] = CSTATUS; #endif #ifdef VWERASE - term.c_cc[VWERASE] = CWERASE; + termio.c_cc[VWERASE] = CWERASE; #endif #ifdef VEOT - term.c_cc[VEOT] = CEOT; + termio.c_cc[VEOT] = CEOT; #endif #ifdef VBRK - term.c_cc[VBRK] = CBRK; + termio.c_cc[VBRK] = CBRK; #endif #ifdef VRPRNT - term.c_cc[VRPRNT] = CRPRNT; + termio.c_cc[VRPRNT] = CRPRNT; #endif #ifdef VFLUSH - term.c_cc[VFLUSH] = CFLUSH + termio.c_cc[VFLUSH] = CFLUSH #endif } - winsize.ws_row = rows; - winsize.ws_col = cols; - if ((pid = forkpty(&in_pipe[1], NULL, &term, &winsize)) == -1) { + winsize.ws_row = term->rows; + winsize.ws_col = term->cols; + if ((pid = forkpty(&in_pipe[1], NULL, &termio, &winsize)) == -1) { if (!(mode & (EX_STDIN | EX_OFFLINE))) { if (passthru_thread_running) passthru_socket_activate(false); @@ -1762,7 +1762,7 @@ int sbbs_t::external(const char* cmdline, int mode, const char* startup_dir) sigfillset(&sigs); sigprocmask(SIG_UNBLOCK, &sigs, NULL); if (!(mode & EX_BIN)) { - if (term_supports(ANSI)) + if (term->supports(ANSI)) SAFEPRINTF(term_env, "TERM=%s", startup->xtrn_term_ansi); else SAFEPRINTF(term_env, "TERM=%s", startup->xtrn_term_dumb); @@ -1971,9 +1971,9 @@ int sbbs_t::external(const char* cmdline, int mode, const char* startup_dir) bp = buf; output_len = rd; } - if (term_supports(PETSCII)) + if (term->charset() == CHARSET_PETSCII) petscii_convert(bp, output_len); - else if (term_supports(UTF8)) + else if (term->charset() == CHARSET_UTF8) bp = cp437_to_utf8(bp, output_len, utf8_buf, sizeof utf8_buf); } /* Did expansion overrun the output buffer? */ @@ -2193,7 +2193,7 @@ char* sbbs_t::cmdstr(const char *instr, const char *fpath, const char *fspec, ch strncat(cmd, cfg.sys_id, avail); break; case 'R': /* Rows */ - strncat(cmd, ultoa(rows, str, 10), avail); + strncat(cmd, ultoa(term->rows, str, 10), avail); break; case 'S': /* File Spec (or Baja command str) or startup-directory */ strncat(cmd, fspec, avail); @@ -2210,7 +2210,7 @@ char* sbbs_t::cmdstr(const char *instr, const char *fpath, const char *fspec, ch strncat(cmd, str, avail); break; case 'W': /* Columns (width) */ - strncat(cmd, ultoa(cols, str, 10), avail); + strncat(cmd, ultoa(term->cols, str, 10), avail); break; case 'X': strncat(cmd, cfg.shell[useron.shell]->code, avail); diff --git a/src/sbbs3/xtrn_sec.cpp b/src/sbbs3/xtrn_sec.cpp index f19a9b7d858a0af629a3ab5bed179801c8de015d..61efa76500e7f8b7d729b87f0ca771fcf3a8db5b 100644 --- a/src/sbbs3/xtrn_sec.cpp +++ b/src/sbbs3/xtrn_sec.cpp @@ -142,7 +142,6 @@ void sbbs_t::xtrndat(const char *name, const char *dropdir, uchar type, uint tle struct tm tm; struct tm tl; stats_t stats; - int term = term_supports(); char node_dir[MAX_PATH + 1]; char ctrl_dir[MAX_PATH + 1]; @@ -204,10 +203,10 @@ void sbbs_t::xtrndat(const char *name, const char *dropdir, uchar type, uint tle , cfg.sys_nodes /* Total system nodes */ , cfg.node_num /* Current node */ , tleft /* User Timeleft in seconds */ - , (term & ANSI) /* User ANSI ? (Yes/Mono/No) */ - ? (term & COLOR) + , (term->supports(ANSI)) /* User ANSI ? (Yes/Mono/No) */ + ? (term->supports(COLOR)) ? "Yes":"Mono":"No" - , rows /* User Screen lines */ + , term->rows /* User Screen lines */ , user_available_credits(&useron)); /* User Credits */ lfexpand(str, misc); fwrite(str, strlen(str), 1, fp); @@ -321,12 +320,12 @@ void sbbs_t::xtrndat(const char *name, const char *dropdir, uchar type, uint tle , user_available_credits(&useron) /* Gold */ , TM_MONTH(tm.tm_mon) /* User last on date (MM/DD/YY) */ , tm.tm_mday, TM_YEAR(tm.tm_year) - , cols /* User screen width */ - , rows /* User screen length */ + , term->cols /* User screen width */ + , term->rows /* User screen length */ , useron.level /* User SL */ , 0 /* Cosysop? */ , SYSOP /* Sysop? (1/0) */ - , INT_TO_BOOL(term & ANSI) /* ANSI ? (1/0) */ + , term->supports(ANSI) /* ANSI ? (1/0) */ , online == ON_REMOTE); /* Remote (1/0) */ lfexpand(str, misc); fwrite(str, strlen(str), 1, fp); @@ -408,15 +407,15 @@ void sbbs_t::xtrndat(const char *name, const char *dropdir, uchar type, uint tle , tm.tm_mday, TM_YEAR(tm.tm_year) , MIN(tleft, INT16_MAX) /* 18: User time left in sec */ , MIN((tleft / 60), INT16_MAX) /* 19: User time left in min */ - , (term & NO_EXASCII) /* 20: GR if COLOR ANSI */ - ? "7E" : (term & (ANSI | COLOR)) == (ANSI | COLOR) ? "GR" : "NG"); + , term->charset() == CHARSET_ASCII /* 20: GR if COLOR ANSI */ + ? "7E" : (term->supports(ANSI | COLOR) ? "GR" : "NG")); lfexpand(str, misc); fwrite(str, strlen(str), 1, fp); t = useron.expire; localtime_r(&t, &tm); safe_snprintf(str, sizeof(str), "%u\n%c\n%s\n%u\n%02u/%02u/%02u\n%u\n%c\n%u\n%u\n" - , rows /* 21: User screen length */ + , term->rows /* 21: User screen length */ , (useron.misc & EXPERT) ? 'Y':'N' /* 22: Expert? (Y/N) */ , u32toaf(useron.flags1, tmp2) /* 23: Registered conferences */ , 0 /* 24: Conference came from */ @@ -454,7 +453,7 @@ void sbbs_t::xtrndat(const char *name, const char *dropdir, uchar type, uint tle localtime_r(&ns_time, &tm); safe_snprintf(str, sizeof(str), "%c\n%c\n%u\n%" PRIu32 "\n%02d/%02d/%02d\n" - , (term & (NO_EXASCII | ANSI | COLOR)) == ANSI + , (term->flags() & (NO_EXASCII | ANSI | COLOR)) == ANSI ? 'Y':'N' /* 39: ANSI supported but NG mode */ , 'Y' /* 40: Use record locking */ , cfg.color[clr_external] /* 41: BBS default color */ @@ -535,7 +534,7 @@ void sbbs_t::xtrndat(const char *name, const char *dropdir, uchar type, uint tle , tmp /* User's firstname */ , p /* User's lastname */ , useron.location /* User's city */ - , INT_TO_BOOL(term & ANSI) /* 1=ANSI 0=ASCII */ + , term->supports(ANSI) /* 1=ANSI 0=ASCII */ , useron.level /* Security level */ , MIN((tleft / 60), INT16_MAX)); /* Time left in minutes */ strupr(str); @@ -572,7 +571,7 @@ void sbbs_t::xtrndat(const char *name, const char *dropdir, uchar type, uint tle exitinfo.UserInfo.Attrib |= QBBS::USER_ATTRIB_CLRSCRN; if (useron.misc & UPAUSE) exitinfo.UserInfo.Attrib |= QBBS::USER_ATTRIB_MORE; - if (term & ANSI) + if (term->supports(ANSI)) exitinfo.UserInfo.Attrib |= QBBS::USER_ATTRIB_ANSI; if (useron.sex == 'F') exitinfo.UserInfo.Attrib |= QBBS::USER_ATTRIB_FEMALE; @@ -584,7 +583,7 @@ void sbbs_t::xtrndat(const char *name, const char *dropdir, uchar type, uint tle exitinfo.UserInfo.UpK = (uint16_t)(useron.ulb / 1024UL); exitinfo.UserInfo.DownK = (uint16_t)(useron.dlb / 1024UL); exitinfo.UserInfo.TodayK = (uint16_t)(logon_dlb / 1024UL); - exitinfo.UserInfo.ScreenLength = (int16_t)rows; + exitinfo.UserInfo.ScreenLength = (int16_t)term->rows; localtime_r(&logontime, &tm); SAFEPRINTF2(tmp, "%02d:%02d", tm.tm_hour, tm.tm_min); exitinfo.LoginTime = tmp; @@ -596,12 +595,12 @@ void sbbs_t::xtrndat(const char *name, const char *dropdir, uchar type, uint tle exitinfo.WantChat = (sys_status & SS_SYSPAGE); exitinfo.ScreenClear = (useron.misc & CLRSCRN); exitinfo.MorePrompts = (useron.misc & UPAUSE); - exitinfo.GraphicsMode = !(term & NO_EXASCII); + exitinfo.GraphicsMode = !(term->charset() == CHARSET_ASCII); exitinfo.ExternEdit = (useron.xedit); - exitinfo.ScreenLength = (int16_t)rows; + exitinfo.ScreenLength = (int16_t)term->rows; exitinfo.MNP_Connect = true; - exitinfo.ANSI_Capable = (term & ANSI); - exitinfo.RIP_Active = (term & RIP); + exitinfo.ANSI_Capable = term->supports(ANSI); + exitinfo.RIP_Active = term->supports(RIP); fwrite(&exitinfo, sizeof(exitinfo), 1, fp); fclose(fp); @@ -650,7 +649,7 @@ void sbbs_t::xtrndat(const char *name, const char *dropdir, uchar type, uint tle , useron.location /* User location */ , useron.level /* Security level */ , MIN((tleft / 60), INT16_MAX) /* Time left in min */ - , (term & ANSI) ? "COLOR":"MONO" /* ANSI ??? */ + , term->supports(ANSI) ? "COLOR":"MONO" /* ANSI ??? */ , useron.pass /* Password */ , useron.number); /* User number */ lfexpand(str, misc); @@ -690,7 +689,7 @@ void sbbs_t::xtrndat(const char *name, const char *dropdir, uchar type, uint tle , TM_MONTH(tm.tm_mon), tm.tm_mday /* File new-scan date */ , TM_YEAR(tm.tm_year) /* in MM/DD/YY */ , useron.logons /* Total logons */ - , rows /* Screen length */ + , term->rows /* Screen length */ , 0 /* Highest message read */ , useron.uls /* Total files uploaded */ , useron.dls); /* Total files downloaded */ @@ -736,7 +735,7 @@ void sbbs_t::xtrndat(const char *name, const char *dropdir, uchar type, uint tle sys.PageBell = sys_status & SS_SYSPAGE; sys.Alarm = startup->sound.answer[0] && !sound_muted(&cfg); sys.ErrorCorrected = true; - sys.GraphicsMode = (term & NO_EXASCII) ? 'N' : 'Y'; + sys.GraphicsMode = term->charset() == CHARSET_ASCII ? 'N' : 'Y'; sys.UserNetStatus = (thisnode.misc & NODE_POFF) ? 'U' : 'A'; /* Node chat status ([A]vailable or [U]navailable) */ SAFEPRINTF(tmp, "%u", dte_rate); sys.ModemSpeed = tmp; @@ -759,7 +758,7 @@ void sbbs_t::xtrndat(const char *name, const char *dropdir, uchar type, uint tle sys.MinutesLeft = (int16_t)(tleft / 60); sys.NodeNum = (uint8_t)cfg.node_num; sys.EventTime = "00:00"; - sys.UseAnsi = INT_TO_BOOL(term & ANSI); + sys.UseAnsi = term->supports(ANSI); sys.YesChar = yes_key(); sys.NoChar = no_key(); sys.Conference2 = cursubnum; @@ -788,7 +787,7 @@ void sbbs_t::xtrndat(const char *name, const char *dropdir, uchar type, uint tle user.fixed.Protocol = useron.prot; user.fixed.SecurityLevel = useron.level; user.fixed.NumTimesOn = useron.logons; - user.fixed.PageLen = (uint8_t)rows; + user.fixed.PageLen = (uint8_t)term->rows; user.fixed.NumUploads = useron.uls; user.fixed.NumDownloads = useron.dls; user.fixed.DailyDnldBytes = (uint32_t)logon_dlb; @@ -853,7 +852,7 @@ void sbbs_t::xtrndat(const char *name, const char *dropdir, uchar type, uint tle "%s\n%s\n%u\n%s\n%u\n%u\n%u\n%u\n%u\n%lu\n%u\n" "%" PRIu64 "\n%" PRIu64 "\n%s\n%s\n" , dropdir - , (term & ANSI) ? "TRUE":"FALSE" /* ANSI ? True or False */ + , term->supports(ANSI) ? "TRUE":"FALSE" /* ANSI ? True or False */ , useron.level /* Security level */ , useron.uls /* Total uploads */ , useron.dls /* Total downloads */ @@ -901,9 +900,9 @@ void sbbs_t::xtrndat(const char *name, const char *dropdir, uchar type, uint tle */ safe_snprintf(str, sizeof(str), "%s\n%d \n%d\n%u\n%u\n%u\n%u\n%s\n" , name // Complete name or handle of user - , INT_TO_BOOL(term & ANSI) // ANSI status: 1 = yes, 0 = no, -1 = don't know - , !INT_TO_BOOL(term & NO_EXASCII) // IBM Graphic characters: 1 = yes, 0 = no, -1 = unknown - , rows // Page length of screen, in lines. Assume 25 if unknown + , term->supports(ANSI) // ANSI status: 1 = yes, 0 = no, -1 = don't know + , !(term->charset() == CHARSET_ASCII) // IBM Graphic characters: 1 = yes, 0 = no, -1 = unknown + , term->rows // Page length of screen, in lines. Assume 25 if unknown , dte_rate // Baud Rate: 300, 1200, 2400, 9600, 19200, etc. , online == ON_LOCAL ? 0:cfg.com_port // Com Port: 1, 2, 3, or 4. , MIN((tleft / 60), INT16_MAX) // Time Limit: (in minutes); -1 if unknown. @@ -931,7 +930,7 @@ void sbbs_t::xtrndat(const char *name, const char *dropdir, uchar type, uint tle , useron.pass /* User's password */ , useron.level /* User's level */ , useron.misc & EXPERT ? 'Y':'N' /* Expert? */ - , (term & ANSI) ? 'Y':'N' /* ANSI? */ + , term->supports(ANSI) ? 'Y':'N' /* ANSI? */ , MIN((tleft / 60), INT16_MAX) /* Minutes left */ , useron.phone /* User's phone number */ , useron.location /* User's city and state */ @@ -978,7 +977,7 @@ void sbbs_t::xtrndat(const char *name, const char *dropdir, uchar type, uint tle , name , useron.level , tleft / 60 - , INT_TO_BOOL(term & ANSI) + , term->supports(ANSI) , cfg.node_num); lfexpand(str, misc); fwrite(str, strlen(str), 1, fp);