Fl_Terminal widget (#800)

Pull Fl_Terminal widget from Greg's fork
This commit is contained in:
erco77 2023-11-14 07:01:52 -08:00 committed by GitHub
parent 83f6336f3b
commit 6842a43a31
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
35 changed files with 7818 additions and 217 deletions

1013
FL/Fl_Terminal.H Normal file

File diff suppressed because it is too large Load Diff

View File

@ -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 \

View File

@ -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) \

Binary file not shown.

After

Width:  |  Height:  |  Size: 62 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 58 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 47 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 37 KiB

View File

@ -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 ---
│ ------------------------
│ <ESC>c - (RIS) Reset term to Initial State
│ <ESC>D - (IND) Index: move cursor down a line, scroll if at bottom
│ <ESC>E - (NEL) Next Line: basically do a crlf, scroll if at bottom
│ <ESC>H - (HTS) Horizontal Tab Set: set a tabstop
│ <ESC>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<disp_rows(); row++ ) { // walk rows: disp_rows = 25
Utf8Char *u8c = u8c_disp_row(row); // get first char in display 'row'
for ( int col=0; col<disp_cols(); col++ ) { // walk cols: disp_cols = 80
u8c[col].do_something(); // work with the char at row/col
}
}
So to recap, the concepts here are:
- The ring buffer itself, a linear array that is conceptually
split into a 2 dimensional array of rows and columns whose
height and width are:
ring_rows -- how many rows in the entire ring buffer
ring_cols -- how many columns in the ring buffer
nchars -- total chars in ring, e.g. (ring_rows * ring_cols)
- The "history" within the ring. For simplicity this is thought of
as starting relative to the top of the ring buffer, occupying
ring buffer rows:
0 .. hist_rows()-1
- The "display", or "disp", within the ring, just after the "history".
It occupies the ring buffer rows:
hist_rows() .. hist_rows()+disp_rows()-1
..or similarly:
(hist_rows)..(ring_rows-1)
The following convenience methods provide access to the
start and end indexes within the ring buffer for each entity:
// Entire ring
ring_srow() -- start row index of the ring buffer (always 0)
ring_erow() -- end row index of the ring buffer
// "history" part of ring
hist_srow() -- start row index of the screen history
hist_erow() -- end row index of the screen history
// "display" part of ring
disp_srow() -- start row index of the display
disp_erow() -- end row index of the display
The values returned by these are as described above.
For the hist_xxx() and disp_xxx() methods the 'offset' included into
the forumula. (For this reason hist_srow() won't always be zero
the way ring_srow() is, due to the 'offset')
The values returned by these methods can all be passed to the
u8c_ring_row() function to access the actual character buffer's contents.
- An "offset" used to move the "history" and "display" around within
the ring buffer to implement the "text scrolling" concept. The offset
is applied when new characters are added to the buffer, and during
drawing to find where the display actually is within the ring.
- The "scrollbar", which only is used when redrawing the screen the user sees,
and is simply an additional offset to all the above, where a scrollbar
value of zero (the scrollbar tab at the bottom) shows the display rows,
and as the scrollbar values increase as the user moves the scrollbar
tab upwards, +1 per line, this is subtracted from the normal starting
index to let the user work their way backwards into the scrollback history.
Again, negative numbers wrap around within the ring buffer automatically.
The ring buffer allows new content to simply be appended to the ring buffer,
and the index# for the start of the display and start of scrollback history are
simply incremented. So the next time the display is "drawn", it starts at
a different position in the ring.
This makes scrolling content at high speed trivial, without memory moves.
It also makes the concept of "scrolling" with the scrollbar simple as well,
simply being an extra index offset applied during drawing.
Mouse Selection
---------------
Dragging the mouse across the screen should highlight the text, allowing the user
to extend the selection either beyond or before the point started. Extending the
drag to the top of the screen should automatically 'scroll up' to select more
lines in the scrollback history, or below the bottom to do the opposite.
The mouse selection is implemented as a class to keep track of the start/end
row/col positions of the selection, and other details such as a flag indicating
if a selection has been made, what color the fg/bg text should appear when
text is selected, and methods that allow setting and extending the selection,
clearing the selection, and "scrolling" the selection, to ensure the row/col
indexes adjust correctly to track when the screen or scrollbar is scrolled.
Redraw Timer
------------
Knowing when to redraw is tricky with a terminal, because sometimes high volumes
of input will come in asynchronously, so in that case we need to determine when
to redraw the screen to show the new content; too quickly will cause the screen
to spend more time redrawing itself, preventing new input from being added. Too
slowly, the user won't see new information appear in a timely manner.
To solve this, a rate timer is used to prevent too many redraws:
- When new data comes in, a 1/10 sec timer is started and a modify flag is set.
- redraw() is NOT called yet, allowing more data to continue to arrive quickly
- When the 1/10th second timer fires, the callback checks the modify flag:
- if set, calls redraw(), resets the modify to 0, and calls
Fl::repeat_timeout() to repeat the callback in another 1/10th sec.
- if clear, no new data came in, so DISABLE the timer, done.
In this way, redraws don't happen more than 10x per second, and redraw() is called
only when there's new content to see.
The redraw rate can be set by the user application using the Fl_Terminal::redraw_rate(),
0.10 being the default.
Some terminal operations necessarily call redraw() directly, such as interactive mouse
selection, or during user scrolling the terminal's scrollbar, where it's important there's
no delay in what the user sees while interacting directly with the widget.
*/

View File

@ -1,5 +1,5 @@
//
// Simple Example app using Fl_Simple_Terminal. - erco 10/12/2017
// Simple Example app using Fl_Terminal. - erco 10/12/2017
//
// Copyright 2017 Greg Ercolano.
// Copyright 1998-2016 by Bill Spitzak and others.
@ -15,17 +15,17 @@
// https://www.fltk.org/bugs.php
//
#include <time.h> //START
#include <time.h>
#include <FL/Fl_Double_Window.H>
#include <FL/Fl_Box.H>
#include <FL/Fl_Simple_Terminal.H>
#include <FL/Fl_Terminal.H>
#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
}

View File

@ -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

3508
src/Fl_Terminal.cxx Normal file

File diff suppressed because it is too large Load Diff

View File

@ -94,6 +94,7 @@ 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 \

586
src/README-Fl_Terminal.txt Normal file
View File

