From 6842a43a3170c6f7a852186d5688baebdac16e2c Mon Sep 17 00:00:00 2001 From: erco77 Date: Tue, 14 Nov 2023 07:01:52 -0800 Subject: [PATCH] Fl_Terminal widget (#800) Pull Fl_Terminal widget from Greg's fork --- FL/Fl_Terminal.H | 1013 +++++ documentation/Doxyfile.in | 1 + documentation/Makefile | 3 +- .../src/Fl_Terminal-24bit-colors.png | Bin 0 -> 63524 bytes documentation/src/Fl_Terminal-3bit-colors.png | Bin 0 -> 59419 bytes documentation/src/Fl_Terminal-demo.png | Bin 0 -> 48352 bytes documentation/src/Fl_Terminal-utf8-demo.png | Bin 0 -> 37477 bytes documentation/src/Fl_Terminal.dox | 514 +++ examples/simple-terminal.cxx | 12 +- src/CMakeLists.txt | 1 + src/Fl_Terminal.cxx | 3508 +++++++++++++++++ src/Makefile | 1 + src/README-Fl_Terminal.txt | 586 +++ src/makedepend | 22 + test/.gitignore | 3 + test/CMakeLists.txt | 3 +- test/Makefile | 8 +- test/browser.cxx | 6 +- test/contrast.cxx | 8 +- test/demo.cxx | 12 +- test/file_chooser.cxx | 7 +- test/input.cxx | 6 +- test/input_choice.cxx | 6 +- test/makedepend | 77 +- test/menubar.cxx | 6 +- test/native-filechooser.cxx | 6 +- test/table.cxx | 6 +- test/terminal.fl | 1972 +++++++++ test/tree.fl | 4 +- test/unittest_core.cxx | 7 +- test/unittest_simple_terminal.cxx | 121 - test/unittest_terminal.cxx | 98 + test/unittests.cxx | 4 +- test/unittests.h | 4 +- test/valuators.fl | 10 +- 35 files changed, 7818 insertions(+), 217 deletions(-) create mode 100644 FL/Fl_Terminal.H create mode 100644 documentation/src/Fl_Terminal-24bit-colors.png create mode 100644 documentation/src/Fl_Terminal-3bit-colors.png create mode 100644 documentation/src/Fl_Terminal-demo.png create mode 100644 documentation/src/Fl_Terminal-utf8-demo.png create mode 100644 documentation/src/Fl_Terminal.dox create mode 100644 src/Fl_Terminal.cxx create mode 100644 src/README-Fl_Terminal.txt create mode 100644 test/terminal.fl delete mode 100644 test/unittest_simple_terminal.cxx create mode 100644 test/unittest_terminal.cxx diff --git a/FL/Fl_Terminal.H b/FL/Fl_Terminal.H new file mode 100644 index 000000000..4b7039f0c --- /dev/null +++ b/FL/Fl_Terminal.H @@ -0,0 +1,1013 @@ +// +// Fl_Terminal.cxx - A terminal widget for Fast Light Tool Kit (FLTK). +// +// Copyright 2022 by Greg Ercolano. +// Copyright 2023 by Bill Spitzak and others. +// +// 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: +// +// https://www.fltk.org/COPYING.php +// +// Please see the following page on how to report bugs and issues: +// +// https://www.fltk.org/bugs.php +// + +/** \file + Fl_Terminal widget. +*/ + +#ifndef Fl_Terminal_H +#define Fl_Terminal_H + +#include +#include +#include +#include +#include + +/** \class Fl_Terminal + + \brief Terminal widget supporting Unicode/utf-8, ANSI/xterm escape codes with full RGB color control. + + \section Fl_Terminal + + \image html Fl_Terminal-demo.png "Fl_Terminal widget showing a linux manual page" + \image latex Fl_Terminal-demo.png "Fl_Terminal widget showing a linux manual page" width=6cm + + Fl_Terminal is an output-only text widget supporting ASCII and UTF-8/Unicode. + It supports most terminal text features, such as most VT100/xterm style escape sequences + (see \ref Fl_Terminal_escape_codes), text colors/attributes, scrollback history, mouse selection, etc. + + It is recommended that accessing features such as setting text colors and cursor positioning + is best done with ANSI/XTERM escape sequences. But if one sets ansi(false) then this is + not possible. Many commonly used API functions are public, such as textfgcolor() for setting + text colors. Others, such as cursor_up() are protected to prevent common misuse, and are + available only to subclasses. + + For applications that need input support, the widget can be subclassed to provide + keyboard input, and advanced features like pseudo ttys, termio, serial port I/O, etc., + as such features are beyond the scope of FLTK. + + \subsection Fl_Terminal_Examples Examples + + \par + \code + // + // Fl_Terminal: Simple Use + // + Fl_Terminal *tty = new Fl_Terminal(...); + : + tty->append("Hello world.\n"); // simple strings + tty->append("\033[31mThis text is red.\033[0m\n"); // colored text + tty->append("\033[32mThis text is green.\033[0m\n"); + tty->printf("The value of x is %.02f\n", x); // printf() formatting + \endcode + \par + There are also public methods for doing what most "\033[" escape codes do, + so that if ansi(bool) is set to "false", one can still change text colors + or clear the screen via application control, e.g. + \par + \code + tty->home(); // home the cursor + tty->clear_screen(); // clear the screen + tty->textfgcolor(0xff000000); // change the text color to RED + tty->textbgcolor(0x0000ff00); // change the background color to BLUE + // ├┘├┘├┘ + // R G B + \endcode + \par + When creating the widget, the width/height determine the default column + and row count for the terminal's display based on the current font size. + The column width determines where text will wrap. + \par + You can specify wider column sizes than the screen using + display_columns(colwidth). When this value is larger than + the widget's width, text will wrap off-screen, and can be revealed by + resizing the widget wider. + + \subsection Fl_Terminal_Writing Writing To Terminal From Applications + + \par + An application needing terminal output as part of its user interface + can instance Fl_Terminal, and write text strings with: + \par + - append() to append strings + - printf() to append formatted strings + \par + Single character output can be done with: + \par + - print_char() to print a single ASCII/UTF-8 char at the cursor + - putchar() to put single ASCII/UTF-8 char at an x,y position + \par + + \subsection Fl_Terminal_Attributes Text Attributes + \par + The terminal's text supports these attributes: + \par + - Italic - italicized text: \\033[3m + - Bold - brighter/thicker text: \\033[1m + - Dim - lower brightness text: \\033[2m + - Underline - text that is underlined: \\033[4m + - Strikeout - text that has a line through the text: \\033[9m + - Inverse - text whose background and foreground colors are swapped: \\033[7m + - Normal - normal text: \\033[0m + \par + \image html Fl_Terminal-utf8-demo.png "Fl_Terminal screen" + \image latex Fl_Terminal-utf8-demo.png "Fl_Terminal screen" width=6cm + + \subsection Fl_Terminal_Colors Text and Background Colors + + \par + There's at least two ways to specify colors for text and background colors: + \par + - 3 bit / 8 Color Values + - Full 24 bit R/G/B colors + \par + Example of 3 bit colors: + \image html Fl_Terminal-3bit-colors.png "Fl_Terminal 3 bit colors" + \image latex Fl_Terminal-3bit-colors.png "Fl_Terminal 3 bit colors" width=6cm + \par + Example application source code using 3 bit colors: + \code + // + // Text colors + // + tty->append("\033[31m Red text.\033[0m\n"); // Print red text.. + tty->append("\033[32m Green text.\033[0m\n"); + : + tty->append("\033[36m Cyan text.\033[0m\n"); + tty->append("\033[37m White text.\033[0m\n"); + // + // Background colors + // + tty->append("\033[41m Red Background.\033[0m\n"); // background will be red + tty->append("\033[42m Green Background.\033[0m\n"); + : + tty->append("\033[46m Cyan Background.\033[0m\n"); + tty->append("\033[47m White Background.\033[0m\n"); + \endcode + \par + Example of 24 bit colors: + \image html Fl_Terminal-24bit-colors.png "Fl_Terminal 24 bit colors" + \image latex Fl_Terminal-24bit-colors.png "Fl_Terminal 24 bit colors" width=6cm + \par + Example application source code using 24 bit colors: + \code + // + // 24 bit Text Color + // + tty->append("\033[38;2;0;0;255m Text is BLUE.\033[0m\n"); // RGB: R=0, G=0, B=255 + tty->append("\033[38;2;255;0;0m Text is RED.\033[0m\n"); // RGB: R=255, G=0, B=0 + tty->append("\033[38;2;127;64;0m Text is DARK ORANGE.\033[0m\n"); // RGB: R=127, G=64, B=0 + // + // 24 bit Background Color + // + tty->append("\033[48;2;0;0;255m Background is BLUE.\033[0m\n"); // RGB: R=0, G=0, B=255 + tty->append("\033[48;2;255;0;0m Background is RED.\033[0m\n"); // RGB: R=255, G=0, B=0 + tty->append("\033[48;2;127;64;0m Background is DARK ORANGE.\033[0m\n"); // RGB: R=127, G=64, B=0 + \endcode + \par + For more on the ANSI escape codes, see \ref Fl_Terminal_escape_codes. + + \subsection Fl_Terminal_Features Features + + \par + Most standard terminal behaviors are supported, e.g. + \par + - ASCII + UTF-8/Unicode + - scrollback history management + - mouse selection + copy/paste (^C, ^A) + - autoscroll during selection + \par + Most popular ANSI/DEC VT100/Xterm escape sequences are supported (see \ref Fl_Terminal_escape_codes), including: + - per-character colors for text and background + - per-character text attributes: bold/dim, underline, strikeout + - scrolling up/down + - character insert/delete for characters/rows/screen + - clearing characters/rows/screen + \par + Does not (yet) support: + - programmable regions (scroll regions and attribute blocks) + - dynamic line wrap (where resizing display dynamically re-wraps long lines) + \par + Will likely never implement as part of this widget: + - pty/termio management (such features should be _subclassed_) + - Different per-character font family + sizes (font family/size is global only) + - variable width fonts + \par + Regarding the font family+size; the way the terminal is currently designed, + the font family and size must not vary within text; rows have to be consistent + height. Varying widths are tricky too, esp. when it comes to moving the cursor + up/down within a column; varying *widths* are supported (due to Unicode characters + sometimes being "wide", but not heights. + + \subsection Fl_Terminal_Margins Margins + + \par + The margins define the amount of space (in pixels) around the outside of the + text display area, the space between the widget's inner edge (inside the box()) + and the text display area's outer edge. The margins can be inspected and changed + with the margin_left(), margin_right(), margin_top() and margin_bottom() methods. + \par + \code +· + TERMINAL WIDGET (Fl_Terminal) + ┏━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┳━━━┓ + ┃ ↑ margin_top ┃ ┃ + ┃ TEXT DISPLAY AREA ↓ ┃ ┃ + ┃ ┌──────────────────────────────────────────────────────┐ ┃ ┃ + ┃ │ ↑ │ ┃ S ┃ + ┃ │ │ │ ┃ c ┃ + ┃ │ display_rows │ ┃ r ┃ + ┃ │ │ │ ┃ o ┃ + ┃ │ │ │ ┃ l ┃ + ┃ │ │ │ ┃ l ┃ + ┃ │◄───display_columns────────┼─────────────────────────►│ ┃ ┃ + ┃ │ │ │ ┃ B ┃ + ┃ │ │ │ ┃ a ┃ + ┃ │ │ │ ┃ r ┃ + ┃ │ │ │ ┃ ┃ + ┃ │ │ │ ┃ ┃ + ┃◄──┬──►│ ↓ │◄──┬──►┃ ┃ + ┃ │ └──────────────────────────────────────────────────────┘ │ ┃ ┃ + ┃ margin_left ↑ margin_right ┃ ┃ + ┃ ↓ margin_bottom ┃ ┃ + ┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┻━━━┛ + Fl_Terminal Margins +· + \endcode + + \subsection Fl_Terminal_Caveats Caveats + + \par + - This widget is not a full terminal emulator; it does not do stdio redirection, + pseudo ttys/termios/character cooking, keyboard input processing, full + curses(3) support. However, such features CAN be implemented with subclassing. + \par + - The printf() and vprintf() functions are limited to strings no longer than + 1024 characters (including NULL). For printing longer strings, use append() + which has no string limits. +**/ + +class Fl_Terminal : public Fl_Group { +public: + ////////////////////////////////////// + ////// Fl_Terminal Public Enums ////// + ////////////////////////////////////// + + /** + \enum RedrawStyle + Determines when Fl_Terminal calls redraw() if new text is added. + RATE_LIMITED is the recommended setting, using redraw_rate(float) to determine + the maximum rate of redraws. + \see redraw_style(), redraw_rate() + */ + enum RedrawStyle { + NO_REDRAW=0, ///< app must call redraw() as needed to update text to screen + RATE_LIMITED, ///< timer controlled redraws. (DEFAULT) + PER_WRITE ///< redraw triggered after *every* append() / printf() / etc. operation + }; + + /** + \enum Attrib + Bits for the per-character attributes, which control text features + such as italic, bold, underlined text, etc. + + NOTE: enum names with a leading underbar must not be used, and are + reserved for future use. + */ + enum Attrib { + NORMAL = 0x00, ///< all attributes off + BOLD = 0x01, ///< bold text: uses bold font, color brighter than normal + DIM = 0x02, ///< dim text; color slightly darker than normal + ITALIC = 0x04, ///< italic font text + UNDERLINE = 0x08, ///< underlined text + _RESERVED_1 = 0x10, ///< (reserved for internal future use) + INVERSE = 0x20, ///< inverse text; fg/bg color are swapped + _RESERVED_2 = 0x40, ///< (reserved for internal future use) + STRIKEOUT = 0x80 ///< strikeout text + }; + + /** + \enum CharFlags + Per-character 8 bit flags (uchar) used to manage special states for characters. + */ + enum CharFlags { + FG_XTERM = 0x01, ///< this char's fg color is an XTERM color; can be affected by Dim+Bold + BG_XTERM = 0x02, ///< this char's bg color is an XTERM color; can be affected by Dim+Bold + EOL = 0x04, ///< this char at end of line, used for line wrap during screen resizing + RESV_A = 0x08, + RESV_B = 0x10, + RESV_C = 0x20, + RESV_D = 0x40, + RESV_E = 0x80, + COLORMASK = (FG_XTERM | BG_XTERM) + }; + +protected: + // Margin Class //////////////////////////////////////////// + // + // Class to manage the terminal's margins + // + class Margin { + int left_, right_, top_, bottom_; + public: + Margin(void) { left_ = right_ = top_ = bottom_ = 3; } + int left(void) const { return left_; } + int right(void) const { return right_; } + int top(void) const { return top_; } + int bottom(void) const { return bottom_; } + void left(int val) { left_ = val; } + void right(int val) { right_ = val; } + void top(int val) { top_ = val; } + void bottom(int val) { bottom_ = val; } + }; + + // CharStyle Class //////////////////////////////////////////// + // + // Class to manage the terminal's character style + // This includes the font, color, and some cached internal + // info for optimized drawing speed. + // + class CharStyle { + uchar attrib_; // bold, underline.. + uchar flags_; // CharFlags + Fl_Color fgcolor_; // foreground color for text + Fl_Color bgcolor_; // background color for text + Fl_Color defaultfgcolor_; // default fg color used by ESC[0m + Fl_Color defaultbgcolor_; // default bg color used by ESC[0m + Fl_Font fontface_; // font face + Fl_Fontsize fontsize_; // font size + int fontheight_; // font height (in pixels) + int fontdescent_; // font descent (pixels below font baseline) + int charwidth_; // width of a fixed width ASCII character + public: + CharStyle(void); + Fl_Color fgcolor(void) const; + Fl_Color bgcolor(void) const; + Fl_Color defaultfgcolor(void) const { return defaultfgcolor_; } + Fl_Color defaultbgcolor(void) const { return defaultbgcolor_; } + uchar attrib(void) const { return attrib_; } + Fl_Font fontface(void) const { return fontface_; } + Fl_Fontsize fontsize(void) const { return fontsize_; } + int fontheight(void) const { return fontheight_; } + int fontdescent(void) const { return fontdescent_; } + int charwidth(void) const { return charwidth_; } + uchar colorbits_only(uchar inflags) const; + void attrib(uchar val) { attrib_ = val; } + void set_flag(uchar val) { flags_ |= val; } + void clr_flag(uchar val) { flags_ &= ~val; } + void fgcolor_uchar(uchar val); + void bgcolor_uchar(uchar val); + void fgcolor(int r,int g,int b) { fgcolor_ = (r<<24) | (g<<16) | (b<<8); clr_flag(FG_XTERM); } + void bgcolor(int r,int g,int b) { bgcolor_ = (r<<24) | (g<<16) | (b<<8); clr_flag(BG_XTERM); } + void fgcolor(Fl_Color val) { fgcolor_ = val; clr_flag(FG_XTERM); } + void bgcolor(Fl_Color val) { bgcolor_ = val; clr_flag(BG_XTERM); } + void defaultfgcolor(Fl_Color val) { defaultfgcolor_ = val; } + void defaultbgcolor(Fl_Color val) { defaultbgcolor_ = val; } + void fontface(Fl_Font val) { fontface_ = val; update(); } + void fontsize(Fl_Fontsize val) { fontsize_ = val; update(); } + void update(void); + // SGR MODES: Set Graphics Rendition + void sgr_reset(void) { // e.g. ESC[0m + attrib(Fl_Terminal::NORMAL); + fgcolor(defaultfgcolor_); + bgcolor(defaultbgcolor_); + } + int onoff(bool flag, Attrib a) { return (flag ? (attrib_ | a) : (attrib_ & ~a)); } + void sgr_bold(bool val) { attrib_ = onoff(val, Fl_Terminal::BOLD); } // e.g. ESC[1m + void sgr_dim(bool val) { attrib_ = onoff(val, Fl_Terminal::DIM); } // e.g. ESC[2m + void sgr_italic(bool val) { attrib_ = onoff(val, Fl_Terminal::ITALIC); } // e.g. ESC[3m + void sgr_underline(bool val) { attrib_ = onoff(val, Fl_Terminal::UNDERLINE); } // e.g. ESC[3m + void sgr_dbl_under(bool val) { attrib_ = onoff(val, Fl_Terminal::UNDERLINE); } // e.g. ESC[21m (TODO!) + void sgr_blink(bool val) { /* NOT IMPLEMENTED */ } // e.g. ESC[5m + void sgr_inverse(bool val) { attrib_ = onoff(val, Fl_Terminal::INVERSE); } // e.g. ESC[7m + void sgr_strike(bool val) { attrib_ = onoff(val, Fl_Terminal::STRIKEOUT); } // e.g. ESC[9m + }; + +protected: + // Cursor Class /////////////////////////////////////////////////////////// + // + // Class to manage the terminal's cursor position, color, etc. + // + class Cursor { + int col_; // cursor's current col (x) position on display + int row_; // cursor's current row (y) position on display + int h_; // cursor's height (affected by font size) + Fl_Color fgcolor_; // cursor's fg color (color of text, if any) + Fl_Color bgcolor_; // cursor's bg color + public: + Cursor(void) { + col_ = 0; + row_ = 0; + h_ = 10; + fgcolor_ = 0xfffff000; // wht + bgcolor_ = 0x00d00000; // grn + } + int col(void) const { return col_; } + int row(void) const { return row_; } + int h(void) const { return h_; } + Fl_Color fgcolor(void) const { return fgcolor_; } + Fl_Color bgcolor(void) const { return bgcolor_; } + void col(int val) { col_ = val >= 0 ? val : 0; } + void row(int val) { row_ = val >= 0 ? val : 0; } + void h(int val) { h_ = val; } + void fgcolor(Fl_Color val) { fgcolor_ = val; } + void bgcolor(Fl_Color val) { bgcolor_ = val; } + int left(void) { col_ = (col_>0) ? (col_-1) : 0; return col_; } + int right(void) { return ++col_; } + int up(void) { row_ = (row_>0) ? (row_-1) : 0; return row_; } + int down(void) { return ++row_; } + bool is_rowcol(int drow,int dcol) const; + void scroll(int nrows); + void home(void) { row_ = 0; col_ = 0; } + }; + + // Utf8Char Class /////////////////////////////////////////////////////////// + // + // Class to manage the terminal's individual UTF-8 characters. + // Includes fg/bg color, attributes (BOLD, UNDERLINE..) + // + class Utf8Char { + static const int max_utf8_ = 4; // RFC 3629 paraphrased: In UTF-8, chars are encoded with 1 to 4 octets + char text_[max_utf8_]; // memory for actual ASCII or UTF-8 byte contents + uchar len_; // length of bytes in text_[] buffer; 1 for ASCII, >1 for UTF-8 + uchar attrib_; // attribute bits for this char (bold, underline..) + uchar flags_; // CharFlags bits + Fl_Color fgcolor_; // fltk fg color (supports 8color or 24bit color set w/ESC[37;;;m) + Fl_Color bgcolor_; // fltk bg color (supports 8color or 24bit color set w/ESC[47;;;m) + // Private methods + void text_utf8_(const char *text, int len); + public: + // Public methods + Utf8Char(void); // ctor + Utf8Char(const Utf8Char& o); // copy ctor + ~Utf8Char(void); // dtor + Utf8Char& operator=(const Utf8Char& o); // assignment + inline int max_utf8() const { return max_utf8_; } + void text_utf8(const char *text, int len, const CharStyle& style); + void text_ascii(char c, const CharStyle& style); + void fl_font_set(const CharStyle& style) const; + + // Return the UTF-8 text string for this character. + // Use length() to get number of bytes in string, which will be 1 for ASCII chars. + // + const char* text_utf8(void) const { return text_; } + // Return the attribute for this char + uchar attrib(void) const { return attrib_; } + uchar flags(void) const { return flags_; } + Fl_Color fgcolor(void) const; + Fl_Color bgcolor(void) const; + // Return the length of this character in bytes (UTF-8 can be multibyte..) + int length(void) const { return int(len_); } + double pwidth(void) const; + int pwidth_int(void) const; + // Clear the character to a 'space' + void clear(const CharStyle& style) { text_ascii(' ', style); } + bool is_char(char c) const { return *text_ == c; } + void show_char(void) const { ::printf("%.*s", len_, text_); } + void show_char_info(void) const { ::fprintf(stderr, "UTF-8('%.*s', len=%d)\n", len_, text_, len_); } + + Fl_Color attr_color(Fl_Color col, const Fl_Widget *grp) const; + Fl_Color attr_fg_color(const Fl_Widget *grp) const; + Fl_Color attr_bg_color(const Fl_Widget *grp) const; + }; + + // RingBuffer Class /////////////////////////////////////////////////// + // + // Manages ring with indexed row/col and "history" vs. "display" concepts. + // + class RingBuffer { + Utf8Char *ring_chars_; // the ring UTF-8 char buffer + int ring_rows_; // #rows in ring total + int ring_cols_; // #columns in ring/hist/disp + int nchars_; // #chars in ring (ring_rows*ring_cols) + int hist_rows_; // #rows in history + int hist_use_; // #rows in use by history + int disp_rows_; // #rows in display + int offset_; // index offset (used for 'scrolling') + +private: + void new_copy(int drows, int dcols, int hrows, const CharStyle& style); + //DEBUG void write_row(FILE *fp, Utf8Char *u8c, int cols) const { + //DEBUG cols = (cols != 0) ? cols : ring_cols(); + //DEBUG for ( int col=0; collength(), u8c->text_utf8()); + //DEBUG } + //DEBUG } + public: + void clear(void); + void clear_hist(void); + RingBuffer(void); + RingBuffer(int drows, int dcols, int hrows); + ~RingBuffer(void); + + // Methods to access ring + // + // The 'offset' concept allows the 'history' and 'display' + // to be scrolled indefinitely. The 'offset' is applied + // to all the row accesses, and are clamped to within their bounds. + // + // For 'raw' access to the ring (without the offset concept), + // use the ring_chars() method, and walk from 0 - ring_rows(). + // + // _____________ + // | | <- hist_srow() <- ring_srow() + // | H i s t | + // | | + // |_____________| <- hist_erow() + // | | <- disp_srow() + // | D i s p | + // | | + // |_____________| <- disp_erow() <- ring_erow() + // + // \___________/ + // ring_cols() + // hist_cols() + // disp_cols() + // + inline int ring_rows(void) const { return ring_rows_; } + inline int ring_cols(void) const { return ring_cols_; } + inline int ring_srow(void) const { return(0); } + inline int ring_erow(void) const { return(ring_rows_ - 1); } + inline int hist_rows(void) const { return hist_rows_; } + inline int hist_cols(void) const { return ring_cols_; } + inline int hist_srow(void) const { return((offset_ + 0) % ring_rows_); } + inline int hist_erow(void) const { return((offset_ + hist_rows_ - 1) % ring_rows_); } + inline int disp_rows(void) const { return disp_rows_; } + inline int disp_cols(void) const { return ring_cols_; } + inline int disp_srow(void) const { return((offset_ + hist_rows_) % ring_rows_); } + inline int disp_erow(void) const { return((offset_ + hist_rows_ + disp_rows_ - 1) % ring_rows_); } + inline int offset(void) const { return offset_; } + // History use + inline int hist_use(void) const { return hist_use_; } + inline void hist_use(int val) { hist_use_ = val; } + inline int hist_use_srow(void) const { return((offset_ + hist_rows_ - hist_use_) % ring_rows_); } + inline Utf8Char *ring_chars(void) { return ring_chars_; } // access ring buffer directly + inline Utf8Char *ring_chars(void) const { return ring_chars_; } // access ring buffer directly + + bool is_hist_ring_row(int grow) const; + bool is_disp_ring_row(int grow) const; + //DEBUG void show_ring_info(void) const; + void move_disp_row(int src_row, int dst_row); + void clear_disp_row(int drow, const CharStyle& style); + void scroll(int rows, const CharStyle& style); + + const Utf8Char* u8c_ring_row(int row) const; + const Utf8Char* u8c_hist_row(int hrow) const; + const Utf8Char* u8c_hist_use_row(int hurow) const; + const Utf8Char* u8c_disp_row(int drow) const; + + // Non-const versions of the above methods + // Using "Effective C++" ugly-as-hell syntax technique. + // + Utf8Char* u8c_ring_row(int row); + Utf8Char* u8c_hist_row(int hrow); + Utf8Char* u8c_hist_use_row(int hurow); + Utf8Char* u8c_disp_row(int drow); + + void create(int drows, int dcols, int hrows); + void resize(int drows, int dcols, int hrows, const CharStyle& style); + + void change_disp_rows(int drows, const CharStyle& style); + void change_disp_cols(int dcols, const CharStyle& style); + }; + + // Selection Class /////////////////////////////////////////////////// + // + // Class to manage mouse selection + // + class Selection { + int srow_, scol_, erow_, ecol_; // selection start/end. NOTE: start *might* be > end + int push_row_, push_col_; // global row/col for last FL_PUSH + Fl_Color selectionbgcolor_; + Fl_Color selectionfgcolor_; + int state_ ; // 0=none, 1=started, 2=extended, 3=done + bool is_selection_; // false: no selection + public: + Selection(void); + int srow(void) const { return srow_; } + int scol(void) const { return scol_; } + int erow(void) const { return erow_; } + int ecol(void) const { return ecol_; } + void push_clear() { push_row_ = push_col_ = -1; } + void push_rowcol(int row,int col) { push_row_ = row; push_col_ = col; } + void start_push() { start(push_row_, push_col_); } + bool dragged_off(int row,int col) { return (push_row_ != row) || (push_col_ != col); } + void selectionfgcolor(Fl_Color val) { selectionfgcolor_ = val; } + void selectionbgcolor(Fl_Color val) { selectionbgcolor_ = val; } + Fl_Color selectionfgcolor(void) const { return selectionfgcolor_; } + Fl_Color selectionbgcolor(void) const { return selectionbgcolor_; } + bool is_selection(void) const { return is_selection_; } + bool get_selection(int &srow,int &scol,int &erow,int &ecol) const; // guarantees return (start < end) + bool start(int row, int col); + bool extend(int row, int col); + void end(void); + void select(int srow, int scol, int erow, int ecol); + bool clear(void); + int state(void) const { return state_; } + void scroll(int nrows); + }; + + // EscapeSeq Class /////////////////////////////////////////////////// + // + // Class to handle parsing ESC sequences + // + // Holds all state information for parsing esc sequences, + // so sequences can span multiple block read(2) operations, etc. + // Handling of parsed sequences is NOT handled in this class, + // just the parsing of the sequences and managing generic integers. + // + class EscapeSeq { + public: + // EscapeSeq Constants + // Maximums + static const int maxbuff = 80; // character buffer + static const int maxvals = 20; // integer value buffer + // 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 csi_; // This is an ESC[.. sequence (Ctrl Seq Introducer) + char buff_[maxbuff]; // escape sequence being parsed + char *buffp_; // parsing ptr into buff[] + char *buffendp_; // end of buff[] (ptr to last valid buff char) + char *valbuffp_; // pointer to first char in buff of integer being parsed + int vals_[maxvals]; // value array for parsing #'s in ESC[#;#;#.. + int vali_; // parsing index into vals_[], 0 if none + int save_row_, save_col_; // used by ESC[s/u for save/restore + + int append_buff(char c); + int append_val(void); + + public: + EscapeSeq(void); + void reset(void); + char esc_mode(void) const; + void esc_mode(char val); + int total_vals(void) const; + int val(int i) const; + int defvalmax(int dval, int max) const; + bool parse_in_progress(void) const; + bool is_csi(void) const; + int parse(char c); + void save_cursor(int row, int col); + void restore_cursor(int &row, int &col); + }; + + // Partial UTF-8 Buffer Class //////////////////////////////////////////// + // + // Class to manage buffering partial UTF-8 characters between write calls. + // + class PartialUtf8Buf { + char buf_[10]; // buffer partial UTF-8 encoded char + int buflen_; // length of buffered UTF-8 encoded char + int clen_; // final byte length of a UTF-8 char + public: + void clear(void) { buflen_ = clen_ = 0; } // clear the buffer + PartialUtf8Buf(void) { clear(); } // Ctor + // Is byte 'c' in the middle of a UTF-8 encoded byte sequence? + bool is_continuation(char c) { + // Byte 1 Byte 2 Byte 3 ..etc.. + // ASCII: 0xxxxxxx + // UTF8(2): 110xxxxx 10xxxxxx + // UTF8(3): 1110xxxx 10xxxxxx 10xxxxxx + // UTF8(4): 11110xxx 10xxxxxx 10xxxxxx 10xxxxxx + // UTF8(5): 111110xx 10xxxxxx 10xxxxxx 10xxxxxx 10xxxxxx + // UTF8(6): 1111110x 10xxxxxx 10xxxxxx 10xxxxxx 10xxxxxx 10xxxxxx + // \______/ \______________________________________________/ + // Start byte Continuation bytes + // (c & 0xc0) == 0x80 + return ((c & 0xc0) == 0x80); + } + // Access buffer + const char* buf(void) const { return buf_; } + // Access buffer length + int buflen(void) const { return buflen_; } + // Append bytes of a partial UTF-8 string to the buffer. + // + // Returns: + // - true if done OK. Use is_complete() to see if a complete char received. + // - false if buffer overrun occurred, class is clear()ed. + // + // An appropriate response to 'false' would be to print the + // "unknown character" and skip all subsequent UTF-8 continuation chars. + // + bool append(const char* p, int len) { + if (len <= 0) return true; // ignore silly requests: say we did but dont + if (buflen_ + len >= (int)sizeof(buf_)) // overrun check + { clear(); return false; } // clear self, return false + if (!buflen_) clen_ = fl_utf8len(*p); // first byte? save char len for later + while (len>0) { buf_[buflen_++] = *p++; len--; } // append byte to buffer + return true; + } + bool is_complete(void) const { return (buflen_ && (buflen_ == clen_)); } + }; + +private: + // Fl_Terminal Members + Fl_Scrollbar *vscroll_; // vertical scrollbar (value: rows above disp_chars[]) + int scrollbar_size_; // local preference for scrollbar size + CharStyle current_style_; // current font, attrib, color.. + + // A ring buffer is used for the terminal's history (hist) and display (disp) buffer. + // See README-Fl_Terminal.txt, section "RING BUFFER DESCRIPTION" for diagrams/info. + // + // Ring buffer + RingBuffer ring_; // terminal history/display ring buffer + Cursor cursor_; // terminal cursor (position, color, etc) + Margin margin_; // terminal margins (top,left,bottom,right) + Selection select_; // mouse selection + EscapeSeq escseq; // Escape sequence parsing (ESC[ xterm/vt100) + bool show_unknown_; // if true, show unknown chars as '¿' (default off) + bool ansi_; // if true, parse ansi codes (default on) + char *tabstops_; // array of tab stops (0|1) \__ TODO: This should probably + int tabstops_size_; // size of tabstops[] array / be a class "TabStops". + Fl_Rect scrn_; // terminal screen xywh inside box(), margins, and vscroll + int autoscroll_dir_; // 0=autoscroll timer off, 3=scrolling up, 4=scrolling down + int autoscroll_amt_; // #pixels above or below edge, used for autoscroll speed + RedrawStyle redraw_style_; // NO_REDRAW, RATE_LIMITED, PER_WRITE + float redraw_rate_; // maximum redraw rate in seconds, default=0.10 + bool redraw_modified_; // display modified; used by update_cb() to rate limit redraws + bool redraw_timer_; // if true, redraw timer is running + PartialUtf8Buf pub_; // handles Partial Utf8 Buffer (pub) + +protected: + // Ring buffer management + const Utf8Char* u8c_ring_row(int grow) const; + const Utf8Char* u8c_hist_row(int hrow) const; + const Utf8Char* u8c_hist_use_row(int hrow) const; + const Utf8Char* u8c_disp_row(int drow) const; + // non-const versions of the above. + // "Effective C++" says: implement non-const method to cast away const + // + Utf8Char* u8c_ring_row(int grow); + Utf8Char* u8c_hist_row(int hrow); + Utf8Char* u8c_hist_use_row(int hurow); + Utf8Char* u8c_disp_row(int drow); +private: + void create_ring(int drows, int dcols, int hrows); +protected: + Utf8Char* u8c_cursor(void); +private: + int vscroll_width(void) const; + // Tabstops + void init_tabstops(int newsize); + void default_tabstops(void); + void clear_all_tabstops(void); + void set_tabstop(void); + void clear_tabstop(void); + // Updates + void update_screen_xywh(void); + void update_screen(bool font_changed); + void update_scrollbar(void); + // Resize + void resize_display_rows(int drows); + void resize_display_columns(int dcols); + void refit_disp_to_screen(void); + // Callbacks + static void scrollbar_cb(Fl_Widget*, void*); // scrollbar manipulation + static void autoscroll_timer_cb(void*); // mouse drag autoscroll + void autoscroll_timer_cb2(void); + static void redraw_timer_cb(void*); // redraw rate limiting timer + void redraw_timer_cb2(void); + + // Screen management +protected: + const CharStyle& current_style(void) const; + void current_style(const CharStyle& sty); + int x_to_glob_col(int X, int grow, int &gcol) const; + int xy_to_glob_rowcol(int X, int Y, int &grow, int &gcol) const; + int w_to_col(int W) const; + int h_to_row(int H) const; + // API: Display clear operations + void clear_screen(bool scroll_to_hist=true); + void clear_screen_home(bool scroll_to_hist=true); + void clear_sod(void); + void clear_eod(void); + void clear_eol(void); + void clear_sol(void); + void clear_line(int row); + void clear_line(void); + const Utf8Char* walk_selection(const Utf8Char *u8c, int &row, int &col) const; + bool get_selection(int &srow,int &scol,int &erow,int &ecol) const; + bool is_selection(void) const; + bool is_inside_selection(int row,int col) const; + bool is_hist_ring_row(int grow) const; + bool is_disp_ring_row(int grow) const; + int selection_text_len(void) const; + const char* selection_text(void) const; + void clear_mouse_selection(void); + bool selection_extend(int X,int Y); + void scroll(int rows); + void insert_rows(int count); + void delete_rows(int count); + void insert_char_eol(char c, int drow, int dcol, int rep); + void insert_char(char c, int rep); + void delete_chars(int drow, int dcol, int rep); + void delete_chars(int rep); + // History + void history_use(int val, bool update=true); +public: + // API: Terminal operations + void clear_history(void); // ESC [ 3 J + void reset_terminal(void); // ESC c +protected: + // Cursor management + int cursor_h(void) const; +public: + // API: Cursor + void cursorfgcolor(Fl_Color val); + void cursorbgcolor(Fl_Color val); + Fl_Color cursorfgcolor(void) const; + Fl_Color cursorbgcolor(void) const; +protected: + void cursor_row(int row); + void cursor_col(int col); +public: + int cursor_row(void) const; + int cursor_col(void) const; +protected: + void cursor_up(int count=1, bool do_scroll=false); + void cursor_down(int count=1, bool do_scroll=false); + void cursor_left(int count=1); + void cursor_right(int count=1, bool do_scroll=false); + void cursor_home(void); + void cursor_eol(void); + void cursor_sol(void); + void cursor_cr(void); + void cursor_crlf(int count=1); + void cursor_tab_right(int count=1); + void cursor_tab_left(int count=1); + void save_cursor(void); + void restore_cursor(void); + // Printing + void handle_ctrl(char c); + bool is_printable(char c); + bool is_ctrl(char c); + void handle_SGR(void); + void handle_DECRARA(void); + void handle_escseq(char c); + // -- + void display_modified(void); + void display_modified_clear(void); + void clear_char_at_disp(int drow, int dcol); + const Utf8Char* utf8_char_at_disp(int drow, int dcol) const; + const Utf8Char* utf8_char_at_glob(int grow, int gcol) const; + void repeat_char(char c, int rep); + void utf8_cache_clear(void); + void utf8_cache_flush(void); + + // API: Character display output +public: + void putchar(const char *text, int len, int drow, int dcol); + void putchar(char c, int drow, int dcol); + void print_char(const char *text, int len=-1); + void print_char(char c); + // API: String display output + void append_utf8(const char *buf, int len=-1); + void append_ascii(const char *s); + void append(const char *s, int len=-1); +protected: + int handle_unknown_char(void); + // Drawing + void draw_row_bg(int grow, int X, int Y) const; + void draw_row(int grow, int Y) const; + void draw_buff(int Y) const; + void handle_selection_autoscroll(void); + int handle_selection(int e); +public: + // FLTK: draw(), resize(), handle() + void draw(void) FL_OVERRIDE; + void resize(int X,int Y,int W,int H) FL_OVERRIDE; + int handle(int e) FL_OVERRIDE; + +protected: + // Internal short names + // Don't make these public, but allow internals and + // derived classes to maintain brevity. + // + inline int ring_rows(void) const { return ring_.ring_rows(); } + inline int ring_cols(void) const { return ring_.ring_cols(); } + inline int ring_srow(void) const { return ring_.ring_srow(); } + inline int ring_erow(void) const { return ring_.ring_erow(); } + inline int hist_rows(void) const { return ring_.hist_rows(); } + inline int hist_cols(void) const { return ring_.hist_cols(); } + inline int hist_srow(void) const { return ring_.hist_srow(); } + inline int hist_erow(void) const { return ring_.hist_erow(); } + inline int hist_use(void) const { return ring_.hist_use(); } + inline int hist_use_srow(void) const { return ring_.hist_use_srow(); } + inline int disp_rows(void) const { return ring_.disp_rows(); } + inline int disp_cols(void) const { return ring_.disp_cols(); } + inline int disp_srow(void) const { return ring_.disp_srow(); } + inline int disp_erow(void) const { return ring_.disp_erow(); } + inline int offset(void) const { return ring_.offset(); } + + // TODO: CLEAN UP WHAT'S PUBLIC, AND WHAT SHOULD BE 'PROTECTED' AND 'PRIVATE' + // Some of the public stuff should, quite simply, "not be". + + // API: Terminal features +public: + // API: Scrollbar + int scrollbar_size(void) const; + void scrollbar_size(int val); + int scrollbar_actual_size(void) const; + // API: History + int history_rows(void) const; + void history_rows(int val); + int history_use(void) const; + // API: Display + int display_rows(void) const; + void display_rows(int val); + int display_columns(void) const; + void display_columns(int val); + // API: Box + /// Sets the box type, updates terminal margins et al. Default is FL_DOWN_FRAME. + /// + /// FL_XXX_FRAME types are handled in a special way by this widget, and guarantee + /// the background is a flat field. + /// + /// FL_XXX_BOX may draw gradients as inherited by Fl::scheme(). + /// + void box(Fl_Boxtype val) { Fl_Group::box(val); update_screen(false); } + /// Returns the current box type. + Fl_Boxtype box(void) const { return Fl_Group::box(); } + // API: Margins + /// Return the left margin; see \ref Fl_Terminal_Margins. + int margin_left(void) const { return margin_.left(); } + /// Return the right margin; see \ref Fl_Terminal_Margins. + int margin_right(void) const { return margin_.right(); } + /// Return the top margin; see \ref Fl_Terminal_Margins. + int margin_top(void) const { return margin_.top(); } + /// Return the bottom margin; see \ref Fl_Terminal_Margins. + int margin_bottom(void) const { return margin_.bottom(); } + void margin_left(int val); + void margin_right(int val); + void margin_top(int val); + void margin_bottom(int val); + // API: Text font/size/color + void textfont(Fl_Font val); + void textsize(Fl_Fontsize val); + void textfgcolor(Fl_Color val); + void textbgcolor(Fl_Color val); + void textfgcolor_default(Fl_Color val); + void textbgcolor_default(Fl_Color val); + /// Return text font used to draw all text in the terminal. + Fl_Font textfont(void) const { return current_style_.fontface(); } + /// Return text font size used to draw all text in the terminal. + Fl_Fontsize textsize(void) const { return current_style_.fontsize(); } + /// Return text's current foreground color. + Fl_Color textfgcolor(void) const { return current_style_.fgcolor(); } + /// Return text's current background color. + Fl_Color textbgcolor(void) const { return current_style_.bgcolor(); } + /// Return text's default foreground color. \see textfgcolor() + Fl_Color textfgcolor_default(void) const { return current_style_.defaultfgcolor(); } + /// Return text's default background color. \see textbgcolor() + Fl_Color textbgcolor_default(void) const { return current_style_.defaultbgcolor(); } + void textfgcolor_xterm(uchar val); + void textbgcolor_xterm(uchar val); + /// Set mouse selection foreground color. + void selectionfgcolor(Fl_Color val) { select_.selectionfgcolor(val); } + /// Set mouse selection background color. + void selectionbgcolor(Fl_Color val) { select_.selectionbgcolor(val); } + /// Get mouse selection foreground color. + Fl_Color selectionfgcolor(void) const { return select_.selectionfgcolor(); } + /// Get mouse selection background color. + Fl_Color selectionbgcolor(void) const { return select_.selectionbgcolor(); } + // API: Text attrib + void textattrib(uchar val); + // API: Redraw style/rate + RedrawStyle redraw_style(void) const; + void redraw_style(RedrawStyle val); +protected: + bool is_redraw_style(RedrawStyle val) { return redraw_style_ == val; } +public: + float redraw_rate(void) const; + void redraw_rate(float val); + // API: Show unknown/unprintable chars + bool show_unknown(void) const; + void show_unknown(bool val); + // API: ANSI sequences + bool ansi(void) const; + void ansi(bool val); + // Fl_Simple_Terminal API compatibility + int history_lines(void) const; + void history_lines(int val); + // API: printf() + void printf(const char *fmt, ...); + void vprintf(const char *fmt, va_list ap); + // Ctor + Fl_Terminal(int X, int Y, int W, int H, const char*L=0); + // Dtor + ~Fl_Terminal(void); + // Debugging features +//DEBUG void show_ring_info() const { ring_.show_ring_info(); } +//DEBUG void write_row(FILE *fp, Utf8Char *u8c, int cols) const; +//DEBUG void show_buffers(RingBuffer *a, RingBuffer *b=0) const; +}; +#endif diff --git a/documentation/Doxyfile.in b/documentation/Doxyfile.in index bf88783f4..130dd1845 100644 --- a/documentation/Doxyfile.in +++ b/documentation/Doxyfile.in @@ -789,6 +789,7 @@ INPUT = @CMAKE_CURRENT_SOURCE_DIR@/src/index.dox \ @CMAKE_CURRENT_SOURCE_DIR@/src/coordinates.dox \ @CMAKE_CURRENT_SOURCE_DIR@/src/resize.dox \ @CMAKE_CURRENT_SOURCE_DIR@/src/editor.dox \ + @CMAKE_CURRENT_SOURCE_DIR@/src/Fl_Terminal.dox \ @CMAKE_CURRENT_SOURCE_DIR@/src/drawing.dox \ @CMAKE_CURRENT_SOURCE_DIR@/src/events.dox \ @CMAKE_CURRENT_SOURCE_DIR@/src/subclassing.dox \ diff --git a/documentation/Makefile b/documentation/Makefile index c75dd567c..5643ba118 100644 --- a/documentation/Makefile +++ b/documentation/Makefile @@ -48,7 +48,8 @@ HTMLFILES = \ $(SRC_DOCDIR)/development.dox \ $(SRC_DOCDIR)/license.dox \ $(SRC_DOCDIR)/examples.dox \ - $(SRC_DOCDIR)/faq.dox + $(SRC_DOCDIR)/faq.dox \ + $(SRC_DOCDIR)/Fl_Terminal.dox MANPAGES = $(SRC_DOCDIR)/fltk.$(CAT3EXT) $(SRC_DOCDIR)/fltk-config.$(CAT1EXT) \ $(SRC_DOCDIR)/fluid.$(CAT1EXT) $(SRC_DOCDIR)/blocks.$(CAT6EXT) \ diff --git a/documentation/src/Fl_Terminal-24bit-colors.png b/documentation/src/Fl_Terminal-24bit-colors.png new file mode 100644 index 0000000000000000000000000000000000000000..5e7f6adb1b4a8fd3929415d2fd773f1f61815cee GIT binary patch literal 63524 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 JfJOCx{12<#@;m?l literal 0 HcmV?d00001 diff --git a/documentation/src/Fl_Terminal-3bit-colors.png b/documentation/src/Fl_Terminal-3bit-colors.png new file mode 100644 index 0000000000000000000000000000000000000000..a0407738ef9f3aeb0a1e2df4b5ae794c9d84191b GIT binary patch literal 59419 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�uBL#56 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≀Wo%i{)cIF-U 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 literal 0 HcmV?d00001 diff --git a/documentation/src/Fl_Terminal-utf8-demo.png b/documentation/src/Fl_Terminal-utf8-demo.png new file mode 100644 index 0000000000000000000000000000000000000000..a04f11bf9f3d1a5ad93e6938180e3bd41aa38a21 GIT binary patch literal 37477 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 literal 0 HcmV?d00001 diff --git a/documentation/src/Fl_Terminal.dox b/documentation/src/Fl_Terminal.dox new file mode 100644 index 000000000..2963218da --- /dev/null +++ b/documentation/src/Fl_Terminal.dox @@ -0,0 +1,514 @@ +// vim:syntax=doxygen +/** + +\page Fl_Terminal_Tech_Docs Fl_Terminal Technical Documentation + +This chapter covers the vt100/xterm style "escape codes" used by +Fl_Terminal for cursor positioning, text colors, and other display +screen control features such as full or partial screen clearing, +up/down scrolling, character insert/delete, etc. + +\section Fl_Terminal_escape_codes The Escape Codes Fl_Terminal Supports + +These are the escape codes Fl_Terminal actually supports, and is not +the 'complete' list that e.g. xterm supports. Most of the important stuff +has been implemented, but esoteric features (such as scroll regions) has not. + +Features will be added as the widget matures. + +\code{.unparsed} +│ -------------------------------------------------------- +│ --- The CSI (Control Sequence Introducer, or "ESC[") --- +│ -------------------------------------------------------- +│ +│ ESC[#@ - (ICH) Insert blank Chars (default=1) +│ ESC[#A - (CUU) Cursor Up, no scroll/wrap +│ ESC[#B - (CUD) Cursor Down, no scroll/wrap +│ ESC[#C - (CUF) Cursor Forward, no wrap +│ ESC[#D - (CUB) Cursor Back, no wrap +│ ESC[#E - (CNL) Cursor Next Line (crlf) xterm, !gnome +│ ESC[#F - (CPL) Cursor Preceding Line: move to sol and up # lines +│ ESC[#G - (CHA) Cursor Horizontal Absolute positioning +│ │ +│ ├── ESC[G - move to column 1 (start of line, sol) +│ └── ESC[#G - move to column # +│ +│ ESC[#H - (CUP) Cursor Position (#'s are 1 based) +│ │ +│ ├── ESC[H - go to row #1 +│ ├── ESC[#H - go to (row #) (default=1) +│ └── ESC[#;#H - go to (row# ; col#) +│ +│ ESC[#I - (CHT) Cursor Horizontal Tab: tab forward +│ │ +│ └── ESC[#I - tab # times (default 1) +│ +│ ESC[#J - (ED) Erase in Display +│ │ +│ ├── ESC[0J - clear to end of display (default) +│ ├── ESC[1J - clear to start of display +│ ├── ESC[2J - clear all lines +│ └── ESC[3J - clear screen history +│ +│ ESC[#K - (EL) Erase in line +│ │ +│ ├── ESC[0K - clear to end of line (default) +│ ├── ESC[1K - clear to start of line +│ └── ESC[2K - clear current line +│ +│ ESC[#L - (IL) Insert # Lines (default=1) +│ ESC[#M - (DL) Delete # Lines (default=1) +│ ESC[#P - (DCH) Delete # Chars (default=1) +│ ESC[#S - (SU) Scroll Up # lines (default=1) +│ ESC[#T - (SD) Scroll Down # lines (default=1) +│ ESC[#X - (ECH) Erase Characters (default=1) +│ +│ ESC[#Z - (CBT) Cursor Backwards Tab +│ │ +│ └── ESC[#Z - backwards tab # times (default=1) +│ +│ ESC[#a - (HPR) move cursor relative [columns] (default=[row,col+1]) (NOT IMPLEMENTED) +│ ESC[#b - (REP) repeat prev graphics char # times (NOT IMPLEMENTED) +│ ESC[#d - (VPA) Line Position Absolute [row] (NOT IMPLEMENTED) +│ ESC[#e - (LPA) Line Position Relative [row] (NOT IMPLEMENTED) +│ ESC[#f - (CUP) cursor position (#'s 1 based), same as ESC[H +│ +│ ESC[#g - (TBC)Tabulation Clear +│ │ +│ ├── ESC[0g - Clear tabstop at cursor +│ └── ESC[3g - Clear all tabstops +│ +│ ESC[#m - (SGR) Set Graphic Rendition +│ │ +│ │ *** Attribute Enable *** +│ │ +│ ├── ESC[0m - reset: normal attribs/default fg/bg color (VT100) +│ ├── ESC[1m - bold (VT100) +│ ├── ESC[2m - dim +│ ├── ESC[3m - italic +│ ├── ESC[4m - underline (VT100) +│ ├── ESC[5m - blink (NOT IMPLEMENTED) (VT100) +│ ├── ESC[6m - (unused) +│ ├── ESC[7m - inverse (VT100) +│ ├── ESC[8m - (unused) +│ ├── ESC[9m - strikeout +│ ├── ESC[21m - doubly underline (Currently this just does single underline) +│ │ +│ │ *** Attribute Disable *** +│ │ +│ ├── ESC[22m - disable bold/dim +│ ├── ESC[23m - disable italic +│ ├── ESC[24m - disable underline +│ ├── ESC[25m - disable blink (NOT IMPLEMENTED) +│ ├── ESC[26m - (unused) +│ ├── ESC[27m - disable inverse +│ ├── ESC[28m - disable hidden +│ ├── ESC[29m - disable strikeout +│ │ +│ │ *** Foreground Text "8 Color" *** +│ │ +│ ├── ESC[30m - fg Black +│ ├── ESC[31m - fg Red +│ ├── ESC[32m - fg Green +│ ├── ESC[33m - fg Yellow +│ ├── ESC[34m - fg Blue +│ ├── ESC[35m - fg Magenta +│ ├── ESC[36m - fg Cyan +│ ├── ESC[37m - fg White +│ ├── ESC[39m - fg default +│ │ +│ │ *** Background Text "8 Color" *** +│ │ +│ ├── ESC[40m - bg Black +│ ├── ESC[41m - bg Red +│ ├── ESC[42m - bg Green +│ ├── ESC[43m - bg Yellow +│ ├── ESC[44m - bg Blue +│ ├── ESC[45m - bg Magenta +│ ├── ESC[46m - bg Cyan +│ ├── ESC[47m - bg White +│ ├── ESC[49m - bg default +│ │ +│ │ *** Special RGB Color *** +│ │ +│ └── ESC [ 38 ; Red ; Grn ; Blue m - where Red,Grn,Blu are decimal (0-255) +│ +│ ESC[s - save cursor pos (ansi.sys+xterm+gnome, but NOT vt100) +│ ESC[u - rest cursor pos (ansi.sys+xterm+gnome, but NOT vt100) +│ +│ ESC[>#q - (DECSCA) Set Cursor style (block/line/blink..) (NOT IMPLEMENTED) +│ ESC[#;#r - (DECSTBM) Set scroll Region top;bot (NOT IMPLEMENTED) +│ ESC[#..$t - (DECRARA) (NOT IMPLEMENTED) +│ +│ ------------------------ +│ --- C1 Control Codes --- +│ ------------------------ +│ +│ c - (RIS) Reset term to Initial State +│ D - (IND) Index: move cursor down a line, scroll if at bottom +│ E - (NEL) Next Line: basically do a crlf, scroll if at bottom +│ H - (HTS) Horizontal Tab Set: set a tabstop +│ M - (RI) Reverse Index (up w/scroll) +│ +│ NOTE: Acronyms in parens are Digital Equipment Corporation's names these VT features. +│ +\endcode + +\section external_escape_codes Useful Terminal Escape Code Documentation + +Useful links for reference: + + - https://vt100.net/docs/vt100-ug/chapter3.html + - https://www.xfree86.org/current/ctlseqs.html + - https://www.x.org/docs/xterm/ctlseqs.pdf + - https://gist.github.com/justinmk/a5102f9a0c1810437885a04a07ef0a91 <-- alphabetic! + - https://invisible-island.net/xterm/ctlseqs/ctlseqs.html + +\section Fl_Terminal_design Fl_Terminal Design Document + +When I started this project, I identified the key concepts needed to +implement Fl_Terminal: + +- Draw and manage multiline Unicode text in FLTK +- Allow per-character colors and attributes +- Efficient screen buffer to handle "scrollback history" +- Efficient scrolling with vertical scrollbar for even large screen history +- Mouse selection for copy/paste +- Escape code management to implement VT100 style / ANSI escape codes. + +A class was created for each character, since characters can be either ASCII +or Utf8 encoded byte sequences. This class is called Utf8Char, and handles +the character, its fg and bg color, and any attributes like dim, bold, italic, etc. + +For managing the screen, after various experiments, I decided a ring buffer +was the best way to manage things, the ring split in two: + +- 'screen history' which is where lines scrolled off the top are saved +- 'display screen' displayed to the user at all times, and where the cursor lives + +Scrolling the display, either by scrollbar or by new text causing the display +to scroll up one line, would simply change an 'offset' index# of where in the +ring buffer the top of the screen is, automatically moving the top line +into the history, all without moving memory around. + +In fact the only time screen memory is moved around is during these infrequent +operations: + +- during scrolling "down" +- character insert/delete operations within a line +- changing the display size +- changing the history size + +So a class "RingBuffer" is defined to manage the ring, and accessing its various +parts, either as the entire entity ring, just the history, or just the display. + +These three concepts, "ring", "history" and "display" are given abbreviated +names in the RingBuffer class's API: + + ┌─────────────────────────────────────────┬──────────────────────────────┐ + │ NOTE: Abbreviations "hist" and "disp" │ │ + ├─────────────────────────────────────────┘ │ + │ │ + │ "history" may be abbreviated as "hist", and "display" as "disp" in │ + │ both this text and the source code. 4 character names are used so │ + │ they line up cleanly in the source, e.g. │ + │ │ + │ ring_rows() ring_cols() │ + │ hist_rows() hist_cols() │ + │ disp_rows() disp_cols() │ + │ └─┬┘ └─┬┘ └─┬┘ └─┬┘ │ + │ └────┴──────────┴────┴───────── 4 characters │ + │ │ + └────────────────────────────────────────────────────────────────────────┘ + +These concepts were able to fit into C++ classes: + +Utf8Char +-------- +Each character on the screen is a "Utf8Char" which can manage +the UTF-8 encoding of any character as one or more bytes. Also +in that class is a byte for an attribute (underline, bold, etc), +and two integers for fg/bg color. + +RingBuffer +---------- +The RingBuffer class keeps track of the buffer itself, a single +array of Utf8Chars called "ring_chars" whose width is ring_cols() +and whose height is ring_rows(). + +The "top" part of the ring is the history, whose width is hist_cols() +and whose height is hist_rows(). hist_use_rows() is used to define +what part of the history is currently in use. + +The "bottom" part of the ring is the display, whose width is disp_cols() +and whose height is disp_rows(). + +An index number called "offset" points to where in the ring buffer +the top of the ring currently is. This index changes each time the +screen is scrolled, and affects both where the top of the display is, +and where the top of the history is. + +The memory layout of the Utf8Char character array is: + + ring_chars[]: + ___________________ _ _ + | | ʌ + | | | + | | | + | H i s t o r y | | hist_rows + | | | + | | | + |___________________| _v_ + | | ʌ + | | | + | D i s p l a y | | disp_rows + | | | + |___________________| _v_ + + |<----------------->| + ring_cols + hist_cols + disp_cols + +So it's basically a single continuous array of Utf8Char instances +where any character can generally be accessed by index# using the formula: + + ring_chars[ (row*ring_cols)+col ] + +..where 'row' is the desired row, 'col' is the desired column, +and 'ring_cols' is how many columns "wide" the buffer is. + +The "offset" index affects that formula as an extra row offset, +and the resulting index is then clamped within the range of the +ring buffer using modulus. + +Methods are used to allow direct access to the characters +in the buffer that automatically handle the offset and modulus +formulas, namely: + + u8c_ring_row(row,col) // access the entire ring by row/col + u8c_hist_row(row,col) // access just the history buffer + u8c_disp_row(row,col) // access just the display buffer + +A key concept is the use of the simple 'offset' index integer +to allow the starting point of the history and display to be +moved around to implement 'text scrolling', such as when +crlf at the screen bottom causes a 'scroll up'. + +This is simply an "index offset" integer applied to the +hist and disp indexes when drawing the display. So after +scrolling two lines up, the offset is just increased by 2, +redefining where the top of the history and display are, e.g. + + Offset is 0: 2 Offset now 2: + ┌───────────────────┐ ──┐ ┌───────────────────┐ + │ │ │ │ D i s p l a y │ + │ │ └─> ├───────────────────┤ + │ │ │ │ + │ H i s t o r y │ │ │ + │ │ │ H i s t o r y │ + │ │ 2 │ │ + ├───────────────────┤ ──┐ │ │ + │ │ │ │ │ + │ │ └─> ├───────────────────┤ + │ D i s p l a y │ │ │ + │ │ │ D i s p l a y │ + │ │ │ │ + └───────────────────┘ └───────────────────┘ + +This 'offset' trivially implements "text scrolling", avoiding having +to physically move memory around. Just the 'offset' changes, the +text remains where it is in memory. + +This also makes it appear the top line in the display is 'scrolled up' +into the bottom of the scrollback 'history'. + +If the offset exceeds the size of the ring buffer, it simply wraps +around back to the beginning of the buffer with a modulo. + +Indexes into the display and history are also modulo their respective +rows, e.g. + + act_ring_index = (hist_rows + disp_row + offset - scrollbar_pos) % ring_rows; + +This way indexes for ranges can run beyond the bottom of the ring, +and automatically wrap around the ring, e.g. + + ┌───────────────────┐ + ┌─> 2 │ │ + │ 3 │ D i s p l a y │ + │ 4 │ │ + │ ├───────────────────┤ <-- offset points here + │ │ │ + disp │ │ │ + index ┤ │ H i s t o r y │ + wraps │ │ │ + │ │ │ + │ │ │ + │ ├───────────────────┤ + │ 0 │ D i s p l a y │ + │ 1 └───────────────────┘ <- ring_rows points to end of ring + └── 2 : : + 3 : : + disp_row(5) -> 4 :...................: + +The dotted lines show where the display would be if not for the fact +it extends beyond the bottom of the ring buffer (due to the current offset), +and therefore wraps up to the top of the ring. + +So to find a particular row in the display, in this case a 5 line display +whose lines lie between 0 and 4, some simple math calculates the row position +into the ring: + + act_ring_index = (histrows // the display exists AFTER the history, so offset the hist_rows + + offset // include the scroll 'offset' + + disp_row // add the desired row relative to the top of the display (0..disp_rows) + ) % ring_rows; // make sure the resulting index is within the ring buffer (0..ring_rows) + +An additional bit of math makes sure if a negative result occurs, that +negative value works relative to the end of the ring, e.g. + + if (act_ring_index < 0) act_ring_index = ring_rows + act_ring_index; + +This guarantees the act_ring_index is within the ring buffer's address space, +with all offsets applied. + +The math that implements this can be found in the u8c_xxxx_row() methods, +where "xxxx" is one of the concept regions "ring", "hist" or "disp": + + Utf8Char *u8c; + u8c = u8c_ring_row(rrow); // address within ring, rrow can be 0..(ring_rows-1) + u8c = u8c_hist_row(hrow); // address within hist, hrow can be 0..(hist_rows-1) + u8c = u8c_disp_row(drow); // address within disp, drow can be 0..(disp_rows-1) + +The small bit of math is only involved whenever a new row address is needed, +so in a display that's 80x25, to walk all the characters in the screen, the +math above would only be called 25 times, once for each row, and each column +in the row is just a simple integer offset: + + for ( int row=0; row //START +#include #include #include -#include +#include #define TERMINAL_HEIGHT 120 // Globals Fl_Double_Window *G_win = 0; Fl_Box *G_box = 0; -Fl_Simple_Terminal *G_tty = 0; +Fl_Terminal *G_tty = 0; // Append a date/time message to the terminal every 2 seconds void tick_cb(void *data) { @@ -43,7 +43,7 @@ int main(int argc, char **argv) { "Your app's debugging output in tty below"); // Add simple terminal to bottom of app window for scrolling history of status messages. - G_tty = new Fl_Simple_Terminal(0,200,G_win->w(),TERMINAL_HEIGHT); + G_tty = new Fl_Terminal(0,200,G_win->w(),TERMINAL_HEIGHT); G_tty->ansi(true); // enable use of "\033[32m" G_win->end(); @@ -51,4 +51,4 @@ int main(int argc, char **argv) { G_win->show(); Fl::add_timeout(0.5, tick_cb); return Fl::run(); -} //END +} diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 0db9631be..f9511f358 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -90,6 +90,7 @@ set (CPPFILES Fl_Table.cxx Fl_Table_Row.cxx Fl_Tabs.cxx + Fl_Terminal.cxx Fl_Text_Buffer.cxx Fl_Text_Display.cxx Fl_Text_Editor.cxx diff --git a/src/Fl_Terminal.cxx b/src/Fl_Terminal.cxx new file mode 100644 index 000000000..6b7d6dd87 --- /dev/null +++ b/src/Fl_Terminal.cxx @@ -0,0 +1,3508 @@ +// +// Fl_Terminal.H - A terminal widget for Fast Light Tool Kit (FLTK). +// +// Copyright 2022 by Greg Ercolano. +// Copyright 2023 by Bill Spitzak and others. +// +// 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: +// +// https://www.fltk.org/COPYING.php +// +// Please see the following page on how to report bugs and issues: +// +// https://www.fltk.org/bugs.php +// + +// TODO: horizontal scrollbar +// TODO: double clicking text should make word selection, +// and drag should word-enlarge selection +// FIXME: While dragging a selection, hitting shift stops the selection + +// This must appear above #include +#ifndef NDEBUG +#define NDEBUG // comment out to enable assert() +#endif + +#include // isdigit +#include // malloc +#include // strlen +#include // vprintf, va_list +#include + +#include +#include +#include // fl_utf8len1 +#include +#include + +///////////////////////////////// +////// Static Functions ///////// +///////////////////////////////// + +#define MIN(a,b) ((a)<=(b)) ? (a) : (b) // Return smaller of two values +#define MAX(a,b) ((a)>=(b)) ? (a) : (b) // Return larger of two values +#define ABS(a) ((a)<0) ? -(a) : (a) // Return abs value + +// Return val clamped between min and max +static int clamp(int val, int min, int max) + { return (valmax) ? max : val; } + +// Swap integer values a and b +static void swap(int &a, int &b) + { int asave = a; a = b; b = asave; } + +static int normalize(int row, int maxrows) { + row = row % maxrows; + if (row < 0) row = maxrows + row; // negative? index relative to end + return row; +} + +// Color channel management +static int red(Fl_Color val) { return (val & 0xff000000) >> 24; } +static int grn(Fl_Color val) { return (val & 0x00ff0000) >> 16; } +static int blu(Fl_Color val) { return (val & 0x0000ff00) >> 8; } +static Fl_Color rgb(int r,int g,int b) { return (r << 24) | (g << 16) | (b << 8); } + +// Return dim version of color 'val' +static Fl_Color dim_color(Fl_Color val) { + int r = clamp(red(val) - 0x20, 0, 255); + int g = clamp(grn(val) - 0x20, 0, 255); + int b = clamp(blu(val) - 0x20, 0, 255); + //DEBUG ::printf("DIM COLOR: %08x -> %08x\n", val, rgb(r,g,b)); + return rgb(r,g,b); +} + +// Return bold version of color 'val' +static Fl_Color bold_color(Fl_Color val) { + int r = clamp(red(val) + 0x20, 0, 255); + int g = clamp(grn(val) + 0x20, 0, 255); + int b = clamp(blu(val) + 0x20, 0, 255); + //DEBUG ::printf("BOLD COLOR: %08x -> %08x\n", val, rgb(r,g,b)); + return rgb(r,g,b); +} + +// Return an FLTK color for given foreground color index (0..7) +static Fl_Color fltk_fg_color(uchar ci) { + static const Fl_Color xterm_fg_colors_[] = { + 0x00000000, // 0 + 0xd0000000, // 1 - red + 0x00d00000, // 2 - grn + 0xd0d00000, // 3 - yel + 0x0000d000, // 4 - blu + 0xd000d000, // 5 - mag + 0x00d0d000, // 6 - cyn + 0xd0d0d000 // 7 - white + }; + ci &= 0x07; // clamp to array size + return xterm_fg_colors_[ci]; +} + +// Return an FLTK color for the given background color index (0..7) and attribute. +// Background colors should be just a little darker than +// the fg colors to prevent too much brightness clashing +// for 'normal' bg vs fg colors. +// +static Fl_Color fltk_bg_color(uchar ci) { + static const Fl_Color xterm_bg_colors_[] = { + 0x00000000, // 0 + 0xc0000000, // 1 - red + 0x00c00000, // 2 - grn + 0xc0c00000, // 3 - yel + 0x0000c000, // 4 - blu + 0xc000c000, // 5 - mag + 0x00c0c000, // 6 - cyn + 0xc0c0c000 // 7 - white + }; + ci &= 0x07; // clamp to array size + return xterm_bg_colors_[ci]; +} + +// See if an Fl_Boxtype is FL_XXX_FRAME +static bool is_frame(Fl_Boxtype b) { + if (b == FL_UP_FRAME || b == FL_DOWN_FRAME || + b == FL_THIN_UP_FRAME || b == FL_THIN_DOWN_FRAME || + b == FL_ENGRAVED_FRAME || b == FL_EMBOSSED_FRAME || + b == FL_BORDER_FRAME) return true; + return false; +} + +/////////////////////////////////////// +////// Selection Class Methods //////// +/////////////////////////////////////// + +// Ctor +Fl_Terminal::Selection::Selection(void) { + // These are used to set/get the mouse selection + srow_ = scol_ = erow_ = ecol_ = 0; + // FL_PUSH event row/col + push_clear(); + selectionfgcolor_ = FL_BLACK; + selectionbgcolor_ = FL_WHITE; + state_ = 0; + is_selection_ = false; +} + +/** + Return selection start/end. + Ensures (start < end) to allow walking 'forward' thru selection, + left-to-right, top-to-bottom. + + Returns: + - true -- valid selection values returned + - false -- no selection was made, returned values undefined +*/ +bool Fl_Terminal::Selection::get_selection(int &srow,int &scol, + int &erow,int &ecol) const { + srow = srow_; scol = scol_; + erow = erow_; ecol = ecol_; + if (!is_selection_) return false; + // Ensure (start < end) on return + if (srow_ == erow_ && scol_ > ecol_) swap(scol, ecol); + if (srow_ > erow_) + { swap(srow, erow); swap(scol, ecol); } + return true; +} + +// Start new selection at specified row,col +// Always returns true. +// +bool Fl_Terminal::Selection::start(int row, int col) { + srow_ = erow_ = row; + scol_ = ecol_ = col; + state_ = 1; // state: "started selection" + is_selection_ = true; + return true; +} + +// Extend existing selection to row,col +// Returns true if anything changed, false if not. +// +bool Fl_Terminal::Selection::extend(int row, int col) { + // no selection started yet? start and return true + if (!is_selection()) return start(row, col); + state_ = 2; // state: "extending selection" + if (erow_ == row && ecol_ == col) return false; // no change + erow_ = row; + ecol_ = col; + return true; +} + +// End selection (turn dragging() off) +void Fl_Terminal::Selection::end(void) { + state_ = 3; // state: "finished selection" + // Order selection + if (erow_ < srow_) + { swap(srow_, erow_); swap(scol_, ecol_); } + if (erow_ == srow_ && scol_ > ecol_) swap(scol_, ecol_); +} + +// Create a complete selection +void Fl_Terminal::Selection::select(int srow, int scol, int erow, int ecol) { + srow_ = srow; scol_ = scol; + erow_ = erow; ecol_ = ecol; + state_ = 3; // state: "finished selection" + is_selection_ = true; +} + +// Clear selection +// Returns true if there was a selection, false if there wasn't +// +bool Fl_Terminal::Selection::clear(void) { + bool was_selected = is_selection(); // save for return + srow_ = scol_ = erow_ = ecol_ = 0; + state_ = 0; + is_selection_ = false; + return was_selected; +} + +// Scroll the selection up(+)/down(-) number of rows +void Fl_Terminal::Selection::scroll(int nrows) { + if (is_selection()) { + srow_ -= nrows; + erow_ -= nrows; + // Selection scrolled off? clear selection + if (srow_ < 0 || erow_ < 0) clear(); + } +} + +/////////////////////////////////////// +////// EscapeSeq Class Methods //////// +/////////////////////////////////////// + +// Append char to buff[] safely (with bounds checking) +// Returns: +// success - ok +// fail - buffer full/overflow +// +int Fl_Terminal::EscapeSeq::append_buff(char c) { + if (buffp_ >= buffendp_) return fail; // end of buffer reached? + *buffp_++ = c; + *buffp_ = 0; // keep buff[] null terminated + return success; +} + +// Append whatever integer string is at valbuffp into vals_[] safely w/bounds checking +// Assumes valbuffp points to a null terminated string. +// Returns: +// success - parsed ok +// fail - error occurred (non-integer, or vals_[] full) +// +int Fl_Terminal::EscapeSeq::append_val(void) { + if (vali_ >= maxvals) // vals_[] full? + { vali_ = maxvals-1; return fail; } // clamp index, fail + if (!valbuffp_ || (*valbuffp_ == 0)) // no integer to parse? e.g. ESC[m, ESC[;m + { vals_[vali_] = 0; return success; } // zero in array, do not inc vali + if (sscanf(valbuffp_, "%d", &vals_[vali_]) != 1) // Parse integer into vals_[] + { return fail; } // fail if parsed a non-integer + vals_[vali_] &= 0x3ff; // sanity: enforce int in range 0 ~ 1023 (prevent DoS attack) + if (++vali_ >= maxvals) // advance val index, fail if too many vals + { vali_ = maxvals-1; return fail; } // clamp + fail + valbuffp_ = 0; // parsed val ok, reset valbuffp to NULL + return success; +} + +// Ctor +Fl_Terminal::EscapeSeq::EscapeSeq(void) { + reset(); + save_row_ = -1; // only in ctor + save_col_ = -1; +} + +// Reset the class +// Named reset to not be confused with clear() screen/line/etc +// +void Fl_Terminal::EscapeSeq::reset(void) { + esc_mode_ = 0; // disable ESC mode, so parse_in_progress() returns false + csi_ = false; // CSI off until '[' received + buffp_ = buff_; // point to beginning of buffer + buffendp_ = buff_ + (maxbuff - 1); // point to end of buffer + valbuffp_ = 0; // disable val ptr (no vals parsed yet) + vali_ = 0; // zero val index + buff_[0] = 0; // null terminate buffer + vals_[0] = 0; // first val[] 0 + memset(vals_, 0, sizeof(vals_)); +} + +// Return current escape mode. +// This is really only valid after parse() returns 'completed'. +// After a reset() this will return 0. +// +char Fl_Terminal::EscapeSeq::esc_mode(void) const { return esc_mode_; } + +// Set current escape mode. +void Fl_Terminal::EscapeSeq::esc_mode(char val) { esc_mode_ = val; } + +// Return the total vals parsed. +// This is really only valid after parse() returns 'completed'. +// +int Fl_Terminal::EscapeSeq::total_vals(void) 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_Terminal::EscapeSeq::val(int i) const { return vals_[i]; } + +// See if we're in the middle of parsing an ESC sequence +bool Fl_Terminal::EscapeSeq::parse_in_progress(void) const { + return (esc_mode_ == 0) ? false : true; +} + +// See if we're in the middle of parsing an ESC sequence +bool Fl_Terminal::EscapeSeq::is_csi(void) const { return csi_; } + +// Return with default value (if none) or vals[0] (if at least one val spec'd). +// Handles default for single values (e.g. ESC[#H vs. ESC[H) +// vals[0] is clamped between 0 and 'max' +// +int Fl_Terminal::EscapeSeq::defvalmax(int dval, int max) const { + if (total_vals() == 0) return dval; + else return clamp(vals_[0], 0, max); +} + +// Save row,col for later retrieval +void Fl_Terminal::EscapeSeq::save_cursor(int row, int col) { + save_row_ = row; + save_col_ = col; +} + +// Return saved position in row,col +void Fl_Terminal::EscapeSeq::restore_cursor(int &row, int &col) { + row = save_row_; + col = save_col_; +} + +// 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). +// +// 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 the operation, e.g. +// 'm' - [1m -- is_csi() will be true, val() has value(s) parsed +// 'A' - A -- is_csi() will be false (no vals) +// +int Fl_Terminal::EscapeSeq::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_buff(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 == '[') { // [? CSI (Ctrl Seq Introducer) + esc_mode(c); // switch to parsing mode for ESC[#;#;#.. + csi_ = true; // this is now a CSI sequence + vali_ = 0; // zero vals_[] index + valbuffp_ = 0; // valbuffp NULL (no vals yet) + if (append_buff(c) < 0) goto pfail; // save '[' in buf + return success; // success + } else if ( (c >= '@' && c <= 'Z') || // C1 control code (e.g. D, c, etc) + (c >= 'a' && c <= 'z') ) { + esc_mode(c); // save op in esc_mode() for caller to see + csi_ = false; // NOT a CSI sequence + vali_ = 0; + valbuffp_ = 0; // valbuffp NULL (no vals yet) + if (append_buff(c) < 0) goto pfail; // save op in buf + return completed; // completed sequence + } else { // ESCx? + goto pfail; // not supported + } + } else if (esc_mode() == '[') { // '[' mode? e.g. ESC[... aka. is_csi() + 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_buff(c) < 0) goto pfail; // save ';' in buf + return success; + } + if (isdigit(c)) { // parsing an integer? + if (!valbuffp_) // valbuffp not set yet? + { valbuffp_ = buffp_; } // point to first char in integer string + if (append_buff(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 >= '@' && 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_buff(c) < 0 ) goto pfail; // save letter in buffer + esc_mode(c); // change mode to the mode setting char + return completed; // completed/done + } + // Any other chars? reset+fail +pfail: + reset(); + return fail; +} + +////////////////////////////////////// +///// CharStyle Class Methods //////// +////////////////////////////////////// + +// Ctor +Fl_Terminal::CharStyle::CharStyle(void) { + attrib_ = 0; + flags_ = 0; + defaultfgcolor_ = 0xd0d0d000; // off white + defaultbgcolor_ = 0xffffffff; // special color: doesn't draw, 'shows thru' to box() + fgcolor_ = defaultfgcolor_; + bgcolor_ = defaultbgcolor_; + flags_ |= (FG_XTERM | BG_XTERM); + fontface_ = FL_COURIER; + fontsize_ = 14; + update(); // updates fontheight_, fontdescent_, charwidth_ +} + +// Update fontheight/descent cache whenever font changes +void Fl_Terminal::CharStyle::update(void) { + // cache these values + fl_font(fontface_, fontsize_); + fontheight_ = int(fl_height() + 0.5); + fontdescent_ = int(fl_descent() + 0.5); + charwidth_ = int(fl_width("X") + 0.5); +} + +// Return fg color +Fl_Color Fl_Terminal::CharStyle::fgcolor(void) const { + return fgcolor_; +} + +// Return bg color +Fl_Color Fl_Terminal::CharStyle::bgcolor(void) const { + return bgcolor_; +} + +// Return only the color bit flags +// Only the color bits of 'inflags' are modified with our color bits. +// +uchar Fl_Terminal::CharStyle::colorbits_only(uchar inflags) const { + return (inflags & ~COLORMASK) | (flags_ & COLORMASK); // add color bits only +} + +void Fl_Terminal::CharStyle::fgcolor_uchar(uchar val) { + fgcolor_ = fltk_fg_color(val); + set_flag(FG_XTERM); +} + +void Fl_Terminal::CharStyle::bgcolor_uchar(uchar val) { + bgcolor_ = fltk_bg_color(val); + set_flag(BG_XTERM); +} + +/////////////////////////////////// +///// Cursor Class Methods //////// +/////////////////////////////////// + +// Is cursor at display row,col? +bool Fl_Terminal::Cursor::is_rowcol(int drow,int dcol) const { + return(drow == row_ && dcol == col_); +} + +// Scroll the cursor row up(+)/down(-) number of rows +void Fl_Terminal::Cursor::scroll(int nrows) { + row_ = MAX(row_ - nrows, 0); // don't let (row_<0) +} + +///////////////////////////////////// +///// Utf8Char Class Methods //////// +///////////////////////////////////// + +// Ctor +Fl_Terminal::Utf8Char::Utf8Char(void) { + text_[0] = ' '; + len_ = 1; + attrib_ = 0; + flags_ = 0; + fgcolor_ = 0xffffff00; + bgcolor_ = 0xffffffff; // special color: doesn't draw, 'shows thru' to box() +} + +// copy ctor +Fl_Terminal::Utf8Char::Utf8Char(const Utf8Char& src) { + // local instance not initialized yet; init first, then copy text + text_[0] = ' '; + len_ = 1; + attrib_ = src.attrib_; + flags_ = src.flags_; + fgcolor_ = src.fgcolor_; + bgcolor_ = src.bgcolor_; + text_utf8_(src.text_utf8(), src.length()); // copy the src text +} + +// assignment +Fl_Terminal::Utf8Char& Fl_Terminal::Utf8Char::operator=(const Utf8Char& src) { + // local instance is already initialized, so just change its contents + text_utf8_(src.text_utf8(), src.length()); // local copy src text + attrib_ = src.attrib_; + flags_ = src.flags_; + fgcolor_ = src.fgcolor_; + bgcolor_ = src.bgcolor_; + return *this; +} + +// dtor +Fl_Terminal::Utf8Char::~Utf8Char(void) { + len_ = 0; +} + +// Set 'text_' to valid UTF-8 string 'text'. +// +// text_ must not be NULL, and len must be in range: 1 <= len <= max_utf8(). +// NOTE: Caller must handle such checks, and use handle_unknown_char() +// for invalid chars. +// +void Fl_Terminal::Utf8Char::text_utf8_(const char *text, int len) { + memcpy(text_, text, len); + len_ = len; // update new length +} + +// Set UTF-8 string for this char. +// +// text_ must not be NULL, and len must be in range: 1 <= len <= max_utf8(). +// NOTE: Caller must handle such checks, and use handle_unknown_char() +// for invalid chars. +// +void Fl_Terminal::Utf8Char::text_utf8(const char *text, + int len, + const CharStyle& style) { + text_utf8_(text, len); // updates text_, len_ + fl_font(style.fontface(), style.fontsize()); // need font to calc UTF-8 width + attrib_ = style.attrib(); + flags_ = style.colorbits_only(flags_); + fgcolor_ = style.fgcolor(); + bgcolor_ = style.bgcolor(); +} + +// Set char to single printable ASCII character 'c' +// 'c' must be "printable" ASCII in the range (0x20 <= c <= 0x7e). +// Anything outside of that is silently ignored. +// +void Fl_Terminal::Utf8Char::text_ascii(char c, const CharStyle& style) { + // Signed char vals above 0x7f are /negative/, so <0x20 check covers those + if (c < 0x20 || c >= 0x7e) return; // ASCII non-printable? + text_utf8(&c, 1, style); +} + +// Set fl_font() based on specified style for this char's attribute +void Fl_Terminal::Utf8Char::fl_font_set(const CharStyle& style) const { + int face = style.fontface() | + ((attrib_ & Fl_Terminal::BOLD) ? FL_BOLD : 0) | + ((attrib_ & Fl_Terminal::ITALIC) ? FL_ITALIC : 0); + fl_font(face, style.fontsize()); +} + +// Return the foreground color as an fltk color +Fl_Color Fl_Terminal::Utf8Char::fgcolor(void) const { + return fgcolor_; +} + +// Return the background color as an fltk color +Fl_Color Fl_Terminal::Utf8Char::bgcolor(void) const { + return bgcolor_; +} + +// Return the width of this character in floating point pixels +// +// WARNING: Uses current font, so assumes fl_font(face,size) +// has already been set to current font! +// +double Fl_Terminal::Utf8Char::pwidth(void) const { + return fl_width(text_, len_); +} + +// Return the width of this character in integer pixels +// +// WARNING: Uses current font, so assumes fl_font(face,size) +// has already been set to current font! +// +int Fl_Terminal::Utf8Char::pwidth_int(void) const { + return int(fl_width(text_, len_) + 0.5); +} + +// Return color \p col, possibly influenced by BOLD or DIM attributes \p attr. +// If a \p grp widget is specified (i.e. not NULL), don't let the color \p col be +// influenced by the attribute bits /if/ \p col matches the \p grp widget's own color(). +// +Fl_Color Fl_Terminal::Utf8Char::attr_color(Fl_Color col, const Fl_Widget *grp) const { + // Don't modify color if it's the special 'see thru' color 0x0 or widget's color() + if (grp && ((col == 0xffffffff) || (col == grp->color()))) return grp->color(); + switch (attrib_ & (Fl_Terminal::BOLD|Fl_Terminal::DIM)) { + case 0: return col; // not bold or dim? no change + case Fl_Terminal::BOLD: return bold_color(col); // bold? use bold_color() + case Fl_Terminal::DIM : return dim_color(col); // dim? use dim_color() + default: return col; // bold + dim? cancel out + } +} + +// Return the fg color of char \p u8c possibly influenced by BOLD or DIM. +// If a \p grp widget is specified (i.e. not NULL), don't let the color \p col be +// influenced by the attribute bits /if/ \p col matches the \p grp widget's own color(). +// +Fl_Color Fl_Terminal::Utf8Char::attr_fg_color(const Fl_Widget *grp) const { + if (grp && (fgcolor_ == 0xffffffff)) // see thru color? + { return grp->color(); } // return grp's color() + return (flags_ & Fl_Terminal::FG_XTERM) // fg is an xterm color? + ? attr_color(fgcolor(), grp) // ..use attributes + : fgcolor(); // ..ignore attributes. +} + +Fl_Color Fl_Terminal::Utf8Char::attr_bg_color(const Fl_Widget *grp) const { + if (grp && (bgcolor_ == 0xffffffff)) // see thru color? + { return grp->color(); } // return grp's color() + return (flags_ & Fl_Terminal::BG_XTERM) // bg is an xterm color? + ? attr_color(bgcolor(), grp) // ..use attributes + : bgcolor(); // ..ignore attributes. +} + + +//////////////////////////////////// +///// RingBuffer Class Methods ///// +//////////////////////////////////// + +// Create a new copy of the buffer with different row/col sizes +// Preserves old contents of display and history in use. +// +// The old buffer might have an offset and the hist/disp might wrap +// around the end of the ring. The NEW buffer's offset will be zero, +// so the hist/disp do NOT wrap around, making the move operation easier to debug. +// +// The copy preservation starts at the LAST ROW in display of both old (src) and new (dst) +// buffers, and copies rows in reverse until hist_use_srow() reached, or if we hit top +// of the new history (index=0), which ever comes first. So in the following where the +// display is being enlarged, the copy preservation starts at "Line 5" (bottom of display) +// and works upwards, ending at "Line 1" (top of history use): +// +// OLD (SRC) NEW (DST) +// _____________ _____________ ___ +// | x x x x x x | | x x x x x x | ʌ 'x' indicates +// | Line 1 | ─┐ | x x x x x x | | hist_rows unused history +// | Line 2 | └─> | Line 1 | v buffer memory. +// |-------------| |-------------| --- +// | Line 3 | | Line 2 | ʌ +// | Line 4 | | Line 3 | | +// | Line 5 | ─┐ | Line 4 | | disp_rows +// |_____________| └─> | Line 5 | | +// |_____________| _v_ +// +void Fl_Terminal::RingBuffer::new_copy(int drows, int dcols, int hrows, const CharStyle& style) { + // Create new buffer + int addhist = disp_rows() - drows; // adjust history use + int new_ring_rows = (drows+hrows); + int new_hist_use = clamp(hist_use_ + addhist, 0, hrows); // clamp incase new_hist_rows smaller than old + int new_nchars = (new_ring_rows * dcols); + Utf8Char *new_ring_chars = new Utf8Char[new_nchars]; // Create new ring buffer (all blanks) + // Preserve old contents in new buffer + int dst_cols = dcols; + int src_stop_row = hist_use_srow(); + int tcols = MIN(ring_cols(), dcols); + int src_row = hist_use_srow() + hist_use_ + disp_rows_ - 1; // use row#s relative to hist_use_srow() + int dst_row = new_ring_rows - 1; + // Copy rows: working up from bottom of disp, stop at top of hist + while ((src_row >= src_stop_row) && (dst_row >= 0)) { + Utf8Char *src = u8c_ring_row(src_row); + Utf8Char *dst = new_ring_chars + (dst_row*dst_cols); + for (int col=0; col= htop) && (grow <= hbot)); +} + +// See if 'grow' is within the display buffer +// It's assumed grow is in the range hist_rows() .. ring_rows()-1. +// +bool Fl_Terminal::RingBuffer::is_disp_ring_row(int grow) const { + grow %= ring_rows_; + grow -= offset_; + if (grow < 0) { grow = (ring_rows_ + grow); } + int dtop = hist_rows_; + int dbot = hist_rows_ + disp_rows_ - 1; + return ((grow >= dtop) && (grow <= dbot)); +} + +// Move display row from src_row to dst_row +void Fl_Terminal::RingBuffer::move_disp_row(int src_row, int dst_row) { + Utf8Char *src = u8c_disp_row(src_row); + Utf8Char *dst = u8c_disp_row(dst_row); + for (int col=0; colclear(style); +} + +// Scroll the ring buffer up or down #rows, using 'style' for empty rows +// > Positive rows scroll "up", moves top line(s) into history, clears bot line(s) +// Increases hist_use (unless maxed out). +// > Negative rows scroll "down", clears top line(s), history unaffected +// +void Fl_Terminal::RingBuffer::scroll(int rows, const CharStyle& style) { + if (rows > 0) { + // Scroll up into history + // Example: scroll(2): + // + // BEFORE AFTER + // --------------- --------------- + // | H i s t | --- | x x x x x x x | \_ blanked rows + // | | \ | x x x x x x x | / + // | | ---> |---------------| <- disp_erow() + // | | | H i s t | + // | | | | + // disp_srow() -> |---------------| --- | 0001 | + // | 0001 | \ | 0002 | + // | 0002 | ---> |---------------| <- disp_srow() + // | 0003 | | 0003 | + // | 0004 | | 0004 | + // | 0005 | --- | 0005 | + // | 0006 | \ | 0006 | + // disp_erow() -> --------------- ---> --------------- + // + // \______/ + // Simple + // Offset + rows = clamp(rows, 1, disp_rows()); // sanity + // Scroll up into history + offset_ = (offset_ + rows) % ring_rows_; + // Adjust hist_use, clamp to max + hist_use_ = clamp(hist_use_ + rows, 0, hist_rows_); + // Clear exposed lines at bottom + int srow = (disp_rows() - rows) % disp_rows(); + int erow = disp_rows(); + for (int row=srow; row | 0001 | + // | 0004 [A] | ---┐ | 0002 | + // | 0005 | | | 0003 | + // | 0006 | └---> | 0004 | + // ----------------- ----------------- + // \______/ + // Memory + // move + rows = clamp(-rows, 1, disp_rows()); // make rows positive + sane + for (int row=disp_rows()-1; row>=0; row--) { + int src_row = (row - rows); + int dst_row = row; + if (src_row >= 0) move_disp_row(row-rows, dst_row); // move rows + else clear_disp_row(dst_row, style); // hit top? blank the rest + } + } +} + +// Return UTF-8 char for 'row' in the ring +// Scrolling offset is NOT applied; this is raw access to the ring's rows. +// +// Example: +// // Walk ALL rows in the ring buffer.. +// for (int row=0; row= 0 && row < ring_rows_); + return &ring_chars_[row * ring_cols()]; +} + +// Return UTF-8 char for beginning of 'row' in the history buffer. +// Example: +// // Walk ALL rows in history.. +// for (int hrow=0; hrow= 0 && rowi <= ring_rows_); + return &ring_chars_[rowi * ring_cols()]; +} + +// Special case to walk the "in use" rows of the history +// Example: +// // Walk the "in use" rows of history.. +// for (int hrow=0; hrow= 0 && hurow <= hist_use()); + return &ring_chars_[hurow * ring_cols()]; +} + +// Return UTF-8 char for beginning of 'row' in the display buffer +// Example: +// // Walk ALL rows in display.. +// for (int drow=0; drow= 0 && rowi <= ring_rows_); + return &ring_chars_[rowi * ring_cols()]; +} + +// non-const versions of the above +Fl_Terminal::Utf8Char* Fl_Terminal::RingBuffer::u8c_ring_row(int row) + { return const_cast(const_cast(this)->u8c_ring_row(row)); } + +Fl_Terminal::Utf8Char* Fl_Terminal::RingBuffer::u8c_hist_row(int hrow) + { return const_cast(const_cast(this)->u8c_hist_row(hrow)); } + +Fl_Terminal::Utf8Char* Fl_Terminal::RingBuffer::u8c_hist_use_row(int hurow) + { return const_cast(const_cast(this)->u8c_hist_use_row(hurow)); } + +Fl_Terminal::Utf8Char* Fl_Terminal::RingBuffer::u8c_disp_row(int drow) + { return const_cast(const_cast(this)->u8c_disp_row(drow)); } + +// Resize ring buffer by creating new one, dumping old (if any). +// Input: +// drows -- display height in lines of text (rows) +// dcols -- display width in characters (columns) +// hrows -- scrollback history size in lines of text (rows) +// +void Fl_Terminal::RingBuffer::create(int drows, int dcols, int hrows) { + clear(); + // History + hist_rows_ = hrows; + hist_use_ = 0; + // Display + disp_rows_ = drows; + // Ring buffer + ring_rows_ = hist_rows_ + disp_rows_; + ring_cols_ = dcols; + nchars_ = ring_rows_ * ring_cols_; + ring_chars_ = new Utf8Char[nchars_]; +} + +// Resize the buffer, preserve previous contents as much as possible +void Fl_Terminal::RingBuffer::resize(int drows, int dcols, int hrows, const CharStyle& style) { + // If dcols or (drows+hrows) changed, make a NEW buffer and copy old contents. + // New copy will have xxxx_rows/cols and nchars adjusted. + // + if (dcols != disp_cols() || // cols changed size? + (drows+hrows) != (disp_rows()+hist_rows()) ) { // total #rows changed? + new_copy(drows, dcols, hrows, style); + } else { + // Cols and total rows the same, probably just changed disp/hist ratio + int addhist = disp_rows() - drows; // adj hist_use smaller if disp enlarged + hist_rows_ = hrows; // adj hist rows for new value + disp_rows_ = drows; // adj disp rows for new value + hist_use_ = clamp(hist_use_ + addhist, 0, hrows); + } +} + +// Change the display rows. Use style for new rows, if any. +void Fl_Terminal::RingBuffer::change_disp_rows(int drows, const CharStyle& style) + { resize(drows, ring_cols(), hist_rows(), style); } + +// Change the display columns. Use style for new columns, if any. +void Fl_Terminal::RingBuffer::change_disp_cols(int dcols, const CharStyle& style) + { resize(disp_rows(), dcols, hist_rows(), style); } + +///////////////////////////////////// +///// Fl_Terminal Class Methods ///// +///////////////////////////////////// + +// Return u8c for beginning of a row inside the ring. +// 'grow' is 'globally' indexed (relative to the beginning of the ring buffer), +// and so can access ANY character in the entire ring buffer (hist or disp) +// by its global index, which is to say without any scrolling offset applied. +// Should really ONLY be used for making a complete copy of the ring. +// +const Fl_Terminal::Utf8Char* Fl_Terminal::u8c_ring_row(int grow) const + { return ring_.u8c_ring_row(grow); } + +// Return u8c for beginning of a row inside the history. +// 'hrow' is indexed relative to the beginning of the history buffer. +// +const Fl_Terminal::Utf8Char* Fl_Terminal::u8c_hist_row(int hrow) const + { return ring_.u8c_hist_row(hrow); } + +// Return u8c for beginning of a row inside the 'in use' history. +// 'hurow' is indexed relative to the beginning of the 'in use' buffer. +// +const Fl_Terminal::Utf8Char* Fl_Terminal::u8c_hist_use_row(int hurow) const + { return ring_.u8c_hist_use_row(hurow); } + +// Return u8c for beginning of a row inside the display +// 'drow' is indexed relative to the beginning of the display buffer. +// +const Fl_Terminal::Utf8Char* Fl_Terminal::u8c_disp_row(int drow) const + { return ring_.u8c_disp_row(drow); } + +// non-const versions of the above +Fl_Terminal::Utf8Char* Fl_Terminal::u8c_ring_row(int grow) + { return const_cast(const_cast(this)->u8c_ring_row(grow)); } + +Fl_Terminal::Utf8Char* Fl_Terminal::u8c_hist_row(int hrow) + { return const_cast(const_cast(this)->u8c_hist_row(hrow)); } + +Fl_Terminal::Utf8Char* Fl_Terminal::u8c_hist_use_row(int hurow) + { return const_cast(const_cast(this)->u8c_hist_use_row(hurow)); } + +Fl_Terminal::Utf8Char* Fl_Terminal::u8c_disp_row(int drow) + { return const_cast(const_cast(this)->u8c_disp_row(drow)); } + +// Create ring buffer. +// Input: +// drows -- display height in lines of text (rows) +// dcols -- display width in characters (columns) +// hrows -- scrollback history size in lines of text (rows) +// +// NOTE: Caller should call update_screen() at some point +// to fix the scrollbar and other things. +// +void Fl_Terminal::create_ring(int drows, int dcols, int hrows) { + // recreate tabstops if col width being changed + if (dcols != ring_.ring_cols()) init_tabstops(dcols); + // recreate ring (dumps old) + ring_.create(drows, dcols, hrows); + // ensure cursor starts at home position + cursor_.home(); +} + +// Return the Utf8Char* for character under cursor. +Fl_Terminal::Utf8Char* Fl_Terminal::u8c_cursor(void) { + return u8c_disp_row(cursor_.row()) + cursor_.col(); +} + +// Return scrollbar width if visible, or 0 if not visible +int Fl_Terminal::vscroll_width(void) const { + return(vscroll_->visible() ? vscroll_->w() : 0); +} + +// Initialize tabstops for terminal +// NOTE: 'newsize' should always be at least 'ring_cols()'.. +// +void Fl_Terminal::init_tabstops(int newsize) { + if (newsize > tabstops_size_) { // enlarge? + char *oldstops = tabstops_; // save old stops + int oldsize = tabstops_size_; // save old size + tabstops_ = (char*)malloc(newsize); // alloc new + for (int t=0; tvalue(); + // Enforce minimum tabsize of 10 or width of scrollbar + // The minimum vert size of tab should be scrollbar's width, + // but not smaller than 10 pixels, so user can grab it easily. + { + float tabsize = disp_rows() / float(disp_rows() + history_use()); + float minpix = float(MAX(10, vscroll_->w())); // scroll width preferred, 10pix minimum + float minfrac = minpix/vscroll_->h(); // scroll wants a fraction, so convert + tabsize = MAX(minfrac, tabsize); // use the best fractional size + vscroll_->slider_size(tabsize); + } + vscroll_->range(hist_use(), 0); // 'minimum' is larger than 'max' + if (value_before == 0) vscroll_->value(0); // was at bottom? stay at bottom + // Ensure scrollbar in proper position + update_screen_xywh(); // ensure scrn_ up to date first + int sx = scrn_.r() + margin_.right(); + int sy = scrn_.y() - margin_.top(); + int sw = scrollbar_actual_size(); + int sh = scrn_.h() + margin_.top() + margin_.bottom(); + if (vscroll_->x() != sx || + vscroll_->y() != sy || + vscroll_->w() != sw || + vscroll_->h() != sh) { + vscroll_->resize(sx, sy, sw, sh); + init_sizes(); // tell Fl_Group child changed size.. + update_screen_xywh(); // ensure scrn_ is aware of sw change + display_modified(); // redraw Fl_Terminal since scroller changed size + } + vscroll_->redraw(); // redraw scroll always +} + +// Refit the display to match screen +void Fl_Terminal::refit_disp_to_screen(void) { + int dh = h_to_row(scrn_.h()); + int dw = MAX(w_to_col(scrn_.w()), disp_cols()); // enlarge cols only + int drows = clamp(dh, 2, dh); // 2 rows minimum + int dcols = clamp(dw, 10, dw); // 10 cols minimum + int drow_diff = drows - ring_.disp_rows(); // change in rows? + ring_.resize(drows, dcols, hist_rows(), current_style_); + cursor_.scroll(-drow_diff); + clear_mouse_selection(); + update_screen(false); +} + +// Resize the display's vertical size to (drows). +// When enlarging / shrinking, KEEP BOTTOM OF DISPLAY THE SAME, e.g. +// +// Display Display +// BEFORE: SMALLER: +// ___________ ___________ +// | Hist -3 | ʌ | Hist -3 | ʌ +// | Hist -2 | |- 3 | Hist -2 | | +// | Hist -1 | v | Hist -1 | |-- new hist size +// |-----------| ---┐ | Line 1 | | +// | Line 1 | | | Line 2 | v +// | Line 2 | └---> |-----------| +// | Line 3 | | Line 3 | ʌ +// | : | | : | |-- new disp size +// | Line 25 | -------> | Line 25 | v +// ----------- ----------- +// +// Display Display +// BEFORE: LARGER +// ___________ ___________ +// | Hist -3 | ʌ | Hist -3 | --- new hist size +// | Hist -2 | |- 3 ┌-->|-----------| +// | Hist -1 | v | | Hist -2 | ʌ +// |-----------| ------┘ | Hist -1 | | +// | Line 1 | | Line 1 | | +// | Line 2 | | Line 2 | |-- new disp size +// | Line 3 | | Line 3 | | +// | : | | : | | +// | Line 25 | --------> | Line 25 | v +// ----------- ----------- +// +// +void Fl_Terminal::resize_display_rows(int drows) { + int drow_diff = drows - ring_.disp_rows(); // Change in rows? + if (drow_diff == 0) return; // No changes? early exit + int new_dcols = ring_cols(); // keep cols the same + int new_hrows = hist_rows() - drow_diff; // keep disp:hist ratio same + if (new_hrows<0) new_hrows = 0; // don't let hist be <0 + ring_.resize(drows, new_dcols, new_hrows, current_style_); + // ..update cursor/selections to track text position + cursor_.scroll(-drow_diff); + select_.clear(); // clear any mouse selection + // ..update scrollbar, since disp_height relative to hist_use changed + update_scrollbar(); +} + +// Resize the display's columns +// This affects the history and entire ring buffer too. +// Make an effort to preserve previous content. +// Up to caller to enforce any 'minimum' size for drows. +// +void Fl_Terminal::resize_display_columns(int dcols) { + // No changes? early exit + if (dcols == disp_cols()) return; + // Change cols, preserves previous content if possible + ring_.resize(disp_rows(), dcols, hist_rows(), current_style_); + update_scrollbar(); +} + +// Update only the internal terminal screen xywh and x2/y2 values +void Fl_Terminal::update_screen_xywh(void) { + const Margin &m = margin_; + scrn_ = *this; // start with widget's current xywh + scrn_.inset(box()); // apply box offset + scrn_.inset(m.left(), m.top(), m.right(), m.bottom()); // apply margins offset + scrn_.inset(0, 0, scrollbar_actual_size(), 0); // apply scrollbar width +} + +// Update internals when something "global" changes +// Call this when something important is changed: +// Resizing screen or changing font/size affect internals globally. +// Font change affects per-character caching of char widths. +// Display resize affects scrn_ cache, scrollbars, etc. +// +void Fl_Terminal::update_screen(bool font_changed) { + // current_style: update cursor's size for current font/size + if (font_changed) { + // Change font and current_style's font height + fl_font(current_style_.fontface(), current_style_.fontsize()); + cursor_.h(current_style_.fontheight()); + } + // Update the scrn_* values + update_screen_xywh(); + // Recalc the scrollbar size/position/etc + update_scrollbar(); +} + +/** + Return terminal's scrollback history buffer size in lines of text (rows). +*/ +int Fl_Terminal::history_rows(void) const { + return hist_rows(); +} + +/** + Set terminal's scrollback history buffer size in lines of text (rows). +*/ +void Fl_Terminal::history_rows(int hrows) { + if (hrows == history_rows()) return; // no change? done + ring_.resize(disp_rows(), disp_cols(), hrows, current_style_); + update_screen(false); // false: no font change + display_modified(); +} + +/** + Returns how many lines are "in use" by the screen history buffer. + + This value will be 0 if history was recently cleared with e.g. + clear_history() or \c "c". + + Return value will be in the range 0 .. (history_lines()-1). +*/ +int Fl_Terminal::history_use(void) const { + return ring_.hist_use(); +} + +/** + Return terminal's display height in lines of text (rows). + + This value is normally managed automatically by resize() + based on the current font size. +*/ +int Fl_Terminal::display_rows(void) const { + return ring_.disp_rows(); +} + +/** + Set terminal's display height in lines of text (rows). + + This value is normally managed automatically by resize() + based on the current font size, and should not be changed. + + To change the display height, use resize() instead. +*/ +void Fl_Terminal::display_rows(int drows) { + if (drows == disp_rows()) return; // no change? early exit + ring_.resize(drows, disp_cols(), hist_rows(), current_style_); + update_screen(false); // false: no font change ?NEED? +} + +/** + Return terminal's display width in columns of text characters. + + This value is normally managed automatically by resize() + based on the current font size. +*/ +int Fl_Terminal::display_columns(void) const { + return ring_.disp_cols(); +} + +/** + Set terminal's display width in columns of text characters. + + This value is normally managed automatically by resize() + based on the current font size, and should not be changed. + + You CAN make the display_columns() larger than the width of + the widget; text in the terminal will simply run off the + screen edge and be clipped; the only way to reveal that + text is if the user enlarges the widget, or the font size + made smaller. + + To change the display width, it is best to use resize() instead. +*/ +void Fl_Terminal::display_columns(int dcols) { + if (dcols == disp_cols()) return; // no change? early exit + // Change cols, preserves previous content if possible + ring_.resize(disp_rows(), dcols, hist_rows(), current_style_); + update_screen(false); // false: no font change ?NEED? +} + +/** Return current style for rendering text. */ +const Fl_Terminal::CharStyle& Fl_Terminal::current_style(void) const { + return current_style_; +} + +/** Set current style for rendering text. */ +void Fl_Terminal::current_style(const CharStyle& sty) { + current_style_ = sty; +} + +/** + Set the left margin; see \ref Fl_Terminal_Margins. +*/ +void Fl_Terminal::margin_left(int val) { + val = clamp(val,0,w()-1); + margin_.left(val); + update_screen(true); + refit_disp_to_screen(); +} + +/** + Set the right margin; see \ref Fl_Terminal_Margins. +*/ +void Fl_Terminal::margin_right(int val) { + val = clamp(val,0,w()-1); + margin_.right(val); + update_screen(true); + refit_disp_to_screen(); +} + +/** + Set the top margin; see \ref Fl_Terminal_Margins. +*/ +void Fl_Terminal::margin_top(int val) { + val = clamp(val,0,h()-1); + margin_.top(val); + update_screen(true); + refit_disp_to_screen(); +} + +/** + Set the bottom margin; see \ref Fl_Terminal_Margins. +*/ +void Fl_Terminal::margin_bottom(int val) { + val = clamp(val,0,h()-1); + margin_.bottom(val); + update_screen(true); + refit_disp_to_screen(); +} + +/** + Sets the font used for all text displayed in the terminal. + + This affects all existing text (in display and history) as well + as any newly printed text. + + Only monospace fonts are recommended, such as FL_COURIER or FL_SCREEN. + Custom fonts configured with Fl::set_font() will also work, as long + as they are monospace. +*/ +void Fl_Terminal::textfont(Fl_Font val) { + current_style_.fontface(val); + update_screen(true); + display_modified(); +} + +/** + Sets the font size used for all text displayed in the terminal. + + This affects all existing text (in display and history) as well + as any newly printed text. + + Changing this will affect the display_rows() and display_columns(). +*/ +void Fl_Terminal::textsize(Fl_Fontsize val) { + current_style_.fontsize(val); + update_screen(true); + // Changing font size affects #lines in display, so resize it + refit_disp_to_screen(); + display_modified(); +} + +/** + Sets the foreground text color as one of the 8 'xterm color' values. + + This will be the foreground color used for all newly printed text, + similar to the \c \[\#m escape sequence, where \# is between 30 and 37. + + This color will be reset to the default fg color if reset_terminal() + is called, or by \c \c, \c \[0m, etc. + + The xterm color intensity values can be influenced by the Dim/Bold/Normal + modes (which can be set with e.g. \c \[1m, textattrib(), etc), so the + actual RGB values of these colors allow room for Dim/Bold to influence their + brightness. For instance, "Normal Red" is not full brightness to allow + "Bold Red" to be brighter. This goes for all colors except 'Black', which + is not influenced by Dim or Bold; Black is always Black. + + The 8 color xterm values are: + - 0 = Black + - 1 = Red + - 2 = Green + - 3 = Yellow + - 4 = Blue + - 5 = Magenta + - 6 = Cyan + - 7 = White + + \see textfgcolor_default(Fl_Color) +*/ +void Fl_Terminal::textfgcolor_xterm(uchar val) { + current_style_.fgcolor(fltk_fg_color(val)); +} + +/** + Sets the background text color as one of the 8 'xterm color' values. + + This will be the foreground color used for all newly printed text, + similar to the \c \[\#m escape sequence, where \# is between 40 and 47. + + This color will be reset to the default bg color if reset_terminal() + is called, or by \c \c, \c \[0m, etc. + + The xterm color intensity values can be influenced by the Dim/Bold/Normal + modes (which can be set with e.g. \c \[1m, textattrib(), etc), so the + actual RGB values of these colors allow room for Dim/Bold to influence their + brightness. For instance, "Normal Red" is not full brightness to allow + "Bold Red" to be brighter. This goes for all colors except 'Black', which + is not influenced by Dim or Bold; Black is always Black. + + The 8 color xterm values are: + - 0 = Black + - 1 = Red + - 2 = Green + - 3 = Yellow + - 4 = Blue + - 5 = Magenta + - 6 = Cyan + - 7 = White + + \see textbgcolor_default(Fl_Color) +*/ +void Fl_Terminal::textbgcolor_xterm(uchar val) { + current_style_.bgcolor(fltk_bg_color(val)); +} + +/** + Set text foreground drawing color to fltk color \p val. + Use this for temporary color changes, similar to \[38;2;\;\;\m + + This setting does _not_ affect the 'default' text colors used by \[0m, + \c, reset_terminal(), etc. To change both the current _and_ + default fg color, also use textfgcolor_default(Fl_Color). Example: + \par + \code + // Set both 'current' and 'default' colors + Fl_Color amber = 0xd0704000; + tty->textfgcolor(amber); // set 'current' fg color + tty->textfgcolor_default(amber); // set 'default' fg color used by ESC[0m reset + \endcode + \see textfgcolor_default(Fl_Color) +*/ +void Fl_Terminal::textfgcolor(Fl_Color val) { + current_style_.fgcolor(val); +} + +/** + Set text background color to fltk color \p val. + Use this for temporary color changes, similar to \[48;2;\;\;\m + + This setting does _not_ affect the 'default' text colors used by \[0m, + \c, reset_terminal(), etc. To change both the current _and_ + default bg color, also use textbgcolor_default(Fl_Color). Example: + \par + \code + // Set both 'current' and 'default' colors + Fl_Color darkamber = 0x20100000; + tty->textbgcolor(darkamber); // set 'current' bg color + tty->textbgcolor_default(darkamber); // set 'default' bg color used by ESC[0m reset + \endcode + + \see textbgcolor_default(Fl_Color) +*/ +void Fl_Terminal::textbgcolor(Fl_Color val) { + current_style_.bgcolor(val); +} + +/** + Set the default text foreground color used by \c \c, \c \[0m, + and reset_terminal(). + + Does not affect the 'current' text fg color; use textfgcolor(Fl_Color) to + set that. + + \see textfgcolor(Fl_Color) +*/ +void Fl_Terminal::textfgcolor_default(Fl_Color val) { + current_style_.defaultfgcolor(val); +} + +/** + Set the default text background color used by \c \c, \c \[0m, + and reset_terminal(). + + Does not affect the 'current' text fg color; use textbgcolor(Fl_Color) to + set that. + + \see textbgcolor(Fl_Color) +*/ +void Fl_Terminal::textbgcolor_default(Fl_Color val) { + current_style_.defaultbgcolor(val); +} + +/** + Set text attribute bits (underline, inverse, etc). + This will be the default attribute used for all newly printed text. + + \see Fl_Terminal::Attrib +*/ +void Fl_Terminal::textattrib(uchar val) { + current_style_.attrib(val); +} + +// Convert fltk window X coord to column 'gcol' on specified global 'grow' +// Returns 1 if 'gcol' was found, or 0 if X not within any char in 'grow' +// +int Fl_Terminal::x_to_glob_col(int X, int grow, int &gcol) const { + int cx = x() + margin_.left(); // char x position + const Utf8Char *u8c = utf8_char_at_glob(grow, 0); + for (gcol=0; gcolfl_font_set(current_style_); // pwidth_int() needs fl_font set + int cx2 = cx + u8c->pwidth_int(); // char x2 (right edge of char) + if (X >= cx && X < cx2) return 1; // found? return with gcol set + cx += u8c->pwidth_int(); // move cx to start x of next char + } + gcol = ring_cols()-1; // don't leave larger than #cols + return 0; // not found +} + +// Convert fltk window X,Y coords to row + column indexing into ring_chars[] +// Returns: +// 1 -- found row,col +// 0 -- not found, outside display's character area +// -1/-2/-3/-4 -- not found, off top/bot/lt/rt edge respectively +// +int Fl_Terminal::xy_to_glob_rowcol(int X, int Y, int &grow, int &gcol) const { + // X,Y outside terminal area? early exit + if (Yscrn_.b()) return -2; // dn (off bot edge) + if (Xscrn_.r()) return -4; // rt (off right edge) + // Find toprow of what's currently drawn on screen + int toprow = disp_srow() - vscroll_->value(); + // Find row the 'Y' value is in + grow = toprow + ( (Y-scrn_.y()) / current_style_.fontheight()); + return x_to_glob_col(X, grow, gcol); +} + +/** + Clear the terminal screen only; does not affect the cursor position. + + Also clears the current mouse selection. + + If \p 'scroll_to_hist' is true, the screen is cleared by scrolling the + contents into the scrollback history, where it can be retrieved with the + scrollbar. This is the default behavior. If false, the screen is cleared + and the scrollback history is unchanged. + + Similar to the escape sequence \c "[2J". + + \see clear_screen_home() +*/ +void Fl_Terminal::clear_screen(bool scroll_to_hist) { + if (scroll_to_hist) { scroll(disp_rows()); return; } + for (int drow=0; drow[2J[H". + + \see clear_screen() +*/ +void Fl_Terminal::clear_screen_home(bool scroll_to_hist) { + cursor_home(); + clear_screen(scroll_to_hist); +} + +/// Clear from cursor to Start Of Display (EOD), like \c "[1J". +void Fl_Terminal::clear_sod(void) { + for (int drow=0; drow <= cursor_.row(); drow++) + if (drow == cursor_.row()) + for (int dcol=0; dcol<=cursor_.col(); dcol++) + putchar(' ', drow, dcol); + else + for (int dcol=0; dcol[J[0J". +void Fl_Terminal::clear_eod(void) { + for (int drow=cursor_.row(); drow[K". +void Fl_Terminal::clear_eol(void) { + Utf8Char *u8c = u8c_disp_row(cursor_.row()) + cursor_.col(); // start at cursor + for (int col=cursor_.col(); colclear(current_style_); + //TODO: Clear mouse selection? +} + +/// Clear from cursor to Start Of Line (SOL), like \c "[1K". +void Fl_Terminal::clear_sol(void) { + Utf8Char *u8c = u8c_disp_row(cursor_.row()); // start at sol + for (int col=0; col<=cursor_.col(); col++) // run from sol to cursor + (u8c++)->clear(current_style_); + //TODO: Clear mouse selection? +} + +/// Clear entire line for specified row. +void Fl_Terminal::clear_line(int drow) { + Utf8Char *u8c = u8c_disp_row(drow); // start at sol + for (int col=0; colclear(current_style_); + //TODO: Clear mouse selection? +} + +/// Clear entire line cursor is currently on. +void Fl_Terminal::clear_line(void) { + clear_line(cursor_.row()); +} + +/// Returns true if there's a mouse selection. +bool Fl_Terminal::is_selection(void) const { + return select_.is_selection(); +} + +/** + Walk the mouse selection one character at a time from beginning to end, + returning a Utf8Char* to the next character in the selection, or NULL + if the end was reached, or if there's no selection. + + This is easier to use for walking the selection than get_selection(). + + \p u8c should start out as NULL, rewinding to the beginning of the selection. + If the returned Utf8Char* is not NULL, \p row and \p col return the + character's row/column position in the ring buffer. + \par + \code + // EXAMPLE: Walk the entire mouse selection, if any + int row,col; // the returned row/col for each char + Utf8Char *u8c = NULL; // start with NULL to begin walk + while ((u8c = walk_selection(u8c, row, col))) { // loop until end char reached + ..do something with *u8c.. + } + \endcode + + \see get_selection(), is_selection() +*/ +const Fl_Terminal::Utf8Char* Fl_Terminal::walk_selection( + const Utf8Char *u8c, ///< NULL on first iter + int &row, ///< returned row# + int &col ///< returned col# + ) const { + if (u8c==NULL) { + if (!is_selection()) return NULL; + row = select_.srow(); + col = select_.scol(); + u8c = u8c_ring_row(row); + } else { + // At end? done + if (row == select_.erow() && col == select_.ecol()) return NULL; + if (++col >= ring_cols()) // advance to next char + { col = 0; ++row; } // wrapped to next row? + } + return u8c_ring_row(row) + col; +} + +/** + Return mouse selection's start/end position in the ring buffer, if any. + + Ensures (start < end) to allow walking 'forward' thru selection, + left-to-right, top-to-bottom. The row/col values are indexes into + the entire ring buffer. + + Example: walk the characters of the mouse selection: + + \par + \code + // Get selection + int srow,scol,erow,ecol; + if (get_selection(srow,scol,erow,ecol)) { // mouse selection exists? + // Walk entire selection from start to end + for (int row=srow; row<=erow; row++) { // walk rows of selection + const Utf8Char *u8c = u8c_ring_row(row); // ptr to first character in row + int col_start = (row==srow) ? scol : 0; // start row? start at scol + int col_end = (row==erow) ? ecol : ring_cols(); // end row? end at ecol + u8c += col_start; // include col offset (if any) + for (int col=col_start; col<=col_end; col++,u8c++) { // walk columns + ..do something with each char at *u8c.. + } + } + } + \endcode + + Returns: + - true -- valid selection values returned + - false -- no selection was made, returned values undefined + + \see walk_selection(), is_selection() +*/ +bool Fl_Terminal::get_selection(int &srow, ///< starting row for selection + int &scol, ///< starting column for selection + int &erow, ///< ending row for selection + int &ecol ///< ending column for selection + ) const { + return select_.get_selection(srow, scol, erow, ecol); +} + +// Is global row,col (relative to ring_chars[]) inside the current mouse selection? +// Retruns: +// true -- (row,col) inside a valid selection. +// false -- (row,col) outside, or no valid selection. +// +bool Fl_Terminal::is_inside_selection(int grow, int gcol) const { + if (!is_selection()) return false; + int ncols = ring_cols(); + // Calculate row/col magnitudes to simplify test + int check = (grow * ncols) + gcol; + int start = (select_.srow() * ncols) + select_.scol(); + int end = (select_.erow() * ncols) + select_.ecol(); + if (start > end) swap(start, end); // ensure (start < end) + return (check >= start && check <= end); +} + +// See if global row (grow) is inside the 'display' area +// +// No wrap case: Wrap case: +// ______________ ______________ +// ring_srow() -> | | ring_srow() -> | [C] | +// | | : | | +// | | : | Display | ... +// | History | : | | : +// | | disp_erow() -> | [C] | : +// | | |--------------| : +// |______________| | | : Display +// disp_srow() -> | [A] | | History | : straddles +// : | | | | : end of ring +// : | Display | |--------------| : +// : | | disp_srow() -> | [D] | ..: +// : | | : | Display | +// : | | : | | +// disp_erow ┬─> | [B] | ring_erow() -> | [D] | +// ring_erow ┘ -------------- -------------- +// +bool Fl_Terminal::is_disp_ring_row(int grow) const { + return ring_.is_disp_ring_row(grow); +} + +// Return byte length of all UTF-8 chars in selection, or 0 if no selection. +// NOTE: Length includes trailing white on each line. +// +int Fl_Terminal::selection_text_len(void) const { + int row,col,len=0; + const Utf8Char *u8c = NULL; // start with NULL to begin walk + while ((u8c = walk_selection(u8c, row, col))) // loop until end char reached + len += u8c->length(); + return len; +} + +// Return text selection (for copy()/paste() operations) +// Returns allocated NULL terminated string for entire selection. +// Caller must free() this memory when done. +// Unicode safe. +// +const char* Fl_Terminal::selection_text(void) const { + if (!is_selection()) return fl_strdup(""); // no selection? empty string + // Allocate buff large enough for all UTF-8 chars + int clen = 0; // char length + int buflen = selection_text_len(); + char *buf = (char*)malloc(buflen+1); // +1 for NULL + char *bufp = buf; + char *nspc = bufp; // last 'non-space' char + // Loop from srow,scol .. erow,ecol + int row,col; + const Utf8Char *u8c = NULL; // start with NULL to begin walk + while ((u8c = walk_selection(u8c, row, col))) { // loop until end char reached + clen = u8c->length(); // get char length + memcpy(bufp, u8c->text_utf8(), clen); // append UTF-8 string to buffer + // Handle ignoring trailing whitespace + if (!u8c->is_char(' ')) nspc = bufp + clen; // save end pos of last non-spc + bufp += clen; // advance into buffer + if (col >= (ring_cols()-1)) { // eol? handle trailing white + if (nspc && nspc != bufp) { // trailing white space? + bufp = nspc; // rewind bufp, and.. + *bufp++ = '\n'; // ..append crlf + nspc = bufp; // becomes new nspc for nxt row + } + } + } + *bufp = 0; + return buf; +} + +// Clear mouse selection +void Fl_Terminal::clear_mouse_selection(void) { + select_.clear(); +} + +// Extend selection to FLTK coords X,Y. +// Returns true if extended, false if nothing done (X,Y offscreen) +// +bool Fl_Terminal::selection_extend(int X,int Y) { + if (is_selection()) { // selection already? + int grow,gcol; + if (xy_to_glob_rowcol(X, Y, grow, gcol) > 0) { + select_.extend(grow, gcol); // extend it + return true; + } else { + // TODO: If X,Y outside row/col area and SHIFT down, + // extend selection to nearest edge. + } + } + return false; +} + +// Scroll the display up(+) or down(-) number of rows. +// Negative row value scrolls "down", clearing top line, and history unaffected. +// Postive row value scrolls "up", clearing bottom line, rotating top line into history. +// +void Fl_Terminal::scroll(int rows) { + // Scroll the ring + ring_.scroll(rows, current_style_); + if (rows > 0) update_scrollbar(); // scroll up? changes hist, so scrollbar affected + else clear_mouse_selection(); // scroll dn? clear mouse select; it might wrap ring +} + +// Insert (count) rows at cursor position. +// Causes rows below to scroll down, and empty lines created. +// Scrolling does not involve history at all. +// +void Fl_Terminal::insert_rows(int count) { + int dst_drow = disp_rows()-1; // dst is bottom of display + int src_drow = clamp((dst_drow-count), 1, (disp_rows()-1)); // src is count lines up from dst + while ( src_drow >= cursor_.row() ) { // walk srcrow upwards to cursor row + Utf8Char *src = u8c_disp_row(src_drow--); + Utf8Char *dst = u8c_disp_row(dst_drow--); + for (int dcol=0; dcol= cursor_.row() ) { // walk srcrow to curs line + Utf8Char *dst = u8c_disp_row(dst_drow--); + for (int dcol=0; dcolclear(current_style_); + } + clear_mouse_selection(); +} + +// Delete (count) rows at cursor position. +// Causes rows to scroll up, and empty lines created at bottom of screen. +// Scrolling does not involve history at all. +// +void Fl_Terminal::delete_rows(int count) { + int dst_drow = cursor_.row(); // dst is cursor row + int src_drow = clamp((dst_drow+count), 1, (disp_rows()-1)); // src is count rows below cursor + while ( src_drow < disp_rows() ) { // walk srcrow to EOD + Utf8Char *src = u8c_disp_row(src_drow++); + Utf8Char *dst = u8c_disp_row(dst_drow++); + for (int dcol=0; dcolclear(current_style_); + } + clear_mouse_selection(); +} + +// Repeat printing char 'c' for 'rep' times, not to exceed end of line. +void Fl_Terminal::repeat_char(char c, int rep) { + rep = clamp(rep, 1, disp_cols()); + while ( rep-- > 0 && cursor_.col() < disp_cols() ) print_char(c); +} + +// Insert char 'c' for 'rep' times at display (drow,dcol). +void Fl_Terminal::insert_char_eol(char c, int drow, int dcol, int rep) { + // Walk the row from the eol backwards to the col position + // In this example, rep=3: + // + // dcol + // v + // BEFORE: |a|b|c|d|e|f|g|h|i|j| <- eol (disp_cols()-1) + // | | | | |_____ + // src end -> |_____ | <-- src start + // | | | | | + // v v v v v + // AFTER: |a|b|‸|‸|‸|c|d|e|f|g| + // |_|_| <-- spaces added last + // + rep = clamp(rep, 0, disp_cols()); // sanity + if (rep == 0) return; + const CharStyle &style = current_style_; + Utf8Char *src = u8c_disp_row(drow)+disp_cols()-1-rep; // start src at 'g' + Utf8Char *dst = u8c_disp_row(drow)+disp_cols()-1; // start dst at 'j' + for (int col=(disp_cols()-1); col>=dcol; col--) { // loop col in reverse: eol -> dcol + if (col >= (dcol+rep)) *dst-- = *src--; // let assignment do move + else (dst--)->clear(style); // clear chars displaced + } +} + +// Insert char 'c' for 'rep' times. +// Does not wrap; characters at end of line are lost. +// +void Fl_Terminal::insert_char(char c, int rep) { + insert_char_eol(c, cursor_.row(), cursor_.col(), rep); +} + +// Delete char(s) at (drow,dcol) for 'rep' times. +void Fl_Terminal::delete_chars(int drow, int dcol, int rep) { + rep = clamp(rep, 0, disp_cols()); // sanity + if (rep == 0) return; + const CharStyle &style = current_style_; + Utf8Char *u8c = u8c_disp_row(drow); + for (int col=dcol; col= disp_cols()) u8c[col].text_ascii(' ', style); // blanks + else u8c[col] = u8c[col+rep]; // move +} + +// Delete char(s) at cursor position for 'rep' times. +void Fl_Terminal::delete_chars(int rep) { + delete_chars(cursor_.row(), cursor_.col(), rep); +} + +/** + Clears the scroll history buffer and adjusts scrollbar, + forcing it to redraw(). +*/ +void Fl_Terminal::clear_history(void) { + // Adjust history use + ring_.clear_hist(); + vscroll_->value(0); // zero scroll position + // Clear entire history buffer + for (int hrow=0; hrowclear(current_style_); + } + } + // Adjust scrollbar (hist_use changed) + update_scrollbar(); +} + +/** + Resets terminal to default colors, clears screen, history and + mouse selection, homes cursor, resets tabstops. Same as \c "c" +*/ +void Fl_Terminal::reset_terminal(void) { + current_style_.sgr_reset(); // reset current style + clear_screen_home(); // clear screen, home cursor + clear_history(); + clear_mouse_selection(); + default_tabstops(); // reset tabstops to default 8 char +} + +//DEBUG void Fl_Terminal::RingBuffer::show_ring_info(void) const { +//DEBUG ::printf("\033[s"); // save cursor +//DEBUG ::printf("\033[05C -- Ring Index\n"); +//DEBUG ::printf("\033[05C ring_rows_: %d\n", ring_rows_); +//DEBUG ::printf("\033[05C ring_cols_: %d\n", ring_cols_); +//DEBUG ::printf("\033[05C offset_: %d\n", offset_); +//DEBUG ::printf("\033[u"); // recall cursor +//DEBUG ::printf("\033[30C -- History Index\n"); +//DEBUG ::printf("\033[30C hist_rows_: %d srow=%d\n", hist_rows(), hist_srow()); +//DEBUG ::printf("\033[30C hist_cols_: %d erow=%d\n", hist_cols(), hist_erow()); +//DEBUG ::printf("\033[30C hist_use_: %d\n", hist_use()); +//DEBUG ::printf("\033[u"); // recall cursor +//DEBUG ::printf("\033[60C -- Display Index\n"); +//DEBUG ::printf("\033[60C disp_rows_: %d srow=%d\n", disp_rows(), disp_srow()); +//DEBUG ::printf("\033[60C disp_cols_: %d erow=%d\n", disp_cols(), disp_erow()); +//DEBUG ::printf("\n\n"); +//DEBUG } + +//DEBUG // Save specified row from ring buffer 'ring' to FILE* +//DEBUG void Fl_Terminal::write_row(FILE *fp, Utf8Char *u8c, int cols) const { +//DEBUG cols = (cols != 0) ? cols : ring_cols(); +//DEBUG for ( int col=0; collength(), u8c->text_utf8()); +//DEBUG } +//DEBUG } + +//DEBUG // Show two buffers side-by-side on stdout. +//DEBUG // Second buffer can be NULL to just show the a buffer. +//DEBUG // +//DEBUG void Fl_Terminal::show_buffers(RingBuffer *a, RingBuffer *b) const { +//DEBUG int arows = a->ring_rows(), acols = a->ring_cols(); +//DEBUG int brows = b ? b->ring_rows() : 0, bcols = b ? b->ring_cols() : 0; +//DEBUG int trows = MAX(arows,brows); +//DEBUG // Show header +//DEBUG ::printf("\033[H"); +//DEBUG if (a) ::printf("SRC %d x %d huse=%d off=%d", arows,acols, a->hist_use(), a->offset()); +//DEBUG if (b) ::printf(", DST %d x %d huse=%d off=%d", brows, bcols, b->hist_use(), b->offset()); +//DEBUG ::printf("\033[K\n"); +//DEBUG Utf8Char *u8c; +//DEBUG // Show rows +//DEBUG for (int row=0; row < trows; row++) { +//DEBUG // 'A' buffer +//DEBUG if (row >= arows) { +//DEBUG ::printf(" %*s ", acols, ""); +//DEBUG } else { +//DEBUG u8c = a->ring_chars()+(arows*acols); +//DEBUG ::printf("%3d/%3d [", row, trows-1); write_row(stdout, u8c, acols); ::printf("] "); +//DEBUG } +//DEBUG if (!b) { ::printf("\033[K\n"); continue; } +//DEBUG // 'B' buffer +//DEBUG if (row < brows) { +//DEBUG u8c = b->ring_chars()+(brows*bcols); +//DEBUG ::printf("["); write_row(stdout, u8c, bcols); ::printf("]"); +//DEBUG } +//DEBUG ::printf("\033[K\n"); +//DEBUG } +//DEBUG ::printf("--- END\033[0J\n"); // clear eos +//DEBUG ::printf(" HIT ENTER TO CONTINUE: "); getchar(); +//DEBUG fflush(stdout); +//DEBUG } + +/////////////////////////////// +////// CURSOR MANAGEMENT ////// +/////////////////////////////// + +/** Set the cursor's foreground color used for text under the cursor. */ +void Fl_Terminal::cursorfgcolor(Fl_Color val) { cursor_.fgcolor(val); } +/** Set the cursor's background color used for the cursor itself. */ +void Fl_Terminal::cursorbgcolor(Fl_Color val) { cursor_.bgcolor(val); } +/** Get the cursor's foreground color used for text under the cursor. */ +Fl_Color Fl_Terminal::cursorfgcolor(void) const { return cursor_.fgcolor(); } +/** Get the cursor's background color used for the cursor itself. */ +Fl_Color Fl_Terminal::cursorbgcolor(void) const { return cursor_.bgcolor(); } + +/** + Move cursor to the specified row \p row. + This value is clamped to the range (0..display_rows()-1). +*/ +void Fl_Terminal::cursor_row(int row) { cursor_.row( clamp(row,0,disp_rows()-1) ); } +/** Move cursor to the specified column \p col. + This value is clamped to the range (0..display_columns()-1). +*/ +void Fl_Terminal::cursor_col(int col) { cursor_.col( clamp(col,0,disp_cols()-1) ); } +/** Return the cursor's current row position on the screen. */ +int Fl_Terminal::cursor_row(void) const { return cursor_.row(); } +/** Return the cursor's current column position on the screen. */ +int Fl_Terminal::cursor_col(void) const { return cursor_.col(); } + +/** + Moves cursor up \p count lines. + If cursor hits screen top, it either stops (does not wrap) if \p do_scroll + is false, or scrolls down if \p do_scroll is true. +*/ +void Fl_Terminal::cursor_up(int count, bool do_scroll) { + count = clamp(count, 1, disp_rows() * 2); // sanity (max 2 scrns) + while (count-- > 0) { + if (cursor_.up() <= 0) { // hit screen top? + cursor_.row(0); // clamp cursor to top + if (do_scroll) scroll(-1); // scrolling on? scroll down + else return; // scrolling off? stop at top + } + } +} + +/** + Moves cursor down \p count lines. + If cursor hits screen bottom, it either stops (does not wrap) if \p do_scroll + is false, or wraps and scrolls up if \p do_scroll is true. +*/ +void Fl_Terminal::cursor_down(int count, ///< Number of lines to move cursor down + bool do_scroll ///< Enable scrolling if set to true + ) { + count = clamp(count, 1, ring_rows()); // sanity + while (count-- > 0) { + if (cursor_.down() >= disp_rows()) { // hit screen bottom? + cursor_.row(disp_rows() - 1); // clamp + if (!do_scroll) break; // don't scroll? done + scroll(1); // scroll up 1 row to make room for new line + } + } +} + +/** + Moves cursor left \p count columns, and cursor stops (does not wrap) + if it hits screen edge. +*/ +void Fl_Terminal::cursor_left(int count) { + count = clamp(count, 1, disp_cols()); // sanity + while (count-- > 0 ) + if (cursor_.left() < 0) // hit left edge of screen? + { cursor_sol(); return; } // stop, done +} + +/** + Moves cursor right \p count columns. If cursor hits right edge of screen, + it either stops (does not wrap) if \p do_scroll is false, or wraps and + scrolls up one line if \p do_scroll is true. +*/ +void Fl_Terminal::cursor_right(int count, bool do_scroll) { + while (count-- > 0 ) { + if (cursor_.right() >= disp_cols()) { // hit right edge? + if (!do_scroll) // no scroll? + { cursor_eol(); return; } // stop at EOL, done + else + { cursor_crlf(1); } // do scroll? crlf + } + } +} + +/** Move cursor to the home position (top/left). */ +void Fl_Terminal::cursor_home(void) { cursor_.col(0); cursor_.row(0); } + +/** Move cursor to the last column (at the far right) on the current line. */ +void Fl_Terminal::cursor_eol(void) { cursor_.col(disp_cols()-1); } + +/** Move cursor to the first column (at the far left) on the current line. */ +void Fl_Terminal::cursor_sol(void) { cursor_.col(0); } + +/** Move cursor as if a CR (\\r) was received. Same as cursor_sol() */ +void Fl_Terminal::cursor_cr(void) { cursor_sol(); } + +/** Move cursor as if a CR/LF pair (\\r\\n) was received. */ +void Fl_Terminal::cursor_crlf(int count) { + const bool do_scroll = true; + count = clamp(count, 1, ring_rows()); // sanity + cursor_sol(); + cursor_down(count, do_scroll); +} + +// Tab right, do not wrap beyond right edge +void Fl_Terminal::cursor_tab_right(int count) { + count = clamp(count, 1, disp_cols()); // sanity + int X = cursor_.col(); + while ( count-- > 0 ) { + // Find next tabstop + while ( ++X < disp_cols() ) { + if ( (X 0 ) + while ( --X > 0 ) // search for tabstop + if ( (X=0x20) && (c<=0x7e)); +} + +// Is char a ctrl character? (0x00 thru 0x1f) +bool Fl_Terminal::is_ctrl(char c) { + return ((c >= 0x00) && (c < 0x20)) ? true : false; +} + +// Handle ESC[m sequences. +// This is different from the others in that the list of vals +// separated by ;'s can be long, to allow combining multiple mode +// settings at once, e.g. fg and bg colors, multiple attributes, etc. +// +void Fl_Terminal::handle_SGR(void) { // ESC[...m? + // Shortcut varnames.. + EscapeSeq &esc = escseq; + int tot = esc.total_vals(); + // Handle ESC[m or ESC[;m + if (tot == 0) + { current_style_.sgr_reset(); return; } + // Handle ESC[#;#;#...m + int rgbcode = 0; // 0=none, 38=fg, 48=bg + int rgbmode = 0; // 0=none, 1="2", 2=, 3=, 4= + int r=0,g=0,b=0; + for (int i=0; i;;m + case 48: // bg RGB mode? e.g. ESC[48;2;;;m + rgbmode = 1; + rgbcode = val; + continue; + } + break; + case 1: if (val == 2) { rgbmode++; continue; } // '2'? + rgbcode = rgbmode = 0; // not '2'? cancel + handle_unknown_char(); + break; + case 2: r=clamp(val,0,255); ++rgbmode; continue; // parse red value + case 3: g=clamp(val,0,255); ++rgbmode; continue; // parse grn value + case 4: b=clamp(val,0,255); // parse blu value + switch (rgbcode) { + case 38: current_style_.fgcolor(r,g,b); // Set fg rgb + break; + case 48: current_style_.bgcolor(r,g,b); // Set bg rgb + break; + } + rgbcode = rgbmode = 0; // done w/rgb mode parsing + continue; // continue loop to parse more vals + } + if (val < 10) { // Set attribute? (bold,underline..) + switch ( val ) { + case 0: current_style_.sgr_reset(); break; // ESC[0m - reset + case 1: current_style_.sgr_bold(1); break; // ESC[1m - bold + case 2: current_style_.sgr_dim(1); break; // ESC[2m - dim + case 3: current_style_.sgr_italic(1); break; // ESC[3m - italic + case 4: current_style_.sgr_underline(1);break; // ESC[4m - underline + case 5: current_style_.sgr_blink(1); break; // ESC[5m - blink + case 6: handle_unknown_char(); break; // ESC[6m - (unused) + case 7: current_style_.sgr_inverse(1); break; // ESC[7m - inverse + case 8: handle_unknown_char(); break; // ESC[8m - (unused) + case 9: current_style_.sgr_strike(1); break; // ESC[9m - strikeout + } + } else if (val >= 21 && val <= 29) { // attribute extras + switch ( val ) { + case 21: current_style_.sgr_dbl_under(1);break; // ESC[21m - doubly underline + case 22: current_style_.sgr_dim(0); // ESC[22m - disable bold/dim + current_style_.sgr_bold(0); break; // + case 23: current_style_.sgr_italic(0); break; // ESC[23m - disable italic + case 24: current_style_.sgr_underline(0);break; // ESC[24m - disable underline + case 25: current_style_.sgr_blink(0); break; // ESC[25m - disable blink + case 26: handle_unknown_char(); break; // ESC[26m - (unused) + case 27: current_style_.sgr_inverse(0); break; // ESC[27m - disable inverse + case 28: handle_unknown_char(); break; // ESC[28m - disable hidden + case 29: current_style_.sgr_strike(0); break; // ESC[29m - disable strikeout + } + } else if (val >= 30 && val <= 37) { // Set fg color? + current_style_.fgcolor_uchar(val - 30); + } else if (val == 39) { // ESC[39m -- "normal" fg color: + Fl_Color fg = current_style_.defaultfgcolor(); // ..get default color + current_style_.fgcolor(fg); // ..set current color + } else if (val >= 40 && val <= 47) { // Set bg color? + current_style_.bgcolor_uchar(val - 40); + } else if (val == 49) { // ESC[49m -- "normal" bg color: + Fl_Color bg = current_style_.defaultbgcolor(); // ..get default bg color + current_style_.bgcolor(bg); // ..set current bg color + } else { + handle_unknown_char(); // does an escseq.reset() // unimplemented SGR codes + } + } +} + +// Handle [;;;;$t +// This one is fun! is the attrib to xor, i.e. 1(bold),4,5,7(inverse). +// gnome-term doesn't support this, but xterm does. +// +void Fl_Terminal::handle_DECRARA(void) { + // TODO: MAYBE NEVER +} + +// Handle an escape sequence character +// If this char is the end of the sequence, do the operation +// if possible, and then reset() to finish parsing. +// +void Fl_Terminal::handle_escseq(char c) { + // NOTE: Use xterm to test. gnome-terminal has bugs, even in 2022. + const bool do_scroll = true; + const bool no_scroll = false; + //UNUSED const bool do_wrap = true; + //UNUSED const bool no_wrap = false; + switch (escseq.parse(c)) { // parse char, advance s.. + case EscapeSeq::fail: escseq.reset(); return; // failed? reset, done + case EscapeSeq::success: return; // keep parsing.. + case EscapeSeq::completed: break; // parsed complete esc sequence? + } + // Shortcut varnames for escseq parsing.. + EscapeSeq &esc = escseq; + char mode = esc.esc_mode(); + int tot = esc.total_vals(); + int val0 = (tot==0) ? 0 : esc.val(0); + int val1 = (tot<2) ? 0 : esc.val(1); + const int& dw = disp_cols(); + const int& dh = disp_rows(); + if (esc.is_csi()) { // Was this a CSI (ESC[..) sequence? + switch ( mode ) { + case '@': // [#@ - (ICH) Insert blank Chars (default=1) + insert_char(' ', esc.defvalmax(1,dw)); + break; + case 'A': // [#A - (CUU) cursor up, no scroll/wrap + cursor_up(esc.defvalmax(1,dh)); + break; + case 'B': // [#B - (CUD) cursor down, no scroll/wrap + cursor_down(esc.defvalmax(1,dh), no_scroll); + break; + case 'C': // [#C - (CUF) cursor right, no wrap + cursor_right(esc.defvalmax(1,dw), no_scroll); + break; + case 'D': // [#D - (CUB) cursor left, no wrap + cursor_left(esc.defvalmax(1,dw)); + break; + case 'E': // [#E - (CNL) cursor next line (crlf) xterm, !gnome + cursor_crlf(esc.defvalmax(1,dh)); + break; + case 'F': // [#F - (CPL) move to sol and up # lines + cursor_cr(); + cursor_up(esc.defvalmax(1,dh)); + break; + case 'G': // [#G - (CHA) cursor horizal absolute + switch (clamp(tot,0,1)) { // │ + case 0: // ├── [G -- move to sol + cursor_sol(); // │ default [1G + break; // │ + case 1: // └── [#G -- move to column + cursor_col(clamp(val0,1,dw)-1); + break; + } + break; + case 'H': +cup: + switch (clamp(tot,0,2)) { // [#H - (CUP) cursor position (#'s are 1 based) + case 0: // ├── [H -- no vals? + cursor_home(); // │ default [1H + break; // │ + case 1: // ├── [#H -- go to (row #) + cursor_row(clamp(val0,1,dh)-1); // │ NOTE: ESC[5H == ESC[5;1H + cursor_col(0); // │ + break; // │ + case 2: // └── [#;#H -- go to (row# ; col#) + cursor_row(clamp(val0,1,dh)-1); + cursor_col(clamp(val1,1,dw)-1); + break; + } + break; + case 'I': // [#I - (CHT) cursor forward tab (default=1) + switch (clamp(tot,0,1)) { // │ + case 0: // ├── [I -- no vals + cursor_tab_right(1); // │ default [1I + break; // │ + case 1: // └── [#I -- tab # times + cursor_tab_right(clamp(val0,1,dw)); // + break; + } + break; + case 'J': // [#J - (ED) erase in display + switch ( clamp(tot,0,1) ) { // │ + case 0: clear_eol(); break; // ├── [J -- no vals: default [0J + case 1: // │ + switch ( clamp(val0,0,3) ) { // │ + case 0: clear_eod(); break; // ├── [0J -- clear to end of display + case 1: clear_sod(); break; // ├── [1J -- clear to start of display + case 2: clear_screen(); break; // ├── [2J -- clear all lines + case 3: clear_history(); break; // └── [3J -- clear screen history + } + break; + } + break; + case 'K': + switch ( clamp(tot,0,1) ) { // [#K - (EL) Erase in Line + case 0: clear_eol(); break; // ├── [K -- no vals + case 1: switch ( clamp(val0,0,2) ) { // │ + case 0: clear_eol(); break; // ├── [0K -- clear to end of line + case 1: clear_sol(); break; // ├── [1K -- clear to start of line + case 2: clear_line(); break; // └── [2K -- clear current line + } + break; + } + break; + case 'L': // ESC[#L - Insert # lines (def=1) + insert_rows(esc.defvalmax(1,dh)); + break; + case 'M': // ESC[#M - Delete # lines (def=1) + delete_rows(esc.defvalmax(1,dh)); + break; + case 'P': // ESC[#P - Delete # chars (def=1) + delete_chars(esc.defvalmax(1,dh)); + break; + case 'S': // ESC[#S - scroll up # lines (def=1) + scroll( +(esc.defvalmax(1,dh)) ); + // ⮤ positive=scroll up + break; + case 'T': // ESC[#T - scroll dn # lines (def=1) + scroll( -(esc.defvalmax(1,dh)) ); + // ⮤ negative=scroll down + break; + case 'X': // [#X - (ECH) Erase Characters (default=1) + repeat_char(' ', esc.defvalmax(1,dw)); + break; + case 'Z': // ESC[#Z - backtab # tabs + switch (clamp(tot,0,1)) { // │ + case 0: // ├── [Z -- no vals + cursor_tab_left(1); // │ default [1Z + break; // │ + case 1: // └── [#Z -- tab # times + cursor_tab_left(clamp(val0,1,dw)); + break; + } + break; + case 'a': // TODO // ESC[#a - (HPR) move cursor relative [columns] (default=[row,col+1]) + case 'b': // TODO // ESC[#b - (REP) repeat prev graphics char # times + case 'd': // TODO // ESC[#d - (VPA) line pos absolute [row] + case 'e': // TODO // ESC[#e - line pos relative [rows] + handle_unknown_char(); // does an escseq.reset() + break; + case 'f': // [#f - (CUP) cursor position (#'s 1 based) + goto cup; // (same as ESC[H) + case 'g': // ESC[...g? Tabulation Clear (TBC) + switch (val0) { + case 0: clear_tabstop(); break; // clears tabstop at cursor + case 3: clear_all_tabstops(); break; // clears all tabstops + default: + handle_unknown_char(); // does an escseq.reset() + break; + } + break; + case 'm': handle_SGR(); break; // ESC[#m - set character attributes (SGR) + case 's': save_cursor(); break; // ESC[s - save cur pos (xterm+gnome) + case 'u': restore_cursor(); break; // ESC[u - rest cur pos (xterm+gnome) + case 'q': // TODO? // ESC[>#q set cursor style (block/line/blink..) + case 'r': // TODO // ESC[#;#r set scroll region top;bot + // default=full window + handle_unknown_char(); // does an escseq.reset() + break; + case 't': handle_DECRARA(); break; // ESC[#..$t -- (DECRARA) + // Reverse attribs in Rect Area (row,col) + default: + handle_unknown_char(); // does an escseq.reset() + break; + } + } else { + // Not CSI? Might be C1 Control code (D, etc) + switch ( esc.esc_mode() ) { + case 'c': // c - Reset term to Initial State (RIS) + reset_terminal(); + break; + case 'D': cursor_down(1, do_scroll); break;// D - down line, scroll at bottom + case 'E': cursor_crlf(); break;// E - do a crlf + case 'H': set_tabstop(); break;// H - set a tabstop + case 'M': cursor_up(1, true); break;// M -- (RI) Reverse Index (up w/scroll) + default: + handle_unknown_char(); // does an escseq.reset() + break; + } + } + esc.reset(); // done handling escseq, reset() +} + +void Fl_Terminal::display_modified_clear(void) { + redraw_modified_ = false; +} + +// Display modified, trigger redraw handling.. +void Fl_Terminal::display_modified(void) { + if (is_redraw_style(RATE_LIMITED)) { + if (!redraw_modified_) { // wasn't before but now is? + if (!redraw_timer_) { + Fl::add_timeout(.01, redraw_timer_cb, this); // turn on timer + redraw_timer_ = true; + } + redraw_modified_ = true; + } + } else if (is_redraw_style(PER_WRITE)) { + if (!redraw_modified_) { + redraw_modified_ = true; + redraw(); // only call redraw once + } + } else { // NO_REDRAW? + // do nothing + } +} + +/** + Clear the character at the specified display row and column. + + No range checking done on drow,dcol: + - \p drow must be in range 0..(disp_rows()-1) + - \p dcol must be in range 0..(disp_cols()-1) + + - Does not trigger redraws +*/ +void Fl_Terminal::clear_char_at_disp(int drow, int dcol) { + Utf8Char *u8c = u8c_disp_row(drow) + dcol; + u8c->clear(current_style_); +} + +/** + Return Utf8Char* for char at specified display row and column. + This accesses any character in the display part of the ring buffer. + + No range checking done on drow,dcol: + - \p drow must be in range 0..(disp_rows()-1) + - \p dcol must be in range 0..(disp_cols()-1) + + \see u8c_disp_row() +*/ +const Fl_Terminal::Utf8Char* Fl_Terminal::utf8_char_at_disp(int drow, int dcol) const { + return u8c_disp_row(drow) + dcol; +} + +/** + Return Utf8Char* for char at specified global (grow,gcol). + This accesses any character in the ring buffer (history + display). + + No range checking done on grow,gcol: + - \p grow must be in range 0..(ring_rows()-1) + - \p gcol must be in range 0..(ring_cols()-1) + + \see u8c_ring_row() +*/ +const Fl_Terminal::Utf8Char* Fl_Terminal::utf8_char_at_glob(int grow, int gcol) const { + return u8c_ring_row(grow) + gcol; +} + +/** + Print UTF-8 character \p text of length \p len at display position \p (drow,dcol). + The character is displayed using the current text color/attributes. + + This is a very low level method. + + No range checking is done on drow,dcol: + - \p drow must be in range 0..(display_rows()-1) + - \p dcol must be in range 0..(display_columns()-1) + + - Does not trigger redraws + - Does not handle ANSI or XTERM escape sequences + - Invalid UTF-8 chars show the error character (¿) depending on show_unknown(bool). + + \see handle_unknown_char() +*/ +void Fl_Terminal::putchar(const char *text, int len, int drow, int dcol) { + Utf8Char *u8c = u8c_disp_row(drow) + dcol; + // text_utf8() warns we must do invalid checks first + if (!text || len<1 || len>u8c->max_utf8() || len!=fl_utf8len(*text)) + { handle_unknown_char(); return; } + u8c->text_utf8(text, len, current_style_); +} + +/** + Print the ASCII character \p c at the terminal's display position \p (drow,dcol). + + The character MUST be printable (in range 0x20 - 0x7e), and is displayed + using the current text color/attributes. Characters outside that range are either + ignored or print the error character (¿), depending on show_unknown(bool). + + This is a very low level method. + + No range checking is done on drow,dcol: + - \p drow must be in range 0..(display_rows()-1) + - \p dcol must be in range 0..(display_columns()-1) + + - Does not trigger redraws + - Does NOT handle control codes, ANSI or XTERM escape sequences. + + \see show_unknown(bool), handle_unknown_char(), is_printable() +*/ +void Fl_Terminal::putchar(char c, int drow, int dcol) { + if (!is_printable(c)) { handle_unknown_char(); return; } + Utf8Char *u8c = u8c_disp_row(drow) + dcol; + u8c->text_ascii(c, current_style_); +} + +/** + Prints single UTF-8 char \p text of optional byte length \p len + at current cursor position, and advances the cursor if the character + is printable. Handles ASCII and control codes (CR, LF, etc). + + The character is displayed at the current cursor position + using the current text color/attributes. + + Handles control codes and can be used to construct ANSI/XTERM + escape sequences. + + - If optional \p len isn't specified or <0, strlen(text) is used. + - \p text must not be NULL. + - \p len must not be 0. + - \p text must be a single char only (whether UTF-8 or ASCII) + - \p text can be an ASCII character, though not as efficent as print_char() + - Invalid UTF-8 chars show the error character (¿) depending on show_unknown(bool). + - Does not trigger redraws + + \see show_unknown(bool), handle_unknown_char() +*/ +void Fl_Terminal::print_char(const char *text, int len/*=-1*/) { + len = len<0 ? fl_utf8len(*text) : len; // int(strlen(text)) : len; + const bool do_scroll = true; + if (is_ctrl(text[0])) { // Handle ctrl character + handle_ctrl(*text); + } else if (escseq.parse_in_progress()) { // ESC sequence in progress? + handle_escseq(*text); + } else { // Handle printable char.. + putchar(text, len, cursor_row(), cursor_col()); + cursor_right(1, do_scroll); + } +} + +/** + Prints single ASCII char \p c at current cursor position, and advances the cursor. + + The character is displayed at the current cursor position + using the current text color/attributes. + + - \p c must be ASCII, not utf-8 + - Does not trigger redraws +*/ +void Fl_Terminal::print_char(char c) { + const bool do_scroll = true; + if (is_ctrl(c)) { // Handle ctrl character + handle_ctrl(c); + } else if (escseq.parse_in_progress()) { // ESC sequence in progress? + handle_escseq(c); + } else { // Handle printable char.. + putchar(c, cursor_row(), cursor_col()); + cursor_right(1, do_scroll); + return; + } +} + +// Clear the Partial UTF-8 Buffer cache +void Fl_Terminal::utf8_cache_clear(void) { + pub_.clear(); +} + +// Flush the Partial UTF-8 Buffer cache, and clear +void Fl_Terminal::utf8_cache_flush(void) { + if (pub_.buflen() > 0) print_char(pub_.buf(), pub_.buflen()); + pub_.clear(); +} + +/** + Append NULL terminated UTF-8 string to terminal. + + - If buf is NULL, UTF-8 cache buffer is cleared + - If optional \p len isn't specified or is -1, strlen(text) is used. + - If \p len is 0 or <-1, no changes are made + - Handles UTF-8 chars split across calls (e.g. block writes from pipes, etc) + - Redraws are triggered automatically, depending on redraw_style() +*/ +void Fl_Terminal::append_utf8(const char *buf, int len/*=-1*/) { + int mod = 0; // assume no modifications + if (!buf) { utf8_cache_clear(); return; } // clear cache, done + if (len == -1) len = int(strlen(buf)); // len optional + if (len<=0) return; // bad len? early exit + + // Handle any partial UTF-8 from last write + // Try to parse up rest of incomplete buffered char from end + // of last block, and flush it to terminal. + // + if (pub_.buflen() > 0) { // partial UTF-8 to deal with? + while (len>0 && pub_.is_continuation(*buf)) { // buffer 'continuation' chars + if (pub_.append(buf, 1) == false) // append byte to partial UTF-8 buffer + { mod |= handle_unknown_char(); break; } // overrun? break loop + else { buf++; len--; } // shrink our buffer + } + if (pub_.is_complete()) utf8_cache_flush(); // complete UTF-8 captured? flush to tty + if (len <= 0) { // check len again, we may have run out + if (mod) display_modified(); + return; + } + } + + // For sure buf is now pointing at a valid char, so walk to end of buffer + int clen; // char length + const char *p = buf; // ptr to walk buffer + while (len>0) { + clen = fl_utf8len(*p); // how many bytes long is this char? + if (clen == -1) { // not expecting bad UTF-8 here + mod |= handle_unknown_char(); + p += 1; + len -= 1; + } else { + if (len && clen>len) { // char longer than buffer? + if (pub_.append(p, len) == false) { // buffer it + mod |= handle_unknown_char(); + utf8_cache_clear(); + } + break; + } + print_char(p, clen); // write complete UTF-8 char to terminal + p += clen; // advance to next char + len -= clen; // adjust len + mod |= 1; + } + } + if (mod) display_modified(); +} + +/** + Append NULL terminated ASCII string to terminal, + slightly more efficient than append_utf8(). + + - If \p s is NULL, behavior is to do nothing + - Redraws are triggered automatically, depending on redraw_style() +*/ +void Fl_Terminal::append_ascii(const char *s) { + if (!s) return; + while ( *s ) print_char(*s++); // handles display_modified() + display_modified(); +} + +/** + Appends string \p s to the terminal at the current cursor position + using the current text color/attributes. + + If \p s is NULL, the UTF-8 character cache is cleared, which is + recommended before starting a block reading loop, and again after the + block loop has completed. + + If \p len is not specified, it's assumed \p s is a NULL terminated + string. If \p len IS specified, it can be used for writing strings + that aren't NULL terminated, such as block reads on a pipe, network, + or other block oriented data source. + + Redraws of the terminal widget are by default handled automatically, + but can be changed with redraw_rate() and redraw_style(). + + Block I/O + + When reading block oriented sources (such as pipes), append() will + handle partial UTF-8 chars straddling the block boundaries. It does + this using an internal byte cache, which should be cleared before + and after block I/O loops by calling append(NULL) as shown + in the example below, to prevent the possibilities of partial UTF-8 + characters left behind by an interrupted or incomplete block loop. + + \par + \code + // Example block reading a command pipe in Unix + + // Run command and read as a pipe + FILE *fp = popen("ls -la", "r"); + if (!fp) { ..error_handling.. } + + // Enable non-blocking I/O + int fd = fileno(fp); + fcntl(fd, F_SETFL, O_NONBLOCK); + + // Clear UTF-8 character cache before starting block loop + G_tty->append(NULL); // prevents leftover partial UTF-8 bytes + + // Block read loop + while (1) { + Fl::wait(0.05); // give fltk .05 secs of cpu to manage UI + ssize_t bytes = read(fd, s, sizeof(s)); // read block from pipe + if (bytes == -1 && errno == EAGAIN) continue; // no data yet? continue + if (bytes > 0) G_tty->append(s); // append output to terminal + else break; // end of pipe? + } + + // Flush cache again after block loop completes + G_tty->append(NULL); + + // Close pipe, done + pclose(fp); + + \endcode + + \note + - String can contain ASCII or UTF-8 chars + - \p len is optional; if unspecified, expects \p s to be a NULL terminated string + - Handles partial UTF-8 chars split between calls (e.g. block oriented writes) + - If \p s is NULL, this clears the "partial UTF-8" character cache + - Redraws are managed automatically by default; see redraw_style() +*/ +void Fl_Terminal::append(const char *s, int len/*=-1*/) { + append_utf8(s, len); +} + +/** + Handle an unknown char by either emitting an error symbol to the tty, or do nothing, + depending on the user configurable value of show_unknown(). + Returns 1 if tty modified, 0 if not. + \see show_unknown() +*/ +int Fl_Terminal::handle_unknown_char(void) { + const char *unknown = "¿"; + if (show_unknown_) { + escseq.reset(); // disable any pending esc seq to prevent eating unknown char + print_char(unknown); + return 1; + } + return 0; +} + +// Handle user interactive scrolling +void Fl_Terminal::scrollbar_cb(Fl_Widget*, void* userdata) { + Fl_Terminal *o = (Fl_Terminal*)userdata; + o->redraw(); +} + +// Handle mouse selection autoscrolling +void Fl_Terminal::autoscroll_timer_cb2(void) { + // Move scrollbar + // NOTE: vscroll is inverted; 0=tab at bot, so minimum() is really max + // + int amt = autoscroll_amt_; // (amt<0):above top, (amt>0):below bottom + int val = vscroll_->value(); + int max = int(vscroll_->minimum()+.5); // NOTE: minimum() is really max + val = (amt<0) ? (val+clamp((-amt/10),1,5)) : // above top edge? + (amt>0) ? (val-clamp((+amt/10),1,5)) : 0; // below bot edge? + val = clamp(val,0,max); // limit val to scroll's range + int diff = ABS(val - vscroll_->value()); // how far scroll tab moved up/dn + // Move scrollbar + vscroll_->value(val); + // Extend selection + if (diff) { // >0 if up or down + int srow = select_.srow(), scol = select_.scol(); + int erow = select_.erow(), ecol = select_.ecol(); + int ltcol = 0, rtcol = ring_cols() - 1; + if (amt<0) { erow -= diff; ecol = ltcol; } // above top? use erow: reverse-selecting + if (amt>0) { erow += diff; ecol = rtcol; } // below bot? use erow: forward-selecting + select_.select(srow, scol, erow, ecol); + } + // Restart timeout + Fl::repeat_timeout(.1, autoscroll_timer_cb, this); + redraw(); +} + +// Handle mouse selection autoscrolling +void Fl_Terminal::autoscroll_timer_cb(void *udata) { + Fl_Terminal *tty = (Fl_Terminal*)udata; + tty->autoscroll_timer_cb2(); +} + +// Handle triggering rate limited redraw() updates +// When data comes in quickly, append() sets the redraw_modified_ flag +// so our timer can trigger the redraw()s at a controlled rate. +// +void Fl_Terminal::redraw_timer_cb2(void) { + //DRAWDEBUG ::printf("--- UPDATE TICK %.02f\n", redraw_rate_); fflush(stdout); + if (redraw_modified_) { + redraw(); // Timer triggered redraw + redraw_modified_ = false; // acknowledge modified flag + Fl::repeat_timeout(redraw_rate_, redraw_timer_cb, this); // restart timer + } else { + // Timer went off and nothing to redraw? disable + Fl::remove_timeout(redraw_timer_cb, this); + redraw_timer_ = false; + } +} + +void Fl_Terminal::redraw_timer_cb(void *udata) { + Fl_Terminal *tty = (Fl_Terminal*)udata; + tty->redraw_timer_cb2(); +} + +/** + The constructor for Fl_Terminal. + + This creates an empty terminal with defaults: + - white on black text; see textfgcolor(Fl_Color), textbgcolor(Fl_Color) + - rows/cols based on the \p W and \p H values, see display_rows(), display_columns() + - scrollback history of 100 lines, see history_rows() + - redraw_style() set to RATE_LIMITED, redraw_rate() set to 0.10 seconds + + Note: While Fl_Terminal derives from Fl_Group, it's not intended for user code + to use it as a parent for other widgets, so end() is called. + + \param[in] X,Y,W,H position and size. + \param[in] L label string (optional), may be NULL. +*/ +Fl_Terminal::Fl_Terminal(int X,int Y,int W,int H,const char*L) : Fl_Group(X,Y,W,H,L) { + // scrollbar_size must be set before scrn_ + scrollbar_size_ = 0; // 0 uses Fl::scrollbar_size() + update_screen_xywh(); + // Tabs + tabstops_ = 0; + tabstops_size_ = 0; + // Init ringbuffer. Also creates default tabstops + create_ring(h_to_row(scrn_.h()), // desired row + w_to_col(scrn_.w()), // desired col + 100); // history size: use 100 for production release + // Misc + redraw_style_ = RATE_LIMITED; // NO_REDRAW, RATE_LIMITED, PER_WRITE + redraw_rate_ = 0.10f; // maximum rate in seconds (1/10=10fps) + redraw_modified_ = false; // display 'modified' flag + redraw_timer_ = false; + autoscroll_dir_ = 0; + autoscroll_amt_ = 0; + + // Create scrollbar + // Final position/size will be set by update_screen() below + // + vscroll_ = new Fl_Scrollbar(x(), y(), scrollbar_size_, h()); + vscroll_->type(FL_VERTICAL); + vscroll_->linesize(1); + vscroll_->slider_size(1); + vscroll_->range(0.0, 0.0); + vscroll_->value(0); + vscroll_->callback(scrollbar_cb, (void*)this); + resizable(0); + Fl_Group::box(FL_DOWN_FRAME); + color(0x0); // black bg by default + update_screen(true); // update internal vars after setting screen size/font + clear_screen_home(); // clear screen, home cursor + clear_history(); // clear history buffer + show_unknown_ = false; // default "off" + ansi_ = true; // default "on" + // End group + end(); +} + +/** + The destructor for Fl_Terminal. + Destroys the terminal display, scroll history, and associated widgets. +*/ +Fl_Terminal::~Fl_Terminal(void) { + // Note: RingBuffer class handles destroying itself + if (tabstops_) + { free(tabstops_); tabstops_ = 0; } + if (autoscroll_dir_) + { Fl::remove_timeout(autoscroll_timer_cb, this); autoscroll_dir_ = 0; } + if (redraw_timer_) + { Fl::remove_timeout(redraw_timer_cb, this); redraw_timer_ = false; } +} + +/** + Returns the scrollbar's actual size; actual width for vertical scrollbars, + actual height for horizontal scrollbars. +*/ +int Fl_Terminal::scrollbar_actual_size(void) const { + return scrollbar_size_ ? scrollbar_size_ : Fl::scrollbar_size(); +} + +/** + Get the current size of the scrollbar's trough, in pixels. + + If this value is zero (default), this widget will use the + Fl::scrollbar_size() value as the scrollbar's width. + + \returns Scrollbar size in pixels, or 0 if the global Fl::scrollbar_size() is being used. + \see Fl::scrollbar_size(int) +*/ +int Fl_Terminal::scrollbar_size(void) const { + return scrollbar_size_; +} + +/** + Set the width of the scrollbar's trough to \p val, in pixels. + + Only use this method if you need to override the global scrollbar size. + + Setting \p val to the special value 0 causes the widget to + track the global Fl::scrollbar_size(). + + \see Fl::scrollbar_size() +*/ +void Fl_Terminal::scrollbar_size(int val) { + scrollbar_size_ = val; + update_scrollbar(); +} + +//////////////////////////// +////// SCREEN DRAWING ////// +//////////////////////////// + +// Draw the background for the specified ring_chars[] row starting at FLTK coords X,Y +// Note we may be called to draw display, or even history if we're scrolled back. +// If there's any change in bg color, we draw the filled rects here. +// +void Fl_Terminal::draw_row_bg(int grow, int X, int Y) const { + int bg_h = current_style_.fontheight(); + int bg_y = Y - current_style_.fontheight() + current_style_.fontdescent(); + Fl_Color bg_col; + int pwidth = 9; + const Utf8Char *u8c = u8c_ring_row(grow); // start of spec'd row + uchar lastattr = u8c->attrib(); + for (int gcol=0; gcolattrib() != lastattr) { + u8c->fl_font_set(current_style_); // pwidth_int() needs fl_font set + lastattr = u8c->attrib(); + } + pwidth = u8c->pwidth_int(); + bg_col = is_inside_selection(grow, gcol) // text in mouse select? + ? select_.selectionbgcolor() // ..use select bg color + : (u8c->attrib() & Fl_Terminal::INVERSE) // Inverse mode? + ? u8c->attr_fg_color(this) // ..use fg color for bg + : u8c->attr_bg_color(this); // ..use bg color for bg + // Draw only if color != 0x0 ('show through' color) or widget's own color(). + if (bg_col != 0xffffffff && bg_col != color()) { + fl_color(bg_col); + fl_rectf(X, bg_y, pwidth, bg_h); + } + X += pwidth; // advance X to next char + } +} + +// Draw specified global row, which is the row in ring_chars[]. +// The global row includes history + display buffers. +// +void Fl_Terminal::draw_row(int grow, int Y) const { + // Draw background color spans, if any + int X = scrn_.x(); + draw_row_bg(grow, X, Y); + + // Draw forground text + int scrollval = vscroll_->value(); + int disp_top = (disp_srow() - scrollval); // top row we need to view + int drow = grow - disp_top; // disp row + bool inside_display = is_disp_ring_row(grow); // row inside 'display'? + int strikeout_y = Y - (current_style_.fontheight() / 3); + int underline_y = Y; + const Utf8Char *u8c = u8c_ring_row(grow); + uchar lastattr = -1; + bool is_cursor; + Fl_Color fg; + for (int gcol=0; gcolattrib() != lastattr) { + u8c->fl_font_set(current_style_); // pwidth_int() needs fl_font set + lastattr = u8c->attrib(); + } + int pwidth = u8c->pwidth_int(); + // DRAW CURSOR BLOCK - TODO: support other cursor types? + if (is_cursor) { + int cx = X; + int cy = Y - cursor_.h() + current_style_.fontdescent(); + int cw = pwidth; + int ch = cursor_.h(); + fl_color(cursorbgcolor()); + if (Fl::focus() == this) fl_rectf(cx, cy, cw, ch); + else fl_rect(cx, cy, cw, ch); + } + // DRAW TEXT + // 1) Color for text + if (is_cursor) fg = cursorfgcolor(); // color for text under cursor + else fg = is_inside_selection(grow, gcol) // text in mouse selection? + ? select_.selectionfgcolor() // ..use selection FG color + : (u8c->attrib() & Fl_Terminal::INVERSE) // Inverse attrib? + ? u8c->attr_bg_color(this) // ..use char's bg color for fg + : u8c->attr_fg_color(this); // ..use char's fg color for fg + fl_color(fg); + // 2) Font for text - already set by u8c->fl_font_set() in the above + if (is_cursor) { + fl_font(fl_font()|FL_BOLD, fl_size()); // force text under cursor BOLD + lastattr = -1; // (ensure font reset on next iter) + } + // 3) Draw text for UTF-8 char. No need to draw spaces + if (!u8c->is_char(' ')) fl_draw(u8c->text_utf8(), u8c->length(), X, Y); + // 4) Strike or underline? + if (u8c->attrib() & Fl_Terminal::UNDERLINE) fl_line(X, underline_y, X+pwidth, underline_y); + if (u8c->attrib() & Fl_Terminal::STRIKEOUT) fl_line(X, strikeout_y, X+pwidth, strikeout_y); + // Move to next char pixel position + X += pwidth; + } +} + +// Draw the part of the buffer we're scrolled to at FLTK position Y. +// +// This could be anywhere in the buffer, and not just the 'active diplay', +// depending on what the scrollbar is set to. +// +// Handles attributes, colors, text selections, cursor. +// +void Fl_Terminal::draw_buff(int Y) const { + int srow = disp_srow() - vscroll_->value(); + int erow = srow + disp_rows(); + const int rowheight = current_style_.fontheight(); + for (int grow=srow; (grow