mirror of
https://github.com/genodelabs/genode.git
synced 2025-04-11 21:32:57 +00:00
Terminal: improved handling of escape sequences
Add additional parsing modes to the sequence decoder to detect and discard unhandled sequences for ECMA-48, DEC private, and Xterm. Add new behavior for cursor movement, cursor hiding, character deletion, and line-wrapping. Fix #2923
This commit is contained in:
parent
f4ea50c6ff
commit
2041f957da
@ -67,9 +67,12 @@ class Char_cell_array_character_screen : public Terminal::Character_screen
|
||||
CURSOR_VISIBLE,
|
||||
CURSOR_VERY_VISIBLE };
|
||||
|
||||
enum Irm { REPLACE, INSERT };
|
||||
|
||||
Cell_array<Char_cell> &_char_cell_array;
|
||||
Terminal::Boundary _boundary;
|
||||
Terminal::Position _cursor_pos { };
|
||||
Terminal::Position _cursor_store { };
|
||||
Terminal::Position _cursor_pos { };
|
||||
|
||||
/**
|
||||
* Color index contains the fg color in the first 3 bits
|
||||
@ -83,6 +86,10 @@ class Char_cell_array_character_screen : public Terminal::Character_screen
|
||||
int _region_end;
|
||||
int _tab_size;
|
||||
|
||||
Irm _irm = REPLACE;
|
||||
|
||||
bool _wrap = false;
|
||||
|
||||
enum { DEFAULT_COLOR_INDEX_BG = 0, DEFAULT_COLOR_INDEX = 7, DEFAULT_TAB_SIZE = 8 };
|
||||
|
||||
struct Cursor_guard
|
||||
@ -107,9 +114,9 @@ class Char_cell_array_character_screen : public Terminal::Character_screen
|
||||
/* if cursor position changed, move cursor */
|
||||
Terminal::Position &new_cursor_pos = cs._cursor_pos;
|
||||
if (old_cursor_pos != new_cursor_pos) {
|
||||
|
||||
cs._char_cell_array.cursor(old_cursor_pos, false, true);
|
||||
cs._char_cell_array.cursor(new_cursor_pos, true, true);
|
||||
cs._char_cell_array.cursor(
|
||||
new_cursor_pos, cs._cursor_visibility != CURSOR_INVISIBLE, true);
|
||||
}
|
||||
}
|
||||
};
|
||||
@ -119,10 +126,16 @@ class Char_cell_array_character_screen : public Terminal::Character_screen
|
||||
Genode::warning(method_name, " not implemented");
|
||||
}
|
||||
|
||||
void _line_feed()
|
||||
static void _missing(char const *method_name, int arg)
|
||||
{
|
||||
Genode::warning(method_name, " not implemented for ", arg);
|
||||
}
|
||||
|
||||
void _new_line()
|
||||
{
|
||||
Cursor_guard guard(*this);
|
||||
|
||||
_cursor_pos.x = 0;
|
||||
_cursor_pos.y++;
|
||||
|
||||
if (_cursor_pos.y > _region_end) {
|
||||
@ -168,24 +181,21 @@ class Char_cell_array_character_screen : public Terminal::Character_screen
|
||||
|
||||
void output(Terminal::Character c)
|
||||
{
|
||||
if (c.ascii() > 0x10) {
|
||||
Cursor_guard guard(*this);
|
||||
_char_cell_array.set_cell(_cursor_pos.x, _cursor_pos.y,
|
||||
Char_cell(c.ascii(), Font_face::REGULAR,
|
||||
_color_index, _inverse, _highlight));
|
||||
_cursor_pos.x++;
|
||||
}
|
||||
if (_irm == INSERT)
|
||||
_missing("insert mode");
|
||||
|
||||
switch (c.ascii()) {
|
||||
|
||||
case '\n': /* 10 */
|
||||
_new_line();
|
||||
break;
|
||||
|
||||
case '\r': /* 13 */
|
||||
_carriage_return();
|
||||
break;
|
||||
|
||||
case '\n': /* 10 */
|
||||
_line_feed();
|
||||
_carriage_return();
|
||||
break;
|
||||
/* 14: shift-out */
|
||||
/* 15: shift-in */
|
||||
|
||||
case 8: /* backspace */
|
||||
{
|
||||
@ -205,30 +215,47 @@ class Char_cell_array_character_screen : public Terminal::Character_screen
|
||||
}
|
||||
|
||||
default:
|
||||
if (0x1f < c.ascii() && c.ascii() < 0x7f) {
|
||||
Cursor_guard guard(*this);
|
||||
_char_cell_array.set_cell(_cursor_pos.x, _cursor_pos.y,
|
||||
Char_cell(c.ascii(), Font_face::REGULAR,
|
||||
_color_index, _inverse, _highlight));
|
||||
_cursor_pos.x++;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
if (_cursor_pos.x >= _boundary.width) {
|
||||
_carriage_return();
|
||||
_line_feed();
|
||||
if (_wrap) {
|
||||
_new_line();
|
||||
} else {
|
||||
_cursor_pos.x = _boundary.width-1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void cbt() override { _missing(__func__); }
|
||||
void cha(int pn) override
|
||||
{
|
||||
Cursor_guard guard(*this);
|
||||
_cursor_pos.x = Genode::max(pn - 1, _boundary.width);
|
||||
}
|
||||
|
||||
void civis() override
|
||||
{
|
||||
_cursor_visibility = CURSOR_INVISIBLE;
|
||||
_char_cell_array.cursor(_cursor_pos, false);
|
||||
}
|
||||
|
||||
void cnorm() override
|
||||
{
|
||||
_cursor_visibility = CURSOR_VISIBLE;
|
||||
_char_cell_array.cursor(_cursor_pos, true);
|
||||
}
|
||||
|
||||
void cvvis() override
|
||||
{
|
||||
_cursor_visibility = CURSOR_VERY_VISIBLE;
|
||||
_char_cell_array.cursor(_cursor_pos, true);
|
||||
}
|
||||
|
||||
void csr(int start, int end) override
|
||||
@ -252,6 +279,14 @@ class Char_cell_array_character_screen : public Terminal::Character_screen
|
||||
_cursor_pos.x = Genode::max(0, _cursor_pos.x);
|
||||
}
|
||||
|
||||
void cud(int dy) override
|
||||
{
|
||||
Cursor_guard guard(*this);
|
||||
|
||||
_cursor_pos.y += dy;
|
||||
_cursor_pos.y = Genode::min(_boundary.height - 1, _cursor_pos.y);
|
||||
}
|
||||
|
||||
void cuf(int dx) override
|
||||
{
|
||||
Cursor_guard guard(*this);
|
||||
@ -275,10 +310,27 @@ class Char_cell_array_character_screen : public Terminal::Character_screen
|
||||
_cursor_pos = Terminal::Position(x, y);
|
||||
}
|
||||
|
||||
void cud(int) override { _missing(__func__); }
|
||||
void cuu1() override { _missing(__func__); }
|
||||
void cuu(int) override { _missing(__func__); }
|
||||
void dch(int) override { _missing(__func__); }
|
||||
void cuu(int dy) override
|
||||
{
|
||||
Cursor_guard guard(*this);
|
||||
|
||||
_cursor_pos.x += dy;
|
||||
_cursor_pos.y = Genode::max(0, _cursor_pos.y);
|
||||
}
|
||||
|
||||
void da(int) override { _missing(__func__); }
|
||||
|
||||
void dch(int pn) override
|
||||
{
|
||||
pn = Genode::min(_boundary.width - _cursor_pos.x, pn);
|
||||
for (int x = _cursor_pos.x; x < _boundary.width; ++x) {
|
||||
_char_cell_array.set_cell(x, _cursor_pos.y,
|
||||
_char_cell_array.get_cell(x+pn, _cursor_pos.y));
|
||||
}
|
||||
for (int x = _boundary.width - pn; x < _boundary.width; ++x) {
|
||||
_char_cell_array.set_cell(x, _cursor_pos.y, Char_cell());
|
||||
}
|
||||
}
|
||||
|
||||
void dl(int num_lines) override
|
||||
{
|
||||
@ -287,21 +339,75 @@ class Char_cell_array_character_screen : public Terminal::Character_screen
|
||||
_char_cell_array.scroll_up(_cursor_pos.y, _region_end);
|
||||
}
|
||||
|
||||
void ed() override
|
||||
/**
|
||||
* Erase character
|
||||
*/
|
||||
void ech(int pn)
|
||||
{
|
||||
int y = _cursor_pos.y;
|
||||
int x = _cursor_pos.x;
|
||||
|
||||
do {
|
||||
while (x < _boundary.width && pn) {
|
||||
_char_cell_array.set_cell(x++, y, Char_cell());
|
||||
--pn;
|
||||
}
|
||||
x = 0;
|
||||
++y;
|
||||
} while (pn);
|
||||
}
|
||||
|
||||
/**
|
||||
* Erase in page
|
||||
*/
|
||||
void ed(int ps) override
|
||||
{
|
||||
/* clear to end of screen */
|
||||
el();
|
||||
_char_cell_array.clear(_cursor_pos.y + 1, _boundary.height - 1);
|
||||
switch(ps) {
|
||||
|
||||
case 0:
|
||||
for (int x = _cursor_pos.x; x < _boundary.width; ++x)
|
||||
_char_cell_array.set_cell(x, _cursor_pos.y, Char_cell());
|
||||
_char_cell_array.clear(_cursor_pos.y + 1, _boundary.height-1);
|
||||
return;
|
||||
|
||||
case 1:
|
||||
_char_cell_array.clear(0, _cursor_pos.y-1);
|
||||
for (int x = 0; x <= _cursor_pos.x; ++x)
|
||||
_char_cell_array.set_cell(x, _cursor_pos.y, Char_cell());
|
||||
return;
|
||||
|
||||
case 2:
|
||||
_char_cell_array.clear(0, _boundary.height-1);
|
||||
return;
|
||||
|
||||
default:
|
||||
Genode::warning(__func__, " not implemented for ", ps);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void el() override
|
||||
void el(int ps) override
|
||||
{
|
||||
/* clear to end of line */
|
||||
for (int x = _cursor_pos.x; x < _boundary.width; x++)
|
||||
_char_cell_array.set_cell(x, _cursor_pos.y, Char_cell());
|
||||
switch (ps) {
|
||||
case 0: /* clear to end of line */
|
||||
for (int x = _cursor_pos.x; x < _boundary.width; ++x)
|
||||
_char_cell_array.set_cell(x, _cursor_pos.y, Char_cell());
|
||||
return;
|
||||
|
||||
case 1: /* clear from begining of line */
|
||||
for (int x = 0; x <= _cursor_pos.x; ++x)
|
||||
_char_cell_array.set_cell(x, _cursor_pos.y, Char_cell());
|
||||
return;
|
||||
|
||||
case 2:
|
||||
_char_cell_array.clear(_cursor_pos.y, _cursor_pos.y);
|
||||
return;
|
||||
|
||||
default: _missing(__func__, ps);
|
||||
}
|
||||
}
|
||||
|
||||
void el1() override { _missing(__func__); }
|
||||
void enacs() override { _missing(__func__); }
|
||||
void flash() override { _missing(__func__); }
|
||||
|
||||
@ -309,11 +415,27 @@ class Char_cell_array_character_screen : public Terminal::Character_screen
|
||||
{
|
||||
Cursor_guard guard(*this);
|
||||
|
||||
_cursor_pos = Terminal::Position(0, 0);
|
||||
_cursor_pos.x = 0;
|
||||
}
|
||||
|
||||
void hts() override { _missing(__func__); }
|
||||
void ich(int) override { _missing(__func__); }
|
||||
void hts() override
|
||||
{
|
||||
_tab_size = _cursor_pos.x;
|
||||
}
|
||||
|
||||
void ich(int pn) override
|
||||
{
|
||||
pn = Genode::min(_boundary.width - _cursor_pos.x, pn);
|
||||
|
||||
for (int x = _boundary.width-1; _cursor_pos.x+pn < x; --x) {
|
||||
_char_cell_array.set_cell(x, _cursor_pos.y,
|
||||
_char_cell_array.get_cell(x-1, _cursor_pos.y));
|
||||
}
|
||||
for (int i = 0; i < pn; ++i) {
|
||||
_char_cell_array.set_cell(
|
||||
_cursor_pos.x+i, _cursor_pos.y, Char_cell());
|
||||
}
|
||||
}
|
||||
|
||||
void il(int value) override
|
||||
{
|
||||
@ -338,12 +460,47 @@ class Char_cell_array_character_screen : public Terminal::Character_screen
|
||||
_color_index = DEFAULT_COLOR_INDEX | (DEFAULT_COLOR_INDEX_BG << 3);
|
||||
}
|
||||
|
||||
void rm(int ps) override
|
||||
{
|
||||
switch (ps) {
|
||||
case 4: /* INSERTION REPLACEMENT MODE */
|
||||
_irm = REPLACE;
|
||||
break;
|
||||
case 34: /* cursor visibility */
|
||||
return cnorm();
|
||||
default:
|
||||
_missing(__func__, ps);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void sm(int ps) override
|
||||
{
|
||||
switch (ps) {
|
||||
case 4: /* INSERTION REPLACEMENT MODE */
|
||||
_irm = INSERT;
|
||||
break;
|
||||
case 34: /* cursor visibility */
|
||||
return civis();
|
||||
break;
|
||||
default:
|
||||
_missing(__func__, ps);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void rc() override { _missing(__func__); }
|
||||
void rs2() override { _missing(__func__); }
|
||||
void rmir() override { _missing(__func__); }
|
||||
void rmcup() override { }
|
||||
void rmkx() override { }
|
||||
|
||||
void sd(int pn) override
|
||||
{
|
||||
for (int i = 0; i < pn; ++i)
|
||||
_char_cell_array.scroll_down(_region_start, _region_end);
|
||||
}
|
||||
|
||||
void setab(int value) override
|
||||
{
|
||||
_color_index &= ~0x38; /* clear 111000 */
|
||||
@ -376,13 +533,99 @@ class Char_cell_array_character_screen : public Terminal::Character_screen
|
||||
}
|
||||
}
|
||||
|
||||
void sgr0() override { sgr(0); }
|
||||
|
||||
void sc() override { _missing(__func__); }
|
||||
void smcup() override { }
|
||||
void smir() override { _missing(__func__); }
|
||||
void smkx() override { }
|
||||
void tbc() override { _missing(__func__); }
|
||||
|
||||
void su(int pn) override
|
||||
{
|
||||
for (int i = 0; i < pn; ++i)
|
||||
_char_cell_array.scroll_up(_region_start, _region_end);
|
||||
}
|
||||
|
||||
void tbc() override { _missing(__func__); }
|
||||
|
||||
void tsr(int pn) override
|
||||
{
|
||||
_missing(__func__, pn);
|
||||
/*
|
||||
int x = pn;
|
||||
for (int y = _cursor_pos.y; y < _boundary.height-1; ++y) {
|
||||
for (int i = 0; i < _tab_size; ++i) {
|
||||
|
||||
}
|
||||
}
|
||||
*/
|
||||
}
|
||||
|
||||
void vpa(int pn)
|
||||
{
|
||||
Cursor_guard guard(*this);
|
||||
_cursor_pos.x = pn;
|
||||
}
|
||||
|
||||
void vpb(int pn)
|
||||
{
|
||||
Cursor_guard guard(*this);
|
||||
_cursor_pos.x = Genode::min(0, _cursor_pos.x - pn);
|
||||
}
|
||||
|
||||
void decsc() override
|
||||
{
|
||||
_cursor_store = _cursor_pos;
|
||||
}
|
||||
|
||||
void decrc() override
|
||||
{
|
||||
Cursor_guard guard(*this);
|
||||
_cursor_pos = _cursor_store;
|
||||
}
|
||||
|
||||
void decsm(int p1, int) override
|
||||
{
|
||||
switch (p1) {
|
||||
case 1: _missing("Application Cursor Keys"); return; //return smkx();
|
||||
case 7: _wrap = false; return;
|
||||
case 25:
|
||||
case 34: return cnorm(); // Visible Cursor
|
||||
case 1000: return _missing("VT200 mouse tracking");
|
||||
case 1002: return _missing("xterm butten event mouse");
|
||||
case 1003: return _missing("xterm any event mouse");
|
||||
case 1049: return _missing("Alternate Screen (new xterm code)"); //smcup();
|
||||
default: break;
|
||||
}
|
||||
_missing(__func__, p1);
|
||||
}
|
||||
|
||||
void decrm(int p1, int) override
|
||||
{
|
||||
switch (p1) {
|
||||
case 1: _missing("Application Cursor Keys"); return; //return rmkx();
|
||||
case 7: _wrap = true; return;
|
||||
case 25:
|
||||
case 34: return civis();
|
||||
case 1000: return _missing("VT200 mouse tracking"); //rs2();
|
||||
case 1002: return _missing("xterm butten event mouse");
|
||||
case 1003: return _missing("xterm any event mouse");
|
||||
case 1049: return _missing("Alternate Screen (new xterm code)"); //rmcup();
|
||||
default: break;
|
||||
}
|
||||
_missing(__func__, p1);
|
||||
}
|
||||
|
||||
void scs_g0(int charset) override { _missing(__func__, charset); }
|
||||
|
||||
void scs_g1(int charset) override { _missing(__func__, charset); }
|
||||
|
||||
void reverse_index()
|
||||
{
|
||||
Cursor_guard guard(*this);
|
||||
if (_cursor_pos.y) {
|
||||
_cursor_pos.y = _cursor_pos.y - 1;
|
||||
} else {
|
||||
_char_cell_array.scroll_down(_region_start, _region_end);
|
||||
}
|
||||
};
|
||||
};
|
||||
|
||||
#endif /* _TERMINAL__CHAR_CELL_ARRAY_CHARACTER_SCREEN_H_ */
|
||||
|
@ -26,7 +26,6 @@ struct Terminal::Character_screen : Genode::Interface
|
||||
{
|
||||
virtual void output(Character c) = 0;
|
||||
|
||||
|
||||
/*******************
|
||||
** VT Operations **
|
||||
*******************/
|
||||
@ -38,9 +37,9 @@ struct Terminal::Character_screen : Genode::Interface
|
||||
*/
|
||||
|
||||
/**
|
||||
* Back tab
|
||||
* Cursor Character Absolute - 8.3.9
|
||||
*/
|
||||
virtual void cbt() = 0;
|
||||
virtual void cha(int pn = 1) = 0;
|
||||
|
||||
/**
|
||||
* Make cursor invisible
|
||||
@ -65,12 +64,12 @@ struct Terminal::Character_screen : Genode::Interface
|
||||
/**
|
||||
* Move cursor backwards
|
||||
*/
|
||||
virtual void cub(int) = 0;
|
||||
virtual void cub(int pn = 1) = 0;
|
||||
|
||||
/**
|
||||
* Non-destructive space - move right #1 spaces
|
||||
* Cursor right - 8.3.20
|
||||
*/
|
||||
virtual void cuf(int) = 0;
|
||||
virtual void cuf(int pn = 1) = 0;
|
||||
|
||||
/**
|
||||
* Move cursor to row #1 column #2
|
||||
@ -78,44 +77,45 @@ struct Terminal::Character_screen : Genode::Interface
|
||||
virtual void cup(int, int) = 0;
|
||||
|
||||
/**
|
||||
* Down #1 lines
|
||||
* Cursor Down - 8.3.19
|
||||
*/
|
||||
virtual void cud(int) = 0;
|
||||
virtual void cud(int pn = 1) = 0;
|
||||
|
||||
/**
|
||||
* Move cursor up one line
|
||||
* Cursor Up - 8.3.22
|
||||
*/
|
||||
virtual void cuu1() = 0;
|
||||
virtual void cuu(int pn = 1) = 0;
|
||||
|
||||
/**
|
||||
* Up #1 lines
|
||||
* Device Attributes - 8.3.24
|
||||
*/
|
||||
virtual void cuu(int) = 0;
|
||||
virtual void da(int ps = 0) = 0;
|
||||
|
||||
/**
|
||||
* Delete #1 characters
|
||||
* Delete Character - 8.3.26
|
||||
*/
|
||||
virtual void dch(int) = 0;
|
||||
virtual void dch(int pn = 1) = 0;
|
||||
|
||||
/**
|
||||
* Delete #1 lines
|
||||
* Delete line - 8.3.32
|
||||
*/
|
||||
virtual void dl(int) = 0;
|
||||
virtual void dl(int pn = 1) = 0;
|
||||
|
||||
|
||||
/**
|
||||
* Clear to end of screen
|
||||
* Erase Character - 8.3.38
|
||||
*/
|
||||
virtual void ed() = 0;
|
||||
virtual void ech(int pn = 1) = 0;
|
||||
|
||||
/**
|
||||
* Clear to end of line
|
||||
* Erase in page - 8.3.39
|
||||
*/
|
||||
virtual void el() = 0;
|
||||
virtual void ed(int ps = 0) = 0;
|
||||
|
||||
/**
|
||||
* Clear to beginning of line
|
||||
* Erase in line - 8.3.41
|
||||
*/
|
||||
virtual void el1() = 0;
|
||||
virtual void el(int ps = 0) = 0;
|
||||
|
||||
/**
|
||||
* Enable alternative character set
|
||||
@ -133,19 +133,19 @@ struct Terminal::Character_screen : Genode::Interface
|
||||
virtual void home() = 0;
|
||||
|
||||
/**
|
||||
* Set a tab in every row, current column
|
||||
* Set a tab in every row, current column - 8.3.62
|
||||
*/
|
||||
virtual void hts() = 0;
|
||||
|
||||
/**
|
||||
* Insert #1 characters
|
||||
* Insert character - 8.3.64
|
||||
*/
|
||||
virtual void ich(int) = 0;
|
||||
virtual void ich(int pn = 1) = 0;
|
||||
|
||||
/**
|
||||
* Insert #1 lines
|
||||
* Insert line - 8.3.67
|
||||
*/
|
||||
virtual void il(int) = 0;
|
||||
virtual void il(int pn = 1) = 0;
|
||||
|
||||
/**
|
||||
* Initialization string
|
||||
@ -167,6 +167,11 @@ struct Terminal::Character_screen : Genode::Interface
|
||||
*/
|
||||
virtual void rc() = 0;
|
||||
|
||||
/**
|
||||
* Reset Mode - 8.3.106
|
||||
*/
|
||||
virtual void rm(int) = 0;
|
||||
|
||||
/**
|
||||
* Reset string
|
||||
*/
|
||||
@ -187,6 +192,11 @@ struct Terminal::Character_screen : Genode::Interface
|
||||
*/
|
||||
virtual void rmkx() = 0;
|
||||
|
||||
/**
|
||||
* Scroll Down - 8.3.113
|
||||
*/
|
||||
virtual void sd(int pn = 1) = 0;
|
||||
|
||||
/**
|
||||
* Set background color to #1, using ANSI escape
|
||||
*/
|
||||
@ -198,19 +208,19 @@ struct Terminal::Character_screen : Genode::Interface
|
||||
virtual void setaf(int) = 0;
|
||||
|
||||
/**
|
||||
* Set attribute
|
||||
* Select Graphic Rendition - 8.3.117
|
||||
*/
|
||||
virtual void sgr(int) = 0;
|
||||
virtual void sgr(int ps = 0) = 0;
|
||||
|
||||
/**
|
||||
* Turn of all attributes
|
||||
* Set mode 8.3.125
|
||||
*/
|
||||
virtual void sgr0() = 0;
|
||||
virtual void sm(int) = 0;
|
||||
|
||||
/**
|
||||
* Save current cursor position
|
||||
* Scroll Up - 8.3.147
|
||||
*/
|
||||
virtual void sc() = 0;
|
||||
virtual void su(int pn = 1) = 0;
|
||||
|
||||
/**
|
||||
* Enter cup mode
|
||||
@ -231,6 +241,59 @@ struct Terminal::Character_screen : Genode::Interface
|
||||
* Clear all tab stops
|
||||
*/
|
||||
virtual void tbc() = 0;
|
||||
|
||||
/**
|
||||
* Tabulation Stop Remove - 8.3.156
|
||||
*/
|
||||
virtual void tsr(int) = 0;
|
||||
|
||||
/**
|
||||
* Line position absolute - 8.3.158
|
||||
*/
|
||||
virtual void vpa(int pn = 1) = 0;
|
||||
|
||||
/**
|
||||
* Line position backward - 8.3.159
|
||||
*/
|
||||
virtual void vpb(int pn = 1) = 0;
|
||||
|
||||
/*****************
|
||||
** DEC private **
|
||||
*****************/
|
||||
|
||||
/**
|
||||
* Save Cursor
|
||||
*/
|
||||
virtual void decsc() = 0;
|
||||
|
||||
/**
|
||||
* Restore Cursor
|
||||
*/
|
||||
virtual void decrc() = 0;
|
||||
|
||||
/**
|
||||
* Set mode
|
||||
*/
|
||||
virtual void decsm(int p1, int p2 = 0) = 0;
|
||||
|
||||
/**
|
||||
* Reset mode
|
||||
*/
|
||||
virtual void decrm(int p1, int p2 = 0) = 0;
|
||||
|
||||
|
||||
/**************************
|
||||
** Select Character Set **
|
||||
**************************/
|
||||
|
||||
virtual void scs_g0(int) = 0;
|
||||
virtual void scs_g1(int) = 0;
|
||||
|
||||
/*************
|
||||
** Unknown **
|
||||
*************/
|
||||
|
||||
virtual void reverse_index() = 0;
|
||||
};
|
||||
|
||||
#endif /* _TERMINAL__CHARACTER_SCREEN_H_ */
|
||||
|
@ -15,10 +15,10 @@
|
||||
#define _TERMINAL__DECODER_H_
|
||||
|
||||
#include <terminal/character_screen.h>
|
||||
#include <terminal/print.h>
|
||||
|
||||
namespace Terminal { class Decoder; }
|
||||
|
||||
|
||||
class Terminal::Decoder
|
||||
{
|
||||
private:
|
||||
@ -61,11 +61,24 @@ class Terminal::Decoder
|
||||
return number % factor;
|
||||
}
|
||||
|
||||
enum State {
|
||||
STATE_IDLE,
|
||||
STATE_ESC_CSI, /* read CONTROL SEQUENCE INTRODUCER */
|
||||
STATE_ESC_ECMA, /* read an ECMA-48 escape sequence */
|
||||
STATE_ESC_SCS, /* read an Select Character Set sequence */
|
||||
STATE_ESC_VT100, /* read a VT100 escape sequence */
|
||||
STATE_ESC_OSC /* skip an Operating System Command */
|
||||
};
|
||||
|
||||
/**
|
||||
* Buffer used for collecting escape sequences
|
||||
*/
|
||||
class Escape_stack
|
||||
{
|
||||
private:
|
||||
|
||||
Log_buffer _dump_log { };
|
||||
|
||||
public:
|
||||
|
||||
struct Entry
|
||||
@ -74,6 +87,22 @@ class Terminal::Decoder
|
||||
|
||||
int type = INVALID;
|
||||
int value = 0;
|
||||
|
||||
void print(Genode::Output &out, State state) const
|
||||
{
|
||||
if (type == NUMBER) {
|
||||
Genode::print(out, value);
|
||||
} else if (state == STATE_ESC_ECMA) {
|
||||
Ecma(value).print(out);
|
||||
} else {
|
||||
Ascii(value).print(out);
|
||||
}
|
||||
}
|
||||
|
||||
void print(Genode::Output &out) const
|
||||
{
|
||||
print(out, STATE_ESC_VT100);
|
||||
}
|
||||
};
|
||||
|
||||
struct Number_entry : Entry
|
||||
@ -90,21 +119,16 @@ class Terminal::Decoder
|
||||
|
||||
private:
|
||||
|
||||
enum { MAX_ENTRIES = 16 };
|
||||
enum { MAX_ENTRIES = 32 };
|
||||
Entry _entries[MAX_ENTRIES];
|
||||
int _index;
|
||||
|
||||
void _dump() const
|
||||
void _dump(State state)
|
||||
{
|
||||
Genode::log("--- escape stack follows ---");
|
||||
_dump_log.print("ESC");
|
||||
for (int i = 0; i < _index; i++) {
|
||||
int type = _entries[i].type;
|
||||
int value = _entries[i].value;
|
||||
Genode::log(type == Entry::INVALID ? " INVALID" :
|
||||
type == Entry::NUMBER ? " NUMBER "
|
||||
: " CODE ",
|
||||
" ", value, " ",
|
||||
"(", Genode::Hex(value), ")");
|
||||
_dump_log.out_char(' ');
|
||||
_entries[i].print(_dump_log, state);
|
||||
}
|
||||
}
|
||||
|
||||
@ -114,11 +138,20 @@ class Terminal::Decoder
|
||||
|
||||
void reset() { _index = 0; }
|
||||
|
||||
void discard(State state = STATE_ESC_VT100)
|
||||
{
|
||||
_dump_log.print("unhandled sequence ");
|
||||
_dump(state);
|
||||
_dump_log.flush_warning();
|
||||
_index = 0;
|
||||
}
|
||||
|
||||
void push(Entry const &entry)
|
||||
{
|
||||
if (_index == MAX_ENTRIES - 1) {
|
||||
Genode::error("escape stack overflow");
|
||||
_dump();
|
||||
_dump(STATE_ESC_VT100);
|
||||
_dump_log.flush_error();
|
||||
reset();
|
||||
return;
|
||||
}
|
||||
@ -135,44 +168,50 @@ class Terminal::Decoder
|
||||
*
|
||||
* 'index' is relative to the bottom of the stack.
|
||||
*/
|
||||
Entry operator [] (int index)
|
||||
Entry operator [] (int index) const
|
||||
{
|
||||
return (index <= _index) ? _entries[index] : Invalid_entry();
|
||||
}
|
||||
|
||||
} _escape_stack { };
|
||||
|
||||
enum State {
|
||||
STATE_IDLE,
|
||||
STATE_ESC_SEQ, /* read escape sequence */
|
||||
STATE_ESC_NUMBER /* read number argument within escape sequence */
|
||||
} _state;
|
||||
|
||||
Character_screen &_screen;
|
||||
|
||||
int _number; /* current number argument supplied in escape sequence */
|
||||
State _state = STATE_IDLE;
|
||||
|
||||
int _number = -1; /* current number argument supplied in escape sequence */
|
||||
|
||||
void _append_to_number(char c)
|
||||
{
|
||||
_number = _number*10 + digit(c);
|
||||
_number = (_number < 0 ? 0 : _number)*10 + digit(c);
|
||||
}
|
||||
|
||||
void _enter_state_idle()
|
||||
{
|
||||
_state = STATE_IDLE;
|
||||
_escape_stack.reset();
|
||||
_number = -1;
|
||||
}
|
||||
|
||||
void _enter_state_esc_seq()
|
||||
void _enter_state_esc_csi()
|
||||
{
|
||||
_state = STATE_ESC_SEQ;
|
||||
_state = STATE_ESC_CSI;
|
||||
_escape_stack.reset();
|
||||
}
|
||||
|
||||
void _enter_state_esc_number()
|
||||
void _enter_state_esc_ecma()
|
||||
{
|
||||
_state = STATE_ESC_NUMBER;
|
||||
_number = 0;
|
||||
_state = STATE_ESC_ECMA;
|
||||
}
|
||||
|
||||
void _enter_state_esc_vt100()
|
||||
{
|
||||
_state = STATE_ESC_VT100;
|
||||
}
|
||||
|
||||
void _enter_state_esc_osc()
|
||||
{
|
||||
_state = STATE_ESC_OSC;
|
||||
}
|
||||
|
||||
bool _sgr(int const p)
|
||||
@ -199,14 +238,11 @@ class Terminal::Decoder
|
||||
bool _handle_esc_seq_1()
|
||||
{
|
||||
switch (_escape_stack[0].value) {
|
||||
case '7': return (_screen.sc(), true);
|
||||
case '8': return (_screen.rc(), true);
|
||||
case 'E': return (_screen.nel(), true);
|
||||
case 'H': return (_screen.hts(), true);
|
||||
case 'M': return (_screen.cuu1(), true);
|
||||
case '=': return true; /* follows 'smkx' */
|
||||
case '>': return true; /* follows 'rmkx' */
|
||||
case 'c': return true; /* prefixes 'rs2' */
|
||||
case 'E': return (_screen.nel(), true);
|
||||
case '>': return true; /* follows 'rmkx' */
|
||||
case '=': return true; /* follows 'smkx' */
|
||||
default: return false;
|
||||
}
|
||||
}
|
||||
@ -216,32 +252,36 @@ class Terminal::Decoder
|
||||
switch (_escape_stack[0].value) {
|
||||
|
||||
case '[':
|
||||
|
||||
switch (_escape_stack[1].value) {
|
||||
case 'C': return (_screen.cuf(1), true);
|
||||
case 'H': return (_screen.home(), true);
|
||||
|
||||
case 'A': return (_screen.cuu(), true);
|
||||
case 'B': return (_screen.cud(), true);
|
||||
case 'C': return (_screen.cuf(), true);
|
||||
case 'D': return (_screen.cub(), true);
|
||||
case 'G': return (_screen.cha(), true);
|
||||
case 'H': return (_screen.cup(1,1), true);
|
||||
case 'J': return (_screen.ed(), true);
|
||||
case 'K': return (_screen.el(), true);
|
||||
case 'L': return (_screen.il(1), true);
|
||||
case 'M': return (_screen.dl(1), true);
|
||||
case 'P': return (_screen.dch(1), true);
|
||||
case 'Z': return (_screen.cbt(), true);
|
||||
case 'm': return (_screen.sgr0(), true);
|
||||
case 'L': return (_screen.il(), true);
|
||||
case 'M': return (_screen.dl(), true);
|
||||
case 'P': return (_screen.dch(), true);
|
||||
case 'm': return _sgr(0);
|
||||
case 'S': return (_screen.su(), true);
|
||||
case 'T': return (_screen.sd(), true);
|
||||
case 'c': return (_screen.da(), true);
|
||||
case 'd': return (_screen.vpa(), true);
|
||||
case 'n': return (_screen.vpb(), true);
|
||||
case '@': return (_screen.ich(), true);
|
||||
default: return false;
|
||||
}
|
||||
break;
|
||||
|
||||
case ')':
|
||||
return (_escape_stack[1].value == 0) && (_screen.is2(), true);
|
||||
|
||||
default: return false;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool _handle_esc_seq_3()
|
||||
{
|
||||
|
||||
/*
|
||||
* All three-element sequences have the form \E[<NUMBER><COMMAND>
|
||||
*/
|
||||
@ -253,25 +293,29 @@ class Terminal::Decoder
|
||||
char const command = _escape_stack[2].value;
|
||||
|
||||
switch (command) {
|
||||
case 'm': return _sgr(p1);
|
||||
case 'D': return (_screen.cub(p1), true);
|
||||
case 'A': return (_screen.cuu(p1), true);
|
||||
case 'B': return (_screen.cud(p1), true);
|
||||
case 'C': return (_screen.cuf(p1), true);
|
||||
case 'D': return (_screen.cub(p1), true);
|
||||
case 'd': return (_screen.vpa(p1), true);
|
||||
case 'g': return (p1 == 3) && (_screen.tbc(), true);
|
||||
case 'h': return ((p1 == 4) && (_screen.smir(), true))
|
||||
|| ((p1 == 34) && (_screen.cnorm(), true));
|
||||
case 'K': return ((p1 == 0) && (_screen.el(), true))
|
||||
|| ((p1 == 1) && (_screen.el1(), true));
|
||||
case 'l': return ((p1 == 4) && (_screen.rmir(), true))
|
||||
|| ((p1 == 34) && (_screen.cvvis(), true));
|
||||
case 'G': return (_screen.cha(p1), true);
|
||||
case 'h': return (_screen.decsm(p1), true);
|
||||
case 'l': return (_screen.decrm(p1), true);
|
||||
case 'J': return (_screen.ed(p1), true);
|
||||
case 'K': return (_screen.el(p1), true);
|
||||
case 'L': return (_screen.il(p1), true);
|
||||
case 'M': return (_screen.dl(p1), true);
|
||||
case 'm': return _sgr(p1);
|
||||
case 'n': return (_screen.vpb(p1), true);
|
||||
case 'P': return (_screen.dch(p1), true);
|
||||
case '@': return (_screen.ich(p1), true);
|
||||
case 'C': return (_screen.cuf(p1), true);
|
||||
|
||||
default: return false;
|
||||
case 'S': return (_screen.su(p1), true);
|
||||
case 'T': return (_screen.sd(p1), true);
|
||||
case 'X': return (_screen.ech(p1), true);
|
||||
default: break;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool _handle_esc_seq_4()
|
||||
@ -289,29 +333,11 @@ class Terminal::Decoder
|
||||
char const command = _escape_stack[3].value;
|
||||
|
||||
switch (command) {
|
||||
case 'l':
|
||||
if (p1 == 1) return (_screen.rmkx(), true);
|
||||
if (p1 == 25) return (_screen.civis(), true);
|
||||
if (p1 == 1000) return (_screen.rs2(), true);
|
||||
if (p1 == 1049) return (_screen.rmcup(), true);
|
||||
if (p1 == 2004) {
|
||||
/* disable bracketed paste */
|
||||
Genode::warning("Sequence '[?2004l' is not implemented");
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
case 'h':
|
||||
if (p1 == 1) return (_screen.smkx(), true);
|
||||
if (p1 == 25) return (_screen.cnorm(), true);
|
||||
if (p1 == 1049) return (_screen.smcup(), true);
|
||||
if (p1 == 2004) {
|
||||
/* enable bracketed paste */
|
||||
Genode::warning("Sequence '[?2004h' is not implemented");
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
default: return false;
|
||||
case 'h': return (_screen.decsm(p1), true);
|
||||
case 'l': return (_screen.decrm(p1), true);
|
||||
default: break;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool _handle_esc_seq_5()
|
||||
@ -351,13 +377,34 @@ class Terminal::Decoder
|
||||
}
|
||||
}
|
||||
|
||||
bool _handle_esc_seq_6()
|
||||
{
|
||||
/*
|
||||
* All five-element escape sequences have the form
|
||||
* \E[?<NUMBER1>;<NUMBER2><COMMAND>
|
||||
*/
|
||||
if ((_escape_stack[0].value != '[')
|
||||
|| (_escape_stack[1].value != '?')
|
||||
|| (_escape_stack[2].type != Escape_stack::Entry::NUMBER)
|
||||
|| (_escape_stack[3].value != ';')
|
||||
|| (_escape_stack[4].type != Escape_stack::Entry::NUMBER))
|
||||
return false;
|
||||
|
||||
int const p[2] = { _escape_stack[2].value,
|
||||
_escape_stack[4].value };
|
||||
switch (_escape_stack[5].value) {
|
||||
case 'h': return (_screen.decsm(p[0], p[1]), true);
|
||||
case 'l': return (_screen.decrm(p[0], p[1]), true);
|
||||
default: return false;
|
||||
}
|
||||
}
|
||||
|
||||
bool _handle_esc_seq_7()
|
||||
{
|
||||
/*
|
||||
* All six-element escape sequences have the form
|
||||
* \E[<NUMBER1>;<NUMBER2>;<NUMBER3><COMMAND>
|
||||
*/
|
||||
|
||||
if ((_escape_stack[0].value != '[')
|
||||
|| (_escape_stack[1].type != Escape_stack::Entry::NUMBER)
|
||||
|| (_escape_stack[2].value != ';')
|
||||
@ -390,10 +437,20 @@ class Terminal::Decoder
|
||||
return true;
|
||||
}
|
||||
|
||||
bool _complete()
|
||||
{
|
||||
return (((_escape_stack.num_elem() == 1) && _handle_esc_seq_1())
|
||||
|| ((_escape_stack.num_elem() == 2) && _handle_esc_seq_2())
|
||||
|| ((_escape_stack.num_elem() == 3) && _handle_esc_seq_3())
|
||||
|| ((_escape_stack.num_elem() == 4) && _handle_esc_seq_4())
|
||||
|| ((_escape_stack.num_elem() == 5) && _handle_esc_seq_5())
|
||||
|| ((_escape_stack.num_elem() == 6) && _handle_esc_seq_6())
|
||||
|| ((_escape_stack.num_elem() == 7) && _handle_esc_seq_7()));
|
||||
}
|
||||
|
||||
public:
|
||||
|
||||
Decoder(Character_screen &screen)
|
||||
: _state(STATE_IDLE), _screen(screen), _number(0) { }
|
||||
Decoder(Character_screen &screen) : _screen(screen) { }
|
||||
|
||||
void insert(unsigned char c)
|
||||
{
|
||||
@ -403,7 +460,7 @@ class Terminal::Decoder
|
||||
|
||||
enum { ESC_PREFIX = 0x1b };
|
||||
if (c == ESC_PREFIX) {
|
||||
_enter_state_esc_seq();
|
||||
_enter_state_esc_csi();
|
||||
break;
|
||||
}
|
||||
|
||||
@ -414,8 +471,48 @@ class Terminal::Decoder
|
||||
|
||||
break;
|
||||
|
||||
case STATE_ESC_SEQ:
|
||||
case STATE_ESC_CSI:
|
||||
/* check that the second byte is in set C1 - ECMA-48 5.3 */
|
||||
switch (c) {
|
||||
case '7':
|
||||
_screen.decsc();
|
||||
_enter_state_idle();
|
||||
break;
|
||||
case '8':
|
||||
_screen.decrc();
|
||||
_enter_state_idle();
|
||||
break;
|
||||
case '(':
|
||||
case ')':
|
||||
_escape_stack.push(Escape_stack::Code_entry(c));
|
||||
_state = STATE_ESC_SCS;
|
||||
break;
|
||||
case ']':
|
||||
_enter_state_esc_osc();
|
||||
break;
|
||||
case 'M':
|
||||
_screen.reverse_index();
|
||||
_enter_state_idle();
|
||||
break;
|
||||
case '=':
|
||||
case '>':
|
||||
/* keypad mode, not useful enough to handle */
|
||||
_enter_state_idle();
|
||||
break;
|
||||
default:
|
||||
if (0x40 <= c && c <= 0x5f) {
|
||||
_escape_stack.push(Escape_stack::Code_entry(c));
|
||||
_enter_state_esc_ecma();
|
||||
break;
|
||||
}
|
||||
Genode::error("unknown CSI ESC", Ascii(c));
|
||||
_enter_state_idle();
|
||||
}
|
||||
|
||||
break;
|
||||
|
||||
case STATE_ESC_ECMA:
|
||||
case STATE_ESC_VT100:
|
||||
/*
|
||||
* We received the prefix character of an escape sequence,
|
||||
* collect the escape-sequence elements until we detect the
|
||||
@ -423,52 +520,50 @@ class Terminal::Decoder
|
||||
*/
|
||||
|
||||
/* check for start of a number argument */
|
||||
if (is_digit(c) && !_number)
|
||||
{
|
||||
_enter_state_esc_number();
|
||||
_append_to_number(c);
|
||||
break;
|
||||
}
|
||||
|
||||
/* non-number character of escape sequence */
|
||||
_escape_stack.push(Escape_stack::Code_entry(c));
|
||||
break;
|
||||
|
||||
case STATE_ESC_NUMBER:
|
||||
|
||||
/*
|
||||
* We got the first character belonging to a number
|
||||
* argument of an escape sequence. Keep reading digits.
|
||||
*/
|
||||
if (is_digit(c)) {
|
||||
_append_to_number(c);
|
||||
break;
|
||||
}
|
||||
|
||||
/*
|
||||
* End of number is reached.
|
||||
*/
|
||||
else /* non-number character of escape sequence */
|
||||
{
|
||||
if (-1 < _number) {
|
||||
_escape_stack.push(Escape_stack::Number_entry(_number));
|
||||
_number = -1;
|
||||
}
|
||||
|
||||
/* push the complete number to the escape stack */
|
||||
_escape_stack.push(Escape_stack::Number_entry(_number));
|
||||
_number = 0;
|
||||
_escape_stack.push(Escape_stack::Code_entry(c));
|
||||
|
||||
/* push non-number character as commend entry */
|
||||
/* check for Final Byte - ECMA-48 5.4 */
|
||||
if (_state == STATE_ESC_ECMA && c > 0x3f && c < 0x7f) {
|
||||
if (!_complete()) {
|
||||
_escape_stack.discard(_state);
|
||||
}
|
||||
_enter_state_idle();
|
||||
} else {
|
||||
if (_complete())
|
||||
_enter_state_idle();
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
case STATE_ESC_SCS:
|
||||
switch (_escape_stack[0].value) {
|
||||
case '(': _screen.scs_g0(c); break;
|
||||
case ')': _screen.scs_g1(c); break;
|
||||
}
|
||||
_enter_state_idle();
|
||||
break;
|
||||
|
||||
case STATE_ESC_OSC:
|
||||
enum { BELL = 07 };
|
||||
_escape_stack.push(Escape_stack::Code_entry(c));
|
||||
if (c == BELL) {
|
||||
_escape_stack.discard(_state);
|
||||
_enter_state_idle();
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
/*
|
||||
* Check for the completeness of an escape sequence.
|
||||
*/
|
||||
if (((_escape_stack.num_elem() == 1) && _handle_esc_seq_1())
|
||||
|| ((_escape_stack.num_elem() == 2) && _handle_esc_seq_2())
|
||||
|| ((_escape_stack.num_elem() == 3) && _handle_esc_seq_3())
|
||||
|| ((_escape_stack.num_elem() == 4) && _handle_esc_seq_4())
|
||||
|| ((_escape_stack.num_elem() == 5) && _handle_esc_seq_5())
|
||||
|| ((_escape_stack.num_elem() == 7) && _handle_esc_seq_7()))
|
||||
_enter_state_idle();
|
||||
};
|
||||
};
|
||||
|
||||
|
140
repos/os/include/terminal/print.h
Normal file
140
repos/os/include/terminal/print.h
Normal file
@ -0,0 +1,140 @@
|
||||
/*
|
||||
* \brief Character printing utilities
|
||||
* \author Emery Hemingway
|
||||
* \date 2018-07-27
|
||||
*/
|
||||
|
||||
/*
|
||||
* Copyright (C) 2018 Genode Labs GmbH
|
||||
*
|
||||
* This file is part of the Genode OS framework, which is distributed
|
||||
* under the terms of the GNU Affero General Public License version 3.
|
||||
*/
|
||||
|
||||
#ifndef _TERMINAL__PRINT_H_
|
||||
#define _TERMINAL__PRINT_H_
|
||||
|
||||
#include <log_session/connection.h>
|
||||
#include <base/output.h>
|
||||
|
||||
namespace Terminal {
|
||||
|
||||
class Log_buffer : public Genode::Output
|
||||
{
|
||||
private:
|
||||
|
||||
enum { BUF_SIZE = Genode::Log_session::MAX_STRING_LEN };
|
||||
|
||||
char _buf[BUF_SIZE];
|
||||
unsigned _num_chars = 0;
|
||||
|
||||
public:
|
||||
|
||||
Log_buffer() { }
|
||||
|
||||
void flush_ok()
|
||||
{
|
||||
log(Genode::Cstring(_buf, _num_chars));
|
||||
_num_chars = 0;
|
||||
}
|
||||
|
||||
void flush_warning()
|
||||
{
|
||||
warning(Genode::Cstring(_buf, _num_chars));
|
||||
_num_chars = 0;
|
||||
}
|
||||
|
||||
void flush_error()
|
||||
{
|
||||
error(Genode::Cstring(_buf, _num_chars));
|
||||
_num_chars = 0;
|
||||
}
|
||||
|
||||
void out_char(char c) override
|
||||
{
|
||||
_buf[_num_chars++] = c;
|
||||
if (_num_chars >= sizeof(_buf))
|
||||
flush_ok();
|
||||
}
|
||||
|
||||
template <typename... ARGS>
|
||||
void print(ARGS &&... args) {
|
||||
Output::out_args(*this, args...); }
|
||||
};
|
||||
|
||||
struct Ascii
|
||||
{
|
||||
unsigned char const _c;
|
||||
|
||||
template <typename T>
|
||||
explicit Ascii(T c) : _c(c) { }
|
||||
|
||||
void print(Genode::Output &out) const
|
||||
{
|
||||
switch (_c) {
|
||||
case 000: out.out_string("NUL"); break;
|
||||
case 001: out.out_string("SOH"); break;
|
||||
case 002: out.out_string("STX"); break;
|
||||
case 003: out.out_string("ETX"); break;
|
||||
case 004: out.out_string("EOT"); break;
|
||||
case 005: out.out_string("ENQ"); break;
|
||||
case 006: out.out_string("ACK"); break;
|
||||
case 007: out.out_string("BEL"); break;
|
||||
case 010: out.out_string("BS"); break;
|
||||
case 011: out.out_string("HT"); break;
|
||||
case 012: out.out_string("LF"); break;
|
||||
case 013: out.out_string("VT"); break;
|
||||
case 014: out.out_string("FF"); break;
|
||||
case 015: out.out_string("CR"); break;
|
||||
case 016: out.out_string("SO"); break;
|
||||
case 017: out.out_string("SI"); break;
|
||||
case 020: out.out_string("DLE"); break;
|
||||
case 021: out.out_string("DC1"); break;
|
||||
case 022: out.out_string("DC2"); break;
|
||||
case 023: out.out_string("DC3"); break;
|
||||
case 024: out.out_string("DC4"); break;
|
||||
case 025: out.out_string("NAK"); break;
|
||||
case 026: out.out_string("SYN"); break;
|
||||
case 027: out.out_string("ETB"); break;
|
||||
case 030: out.out_string("CAN"); break;
|
||||
case 031: out.out_string("EM"); break;
|
||||
case 032: out.out_string("SUB"); break;
|
||||
case 033: out.out_string("ESC"); break;
|
||||
case 034: out.out_string("FS"); break;
|
||||
case 035: out.out_string("GS"); break;
|
||||
case 036: out.out_string("RS"); break;
|
||||
case 037: out.out_string("US"); break;
|
||||
case 040: out.out_string("SPACE"); break;
|
||||
case 0177: out.out_string("DEL"); break;
|
||||
default:
|
||||
if (_c & 0x80)
|
||||
Genode::Hex(_c).print(out);
|
||||
else
|
||||
out.out_char(_c);
|
||||
break;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
struct Ecma
|
||||
{
|
||||
unsigned char const _c;
|
||||
|
||||
template <typename T>
|
||||
explicit Ecma(T c) : _c(c) { }
|
||||
|
||||
void print(Genode::Output &out) const
|
||||
{
|
||||
Ascii(_c).print(out);
|
||||
out.out_char('(');
|
||||
|
||||
Genode::print(out, (_c)/160, (_c>>4)%10,
|
||||
"/", (_c&0xf)/10, (_c&0xf)%10);
|
||||
|
||||
out.out_char(')');
|
||||
}
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif
|
@ -84,6 +84,9 @@ struct Terminal::Position
|
||||
return x >= 0 && x < boundary.width
|
||||
&& y >= 0 && y < boundary.height;
|
||||
}
|
||||
|
||||
void print(Genode::Output &out) const {
|
||||
Genode::print(out, y, ",", x); }
|
||||
};
|
||||
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user