LCOV - code coverage report
Current view: top level - src/frontend - terminaloverlay.h (source / functions) Hit Total Coverage
Test: mosh-1.3.2 Code Coverage Lines: 80 91 87.9 %
Date: 2022-02-06 20:19:53 Functions: 9 10 90.0 %
Legend: Lines: hit not hit

          Line data    Source code
       1             : /*
       2             :     Mosh: the mobile shell
       3             :     Copyright 2012 Keith Winstein
       4             : 
       5             :     This program is free software: you can redistribute it and/or modify
       6             :     it under the terms of the GNU General Public License as published by
       7             :     the Free Software Foundation, either version 3 of the License, or
       8             :     (at your option) any later version.
       9             : 
      10             :     This program is distributed in the hope that it will be useful,
      11             :     but WITHOUT ANY WARRANTY; without even the implied warranty of
      12             :     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
      13             :     GNU General Public License for more details.
      14             : 
      15             :     You should have received a copy of the GNU General Public License
      16             :     along with this program.  If not, see <http://www.gnu.org/licenses/>.
      17             : 
      18             :     In addition, as a special exception, the copyright holders give
      19             :     permission to link the code of portions of this program with the
      20             :     OpenSSL library under certain conditions as described in each
      21             :     individual source file, and distribute linked combinations including
      22             :     the two.
      23             : 
      24             :     You must obey the GNU General Public License in all respects for all
      25             :     of the code used other than OpenSSL. If you modify file(s) with this
      26             :     exception, you may extend this exception to your version of the
      27             :     file(s), but you are not obligated to do so. If you do not wish to do
      28             :     so, delete this exception statement from your version. If you delete
      29             :     this exception statement from all source files in the program, then
      30             :     also delete it here.
      31             : */
      32             : 
      33             : #ifndef TERMINAL_OVERLAY_HPP
      34             : #define TERMINAL_OVERLAY_HPP
      35             : 
      36             : #include "terminalframebuffer.h"
      37             : #include "network.h"
      38             : #include "transportsender.h"
      39             : #include "parser.h"
      40             : 
      41             : #include <vector>
      42             : #include <limits.h>
      43             : 
      44             : namespace Overlay {
      45             :   using namespace Terminal;
      46             :   using namespace Network;
      47             :   using std::deque;
      48             :   using std::list;
      49             :   using std::vector;
      50             :   using std::wstring;
      51             : 
      52             :   enum Validity {
      53             :     Pending,
      54             :     Correct,
      55             :     CorrectNoCredit,
      56             :     IncorrectOrExpired,
      57             :     Inactive
      58             :   };
      59             : 
      60         468 :   class ConditionalOverlay {
      61             :   public:
      62             :     uint64_t expiration_frame;
      63             :     int col;
      64             :     bool active; /* represents a prediction at all */
      65             :     uint64_t tentative_until_epoch; /* when to show */
      66             :     uint64_t prediction_time; /* used to find long-pending predictions */
      67             : 
      68        1908 :     ConditionalOverlay( uint64_t s_exp, int s_col, uint64_t s_tentative )
      69        1908 :       : expiration_frame( s_exp ), col( s_col ),
      70        1908 :         active( false ),
      71        1908 :         tentative_until_epoch( s_tentative ), prediction_time( uint64_t( -1 ) )
      72             :     {}
      73             : 
      74         468 :     virtual ~ConditionalOverlay() {}
      75             : 
      76       52898 :     bool tentative( uint64_t confirmed_epoch ) const { return tentative_until_epoch > confirmed_epoch; }
      77       49102 :     void reset( void ) { expiration_frame = tentative_until_epoch = -1; active = false; }
      78       36618 :     void expire( uint64_t s_exp, uint64_t now )
      79             :     {
      80       36618 :       expiration_frame = s_exp; prediction_time = now;
      81           0 :     }
      82             :   };
      83             : 
      84         468 :   class ConditionalCursorMove : public ConditionalOverlay {
      85             :   public:
      86             :     int row;
      87             : 
      88             :     void apply( Framebuffer &fb, uint64_t confirmed_epoch ) const;
      89             : 
      90             :     Validity get_validity( const Framebuffer &fb, uint64_t early_ack, uint64_t late_ack ) const;
      91             : 
      92         468 :     ConditionalCursorMove( uint64_t s_exp, int s_row, int s_col, uint64_t s_tentative )
      93         468 :       : ConditionalOverlay( s_exp, s_col, s_tentative ), row( s_row )
      94             :     {}
      95             :   };
      96             : 
      97             :   class ConditionalOverlayCell : public ConditionalOverlay {
      98             :   public:
      99             :     Cell replacement;
     100             :     bool unknown;
     101             : 
     102             :     vector<Cell> original_contents; /* we don't give credit for correct predictions
     103             :                                        that match the original contents */
     104             : 
     105             :     void apply( Framebuffer &fb, uint64_t confirmed_epoch, int row, bool flag ) const;
     106             :     Validity get_validity( const Framebuffer &fb, int row, uint64_t early_ack, uint64_t late_ack ) const;
     107             : 
     108        1440 :     ConditionalOverlayCell( uint64_t s_exp, int s_col, uint64_t s_tentative )
     109        1440 :       : ConditionalOverlay( s_exp, s_col, s_tentative ),
     110        1440 :         replacement( 0 ),
     111        1440 :         unknown( false ),
     112        1440 :         original_contents()
     113             :     {}
     114             : 
     115       13466 :     void reset( void ) { unknown = false; original_contents.clear(); ConditionalOverlay::reset(); }
     116       35636 :     void reset_with_orig( void ) {
     117       35636 :       if ( (!active) || unknown ) {
     118       13615 :         reset();
     119       13615 :         return;
     120             :       }
     121             : 
     122       22021 :       original_contents.push_back( replacement );
     123       22021 :       ConditionalOverlay::reset();
     124             :     }
     125             :   };
     126             : 
     127          54 :   class ConditionalOverlayRow {
     128             :   public:
     129             :     int row_num;
     130             : 
     131             :     typedef vector<ConditionalOverlayCell> overlay_cells_type;
     132             :     overlay_cells_type overlay_cells;
     133             : 
     134             :     void apply( Framebuffer &fb, uint64_t confirmed_epoch, bool flag ) const;
     135             : 
     136          18 :     ConditionalOverlayRow( int s_row_num ) : row_num( s_row_num ), overlay_cells() {}
     137             :   };
     138             : 
     139             :   /* the various overlays */
     140             :   class NotificationEngine {
     141             :   private:
     142             :     uint64_t last_word_from_server;
     143             :     uint64_t last_acked_state;
     144             :     string escape_key_string;
     145             :     wstring message;
     146             :     bool message_is_network_error;
     147             :     uint64_t message_expiration;
     148             :     bool show_quit_keystroke;
     149             : 
     150       12624 :     bool server_late( uint64_t ts ) const { return (ts - last_word_from_server) > 6500; }
     151       12624 :     bool reply_late( uint64_t ts ) const { return (ts - last_acked_state) > 10000; }
     152       12624 :     bool need_countup( uint64_t ts ) const { return server_late( ts ) || reply_late( ts ); }
     153             : 
     154             :   public:
     155             :     void adjust_message( void );
     156             :     void apply( Framebuffer &fb ) const;
     157        5154 :     const wstring &get_notification_string( void ) const { return message; }
     158        3781 :     void server_heard( uint64_t s_last_word ) { last_word_from_server = s_last_word; }
     159        3333 :     void server_acked( uint64_t s_last_acked ) { last_acked_state = s_last_acked; }
     160             :     int wait_time( void ) const;
     161             : 
     162         448 :     void set_notification_string( const wstring &s_message, bool permanent = false, bool s_show_quit_keystroke = true )
     163             :     {
     164         448 :       message = s_message;
     165         448 :       if ( permanent ) {
     166           0 :         message_expiration = -1;
     167             :       } else {
     168         448 :         message_expiration = timestamp() + 1000;
     169             :       }
     170         448 :       message_is_network_error = false;
     171         448 :       show_quit_keystroke = s_show_quit_keystroke;
     172         448 :     }
     173             : 
     174         448 :     void set_escape_key_string( const string &s_name )
     175             :     {
     176         448 :       char tmp[ 128 ];
     177         448 :       snprintf( tmp, sizeof tmp, " [To quit: %s .]", s_name.c_str() );
     178         448 :       escape_key_string = tmp;
     179         448 :     }
     180             : 
     181           0 :     void set_network_error( const std::string &s )
     182             :     {
     183           0 :       wchar_t tmp[ 128 ];
     184           0 :       swprintf( tmp, 128, L"%s", s.c_str() );
     185             : 
     186           0 :       message = tmp;
     187           0 :       message_is_network_error = true;
     188           0 :       message_expiration = timestamp() + Network::ACK_INTERVAL + 100;
     189           0 :     }
     190             : 
     191        5640 :     void clear_network_error()
     192             :     {
     193        5640 :       if ( message_is_network_error ) {
     194           0 :         message_expiration = std::min( message_expiration, timestamp() + 1000 );
     195             :       }
     196        5640 :     }
     197             : 
     198             :     NotificationEngine();
     199             :   };
     200             : 
     201             :   class PredictionEngine {
     202             :   private:
     203             :     static const uint64_t SRTT_TRIGGER_LOW = 20; /* <= ms cures SRTT trigger to show predictions */
     204             :     static const uint64_t SRTT_TRIGGER_HIGH = 30; /* > ms starts SRTT trigger */
     205             : 
     206             :     static const uint64_t FLAG_TRIGGER_LOW = 50; /* <= ms cures flagging */
     207             :     static const uint64_t FLAG_TRIGGER_HIGH = 80; /* > ms starts flagging */
     208             : 
     209             :     static const uint64_t GLITCH_THRESHOLD = 250; /* prediction outstanding this long is glitch */
     210             :     static const uint64_t GLITCH_REPAIR_COUNT = 10; /* non-glitches required to cure glitch trigger */
     211             :     static const uint64_t GLITCH_REPAIR_MININTERVAL = 150; /* required time in between non-glitches */
     212             : 
     213             :     static const uint64_t GLITCH_FLAG_THRESHOLD = 5000; /* prediction outstanding this long => underline */
     214             : 
     215             :     char last_byte;
     216             :     Parser::UTF8Parser parser;
     217             : 
     218             :     typedef list<ConditionalOverlayRow> overlays_type;
     219             :     overlays_type overlays;
     220             : 
     221             :     typedef list<ConditionalCursorMove> cursors_type;
     222             :     cursors_type cursors;
     223             : 
     224             :     typedef ConditionalOverlayRow::overlay_cells_type overlay_cells_type;
     225             : 
     226             :     uint64_t local_frame_sent, local_frame_acked, local_frame_late_acked;
     227             : 
     228             :     ConditionalOverlayRow & get_or_make_row( int row_num, int num_cols );
     229             : 
     230             :     uint64_t prediction_epoch;
     231             :     uint64_t confirmed_epoch;
     232             : 
     233             :     void become_tentative( void );
     234             : 
     235             :     void newline_carriage_return( const Framebuffer &fb );
     236             : 
     237             :     bool flagging; /* whether we are underlining predictions */
     238             :     bool srtt_trigger; /* show predictions because of slow round trip time */
     239             :     unsigned int glitch_trigger; /* show predictions temporarily because of long-pending prediction */
     240             :     uint64_t last_quick_confirmation;
     241             : 
     242      113681 :     ConditionalCursorMove & cursor( void ) { assert( !cursors.empty() ); return cursors.back(); }
     243             : 
     244             :     void kill_epoch( uint64_t epoch, const Framebuffer &fb );
     245             : 
     246             :     void init_cursor( const Framebuffer &fb );
     247             : 
     248             :     unsigned int send_interval;
     249             : 
     250             :     int last_height, last_width;
     251             : 
     252             :   public:
     253             :     enum DisplayPreference {
     254             :       Always,
     255             :       Never,
     256             :       Adaptive,
     257             :       Experimental
     258             :     };
     259             : 
     260             :   private:
     261             :     DisplayPreference display_preference;
     262             :     bool predict_overwrite;
     263             : 
     264             :     bool active( void ) const;
     265             : 
     266        6088 :     bool timing_tests_necessary( void ) const {
     267             :       /* Are there any timing-based triggers that haven't fired yet? */
     268         256 :       return !( glitch_trigger && flagging );
     269             :     }
     270             : 
     271             :   public:
     272         448 :     void set_display_preference( DisplayPreference s_pref ) { display_preference = s_pref; }
     273           0 :     void set_predict_overwrite( bool overwrite ) { predict_overwrite = overwrite; }
     274             : 
     275             :     void apply( Framebuffer &fb ) const;
     276             :     void new_user_byte( char the_byte, const Framebuffer &fb );
     277             :     void cull( const Framebuffer &fb );
     278             : 
     279             :     void reset( void );
     280             : 
     281         659 :     void set_local_frame_sent( uint64_t x ) { local_frame_sent = x; }
     282        3333 :     void set_local_frame_acked( uint64_t x ) { local_frame_acked = x; }
     283        3333 :     void set_local_frame_late_acked( uint64_t x ) { local_frame_late_acked = x; }
     284             : 
     285        3333 :     void set_send_interval( unsigned int x ) { send_interval = x; }
     286             : 
     287        6088 :     int wait_time( void ) const
     288             :     {
     289        6304 :       return ( timing_tests_necessary() && active() )
     290             :           ? 50
     291        6088 :           : INT_MAX;
     292             :     }
     293             : 
     294         448 :     PredictionEngine( void ) : last_byte( 0 ), parser(), overlays(), cursors(),
     295         448 :                                local_frame_sent( 0 ), local_frame_acked( 0 ),
     296         448 :                                local_frame_late_acked( 0 ),
     297         448 :                                prediction_epoch( 1 ), confirmed_epoch( 0 ),
     298         448 :                                flagging( false ),
     299         448 :                                srtt_trigger( false ),
     300         448 :                                glitch_trigger( 0 ),
     301         448 :                                last_quick_confirmation( 0 ),
     302         448 :                                send_interval( 250 ),
     303         448 :                                last_height( 0 ), last_width( 0 ),
     304         448 :                                display_preference( Adaptive ),
     305         448 :                                predict_overwrite( false )
     306             :     {
     307         448 :     }
     308             :   };
     309             : 
     310             :   class TitleEngine {
     311             :   private:
     312             :     Terminal::Framebuffer::title_type prefix;
     313             : 
     314             :   public:
     315        6536 :     void apply( Framebuffer &fb ) const { fb.prefix_window_title( prefix ); }
     316         448 :     TitleEngine() : prefix() {}
     317             :     void set_prefix( const wstring &s );
     318             :   };
     319             : 
     320             :   /* the overlay manager */
     321             :   class OverlayManager {
     322             :   private:
     323             :     NotificationEngine notifications;
     324             :     PredictionEngine predictions;
     325             :     TitleEngine title;
     326             : 
     327             :   public:
     328             :     void apply( Framebuffer &fb );
     329             : 
     330       15471 :     NotificationEngine & get_notification_engine( void ) { return notifications; }
     331        5667 :     PredictionEngine & get_prediction_engine( void ) { return predictions; }
     332             : 
     333         896 :     void set_title_prefix( const wstring &s ) { title.set_prefix( s ); }
     334             : 
     335         448 :     OverlayManager() : notifications(), predictions(), title() {}
     336             : 
     337        6088 :     int wait_time( void ) const
     338             :     {
     339        6088 :       return std::min( notifications.wait_time(), predictions.wait_time() );
     340             :     }
     341             :   };
     342             : }
     343             : 
     344             : #endif

Generated by: LCOV version 1.14