Simple actions dispatching on terminal emulator
This commit is contained in:
@@ -1,6 +1,6 @@
|
|||||||
source = parse.cpp parserstate.cpp parser.cpp templates.cpp terminal.cpp
|
source = parse.cpp parserstate.cpp parser.cpp templates.cpp terminal.cpp termemu.cpp parseraction.cpp
|
||||||
objects = parserstate.o parser.o templates.o terminal.o
|
objects = parserstate.o parser.o templates.o terminal.o parseraction.o
|
||||||
executables = parse
|
executables = parse termemu
|
||||||
|
|
||||||
CXX = g++
|
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
|
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)
|
parse: parse.o $(objects)
|
||||||
$(CXX) $(CXXFLAGS) -o $@ $+ $(LIBS)
|
$(CXX) $(CXXFLAGS) -o $@ $+ $(LIBS)
|
||||||
|
|
||||||
|
termemu: termemu.o $(objects)
|
||||||
|
$(CXX) $(CXXFLAGS) -o $@ $+ $(LIBS)
|
||||||
|
|
||||||
templates.o: templates.cpp
|
templates.o: templates.cpp
|
||||||
$(CXX) $(CXXFLAGS) -frepo -c -o $@ $<
|
$(CXX) $(CXXFLAGS) -frepo -c -o $@ $<
|
||||||
|
|
||||||
|
|||||||
+1
-1
@@ -23,7 +23,7 @@ namespace Parser {
|
|||||||
Parser() : family(), state( &family.s_Ground ) {}
|
Parser() : family(), state( &family.s_Ground ) {}
|
||||||
|
|
||||||
Parser( const Parser & );
|
Parser( const Parser & );
|
||||||
bool operator=( const Parser & );
|
Parser & operator=( const Parser & );
|
||||||
~Parser() {}
|
~Parser() {}
|
||||||
|
|
||||||
std::vector<Action *> input( wchar_t ch );
|
std::vector<Action *> input( wchar_t ch );
|
||||||
|
|||||||
@@ -0,0 +1,9 @@
|
|||||||
|
#include "parseraction.hpp"
|
||||||
|
#include "terminal.hpp"
|
||||||
|
|
||||||
|
using namespace Parser;
|
||||||
|
|
||||||
|
void Print::act_on_terminal( Terminal::Emulator *emu )
|
||||||
|
{
|
||||||
|
emu->print( this );
|
||||||
|
}
|
||||||
+9
-1
@@ -3,6 +3,10 @@
|
|||||||
|
|
||||||
#include <string>
|
#include <string>
|
||||||
|
|
||||||
|
namespace Terminal {
|
||||||
|
class Emulator;
|
||||||
|
}
|
||||||
|
|
||||||
namespace Parser {
|
namespace Parser {
|
||||||
class Action
|
class Action
|
||||||
{
|
{
|
||||||
@@ -12,6 +16,8 @@ namespace Parser {
|
|||||||
|
|
||||||
virtual std::string name( void ) = 0;
|
virtual std::string name( void ) = 0;
|
||||||
|
|
||||||
|
virtual void act_on_terminal( Terminal::Emulator * ) {};
|
||||||
|
|
||||||
Action() : char_present( false ), ch( -1 ) {};
|
Action() : char_present( false ), ch( -1 ) {};
|
||||||
virtual ~Action() {};
|
virtual ~Action() {};
|
||||||
};
|
};
|
||||||
@@ -20,7 +26,9 @@ namespace Parser {
|
|||||||
public: std::string name( void ) { return std::string( "Ignore" ); }
|
public: std::string name( void ) { return std::string( "Ignore" ); }
|
||||||
};
|
};
|
||||||
class Print : public Action {
|
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 {
|
class Execute : public Action {
|
||||||
public: std::string name( void ) { return std::string( "Execute" ); }
|
public: std::string name( void ) { return std::string( "Execute" ); }
|
||||||
|
|||||||
+10
-1
@@ -1,5 +1,14 @@
|
|||||||
#include <vector>
|
#include <vector>
|
||||||
|
#include <wchar.h>
|
||||||
|
|
||||||
#include "parseraction.hpp"
|
#include "terminal.hpp"
|
||||||
|
|
||||||
|
namespace Parser {
|
||||||
|
class Action;
|
||||||
|
}
|
||||||
|
|
||||||
template class std::vector<Parser::Action *>;
|
template class std::vector<Parser::Action *>;
|
||||||
|
template class std::vector<Terminal::Cell>;
|
||||||
|
template class std::vector<Terminal::Row>;
|
||||||
|
template class std::vector<Terminal::Cell *>;
|
||||||
|
template class std::vector<wchar_t>;
|
||||||
|
|||||||
+186
@@ -0,0 +1,186 @@
|
|||||||
|
#include <pty.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <signal.h>
|
||||||
|
#include <errno.h>
|
||||||
|
#include <poll.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <locale.h>
|
||||||
|
#include <langinfo.h>
|
||||||
|
#include <wchar.h>
|
||||||
|
#include <assert.h>
|
||||||
|
#include <wctype.h>
|
||||||
|
#include <iostream>
|
||||||
|
#include <typeinfo>
|
||||||
|
|
||||||
|
#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;
|
||||||
|
}
|
||||||
@@ -1 +1,63 @@
|
|||||||
#include "terminal.hpp"
|
#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<Parser::Action *> vec = parser.input( c );
|
||||||
|
|
||||||
|
for ( std::vector<Parser::Action *>::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 );
|
||||||
|
}
|
||||||
|
|||||||
+33
-7
@@ -5,23 +5,49 @@
|
|||||||
#include "parser.hpp"
|
#include "parser.hpp"
|
||||||
|
|
||||||
namespace Terminal {
|
namespace Terminal {
|
||||||
class Terminal {
|
class Cell {
|
||||||
|
public:
|
||||||
|
Cell *overlapping_cell;
|
||||||
|
std::vector<wchar_t> contents;
|
||||||
|
std::vector<Cell *> overlapped_cells;
|
||||||
|
|
||||||
|
Cell();
|
||||||
|
|
||||||
|
Cell( const Cell & );
|
||||||
|
Cell & operator=( const Cell & );
|
||||||
|
};
|
||||||
|
|
||||||
|
class Row {
|
||||||
|
public:
|
||||||
|
std::vector<Cell> cells;
|
||||||
|
|
||||||
|
Row( size_t s_width );
|
||||||
|
};
|
||||||
|
|
||||||
|
class Emulator {
|
||||||
|
friend void Parser::Print::act_on_terminal( Emulator * );
|
||||||
|
|
||||||
private:
|
private:
|
||||||
Parser::UTF8Parser parser;
|
Parser::UTF8Parser parser;
|
||||||
|
|
||||||
wchar_t *framebuffer;
|
|
||||||
size_t width, height;
|
size_t width, height;
|
||||||
|
|
||||||
size_t cursor_col, cursor_row;
|
size_t cursor_col, cursor_row;
|
||||||
|
size_t combining_char_col, combining_char_row;
|
||||||
|
|
||||||
|
std::vector<Row> rows;
|
||||||
|
|
||||||
|
void print( Parser::Print *act );
|
||||||
|
|
||||||
public:
|
public:
|
||||||
Terminal();
|
Emulator( size_t s_width, size_t s_height );
|
||||||
~Terminal();
|
~Emulator();
|
||||||
|
|
||||||
|
void input( char c );
|
||||||
|
|
||||||
|
void resize( size_t s_width, size_t s_height );
|
||||||
|
|
||||||
Terminal( const Terminal & );
|
size_t get_width( void ) { return width; }
|
||||||
Terminal &operator=( const Terminal & );
|
size_t get_height( void ) { return height; }
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user