Rewrite terminal overlay
This commit is contained in:
@@ -63,6 +63,13 @@ namespace Terminal {
|
|||||||
}
|
}
|
||||||
|
|
||||||
wchar_t debug_contents( void ) const;
|
wchar_t debug_contents( void ) const;
|
||||||
|
|
||||||
|
bool is_blank( void ) const
|
||||||
|
{
|
||||||
|
return ( contents.empty()
|
||||||
|
|| ( (contents.size() == 1) && ( (contents.front() == 0x20)
|
||||||
|
|| (contents.front() == 0xA0) ) ) );
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
class Row {
|
class Row {
|
||||||
|
|||||||
+337
-136
@@ -8,7 +8,16 @@
|
|||||||
|
|
||||||
using namespace Overlay;
|
using namespace Overlay;
|
||||||
|
|
||||||
void ConditionalOverlayCell::apply( Framebuffer &fb, bool show_tentative, int row, bool flag ) const
|
bool ConditionalOverlay::start_clock( uint64_t local_frame_acked, uint64_t now )
|
||||||
|
{
|
||||||
|
if ( (local_frame_acked >= expiration_frame) && (expiration_time == uint64_t(-1)) ) {
|
||||||
|
expiration_time = now + 25;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
void ConditionalOverlayCell::apply( Framebuffer &fb, uint64_t confirmed_epoch, int row, bool flag ) const
|
||||||
{
|
{
|
||||||
if ( (!active)
|
if ( (!active)
|
||||||
|| (row >= fb.ds.get_height())
|
|| (row >= fb.ds.get_height())
|
||||||
@@ -16,7 +25,12 @@ void ConditionalOverlayCell::apply( Framebuffer &fb, bool show_tentative, int ro
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if ( tentative && (!show_tentative) ) {
|
if ( tentative( confirmed_epoch ) ) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ( unknown ) {
|
||||||
|
fb.get_mutable_cell( row, col )->contents.clear();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -37,7 +51,7 @@ void ConditionalOverlayCell::apply( Framebuffer &fb, bool show_tentative, int ro
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Validity ConditionalOverlayCell::get_validity( const Framebuffer &fb, int row, uint64_t current_frame ) const
|
Validity ConditionalOverlayCell::get_validity( const Framebuffer &fb, int row, uint64_t current_frame, uint64_t now ) const
|
||||||
{
|
{
|
||||||
if ( !active ) {
|
if ( !active ) {
|
||||||
return Inactive;
|
return Inactive;
|
||||||
@@ -51,44 +65,58 @@ Validity ConditionalOverlayCell::get_validity( const Framebuffer &fb, int row, u
|
|||||||
const Cell ¤t = *( fb.get_cell( row, col ) );
|
const Cell ¤t = *( fb.get_cell( row, col ) );
|
||||||
|
|
||||||
/* see if it hasn't been updated yet */
|
/* see if it hasn't been updated yet */
|
||||||
if ( (current_frame < expiration_frame) ) {
|
if ( current_frame < expiration_frame ) {
|
||||||
|
return Pending;
|
||||||
|
}
|
||||||
|
|
||||||
|
assert( expiration_time != uint64_t(-1) );
|
||||||
|
|
||||||
|
if ( now < expiration_time ) {
|
||||||
return Pending;
|
return Pending;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* special case deletion */
|
/* special case deletion */
|
||||||
if ( replacement.contents.empty() && (current.contents.size() == 1) && (current.contents.front() == 0x20) ) {
|
if ( current.is_blank() && replacement.is_blank() ) {
|
||||||
return Correct;
|
return CorrectNoCredit;
|
||||||
}
|
}
|
||||||
|
|
||||||
if ( replacement.contents.empty() && current.contents.empty() ) {
|
if ( unknown ) {
|
||||||
return Correct;
|
return CorrectNoCredit;
|
||||||
}
|
}
|
||||||
|
|
||||||
if ( current == replacement ) {
|
if ( current.contents == replacement.contents ) {
|
||||||
return Correct;
|
return Correct;
|
||||||
} else {
|
} else {
|
||||||
return IncorrectOrExpired;
|
return IncorrectOrExpired;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Validity ConditionalCursorMove::get_validity( const Framebuffer &fb, uint64_t current_frame ) const
|
Validity ConditionalCursorMove::get_validity( const Framebuffer &fb, uint64_t current_frame, uint64_t now ) const
|
||||||
{
|
{
|
||||||
if ( !active ) {
|
if ( !active ) {
|
||||||
assert( !show_frozen_cursor );
|
|
||||||
return Inactive;
|
return Inactive;
|
||||||
}
|
}
|
||||||
|
|
||||||
if ( (row >= fb.ds.get_height())
|
if ( (row >= fb.ds.get_height())
|
||||||
|| (col >= fb.ds.get_width()) ) {
|
|| (col >= fb.ds.get_width()) ) {
|
||||||
|
assert( false );
|
||||||
// fprintf( stderr, "Crazy cursor (%d,%d)!\n", row, col );
|
// fprintf( stderr, "Crazy cursor (%d,%d)!\n", row, col );
|
||||||
return IncorrectOrExpired;
|
return IncorrectOrExpired;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if ( current_frame < expiration_frame ) {
|
||||||
|
return Pending;
|
||||||
|
}
|
||||||
|
|
||||||
|
assert( expiration_time != uint64_t(-1) );
|
||||||
|
|
||||||
|
if ( now < expiration_time ) {
|
||||||
|
return Pending;
|
||||||
|
}
|
||||||
|
|
||||||
if ( (fb.ds.get_cursor_col() == col)
|
if ( (fb.ds.get_cursor_col() == col)
|
||||||
&& (fb.ds.get_cursor_row() == row) ) {
|
&& (fb.ds.get_cursor_row() == row) ) {
|
||||||
return Correct;
|
return Correct;
|
||||||
} else if ( current_frame < expiration_frame ) {
|
|
||||||
return Pending;
|
|
||||||
} else {
|
} else {
|
||||||
/*
|
/*
|
||||||
fprintf( stderr, "Bad cursor in %d (I thought (%d,%d) vs actual (%d,%d)).\n", (int)current_frame,
|
fprintf( stderr, "Bad cursor in %d (I thought (%d,%d) vs actual (%d,%d)).\n", (int)current_frame,
|
||||||
@@ -98,26 +126,22 @@ Validity ConditionalCursorMove::get_validity( const Framebuffer &fb, uint64_t cu
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void ConditionalCursorMove::apply( Framebuffer &fb ) const
|
void ConditionalCursorMove::apply( Framebuffer &fb, uint64_t confirmed_epoch ) const
|
||||||
{
|
{
|
||||||
if ( !active ) {
|
if ( !active ) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
int target_row = row;
|
if ( tentative( confirmed_epoch ) ) {
|
||||||
int target_col = col;
|
return;
|
||||||
|
|
||||||
if ( show_frozen_cursor ) {
|
|
||||||
target_row = frozen_row;
|
|
||||||
target_col = frozen_col;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
assert( target_row < fb.ds.get_height() );
|
assert( row < fb.ds.get_height() );
|
||||||
assert( target_col < fb.ds.get_width() );
|
assert( col < fb.ds.get_width() );
|
||||||
assert( !fb.ds.origin_mode );
|
assert( !fb.ds.origin_mode );
|
||||||
|
|
||||||
fb.ds.move_row( target_row, false );
|
fb.ds.move_row( row, false );
|
||||||
fb.ds.move_col( target_col, false, false );
|
fb.ds.move_col( col, false, false );
|
||||||
}
|
}
|
||||||
|
|
||||||
NotificationEngine::NotificationEngine()
|
NotificationEngine::NotificationEngine()
|
||||||
@@ -242,16 +266,22 @@ int OverlayManager::wait_time( void )
|
|||||||
{
|
{
|
||||||
uint64_t next_expiry = INT_MAX;
|
uint64_t next_expiry = INT_MAX;
|
||||||
|
|
||||||
uint64_t message_delay = notifications.get_message_expiration() - timestamp();
|
uint64_t now = timestamp();
|
||||||
|
|
||||||
|
uint64_t message_delay = notifications.get_message_expiration() - now;
|
||||||
|
|
||||||
if ( message_delay < next_expiry ) {
|
if ( message_delay < next_expiry ) {
|
||||||
next_expiry = message_delay;
|
next_expiry = message_delay;
|
||||||
}
|
}
|
||||||
|
|
||||||
if ( notifications.need_countup( timestamp() ) && ( next_expiry > 1000 ) ) {
|
if ( notifications.need_countup( now ) && ( next_expiry > 1000 ) ) {
|
||||||
next_expiry = 1000;
|
next_expiry = 1000;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if ( predictions.active() && ( next_expiry > 10 ) ) {
|
||||||
|
next_expiry = 10;
|
||||||
|
}
|
||||||
|
|
||||||
return next_expiry;
|
return next_expiry;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -260,47 +290,66 @@ void TitleEngine::set_prefix( const wstring s )
|
|||||||
prefix = deque<wchar_t>( s.begin(), s.end() );
|
prefix = deque<wchar_t>( s.begin(), s.end() );
|
||||||
}
|
}
|
||||||
|
|
||||||
void ConditionalOverlayRow::apply( Framebuffer &fb, bool show_tentative, bool flag ) const
|
void ConditionalOverlayRow::apply( Framebuffer &fb, uint64_t confirmed_epoch, bool flag ) const
|
||||||
{
|
{
|
||||||
for_each( overlay_cells.begin(), overlay_cells.end(), [&]( const ConditionalOverlayCell &x ) { x.apply( fb, show_tentative, row_num, flag ); } );
|
for_each( overlay_cells.begin(), overlay_cells.end(), [&]( const ConditionalOverlayCell &x ) { x.apply( fb, confirmed_epoch, row_num, flag ); } );
|
||||||
}
|
}
|
||||||
|
|
||||||
void PredictionEngine::apply( Framebuffer &fb ) const
|
void PredictionEngine::apply( Framebuffer &fb ) const
|
||||||
{
|
{
|
||||||
if ( (score > 0) || cursor.show_frozen_cursor ) {
|
for_each( cursors.begin(), cursors.end(), [&]( const ConditionalCursorMove &x ) { x.apply( fb, confirmed_epoch ); } );
|
||||||
cursor.apply( fb );
|
|
||||||
|
for_each( overlays.begin(), overlays.end(), [&]( const ConditionalOverlayRow &x ){ x.apply( fb, confirmed_epoch, flagging ); } );
|
||||||
}
|
}
|
||||||
for_each( overlays.begin(), overlays.end(), [&]( const ConditionalOverlayRow &x ){ x.apply( fb, score > 0, flagging ); } );
|
|
||||||
|
void PredictionEngine::kill_epoch( uint64_t epoch, const Framebuffer &fb )
|
||||||
|
{
|
||||||
|
cursors.remove_if( [&]( ConditionalCursorMove &x ) { return x.tentative( epoch - 1 ); } );
|
||||||
|
|
||||||
|
cursors.push_back( ConditionalCursorMove( local_frame_sent + 1,
|
||||||
|
fb.ds.get_cursor_row(),
|
||||||
|
fb.ds.get_cursor_col(),
|
||||||
|
prediction_epoch ) );
|
||||||
|
cursor().active = true;
|
||||||
|
|
||||||
|
for ( auto i = overlays.begin(); i != overlays.end(); i++ ) {
|
||||||
|
for ( auto j = i->overlay_cells.begin(); j != i->overlay_cells.end(); j++ ) {
|
||||||
|
if ( j->tentative( epoch - 1 ) ) {
|
||||||
|
j->reset();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
become_tentative();
|
||||||
}
|
}
|
||||||
|
|
||||||
void PredictionEngine::reset( void )
|
void PredictionEngine::reset( void )
|
||||||
{
|
{
|
||||||
|
cursors.clear();
|
||||||
overlays.clear();
|
overlays.clear();
|
||||||
cursor.reset();
|
|
||||||
become_tentative();
|
become_tentative();
|
||||||
|
|
||||||
|
// fprintf( stderr, "RESETTING\n" );
|
||||||
|
}
|
||||||
|
|
||||||
|
void PredictionEngine::init_cursor( const Framebuffer &fb )
|
||||||
|
{
|
||||||
|
if ( cursors.empty() ) {
|
||||||
|
/* initialize new cursor prediction */
|
||||||
|
|
||||||
|
cursors.push_back( ConditionalCursorMove( local_frame_sent + 1,
|
||||||
|
fb.ds.get_cursor_row(),
|
||||||
|
fb.ds.get_cursor_col(),
|
||||||
|
prediction_epoch ) );
|
||||||
|
cursor().active = true;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void PredictionEngine::cull( const Framebuffer &fb )
|
void PredictionEngine::cull( const Framebuffer &fb )
|
||||||
{
|
{
|
||||||
if ( score > 0 ) {
|
|
||||||
cursor.thaw();
|
|
||||||
}
|
|
||||||
|
|
||||||
uint64_t now = timestamp();
|
uint64_t now = timestamp();
|
||||||
|
|
||||||
/* don't increment score just for correct cursor position */
|
/* go through cell predictions */
|
||||||
switch ( cursor.get_validity( fb, local_frame_acked) ) {
|
|
||||||
case IncorrectOrExpired:
|
|
||||||
cursor.reset();
|
|
||||||
become_tentative();
|
|
||||||
return;
|
|
||||||
break;
|
|
||||||
case Correct:
|
|
||||||
cursor.thaw();
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
uint64_t max_delay = 0;
|
uint64_t max_delay = 0;
|
||||||
|
|
||||||
@@ -314,16 +363,34 @@ void PredictionEngine::cull( const Framebuffer &fb )
|
|||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* smash through "shell prompt" barrier if host has let us */
|
||||||
|
if ( fb.ds.get_cursor_row() == i->row_num ) {
|
||||||
|
if ( i->first_col != INT_MAX ) {
|
||||||
|
if ( fb.ds.get_cursor_col() < i->first_col ) {
|
||||||
|
i->first_col = 0;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
i->first_col = fb.ds.get_cursor_col();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
for ( auto j = i->overlay_cells.begin(); j != i->overlay_cells.end(); j++ ) {
|
for ( auto j = i->overlay_cells.begin(); j != i->overlay_cells.end(); j++ ) {
|
||||||
switch ( j->get_validity( fb, i->row_num, local_frame_acked ) ) {
|
if ( j->start_clock( local_frame_acked, now ) ) {
|
||||||
|
last_scheduled_timeout = max( last_scheduled_timeout, j->expiration_time );
|
||||||
|
}
|
||||||
|
switch ( j->get_validity( fb, i->row_num, local_frame_acked, now ) ) {
|
||||||
case IncorrectOrExpired:
|
case IncorrectOrExpired:
|
||||||
if ( j->tentative ) {
|
if ( j->tentative( confirmed_epoch ) ) {
|
||||||
|
|
||||||
/*
|
/*
|
||||||
fprintf( stderr, "Bad tentative prediction in row %d, col %d\n",
|
fprintf( stderr, "Bad tentative prediction in row %d, col %d (think %lc, actually %lc)\n",
|
||||||
i->row_num, j->col );
|
i->row_num, j->col,
|
||||||
|
j->replacement.debug_contents(),
|
||||||
|
fb.get_cell( i->row_num, j->col )->debug_contents()
|
||||||
|
);
|
||||||
*/
|
*/
|
||||||
j->reset();
|
|
||||||
become_tentative();
|
kill_epoch( j->tentative_until_epoch, fb );
|
||||||
/*
|
/*
|
||||||
if ( j->display_time != uint64_t(-1) ) {
|
if ( j->display_time != uint64_t(-1) ) {
|
||||||
fprintf( stderr, "TIMING %ld - %ld (TENT)\n", time(NULL), now - j->display_time );
|
fprintf( stderr, "TIMING %ld - %ld (TENT)\n", time(NULL), now - j->display_time );
|
||||||
@@ -331,14 +398,16 @@ void PredictionEngine::cull( const Framebuffer &fb )
|
|||||||
*/
|
*/
|
||||||
} else {
|
} else {
|
||||||
/*
|
/*
|
||||||
fprintf( stderr, "[%d=>%d] (score=%d) Killing prediction in row %d, col %d\n",
|
fprintf( stderr, "[%d=>%d] Killing prediction in row %d, col %d (think %lc, actually %lc)\n",
|
||||||
(int)local_frame_acked, (int)j->expiration_frame,
|
(int)local_frame_acked, (int)j->expiration_frame,
|
||||||
score,
|
i->row_num, j->col,
|
||||||
i->row_num, j->col );
|
j->replacement.debug_contents(),
|
||||||
|
fb.get_cell( i->row_num, j->col )->debug_contents() );
|
||||||
if ( j->display_time != uint64_t(-1) ) {
|
if ( j->display_time != uint64_t(-1) ) {
|
||||||
fprintf( stderr, "TIMING %ld - %ld\n", time(NULL), now - j->display_time );
|
fprintf( stderr, "TIMING %ld - %ld\n", time(NULL), now - j->display_time );
|
||||||
}
|
}
|
||||||
*/
|
*/
|
||||||
|
|
||||||
reset();
|
reset();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -350,15 +419,31 @@ void PredictionEngine::cull( const Framebuffer &fb )
|
|||||||
}
|
}
|
||||||
*/
|
*/
|
||||||
|
|
||||||
if ( j->prediction_time > prediction_checkpoint ) {
|
if ( j->tentative_until_epoch > confirmed_epoch ) {
|
||||||
score++;
|
confirmed_epoch = j->tentative_until_epoch;
|
||||||
|
/*
|
||||||
|
fprintf( stderr, "Confirmed epoch %lu (predicting in epoch %lu)\n",
|
||||||
|
confirmed_epoch, prediction_epoch );
|
||||||
|
*/
|
||||||
|
}
|
||||||
|
|
||||||
|
/* no break */
|
||||||
|
case CorrectNoCredit:
|
||||||
|
if ( i->first_col != INT_MAX ) {
|
||||||
|
if ( j->col < i->first_col ) {
|
||||||
|
i->first_col = 0;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
i->first_col = j->col;
|
||||||
}
|
}
|
||||||
|
|
||||||
j->reset();
|
j->reset();
|
||||||
|
|
||||||
break;
|
break;
|
||||||
case Pending:
|
case Pending:
|
||||||
max_delay = max( max_delay, now - j->prediction_time );
|
if ( j->display_time != uint64_t(-1) ) {
|
||||||
|
max_delay = max( max_delay, now - j->display_time );
|
||||||
|
}
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
break;
|
break;
|
||||||
@@ -373,6 +458,30 @@ void PredictionEngine::cull( const Framebuffer &fb )
|
|||||||
} else if ( max_delay < 50 ) {
|
} else if ( max_delay < 50 ) {
|
||||||
flagging = false;
|
flagging = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* go through cursor predictions */
|
||||||
|
for ( auto it = cursors.begin(); it != cursors.end(); it++ ) {
|
||||||
|
if ( it->start_clock( local_frame_acked, now ) ) {
|
||||||
|
last_scheduled_timeout = max( last_scheduled_timeout, it->expiration_time ); }
|
||||||
|
}
|
||||||
|
|
||||||
|
if ( !cursors.empty() ) {
|
||||||
|
if ( cursor().get_validity( fb, local_frame_acked, now ) == IncorrectOrExpired ) {
|
||||||
|
/*
|
||||||
|
fprintf( stderr, "Sadly, we're predicting (%d,%d) vs. (%d,%d) [tau: %ld, expiration_time=%ld, now=%ld]\n",
|
||||||
|
cursor().row, cursor().col,
|
||||||
|
fb.ds.get_cursor_row(),
|
||||||
|
fb.ds.get_cursor_col(),
|
||||||
|
cursor().tentative_until_epoch,
|
||||||
|
cursor().expiration_time,
|
||||||
|
now );
|
||||||
|
*/
|
||||||
|
reset();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
cursors.remove_if( [&]( ConditionalCursorMove &x ) { return (x.get_validity( fb, local_frame_acked, now ) != Pending); } );
|
||||||
}
|
}
|
||||||
|
|
||||||
ConditionalOverlayRow & PredictionEngine::get_or_make_row( int row_num, int num_cols )
|
ConditionalOverlayRow & PredictionEngine::get_or_make_row( int row_num, int num_cols )
|
||||||
@@ -387,7 +496,7 @@ ConditionalOverlayRow & PredictionEngine::get_or_make_row( int row_num, int num_
|
|||||||
ConditionalOverlayRow r( row_num );
|
ConditionalOverlayRow r( row_num );
|
||||||
r.overlay_cells.reserve( num_cols );
|
r.overlay_cells.reserve( num_cols );
|
||||||
for ( int i = 0; i < num_cols; i++ ) {
|
for ( int i = 0; i < num_cols; i++ ) {
|
||||||
r.overlay_cells.push_back( ConditionalOverlayCell( i ) );
|
r.overlay_cells.push_back( ConditionalOverlayCell( 0, i, prediction_epoch ) );
|
||||||
assert( r.overlay_cells[ i ].col == i );
|
assert( r.overlay_cells[ i ].col == i );
|
||||||
}
|
}
|
||||||
overlays.push_back( r );
|
overlays.push_back( r );
|
||||||
@@ -397,130 +506,222 @@ ConditionalOverlayRow & PredictionEngine::get_or_make_row( int row_num, int num_
|
|||||||
|
|
||||||
void PredictionEngine::new_user_byte( char the_byte, const Framebuffer &fb )
|
void PredictionEngine::new_user_byte( char the_byte, const Framebuffer &fb )
|
||||||
{
|
{
|
||||||
list<Parser::Action *> actions( parser.input( the_byte ) );
|
cull( fb );
|
||||||
|
|
||||||
uint64_t now = timestamp();
|
init_cursor( fb );
|
||||||
|
|
||||||
|
assert( !cursors.empty() );
|
||||||
|
|
||||||
|
/* translate application-mode cursor control function to ANSI cursor control sequence */
|
||||||
|
if ( (last_byte == 0x1b)
|
||||||
|
&& (the_byte == 'O') ) {
|
||||||
|
the_byte = '[';
|
||||||
|
}
|
||||||
|
last_byte = the_byte;
|
||||||
|
|
||||||
|
list<Parser::Action *> actions( parser.input( the_byte ) );
|
||||||
|
|
||||||
for ( auto it = actions.begin(); it != actions.end(); it++ ) {
|
for ( auto it = actions.begin(); it != actions.end(); it++ ) {
|
||||||
Parser::Action *act = *it;
|
Parser::Action *act = *it;
|
||||||
|
|
||||||
if ( !cursor.active ) {
|
/*
|
||||||
/* initialize new cursor prediction */
|
fprintf( stderr, "Action: %s (%lc)\n",
|
||||||
cursor.row = fb.ds.get_cursor_row();
|
act->name().c_str(), act->char_present ? act->ch : L'_' );
|
||||||
cursor.col = fb.ds.get_cursor_col();
|
*/
|
||||||
cursor.active = true;
|
|
||||||
cursor.prediction_time = now;
|
|
||||||
cursor.expiration_frame = local_frame_sent + 1;
|
|
||||||
assert( !cursor.show_frozen_cursor );
|
|
||||||
}
|
|
||||||
|
|
||||||
if ( typeid( *act ) == typeid( Parser::Print ) ) {
|
if ( typeid( *act ) == typeid( Parser::Print ) ) {
|
||||||
/* make new prediction */
|
/* make new prediction */
|
||||||
|
|
||||||
|
assert( act->char_present );
|
||||||
|
|
||||||
wchar_t ch = act->ch;
|
wchar_t ch = act->ch;
|
||||||
/* XXX handle wide characters */
|
/* XXX handle wide characters */
|
||||||
|
|
||||||
if ( ch == 0x7f ) { /* backspace */
|
if ( ch == 0x7f ) { /* backspace */
|
||||||
// fprintf( stderr, "Backspace.\n" );
|
// fprintf( stderr, "Backspace.\n" );
|
||||||
if ( cursor.col > 0 ) {
|
ConditionalOverlayRow &the_row = get_or_make_row( cursor().row, fb.ds.get_width() );
|
||||||
cursor.col--;
|
if ( cursor().col <= the_row.first_col ) {
|
||||||
|
become_tentative();
|
||||||
cursor.prediction_time = now;
|
|
||||||
cursor.expiration_frame = local_frame_sent + 1;
|
|
||||||
|
|
||||||
ConditionalOverlayCell &cell = get_or_make_row( cursor.row, fb.ds.get_width() ).overlay_cells[ cursor.col ];
|
|
||||||
cell.active = true;
|
|
||||||
cell.tentative = (score <= 0);
|
|
||||||
cell.prediction_time = now;
|
|
||||||
cell.expiration_frame = local_frame_sent + 1;
|
|
||||||
cell.replacement.renditions = fb.ds.get_renditions();
|
|
||||||
cell.replacement.contents.clear();
|
|
||||||
cell.display_time = -1;
|
|
||||||
}
|
}
|
||||||
} else if ( (ch < 0x20) || (ch > 0x7E) ) {
|
|
||||||
|
if ( cursor().col > 0 ) {
|
||||||
|
cursor().col--;
|
||||||
|
cursor().expire( local_frame_sent + 1 );
|
||||||
|
|
||||||
|
for ( int i = cursor().col; i < fb.ds.get_width(); i++ ) {
|
||||||
|
ConditionalOverlayCell &cell = the_row.overlay_cells[ i ];
|
||||||
|
cell.reset();
|
||||||
|
cell.active = true;
|
||||||
|
cell.tentative_until_epoch = prediction_epoch;
|
||||||
|
cell.expire( local_frame_sent + 1 );
|
||||||
|
|
||||||
|
if ( i + 2 < fb.ds.get_width() ) {
|
||||||
|
ConditionalOverlayCell &next_cell = the_row.overlay_cells[ i + 1 ];
|
||||||
|
const Cell *next_cell_actual = fb.get_cell( cursor().row, i + 1 );
|
||||||
|
|
||||||
|
if ( next_cell.active ) {
|
||||||
|
if ( next_cell.unknown ) {
|
||||||
|
cell.unknown = true;
|
||||||
|
} else {
|
||||||
|
cell.unknown = false;
|
||||||
|
cell.replacement = next_cell.replacement;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
cell.unknown = false;
|
||||||
|
cell.replacement = *next_cell_actual;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
cell.unknown = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else if ( (ch < 0x20) || (wcwidth( ch ) != 1) ) {
|
||||||
/* unknown print */
|
/* unknown print */
|
||||||
become_tentative();
|
become_tentative();
|
||||||
// fprintf( stderr, "Unknown print 0x%x\n", ch );
|
// fprintf( stderr, "Unknown print 0x%x\n", ch );
|
||||||
} else {
|
} else {
|
||||||
/* don't attempt to change existing blank or space cells if user has typed space */
|
assert( cursor().row >= 0 );
|
||||||
const Cell *existing_cell = fb.get_cell( cursor.row, cursor.col );
|
assert( cursor().col >= 0 );
|
||||||
if ( ( existing_cell->contents.empty() || ((existing_cell->contents.size() == 1)
|
assert( cursor().row < fb.ds.get_height() );
|
||||||
&& ( (existing_cell->contents.front() == 0x20)
|
assert( cursor().col < fb.ds.get_width() );
|
||||||
|| (existing_cell->contents.front() == 0xA0) )))
|
|
||||||
&& ( ch == 0x20 ) ) {
|
|
||||||
/* do nothing */
|
|
||||||
} else {
|
|
||||||
assert( cursor.row >= 0 );
|
|
||||||
assert( cursor.col >= 0 );
|
|
||||||
assert( cursor.row < fb.ds.get_height() );
|
|
||||||
assert( cursor.col < fb.ds.get_width() );
|
|
||||||
|
|
||||||
if ( cursor.col + 1 >= fb.ds.get_width() ) {
|
ConditionalOverlayRow &the_row = get_or_make_row( cursor().row, fb.ds.get_width() );
|
||||||
|
|
||||||
|
if ( cursor().col + 1 >= fb.ds.get_width() ) {
|
||||||
/* prediction in the last column is tricky */
|
/* prediction in the last column is tricky */
|
||||||
/* e.g., emacs will show wrap character, shell will just put the character there */
|
/* e.g., emacs will show wrap character, shell will just put the character there */
|
||||||
become_tentative();
|
become_tentative();
|
||||||
cursor.freeze();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
ConditionalOverlayCell &cell = get_or_make_row( cursor.row, fb.ds.get_width() ).overlay_cells[ cursor.col ];
|
/* do the insert */
|
||||||
|
for ( int i = fb.ds.get_width() - 1; i > cursor().col; i-- ) {
|
||||||
|
ConditionalOverlayCell &cell = the_row.overlay_cells[ i ];
|
||||||
|
cell.reset();
|
||||||
cell.active = true;
|
cell.active = true;
|
||||||
cell.tentative = (score <= 0);
|
cell.tentative_until_epoch = prediction_epoch;
|
||||||
cell.prediction_time = now;
|
cell.expire( local_frame_sent + 1 );
|
||||||
cell.expiration_frame = local_frame_sent + 1;
|
|
||||||
|
ConditionalOverlayCell &prev_cell = the_row.overlay_cells[ i - 1 ];
|
||||||
|
const Cell *prev_cell_actual = fb.get_cell( cursor().row, i - 1 );
|
||||||
|
|
||||||
|
if ( i == fb.ds.get_width() - 1 ) {
|
||||||
|
cell.unknown = true;
|
||||||
|
} else if ( prev_cell.active ) {
|
||||||
|
if ( prev_cell.unknown ) {
|
||||||
|
cell.unknown = true;
|
||||||
|
} else {
|
||||||
|
cell.unknown = false;
|
||||||
|
cell.replacement = prev_cell.replacement;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
cell.unknown = false;
|
||||||
|
cell.replacement = *prev_cell_actual;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ConditionalOverlayCell &cell = the_row.overlay_cells[ cursor().col ];
|
||||||
|
cell.reset();
|
||||||
|
cell.active = true;
|
||||||
|
cell.tentative_until_epoch = prediction_epoch;
|
||||||
|
cell.expire( local_frame_sent + 1 );
|
||||||
cell.replacement.renditions = fb.ds.get_renditions();
|
cell.replacement.renditions = fb.ds.get_renditions();
|
||||||
cell.replacement.contents.clear();
|
cell.replacement.contents.clear();
|
||||||
cell.replacement.contents.push_back( ch );
|
cell.replacement.contents.push_back( ch );
|
||||||
cell.display_time = -1;
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
fprintf( stderr, "[%d=>%d] Predicting %lc in row %d, col %d\n",
|
fprintf( stderr, "[%d=>%d] Predicting %lc in row %d, col %d [tue: %lu]\n",
|
||||||
(int)local_frame_acked, (int)cell.expiration_frame,
|
(int)local_frame_acked, (int)cell.expiration_frame,
|
||||||
ch, cursor.row, cursor.col );
|
ch, cursor().row, cursor().col,
|
||||||
|
cell.tentative_until_epoch );
|
||||||
*/
|
*/
|
||||||
}
|
|
||||||
|
|
||||||
cursor.prediction_time = now;
|
cursor().expire( local_frame_sent + 1 );
|
||||||
cursor.expiration_frame = local_frame_sent + 1;
|
cursor().col++;
|
||||||
cursor.col++;
|
|
||||||
|
|
||||||
/* do we need to wrap? */
|
/* do we need to wrap? */
|
||||||
if ( cursor.col >= fb.ds.get_width() ) {
|
if ( cursor().col >= fb.ds.get_width() ) {
|
||||||
become_tentative();
|
newline_carriage_return( fb );
|
||||||
cursor.col--;
|
|
||||||
cursor.freeze();
|
|
||||||
cursor.col = 0;
|
|
||||||
if ( cursor.row == fb.ds.get_height() - 1 ) {
|
|
||||||
for ( auto i = overlays.begin(); i != overlays.end(); i++ ) {
|
|
||||||
i->row_num--;
|
|
||||||
for ( auto j = i->overlay_cells.begin(); j != i->overlay_cells.end(); j++ ) {
|
|
||||||
if ( j->active ) {
|
|
||||||
// j->tentative = (score <= 0);
|
|
||||||
j->prediction_time = now;
|
|
||||||
j->expiration_frame = local_frame_sent + 1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
cursor.row++;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else if ( typeid( *act ) == typeid( Parser::Execute ) ) {
|
} else if ( typeid( *act ) == typeid( Parser::Execute ) ) {
|
||||||
|
if ( act->char_present && (act->ch == 0x0d) /* CR */ ) {
|
||||||
become_tentative();
|
become_tentative();
|
||||||
cursor.freeze();
|
newline_carriage_return( fb );
|
||||||
|
} else {
|
||||||
// fprintf( stderr, "Execute 0x%x\n", act->ch );
|
// fprintf( stderr, "Execute 0x%x\n", act->ch );
|
||||||
|
become_tentative();
|
||||||
|
}
|
||||||
} else if ( typeid( *act ) == typeid( Parser::Esc_Dispatch ) ) {
|
} else if ( typeid( *act ) == typeid( Parser::Esc_Dispatch ) ) {
|
||||||
// fprintf( stderr, "Escape sequence\n" );
|
// fprintf( stderr, "Escape sequence\n" );
|
||||||
|
become_tentative();
|
||||||
} else if ( typeid( *act ) == typeid( Parser::CSI_Dispatch ) ) {
|
} else if ( typeid( *act ) == typeid( Parser::CSI_Dispatch ) ) {
|
||||||
// fprintf( stderr, "CSI sequence\n" );
|
if ( act->char_present && (act->ch == L'C') ) { /* right arrow */
|
||||||
|
if ( cursor().col < fb.ds.get_width() - 1 ) {
|
||||||
|
cursor().col++;
|
||||||
|
cursor().expire( local_frame_sent + 1 );
|
||||||
|
}
|
||||||
|
} else if ( act->char_present && (act->ch == L'D') ) { /* left arrow */
|
||||||
|
ConditionalOverlayRow &the_row = get_or_make_row( cursor().row, fb.ds.get_width() );
|
||||||
|
if ( cursor().col <= the_row.first_col ) {
|
||||||
|
become_tentative();
|
||||||
|
}
|
||||||
|
|
||||||
|
if ( cursor().col > 0 ) {
|
||||||
|
cursor().col--;
|
||||||
|
cursor().expire( local_frame_sent + 1 );
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// fprintf( stderr, "CSI sequence %lc\n", act->ch );
|
||||||
|
become_tentative();
|
||||||
|
}
|
||||||
|
} else if ( typeid( *act ) == typeid( Parser::Clear ) ) {
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
delete act;
|
delete act;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void PredictionEngine::newline_carriage_return( const Framebuffer &fb )
|
||||||
|
{
|
||||||
|
cursor().col = 0;
|
||||||
|
if ( cursor().row == fb.ds.get_height() - 1 ) {
|
||||||
|
for ( auto i = overlays.begin(); i != overlays.end(); i++ ) {
|
||||||
|
i->row_num--;
|
||||||
|
for ( auto j = i->overlay_cells.begin(); j != i->overlay_cells.end(); j++ ) {
|
||||||
|
if ( j->active ) {
|
||||||
|
j->expire( local_frame_sent + 1 );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* make blank prediction for last row */
|
||||||
|
ConditionalOverlayRow &the_row = get_or_make_row( cursor().row, fb.ds.get_width() );
|
||||||
|
for ( auto j = the_row.overlay_cells.begin(); j != the_row.overlay_cells.end(); j++ ) {
|
||||||
|
j->active = true;
|
||||||
|
j->tentative_until_epoch = prediction_epoch;
|
||||||
|
j->expire( local_frame_sent + 1 );
|
||||||
|
j->replacement.contents.clear();
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
cursor().row++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void PredictionEngine::become_tentative( void )
|
void PredictionEngine::become_tentative( void )
|
||||||
{
|
{
|
||||||
prediction_checkpoint = timestamp();
|
prediction_epoch++;
|
||||||
score = 0;
|
|
||||||
|
if ( !cursors.empty() ) {
|
||||||
|
cursors.push_back( ConditionalCursorMove( local_frame_sent + 1,
|
||||||
|
cursor().row,
|
||||||
|
cursor().col,
|
||||||
|
prediction_epoch ) );
|
||||||
|
cursor().active = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
fprintf( stderr, "Now tentative in epoch %lu (confirmed=%lu)\n",
|
||||||
|
prediction_epoch, confirmed_epoch );
|
||||||
|
*/
|
||||||
}
|
}
|
||||||
|
|||||||
+46
-33
@@ -15,75 +15,76 @@ namespace Overlay {
|
|||||||
enum Validity {
|
enum Validity {
|
||||||
Pending,
|
Pending,
|
||||||
Correct,
|
Correct,
|
||||||
|
CorrectNoCredit,
|
||||||
IncorrectOrExpired,
|
IncorrectOrExpired,
|
||||||
Inactive
|
Inactive
|
||||||
};
|
};
|
||||||
|
|
||||||
class ConditionalOverlay {
|
class ConditionalOverlay {
|
||||||
public:
|
public:
|
||||||
uint64_t prediction_time, expiration_frame;
|
uint64_t expiration_frame;
|
||||||
|
uint64_t expiration_time; /* after frame is hit */
|
||||||
int col;
|
int col;
|
||||||
bool active; /* represents a prediction at all */
|
bool active; /* represents a prediction at all */
|
||||||
bool tentative; /* whether to hide when score < 0 */
|
uint64_t tentative_until_epoch; /* when to show */
|
||||||
|
|
||||||
ConditionalOverlay( uint64_t s_exp, int s_col )
|
ConditionalOverlay( uint64_t s_exp, int s_col, uint64_t s_tentative )
|
||||||
: prediction_time( timestamp() ),
|
: expiration_frame( s_exp ), expiration_time( -1 ), col( s_col ),
|
||||||
expiration_frame( s_exp ), col( s_col ),
|
|
||||||
active( false ),
|
active( false ),
|
||||||
tentative( false )
|
tentative_until_epoch( s_tentative )
|
||||||
{}
|
{}
|
||||||
|
|
||||||
virtual ~ConditionalOverlay() {}
|
virtual ~ConditionalOverlay() {}
|
||||||
|
|
||||||
|
bool tentative( uint64_t confirmed_epoch ) const { return tentative_until_epoch > confirmed_epoch; }
|
||||||
|
void reset( void ) { expiration_frame = expiration_time = tentative_until_epoch = -1; active = false; }
|
||||||
|
bool start_clock( uint64_t local_frame_acked, uint64_t now );
|
||||||
|
void expire( uint64_t s_exp ) { expiration_frame = s_exp; expiration_time = uint64_t(-1); }
|
||||||
};
|
};
|
||||||
|
|
||||||
class ConditionalCursorMove : public ConditionalOverlay {
|
class ConditionalCursorMove : public ConditionalOverlay {
|
||||||
public:
|
public:
|
||||||
int row;
|
int row;
|
||||||
|
|
||||||
int frozen_col, frozen_row; /* after a control character */
|
void apply( Framebuffer &fb, uint64_t confirmed_epoch ) const;
|
||||||
|
|
||||||
bool show_frozen_cursor;
|
Validity get_validity( const Framebuffer &fb, uint64_t current_frame, uint64_t now ) const;
|
||||||
|
|
||||||
void apply( Framebuffer &fb ) const;
|
ConditionalCursorMove( uint64_t s_exp, int s_row, int s_col, uint64_t s_tentative )
|
||||||
|
: ConditionalOverlay( s_exp, s_col, s_tentative ), row( s_row )
|
||||||
Validity get_validity( const Framebuffer &fb, uint64_t current_frame ) const;
|
|
||||||
|
|
||||||
ConditionalCursorMove( uint64_t s_exp, int s_row, int s_col )
|
|
||||||
: ConditionalOverlay( s_exp, s_col ), row( s_row ), frozen_col( 0 ), frozen_row( 0 ),
|
|
||||||
show_frozen_cursor( false )
|
|
||||||
{}
|
{}
|
||||||
|
|
||||||
void freeze( void ) { frozen_col = col; frozen_row = row; show_frozen_cursor = true; }
|
|
||||||
void thaw( void ) { show_frozen_cursor = false; }
|
|
||||||
void reset( void ) { active = false; thaw(); }
|
|
||||||
};
|
};
|
||||||
|
|
||||||
class ConditionalOverlayCell : public ConditionalOverlay {
|
class ConditionalOverlayCell : public ConditionalOverlay {
|
||||||
public:
|
public:
|
||||||
Cell replacement;
|
Cell replacement;
|
||||||
|
bool unknown;
|
||||||
|
|
||||||
mutable uint64_t display_time;
|
mutable uint64_t display_time;
|
||||||
|
|
||||||
void apply( Framebuffer &fb, bool show_tentative, int row, bool flag ) const;
|
void apply( Framebuffer &fb, uint64_t confirmed_epoch, int row, bool flag ) const;
|
||||||
Validity get_validity( const Framebuffer &fb, int row, uint64_t current_frame ) const;
|
Validity get_validity( const Framebuffer &fb, int row, uint64_t current_frame, uint64_t now ) const;
|
||||||
|
|
||||||
ConditionalOverlayCell( int s_col )
|
ConditionalOverlayCell( uint64_t s_exp, int s_col, uint64_t s_tentative )
|
||||||
: ConditionalOverlay( 0, s_col ),
|
: ConditionalOverlay( s_exp, s_col, s_tentative ),
|
||||||
replacement( 0 ),
|
replacement( 0 ),
|
||||||
|
unknown( false ),
|
||||||
display_time( -1 )
|
display_time( -1 )
|
||||||
{}
|
{}
|
||||||
|
|
||||||
void reset( void ) { active = false; display_time = -1; }
|
void reset( void ) { unknown = false; display_time = -1; ConditionalOverlay::reset(); }
|
||||||
};
|
};
|
||||||
|
|
||||||
class ConditionalOverlayRow {
|
class ConditionalOverlayRow {
|
||||||
public:
|
public:
|
||||||
int row_num;
|
int row_num;
|
||||||
|
int first_col;
|
||||||
|
|
||||||
vector<ConditionalOverlayCell> overlay_cells;
|
vector<ConditionalOverlayCell> overlay_cells;
|
||||||
|
|
||||||
void apply( Framebuffer &fb, bool show_tentative, bool flag ) const;
|
void apply( Framebuffer &fb, uint64_t confirmed_epoch, bool flag ) const;
|
||||||
|
|
||||||
ConditionalOverlayRow( int s_row_num ) : row_num( s_row_num ), overlay_cells() {}
|
ConditionalOverlayRow( int s_row_num ) : row_num( s_row_num ), first_col( INT_MAX ), overlay_cells() {}
|
||||||
};
|
};
|
||||||
|
|
||||||
/* the various overlays */
|
/* the various overlays */
|
||||||
@@ -107,24 +108,34 @@ namespace Overlay {
|
|||||||
|
|
||||||
class PredictionEngine {
|
class PredictionEngine {
|
||||||
private:
|
private:
|
||||||
|
char last_byte;
|
||||||
Parser::UTF8Parser parser;
|
Parser::UTF8Parser parser;
|
||||||
|
|
||||||
list<ConditionalOverlayRow> overlays;
|
list<ConditionalOverlayRow> overlays;
|
||||||
|
|
||||||
ConditionalCursorMove cursor;
|
list<ConditionalCursorMove> cursors;
|
||||||
|
|
||||||
int score;
|
|
||||||
|
|
||||||
uint64_t local_frame_sent, local_frame_acked;
|
uint64_t local_frame_sent, local_frame_acked;
|
||||||
|
|
||||||
ConditionalOverlayRow & get_or_make_row( int row_num, int num_cols );
|
ConditionalOverlayRow & get_or_make_row( int row_num, int num_cols );
|
||||||
|
|
||||||
uint64_t prediction_checkpoint;
|
uint64_t prediction_epoch;
|
||||||
|
uint64_t confirmed_epoch;
|
||||||
|
|
||||||
void become_tentative( void );
|
void become_tentative( void );
|
||||||
|
|
||||||
|
void newline_carriage_return( const Framebuffer &fb );
|
||||||
|
|
||||||
bool flagging;
|
bool flagging;
|
||||||
|
|
||||||
|
ConditionalCursorMove & cursor( void ) { return cursors.back(); }
|
||||||
|
|
||||||
|
void kill_epoch( uint64_t epoch, const Framebuffer &fb );
|
||||||
|
|
||||||
|
void init_cursor( const Framebuffer &fb );
|
||||||
|
|
||||||
|
uint64_t last_scheduled_timeout;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
void apply( Framebuffer &fb ) const;
|
void apply( Framebuffer &fb ) const;
|
||||||
void new_user_byte( char the_byte, const Framebuffer &fb );
|
void new_user_byte( char the_byte, const Framebuffer &fb );
|
||||||
@@ -132,14 +143,16 @@ namespace Overlay {
|
|||||||
|
|
||||||
void reset( void );
|
void reset( void );
|
||||||
|
|
||||||
|
bool active( void ) { return timestamp() <= last_scheduled_timeout; }
|
||||||
|
|
||||||
void set_local_frame_sent( uint64_t x ) { local_frame_sent = x; }
|
void set_local_frame_sent( uint64_t x ) { local_frame_sent = x; }
|
||||||
void set_local_frame_acked( uint64_t x ) { local_frame_acked = x; }
|
void set_local_frame_acked( uint64_t x ) { local_frame_acked = x; }
|
||||||
|
|
||||||
PredictionEngine( void ) : parser(), overlays(), cursor( 0, 0, 0 ), score( 0 ),
|
PredictionEngine( void ) : last_byte( 0 ), parser(), overlays(), cursors(),
|
||||||
local_frame_sent( 0 ), local_frame_acked( 0 ),
|
local_frame_sent( 0 ), local_frame_acked( 0 ),
|
||||||
prediction_checkpoint( timestamp() ), flagging( false )
|
prediction_epoch( 0 ), confirmed_epoch( 0 ),
|
||||||
|
flagging( false ), last_scheduled_timeout( 0 )
|
||||||
{
|
{
|
||||||
become_tentative();
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
+17
-4
@@ -20,6 +20,7 @@ TransportSender<MyState>::TransportSender( Connection *s_connection, MyState &in
|
|||||||
shutdown_tries( 0 ),
|
shutdown_tries( 0 ),
|
||||||
ack_num( 0 ),
|
ack_num( 0 ),
|
||||||
pending_data_ack( false ),
|
pending_data_ack( false ),
|
||||||
|
ack_timestamp( 0 ),
|
||||||
SEND_MINDELAY( 15 )
|
SEND_MINDELAY( 15 )
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
@@ -218,6 +219,9 @@ template <class MyState>
|
|||||||
void TransportSender<MyState>::send_in_fragments( string diff, uint64_t new_num )
|
void TransportSender<MyState>::send_in_fragments( string diff, uint64_t new_num )
|
||||||
{
|
{
|
||||||
Instruction inst;
|
Instruction inst;
|
||||||
|
|
||||||
|
uint64_t now = timestamp();
|
||||||
|
|
||||||
inst.set_protocol_version( MOSH_PROTOCOL_VERSION );
|
inst.set_protocol_version( MOSH_PROTOCOL_VERSION );
|
||||||
inst.set_old_num( assumed_receiver_state->num );
|
inst.set_old_num( assumed_receiver_state->num );
|
||||||
inst.set_new_num( new_num );
|
inst.set_new_num( new_num );
|
||||||
@@ -235,11 +239,12 @@ void TransportSender<MyState>::send_in_fragments( string diff, uint64_t new_num
|
|||||||
connection->send( i->tostring() );
|
connection->send( i->tostring() );
|
||||||
|
|
||||||
if ( verbose ) {
|
if ( verbose ) {
|
||||||
fprintf( stderr, "[%u] Sent [%d=>%d] id %d, frag %d ack=%d, throwaway=%d, len=%d, frame rate=%.2f, timeout=%d, srtt=%.1f\n",
|
fprintf( stderr, "[%u] Sent [%d=>%d] id %d, frag %d ack=%d, throwaway=%d, len=%d, frame rate=%.2f, timeout=%d, srtt=%.1f age=%lu\n",
|
||||||
(unsigned int)(timestamp() % 100000), (int)inst.old_num(), (int)inst.new_num(), (int)i->id, (int)i->fragment_num,
|
(unsigned int)(timestamp() % 100000), (int)inst.old_num(), (int)inst.new_num(), (int)i->id, (int)i->fragment_num,
|
||||||
(int)inst.ack_num(), (int)inst.throwaway_num(), (int)i->contents.size(),
|
(int)inst.ack_num(), (int)inst.throwaway_num(), (int)i->contents.size(),
|
||||||
1000.0 / (double)send_interval(),
|
1000.0 / (double)send_interval(),
|
||||||
(int)connection->timeout(), connection->get_SRTT() );
|
(int)connection->timeout(), connection->get_SRTT(),
|
||||||
|
now - ack_timestamp );
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
@@ -253,8 +258,8 @@ void TransportSender<MyState>::process_acknowledgment_through( uint64_t ack_num
|
|||||||
/* Ignore ack if we have culled the state it's acknowledging */
|
/* Ignore ack if we have culled the state it's acknowledging */
|
||||||
|
|
||||||
if ( sent_states.end() != find_if( sent_states.begin(), sent_states.end(),
|
if ( sent_states.end() != find_if( sent_states.begin(), sent_states.end(),
|
||||||
[&]( TimestampedState<MyState> x ) { return x.num == ack_num; } ) ) {
|
[&]( TimestampedState<MyState> &x ) { return x.num == ack_num; } ) ) {
|
||||||
sent_states.remove_if( [&]( TimestampedState<MyState> x ) { return x.num < ack_num; } );
|
sent_states.remove_if( [&]( TimestampedState<MyState> &x ) { return x.num < ack_num; } );
|
||||||
}
|
}
|
||||||
|
|
||||||
assert( !sent_states.empty() );
|
assert( !sent_states.empty() );
|
||||||
@@ -266,3 +271,11 @@ bool TransportSender<MyState>::shutdown_ack_timed_out( void )
|
|||||||
{
|
{
|
||||||
return shutdown_tries >= SHUTDOWN_RETRIES;
|
return shutdown_tries >= SHUTDOWN_RETRIES;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Executed upon entry to new receiver state */
|
||||||
|
template <class MyState>
|
||||||
|
void TransportSender<MyState>::set_ack_num( uint64_t s_ack_num )
|
||||||
|
{
|
||||||
|
ack_num = s_ack_num;
|
||||||
|
ack_timestamp = timestamp();
|
||||||
|
}
|
||||||
|
|||||||
+3
-2
@@ -60,8 +60,9 @@ namespace Network {
|
|||||||
/* information about receiver state */
|
/* information about receiver state */
|
||||||
uint64_t ack_num;
|
uint64_t ack_num;
|
||||||
bool pending_data_ack;
|
bool pending_data_ack;
|
||||||
|
uint64_t ack_timestamp;
|
||||||
|
|
||||||
int SEND_MINDELAY; /* ms to collect all input */
|
unsigned int SEND_MINDELAY; /* ms to collect all input */
|
||||||
|
|
||||||
public:
|
public:
|
||||||
/* constructor */
|
/* constructor */
|
||||||
@@ -77,7 +78,7 @@ namespace Network {
|
|||||||
void process_acknowledgment_through( uint64_t ack_num );
|
void process_acknowledgment_through( uint64_t ack_num );
|
||||||
|
|
||||||
/* Executed upon entry to new receiver state */
|
/* Executed upon entry to new receiver state */
|
||||||
void set_ack_num( uint64_t s_ack_num ) { ack_num = s_ack_num; }
|
void set_ack_num( uint64_t s_ack_num );
|
||||||
|
|
||||||
/* Accelerate reply ack */
|
/* Accelerate reply ack */
|
||||||
void set_data_ack( void ) { pending_data_ack = true; }
|
void set_data_ack( void ) { pending_data_ack = true; }
|
||||||
|
|||||||
Reference in New Issue
Block a user