mirror of
https://github.com/fltk/fltk.git
synced 2026-08-18 08:58:25 +08:00
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:
+10
-6
@@ -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
@@ -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
@@ -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();} {}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user