diff --git a/Makefile b/Makefile index 84e718a..de78ca1 100644 --- a/Makefile +++ b/Makefile @@ -1,5 +1,5 @@ -source = parse.cpp parserstate.cpp parser.cpp templates.cpp terminal.cpp termemu.cpp parseraction.cpp terminalcsi.cpp swrite.cpp terminalframebuffer.cpp terminaldispatcher.cpp -objects = parserstate.o parser.o templates.o terminal.o parseraction.o terminalcsi.o swrite.o terminalframebuffer.o terminaldispatcher.o +source = parse.cpp parserstate.cpp parser.cpp templates.cpp terminal.cpp termemu.cpp parseraction.cpp terminalfunctions.cpp swrite.cpp terminalframebuffer.cpp terminaldispatcher.cpp +objects = parserstate.o parser.o templates.o terminal.o parseraction.o terminalfunctions.o swrite.o terminalframebuffer.o terminaldispatcher.o repos = templates.rpo executables = parse termemu diff --git a/templates.cpp b/templates.cpp index 9530e4f..78eeb82 100644 --- a/templates.cpp +++ b/templates.cpp @@ -1,6 +1,7 @@ #include #include #include +#include #include "terminal.hpp" @@ -14,3 +15,4 @@ template class std::deque; template class std::vector; template class std::vector; template class std::vector; +template class std::map; diff --git a/terminal.cpp b/terminal.cpp index 885f9fa..9d0c46c 100644 --- a/terminal.cpp +++ b/terminal.cpp @@ -10,12 +10,12 @@ using namespace Terminal; Emulator::Emulator( size_t s_width, size_t s_height ) - : parser(), fb( s_width, s_height ), dispatch(), terminal_to_host() + : parser(), fb( s_width, s_height ), dispatch() {} std::string Emulator::input( char c, int actfd ) { - terminal_to_host.clear(); + dispatch.terminal_to_host.clear(); std::vector vec = parser.input( c ); @@ -36,7 +36,7 @@ std::string Emulator::input( char c, int actfd ) } /* the user is supposed to send this string to the host, as if typed */ - return terminal_to_host; + return dispatch.terminal_to_host; } void Emulator::execute( Parser::Execute *act ) @@ -118,48 +118,12 @@ void Emulator::print( Parser::Print *act ) void Emulator::CSI_dispatch( Parser::CSI_Dispatch *act ) { - /* add final char to dispatch key */ - assert( act->char_present ); - Parser::Collect act2; - act2.char_present = true; - act2.ch = act->ch; - dispatch.collect( &act2 ); - - std::string dispatch_chars = dispatch.dispatch_chars; - - if ( dispatch_chars == "K" ) { - CSI_EL(); - act->handled = true; - } else if ( dispatch_chars == "J" ) { - CSI_ED(); - act->handled = true; - } else if ( (dispatch_chars == "A") - || (dispatch_chars == "B") - || (dispatch_chars == "C") - || (dispatch_chars == "D") - || (dispatch_chars == "H") - || (dispatch_chars == "f") ) { - CSI_cursormove(); - act->handled = true; - } else if ( dispatch_chars == "c" ) { - CSI_DA(); - act->handled = true; - } + dispatch.dispatch( CSI, act, &fb ); } void Emulator::Esc_dispatch( Parser::Esc_Dispatch *act ) { - /* add final char to dispatch key */ - assert( act->char_present ); - Parser::Collect act2; - act2.char_present = true; - act2.ch = act->ch; - dispatch.collect( &act2 ); - - if ( dispatch.dispatch_chars == "#8" ) { - Esc_DECALN(); - act->handled = true; - } + dispatch.dispatch( ESCAPE, act, &fb ); } void Emulator::debug_printout( int fd ) diff --git a/terminal.hpp b/terminal.hpp index 76f2823..c2ad8f4 100644 --- a/terminal.hpp +++ b/terminal.hpp @@ -25,8 +25,6 @@ namespace Terminal { Framebuffer fb; Dispatcher dispatch; - std::string terminal_to_host; - /* action methods */ void print( Parser::Print *act ); void execute( Parser::Execute *act ); diff --git a/terminalcsi.cpp b/terminalcsi.cpp deleted file mode 100644 index c04e61f..0000000 --- a/terminalcsi.cpp +++ /dev/null @@ -1,86 +0,0 @@ -#include - -#include "terminal.hpp" - -using namespace Terminal; - -static void clearline( Framebuffer *fb, int row, int start, int end ) -{ - for ( int col = start; col <= end; col++ ) { - fb->get_cell( row, col )->reset(); - } -} - -void Emulator::CSI_EL( void ) -{ - /* default: active position to end of line, inclusive */ - int start = fb.ds.get_cursor_col(), end = fb.ds.get_width() - 1; - - if ( dispatch.params == "1" ) { /* start of screen to active position, inclusive */ - start = 0; - end = fb.ds.get_cursor_col(); - } else if ( dispatch.params == "2" ) { /* all of line */ - start = 0; - } - - clearline( &fb, -1, start, end ); -} - -void Emulator::CSI_ED( void ) { - if ( dispatch.params == "1" ) { /* start of screen to active position, inclusive */ - for ( int y = 0; y < fb.ds.get_cursor_row(); y++ ) { - clearline( &fb, y, 0, fb.ds.get_width() - 1 ); - } - clearline( &fb, -1, 0, fb.ds.get_cursor_col() ); - } else if ( dispatch.params == "2" ) { /* entire screen */ - for ( int y = 0; y < fb.ds.get_height(); y++ ) { - clearline( &fb, y, 0, fb.ds.get_width() - 1 ); - } - } else { /* active position to end of screen, inclusive */ - clearline( &fb, -1, fb.ds.get_cursor_col(), fb.ds.get_width() - 1 ); - for ( int y = fb.ds.get_cursor_row() + 1; y < fb.ds.get_height(); y++ ) { - clearline( &fb, y, 0, fb.ds.get_width() - 1 ); - } - } -} - -void Emulator::CSI_cursormove( void ) -{ - int num = dispatch.getparam( 0, 1 ); - - switch ( dispatch.dispatch_chars[ 0 ] ) { - case 'A': - fb.ds.move_row( -num, true ); - break; - case 'B': - fb.ds.move_row( num, true ); - break; - case 'C': - fb.ds.move_col( num, true ); - break; - case 'D': - fb.ds.move_col( -num, true ); - break; - case 'H': - case 'f': - int x = dispatch.getparam( 0, 1 ); - int y = dispatch.getparam( 1, 1 ); - fb.ds.move_row( x - 1 ); - fb.ds.move_col( y - 1 ); - } -} - -void Emulator::CSI_DA( void ) -{ - terminal_to_host.append( "\033[?1;0c" ); -} - -void Emulator::Esc_DECALN( void ) -{ - for ( int y = 0; y < fb.ds.get_height(); y++ ) { - for ( int x = 0; x < fb.ds.get_width(); x++ ) { - fb.get_cell( y, x )->reset(); - fb.get_cell( y, x )->contents.push_back( L'E' ); - } - } -} diff --git a/terminaldispatcher.cpp b/terminaldispatcher.cpp index 413f00f..8110ac1 100644 --- a/terminaldispatcher.cpp +++ b/terminaldispatcher.cpp @@ -8,7 +8,8 @@ using namespace Terminal; Dispatcher::Dispatcher() - : params(), parsed_params(), parsed( false ), dispatch_chars() + : params(), parsed_params(), parsed( false ), dispatch_chars(), + terminal_to_host() {} void Dispatcher::newparamchar( Parser::Param *act ) @@ -106,3 +107,44 @@ std::string Dispatcher::str( void ) dispatch_chars.c_str(), params.c_str() ); return std::string( assum ); } + +static void register_function( Function_Type type, + std::string dispatch_chars, + Function f ) +{ + switch ( type ) { + case ESCAPE: + global_dispatch_registry.escape.insert( dispatch_map_t::value_type( dispatch_chars, f ) ); + break; + case CSI: + global_dispatch_registry.CSI.insert( dispatch_map_t::value_type( dispatch_chars, f ) ); + break; + } +} + +Function::Function( Function_Type type, std::string dispatch_chars, + void (*s_function)( Framebuffer *, Dispatcher * ) ) + : function( s_function ) +{ + register_function( type, dispatch_chars, *this ); +} + +void Dispatcher::dispatch( Function_Type type, Parser::Action *act, Framebuffer *fb ) +{ + /* add final char to dispatch key */ + assert( act->char_present ); + Parser::Collect act2; + act2.char_present = true; + act2.ch = act->ch; + collect( &act2 ); + + dispatch_map_t *map = (type == ESCAPE) ? &global_dispatch_registry.escape : &global_dispatch_registry.CSI; + + dispatch_map_t::const_iterator i = map->find( dispatch_chars ); + if ( i == map->end() ) { + return; + } else { + act->handled = true; + return i->second.function( fb, this ); + } +} diff --git a/terminaldispatcher.hpp b/terminaldispatcher.hpp index 67e80fa..0b30e13 100644 --- a/terminaldispatcher.hpp +++ b/terminaldispatcher.hpp @@ -3,17 +3,45 @@ #include #include +#include namespace Parser { + class Action; class Param; class Collect; class Clear; + class Esc_Dispatch; + class CSI_Dispatch; } namespace Terminal { + class Framebuffer; + class Dispatcher; + + enum Function_Type { ESCAPE, CSI }; + + class Function { + public: + Function() : function( NULL ) {} + Function( Function_Type type, std::string dispatch_chars, + void (*s_function)( Framebuffer *, Dispatcher * ) ); + void (*function)( Framebuffer *, Dispatcher * ); + }; + + typedef std::map dispatch_map_t; + + class DispatchRegistry { + public: + dispatch_map_t escape; + dispatch_map_t CSI; + + DispatchRegistry() : escape(), CSI() {} + }; + + static DispatchRegistry global_dispatch_registry; + class Dispatcher { private: - public: /* tmp */ std::string params; std::vector parsed_params; bool parsed; @@ -23,6 +51,8 @@ namespace Terminal { void parse_params( void ); public: + std::string terminal_to_host; /* this is the reply string */ + Dispatcher(); int getparam( size_t N, int defaultval ); @@ -31,6 +61,9 @@ namespace Terminal { void clear( Parser::Clear *act ); std::string str( void ); + + void dispatch( Function_Type type, Parser::Action *act, Framebuffer *fb ); + const std::string get_dispatch_chars( void ) { return dispatch_chars; } }; } diff --git a/terminalframebuffer.hpp b/terminalframebuffer.hpp index f255e64..d2ea01e 100644 --- a/terminalframebuffer.hpp +++ b/terminalframebuffer.hpp @@ -16,7 +16,7 @@ namespace Terminal { int width; Cell(); - + Cell( const Cell & ); Cell & operator=( const Cell & ); diff --git a/terminalfunctions.cpp b/terminalfunctions.cpp new file mode 100644 index 0000000..9731305 --- /dev/null +++ b/terminalfunctions.cpp @@ -0,0 +1,106 @@ +#include + +#include "terminaldispatcher.hpp" +#include "terminalframebuffer.hpp" + +using namespace Terminal; + +static void clearline( Framebuffer *fb, int row, int start, int end ) +{ + for ( int col = start; col <= end; col++ ) { + fb->get_cell( row, col )->reset(); + } +} + +void CSI_EL( Framebuffer *fb, Dispatcher *dispatch ) +{ + switch ( dispatch->getparam( 0, 0 ) ) { + case 0: /* default: active position to end of line, inclusive */ + clearline( fb, -1, fb->ds.get_cursor_col(), fb->ds.get_width() - 1 ); + break; + case 1: /* start of screen to active position, inclusive */ + clearline( fb, -1, 0, fb->ds.get_cursor_col() ); + break; + case 2: /* all of line */ + clearline( fb, -1, 0, fb->ds.get_width() - 1 ); + break; + } +} + +static Function func_CSI_EL( CSI, "K", CSI_EL ); + +void CSI_ED( Framebuffer *fb, Dispatcher *dispatch ) { + switch ( dispatch->getparam( 0, 0 ) ) { + case 0: /* active position to end of screen, inclusive */ + clearline( fb, -1, fb->ds.get_cursor_col(), fb->ds.get_width() - 1 ); + for ( int y = fb->ds.get_cursor_row() + 1; y < fb->ds.get_height(); y++ ) { + clearline( fb, y, 0, fb->ds.get_width() - 1 ); + } + break; + case 1: /* start of screen to active position, inclusive */ + for ( int y = 0; y < fb->ds.get_cursor_row(); y++ ) { + clearline( fb, y, 0, fb->ds.get_width() - 1 ); + } + clearline( fb, -1, 0, fb->ds.get_cursor_col() ); + break; + case 2: /* entire screen */ + for ( int y = 0; y < fb->ds.get_height(); y++ ) { + clearline( fb, y, 0, fb->ds.get_width() - 1 ); + } + break; + } +} + +static Function func_CSI_ED( CSI, "J", CSI_ED ); + +void CSI_cursormove( Framebuffer *fb, Dispatcher *dispatch ) +{ + int num = dispatch->getparam( 0, 1 ); + + switch ( dispatch->get_dispatch_chars()[ 0 ] ) { + case 'A': + fb->ds.move_row( -num, true ); + break; + case 'B': + fb->ds.move_row( num, true ); + break; + case 'C': + fb->ds.move_col( num, true ); + break; + case 'D': + fb->ds.move_col( -num, true ); + break; + case 'H': + case 'f': + int x = dispatch->getparam( 0, 1 ); + int y = dispatch->getparam( 1, 1 ); + fb->ds.move_row( x - 1 ); + fb->ds.move_col( y - 1 ); + } +} + +static Function func_CSI_cursormove_A( CSI, "A", CSI_cursormove ); +static Function func_CSI_cursormove_B( CSI, "B", CSI_cursormove ); +static Function func_CSI_cursormove_C( CSI, "C", CSI_cursormove ); +static Function func_CSI_cursormove_D( CSI, "D", CSI_cursormove ); +static Function func_CSI_cursormove_H( CSI, "H", CSI_cursormove ); +static Function func_CSI_cursormove_f( CSI, "f", CSI_cursormove ); + +void CSI_DA( Framebuffer *fb __attribute((unused)), Dispatcher *dispatch ) +{ + dispatch->terminal_to_host.append( "\033[?1;0c" ); +} + +static Function func_CSI_DA( CSI, "c", CSI_DA ); + +void Esc_DECALN( Framebuffer *fb, Dispatcher *dispatch __attribute((unused)) ) +{ + for ( int y = 0; y < fb->ds.get_height(); y++ ) { + for ( int x = 0; x < fb->ds.get_width(); x++ ) { + fb->get_cell( y, x )->reset(); + fb->get_cell( y, x )->contents.push_back( L'E' ); + } + } +} + +static Function func_Esc_DECALN( ESCAPE, "#8", Esc_DECALN );