diff options
Diffstat (limited to 'src')
| -rw-r--r-- | src/CMakeLists.txt | 1 | ||||
| -rw-r--r-- | src/Fl_Simple_Terminal.cxx | 747 | ||||
| -rw-r--r-- | src/Makefile | 1 |
3 files changed, 749 insertions, 0 deletions
diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 5c6532a59..ca6dc74be 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -59,6 +59,7 @@ set (CPPFILES Fl_Scroll.cxx Fl_Scrollbar.cxx Fl_Shared_Image.cxx + Fl_Simple_Terminal.cxx Fl_Single_Window.cxx Fl_Slider.cxx Fl_Spinner.cxx diff --git a/src/Fl_Simple_Terminal.cxx b/src/Fl_Simple_Terminal.cxx new file mode 100644 index 000000000..c10e8c832 --- /dev/null +++ b/src/Fl_Simple_Terminal.cxx @@ -0,0 +1,747 @@ +// +// "$Id$" +// +// A simple terminal widget for Fast Light Tool Kit (FLTK). +// +// Copyright 1998-2011 by Bill Spitzak and others. +// Copyright 2017 by Greg Ercolano. +// +// This library is free software. Distribution and use rights are outlined in +// the file "COPYING" which should have been included with this file. If this +// file is missing or damaged, see the license at: +// +// http://www.fltk.org/COPYING.php +// +// Please report all bugs and problems on the following page: +// +// http://www.fltk.org/str.php +// + +#include <ctype.h> /* isdigit */ +#include <string.h> /* memset */ +#include <stdlib.h> /* strtol */ +#include <FL/Fl_Simple_Terminal.H> +#include <FL/Fl.H> +#include <stdarg.h> +#include "flstring.h" + +#define STE_SIZE sizeof(Fl_Text_Display::Style_Table_Entry) + +// Default style table +// Simple ANSI style colors with an FL_COURIER font. +// Due to how the modulo works for 20 items, the first 10 map to 40 +// and the second 10 map to 30. +// +static const Fl_Text_Display::Style_Table_Entry builtin_stable[] = { + // FONT COLOR FONT FACE SIZE INDEX COLOR NAME ANSI ANSI MODULO INDEX + // ---------- --------------- ------ ------ -------------- -------- ----------------- + { 0x80808000, FL_COURIER, 14 }, // 0 - Bright Black \033[40m 0,20,40,.. + { 0xff000000, FL_COURIER, 14 }, // 1 - Bright Red \033[41m ^^ + { 0x00ff0000, FL_COURIER, 14 }, // 2 - Bright Green \033[42m + { 0xffff0000, FL_COURIER, 14 }, // 3 - Bright Yellow \033[43m + { 0x0000ff00, FL_COURIER, 14 }, // 4 - Bright Blue \033[44m + { 0xff00ff00, FL_COURIER, 14 }, // 5 - Bright Magenta \033[45m + { 0x00ffff00, FL_COURIER, 14 }, // 6 - Bright Cyan \033[46m + { 0xffffff00, FL_COURIER, 14 }, // 7 - Bright White \033[47m + { 0x00000000, FL_COURIER, 14 }, // 8 - x + { 0x00000000, FL_COURIER, 14 }, // 9 - x + { 0x00000000, FL_COURIER, 14 }, // 10 - Medium Black \033[30m 10,30,50,.. + { 0xbb000000, FL_COURIER, 14 }, // 11 - Medium Red \033[31m ^^ + { 0x00bb0000, FL_COURIER, 14 }, // 12 - Medium Green \033[32m + { 0xbbbb0000, FL_COURIER, 14 }, // 13 - Medium Yellow \033[33m + { 0x0000cc00, FL_COURIER, 14 }, // 14 - Medium Blue \033[34m + { 0xbb00bb00, FL_COURIER, 14 }, // 15 - Medium Magenta \033[35m + { 0x00bbbb00, FL_COURIER, 14 }, // 16 - Medium Cyan \033[36m + { 0xbbbbbb00, FL_COURIER, 14 }, // 17 - Medium White \033[37m (also "\033[0m" reset) + { 0x00000000, FL_COURIER, 14 }, // 18 - x + { 0x00000000, FL_COURIER, 14 } // 19 - x +}; +static const int builtin_stable_size = sizeof(builtin_stable); +static const char builtin_normal_index = 17; // the reset style index used by \033[0m + +// Count how many times character 'c' appears in string 's' +static int strcnt(const char *s, char c) { + int count = 0; + while ( *s ) { if ( *s++ == c ) ++count; } + return count; +} + +// 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; +} +void Fl_Simple_Terminal::vscroll_cb(Fl_Widget *w, void *data) { + Fl_Simple_Terminal *o = (Fl_Simple_Terminal*)data; + o->vscroll_cb2(w,(void*)0); +} + +/** + Creates a new Fl_Simple_Terminal widget that can be a child of other FLTK widgets. +*/ +Fl_Simple_Terminal::Fl_Simple_Terminal(int X,int Y,int W,int H,const char *l) : Fl_Text_Display(X,Y,W,H,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; + // These defaults similar to typical DOS/unix terminals + textfont(FL_COURIER); + color(FL_BLACK); + textcolor(FL_WHITE); + selection_color(FL_YELLOW); // default dark blue looks bad for black background + show_cursor(true); + cursor_color(FL_GREEN); + cursor_style(Fl_Text_Display::BLOCK_CURSOR); + // Setup text buffer + buf = new Fl_Text_Buffer(); + buffer(buf); + sbuf = new Fl_Text_Buffer(); // allocate whether we use it or not + // XXX: We use WRAP_AT_BOUNDS to prevent the hscrollbar from /always/ + // 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; + normal_style_index_ = builtin_normal_index; + current_style_index_ = builtin_normal_index; + // Intercept vertical scrolling + orig_vscroll_cb = mVScrollBar->callback(); + orig_vscroll_data = mVScrollBar->user_data(); + mVScrollBar->callback(vscroll_cb, (void*)this); +} + +/** + Destructor for this widget; removes any internal allocations + for the terminal, including text buffer, style buffer, etc. +*/ +Fl_Simple_Terminal::~Fl_Simple_Terminal() { + buffer(0); // disassociate buffer /before/ we delete it + if ( buf ) { delete buf; buf = 0; } + if ( sbuf ) { delete sbuf; sbuf = 0; } +} + +/** + Gets the current value of the stay_at_bottom(bool) flag. + + When true, the terminal tries to keep the scrollbar scrolled + to the bottom when new text is added. + + \see stay_at_bottom(bool) +*/ +bool Fl_Simple_Terminal::stay_at_bottom() const { + return stay_at_bottom_; +} + +/** + Configure the terminal to remain scrolled to the bottom when possible, + chasing the end of the buffer whenever new text is added. + + If disabled, the terminal behaves more like a text display widget; + the scrollbar does not chase the bottom of the buffer. + + If the user scrolls away from the bottom, this 'chasing' feature is + temporarily disabled. This prevents the user from having to fight + the scrollbar chasing the end of the buffer while browsing when + new text is also being added asynchronously. When the user returns the + scroller to the bottom of the display, the chasing behavior resumes. + + The default is 'true'. +*/ +void Fl_Simple_Terminal::stay_at_bottom(bool val) { + if ( stay_at_bottom_ == val ) return; // no change + stay_at_bottom_ = val; + if ( stay_at_bottom_ ) enforce_stay_at_bottom(); +} + +/** + Get the maximum number of terminal history lines last set by history_lines(int). + + -1 indicates an unlimited scroll history. + + \see history_lines(int) +*/ +int Fl_Simple_Terminal::history_lines() const { + return history_lines_; +} + +/** + Sets the maximum number of lines for the terminal history. + + The new limit value is automatically enforced on the current screen + history, truncating off any lines that exceed the new limit. + + When a limit is set, the buffer is trimmed as new text is appended, + ensuring the buffer never displays more than the specified number of lines. + + The default maximum is 500 lines. + + \param maxlines Maximum number of lines kept on the terminal buffer history. + Use -1 for an unlimited scroll history. + A value of 0 is not recommended. +*/ +void Fl_Simple_Terminal::history_lines(int maxlines) { + history_lines_ = maxlines; + enforce_history_lines(); +} + +/** + Get the state of the ANSI flag which enables/disables + the handling of ANSI sequences in text. + + When true, ANSI sequences in the text stream control color, font + and font sizes of text (e.g. "\033[41mThis is Red\033[0m"). + For more info, see ansi(bool). + + \see ansi(bool) +*/ +bool Fl_Simple_Terminal::ansi() const { + return ansi_; +} + +/** + Enable/disable support of ANSI sequences like "\033[31m", which sets the + color/font/weight/size of any text that follows. + + If enabled, ANSI sequences of the form "\033[#m" can be used to change + font color, face, and size, where '#' is an index number into the current + style table. These "escape sequences" are hidden from view. + + If disabled, the textcolor() / textfont() / textsize() methods define + the color and font for all text in the terminal. ANSI sequences are not + handled specially, and rendered as raw text. + + A built-in style table is provided, but you can configure a custom style table + using style_table(Style_Table_Entry*,int,int) for your own colors and fonts. + + The built-in style table supports these ANSI sequences: + + ANSI Sequence Color Name Font Face + Size Remarks + ------------- -------------- ---------------- ----------------------- + "\033[0m" "Normal" FL_COURIER, 14 Resets to default color/font/weight/size + "\033[30m" Medium Black FL_COURIER, 14 + "\033[31m" Medium Red FL_COURIER, 14 + "\033[32m" Medium Green FL_COURIER, 14 + "\033[33m" Medium Yellow FL_COURIER, 14 + "\033[34m" Medium Blue FL_COURIER, 14 + "\033[35m" Medium Magenta FL_COURIER, 14 + "\033[36m" Medium Cyan FL_COURIER, 14 + "\033[37m" Medium White FL_COURIER, 14 The color when "\033[0m" reset is used + "\033[40m" Bright Black FL_COURIER, 14 + "\033[41m" Bright Red FL_COURIER, 14 + "\033[42m" Bright Green FL_COURIER, 14 + "\033[43m" Bright Yellow FL_COURIER, 14 + "\033[44m" Bright Blue FL_COURIER, 14 + "\033[45m" Bright Magenta FL_COURIER, 14 + "\033[46m" Bright Cyan FL_COURIER, 14 + "\033[47m" Bright White FL_COURIER, 14 + + Here's example code demonstrating the use of ANSI codes to select + the built-in colors, and how it looks in the terminal: + + \image html simple-terminal-default-ansi.png "Fl_Simple_Terminal built-in ANSI sequences" + \image latex simple-terminal-default-ansi.png "Fl_Simple_Terminal built-in ANSI sequences" width=4cm + + \note Changing the ansi(bool) value clears the buffer and forces a redraw(). + \note Enabling ANSI mode overrides textfont(), textsize(), textcolor() + completely, which are controlled instead by current_style_index() + and the current style_table(). + \see style_table(Style_Table_Entry*,int,int), + current_style_index(), + normal_style_index() +*/ +void Fl_Simple_Terminal::ansi(bool val) { + ansi_ = val; + clear(); + if ( ansi_ ) { + highlight_data(sbuf, stable_, stable_size_/STE_SIZE, 'A', 0, 0); + } else { + // XXX: highlight_data(0,0,0,'A',0,0) can crash, so to disable + // we use sbuf + builtin_stable but /set nitems to 0/. + highlight_data(sbuf, builtin_stable, 0, 'A', 0, 0); + } + redraw(); +} + +/** + Return the current style table being used. + + This is the value last passed as the 1st argument to + style_table(Style_Table_Entry*,int,int). If no style table + was defined, the built-in style table is returned. + + ansi(bool) must be set to 'true' for the style table to be used at all. + + \see style_table(Style_Table_Entry*,int,int) +*/ +const Fl_Text_Display::Style_Table_Entry *Fl_Simple_Terminal::style_table() const { + return stable_; +} + +/** + Return the current style table's size (in bytes). + + This is the value last passed as the 2nd argument to + style_table(Style_Table_Entry*,int,int). +*/ +int Fl_Simple_Terminal::style_table_size() const { + return stable_size_; +} + +/** + Sets the style table index used by the ANSI terminal reset + sequence "\033[0m", which resets the current drawing + color/font/weight/size to "normal". + + Effective only when ansi(bool) is 'true'. + + \see ansi(bool), style_table(Style_Table_Entry*,int,int) + \note Changing this value does *not* change the current drawing color. + To change that, use current_style_index(int). +*/ +void Fl_Simple_Terminal::normal_style_index(int val) { + // Wrap index to ensure it's never larger than table + normal_style_index_ = val % (stable_size_ / STE_SIZE); +} + +/** + Gets the style table index used by the ANSI terminal reset + sequence "\033[0m". + + This is the value last set by normal_style_index(int), or as set by + the 3rd argument to style_table(Style_Table_Entry*,int,int). + + \see normal_style_index(int), ansi(bool), style_table(Style_Table_Entry*,int,int) +*/ +int Fl_Simple_Terminal::normal_style_index() const { + return normal_style_index_; +} + +/** + Set the style table index used as the current drawing + color/font/weight/size for new text. + + For example: + \code + : + tty->ansi(true); + tty->append("Some normal text.\n"); + tty->current_style_index(2); // same as "\033[2m" + tty->append("This text will be green.\n"); + tty->current_style_index(tty->normal_style_index()); // same as "\033[0m" + tty->append("Back to normal text.\n"); + : + \endcode + + This value can also be changed by an ANSI sequence like "\033[#m", + where # would be a new style index value. So if the application executes: + <tt>term->append("\033[4mTesting")</tt>, then current_style_index() + will be left set to 4. + + The index number specified should be within the number of items in the + current style table. Values larger than the table will be clamped to + the size of the table with a modulus operation. + + Effective only when ansi(bool) is 'true'. +*/ +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); +} + +/** + Get the style table index used as the current drawing + color/font/weight/size for new text. + + This value is also controlled by the ANSI sequence "\033[#m", + where # would be a new style index value. So if the application executes: + <tt>term->append("\033[4mTesting")</tt>, then current_style_index() + returns 4. + + \see current_style_index(int) +*/ +int Fl_Simple_Terminal::current_style_index() const { + return current_style_index_; +} + +/** + Set a user defined style table, which controls the font colors, + faces, weights and sizes available for the terminal's text content. + + ansi(bool) must be set to 'true' for the defined style table + to be used at all. + + If 'table' and 'size' are 0, then the "built in" style table is used. + For info about the built-in colors, see ansi(bool). + + Which style table entry used for drawing depends on the value last set + by current_style_index(), or by the ANSI sequence "\033[#m", where '#' + is the index into the style table, limited to the size of the table + via modulus. + + If the index# passed via "\033[#m" is larger than the number of elements + in the table, the value is clamped via modulus. So for a 10 element table, + the following ANSI codes would all be equivalent, selecting the 5th element + in the table: "\033[5m", "\033[15m", "\033[25m", etc. This is because + 5==(15%10)==(25%10), etc. + + A special exception is made for "\033[0m", which is supposed to "reset" + the current style table to default color/font/weight/size, as last set by + \p normal_style_index, or by the API method normal_style_index(int). + + In cases like the built-in style table, where the 17th item is the + "normal" color, the 'normal_style_index' is set to 17 so that "\033[0m" + resets to that color, instead of the first element in the table. + + If you want "\033[0m" to simply pick the first element in the table, + then set 'normal_style_index' to 0. + + An example of defining a custom style table (white courier 14, red courier 14, + and white helvetica 14): + \code + int main() { + : + // Our custom style table + Fl_Text_Display::Style_Table_Entry mystyle[] = { + // Font Color Font Face Font Size Index ANSI Sequence + // ---------- ---------------- --------- ----- ------------- + { FL_WHITE, FL_COURIER_BOLD, 14 }, // 0 "\033[0m" ("default") + { FL_RED, FL_COURIER_BOLD, 14 }, // 1 "\033[1m" + { FL_WHITE, FL_HELVETICA, 14 } // 2 "\033[2m" + }; + // Create terminal, enable ANSI and our style table + tty = new Fl_Simple_Terminal(..); + tty->ansi(true); // enable ANSI codes + tty->style_table(&mystyle[0], sizeof(mystyle), 0); // use our custom style table + : + // Now write to terminal, with ANSI that uses our style table + tty->printf("\033[0mNormal Text\033[1mRed Courier Text\n"); + tty->append("\033[2mWhite Helvetica\033[0mBack to normal.\n"); + : + \endcode + + \note Changing the style table clear()s the terminal. + \note You currently can't control /background/ color of text, + a limitation of Fl_Text_Display's current implementation. + \note The caller is responsible for managing the memory of the style table. + \note Until STR#3412 is repaired, Fl_Text_Display has scrolling bug if the + style table's font size != textsize() + + \param stable - the style table, an array of structs of the type + Fl_Text_Display::Style_Table_Entry + \param stable_size - the sizeof() the style table (in bytes) + \param normal_style_index - the style table index# used when the special + ANSI sequence "\033[0m" is encountered. + Normally use 0 so that sequence selects the + first item in the table. Only use different + values if a different entry in the table + should be the default. This value should + not be larger than the number of items in + the table, or it will be clamped with a + modulus operation. +*/ +void Fl_Simple_Terminal::style_table(Fl_Text_Display::Style_Table_Entry *stable, + int stable_size, int normal_style_index) { + // Wrap index to ensure it's never larger than table + normal_style_index = abs(normal_style_index) % (stable_size/STE_SIZE); + + if ( stable_ == 0 ) { + // User wants built-in style table? + stable_ = &builtin_stable[0]; + stable_size_ = builtin_stable_size; + normal_style_index_ = builtin_normal_index; // set the index used by \033[0m + current_style_index_ = builtin_normal_index; // set the index used for drawing new text + } else { + // User supplying custom style table + stable_ = stable; + stable_size_ = stable_size; + normal_style_index_ = normal_style_index; // set the index used by \033[0m + current_style_index_ = normal_style_index; // set the index used for drawing new text + } + clear(); // don't take any chances with old style info + highlight_data(sbuf, stable_, stable_size/STE_SIZE, 'A', 0, 0); +} + +/** + Scroll to last line unless someone has manually scrolled + the vertical scrollbar away from the bottom. + + This is a protected member called automatically by the public API functions. + Only internal methods or subclasses adjusting the internal buffer directly + should need to call this. +*/ +void Fl_Simple_Terminal::enforce_stay_at_bottom() { + if ( stay_at_bottom_ && buffer() && !scrollaway ) { + scroll(mNBufferLines, 0); + } +} + +/** + Enforce 'history_lines' limit on the history buffer by trimming off + lines from the top of the buffer. + + This is a protected member called automatically by the public API functions. + Only internal methods or subclasses adjusting the internal buffer directly + should need to call this. +*/ +void Fl_Simple_Terminal::enforce_history_lines() { + if ( history_lines() > -1 && lines > history_lines() ) { + int trimlines = lines - history_lines(); + remove_lines(0, trimlines); // remove lines from top + } +} + +/** + 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() + + \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() ) { + int nstyles = stable_size_ / STE_SIZE; + if ( len < 0 ) len = strlen(s); + // New text buffer (after ansi codes parsed+removed) + char *ntm = (char*)malloc(len+1); // new text memory + char *ntp = ntm; + char *nsm = (char*)malloc(len+1); // new style memory + char *nsp = nsm; + // ANSI values + char astyle = 'A'+current_style_index_; // the running style index + const char *esc = 0; + const char *sp = s; + // Walk user's string looking for codes, modify new text/style text as needed + while ( *sp ) { + if ( *sp == 033 ) { // "\033.." + esc = sp++; + switch (*sp) { + case 0: // "\033<NUL>"? stop + continue; + case '[': { // "\033[.." + ++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 { + // Non-ANSI character? + if ( *sp == '\n' ) ++lines; // keep track of #lines + *ntp++ = *sp++; // pass char thru + *nsp++ = astyle; // use current style + } + } // while + *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); + } else { + // non-ansi buffer + buf->append(s); + lines += ::strcnt(s, '\n'); // count total line feeds in string added + } + enforce_history_lines(); + enforce_stay_at_bottom(); +} + +/** + Replaces the terminal with new text content in string 's'. + + The string can contain UTF-8, crlf's, and ANSI sequences are + also supported when ansi(bool) is set to 'true'. + + Old terminal content is completely cleared. + + \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 append(), printf(), vprintf(), clear() + +*/ +void Fl_Simple_Terminal::text(const char *s, int len) { + clear(); + append(s, len); +} + +/** + Returns entire text content of the terminal as a single string. + + This includes the screen history, as well as the visible + onscreen content. +*/ +const char* Fl_Simple_Terminal::text() const { + return buf->text(); +} + +/** + Appends printf formatted messages to the terminal. + + The string can contain UTF-8, crlf's, and ANSI sequences are + also supported when ansi(bool) is set to 'true'. + + Example: + \code + #include <FL/Fl_Simple_Terminal.H> + int main(..) { + : + // Create a simple terminal, and append some messages to it + Fl_Simple_Terminal *tty = new Fl_Simple_Terminal(..); + : + // Append three lines of formatted text to the buffer + tty->printf("The current date is: %s.\nThe time is: %s\n", date_str, time_str); + tty->printf("The current PID is %ld.\n", (long)getpid()); + : + \endcode + \note See Fl_Text_Buffer::vprintf() for limitations. + \param[in] fmt is a printf format string for the message text. +*/ +void Fl_Simple_Terminal::printf(const char *fmt, ...) { + va_list ap; + va_start(ap, fmt); + Fl_Simple_Terminal::vprintf(fmt, ap); + va_end(ap); +} + +/** + Appends printf formatted messages to the terminal. + + Subclasses can use this to implement their own printf() + functionality. + + The string can contain UTF-8, crlf's, and ANSI sequences are + also supported when ansi(bool) is set to 'true'. + + \note The expanded string is currently limited to 1024 characters. + \param fmt is a printf format string for the message text. + \param ap is a va_list created by va_start() and closed with va_end(), + which the caller is responsible for handling. +*/ +void Fl_Simple_Terminal::vprintf(const char *fmt, va_list ap) { + char buffer[1024]; // XXX: should be user configurable.. + ::vsnprintf(buffer, 1024, fmt, ap); + buffer[1024-1] = 0; // XXX: MICROSOFT + append(buffer); + enforce_history_lines(); +} + +/** + Clears the terminal's screen and history. Cursor moves to top of window. +*/ +void Fl_Simple_Terminal::clear() { + buf->text(""); + sbuf->text(""); + lines = 0; +} + +/** + Remove the specified range of lines from the terminal, starting + with line 'start' and removing 'count' lines. + + This method is used to enforce the history limit. + + \param start -- starting line to remove + \param count -- number of lines to remove +*/ +void Fl_Simple_Terminal::remove_lines(int start, int count) { + int spos = skip_lines(0, start, true); + int epos = skip_lines(spos, count, true); + if ( ansi() ) { + buf->remove(spos, epos); + sbuf->remove(spos, epos); + } else { + buf->remove(spos, epos); + } + lines -= count; + if ( lines < 0 ) lines = 0; +} + +/** + Draws the widget, including a cursor at the end of the buffer. + This is needed since currently Fl_Text_Display doesn't provide + a reliable way to always do this. +*/ +void Fl_Simple_Terminal::draw() { + // XXX: To do this right, we have to steal some of Fl_Text_Display's internal + // magic numbers to do it right, e.g. LEFT_MARGIN, RIGHT_MARGIN.. :/ + // +#define LEFT_MARGIN 3 +#define RIGHT_MARGIN 3 + int buflen = buf->length(); + // Force cursor to EOF so it doesn't draw at user's last left-click + insert_position(buflen); + // Let widget draw itself + Fl_Text_Display::draw(); + // Now draw cursor at the end of the buffer + fl_push_clip(text_area.x-LEFT_MARGIN, + text_area.y, + text_area.w+LEFT_MARGIN+RIGHT_MARGIN, + text_area.h); + int X = 0, Y = 0; + if (position_to_xy(buflen, &X, &Y)) draw_cursor(X, Y); + fl_pop_clip(); +} diff --git a/src/Makefile b/src/Makefile index 5803413b3..b943f813c 100644 --- a/src/Makefile +++ b/src/Makefile @@ -78,6 +78,7 @@ CPPFILES = \ Fl_Scroll.cxx \ Fl_Scrollbar.cxx \ Fl_Shared_Image.cxx \ + Fl_Simple_Terminal.cxx \ Fl_Single_Window.cxx \ Fl_Slider.cxx \ Fl_Spinner.cxx \ |
