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
|