Fl_Terminal better detecting of invalid ANSI/UTF-8 (#950)

Codex double-checked my work.

This improves ANSI/XTERM parsing error detection, and also handles
detecting bad UTF-8 sequences better by testing for negative returns
from fl_utf8len().

I'd lost track of these changes for several years due to a workstation
outage, delaying my solution for #950.
This commit is contained in:
Greg Ercolano
2026-08-12 10:03:56 -07:00
parent e11495ae85
commit 47d0c95f8a
3 changed files with 159 additions and 120 deletions
+10 -6
View File
@@ -30,6 +30,7 @@
#include <FL/fl_utf8.h> // fl_utf8_is_continuation()
#include <stdarg.h> // va_list (MinGW)
#include <string>
/** \class Fl_Terminal
@@ -89,7 +90,7 @@
- Printing text to the terminal, see Fl_Terminal::printf() and Fl_Terminal::append()
- Clearing the screen, see clear()
- Getting the terminal's buffer contents, see text()
- Getting single utf8 characters by row/col from the terminal display, see utf8_char_at_disp()
- Getting single UTF-8 characters by row/col from the terminal display, see utf8_char_at_disp()
- Getting the text from a text selection, see get_selection()
For applications that need input support, the widget can be subclassed to provide
@@ -842,7 +843,7 @@ public:
Fl_Scrollbar *hscrollbar; // horizontal scrollbar
private:
// Special utf8 symbols
const char *error_char_; // utf8 string shown for invalid utf8, bad ANSI, etc
std::string error_char_; // utf8 string shown for invalid utf8, bad ANSI, etc
bool fontsize_defer_; // flag defers font calcs until first draw() (issue 837)
int scrollbar_size_; // local preference for scrollbar size
ScrollbarStyle hscrollbar_style_;
@@ -1164,13 +1165,16 @@ public:
// API: Show unknown/invalid utf8/ANSI sequences with an error character (¿).
bool show_unknown(void) const;
void show_unknown(bool val);
/// Sets the "error character" utf8 string shown for invalid utf8
/// Sets the "error character" UTF-8 string shown for invalid UTF-8
/// or bad ANSI sequences if show_unknown() is true. Default: "¿".
/// This method internally makes a copy of the specified string.
/// \p val must be a valid null terminated UTF-8 string containing exactly one
/// character of at most four bytes, or undefined behavior will result.
/// \see show_unknown(bool)
void error_char(const char* val) { error_char_ = val; }
/// Returns the "error character" utf8 string, which is shown for invalid utf8
void error_char(const char* val) { error_char_ = val ? val : ""; }
/// Returns the "error character" UTF-8 string, which is shown for invalid UTF-8
/// or bad ANSI sequences if show_unknown() is true. \see show_unknown(bool)
const char* error_char(void) const { return error_char_; }
const char* error_char(void) const { return error_char_.c_str(); }
// API: ANSI sequences
bool ansi(void) const;
void ansi(bool val);
+98 -87
View File
@@ -2275,10 +2275,14 @@ void Fl_Terminal::delete_rows(int count) {
clear_mouse_selection();
}
// Repeat printing char 'c' for 'rep' times, not to exceed end of line.
// Repeat plotting char 'c' for 'rep' times, not to exceed end of line.
// Does not process control sequences or move the cursor.
void Fl_Terminal::repeat_char(char c, int rep) {
rep = clamp(rep, 1, disp_cols());
while ( rep-- > 0 && cursor_.col() < disp_cols() ) print_char(c);
const int row = cursor_.row();
const int col = cursor_.col();
rep = clamp(rep, 1, disp_cols() - col);
for (int n = 0; n < rep; n++)
plot_char(c, row, col + n);
}
/// Insert char 'c' for 'rep' times at display row \p 'drow' and column \p 'dcol'.
@@ -2590,12 +2594,13 @@ void Fl_Terminal::handle_lf(void) {
// Handle '\e' escape character.
void Fl_Terminal::handle_esc(void) {
if (!ansi_) // not in ansi mode?
{ handle_unknown_char(); return; } // ..show unknown char, early exit
if (escseq.esc_mode() == 0x1b) // already in esc mode?
{ handle_unknown_char(); } // ..show 1st esc as unknown char, parse 2nd
if (escseq.parse(0x1b) == EscapeSeq::fail) // parse esc
{ handle_unknown_char(); return; } // ..error? show unknown char
if (!ansi_) // not in ansi mode?
{ handle_unknown_char(); return; } // ..show unknown char, early exit
if (escseq.esc_mode() == 0x1b) // already in esc mode?
{ escseq.reset(); handle_unknown_char(); } // ..show first esc as unknown
if (escseq.parse(0x1b) == EscapeSeq::fail) // parse esc
{ escseq.reset(); handle_unknown_char(); return; } // ..error? reset, show error char
// successfully entered esc mode
}
/**
@@ -2640,7 +2645,10 @@ void Fl_Terminal::handle_ctrl(char c) {
case '\n': handle_lf(); return; // LF?
case '\t': cursor_tab_right(); return; // TAB?
case 0x1b: handle_esc(); return; // ESC?
default: handle_unknown_char(); return; // Unknown ctrl char?
default: // Unknown ctrl char?
if (ansi_) escseq.reset();
handle_unknown_char();
return;
}
}
@@ -2684,9 +2692,7 @@ void Fl_Terminal::handle_SGR(void) { // ESC[...m?
}
break;
case 1: if (val == 2) { rgbmode++; continue; } // '2'?
rgbcode = rgbmode = 0; // not '2'? cancel
handle_unknown_char();
break;
goto not_implemented; // not '2'? unsupported
case 2: r=clamp(val,0,255); ++rgbmode; continue; // parse red value
case 3: g=clamp(val,0,255); ++rgbmode; continue; // parse grn value
case 4: b=clamp(val,0,255); // parse blu value
@@ -2707,9 +2713,9 @@ void Fl_Terminal::handle_SGR(void) { // ESC[...m?
case 3: current_style_->sgr_italic(1); break; // ESC[3m - italic
case 4: current_style_->sgr_underline(1);break; // ESC[4m - underline
case 5: current_style_->sgr_blink(1); break; // ESC[5m - blink
case 6: handle_unknown_char(); break; // ESC[6m - (unused)
case 6: goto not_implemented; // ESC[6m - (unused)
case 7: current_style_->sgr_inverse(1); break; // ESC[7m - inverse
case 8: handle_unknown_char(); break; // ESC[8m - (unused)
case 8: goto not_implemented; // ESC[8m - (unused)
case 9: current_style_->sgr_strike(1); break; // ESC[9m - strikeout
}
} else if (val >= 21 && val <= 29) { // attribute extras
@@ -2720,9 +2726,9 @@ void Fl_Terminal::handle_SGR(void) { // ESC[...m?
case 23: current_style_->sgr_italic(0); break; // ESC[23m - disable italic
case 24: current_style_->sgr_underline(0);break; // ESC[24m - disable underline
case 25: current_style_->sgr_blink(0); break; // ESC[25m - disable blink
case 26: handle_unknown_char(); break; // ESC[26m - (unused)
case 26: goto not_implemented; // ESC[26m - (unused)
case 27: current_style_->sgr_inverse(0); break; // ESC[27m - disable inverse
case 28: handle_unknown_char(); break; // ESC[28m - disable hidden
case 28: goto not_implemented; // ESC[28m - disable hidden
case 29: current_style_->sgr_strike(0); break; // ESC[29m - disable strikeout
}
} else if (val >= 30 && val <= 37) { // Set fg color?
@@ -2737,10 +2743,15 @@ void Fl_Terminal::handle_SGR(void) { // ESC[...m?
} else if (val == 49) { // ESC[49m -- "normal" bg color:
Fl_Color bg = current_style_->defaultbgcolor(); // ..get default bg color
current_style_->bgcolor_xterm(bg); // ..set current bg color
} else {
handle_unknown_char(); // does an escseq.reset() // unimplemented SGR codes
} else { // unimplemented SGR codes
goto not_implemented;
}
}
if (!rgbmode) return; // RGB sequence complete?
not_implemented:
escseq.reset();
handle_unknown_char();
return;
}
/**
@@ -2771,7 +2782,7 @@ void Fl_Terminal::handle_escseq(char c) {
const bool no_scroll = false;
switch (escseq.parse(c)) { // parse char, advance s..
case EscapeSeq::fail: // failed?
escseq.reset(); // ..reset to let error_char be visible
escseq.reset(); // ..reset to ensure error_char emitted
handle_unknown_char(); // ..show error char (if enabled)
print_char(c); // ..show char we couldn't handle
return; // ..done.
@@ -2781,36 +2792,35 @@ void Fl_Terminal::handle_escseq(char c) {
break; // ..fall through to handle operation
}
// Shortcut varnames for escseq parsing..
EscapeSeq &esc = escseq;
char mode = esc.esc_mode();
int tot = esc.total_vals();
int val0 = (tot==0) ? 0 : esc.val(0);
int val1 = (tot<2) ? 0 : esc.val(1);
char mode = escseq.esc_mode();
int tot = escseq.total_vals();
int val0 = (tot==0) ? 0 : escseq.val(0);
int val1 = (tot<2) ? 0 : escseq.val(1);
const int& dw = disp_cols();
const int& dh = disp_rows();
if (esc.is_csi()) { // Was this a CSI (ESC[..) sequence?
if (escseq.is_csi()) { // Was this a CSI (ESC[..) sequence?
switch (mode) {
case '@': // <ESC>[#@ - (ICH) Insert blank Chars (default=1)
insert_char(' ', esc.defvalmax(1,dw));
insert_char(' ', escseq.defvalmax(1,dw));
break;
case 'A': // <ESC>[#A - (CUU) cursor up, no scroll/wrap
cursor_up(esc.defvalmax(1,dh));
cursor_up(escseq.defvalmax(1,dh));
break;
case 'B': // <ESC>[#B - (CUD) cursor down, no scroll/wrap
cursor_down(esc.defvalmax(1,dh), no_scroll);
cursor_down(escseq.defvalmax(1,dh), no_scroll);
break;
case 'C': // <ESC>[#C - (CUF) cursor right, no wrap
cursor_right(esc.defvalmax(1,dw), no_scroll);
cursor_right(escseq.defvalmax(1,dw), no_scroll);
break;
case 'D': // <ESC>[#D - (CUB) cursor left, no wrap
cursor_left(esc.defvalmax(1,dw));
cursor_left(escseq.defvalmax(1,dw));
break;
case 'E': // <ESC>[#E - (CNL) cursor next line (crlf) xterm, !gnome
cursor_crlf(esc.defvalmax(1,dh));
cursor_crlf(escseq.defvalmax(1,dh));
break;
case 'F': // <ESC>[#F - (CPL) move to sol and up # lines
cursor_cr();
cursor_up(esc.defvalmax(1,dh));
cursor_up(escseq.defvalmax(1,dh));
break;
case 'G': // <ESC>[#G - (CHA) cursor horizal absolute
switch (clamp(tot,0,1)) { // │
@@ -2873,24 +2883,24 @@ cup:
}
break;
case 'L': // ESC[#L - Insert # lines (def=1)
insert_rows(esc.defvalmax(1,dh));
insert_rows(escseq.defvalmax(1,dh));
break;
case 'M': // ESC[#M - Delete # lines (def=1)
delete_rows(esc.defvalmax(1,dh));
delete_rows(escseq.defvalmax(1,dh));
break;
case 'P': // ESC[#P - Delete # chars (def=1)
delete_chars(esc.defvalmax(1,dh));
delete_chars(escseq.defvalmax(1,dh));
break;
case 'S': // ESC[#S - scroll up # lines (def=1)
scroll( +(esc.defvalmax(1,dh)) );
scroll( +(escseq.defvalmax(1,dh)) );
// ⮤ positive=scroll up
break;
case 'T': // ESC[#T - scroll dn # lines (def=1)
scroll( -(esc.defvalmax(1,dh)) );
scroll( -(escseq.defvalmax(1,dh)) );
// ⮤ negative=scroll down
break;
case 'X': // <ESC>[#X - (ECH) Erase Characters (default=1)
repeat_char(' ', esc.defvalmax(1,dw));
repeat_char(' ', escseq.defvalmax(1,dw));
break;
case 'Z': // ESC[#Z - backtab # tabs
switch (clamp(tot,0,1)) { // │
@@ -2902,53 +2912,45 @@ cup:
break;
}
break;
case 'a': // TODO // ESC[#a - (HPR) move cursor relative [columns] (default=[row,col+1])
case 'b': // TODO // ESC[#b - (REP) repeat prev graphics char # times
case 'd': // TODO // ESC[#d - (VPA) line pos absolute [row]
case 'e': // TODO // ESC[#e - line pos relative [rows]
handle_unknown_char(); // does an escseq.reset()
break;
case 'a': goto not_implemented; // TODO // ESC[#a - (HPR) move cursor relative [columns] (default=[row,col+1])
case 'b': goto not_implemented; // TODO // ESC[#b - (REP) repeat prev graphics char # times
case 'd': goto not_implemented; // TODO // ESC[#d - (VPA) line pos absolute [row]
case 'e': goto not_implemented; // TODO // ESC[#e - line pos relative [rows]
case 'f': // <ESC>[#f - (CUP) cursor position (#'s 1 based)
goto cup; // (same as ESC[H)
case 'g': // ESC[...g? Tabulation Clear (TBC)
switch (val0) {
case 0: clear_tabstop(); break; // clears tabstop at cursor
case 3: clear_all_tabstops(); break; // clears all tabstops
default:
handle_unknown_char(); // does an escseq.reset()
break;
default: goto not_implemented;
}
break;
case 'm': handle_SGR(); break; // ESC[#m - set character attributes (SGR)
case 's': save_cursor(); break; // ESC[s - save cur pos (xterm+gnome)
case 'u': restore_cursor(); break; // ESC[u - restore cur pos (xterm+gnome)
case 'q': // TODO? // ESC[>#q set cursor style (block/line/blink..)
case 'r': // TODO // ESC[#;#r set scroll region top;bot
// default=full window
handle_unknown_char(); // does an escseq.reset()
break;
case 't': handle_DECRARA(); break; // ESC[#..$t -- (DECRARA)
// Reverse attribs in Rect Area (row,col)
default:
handle_unknown_char(); // does an escseq.reset()
break;
case 'q': goto not_implemented; // TODO? // ESC[>#q set cursor style (block/line/blink..)
case 'r': goto not_implemented; // TODO // ESC[#;#r set scroll region top;bot (default=full window)
case 't': handle_DECRARA(); break; // ESC[#..$t -- (DECRARA) Reverse attribs in Rect Area (row,col)
default: goto not_implemented;
}
} else {
// Not CSI? Might be C1 Control code (<ESC>D, etc)
switch (esc.esc_mode()) {
switch (escseq.esc_mode()) {
case 'c': reset_terminal(); break;// <ESC>c - Reset term to Initial State (RIS)
case 'D': cursor_down(1, do_scroll); break;// <ESC>D - down line, scroll at bottom
case 'E': cursor_crlf(); break;// <ESC>E - do a crlf
case 'H': set_tabstop(); break;// <ESC>H - set a tabstop
case 'M': cursor_up(1, true); break;// <ESC>M - (RI) Reverse Index (up w/scroll)
case '7': handle_unknown_char(); break;// <ESC>7 - Save cursor & attrs // TODO
case '8': handle_unknown_char(); break;// <ESC>8 - Restore cursor & attrs // TODO
case '7': goto not_implemented; // <ESC>7 - Save cursor & attrs // TODO
case '8': goto not_implemented; // <ESC>8 - Restore cursor & attrs // TODO
default:
handle_unknown_char(); // does an escseq.reset()
break;
not_implemented:
escseq.reset();
handle_unknown_char();
return;
}
}
esc.reset(); // done handling escseq, reset()
escseq.reset(); // done handling escseq, reset()
}
/**
@@ -3042,11 +3044,14 @@ const Fl_Terminal::Utf8Char* Fl_Terminal::utf8_char_at_glob(int grow, int gcol)
*/
void Fl_Terminal::plot_char(const char *text, int len, int drow, int dcol) {
Utf8Char *u8c = u8c_disp_row(drow) + dcol;
// text_utf8() warns we must do invalid checks first
if (!text || len<1 || len>u8c->max_utf8() || len!=fl_utf8len(*text)) {
if (!text || len<1) {
fail:
handle_unknown_char(drow, dcol);
return;
}
int u8len = fl_utf8len(*text); // -1 if invalid UTF-8
// text_utf8() warns we must do invalid checks first
if (len>u8c->max_utf8() || u8len<0 || u8len!=len) goto fail;
u8c->text_utf8(text, len, *current_style_);
}
@@ -3117,7 +3122,7 @@ void Fl_Terminal::print_char(const char *text, int len/*=-1*/) {
The character is displayed at the current cursor position
using the current text color/attributes.
- \p c must be ASCII, not utf-8
- \p c must be ASCII, not UTF-8
- Does not trigger redraws
*/
void Fl_Terminal::print_char(char c) {
@@ -3165,8 +3170,11 @@ void Fl_Terminal::append_utf8(const char *buf, int len/*=-1*/) {
//
if (pub_.buflen() > 0) { // partial UTF-8 to deal with?
while (len>0 && pub_.is_continuation(*buf)) { // buffer 'continuation' chars
if (pub_.append(buf, 1) == false) // append byte to partial UTF-8 buffer
{ mod |= handle_unknown_char(); break; } // overrun? break loop
if (pub_.append(buf, 1) == false) { // append to partial UTF-8 buffer. Overrun?
if (ansi_) escseq.reset(); // ..reset escseq
mod |= handle_unknown_char(); // ..show error char
break; // ..break loop
}
else { buf++; len--; } // shrink our buffer
}
if (pub_.is_complete()) utf8_cache_flush(); // complete UTF-8 captured? flush to tty
@@ -3177,19 +3185,20 @@ void Fl_Terminal::append_utf8(const char *buf, int len/*=-1*/) {
}
// For sure buf is now pointing at a valid char, so walk to end of buffer
int clen; // char length
const char *p = buf; // ptr to walk buffer
while (len>0) {
clen = fl_utf8len(*p); // how many bytes long is this char?
if (clen == -1) { // not expecting bad UTF-8 here
mod |= handle_unknown_char();
p += 1;
len -= 1;
const int clen = fl_utf8len(*p); // save byte length of char
if (clen == -1) { // Encountered invalid UTF-8?
if (ansi_) escseq.reset(); // ..reset escseq
mod |= handle_unknown_char(); // ..show err char
p += 1; // ..skip char
len -= 1; // ..adj len
} else {
if (len && clen>len) { // char longer than buffer?
if (pub_.append(p, len) == false) { // buffer it
mod |= handle_unknown_char();
utf8_cache_clear();
if (pub_.append(p, len) == false) { // buffer it. Fail?
if (ansi_) escseq.reset(); // ..reset escseq
mod |= handle_unknown_char(); // ..show err char
utf8_cache_clear(); // ..clear utf8 cache
}
break;
}
@@ -3290,13 +3299,16 @@ void Fl_Terminal::append(const char *s, int len/*=-1*/) {
This writes the "unknown" character to the output stream
if show_unknown() is true.
Note: This method uses print_char() to emit the error_char(); if ansi() is
enabled and an ansi sequence is in progress, the error_char() may be consumed
as part of the ANSI sequence. It is therefore best to call escseq.reset() first.
Returns 1 if tty modified, 0 if not.
\see show_unknown()
*/
int Fl_Terminal::handle_unknown_char(void) {
if (!show_unknown_) return 0;
escseq.reset(); // disable any pending esc seq to prevent eating unknown char
print_char(error_char_);
if (!show_unknown_ || error_char_.empty()) return 0;
print_char(error_char_.c_str());
return 1;
}
@@ -3311,10 +3323,10 @@ int Fl_Terminal::handle_unknown_char(void) {
\see show_unknown()
*/
int Fl_Terminal::handle_unknown_char(int drow, int dcol) {
if (!show_unknown_) return 0;
int len = (int)strlen(error_char_);
if (!show_unknown_ || error_char_.empty()) return 0;
int len = (int)error_char_.length();
Utf8Char *u8c = u8c_disp_row(drow) + dcol;
u8c->text_utf8(error_char_, len, *current_style_);
u8c->text_utf8(error_char_.c_str(), len, *current_style_);
return 1;
}
@@ -4098,10 +4110,10 @@ bool Fl_Terminal::show_unknown(void) const {
/**
Set the "show unknown" flag.
If true, invalid utf8 and invalid ANSI sequences will be shown
If true, invalid UTF-8 and invalid ANSI sequences will be shown
with the error character "¿".
If false, errors characters won't be shown.
If false, error characters won't be shown.
\see handle_unknown_char(), error_char(const char*).
*/
@@ -4191,4 +4203,3 @@ void Fl_Terminal::vprintf(const char *fmt, va_list ap) {
buffer[1024-1] = 0; // XXX: MICROSOFT
append(buffer);
}
+51 -27
View File
@@ -1,5 +1,5 @@
# data file for the Fltk User Interface Designer (fluid)
version 1.0500
version 1.050020
utf8_in_src
header_name {.h}
code_name {.cxx}
@@ -428,7 +428,7 @@ const char *test[] = {
" \\033[2;35m Dim Mag(35) [█]\\033[0m \\033[35m Normal Mag(35) [█]\\033[0m \\033[1;35m Bold Mag(35) [█]\\033[0m\\n"
" \\033[2;36m Dim Cyn(36) [█]\\033[0m \\033[36m Normal Cyn(36) [█]\\033[0m \\033[1;36m Bold Cyn(36) [█]\\033[0m\\n"
" \\033[2;37m Dim ---(37) [█]\\033[0m \\033[37m Normal ---(37) [█]\\033[0m \\033[1;37m Bold ---(37) [█]\\033[0m\\n"
" \\033[2;38m Dim ---(38) [█]\\033[0m \\033[38m Normal ---(38) [█]\\033[0m \\033[1;38m Bold ---(38) [█]\\033[0m\\n"
//" \\033[2;38m Dim ---(38) [█]\\033[0m \\033[38m Normal ---(38) [█]\\033[0m \\033[1;38m Bold ---(38) [█]\\033[0m\\n" // 38 is reserved
" \\033[2;39m Dim Def(39) [█]\\033[0m \\033[39m Normal Def(39) [█]\\033[0m \\033[1;39m Bold Def(39) [█]\\033[0m\\n"
"\\n"
"Xterm Fg/Bg Colors With Attributes\\n"
@@ -1189,7 +1189,7 @@ return show_test(test, index);} {}
} {
code {static int index = 0;
const char *test[] = {
// Screen 90
// Screen 0090
"\\033[2J\\033[H" "ESC TEST 0090: Scrolling up/down\\n",
"Line 2 🡅\\n",
"Line 3 [A]\\n",
@@ -1229,8 +1229,21 @@ const char *test[] = {
"\\033[H\\033M\\033M\\033M\\033[19H", // Line 18 because down 3 lines
//TODO Other ESC commands to scroll up/down?
"---> Hit 'e' for next test: \\033[K",
// Screen 100
"\\033[2J\\033[HESC TEST 0091: Cursor Save/Restore Test\\n",
"\\033[2J\\033[HESC TEST 0091: Handle Bad ANSI/UTF-8 Chars\\n",
"Should show the error char if show_unknown() is ON.\\n",
"\\n",
" Double escape ESC+ESC: -->\\033\\033<-- ESC+ESC is unknown escape sequence\\n",
" Bad escape sequence ESC(_): -->\\033_<-- ESC(_) is an unknown escape sequence\\n",
" Double Height Letters ESC\#3: -->\\033\#3<-- ESC\#3 is not supported\\n",
" Double Height Letters ESC\#4: -->\\033\#4<-- ESC\#4 is not supported\\n",
" Typo CSI ESC]m: -->\\033]m<-- ESC]m is a typo for ESC[m, not supported\\n",
" Bad UTF-8 bytes: 0xC0 >\\xC0<, 0xC1 >\\xC1\\n"
" 0xF5 >\\xF5<, 0xF6 >\\xF6<, 0xF7 >\\xF7<, 0xF8 >\\xF8<,\\n",
" 0xF9 >\\xF9<, 0xFA >\\xFA<, 0xFB >\\xFB<, 0xFC >\\xFC<,\\n",
" 0xFD >\\xFD<, 0xFE >\\xFE<, 0xFF >\\xFF<\\n",
"\\n",
"---> Hit 'e' for next test: \\033[K",
"\\033[2J\\033[HESC TEST 0092: Cursor Save/Restore Test\\n",
"\\n",
" Cursor positon at 'A' was saved with ESC[s.\\n",
"\\033[12;39H>\\033[1;31m\\033[s""A""\\033[0m<\\033[20H",
@@ -1540,6 +1553,11 @@ switch ( G_tty->box() ) {
outflags_lf_to_crlf->value((out&Fl_Terminal::LF_TO_CRLF) ? 1 : 0);
outflags_lf_to_cr->value( (out&Fl_Terminal::LF_TO_CR) ? 1 : 0);
outflags_cr_to_lf->value( (out&Fl_Terminal::CR_TO_LF) ? 1 : 0);
}
// error_char()
{
error_char_input->value(G_tty->error_char());
}} {}
}
Function {handle_output_translate(void)} {
@@ -1557,7 +1575,7 @@ G_tty->output_translate(Fl_Terminal::OutFlags(out));} {}
Fl_Window win {
label {Fl_Terminal Test}
callback {exit(0);} open
xywh {800 79 897 838} type Double size_range {897 330 0 0} visible
xywh {610 66 897 838} type Double size_range {897 330 0 0} visible
} {
Fl_Spinner scrollhistory_input {
label {Scroll History}
@@ -1703,39 +1721,39 @@ G_tty->redraw();}
Fl_Check_Button outflags_lf_to_crlf {
label LF_TO_CRLF
callback {handle_output_translate();}
tooltip {Line-feed (\\n) performs carriage-return and line-feed (CRLF)} xywh {333 7 77 24} down_box DOWN_BOX labelsize 9
tooltip {Line-feed (\\n) performs carriage-return and line-feed (CRLF)} xywh {331 5 77 20} down_box DOWN_BOX labelsize 9
}
Fl_Check_Button outflags_lf_to_cr {
label LF_TO_CR
callback {handle_output_translate();}
tooltip {Line-feed (\\n) performs carriage-return (\\r)} xywh {432 7 70 24} down_box DOWN_BOX labelsize 9
tooltip {Line-feed (\\n) performs carriage-return (\\r)} xywh {432 5 70 20} down_box DOWN_BOX labelsize 9
}
Fl_Check_Button outflags_cr_to_lf {
label CR_TO_LF
callback {handle_output_translate();}
tooltip {Carriage-return (\\r) performs line-feed (\\n)} xywh {528 7 70 24} down_box DOWN_BOX labelsize 9
tooltip {Carriage-return (\\r) performs line-feed (\\n)} xywh {528 5 70 20} down_box DOWN_BOX labelsize 9
}
Fl_Check_Button showunknown_radio {
label {show_unknown()}
callback {G_tty->show_unknown(showunknown_radio->value() ? true : false);
G_tty->redraw();}
tooltip {Shows unknown escape sequences/unprintable chars as "¿" character} xywh {331 31 105 24} down_box DOWN_BOX labelsize 9
tooltip {Shows unknown escape sequences/unprintable chars as "¿" character} xywh {331 25 105 20} down_box DOWN_BOX labelsize 9
}
Fl_Check_Button interactivecursor_radio {
label {Interactive Cursor}
callback {bool val = interactivecursor_radio->value() ? true : false;
G_tty->interactive_cursor(val);}
tooltip {Allow Up/Dn/Lt/Rt keys to move cursor
when terminal has focus} xywh {473 31 125 24} down_box DOWN_BOX labelsize 9
when terminal has focus} xywh {473 25 125 20} down_box DOWN_BOX labelsize 9
}
Fl_Check_Button ansi_radio {
label {ansi()}
callback {G_tty->ansi(ansi_radio->value() ? true : false);}
tooltip {Handle ANSI/xterm escape sequences} xywh {331 55 95 24} down_box DOWN_BOX labelsize 9
tooltip {Handle ANSI/xterm escape sequences} xywh {331 45 95 20} down_box DOWN_BOX labelsize 9
}
Fl_Check_Button stdout_radio {
label {Echo tests to stdout}
tooltip {Also send test output to stdout} xywh {473 55 125 24} down_box DOWN_BOX labelsize 9
tooltip {Also send test output to stdout} xywh {473 45 125 20} down_box DOWN_BOX labelsize 9
}
Fl_Input textcolor_input {
label {textcolor()}
@@ -1748,7 +1766,7 @@ G_tty->redraw();}
tooltip {The widget's text color. Has the effect of simultaneously setting:
> textfgcolor()
>textfgcolor_default()
Can be decimal (e.g. 12) or hex (e.g. \#0c, \#0000000c, etc)} xywh {333 79 77 20} labelsize 9 when 28 textfont 4 textsize 9
Can be decimal (e.g. 12) or hex (e.g. \#0c, \#0000000c, etc)} xywh {333 65 77 20} labelsize 9 when 28 textfont 4 textsize 9
}
Fl_Input textfgcolor_input {
label {textfgcolor()}
@@ -1759,7 +1777,7 @@ update_inputs();
//DEBUG ::printf("IVAL is %08lx\\n",ival);
G_tty->redraw();}
tooltip {The text foreground color.
Can be decimal (e.g. 12) or hex (e.g. \#0c, \#0000000c, etc)} xywh {333 103 77 20} labelsize 9 when 28 textfont 4 textsize 9
Can be decimal (e.g. 12) or hex (e.g. \#0c, \#0000000c, etc)} xywh {333 89 77 20} labelsize 9 when 28 textfont 4 textsize 9
}
Fl_Input textfgcolor_default_input {
label {textfgcolor_default()}
@@ -1770,7 +1788,7 @@ update_inputs();
//DEBUG ::printf("IVAL is %08lx\\n",ival);
G_tty->redraw();}
tooltip {The text foreground default color.
Can be decimal (e.g. 12) or hex (e.g. \#0c, \#0000000c, etc)} xywh {333 127 77 20} labelsize 9 when 28 textfont 4 textsize 9
Can be decimal (e.g. 12) or hex (e.g. \#0c, \#0000000c, etc)} xywh {333 113 77 20} labelsize 9 when 28 textfont 4 textsize 9
}
Fl_Input cursorfgcolor_input {
label {cursorfgcolor()}
@@ -1780,7 +1798,7 @@ G_tty->cursorfgcolor(c);
update_inputs();
//DEBUG ::printf("IVAL is %08lx\\n",ival);
G_tty->redraw();}
tooltip {Foreground color for text under the cursor.} xywh {333 151 77 20} labelsize 9 when 28 textfont 4 textsize 9
tooltip {Foreground color for text under the cursor.} xywh {333 137 77 20} labelsize 9 when 28 textfont 4 textsize 9
}
Fl_Input selectionfgcolor_input {
label {selectionfgcolor()}
@@ -1791,7 +1809,13 @@ update_inputs();
//DEBUG ::printf("IVAL is %08lx\\n",ival);
G_tty->redraw();}
tooltip {The mouse selection foreground color.
Can be decimal (e.g. 12) or hex (e.g. \#0c, \#0000000c, etc)} xywh {333 175 77 20} labelsize 9 when 28 textfont 4 textsize 9
Can be decimal (e.g. 12) or hex (e.g. \#0c, \#0000000c, etc)} xywh {333 161 77 20} labelsize 9 when 28 textfont 4 textsize 9
}
Fl_Input error_char_input {
label {error_char()}
callback {G_tty->error_char(error_char_input->value());
G_tty->redraw();} selected
tooltip {The error character shown for bad UTF-8/ANSI sequences if show_unknown() is enabled.} xywh {333 184 77 20} labelsize 9 when 28 textfont 4 textsize 9
}
Fl_Input color_input {
label {color()}
@@ -1802,7 +1826,7 @@ update_inputs();
//DEBUG ::printf("IVAL is %08lx\\n",ival);
G_tty->redraw();}
tooltip {The widget's background color().
Can be decimal (e.g. 12) or hex (e.g. \#0c, \#0000000c, etc)} xywh {521 79 77 20} labelsize 9 when 28 textfont 4 textsize 9
Can be decimal (e.g. 12) or hex (e.g. \#0c, \#0000000c, etc)} xywh {521 65 77 20} labelsize 9 when 28 textfont 4 textsize 9
}
Fl_Input textbgcolor_input {
label {textbgcolor()}
@@ -1814,7 +1838,7 @@ update_inputs();
G_tty->redraw();}
tooltip {The text background color.
Refer to the docs for the special value 0xffffffff.
Can be decimal (e.g. 12) or hex (e.g. \#0c, \#0000000c, etc)} xywh {521 103 77 20} labelsize 9 when 28 textfont 4 textsize 9
Can be decimal (e.g. 12) or hex (e.g. \#0c, \#0000000c, etc)} xywh {521 89 77 20} labelsize 9 when 28 textfont 4 textsize 9
}
Fl_Input textbgcolor_default_input {
label {textbgcolor_default()}
@@ -1826,7 +1850,7 @@ update_inputs();
G_tty->redraw();}
tooltip {The text background default color.
Refer to the docs for the special value 0xffffffff.
Can be decimal (e.g. 12) or hex (e.g. \#0c, \#0000000c, etc)} xywh {521 127 77 20} labelsize 9 when 28 textfont 4 textsize 9
Can be decimal (e.g. 12) or hex (e.g. \#0c, \#0000000c, etc)} xywh {521 113 77 20} labelsize 9 when 28 textfont 4 textsize 9
}
Fl_Input cursorbgcolor_input {
label {cursorbgcolor()}
@@ -1837,7 +1861,7 @@ update_inputs();
//DEBUG ::printf("IVAL is %08lx\\n",ival);
G_tty->redraw();}
tooltip {Background color for the cursor.
This is the cursor block's color} xywh {521 151 77 20} labelsize 9 when 28 textfont 4 textsize 9
This is the cursor block's color} xywh {521 137 77 20} labelsize 9 when 28 textfont 4 textsize 9
}
Fl_Input selectionbgcolor_input {
label {selectionbgcolor()}
@@ -1848,11 +1872,11 @@ update_inputs();
//DEBUG ::printf("IVAL is %08lx\\n",ival);
G_tty->redraw();}
tooltip {The mouse selection background color.
Can be decimal (e.g. 12) or hex (e.g. \#0c, \#0000000c, etc)} xywh {521 175 77 20} labelsize 9 when 28 textfont 4 textsize 9
Can be decimal (e.g. 12) or hex (e.g. \#0c, \#0000000c, etc)} xywh {521 161 77 20} labelsize 9 when 28 textfont 4 textsize 9
}
Fl_Choice {} {
label {Terminal Color} open
tooltip {The (XTERM) colors can be influenced by Dim/Bold, whereas the (RGB) colors are not.} xywh {379 206 180 20} down_box BORDER_BOX labelsize 9 textsize 9
tooltip {The (XTERM) colors can be influenced by Dim/Bold, whereas the (RGB) colors are not.} xywh {379 211 180 20} down_box BORDER_BOX labelsize 9 textsize 9
} {
MenuItem {} {
label {White on DarkAmber (XTERM)}
@@ -2009,7 +2033,7 @@ Moves what was on the screen to the scroll history.} xywh {616 138 64 24} labels
Fl_Input clear_color_input {
tooltip {The color used by "Clear Screen To Color" button
Can be decimal (e.g. 12) or hex (e.g. \#0c, \#ff000000, etc)} xywh {686 138 64 24} labelsize 9 when 28 textfont 4 textsize 9
code0 {clear_color_input->value("\#ff000000");}
code3 {clear_color_input->value("\#ff000000");}
}
}
Fl_Button {} {
@@ -2112,6 +2136,7 @@ The command's stdout will appear in terminal.} xywh {109 269 773 25} labelsize 1
G_tty = tty; // global access
G_tty->color(0x30200000); // dark orange (makes true black easy to see)
G_tty->textsize(12); // smaller text (RGB demo needs this)
G_tty->show_unknown(1); // on by default for tests
win->resizable(tty);
command_input->when(FL_WHEN_ENTER_KEY_ALWAYS);
@@ -2126,8 +2151,7 @@ command_input->value("ls -la --color && ping google.com -c 5");
\#endif
// Sync input vals with widget's current values
update_inputs();} {selected
}
update_inputs();} {}
}
}