@ -0,0 +1,586 @@
// vim: autoindent tabstop=8 shiftwidth=4 expandtab softtabstop=4
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 effectively,
allowing per-character colors and attributes like underline,
strikeout, background, etc.
- An efficient screen buffer to handle the "scrollback history"
and "screen display" concepts; the "history" being a scrollback
history of text that scrolls up and off screen from the "display",
and the "display" being where the action is: the cursor can be
moved around and text scrolled up or down.
- How the vertical scrollbar should provide the user with a way to
scroll back into the scrollback history to allow the user to scroll back
to view the "scrollback history", without stopping the "screen display"
from operating
- How to manage mouse selection for copy/paste
- Escape code management to implement VT100 style / ANSI escape codes.
┌─────────────────────────────────────────┬──────────────────────────────┐
│ 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_cols = 0; ring_rows = 0; │
│ hist_cols = 0; ring_cols = 0; │
│ disp_cols = 0; ring_cols = 0; │
│ └─┬┘ └─┬┘ │
│ └────┴─── 4 characters │
│ │
└────────────────────────────────────────────────────────────────────────┘
So the of these concepts were able to fit into C++ class concepts well.
Those classes being:
Utf8Char
========
Each character on the screen is a "Utf8Char" which can manage
the utf8 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", and some index numbers
to keep track of how many rows are in the screen's history and
display, named "hist_rows" and "disp_rows".
The memory layout of the Utf8Char array is:
___________________ _ _
| | ʌ
| | |
| | |
| H i s t o r y | | hist_rows
| | |
| | |
|___________________| _v_
| | ʌ
| | |
| D i s p l a y | | disp_rows
| | |
|___________________| _v_
|<----------------->|
ring_cols
So it's basically a single continguous array of Utf8Char instances
where any character can 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.
Methods are used to give access the characters in the buffer.
A key concept is 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, 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 │
│ │ │ │
└───────────────────┘ └───────────────────┘
Offset is 0: 4 Offset now 4:
┌───────────────────┐ ──┐ ┌───────────────────┐
│ │ │ │ │
│ │ │ │ D i s p l a y │
│ │ │ │ │
│ H i s t o r y │ └─> ├───────────────────┤
│ │ │ │
│ │ 4 │ │
├───────────────────┤ ──┐ │ H i s t o r y │
│ │ │ │ │
│ │ │ │ │
│ D i s p l a y │ │ │ │
│ │ └─> ├───────────────────┤
│ │ │ D i s p l a y │
└───────────────────┘ └───────────────────┘
The effect of applying an offset trivially implements "text scrolling",
so that no screen memory has to physically moved around, simply changing
the single integer "offset" is enough. The text remains where it was, and
the offset is simply incremented to scroll up. This also automatically
makes it appear the top line in the display is 'scrolled up' into the
last line of the scrollback history.
If the offset exceeds the size of the ring buffer, it is simply wrapped
back to the beginning of the buffer with a modulo: offset =% ring_rows;
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.
Offset now 4:
┌───────────────────┐
2 │ │
3 │ D i s p l a y │
4 │ │ <- act_disp_row(4)
├───────────────────┤
│ │
│ │
│ H i s t o r y │
│ │
│ │
│ │
├───────────────────┤
0 │ D i s p l a y │
1 └───────────────────┘ <- ring_rows
2 : :
3 : :
disp_row(5) -> 4 :...................:
Here the "disp_row" is the desired offset into the display, but we
need the actual index into the ring from the top, since that's the
physical array.
So some simple math calculates the row position based on the "offset",
and the "hist" vs "disp" concepts:
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 guaratnees 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, e.g.
for ( int row=0; row<disp_rows(); row++ ) { // walk rows: disp_rows = 25
Utf8Char *u8c = u8c_disp_row(row); // get first char in display 'row'
for ( int col=0; col<disp_cols(); col++ ) { // walk cols: disp_cols = 80
u8c[col].do_something(); // work with the character at row/col
}
}
So to recap, the concepts here are:
- The ring buffer itself, a linear array that is conceptually
split into a 2 dimensional array of rows and columns whose
height and width are:
ring_rows -- how many rows in the entire ring buffer
ring_cols -- how many columns in the ring buffer
nchars -- total chars in ring, e.g. (ring_rows * ring_cols)
- The "history" within the ring. For simplicity this is thought of
as starting relative to the top of the ring buffer, occupying
ring buffer rows:
0..(hist_rows-1)
- The "display", or "disp", within the ring, just after the "history".
It occupies the ring buffer rows:
(hist_rows)..(hist_rows+disp_rows-1)
..or similarly:
(hist_rows)..(ring_rows-1)
- An "offset" used to move the "history" and "display" around within
the ring buffer to implement the "text scrolling" concept. The offset
is applied when new characters are added to the buffer, and during
drawing to find where the display actually is within the ring.
- A "scrollbar", which only is used when redrawing the screen the user sees,
and is simply an additional offset to all the above, where a scrollback
value of zero (the scrollbar tab at the bottom) shows the display rows,
and the values increase as the user moves the scrolltab upwards, 1 per line,
which is subtracted from the normal starting index to let the user work their
way backwards into the scrollback history.
The ring buffer allows new content to simply be appended to the ring buffer,
and the index# for the start of the display and start of scrollback history are
simply incremented. So the next time the display is "drawn", it starts at
a different position in the ring.
This makes scrolling content at high speed trivial, without memory moves.
It also makes the concept of "scrolling" with the scrollbar simple as well,
simply being an extra index offset applied during drawing.
If the display is enlarged vertically, that's easy too; the display
area is simply defined as being more rows, the history as less rows,
the history use decreased (since what was in the history before is now
being moved into the display), and all the math adjusts accordingly.
Mouse Selection
===============
Dragging the mouse across the screen should highlight the text, allowing the user
to extend the selection either beyond or before the point started. Extending the
drag to the top of the screen should automatically 'scroll up' to select more
lines in the scrollback history, or below the bottom to do the opposite.
The mouse selection is implemented as a class to keep track of the start/end
row/col positions of the selection, and other details such as a flag indicating
if a selection has been made, what color the fg/bg text should appear when
text is selected, and methods that allow setting and extending the selection,
clearing the selection, and "scrolling" the selection, to ensure the row/col
indexes adjust correctly to track when the screen or scrollbar is scrolled.
Redraw Timer
============
Knowing when to redraw is tricky with a terminal, because sometimes high volumes
of input will come in asynchronously, so in that case we need to determine when
to redraw the screen to show the new content; too quickly will cause the screen
to spend more time redrawing itself, preventing new input from being added. Too
slowly, the user won't see new information appear in a timely manner.
To solve this, a rate timer is used to prevent too many redraws:
- When new data comes in, a 1/10 sec timer is started and a modify flag is set.
redraw() is NOT called at this time, allowing new data to continue to arrive
quickly. Once the modify flag is set, nothing changes from there.
- When the 1/10th second timer fires, the callback checks the modify flag:
- if set, calls redraw(), resets the modify to 0, and calls
Fl::repeat_timeout() to repeat the callback in another 1/10th sec.
- if clear, no new data came in, so DISABLE the timer, done.
In this way, redraws don't happen more than 10x per second, and redraw() is called
only when there's new content to see.
The redraw rate can be set by the user application using the Fl_Terminal::redraw_rate(),
0.10 being the default.
Some terminal operations necessarily call redraw() directly, such as interactive mouse
selection, or during user scrolling the terminal's scrollbar, where it's important there's
no delay in what the user sees while interacting directly with the widget.
OLD OLD OLD OLD OLD OLD OLD OLD OLD OLD OLD
OLD OLD OLD OLD OLD OLD OLD OLD OLD OLD OLD
OLD OLD OLD OLD OLD OLD OLD OLD OLD OLD OLD
OLD OLD OLD OLD OLD OLD OLD OLD OLD OLD OLD
RING BUFFER DESCRIPTION
=======================
The history and display are defined by row indexes into this buffer
which are adjusted whenever the text display is 'scrolled' (e.g. by crlfs)
The scrollbar is a secondary offset on top of that applied during drawing.
the display.
Here's what the variables managing the split ring buffer look like:
RING BUFFER
─ ring_chars[] ─┬─>┌───────────────┐
ʌ [hist_srow] ──┘ │ │
┊ │ │
┊ │ H i s t o r y ┊
hist_rows_┊ │ │
┊ │ │
┊ │ │
┊ │ │
v [hist_erow] ───>│ │
─ [disp_srow] ───>├───────────────┤
ʌ │ │
┊ │ │
disp_rows_┊ │ D i s p l a y │
┊ │ │
┊ │ │
v [ring_erow] ───>│ +│<── ring_bot_
─ └───────────────┘ (last valid ptr address in ring)
│<──────┬──────>│
ring_cols
hist_cols
disp_cols
The concept here is the single ring buffer is split into two parts that can differ
in height (rows) but not in width (cols). For instance, typically the history is
many times larger than the display; a typical old school display might be 80x25,
but the history might be 2000 lines.
ring_row() handles the fact that 'row' might run off the end of the buffer,
depending on where hist_srow starts. For instance, if the display has scrolled
a few lines, the ring buffer arrangement might look like:
RING BUFFER
ring_chars[] ───>┌───────────────┐
│ D i s p l a y │
[disp_erow] ───>│ │
├───────────────┤
[hist_srow] ───>│ │
│ │
│ H i s t o r y │
│ │
│ │
│ │
[hist_erow] ───>│ │
├───────────────┤
[disp_srow] ───>│ │
│ D i s p l a y │
│ │
[ring_erow] ───>│ +│<── ring_bot_
└───────────────┘ (last valid ptr address in ring)
Note how the display 'wraps around', straddling the end of the ring buffer.
So trivially walking ring_chars[] from 'disp_srow' for 'disp_rows' would run
off the end of memory for the ring buffer. Example:
// BAD!
for ( int row=disp_srow; row<disp_rows; row++ ) {
for ( int col=0; col<disp_cols; col++ ) {
ring_chars[row*disp_cols+col]->do_something(); // BAD! can run off end of array
}
}
The function u8c_row() can access the Utf8Char* of each row more safely,
ensuring that even if the 'row' index runs off the end of the array,
u8c_row() handles wrapping it for you. So the safe way to walk the chars
of the display can be done this way:
// GOOD!
for ( int row=disp_srow; row<disp_rows; row++ ) {
Utf8Char *u8c = u8c_row(row); // safe: returns utf8 char for start of each row
for ( int col=0; col<disp_cols; col++ ) { // walk the columns safely
(u8c++)->do_something(); // get/set the utf8 char
}
}
Walking the history would be the same, just replace disp_xxxx with hist_xxxx.
One can also use ring_row_normalize() to return an index# that can be directly
used with ring_chars[], the value kept in range of the buffer.
RING BUFFER "SCROLLING"
=======================
A ring buffer is used to greatly simplify the act of 'scrolling', which happens a lot
when large amounts of data come in, each CRLF triggering a "scroll" that moves the top
line up into the history buffer. The history buffer can be quite large (1000's of lines),
so it would suck if, on each line scroll, thousands of rows of Utf8Chars had to be
physically moved in memory.
Much easier to just adjust the srow/erow pointers, which simply affect how drawing
is done, and where the display area is. This trivially handles scrolling by just
adjusting some integers by 1.
-- -- HOW SCROLLING UP ONE LINE IS DONE -- --
ring_chars[] ─┬─>┌───────────────┐ ─┐ ring_chars[] ──>┌─────────────────┐
[hist_srow] ──┘ │ │ │ │x x x x x x x x x│ <-- blanks
│ │ └─> [hist_srow] ──>├─────────────────┤
│ H i s t │ │ │
│ │ │ │
│ │ │ H i s t │
│ │ │ │
│ │ │ │
[hist_erow] ───>│ │ │ │
[disp_srow] ───>├Line 1─────────┤ ─┐ [hist_erow] ──>│Line 1 │
│Line 2 │ └─> [disp_srow] ──>├Line 2───────────┤
│Line 3 │ │Line 3 │
│ │ │ │
│ D i s p │ │ D i s p │
│ │ │ │
[disp_erow][ring_erow] ───>│Line 24 +│ [ring_erow] ──>│Line 24 +│
└───────────────┘ └─────────────────┘
In the above, Line 1 has effectively "moved" into history because the disp_s/erow
and hist_s/erow variables have just been incremented.
During resize_display(), we need to preserve the display and history as much as possible
when the ring buffer is enlarged/shrank; the hist_rows size should be maintained, and only
display section changes size based on the FLTK window size.
===================== OLD ====================== OLD ====================== OLD ======================
Conventions used for the internals
==================================
This is a large widget, and these are some breadcrumbs for anyone
working on the internals of this class.
> There is one utf8 char buffer, buff_chars[], the top part is the 'history buffer'
(which the user can scroll back to see), and the 'display buffer' which is the
'active display'.
> glob or global - refers to global buffer buff_chars[]
> disp or display - refers to display buffer disp_chars[]
> Abbreviations glob/disp/buff/hist used because 4 chars line up nicely
> row/col variable names use a 'g' or 'd' prefix to convey 'g'lobal or 'd'isplay.
> The 'Cursor' class uses row/col for the display (disp_chars[]) because the
cursor is only ever inside the display.
> The 'Selection' class uses row/col for the global buffer (buff_chars[])
because it can be in the 'history' or the 'display'
> These concepts talk about the same thing:
> global buffer == buff_chars[] == "history buffer" == hist == grow/gcol
> display == disp_chars[] == "active display" == drow/dcol
> There is no hist_chars[] because it's just the top half of buff_chars[]
> There is no hist_height_ because it's the same as hist_max_
> There is no hist_width_ because it's the same as buff_width_.
Fl_Terminal's Class Hierarchy
=============================
class Fl_Terminal -- Derived from Fl_Group (to parent scrollbars, popup menus, etc)
We mainly use the group's background to draw over in draw().
Within the terminal classes are the following private/protected classes
that help with bookkeeping and operation of the terminal class:
class Margin -- Handles the margins around the terminal drawing area
class CharStyle -- The styling for the characters: single byte color + attribute (bold/inverse/etc)
class Cursor -- The attributes of the cursor -- position, color, etc, and some simple movement logic
class Utf8Char -- Visible screen buffer is an array of these, one per character
class RingBuffer -- The ring buffer of Utf8Char's, with the "history" and "display" concept.
class EscapeSeq -- A class to handle parsing Esc sequences, and keeping state info between chars
Single chars go in, and when a complete esc sequence is parsed, the caller
can find out all the integer values and command code easily to figure out
what op to do.
OVERALL DESIGN:
To handle unicode, the terminal's visible display area is a linear array of pointers to
instances of the 'Utf8Char' class, one instance per character. The arrangement of the array
is much like the IBM PC's video memory, but instead of Char/Attrib byte pairs, the Utf8Char
class handles the more complex per-character data and colors/attributes.
The cursor x,y value can be quickly converted to an index into this buffer.
Strings are printed into the buffer, again, similar to the IBM PC video memory;
one character at a time into the Utf8Char class instances.
When the screen redraws, it just walks this array, and calls fl_draw() to draw
the text, one utf8 char at a time, with the colors/fonts/attributes from the Utf8Char class.
As characters are added, Esc sequences are intercepted and parsed into the EscapeSeq class,
which has a single instance for the terminal.
For the scrollback history, as lines scrolls off the top of the active display area,
the Utf8Char's are copied to the history buffer, and the active display's top line
is simply rotated to the bottom line and cleared, allowing memory reuse of the Utf8Char's,
to prevent memory churn for the display. The goal is to allow high volume output to the
terminal with a minimum affect on realloc'ing memory.
OPTIMIZATIONS
Where possible, caching is used to prevent repeated calls to cpu expensive operations,
such as anything to do with calculating unicode character width/height/etc.
RingBuffer
The ring buffer is split in two; the top part is the history, the bottom part is
the "display area", where new text comes in, where the cursor can be positioned,
and concepts like "scroll up" and "scroll down" all happen. The "history" is simply
a linear buffer where lines pushed up from the display are moved into.
Methods let one access the ring with index#s:
- The entire ring buffer can be accessed with:
for (int i=0; i<ring.ring_rows(); i++) {
Utf8Char *u8c_row = ring.u8c_ring_row(i);
for (int col=0; i<ring.ring_cols(); i++) {
u8c_row[col].xxx(); // access each Utf8Char at the row/col
}
}
Row#s can be given that are larger than the ring; these are automatically
wrapped around to the top. The ring, "history" and "display" can each be
accessed separately with index#s relative to their position
//////////////////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////////////////
Moved this down to the bottom of the file for now -- not sure where to put this,
but it's useful if one wants to reuse the EscapeSeq class somewhere else. -erco Dec 2022
Typical use pattern of EscapeSeq class.
This is unverified code, but should give the general gist;
while ( *s ) { // walk text that may contain ESC sequences
if ( *s == 0x1b ) {
escseq.parse(*s++); // start parsing ESC seq (does a reset())
continue;
} else if ( escseq.parse_in_progress() ) { // continuing to parse an ESC seq?
switch (escseq.parse(*s++)) { // parse char, advance s..
case fail: escseq.reset(); continue; // failed? reset, continue..
case success: continue; // keep parsing..
case completed: // parsed complete esc sequence?
break;
}
// Handle parsed esc sequence here..
switch ( escseq.esc_mode() ) {
case 'm': // ESC[...m?
for ( int i=0; i<escseq.total_vals(); i++ ) {
int val = escseq.val(i);
..handle values here..
}
break;
case 'J': // ESC[#J?
..handle..
break;
}
escseq.reset(); // done handling escseq, reset()
continue;
} else {
..handle non-escape chars here..
}
++s; // advance thru string
}
----------------------------------------------------------------------------------------

View File

@ -3878,6 +3878,28 @@ Fl_Tabs.o: ../FL/fl_utf8.h
Fl_Tabs.o: ../FL/Fl_Widget.H
Fl_Tabs.o: ../FL/Fl_Window.H
Fl_Tabs.o: ../FL/platform_types.h
Fl_Terminal.o: ../FL/Enumerations.H
Fl_Terminal.o: ../FL/Fl.H
Fl_Terminal.o: ../FL/fl_attr.h
Fl_Terminal.o: ../FL/Fl_Bitmap.H
Fl_Terminal.o: ../FL/Fl_Cairo.H
Fl_Terminal.o: ../FL/fl_casts.H
Fl_Terminal.o: ../FL/fl_config.h
Fl_Terminal.o: ../FL/fl_draw.H
Fl_Terminal.o: ../FL/Fl_Export.H
Fl_Terminal.o: ../FL/Fl_Group.H
Fl_Terminal.o: ../FL/Fl_Image.H
Fl_Terminal.o: ../FL/Fl_Rect.H
Fl_Terminal.o: ../FL/Fl_Scrollbar.H
Fl_Terminal.o: ../FL/Fl_Slider.H
Fl_Terminal.o: ../FL/fl_string_functions.h
Fl_Terminal.o: ../FL/Fl_Terminal.H
Fl_Terminal.o: ../FL/fl_types.h
Fl_Terminal.o: ../FL/fl_utf8.h
Fl_Terminal.o: ../FL/Fl_Valuator.H
Fl_Terminal.o: ../FL/Fl_Widget.H
Fl_Terminal.o: ../FL/Fl_Window.H
Fl_Terminal.o: ../FL/platform_types.h
Fl_Text_Buffer.o: ../config.h
Fl_Text_Buffer.o: ../FL/Enumerations.H
Fl_Text_Buffer.o: ../FL/Fl.H

3
test/.gitignore vendored
View File

@ -103,6 +103,7 @@ sudoku
symbols
table
tabs
terminal
threads
tile
tiled_image
@ -141,6 +142,8 @@ resize.cxx
resize.h
tabs.cxx
tabs.h
terminal.cxx
terminal.h
tree.cxx
tree.h
valuators.cxx

View File

@ -157,6 +157,7 @@ CREATE_EXAMPLE (sudoku "sudoku.cxx;sudoku.plist;sudoku.icns;sudoku.rc" "fltk_ima
CREATE_EXAMPLE (symbols symbols.cxx fltk)
CREATE_EXAMPLE (tabs tabs.fl fltk)
CREATE_EXAMPLE (table table.cxx fltk)
CREATE_EXAMPLE (terminal terminal.fl fltk)
CREATE_EXAMPLE (threads threads.cxx fltk)
CREATE_EXAMPLE (tile tile.cxx fltk)
CREATE_EXAMPLE (tiled_image tiled_image.cxx fltk)
@ -185,7 +186,7 @@ SET (UNITTEST_SRCS
unittest_viewport.cxx
unittest_scrollbarsize.cxx
unittest_schemes.cxx
unittest_simple_terminal.cxx
unittest_terminal.cxx
)
CREATE_EXAMPLE (unittests "${UNITTEST_SRCS}" "${GLDEMO_LIBS}")

View File

@ -30,7 +30,7 @@ CPPUNITTEST = \
unittest_viewport.cxx \
unittest_scrollbarsize.cxx \
unittest_schemes.cxx \
unittest_simple_terminal.cxx \
unittest_terminal.cxx \
unittest_core.cxx
OBJUNITTEST = \
@ -125,6 +125,7 @@ CPPFILES =\
symbols.cxx \
table.cxx \
tabs.cxx \
terminal.cxx \
threads.cxx \
tile.cxx \
tiled_image.cxx \
@ -206,6 +207,7 @@ ALL = \
sudoku$(EXEEXT) \
symbols$(EXEEXT) \
table$(EXEEXT) \
terminal$(EXEEXT) \
$(THREADS) \
tile$(EXEEXT) \
tiled_image$(EXEEXT) \
@ -268,6 +270,7 @@ clean:
$(RM) radio.cxx radio.h
$(RM) resize.cxx resize.h
$(RM) tabs.cxx tabs.h
$(RM) terminal.cxx terminal.h
$(RM) tree.cxx tree.h
$(RM) valuators.cxx valuators.h
@ -618,6 +621,9 @@ table$(EXEEXT): table.o
tabs$(EXEEXT): tabs.o
tabs.cxx: tabs.fl ../fluid/fluid$(EXEEXT)
terminal$(EXEEXT): terminal.o
terminal.cxx: terminal.fl ../fluid/fluid$(EXEEXT)
threads$(EXEEXT): threads.o
# This ensures that we have this dependency even if threads are not
# enabled in the current tree...

View File

@ -64,7 +64,7 @@ That was a blank line above this.
#include <FL/Fl_Button.H>
#include <FL/Fl_Int_Input.H>
#include <FL/Fl_Choice.H>
#include <FL/Fl_Simple_Terminal.H>
#include <FL/Fl_Terminal.H>
#include <FL/fl_ask.H>
#include <stdio.h>
#include <string.h>
@ -81,7 +81,7 @@ Fl_Button *top,
Fl_Choice *btype;
Fl_Choice *wtype;
Fl_Int_Input *field;
Fl_Simple_Terminal *tty = 0;
Fl_Terminal *tty = 0;
typedef struct {
const char *name;
@ -219,7 +219,7 @@ int main(int argc, char **argv) {
wtype->value(4); // FL_WHEN_RELEASE_ALWAYS is Fl_Browser's default
// Small terminal window for callback messages
tty = new Fl_Simple_Terminal(0,400,720,120);
tty = new Fl_Terminal(0,400,720,120);
tty->history_lines(50);
tty->ansi(true);

View File

@ -26,7 +26,7 @@
#include <FL/Fl_Color_Chooser.H>
#include <FL/Fl_Output.H>
#include <FL/Fl_Hor_Value_Slider.H>
#include <FL/Fl_Simple_Terminal.H>
#include <FL/Fl_Terminal.H>
#include <FL/fl_draw.H>
#include <math.h>
@ -93,7 +93,7 @@ public:
// global variables
Fl_Simple_Terminal *term = 0;
Fl_Terminal *term = 0;
double g_lfg; // perceived lightness of foreground color
double g_lbg; // perceived lightness of background color
@ -428,9 +428,9 @@ int main(int argc, char **argv) {
int ttw = window.w() - 20;
int tth = window.h() - tty - 10;
term = new Fl_Simple_Terminal(ttx, tty, ttw, tth);
term = new Fl_Terminal(ttx, tty, ttw, tth);
term->color(FL_WHITE);
term->textcolor(FL_BLACK);
term->textfgcolor(FL_BLACK);
term->textsize(13);
term->printf("FLTK fl_contrast() test program with different contrast algorithms, version %s\n", version);

View File

@ -75,7 +75,7 @@
#include <FL/Fl_Button.H>
#include <FL/Fl_Menu_Button.H> // right click popup menu
#include <FL/Fl_Scheme_Choice.H>
#include <FL/Fl_Simple_Terminal.H> // tty
#include <FL/Fl_Terminal.H> // tty
#include <FL/filename.H>
#include <FL/platform.H>
#include <FL/fl_ask.H> // fl_alert()
@ -102,7 +102,7 @@ void dobut(Fl_Widget *, long);
Fl_Double_Window *form = 0;
Fl_Group *demogrp = 0;
Fl_Simple_Terminal *tty = 0;
Fl_Terminal *tty = 0;
Fl_Scheme_Choice *scheme_choice = 0;
Fl_Button *but[9];
Fl_Button *exit_button;
@ -231,11 +231,15 @@ void create_the_forms() {
demogrp->end();
// Small debug terminal window parented to window, not demogrp
tty = new Fl_Simple_Terminal(10, FORM_H - 1, FORM_W - 20, 1);
// To show/hide debug terminal, use demo's right-click menu
//
tty = new Fl_Terminal(10, FORM_H - 1, FORM_W - 20, 1);
tty->history_lines(50);
tty->display_rows(2); // make display at least 2 rows high, even if not seen
tty->display_columns(100); // make display at least 100 cols wide, even if not seen
tty->ansi(true);
tty->hide();
tty->textsize(10);
tty->textsize(12);
// End window
form->end();

View File

@ -36,7 +36,7 @@
#include <FL/Fl_PNM_Image.H>
#include <FL/Fl_Light_Button.H>
#include <FL/Fl_Double_Window.H>
#include <FL/Fl_Simple_Terminal.H>
#include <FL/Fl_Terminal.H>
#include <stdio.h>
#include <string.h>
@ -55,7 +55,7 @@ Fl_Input *filter;
Fl_File_Browser *files;
Fl_File_Chooser *fc;
Fl_Shared_Image *image = 0;
Fl_Simple_Terminal *tty = 0;
Fl_Terminal *tty = 0;
// for choosing extra groups
Fl_Choice *ch_extra;
@ -109,8 +109,9 @@ main(int argc, // I - Number of command-line arguments
// Make the main window...
window = new Fl_Double_Window(400, 215+TERMINAL_HEIGHT, "File Chooser Test");
tty = new Fl_Simple_Terminal(0,215,window->w(),TERMINAL_HEIGHT);
tty = new Fl_Terminal(0,215,window->w(),TERMINAL_HEIGHT);
tty->ansi(true);
tty->display_columns(100); // at least 100 cols wide, even tho actual window smaller
// Group: limit resizing to filter input (not browse button)
grp = new Fl_Group(0,10,400,25);

View File

@ -26,12 +26,12 @@
#include <FL/Fl_Toggle_Button.H>
#include <FL/Fl_Light_Button.H>
#include <FL/Fl_Color_Chooser.H>
#include <FL/Fl_Simple_Terminal.H>
#include <FL/Fl_Terminal.H>
#define TERMINAL_HEIGHT 120
// Globals
Fl_Simple_Terminal *G_tty = 0;
Fl_Terminal *G_tty = 0;
void cb(Fl_Widget *ob) {
G_tty->printf("Callback for %s '%s'\n",ob->label(),((Fl_Input*)ob)->value());
@ -91,7 +91,7 @@ int main(int argc, char **argv) {
Fl::args(argc, argv);
Fl::get_system_colors();
Fl_Window *window = new Fl_Window(400,420+TERMINAL_HEIGHT);
G_tty = new Fl_Simple_Terminal(0,420,window->w(),TERMINAL_HEIGHT);
G_tty = new Fl_Terminal(0,420,window->w(),TERMINAL_HEIGHT);
int y = 10;
input[0] = new Fl_Input(70,y,300,30,"Normal:"); y += 35;

View File

@ -18,12 +18,12 @@
#include <FL/Fl_Button.H>
#include <FL/Fl_Double_Window.H>
#include <FL/Fl_Input_Choice.H>
#include <FL/Fl_Simple_Terminal.H>
#include <FL/Fl_Terminal.H>
#define TERMINAL_HEIGHT 120
// Globals
Fl_Simple_Terminal *G_tty = 0;
Fl_Terminal *G_tty = 0;
void buttcb(Fl_Widget*,void*data) {
Fl_Input_Choice *in=(Fl_Input_Choice *)data;
@ -44,7 +44,7 @@ void input_choice_cb(Fl_Widget*,void*data) {
int main(int argc, char **argv) {
Fl_Double_Window win(300, 200+TERMINAL_HEIGHT);
G_tty = new Fl_Simple_Terminal(0,200,win.w(),TERMINAL_HEIGHT);
G_tty = new Fl_Terminal(0,200,win.w(),TERMINAL_HEIGHT);
Fl_Input_Choice in(40,40,100,28,"Test");
in.callback(input_choice_cb, (void*)&in);

View File

@ -222,7 +222,7 @@ browser.o: ../FL/Fl_Rect.H
browser.o: ../FL/Fl_RGB_Image.H
browser.o: ../FL/Fl_Scrollbar.H
browser.o: ../FL/Fl_Select_Browser.H
browser.o: ../FL/Fl_Simple_Terminal.H
browser.o: ../FL/Fl_Terminal.H
browser.o: ../FL/Fl_Slider.H
browser.o: ../FL/Fl_Text_Buffer.H
browser.o: ../FL/Fl_Text_Display.H
@ -507,7 +507,7 @@ contrast.o: ../FL/Fl_Return_Button.H
contrast.o: ../FL/Fl_RGB_Image.H
contrast.o: ../FL/Fl_Round_Button.H
contrast.o: ../FL/Fl_Scrollbar.H
contrast.o: ../FL/Fl_Simple_Terminal.H
contrast.o: ../FL/Fl_Terminal.H
contrast.o: ../FL/Fl_Slider.H
contrast.o: ../FL/Fl_Text_Buffer.H
contrast.o: ../FL/Fl_Text_Display.H
@ -693,7 +693,7 @@ demo.o: ../FL/Fl_RGB_Image.H
demo.o: ../FL/Fl_Scheme.H
demo.o: ../FL/Fl_Scheme_Choice.H
demo.o: ../FL/Fl_Scrollbar.H
demo.o: ../FL/Fl_Simple_Terminal.H
demo.o: ../FL/Fl_Terminal.H
demo.o: ../FL/Fl_Slider.H
demo.o: ../FL/Fl_Text_Buffer.H
demo.o: ../FL/Fl_Text_Display.H
@ -911,7 +911,7 @@ file_chooser.o: ../FL/Fl_Return_Button.H
file_chooser.o: ../FL/Fl_RGB_Image.H
file_chooser.o: ../FL/Fl_Scrollbar.H
file_chooser.o: ../FL/Fl_Shared_Image.H
file_chooser.o: ../FL/Fl_Simple_Terminal.H
file_chooser.o: ../FL/Fl_Terminal.H
file_chooser.o: ../FL/Fl_Slider.H
file_chooser.o: ../FL/Fl_Text_Buffer.H
file_chooser.o: ../FL/Fl_Text_Display.H
@ -1409,7 +1409,7 @@ input.o: ../FL/Fl_Return_Button.H
input.o: ../FL/Fl_RGB_Image.H
input.o: ../FL/Fl_Scrollbar.H
input.o: ../FL/Fl_Secret_Input.H
input.o: ../FL/Fl_Simple_Terminal.H
input.o: ../FL/Fl_Terminal.H
input.o: ../FL/Fl_Slider.H
input.o: ../FL/Fl_Text_Buffer.H
input.o: ../FL/Fl_Text_Display.H
@ -1448,7 +1448,7 @@ input_choice.o: ../FL/Fl_Preferences.H
input_choice.o: ../FL/Fl_Rect.H
input_choice.o: ../FL/Fl_RGB_Image.H
input_choice.o: ../FL/Fl_Scrollbar.H
input_choice.o: ../FL/Fl_Simple_Terminal.H
input_choice.o: ../FL/Fl_Terminal.H
input_choice.o: ../FL/Fl_Slider.H
input_choice.o: ../FL/Fl_Text_Buffer.H
input_choice.o: ../FL/Fl_Text_Display.H
@ -1626,7 +1626,7 @@ menubar.o: ../FL/Fl_RGB_Image.H
menubar.o: ../FL/Fl_Scheme.H
menubar.o: ../FL/Fl_Scheme_Choice.H
menubar.o: ../FL/Fl_Scrollbar.H
menubar.o: ../FL/Fl_Simple_Terminal.H
menubar.o: ../FL/Fl_Terminal.H
menubar.o: ../FL/Fl_Slider.H
menubar.o: ../FL/fl_string_functions.h
menubar.o: ../FL/Fl_Sys_Menu_Bar.H
@ -1719,7 +1719,7 @@ native-filechooser.o: ../FL/Fl_Rect.H
native-filechooser.o: ../FL/Fl_Return_Button.H
native-filechooser.o: ../FL/Fl_RGB_Image.H
native-filechooser.o: ../FL/Fl_Scrollbar.H
native-filechooser.o: ../FL/Fl_Simple_Terminal.H
native-filechooser.o: ../FL/Fl_Terminal.H
native-filechooser.o: ../FL/Fl_Slider.H
native-filechooser.o: ../FL/Fl_Text_Buffer.H
native-filechooser.o: ../FL/Fl_Text_Display.H
@ -2446,7 +2446,7 @@ table.o: ../FL/Fl_Rect.H
table.o: ../FL/Fl_RGB_Image.H
table.o: ../FL/Fl_Scroll.H
table.o: ../FL/Fl_Scrollbar.H
table.o: ../FL/Fl_Simple_Terminal.H
table.o: ../FL/Fl_Terminal.H
table.o: ../FL/Fl_Slider.H
table.o: ../FL/Fl_Table.H
table.o: ../FL/Fl_Table_Row.H
@ -2595,7 +2595,7 @@ tree.o: ../FL/Fl_Rect.H
tree.o: ../FL/Fl_Return_Button.H
tree.o: ../FL/Fl_RGB_Image.H
tree.o: ../FL/Fl_Scrollbar.H
tree.o: ../FL/Fl_Simple_Terminal.H
tree.o: ../FL/Fl_Terminal.H
tree.o: ../FL/Fl_Slider.H
tree.o: ../FL/Fl_Text_Buffer.H
tree.o: ../FL/Fl_Text_Display.H
@ -2660,7 +2660,7 @@ unittests.o: ../FL/Fl_Preferences.H
unittests.o: ../FL/Fl_Rect.H
unittests.o: ../FL/Fl_RGB_Image.H
unittests.o: ../FL/Fl_Scrollbar.H
unittests.o: ../FL/Fl_Simple_Terminal.H
unittests.o: ../FL/Fl_Terminal.H
unittests.o: ../FL/Fl_Slider.H
unittests.o: ../FL/fl_string_functions.h
unittests.o: ../FL/Fl_Text_Buffer.H
@ -2788,7 +2788,7 @@ unittest_core.o: ../FL/Fl_Preferences.H
unittest_core.o: ../FL/Fl_Rect.H
unittest_core.o: ../FL/Fl_RGB_Image.H
unittest_core.o: ../FL/Fl_Scrollbar.H
unittest_core.o: ../FL/Fl_Simple_Terminal.H
unittest_core.o: ../FL/Fl_Terminal.H
unittest_core.o: ../FL/Fl_Slider.H
unittest_core.o: ../FL/Fl_Text_Buffer.H
unittest_core.o: ../FL/Fl_Text_Display.H
@ -2980,37 +2980,26 @@ unittest_scrollbarsize.o: ../FL/Fl_Widget.H
unittest_scrollbarsize.o: ../FL/Fl_Window.H
unittest_scrollbarsize.o: ../FL/platform_types.h
unittest_scrollbarsize.o: unittests.h
unittest_simple_terminal.o: ../FL/Enumerations.H
unittest_simple_terminal.o: ../FL/Fl.H
unittest_simple_terminal.o: ../FL/fl_attr.h
unittest_simple_terminal.o: ../FL/Fl_Bitmap.H
unittest_simple_terminal.o: ../FL/Fl_Cairo.H
unittest_simple_terminal.o: ../FL/fl_casts.H
unittest_simple_terminal.o: ../FL/fl_config.h
unittest_simple_terminal.o: ../FL/Fl_Device.H
unittest_simple_terminal.o: ../FL/Fl_Double_Window.H
unittest_simple_terminal.o: ../FL/fl_draw.H
unittest_simple_terminal.o: ../FL/Fl_Export.H
unittest_simple_terminal.o: ../FL/Fl_Graphics_Driver.H
unittest_simple_terminal.o: ../FL/Fl_Group.H
unittest_simple_terminal.o: ../FL/Fl_Image.H
unittest_simple_terminal.o: ../FL/Fl_Pixmap.H
unittest_simple_terminal.o: ../FL/Fl_Plugin.H
unittest_simple_terminal.o: ../FL/Fl_Preferences.H
unittest_simple_terminal.o: ../FL/Fl_Rect.H
unittest_simple_terminal.o: ../FL/Fl_RGB_Image.H
unittest_simple_terminal.o: ../FL/Fl_Scrollbar.H
unittest_simple_terminal.o: ../FL/Fl_Simple_Terminal.H
unittest_simple_terminal.o: ../FL/Fl_Slider.H
unittest_simple_terminal.o: ../FL/Fl_Text_Buffer.H
unittest_simple_terminal.o: ../FL/Fl_Text_Display.H
unittest_simple_terminal.o: ../FL/fl_types.h
unittest_simple_terminal.o: ../FL/fl_utf8.h
unittest_simple_terminal.o: ../FL/Fl_Valuator.H
unittest_simple_terminal.o: ../FL/Fl_Widget.H
unittest_simple_terminal.o: ../FL/Fl_Window.H
unittest_simple_terminal.o: ../FL/platform_types.h
unittest_simple_terminal.o: unittests.h
unittest_terminal.o: ../FL/Enumerations.H
unittest_terminal.o: ../FL/Fl.H
unittest_terminal.o: ../FL/fl_attr.h
unittest_terminal.o: ../FL/Fl_Bitmap.H
unittest_terminal.o: ../FL/Fl_Cairo.H
unittest_terminal.o: ../FL/fl_casts.H
unittest_terminal.o: ../FL/fl_config.h
unittest_terminal.o: ../FL/Fl_Double_Window.H
unittest_terminal.o: ../FL/Fl_Export.H
unittest_terminal.o: ../FL/Fl_Group.H
unittest_terminal.o: ../FL/Fl_Image.H
unittest_terminal.o: ../FL/Fl_Rect.H
unittest_terminal.o: ../FL/Fl_Scrollbar.H
unittest_terminal.o: ../FL/Fl_Terminal.H
unittest_terminal.o: ../FL/fl_types.h
unittest_terminal.o: ../FL/fl_utf8.h
unittest_terminal.o: ../FL/Fl_Widget.H
unittest_terminal.o: ../FL/Fl_Window.H
unittest_terminal.o: ../FL/platform_types.h
unittest_terminal.o: unittests.h
unittest_symbol.o: ../FL/Enumerations.H
unittest_symbol.o: ../FL/Fl.H
unittest_symbol.o: ../FL/fl_attr.h
@ -3203,7 +3192,7 @@ valuators.o: ../FL/Fl_Repeat_Button.H
valuators.o: ../FL/Fl_RGB_Image.H
valuators.o: ../FL/Fl_Roller.H
valuators.o: ../FL/Fl_Scrollbar.H
valuators.o: ../FL/Fl_Simple_Terminal.H
valuators.o: ../FL/Fl_Terminal.H
valuators.o: ../FL/Fl_Slider.H
valuators.o: ../FL/Fl_Spinner.H
valuators.o: ../FL/Fl_Text_Buffer.H

View File

@ -31,14 +31,14 @@
#include <stdio.h>
#include <stdlib.h>
#include <FL/fl_draw.H>
#include <FL/Fl_Simple_Terminal.H>
#include <FL/Fl_Terminal.H>
#include <FL/fl_ask.H>
#include <FL/fl_string_functions.h>
#define TERMINAL_HEIGHT 120
// Globals
Fl_Simple_Terminal *G_tty = 0;
Fl_Terminal *G_tty = 0;
void window_cb(Fl_Widget* w, void*) {
puts("window callback called"); // end of program, so stdout instead of G_tty
@ -235,7 +235,7 @@ int main(int argc, char **argv) {
Fl_Scheme_Choice scheme_choice(300, 50, 100, 25, "&scheme");
G_tty = new Fl_Simple_Terminal(0,400,WIDTH,TERMINAL_HEIGHT);
G_tty = new Fl_Terminal(0,400,WIDTH,TERMINAL_HEIGHT);
window.callback(window_cb);
Fl_Menu_Bar menubar(0,0,WIDTH,30); menubar.menu(menutable);

View File

@ -25,14 +25,14 @@
#include <FL/Fl_Box.H>
#include <FL/Fl_Native_File_Chooser.H>
#include <FL/Fl_Help_View.H>
#include <FL/Fl_Simple_Terminal.H>
#include <FL/Fl_Terminal.H>
#define TERMINAL_HEIGHT 120
// GLOBALS
Fl_Input *G_filename = NULL;
Fl_Multiline_Input *G_filter = NULL;
Fl_Simple_Terminal *G_tty = NULL;
Fl_Terminal *G_tty = NULL;
void PickFile_CB(Fl_Widget*, void*) {
// Create native chooser
@ -106,7 +106,7 @@ int main(int argc, char **argv) {
win->size_range(win->w(), win->h(), 0, 0);
win->begin();
{
G_tty = new Fl_Simple_Terminal(0,400,win->w(),TERMINAL_HEIGHT);
G_tty = new Fl_Terminal(0,400,win->w(),TERMINAL_HEIGHT);
int x = 80, y = 10;
G_filename = new Fl_Input(x, y, win->w()-80-10, 25, "Filename");

View File

@ -10,7 +10,7 @@
#include <FL/fl_draw.H>
#include <FL/fl_ask.H>
#include <FL/Fl_Table_Row.H>
#include <FL/Fl_Simple_Terminal.H>
#include <FL/Fl_Terminal.H>
#include <stdio.h>
#include <string.h>
@ -19,7 +19,7 @@
#define TERMINAL_HEIGHT 120
// Globals
Fl_Simple_Terminal *G_tty = 0;
Fl_Terminal *G_tty = 0;
// Simple demonstration class to derive from Fl_Table_Row
class DemoTable : public Fl_Table_Row
@ -349,7 +349,7 @@ int main(int argc, char **argv)
{
Fl_Window win(900, 730+TERMINAL_HEIGHT);
G_tty = new Fl_Simple_Terminal(0,730,win.w(),TERMINAL_HEIGHT);
G_tty = new Fl_Terminal(0,730,win.w(),TERMINAL_HEIGHT);
G_table = new DemoTable(20, 20, 860, 460, "Demo");
G_table->selection_color(FL_YELLOW);

1972
test/terminal.fl Normal file

File diff suppressed because it is too large Load Diff

View File

@ -38,7 +38,7 @@ decl {\#include <FL/Fl_Color_Chooser.H>} {public global
decl {\#include <FL/Fl_Text_Display.H>} {public global
}
decl {\#include <FL/Fl_Simple_Terminal.H>} {public global
decl {\#include <FL/Fl_Terminal.H>} {public global
}
decl {int G_cb_counter = 0;} {
@ -1749,7 +1749,7 @@ helpwin->show();}
Fl_Box tty {
label label
xywh {16 571 1014 149} box DOWN_BOX color 0
class Fl_Simple_Terminal
class Fl_Terminal
}
}
code {// Initialize Tree

View File

@ -18,8 +18,9 @@
#include <FL/Fl_Group.H>
#include <FL/Fl_Button.H>
#include <FL/Fl_Simple_Terminal.H>
#include <FL/Fl_Terminal.H>
#include "../src/Fl_String.H"
#include <FL/Fl_Preferences.H>
#include <FL/fl_callback_macros.H>
#include <FL/filename.H>
#include <FL/fl_utf8.h>
@ -374,7 +375,7 @@ TEST(Fl_Callback_Macros, FL_INLINE_CALLBACK) {
*/
class Ut_Core_Test : public Fl_Group {
Fl_Simple_Terminal *tty;
Fl_Terminal *tty;
bool suite_ran_;
public:
@ -390,7 +391,7 @@ public:
tty(NULL),
suite_ran_(false)
{
tty = new Fl_Simple_Terminal(x+4, y+4, w-8, h-8, "Unittest Log");
tty = new Fl_Terminal(x+4, y+4, w-8, h-8, "Unittest Log");
tty->ansi(true);
end();
Ut_Suite::tty = tty;

View File

@ -1,121 +0,0 @@
//
// Unit tests for the Fast Light Tool Kit (FLTK).
//
// Copyright 1998-2022 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
//
#include "unittests.h"
#include <time.h>
#include <FL/Fl_Group.H>
#include <FL/Fl_Simple_Terminal.H>
//
//------- test the Fl_Simple_Terminal drawing capabilities ----------
//
class Ut_Simple_Terminal_Test : public Fl_Group {
Fl_Simple_Terminal *tty1;
Fl_Simple_Terminal *tty2;
Fl_Simple_Terminal *tty3;
void ansi_test_pattern(Fl_Simple_Terminal *tty) {
tty->append("\033[30mBlack Courier 14\033[0m Normal text\n"
"\033[31mRed Courier 14\033[0m Normal text\n"
"\033[32mGreen Courier 14\033[0m Normal text\n"
"\033[33mYellow Courier 14\033[0m Normal text\n"
"\033[34mBlue Courier 14\033[0m Normal text\n"
"\033[35mMagenta Courier 14\033[0m Normal text\n"
"\033[36mCyan Courier 14\033[0m Normal text\n"
"\033[37mWhite Courier 14\033[0m Normal text\n"
"\033[40mBright Black Courier 14\033[0m Normal text\n"
"\033[41mBright Red Courier 14\033[0m Normal text\n"
"\033[42mBright Green Courier 14\033[0m Normal text\n"
"\033[43mBright Yellow Courier 14\033[0m Normal text\n"
"\033[44mBright Blue Courier 14\033[0m Normal text\n"
"\033[45mBright Magenta Courier 14\033[0m Normal text\n"
"\033[46mBright Cyan Courier 14\033[0m Normal text\n"
"\033[47mBright White Courier 14\033[0m Normal text\n"
"\n"
"\033[31mRed\033[32mGreen\033[33mYellow\033[34mBlue\033[35mMagenta\033[36mCyan\033[37mWhite\033[0m - "
"\033[31mX\033[32mX\033[33mX\033[34mX\033[35mX\033[36mX\033[37mX\033[0m\n"
"\033[41mRed\033[42mGreen\033[43mYellow\033[44mBlue\033[45mMagenta\033[46mCyan\033[47mWhite\033[0m - "
"\033[41mX\033[42mX\033[43mX\033[44mX\033[45mX\033[46mX\033[47mX\033[0m\n");
}
void gray_test_pattern(Fl_Simple_Terminal *tty) {
tty->append("Grayscale Test Pattern\n"
"--------------------------\n"
"\033[0m 100% white Courier 14\n"
"\033[1m 90% white Courier 14\n"
"\033[2m 80% white Courier 14\n"
"\033[3m 70% white Courier 14\n"
"\033[4m 60% white Courier 14\n"
"\033[5m 50% white Courier 14\n"
"\033[6m 40% white Courier 14\n"
"\033[7m 30% white Courier 14\n"
"\033[8m 20% white Courier 14\n"
"\033[9m 10% white Courier 14\n"
"\033[0m");
}
static void date_timer_cb(void *data) {
Fl_Simple_Terminal *tty = (Fl_Simple_Terminal*)data;
time_t lt = time(NULL);
tty->printf("The time and date is now: %s", ctime(&lt));
Fl::repeat_timeout(3.0, date_timer_cb, data);
}
public:
static Fl_Widget *create() {
return new Ut_Simple_Terminal_Test(UT_TESTAREA_X, UT_TESTAREA_Y, UT_TESTAREA_W, UT_TESTAREA_H);
}
Ut_Simple_Terminal_Test(int x, int y, int w, int h)
: Fl_Group(x, y, w, h) {
static Fl_Text_Display::Style_Table_Entry my_stable[] = { // 10 entry grayscale
// Font Color Font Face Font Size ANSI Sequence
// ---------- ---------------- --------- -------------
{ 0xffffff00, FL_COURIER_BOLD, 14 }, // "\033[0m" 0 white 100%
{ 0xe6e6e600, FL_COURIER_BOLD, 14 }, // "\033[1m" 1 white 90%
{ 0xcccccc00, FL_COURIER_BOLD, 14 }, // "\033[2m" 2 white 80%
{ 0xb3b3b300, FL_COURIER_BOLD, 14 }, // "\033[3m" 3 white 70%
{ 0x99999900, FL_COURIER_BOLD, 14 }, // "\033[4m" 4 white 60%
{ 0x80808000, FL_COURIER_BOLD, 14 }, // "\033[5m" 5 white 50% "\033[0m"
{ 0x66666600, FL_COURIER_BOLD, 14 }, // "\033[6m" 6 white 40%
{ 0x4d4d4d00, FL_COURIER_BOLD, 14 }, // "\033[7m" 7 white 30%
{ 0x33333300, FL_COURIER_BOLD, 14 }, // "\033[8m" 8 white 20%
{ 0x1a1a1a00, FL_COURIER_BOLD, 14 }, // "\033[9m" 9 white 10%
};
int tty_h = (int)(h/3.5);
int tty_y1 = y+(tty_h*0)+20;
int tty_y2 = y+(tty_h*1)+40;
int tty_y3 = y+(tty_h*2)+60;
// TTY1
tty1 = new Fl_Simple_Terminal(x, tty_y1, w, tty_h,"Tty 1: ANSI off");
tty1->ansi(false);
Fl::add_timeout(0.5, date_timer_cb, (void*)tty1);
// TTY2
tty2 = new Fl_Simple_Terminal(x, tty_y2, w, tty_h,"Tty 2: ANSI on");
tty2->ansi(true);
ansi_test_pattern(tty2);
Fl::add_timeout(0.5, date_timer_cb, (void*)tty2);
// TTY3
tty3 = new Fl_Simple_Terminal(x, tty_y3, w, tty_h, "Tty 3: Grayscale Style Table");
tty3->style_table(my_stable, sizeof(my_stable), 0);
tty3->ansi(true);
gray_test_pattern(tty3);
Fl::add_timeout(0.5, date_timer_cb, (void*)tty3);
end();
}
};
UnitTest simple_terminal(UT_TEST_SIMPLE_TERMINAL, "Simple Terminal", Ut_Simple_Terminal_Test::create);

View File

@ -0,0 +1,98 @@
//
// Unit tests for the Fast Light Tool Kit (FLTK).
//
// Copyright 1998-2022 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
//
#include "unittests.h"
#include <time.h>
#include <FL/Fl_Group.H>
#include <FL/Fl_Terminal.H>
//
//------- test the Fl_Terminal drawing capabilities ----------
//
class Ut_Terminal_Test : public Fl_Group {
Fl_Terminal *tty1;
Fl_Terminal *tty2;
void ansi_test_pattern(Fl_Terminal *tty) {
tty->append("\033[30mBlack Courier 14\033[0m Normal text\n"
"\033[31mRed Courier 14\033[0m Normal text\n"
"\033[32mGreen Courier 14\033[0m Normal text\n"
"\033[33mYellow Courier 14\033[0m Normal text\n"
"\033[34mBlue Courier 14\033[0m Normal text\n"
"\033[35mMagenta Courier 14\033[0m Normal text\n"
"\033[36mCyan Courier 14\033[0m Normal text\n"
"\033[37mWhite Courier 14\033[0m Normal text\n"
"\033[1;30mBright Black Courier 14\033[0m Normal text\n"
"\033[1;31mBright Red Courier 14\033[0m Normal text\n"
"\033[1;32mBright Green Courier 14\033[0m Normal text\n"
"\033[1;33mBright Yellow Courier 14\033[0m Normal text\n"
"\033[1;34mBright Blue Courier 14\033[0m Normal text\n"
"\033[1;35mBright Magenta Courier 14\033[0m Normal text\n"
"\033[1;36mBright Cyan Courier 14\033[0m Normal text\n"
"\033[1;37mBright White Courier 14\033[0m Normal text\n"
"\n"
"\033[31mRed\033[32mGreen\033[33mYellow\033[34mBlue\033[35mMagenta\033[36mCyan\033[37mWhite\033[0m - "
"\033[31mX\033[32mX\033[33mX\033[34mX\033[35mX\033[36mX\033[37mX\033[0m\n"
"\033[1;31mRed\033[1;32mGreen\033[1;33mYellow\033[34mBlue\033[35mMagenta\033[36mCyan\033[1;37mWhite\033[1;0m - "
"\033[1;31mX\033[1;32mX\033[1;33mX\033[1;34mX\033[1;35mX\033[1;36mX\033[1;37mX\033[0m\n");
}
void gray_test_pattern(Fl_Terminal *tty) {
tty->append("Grayscale Test Pattern\n"
"--------------------------\n"
"\033[38;2;255;255;255m 100% white Courier 14\n" // ESC xterm codes for setting r;g;b colors
"\033[38;2;230;230;230m 90% white Courier 14\n"
"\033[38;2;205;205;205m 80% white Courier 14\n"
"\033[38;2;179;179;179m 70% white Courier 14\n"
"\033[38;2;154;154;154m 60% white Courier 14\n"
"\033[38;2;128;128;128m 50% white Courier 14\n"
"\033[38;2;102;102;102m 40% white Courier 14\n"
"\033[38;2;77;77;77m" " 30% white Courier 14\n"
"\033[38;2;51;51;51m" " 20% white Courier 14\n"
"\033[38;2;26;26;26m" " 10% white Courier 14\n"
"\033[38;2;0;0;0m" " 0% white Courier 14\n"
"\033[0m");
}
static void date_timer_cb(void *data) {
Fl_Terminal *tty = (Fl_Terminal*)data;
time_t lt = time(NULL);
tty->printf("The time and date is now: %s", ctime(&lt));
Fl::repeat_timeout(3.0, date_timer_cb, data);
}
public:
static Fl_Widget *create() {
return new Ut_Terminal_Test(UT_TESTAREA_X, UT_TESTAREA_Y, UT_TESTAREA_W, UT_TESTAREA_H);
}
Ut_Terminal_Test(int x, int y, int w, int h)
: Fl_Group(x, y, w, h) {
int tty_h = (int)(h/2.25+.5);
int tty_y1 = y+(tty_h*0)+20;
int tty_y2 = y+(tty_h*1)+40;
// TTY1
tty1 = new Fl_Terminal(x, tty_y1, w, tty_h,"Tty 1: Colors");
ansi_test_pattern(tty1);
Fl::add_timeout(0.5, date_timer_cb, (void*)tty1);
// TTY2
tty2 = new Fl_Terminal(x, tty_y2, w, tty_h,"Tty 2: Grayscale");
gray_test_pattern(tty2);
Fl::add_timeout(0.5, date_timer_cb, (void*)tty2);
end();
}
};
UnitTest simple_terminal(UT_TEST_SIMPLE_TERMINAL, "Terminal", Ut_Terminal_Test::create);

View File

@ -29,7 +29,7 @@
#include <FL/Fl_Double_Window.H>
#include <FL/Fl_Hold_Browser.H>
#include <FL/Fl_Help_View.H>
#include <FL/Fl_Simple_Terminal.H>
#include <FL/Fl_Terminal.H>
#include <FL/Fl_Group.H>
#include <FL/Fl_Box.H>
#include <FL/fl_draw.H> // fl_text_extents()
@ -181,7 +181,7 @@ int Ut_Suite::num_failed_ = 0;
const char *Ut_Suite::red = "\033[31m";
const char *Ut_Suite::green = "\033[32m";
const char *Ut_Suite::normal = "\033[0m";
Fl_Simple_Terminal *Ut_Suite::tty = NULL;
Fl_Terminal *Ut_Suite::tty = NULL;
/** Switch the user of color escape sequnces in the log text. */
void Ut_Suite::color(int v) {

View File

@ -22,7 +22,7 @@
#include <stdarg.h>
class Fl_Simple_Terminal;
class Fl_Terminal;
// WINDOW/WIDGET SIZES
const int UT_MAINWIN_W = 700; // main window w()
@ -153,7 +153,7 @@ public:
static const char *red;
static const char *green;
static const char *normal;
static Fl_Simple_Terminal *tty;
static Fl_Terminal *tty;
};
#define UT_CONCAT_(prefix, suffix) prefix##suffix

View File

@ -2,7 +2,7 @@
version 1.0400
header_name {.h}
code_name {.cxx}
decl {\#include <FL/Fl_Simple_Terminal.H>} {public global
decl {\#include <FL/Fl_Terminal.H>} {public global
}
Function {} {open
@ -232,9 +232,9 @@ Function {} {open
xywh {460 385 110 115} box BORDER_FRAME color 0 selection_color 0 labelsize 11 align 128
}
Fl_Box tty {selected
xywh {10 513 560 117} color 0
xywh {10 513 560 117} box DOWN_BOX color 0
code0 {o->ansi(true);}
class Fl_Simple_Terminal
class Fl_Terminal
}
}
}
@ -242,7 +242,7 @@ Function {} {open
Function {callback(Fl_Widget* o, void*)} {open return_type void
} {
code {const char *name = (const char*)(o->user_data() ? o->user_data() : "???");
tty->printf("callback(): %s value() = \\033[2m%g\\033[0m\\n",
tty->printf("callback(): %s value() = \\033[1m%g\\033[0m\\n",
name, ((Fl_Valuator*)o)->value());} {}
}
@ -250,6 +250,6 @@ Function {callback_spinner(Fl_Widget* o, void*)} {
comment {Spinner doesn't derive from Fl_Valuator..} open return_type void
} {
code {const char *name = (const char*)(o->user_data() ? o->user_data() : "???");
tty->printf("callback(): %s value() = \\033[2m%g\\033[0m\\n",
tty->printf("callback(): %s value() = \\033[1m%g\\033[0m\\n",
name, ((Fl_Spinner*)o)->value());} {}
}