From f40d539f7bde067eea45bd40999bd8408ee3639a Mon Sep 17 00:00:00 2001 From: Keith Winstein Date: Fri, 21 Jan 2011 15:28:13 -0500 Subject: [PATCH] Simple actions dispatching on terminal emulator --- Makefile | 9 ++- parser.hpp | 2 +- parseraction.cpp | 9 +++ parseraction.hpp | 10 ++- templates.cpp | 11 ++- termemu.cpp | 186 +++++++++++++++++++++++++++++++++++++++++++++++ terminal.cpp | 62 ++++++++++++++++ terminal.hpp | 44 ++++++++--- 8 files changed, 318 insertions(+), 15 deletions(-) create mode 100644 parseraction.cpp create mode 100644 termemu.cpp diff --git a/Makefile b/Makefile index f8ecbd8..3535420 100644 --- a/Makefile +++ b/Makefile @@ -1,6 +1,6 @@ -source = parse.cpp parserstate.cpp parser.cpp templates.cpp terminal.cpp -objects = parserstate.o parser.o templates.o terminal.o -executables = parse +source = parse.cpp parserstate.cpp parser.cpp templates.cpp terminal.cpp termemu.cpp parseraction.cpp +objects = parserstate.o parser.o templates.o terminal.o parseraction.o +executables = parse termemu CXX = g++ CXXFLAGS = -g --std=c++0x -pedantic -Werror -Wall -Wextra -Weffc++ -fno-implicit-templates -fno-default-inline -pipe -D_FILE_OFFSET_BITS=64 -D_XOPEN_SOURCE=500 -D_GNU_SOURCE @@ -11,6 +11,9 @@ all: $(executables) parse: parse.o $(objects) $(CXX) $(CXXFLAGS) -o $@ $+ $(LIBS) +termemu: termemu.o $(objects) + $(CXX) $(CXXFLAGS) -o $@ $+ $(LIBS) + templates.o: templates.cpp $(CXX) $(CXXFLAGS) -frepo -c -o $@ $< diff --git a/parser.hpp b/parser.hpp index a63b61d..a689f48 100644 --- a/parser.hpp +++ b/parser.hpp @@ -23,7 +23,7 @@ namespace Parser { Parser() : family(), state( &family.s_Ground ) {} Parser( const Parser & ); - bool operator=( const Parser & ); + Parser & operator=( const Parser & ); ~Parser() {} std::vector input( wchar_t ch ); diff --git a/parseraction.cpp b/parseraction.cpp new file mode 100644 index 0000000..d284353 --- /dev/null +++ b/parseraction.cpp @@ -0,0 +1,9 @@ +#include "parseraction.hpp" +#include "terminal.hpp" + +using namespace Parser; + +void Print::act_on_terminal( Terminal::Emulator *emu ) +{ + emu->print( this ); +} diff --git a/parseraction.hpp b/parseraction.hpp index 58bc1aa..86e5268 100644 --- a/parseraction.hpp +++ b/parseraction.hpp @@ -3,6 +3,10 @@ #include +namespace Terminal { + class Emulator; +} + namespace Parser { class Action { @@ -12,6 +16,8 @@ namespace Parser { virtual std::string name( void ) = 0; + virtual void act_on_terminal( Terminal::Emulator * ) {}; + Action() : char_present( false ), ch( -1 ) {}; virtual ~Action() {}; }; @@ -20,7 +26,9 @@ namespace Parser { public: std::string name( void ) { return std::string( "Ignore" ); } }; class Print : public Action { - public: std::string name( void ) { return std::string( "Print" ); } + public: + std::string name( void ) { return std::string( "Print" ); } + void act_on_terminal( Terminal::Emulator *emu ); }; class Execute : public Action { public: std::string name( void ) { return std::string( "Execute" ); } diff --git a/templates.cpp b/templates.cpp index ec0398b..49a2ef5 100644 --- a/templates.cpp +++ b/templates.cpp @@ -1,5 +1,14 @@ #include +#include -#include "parseraction.hpp" +#include "terminal.hpp" + +namespace Parser { + class Action; +} template class std::vector; +template class std::vector; +template class std::vector; +template class std::vector; +template class std::vector; diff --git a/termemu.cpp b/termemu.cpp new file mode 100644 index 0000000..fc0cd3c --- /dev/null +++ b/termemu.cpp @@ -0,0 +1,186 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "terminal.hpp" + +#ifndef __STDC_ISO_10646__ +#error "Must have __STDC_ISO_10646__" +#endif + +const size_t buf_size = 1024; + +void emulate_terminal( int fd ); +int copy( int src, int dest ); +int termemu( int fd, Terminal::Emulator *terminal ); + +int main( int argc __attribute__((unused)), + char *argv[] __attribute__((unused)), + char *envp[] ) +{ + int master; + struct termios saved_termios, raw_termios, child_termios; + + if ( NULL == setlocale( LC_ALL, "" ) ) { + perror( "setlocale" ); + exit( 1 ); + } + + if ( strcmp( nl_langinfo( CODESET ), "UTF-8" ) != 0 ) { + fprintf( stderr, "rtm requires a UTF-8 locale.\n" ); + exit( 1 ); + } + + if ( tcgetattr( STDIN_FILENO, &saved_termios ) < 0 ) { + perror( "tcgetattr" ); + exit( 1 ); + } + + child_termios = saved_termios; + + if ( !(child_termios.c_iflag & IUTF8) ) { + fprintf( stderr, "Warning: Locale is UTF-8 but termios IUTF8 flag not set. Setting IUTF8 flag.\n" ); + child_termios.c_iflag |= IUTF8; + } + + pid_t child = forkpty( &master, NULL, &child_termios, NULL ); + + if ( child == -1 ) { + perror( "forkpty" ); + exit( 1 ); + } + + if ( child == 0 ) { + /* child */ + if ( chdir( "/" ) < 0 ) { + perror( "chdir" ); + exit( 1 ); + } + + char *my_argv[ 2 ]; + my_argv[ 0 ] = strdup( "/bin/bash" ); + assert( my_argv[ 0 ] ); + + my_argv[ 1 ] = NULL; + + if ( execve( "/bin/bash", my_argv, envp ) < 0 ) { + perror( "execve" ); + exit( 1 ); + } + exit( 0 ); + } else { + /* parent */ + raw_termios = saved_termios; + + cfmakeraw( &raw_termios ); + + if ( tcsetattr( STDIN_FILENO, TCSANOW, &raw_termios ) < 0 ) { + perror( "tcsetattr" ); + exit( 1 ); + } + + emulate_terminal( master ); + + if ( tcsetattr( STDIN_FILENO, TCSANOW, &saved_termios ) < 0 ) { + perror( "tcsetattr" ); + exit( 1 ); + } + } + + return 0; +} + +void emulate_terminal( int fd ) +{ + Terminal::Emulator terminal( 80, 24 ); + struct pollfd pollfds[ 2 ]; + + pollfds[ 0 ].fd = STDIN_FILENO; + pollfds[ 0 ].events = POLLIN; + + pollfds[ 1 ].fd = fd; + pollfds[ 1 ].events = POLLIN; + + while ( 1 ) { + int active_fds = poll( pollfds, 2, -1 ); + if ( active_fds <= 0 ) { + perror( "poll" ); + return; + } + + if ( pollfds[ 0 ].revents & POLLIN ) { + if ( copy( STDIN_FILENO, fd ) < 0 ) { + return; + } + } else if ( pollfds[ 1 ].revents & POLLIN ) { + if ( termemu( fd, &terminal ) < 0 ) { + return; + } + } else if ( (pollfds[ 0 ].revents | pollfds[ 1 ].revents) + & (POLLERR | POLLHUP | POLLNVAL) ) { + return; + } else { + fprintf( stderr, "poll mysteriously woken up\n" ); + } + } +} + +int copy( int src, int dest ) +{ + char buf[ buf_size ]; + + ssize_t bytes_read = read( src, buf, buf_size ); + if ( bytes_read == 0 ) { /* EOF */ + return -1; + } else if ( bytes_read < 0 ) { + perror( "read" ); + return -1; + } else { + ssize_t total_bytes_written = 0; + while ( total_bytes_written < bytes_read ) { + ssize_t bytes_written = write( dest, buf + total_bytes_written, + bytes_read - total_bytes_written ); + if ( bytes_written <= 0 ) { + perror( "write" ); + return -1; + } else { + total_bytes_written += bytes_written; + } + } + } + + return 0; +} + +int termemu( int fd, Terminal::Emulator *terminal ) +{ + char buf[ buf_size ]; + + /* fill buffer if possible */ + ssize_t bytes_read = read( fd, buf, buf_size ); + if ( bytes_read == 0 ) { /* EOF */ + return -1; + } else if ( bytes_read < 0 ) { + perror( "read" ); + return -1; + } + + /* feed to terminal */ + for ( int i = 0; i < bytes_read; i++ ) { + terminal->input( buf[ i ] ); + } + + return 0; +} diff --git a/terminal.cpp b/terminal.cpp index 9c17ba7..cd3c3cb 100644 --- a/terminal.cpp +++ b/terminal.cpp @@ -1 +1,63 @@ #include "terminal.hpp" + +using namespace Terminal; + +Cell::Cell() + : overlapping_cell( NULL ), + contents(), + overlapped_cells() +{} + +Cell::Cell( const Cell &x ) + : overlapping_cell( x.overlapping_cell ), + contents( x.contents ), + overlapped_cells( x.overlapped_cells ) +{} + +Cell & Cell::operator=( const Cell &x ) +{ + overlapping_cell = x.overlapping_cell; + contents = x.contents; + overlapped_cells = x.overlapped_cells; + + return *this; +} + +Row::Row( size_t s_width ) + : cells( s_width ) +{} + +Emulator::Emulator( size_t s_width, size_t s_height ) + : parser(), + width( s_width ), height( s_height ), + cursor_col( 0 ), cursor_row( 0 ), + combining_char_col( 0 ), combining_char_row( 0 ), + rows( height, Row( width ) ) +{ + +} + +Emulator::~Emulator() +{ + +} + +void Emulator::input( char c ) +{ + std::vector vec = parser.input( c ); + + for ( std::vector::iterator i = vec.begin(); + i != vec.end(); + i++ ) { + Parser::Action *act = *i; + + act->act_on_terminal( this ); + + delete act; + } +} + +void Emulator::print( Parser::Print *act ) +{ + fprintf( stderr, "print (%lc)!\r\n", act->ch ); +} diff --git a/terminal.hpp b/terminal.hpp index 65ac424..d5a0d70 100644 --- a/terminal.hpp +++ b/terminal.hpp @@ -5,23 +5,49 @@ #include "parser.hpp" namespace Terminal { - class Terminal { + class Cell { + public: + Cell *overlapping_cell; + std::vector contents; + std::vector overlapped_cells; + + Cell(); + + Cell( const Cell & ); + Cell & operator=( const Cell & ); +}; + + class Row { + public: + std::vector cells; + + Row( size_t s_width ); + }; + + class Emulator { + friend void Parser::Print::act_on_terminal( Emulator * ); + private: Parser::UTF8Parser parser; - wchar_t *framebuffer; size_t width, height; - size_t cursor_col, cursor_row; + size_t combining_char_col, combining_char_row; + + std::vector rows; + + void print( Parser::Print *act ); public: - Terminal(); - ~Terminal(); + Emulator( size_t s_width, size_t s_height ); + ~Emulator(); - - - Terminal( const Terminal & ); - Terminal &operator=( const Terminal & ); + void input( char c ); + + void resize( size_t s_width, size_t s_height ); + + size_t get_width( void ) { return width; } + size_t get_height( void ) { return height; } }; }