Abstract cursor movement
This commit is contained in:
+65
-76
@@ -45,24 +45,18 @@ void Emulator::execute( Parser::Execute *act )
|
||||
|
||||
switch ( act->ch ) {
|
||||
case 0x0a: /* LF */
|
||||
fb.cursor_row++;
|
||||
fb.autoscroll();
|
||||
fb.newgrapheme();
|
||||
fb.move_rows_autoscroll( 1 );
|
||||
act->handled = true;
|
||||
break;
|
||||
|
||||
case 0x0d: /* CR */
|
||||
fb.cursor_col = 0;
|
||||
fb.newgrapheme();
|
||||
fb.ds.move_col( 0 );
|
||||
act->handled = true;
|
||||
break;
|
||||
|
||||
case 0x08: /* BS */
|
||||
if ( fb.cursor_col > 0 ) {
|
||||
fb.cursor_col--;
|
||||
fb.newgrapheme(); /* this is not xterm's behavior */
|
||||
act->handled = true;
|
||||
}
|
||||
fb.ds.move_col( -1, true );
|
||||
act->handled = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
@@ -71,56 +65,47 @@ void Emulator::print( Parser::Print *act )
|
||||
{
|
||||
assert( act->char_present );
|
||||
|
||||
if ( (fb.width == 0) || (fb.height == 0) ) {
|
||||
int chwidth = act->ch == L'\0' ? -1 : wcwidth( act->ch );
|
||||
|
||||
Cell *this_cell = fb.get_cell();
|
||||
|
||||
if ( !this_cell ) { /* zero-size framebuffer */
|
||||
return;
|
||||
}
|
||||
|
||||
assert( fb.cursor_row < fb.height ); /* must be on screen */
|
||||
assert( fb.cursor_col <= fb.width + 1 ); /* two off is ok */
|
||||
assert( fb.combining_char_row < fb.height );
|
||||
assert( fb.combining_char_col < fb.width );
|
||||
|
||||
int chwidth = act->ch == L'\0' ? -1 : wcwidth( act->ch );
|
||||
|
||||
Cell *this_cell;
|
||||
Cell *combining_cell = fb.get_combining_cell();
|
||||
|
||||
switch ( chwidth ) {
|
||||
case 1: /* normal character */
|
||||
case 2: /* wide character */
|
||||
if ( fb.cursor_col >= fb.width ) { /* wrap */
|
||||
fb.cursor_col = 0;
|
||||
fb.cursor_row++;
|
||||
if ( fb.ds.next_print_will_wrap ) {
|
||||
fb.ds.move_col( 0 );
|
||||
fb.move_rows_autoscroll( 1 );
|
||||
}
|
||||
|
||||
fb.autoscroll();
|
||||
fb.newgrapheme();
|
||||
this_cell = fb.get_cell();
|
||||
|
||||
this_cell = &fb.rows[ fb.cursor_row ].cells[ fb.cursor_col ];
|
||||
this_cell->reset();
|
||||
this_cell->contents.push_back( act->ch );
|
||||
this_cell->width = chwidth;
|
||||
|
||||
if ( (fb.cursor_col < fb.width - 1) && (chwidth == 2) ) {
|
||||
Cell *next_cell = &fb.rows[ fb.cursor_row ].cells[ fb.cursor_col + 1 ];
|
||||
this_cell->overlapped_cells.push_back( next_cell );
|
||||
next_cell->overlapping_cell = this_cell;
|
||||
}
|
||||
fb.claim_overlap( fb.ds.get_cursor_row(), fb.ds.get_cursor_col() );
|
||||
|
||||
fb.ds.move_col( chwidth, true, true );
|
||||
|
||||
fb.cursor_col += chwidth;
|
||||
act->handled = true;
|
||||
break;
|
||||
case 0: /* combining character */
|
||||
if ( fb.rows[ fb.combining_char_row ].cells[ fb.combining_char_col ].contents.size() == 0 ) {
|
||||
if ( combining_cell->contents.size() == 0 ) {
|
||||
/* cell starts with combining character */
|
||||
fb.rows[ fb.combining_char_row ].cells[ fb.combining_char_col ].fallback = true;
|
||||
assert( fb.cursor_col == fb.combining_char_col );
|
||||
assert( fb.cursor_row == fb.combining_char_row );
|
||||
assert( fb.cursor_col < fb.width );
|
||||
fb.cursor_col++;
|
||||
/* a combining character should never be able to wrap us */
|
||||
assert( this_cell == combining_cell );
|
||||
assert( combining_cell->width == 1 );
|
||||
combining_cell->fallback = true;
|
||||
fb.ds.move_col( 1, true, true );
|
||||
}
|
||||
|
||||
if ( fb.rows[ fb.combining_char_row ].cells[ fb.combining_char_col ].contents.size() < 16 ) { /* seems like a reasonable limit on combining character */
|
||||
fb.rows[ fb.combining_char_row ].cells[ fb.combining_char_col ].contents.push_back( act->ch );
|
||||
if ( combining_cell->contents.size() < 16 ) { /* seems like a reasonable limit on combining characters */
|
||||
combining_cell->contents.push_back( act->ch );
|
||||
}
|
||||
act->handled = true;
|
||||
break;
|
||||
@@ -131,42 +116,6 @@ void Emulator::print( Parser::Print *act )
|
||||
}
|
||||
}
|
||||
|
||||
void Emulator::debug_printout( int fd )
|
||||
{
|
||||
std::string screen;
|
||||
screen.append( "\033[H\033[2J" );
|
||||
|
||||
for ( int y = 0; y < fb.height; y++ ) {
|
||||
for ( int x = 0; x < fb.width; x++ ) {
|
||||
char curmove[ 32 ];
|
||||
snprintf( curmove, 32, "\033[%d;%dH", y + 1, x + 1 );
|
||||
screen.append( curmove );
|
||||
Cell *cell = &fb.rows[ y ].cells[ x ];
|
||||
if ( cell->overlapping_cell ) continue;
|
||||
|
||||
if ( cell->fallback ) {
|
||||
char utf8[ 8 ];
|
||||
snprintf( utf8, 8, "%lc", 0xA0 );
|
||||
screen.append( utf8 );
|
||||
}
|
||||
|
||||
for ( std::vector<wchar_t>::iterator i = cell->contents.begin();
|
||||
i != cell->contents.end();
|
||||
i++ ) {
|
||||
char utf8[ 8 ];
|
||||
snprintf( utf8, 8, "%lc", *i );
|
||||
screen.append( utf8 );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
char curmove[ 32 ];
|
||||
snprintf( curmove, 32, "\033[%d;%dH", fb.cursor_row + 1, fb.cursor_col + 1 );
|
||||
screen.append( curmove );
|
||||
|
||||
swrite( fd, screen.c_str() );
|
||||
}
|
||||
|
||||
void Emulator::CSI_dispatch( Parser::CSI_Dispatch *act )
|
||||
{
|
||||
/* add final char to dispatch key */
|
||||
@@ -212,3 +161,43 @@ void Emulator::Esc_dispatch( Parser::Esc_Dispatch *act )
|
||||
act->handled = true;
|
||||
}
|
||||
}
|
||||
|
||||
void Emulator::debug_printout( int fd )
|
||||
{
|
||||
std::string screen;
|
||||
screen.append( "\033[H\033[2J" );
|
||||
|
||||
for ( int y = 0; y < fb.ds.get_height(); y++ ) {
|
||||
for ( int x = 0; x < fb.ds.get_width(); x++ ) {
|
||||
char curmove[ 32 ];
|
||||
snprintf( curmove, 32, "\033[%d;%dH", y + 1, x + 1 );
|
||||
screen.append( curmove );
|
||||
Cell *cell = fb.get_cell( y, x );
|
||||
if ( cell->overlapping_cell ) continue;
|
||||
|
||||
assert( (cell->overlapped_cells.size() + 1 == (size_t)cell->width)
|
||||
|| (x == fb.ds.get_width() - 1) );
|
||||
|
||||
if ( cell->fallback ) {
|
||||
char utf8[ 8 ];
|
||||
snprintf( utf8, 8, "%lc", 0xA0 );
|
||||
screen.append( utf8 );
|
||||
}
|
||||
|
||||
for ( std::vector<wchar_t>::iterator i = cell->contents.begin();
|
||||
i != cell->contents.end();
|
||||
i++ ) {
|
||||
char utf8[ 8 ];
|
||||
snprintf( utf8, 8, "%lc", *i );
|
||||
screen.append( utf8 );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
char curmove[ 32 ];
|
||||
snprintf( curmove, 32, "\033[%d;%dH", fb.ds.get_cursor_row() + 1,
|
||||
fb.ds.get_cursor_col() + 1 );
|
||||
screen.append( curmove );
|
||||
|
||||
swrite( fd, screen.c_str() );
|
||||
}
|
||||
|
||||
+33
-47
@@ -4,49 +4,42 @@
|
||||
|
||||
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 ( as.params == "1" ) { /* start of screen to active position, inclusive */
|
||||
for ( int x = 0; x <= fb.cursor_col; x++ ) {
|
||||
if ( x < fb.width ) {
|
||||
fb.rows[ fb.cursor_row ].cells[ x ].reset();
|
||||
}
|
||||
}
|
||||
start = 0;
|
||||
end = fb.ds.get_cursor_col();
|
||||
} else if ( as.params == "2" ) { /* all of line */
|
||||
fb.rows[ fb.cursor_row ] = Row( fb.width );
|
||||
} else { /* active position to end of line, inclusive */
|
||||
for ( int x = fb.cursor_col; x < fb.width; x++ ) {
|
||||
fb.rows[ fb.cursor_row ].cells[ x ].reset();
|
||||
}
|
||||
start = 0;
|
||||
}
|
||||
|
||||
clearline( &fb, -1, start, end );
|
||||
}
|
||||
|
||||
void Emulator::CSI_ED( void ) {
|
||||
if ( as.params == "1" ) { /* start of screen to active position, inclusive */
|
||||
for ( int y = 0; y < fb.cursor_row; y++ ) {
|
||||
for ( int x = 0; x < fb.width; x++ ) {
|
||||
fb.rows[ y ].cells[ x ].reset();
|
||||
}
|
||||
}
|
||||
for ( int x = 0; x <= fb.cursor_col; x++ ) {
|
||||
if ( x < fb.width ) {
|
||||
fb.rows[ fb.cursor_row ].cells[ x ].reset();
|
||||
}
|
||||
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 ( as.params == "2" ) { /* entire screen */
|
||||
for ( int y = 0; y < fb.height; y++ ) {
|
||||
for ( int x = 0; x < fb.width; x++ ) {
|
||||
fb.rows[ y ].cells[ x ].reset();
|
||||
}
|
||||
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 */
|
||||
for ( int x = fb.cursor_col; x < fb.width; x++ ) {
|
||||
fb.rows[ fb.cursor_row ].cells[ x ].reset();
|
||||
}
|
||||
for ( int y = fb.cursor_row + 1; y < fb.height; y++ ) {
|
||||
for ( int x = 0; x < fb.width; x++ ) {
|
||||
fb.rows[ y ].cells[ x ].reset();
|
||||
}
|
||||
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 );
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -57,31 +50,24 @@ void Emulator::CSI_cursormove( void )
|
||||
|
||||
switch ( as.dispatch_chars[ 0 ] ) {
|
||||
case 'A':
|
||||
fb.cursor_row -= num;
|
||||
fb.ds.move_row( -num, true );
|
||||
break;
|
||||
case 'B':
|
||||
fb.cursor_row += num;
|
||||
fb.ds.move_row( num, true );
|
||||
break;
|
||||
case 'C':
|
||||
fb.cursor_col += num;
|
||||
fb.ds.move_col( num, true );
|
||||
break;
|
||||
case 'D':
|
||||
fb.cursor_col -= num;
|
||||
fb.ds.move_col( -num, true );
|
||||
break;
|
||||
case 'H':
|
||||
case 'f':
|
||||
int x = as.getparam( 0, 1 );
|
||||
int y = as.getparam( 1, 1 );
|
||||
fb.cursor_row = x - 1;
|
||||
fb.cursor_col = y - 1;
|
||||
fb.ds.move_row( x - 1 );
|
||||
fb.ds.move_col( y - 1 );
|
||||
}
|
||||
|
||||
if ( fb.cursor_row < 0 ) fb.cursor_row = 0;
|
||||
if ( fb.cursor_row >= fb.height ) fb.cursor_row = fb.height - 1;
|
||||
if ( fb.cursor_col < 0 ) fb.cursor_col = 0;
|
||||
if ( fb.cursor_col >= fb.width ) fb.cursor_col = fb.width - 1;
|
||||
|
||||
fb.newgrapheme();
|
||||
}
|
||||
|
||||
void Emulator::CSI_DA( void )
|
||||
@@ -91,10 +77,10 @@ void Emulator::CSI_DA( void )
|
||||
|
||||
void Emulator::Esc_DECALN( void )
|
||||
{
|
||||
for ( int y = 0; y < fb.height; y++ ) {
|
||||
for ( int x = 0; x < fb.width; x++ ) {
|
||||
fb.rows[ y ].cells[ x ].reset();
|
||||
fb.rows[ y ].cells[ x ].contents.push_back( L'E' );
|
||||
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' );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+98
-11
@@ -8,14 +8,16 @@ Cell::Cell()
|
||||
: overlapping_cell( NULL ),
|
||||
contents(),
|
||||
overlapped_cells(),
|
||||
fallback( false )
|
||||
fallback( false ),
|
||||
width( 1 )
|
||||
{}
|
||||
|
||||
Cell::Cell( const Cell &x )
|
||||
: overlapping_cell( x.overlapping_cell ),
|
||||
contents( x.contents ),
|
||||
overlapped_cells( x.overlapped_cells ),
|
||||
fallback( x.fallback )
|
||||
fallback( x.fallback ),
|
||||
width( 1 )
|
||||
{}
|
||||
|
||||
Cell & Cell::operator=( const Cell &x )
|
||||
@@ -36,6 +38,7 @@ void Cell::reset( void )
|
||||
{
|
||||
contents.clear();
|
||||
fallback = false;
|
||||
width = 1;
|
||||
|
||||
if ( overlapping_cell ) {
|
||||
assert( overlapped_cells.size() == 0 );
|
||||
@@ -50,11 +53,15 @@ void Cell::reset( void )
|
||||
}
|
||||
}
|
||||
|
||||
Framebuffer::Framebuffer( int s_width, int s_height )
|
||||
DrawState::DrawState( int s_width, int s_height )
|
||||
: width( s_width ), height( s_height ),
|
||||
cursor_col( 0 ), cursor_row( 0 ),
|
||||
combining_char_col( 0 ), combining_char_row( 0 ),
|
||||
rows( height, Row( width ) )
|
||||
next_print_will_wrap( false )
|
||||
{}
|
||||
|
||||
Framebuffer::Framebuffer( int s_width, int s_height )
|
||||
: rows( s_height, Row( s_width ) ), ds( s_width, s_height )
|
||||
{}
|
||||
|
||||
void Framebuffer::scroll( int N )
|
||||
@@ -63,22 +70,102 @@ void Framebuffer::scroll( int N )
|
||||
|
||||
for ( int i = 0; i < N; i++ ) {
|
||||
rows.pop_front();
|
||||
rows.push_back( Row( width ) );
|
||||
cursor_row--;
|
||||
combining_char_row--;
|
||||
rows.push_back( Row( ds.get_width() ) );
|
||||
ds.move_row( -1, true );
|
||||
}
|
||||
}
|
||||
|
||||
void Framebuffer::newgrapheme( void )
|
||||
void DrawState::new_grapheme( void )
|
||||
{
|
||||
combining_char_col = cursor_col;
|
||||
combining_char_row = cursor_row;
|
||||
}
|
||||
|
||||
void Framebuffer::autoscroll( void )
|
||||
void DrawState::snap_cursor_to_border( void )
|
||||
{
|
||||
if ( cursor_row >= height ) { /* scroll */
|
||||
scroll( cursor_row - height + 1 );
|
||||
if ( cursor_row < 0 ) cursor_row = 0;
|
||||
if ( cursor_row >= height ) cursor_row = height - 1;
|
||||
if ( cursor_col < 0 ) cursor_col = 0;
|
||||
if ( cursor_col >= width ) cursor_col = width - 1;
|
||||
}
|
||||
|
||||
void DrawState::move_row( int N, bool relative )
|
||||
{
|
||||
if ( relative ) {
|
||||
cursor_row += N;
|
||||
} else {
|
||||
cursor_row = N;
|
||||
}
|
||||
|
||||
snap_cursor_to_border();
|
||||
new_grapheme();
|
||||
next_print_will_wrap = false;
|
||||
}
|
||||
|
||||
void DrawState::move_col( int N, bool relative, bool implicit )
|
||||
{
|
||||
if ( implicit ) {
|
||||
new_grapheme();
|
||||
}
|
||||
|
||||
if ( relative ) {
|
||||
cursor_col += N;
|
||||
} else {
|
||||
cursor_col = N;
|
||||
}
|
||||
|
||||
if ( implicit && (cursor_col >= width) ) {
|
||||
next_print_will_wrap = true;
|
||||
}
|
||||
|
||||
snap_cursor_to_border();
|
||||
if ( !implicit ) {
|
||||
new_grapheme();
|
||||
next_print_will_wrap = false;
|
||||
}
|
||||
}
|
||||
|
||||
void Framebuffer::move_rows_autoscroll( int rows )
|
||||
{
|
||||
if ( ds.get_cursor_row() + rows >= ds.get_height() ) {
|
||||
scroll( ds.get_height() - ds.get_cursor_row() - rows + 1 );
|
||||
}
|
||||
|
||||
ds.move_row( rows, true );
|
||||
}
|
||||
|
||||
Cell *Framebuffer::get_cell( void )
|
||||
{
|
||||
if ( (ds.get_width() == 0) || (ds.get_height() == 0) ) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
return &rows[ ds.get_cursor_row() ].cells[ ds.get_cursor_col() ];
|
||||
}
|
||||
|
||||
Cell *Framebuffer::get_cell( int row, int col )
|
||||
{
|
||||
if ( row == -1 ) row = ds.get_cursor_row();
|
||||
if ( col == -1 ) col = ds.get_cursor_col();
|
||||
|
||||
return &rows[ row ].cells[ col ];
|
||||
}
|
||||
|
||||
Cell *Framebuffer::get_combining_cell( void )
|
||||
{
|
||||
return &rows[ ds.get_combining_char_row() ].cells[ ds.get_combining_char_col() ];
|
||||
}
|
||||
|
||||
void Framebuffer::claim_overlap( int row, int col )
|
||||
{
|
||||
Cell *the_cell = &rows[ row ].cells[ col ];
|
||||
|
||||
for ( int i = col + 1; i < col + the_cell->width; i++ ) {
|
||||
if ( i < ds.get_width() ) {
|
||||
Cell *next_cell = get_cell( row, i );
|
||||
next_cell->reset();
|
||||
the_cell->overlapped_cells.push_back( next_cell );
|
||||
next_cell->overlapping_cell = the_cell;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+37
-4
@@ -13,6 +13,7 @@ namespace Terminal {
|
||||
std::vector<wchar_t> contents;
|
||||
std::vector<Cell *> overlapped_cells;
|
||||
char fallback; /* first character is combining character */
|
||||
int width;
|
||||
|
||||
Cell();
|
||||
|
||||
@@ -29,19 +30,51 @@ namespace Terminal {
|
||||
Row( size_t s_width );
|
||||
};
|
||||
|
||||
class Framebuffer {
|
||||
public:
|
||||
class DrawState {
|
||||
private:
|
||||
int width, height;
|
||||
|
||||
void new_grapheme( void );
|
||||
void snap_cursor_to_border( void );
|
||||
|
||||
int cursor_col, cursor_row;
|
||||
int combining_char_col, combining_char_row;
|
||||
|
||||
public:
|
||||
bool next_print_will_wrap;
|
||||
|
||||
/* bold, etc. */
|
||||
|
||||
void move_row( int N, bool relative = false );
|
||||
void move_col( int N, bool relative = false, bool implicit = false );
|
||||
|
||||
int get_cursor_col( void ) { return cursor_col; }
|
||||
int get_cursor_row( void ) { return cursor_row; }
|
||||
int get_combining_char_col( void ) { return combining_char_col; }
|
||||
int get_combining_char_row( void ) { return combining_char_row; }
|
||||
int get_width( void ) { return width; }
|
||||
int get_height( void ) { return height; }
|
||||
|
||||
DrawState( int s_width, int s_height );
|
||||
};
|
||||
|
||||
class Framebuffer {
|
||||
private:
|
||||
std::deque<Row> rows;
|
||||
|
||||
void scroll( int N );
|
||||
void autoscroll( void );
|
||||
void newgrapheme( void );
|
||||
|
||||
public:
|
||||
Framebuffer( int s_width, int s_height );
|
||||
DrawState ds;
|
||||
|
||||
void move_rows_autoscroll( int rows );
|
||||
|
||||
Cell *get_cell( void );
|
||||
Cell *get_cell( int row, int col );
|
||||
Cell *get_combining_cell( void );
|
||||
|
||||
void claim_overlap( int row, int col );
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user