summaryrefslogtreecommitdiff
path: root/FL
diff options
context:
space:
mode:
authorerco77 <erco@seriss.com>2023-11-14 07:01:52 -0800
committerGitHub <noreply@github.com>2023-11-14 07:01:52 -0800
commit6842a43a3170c6f7a852186d5688baebdac16e2c (patch)
tree04493580bf8fc798858720c7ab7ffecedeb9ee3e /FL
parent83f6336f3b024f4a7c55a7499d036f03d946c1b9 (diff)
Fl_Terminal widget (#800)
Pull Fl_Terminal widget from Greg's fork
Diffstat (limited to 'FL')
-rw-r--r--FL/Fl_Terminal.H1013
1 files changed, 1013 insertions, 0 deletions
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 <FL/Fl.H>
+#include <FL/Fl_Window.H>
+#include <FL/Fl_Group.H>
+#include <FL/Fl_Scrollbar.H>
+#include <FL/Fl_Rect.H>
+
+/** \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: <TT>\\033[3m</TT>
+ - Bold - brighter/thicker text: <TT>\\033[1m</TT>
+ - Dim - lower brightness text: <TT>\\033[2m</TT>
+ - Underline - text that is underlined: <TT>\\033[4m</TT>
+ - Strikeout - text that has a line through the text: <TT>\\033[9m</TT>
+ - Inverse - text whose background and foreground colors are swapped: <TT>\\033[7m</TT>
+ - Normal - normal text: <TT>\\033[0m</TT>
+ \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, ///< <EM>(reserved for internal future use)</EM>
+ INVERSE = 0x20, ///< inverse text; fg/bg color are swapped
+ _RESERVED_2 = 0x40, ///< <EM>(reserved for internal future use)</EM>
+ 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;<r>;<g>;<b>m)
+ Fl_Color bgcolor_; // fltk bg color (supports 8color or 24bit color set w/ESC[47;<r>;<g>;<b>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; col<cols; col++, u8c++ ) {
+ //DEBUG ::fprintf(fp, "%.*s", u8c->length(), 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