diff --git a/repos/os/include/terminal/char_cell_array_character_screen.h b/repos/os/include/terminal/char_cell_array_character_screen.h index fc616c5b5b..45eb3dd7ba 100644 --- a/repos/os/include/terminal/char_cell_array_character_screen.h +++ b/repos/os/include/terminal/char_cell_array_character_screen.h @@ -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_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_ */ diff --git a/repos/os/include/terminal/character_screen.h b/repos/os/include/terminal/character_screen.h index c0a1c5f220..c4e66bb55c 100644 --- a/repos/os/include/terminal/character_screen.h +++ b/repos/os/include/terminal/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_ */ diff --git a/repos/os/include/terminal/decoder.h b/repos/os/include/terminal/decoder.h index 351e0834b8..91c3411590 100644 --- a/repos/os/include/terminal/decoder.h +++ b/repos/os/include/terminal/decoder.h @@ -15,10 +15,10 @@ #define _TERMINAL__DECODER_H_ #include +#include 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[ */ @@ -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[?; + */ + 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[;; */ - 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(); }; }; diff --git a/repos/os/include/terminal/print.h b/repos/os/include/terminal/print.h new file mode 100644 index 0000000000..3756128543 --- /dev/null +++ b/repos/os/include/terminal/print.h @@ -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 +#include + +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 + void print(ARGS &&... args) { + Output::out_args(*this, args...); } + }; + + struct Ascii + { + unsigned char const _c; + + template + 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 + 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 diff --git a/repos/os/include/terminal/types.h b/repos/os/include/terminal/types.h index 1d58f4aa90..71af11cc0b 100644 --- a/repos/os/include/terminal/types.h +++ b/repos/os/include/terminal/types.h @@ -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); } };