summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--FL/Fl_Simple_Terminal.H64
-rw-r--r--src/Fl_Simple_Terminal.cxx484
2 files changed, 418 insertions, 130 deletions
diff --git a/FL/Fl_Simple_Terminal.H b/FL/Fl_Simple_Terminal.H
index bee182bc4..10ed81bdb 100644
--- a/FL/Fl_Simple_Terminal.H
+++ b/FL/Fl_Simple_Terminal.H
@@ -123,21 +123,64 @@ protected:
Fl_Text_Buffer *sbuf; // style buffer
private:
- int history_lines_; // max lines allowed in screen history
+ // Private class to handle parsing ESC sequences
+ // Holds all state information for parsing esc sequences,
+ // so sequences can span multiple block read(2) operations, etc.
+ //
+ class Fl_Escape_Seq {
+ public:
+ static const int maxbuf = 80;
+ static const int maxvals = 10;
+ // Return codes
+ static const int success = 0; // operation succeeded
+ static const int fail = -1; // operation failed
+ static const int completed = 1; // multi-step operation completed successfully
+ private:
+ char esc_mode_; // escape parsing mode state
+ char buf_[maxbuf]; // escape sequence being parsed
+ char *bufp_; // parsing ptr into buf[]
+ char *bufendp_; // end of buf[] (ptr to last valid buf char)
+ char *valbufp_; // pointer to first char in buf of integer being parsed
+ int vals_[maxvals]; // value array for parsing #'s in ESC[#;#;#..
+ int vali_; // parsing index into vals_[], 0 if none
+
+ int append_buf(char c);
+ int append_val();
+
+ public:
+ Fl_Escape_Seq();
+ void reset();
+ char esc_mode() const;
+ void esc_mode(char val);
+ int total_vals() const;
+ int val(int i) const;
+ bool parse_in_progress() const;
+ int parse(char c);
+ };
+
+private:
+ int history_lines_; // max lines allowed in screen history
bool stay_at_bottom_; // lets scroller chase last line in buffer
bool ansi_; // enables ANSI sequences
// scroll management
- int lines; // #lines in buffer (optimization: Fl_Text_Buffer slow to calc this)
- bool scrollaway; // true when user changed vscroll away from bottom
- bool scrolling; // true while scroll callback active
+ int lines_; // #lines in buffer (optimization: Fl_Text_Buffer slow to calc this)
+ bool scrollaway_; // true when user changed vscroll away from bottom
+ bool scrolling_; // true while scroll callback active
// Fl_Text_Display vscrollbar's callback+data
- Fl_Callback *orig_vscroll_cb;
- void *orig_vscroll_data;
+ Fl_Callback *orig_vscroll_cb_;
+ void *orig_vscroll_data_;
// Style table
const Fl_Text_Display::Style_Table_Entry *stable_; // the active style table
- int stable_size_; // active style table size (in bytes)
- int normal_style_index_; // "normal" style used by "\033[0m" reset sequence
- int current_style_index_; // current style used for drawing text
+ int stable_size_; // active style table size (in bytes)
+ int normal_style_index_; // "normal" style used by "\033[0m" reset sequence
+ int current_style_index_; // current style used for drawing text
+ char current_style_; // current 'style char' (e.g. 'A' = first style entry)
+ Fl_Escape_Seq escseq; // escape sequence state handler
+ // String parsing vars initialized/used by append(), used by handle_backspace() etc.
+ char *ntm_; // new text memory (ntm) - malloc()ed by append() for output text
+ char *ntp_; // new text ptr (ntp) - points into ntm buffer
+ char *nsm_; // new style memory (nsm) - malloc()ed by append() for output style
+ char *nsp_; // new style ptr (nsp) - points into nsm buffer
public:
Fl_Simple_Terminal(int X,int Y,int W,int H,const char *l=0);
@@ -157,6 +200,7 @@ public:
int normal_style_index() const;
void current_style_index(int);
int current_style_index() const;
+ int current_style() const;
// Terminal text management
void append(const char *s, int len=-1);
@@ -188,6 +232,8 @@ protected:
void vscroll_cb2(Fl_Widget*, void*);
static void vscroll_cb(Fl_Widget*, void*);
void backspace_buffer(unsigned int count);
+ void handle_backspace();
+ void append_ansi(const char *s, int len);
};
#endif
diff --git a/src/Fl_Simple_Terminal.cxx b/src/Fl_Simple_Terminal.cxx
index 67989a5e1..680b157dd 100644
--- a/src/Fl_Simple_Terminal.cxx
+++ b/src/Fl_Simple_Terminal.cxx
@@ -64,12 +64,217 @@ static int strcnt(const char *s, char c) {
return count;
}
+// --- Fl_Escape_Seq ----------------------------------------------------------
+
+// Append char to buf[] safely (with bounds checking)
+// Returns:
+// success - ok
+// fail - buffer full/overflow
+//
+int Fl_Simple_Terminal::Fl_Escape_Seq::append_buf(char c) {
+ if ( bufp_ >= bufendp_ ) return fail; // end of buffer reached?
+ *bufp_++ = c;
+ *bufp_ = 0; // keep buf[] null terminated
+ return success;
+}
+
+// Append whatever integer string is at valbufp into vals_[] safely w/bounds checking
+// Assumes valbufp points to a null terminated string.
+// Returns:
+// success - parsed ok
+// fail - error occurred (non-integer, or vals_[] full)
+//
+int Fl_Simple_Terminal::Fl_Escape_Seq::append_val() {
+ if ( vali_ == maxvals ) { // too many vals_[] already?
+ return fail; // fail if vals_[] full
+ }
+ if ( !valbufp_ || (*valbufp_ == 0) ) { // no integer to parse? e.g. ESC[;m
+ vals_[vali_] = 0; // handle as if it was zero, e.g. ESC[0;
+ } else if ( sscanf(valbufp_, "%d", &vals_[vali_]) != 1 ) { // Parse integer into vals_[]
+ return fail; // fail if parsed a non-integer
+ }
+ if ( ++vali_ >= maxvals ) { // advance val index, fail if too many vals
+ vali_ = maxvals-1; // clamp
+ return fail; // fail
+ }
+ valbufp_ = 0; // parsed val ok, reset valbufp to NULL
+ return success;
+}
+
+// Ctor
+Fl_Simple_Terminal::Fl_Escape_Seq::Fl_Escape_Seq() {
+ reset();
+}
+
+// Reset the class
+void Fl_Simple_Terminal::Fl_Escape_Seq::reset() {
+ esc_mode_ = 0; // disable ESC mode, so parse_in_progress() returns false
+ bufp_ = buf_; // point to beginning of buffer
+ bufendp_ = buf_ + (maxbuf - 1); // point to end of buffer
+ valbufp_ = 0; // disable val ptr (no vals parsed yet)
+ vali_ = 0; // zero val index
+ buf_[0] = 0; // null terminate buffer
+ vals_[0] = 0; // first val[] 0
+}
+
+// Return current escape mode.
+// This is really only valid after parse() returns 'completed'.
+// After a reset() this will return 0.
+//
+char Fl_Simple_Terminal::Fl_Escape_Seq::esc_mode() const {
+ return esc_mode_;
+}
+
+// Set current escape mode.
+void Fl_Simple_Terminal::Fl_Escape_Seq::esc_mode(char val) {
+ esc_mode_ = val;
+}
+
+// Return the total vals parsed.
+// This is really only valid after parse() returns 'completed'.
+//
+int Fl_Simple_Terminal::Fl_Escape_Seq::total_vals() const {
+ return vali_;
+}
+
+// Return the value at index i.
+// i is not range checked; it's assumed 0 <= i < total_vals().
+// It is only valid to call this after parse() returns 'completed'.
+//
+int Fl_Simple_Terminal::Fl_Escape_Seq::val(int i) const {
+ return vals_[i];
+}
+
+// See if we're in the middle of parsing an ESC sequence
+bool Fl_Simple_Terminal::Fl_Escape_Seq::parse_in_progress() const {
+ return (esc_mode_ == 0) ? false : true;
+}
+
+// Handle parsing an escape sequence.
+//
+// Call this only if parse_in_progress() is true.
+// Passing ESC does a reset() and sets esc_mode() to ESC.
+// When a full escape sequence has been parsed, 'completed' is returned (see below)
+//
+// Typical use pattern (this is unverified code, shown just to give a general gist):
+//
+// while ( *s ) { // walk text that may contain ESC sequences
+// if ( *s == 0x1b ) {
+// escseq.parse(*s++); // start parsing ESC seq (does a reset())
+// continue;
+// } else if ( escseq.parse_in_progress() ) { // continuing to parse an ESC seq?
+// switch (escseq.parse(*s++)) { // parse char, advance s..
+// case fail: escseq.reset(); continue; // failed? reset, continue..
+// case success: continue; // keep parsing..
+// case completed: // parsed complete esc sequence?
+// break;
+// }
+// // Handle parsed esc sequence here..
+// switch ( escseq.esc_mode() ) {
+// case 'm': // ESC[...m?
+// for ( int i=0; i<escseq.total_vals(); i++ ) {
+// int val = escseq.val(i);
+// ..handle values here..
+// }
+// break;
+// case 'J': // ESC[#J?
+// ..handle..
+// break;
+// }
+// escseq.reset(); // done handling escseq, reset()
+// continue;
+// } else {
+// ..handle non-escape chars here..
+// }
+// ++s; // advance thru string
+// }
+//
+// Returns:
+// fail - error occurred: escape sequence invalid, class is reset()
+// success - parsing ESC sequence OK so far, still in progress/not done yet
+// completed - complete ESC sequence was parsed, esc_mode() will be, e.g.
+// 'm' - ESC[#;#..m sequence parsed, val() has value(s) parsed
+// 'J' - ESC[#J sequence parsed, val() has value(s) parsed
+// 'A' thru 'D' - cursor up/down/right/left movement (ESC A/B/C/D)
+//
+int Fl_Simple_Terminal::Fl_Escape_Seq::parse(char c) {
+ // NOTE: During parsing esc_mode() will be:
+ // 0 - reset/not parsing
+ // 0x1b - ESC received, expecting next one of A/B/C/D or '['
+ // '[' - actively parsing CSI sequence, e.g. ESC[
+ //
+ // At the /end/ of parsing, after 'completed' is returned,
+ // esc_mode() will be the mode setting char, e.g. 'm' for 'ESC[0m', etc.
+ //
+ if ( c == 0 ) { // NULL? (caller should really never send us this)
+ return success; // do nothing -- leave state unchanged, return 'success'
+ } else if ( c == 0x1b ) { // ESC at ANY time resets class/begins new ESC sequence
+ reset();
+ esc_mode(0x1b);
+ if ( append_buf(c) < 0 ) goto pfail; // save ESC in buf
+ return success;
+ } else if ( c < ' ' || c >= 0x7f ) { // any other control or binary characters?
+ goto pfail; // reset + fail out of esc sequence parsing
+ }
+
+ // Whatever the character is, handle it depending on esc_mode..
+ if ( esc_mode() == 0x1b ) { // in ESC mode?
+ if ( c == '[' ) { // ESC[?
+ esc_mode(c); // switch to parsing mode for ESC[#;#;#..
+ vali_ = 0; // zero vals_[] index
+ valbufp_ = 0; // valbufp NULL (no vals yet)
+ if ( append_buf(c) < 0 ) goto pfail; // save '[' in buf
+ return success; // success
+ } else if ( c >= 'A' && c <= 'D' ) { // ESC A/B/C/D? (cursor movement?)
+ esc_mode(c); // use as mode
+ vali_ = 0;
+ if ( append_buf(c) < 0 ) goto pfail; // save A/B/C/D in buf
+ return success; // success
+ } else { // ESCx? not supported
+ goto pfail;
+ }
+ } else if ( esc_mode() == '[' ) { // '[' mode? e.g. ESC[...
+ if ( c == ';' ) { // ';' indicates end of a value, e.g. ESC[0;2..
+ if (append_val() < 0 ) goto pfail; // append value parsed so far, vali gets inc'ed
+ if (append_buf(c) < 0 ) goto pfail; // save ';' in buf
+ return success;
+ }
+ if ( isdigit(c) ) { // parsing an integer?
+ if ( !valbufp_ ) // valbufp not set yet?
+ { valbufp_ = bufp_; } // point to first char in integer string
+ if ( append_buf(c) < 0 ) goto pfail; // add value to buffer
+ return success;
+ }
+ // Not a ; or digit? fall thru to [A-Z,a-z] check
+ } else { // all other esc_mode() chars are fail/unknown
+ goto pfail;
+ }
+ if ( ( c >= 'A' && c<= 'Z') || // ESC#X or ESC[...X, where X is [A-Z,a-z]?
+ ( c >= 'a' && c<= 'z') ) {
+ if (append_val() < 0 ) goto pfail; // append any trailing vals just before letter
+ if (append_buf(c) < 0 ) goto pfail; // save letter in buffer
+ esc_mode(c); // change mode to the mode setting char
+ if ( vali_ == 0 ) { // no vals were specified? assume 0 (e.g. ESC[J? assume ESC[0J)
+ vals_[vali_++] = 0; // force vals_[0] to be 0, and vali = 1;
+ }
+ return completed; // completed/done
+ }
+ // Any other chars? reset+fail
+pfail:
+ reset();
+ return fail;
+}
+
+//
+// --- Fl_Simple_Terminal -----------------------------------------------------
+//
+
// Vertical scrollbar callback intercept
void Fl_Simple_Terminal::vscroll_cb2(Fl_Widget *w, void*) {
- scrolling = 1;
- orig_vscroll_cb(w, orig_vscroll_data);
- scrollaway = (mVScrollBar->value() != mVScrollBar->maximum());
- scrolling = 0;
+ scrolling_ = 1;
+ orig_vscroll_cb_(w, orig_vscroll_data_);
+ scrollaway_ = (mVScrollBar->value() != mVScrollBar->maximum());
+ scrolling_ = 0;
}
void Fl_Simple_Terminal::vscroll_cb(Fl_Widget *w, void *data) {
Fl_Simple_Terminal *o = (Fl_Simple_Terminal*)data;
@@ -83,9 +288,9 @@ Fl_Simple_Terminal::Fl_Simple_Terminal(int X,int Y,int W,int H,const char *l) :
history_lines_ = 500; // something 'reasonable'
stay_at_bottom_ = true;
ansi_ = false;
- lines = 0; // note: lines!=mNBufferLines when lines are wrapping
- scrollaway = false;
- scrolling = false;
+ lines_ = 0; // note: lines!=mNBufferLines when lines are wrapping
+ scrollaway_ = false;
+ scrolling_ = false;
// These defaults similar to typical DOS/unix terminals
textfont(FL_COURIER);
color(FL_BLACK);
@@ -102,13 +307,14 @@ Fl_Simple_Terminal::Fl_Simple_Terminal(int X,int Y,int W,int H,const char *l) :
// being present, an annoying UI bug in Fl_Text_Display.
wrap_mode(Fl_Text_Display::WRAP_AT_BOUNDS, 0);
// Style table
- stable_ = &builtin_stable[0];
- stable_size_ = builtin_stable_size;
+ stable_ = &builtin_stable[0];
+ stable_size_ = builtin_stable_size;
normal_style_index_ = builtin_normal_index;
current_style_index_ = builtin_normal_index;
+ current_style_ = 'A' + 0;
// Intercept vertical scrolling
- orig_vscroll_cb = mVScrollBar->callback();
- orig_vscroll_data = mVScrollBar->user_data();
+ orig_vscroll_cb_ = mVScrollBar->callback();
+ orig_vscroll_data_ = mVScrollBar->user_data();
mVScrollBar->callback(vscroll_cb, (void*)this);
}
@@ -348,6 +554,7 @@ int Fl_Simple_Terminal::normal_style_index() const {
void Fl_Simple_Terminal::current_style_index(int val) {
// Wrap index to ensure it's never larger than table
current_style_index_ = abs(val) % (stable_size_ / STE_SIZE);
+ current_style_ = 'A' + current_style_index_;
}
/**
@@ -366,6 +573,19 @@ int Fl_Simple_Terminal::current_style_index() const {
}
/**
+ Get the current style char used for style buffer.
+ This character appends in parallel with any text in the text buffer
+ to specify the per-character styling. This is typically 'A' for the
+ first entry, 'B' for the second entry, etc.
+
+ This value is changed by current_style_index(int).
+ \see current_style_index(int)
+*/
+int Fl_Simple_Terminal::current_style() const {
+ return current_style_;
+}
+
+/**
Set a user defined style table, which controls the font colors,
faces, weights and sizes available for the terminal's text content.
@@ -475,7 +695,7 @@ void Fl_Simple_Terminal::style_table(Fl_Text_Display::Style_Table_Entry *stable,
should need to call this.
*/
void Fl_Simple_Terminal::enforce_stay_at_bottom() {
- if ( stay_at_bottom_ && buffer() && !scrollaway ) {
+ if ( stay_at_bottom_ && buffer() && !scrollaway_ ) {
scroll(mNBufferLines, 0);
}
}
@@ -489,8 +709,8 @@ void Fl_Simple_Terminal::enforce_stay_at_bottom() {
should need to call this.
*/
void Fl_Simple_Terminal::enforce_history_lines() {
- if ( history_lines() > -1 && lines > history_lines() ) {
- int trimlines = lines - history_lines();
+ if ( history_lines() > -1 && lines_ > history_lines() ) {
+ int trimlines = lines_ - history_lines();
remove_lines(0, trimlines); // remove lines from top
}
}
@@ -517,125 +737,147 @@ void Fl_Simple_Terminal::backspace_buffer(unsigned int count) {
}
/**
- Appends new string 's' to terminal.
-
- The string can contain UTF-8, crlf's, and ANSI sequences are
- also supported when ansi(bool) is set to 'true'.
-
- \param s string to append.
-
- \param len optional length of string can be specified if known
- to save the internals from having to call strlen()
+ Handle a Unicode aware backspace.
+ This flushes the string parsed so far to Fl_Text_Display,
+ then lets Fl_Text_Display handle the unicode aware backspace.
+*/
+void Fl_Simple_Terminal::handle_backspace() {
+ // FLUSH TEXT TO TEXT DISPLAY
+ // This prevents any Unicode multibyte char split across
+ // our string buffer and Fl_Text_Display, and also allows
+ // Fl_Text_Display to handle unicode aware backspace.
+
+ // 1) Null temrinate buffers
+ *ntp_ = 0;
+ *nsp_ = 0;
+ // 2) Flush text to Fl_Text_Display
+ buf->append(ntm_); // flush text to FTD
+ sbuf->append(nsm_); // flush style to FTD
+ // 3) Rewind buffer and sp, restore saved chars
+ ntp_ = ntm_;
+ nsp_ = nsm_;
+ // 4) Let Fl_Text_Display handle unicode aware backspace
+ backspace_buffer(1);
+}
- \see printf(), vprintf(), text(), clear()
+/**
+ Handle appending string with ANSI escape sequences, and other 'special'
+ character processing (such as backspaces).
*/
-void Fl_Simple_Terminal::append(const char *s, int len) {
- // Remove ansi codes and adjust style buffer accordingly.
- if ( ansi() ) {
+void Fl_Simple_Terminal::append_ansi(const char *s, int len) {
int nstyles = stable_size_ / STE_SIZE;
- int trim = 0; // #chars to trim (backspace into existing buffer)
if ( len < 0 ) len = (int)strlen(s);
- // New text buffer (after ansi codes parsed+removed)
- char *ntm = (char*)malloc(len+1); // new text memory
- char *ntp = ntm; // new text ptr
- char *nsm = (char*)malloc(len+1); // new style memory
- char *nsp = nsm; // new style ptr
- // ANSI values
- char astyle = 'A'+current_style_index_; // the running style index
- const char *esc = 0;
- const char *sp = s;
+ // ntm/tsm - new text buffer (after ansi codes parsed+removed)
+ ntm_ = (char*)malloc(len+1); // new text memory
+ ntp_ = ntm_; // new text ptr
+ nsm_ = (char*)malloc(len+1); // new style memory
+ nsp_ = nsm_; // new style ptr
// Walk user's string looking for codes, modify new text/style text as needed
+ const char *sp = s;
while ( *sp ) {
- if ( *sp == 033 ) { // "\033.."
- esc = sp++;
- switch (*sp) {
- case 0: // "\033<NUL>"? stop
+ if (*sp == 0x1b ) { // start of ESC sequence?
+ escseq.parse(*sp++); // start parsing..
+ continue;
+ }
+ if ( escseq.parse_in_progress() ) { // ESC sequence in progress?
+ switch ( escseq.parse(*sp) ) { // parse until completed or fail
+ case Fl_Escape_Seq::fail: // parsing error?
+ escseq.reset(); // ..reset and continue
+ ++sp;
continue;
- case '[': { // "\033[.."
+ case Fl_Escape_Seq::success: // parsed ok / still in progress
++sp;
- int vals[4], tv=0, seqdone=0;
- while ( *sp && !seqdone && isdigit(*sp) ) { // "\033[#;#.."
- char *newsp;
- long a = strtol(sp, &newsp, 10);
- sp = newsp;
- vals[tv++] = (a<0) ? 0 : a; // prevent negative values
- if ( tv >= 4 ) // too many #'s specified? abort sequence
- { seqdone = 1; sp = esc+1; continue; }
- switch(*sp) {
- case ';': // numeric separator
- ++sp;
- continue;
- case 'J': // erase in display
- switch (vals[0]) {
- case 0: // \033[0J -- clear to eol
- // unsupported
- break;
- case 1: // \033[1J -- clear to sol
- // unsupported
- break;
- case 2: // \033[2J -- clear entire screen
- clear(); // clear text buffer
- ntp = ntm; // clear text contents accumulated so far
- nsp = nsm; // clear style contents ""
- break;
- }
- ++sp;
- seqdone = 1;
- continue;
- case 'm': // set color
- if ( tv > 0 ) { // at least one value parsed?
- current_style_index_ = (vals[0] == 0) // "reset"?
- ? normal_style_index_ // use normal color for "reset"
- : (vals[0] % nstyles); // use user's value, wrapped to ensure not larger than table
- astyle = 'A' + current_style_index_; // convert index -> style buffer char
- }
- ++sp;
- seqdone = 1;
- continue;
- case '\0': // EOS in middle of sequence?
- *ntp = 0; // end of text
- *nsp = 0; // end of style
- seqdone = 1;
- continue;
- default: // un-supported cmd?
- seqdone = 1;
- sp = esc+1; // continue parsing just past esc
- break;
- } // switch
- } // while
- } // case '['
- } // switch
- } // \033
- else if ( *sp == 8 ) { // backspace?
- if ( --ntp < ntm ) { // dec ntp, see if beyond start?
- ++trim; // flag need to trim existing buffer
- ntp = ntm; // clamp
+ continue;
+ case Fl_Escape_Seq::completed: // completed parsing ESC sequence?
+ break;
}
- if ( --nsp < nsm ) { // adjust style buffer too
- nsp = nsm; // clamp
+ // Escape sequence completed ok? handle it
+ // Walk all values parsed from ESC[#;#;#..x
+ //
+ for ( int i=0; i<escseq.total_vals(); i++ ) {
+ int val = escseq.val(i);
+ switch (escseq.esc_mode() ) {
+ // ERASE IN DISPLAY (ED)
+ case 'J': // ESC[#J
+ switch (val) {
+ case 0: // ESC[0J -- clear to eol
+ // unsupported
+ break;
+ case 1: // ESC[1J -- clear to sol
+ // unsupported
+ break;
+ case 2: // ESC[2J -- clear visible screen
+ // NOTE: Currently we clear the /entire screen history/, and
+ // moves cursor to the top of buffer.
+ //
+ // ESC[2J should really only clear the /visible display/
+ // not affecting screen history or cursor position.
+ //
+ clear(); // clear text buffer
+ ntp_ = ntm_; // clear text contents accumulated so far
+ nsp_ = nsm_; // clear style contents ""
+ break;
+ }
+ break;
+ // SELECT GRAPHIC RENDITION (SGR)
+ case 'm':
+ if ( val == 0 ) { // ESC[0m? (reset color)
+ // Switch to "normal color"
+ current_style_index(normal_style_index_);
+ } else { // ESC[#m? (set some specific color)
+ // Use modulus to map into styles[] buffer
+ current_style_index(val % nstyles);
+ }
+ break;
+ // UNSUPPORTED MODES
+ default:
+ break; // unsupported
+ }
}
+ escseq.reset(); // reset after handling escseq
+ ++sp; // advance thru string
+ continue;
+ } else if ( *sp == 8 ) { // backspace?
+ handle_backspace();
sp++;
- }
- else {
- // Non-ANSI character?
- if ( *sp == '\n' ) ++lines; // keep track of #lines
- *ntp++ = *sp++; // pass char thru
- *nsp++ = astyle; // use current style
+ } else { // Not ANSI or backspace? append to display
+ if ( *sp == '\n' ) ++lines_; // crlf? keep track of #lines
+ *ntp_++ = *sp++; // pass char thru
+ *nsp_++ = current_style(); // use current style
}
} // while
- *ntp = 0;
- *nsp = 0;
- //::printf(" RESULT: ntm='%s'\n", ntm);
- //::printf(" RESULT: nsm='%s'\n", nsm);
- backspace_buffer(trim); // destructive backspace into buffer (if any)
- buf->append(ntm); // new text memory
- sbuf->append(nsm); // new style memory
- free(ntm);
- free(nsm);
+ *ntp_ = 0;
+ *nsp_ = 0;
+ //::printf(" RESULT: ntm='%s'\n", ntm_);
+ //::printf(" RESULT: nsm='%s'\n", nsm_);
+ buf->append(ntm_); // new text memory
+ sbuf->append(nsm_); // new style memory
+ free(ntm_);
+ free(nsm_);
+}
+
+/**
+ Appends new string 's' to terminal.
+
+ The string can contain UTF-8, crlf's.
+ And if ansi(bool) is set to 'true', ANSI 'ESC' sequences (such as ESC[1m)
+ and other control characters (such as backspace) are handled.
+
+ \param s string to append.
+
+ \param len optional length of string can be specified if known
+ to save the internals from having to call strlen()
+
+ \see printf(), vprintf(), text(), clear()
+*/
+void Fl_Simple_Terminal::append(const char *s, int len) {
+ // Remove ansi codes and adjust style buffer accordingly.
+ if ( ansi() ) {
+ append_ansi(s, len);
} else {
- // non-ansi buffer
+ // raw append
buf->append(s);
- lines += ::strcnt(s, '\n'); // count total line feeds in string added
+ lines_ += ::strcnt(s, '\n'); // count total line feeds in string added
}
enforce_history_lines();
enforce_stay_at_bottom();
@@ -729,7 +971,7 @@ void Fl_Simple_Terminal::vprintf(const char *fmt, va_list ap) {
void Fl_Simple_Terminal::clear() {
buf->text("");
sbuf->text("");
- lines = 0;
+ lines_ = 0;
}
/**
@@ -750,8 +992,8 @@ void Fl_Simple_Terminal::remove_lines(int start, int count) {
} else {
buf->remove(spos, epos);
}
- lines -= count;
- if ( lines < 0 ) lines = 0;
+ lines_ -= count;
+ if ( lines_ < 0 ) lines_ = 0;
}
/**