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 TERMINALFB_HPP
34 : #define TERMINALFB_HPP
35 :
36 : #include <assert.h>
37 : #include <limits.h>
38 : #include <stdint.h>
39 :
40 : #include <vector>
41 : #include <deque>
42 : #include <string>
43 : #include <list>
44 :
45 : #include "shared.h"
46 :
47 : /* Terminal framebuffer */
48 :
49 : namespace Terminal {
50 : using shared::shared_ptr;
51 : using shared::make_shared;
52 : typedef uint32_t color_type;
53 :
54 : class Renditions {
55 : public:
56 : typedef enum { bold, faint, italic, underlined, blink, inverse, invisible, SIZE } attribute_type;
57 :
58 : private:
59 : static const uint64_t true_color_mask = 0x1000000;
60 : uint64_t foreground_color : 25;
61 : uint64_t background_color : 25;
62 : uint64_t attributes : 8;
63 :
64 : public:
65 : Renditions( color_type s_background );
66 : void set_foreground_color( int num );
67 : void set_background_color( int num );
68 : void set_rendition( color_type num );
69 : std::string sgr( void ) const;
70 :
71 7398 : static unsigned int make_true_color( unsigned int r, unsigned int g, unsigned int b ) {
72 7398 : return true_color_mask | (r << 16) | (g << 8) | b;
73 : }
74 :
75 : static bool is_true_color(unsigned int color) {
76 : return (color & true_color_mask) != 0;
77 : }
78 :
79 : // unsigned int get_foreground_rendition() const { return foreground_color; }
80 48051945 : unsigned int get_background_rendition() const { return background_color; }
81 :
82 42452437 : bool operator==( const Renditions &x ) const
83 : {
84 42452437 : return ( attributes == x.attributes )
85 42452028 : && ( foreground_color == x.foreground_color )
86 84884949 : && ( background_color == x.background_color );
87 : }
88 4208 : void set_attribute( attribute_type attr, bool val )
89 : {
90 4208 : attributes = val ?
91 4206 : ( attributes | (1 << attr) ) :
92 2 : ( attributes & ~(1 << attr) );
93 4208 : }
94 41830 : bool get_attribute( attribute_type attr ) const { return attributes & ( 1 << attr ); }
95 10550 : void clear_attributes() { attributes = 0; }
96 : };
97 :
98 1940974982 : class Cell {
99 : private:
100 : typedef std::string content_type; /* can be std::string, std::vector<uint8_t>, or __gnu_cxx::__vstring */
101 : content_type contents;
102 : Renditions renditions;
103 : unsigned int wide : 1; /* 0 = narrow, 1 = wide */
104 : unsigned int fallback : 1; /* first character is combining character */
105 : unsigned int wrap : 1;
106 :
107 : private:
108 : Cell();
109 : public:
110 : Cell( color_type background_color );
111 :
112 : void reset( color_type background_color );
113 :
114 31060700 : bool operator==( const Cell &x ) const
115 : {
116 31060700 : return ( (contents == x.contents)
117 30474865 : && (fallback == x.fallback)
118 30474865 : && (wide == x.wide)
119 30474865 : && (renditions == x.renditions)
120 61535148 : && (wrap == x.wrap) );
121 : }
122 :
123 43467 : bool operator!=( const Cell &x ) const { return !operator==( x ); }
124 :
125 : /* Accessors for contents field */
126 : std::string debug_contents( void ) const;
127 :
128 1655001 : bool empty( void ) const { return contents.empty(); }
129 : /* 32 seems like a reasonable limit on combining characters */
130 12 : bool full( void ) const { return contents.size() >= 32; }
131 982 : void clear( void ) { contents.clear(); }
132 :
133 20423150 : bool is_blank( void ) const
134 : {
135 : // XXX fix.
136 20423150 : return ( contents.empty()
137 241516 : || contents == " "
138 20618438 : || contents == "\xC2\xA0" );
139 : }
140 :
141 10257723 : bool contents_match ( const Cell &other ) const
142 : {
143 20323449 : return ( is_blank() && other.is_blank() )
144 10259013 : || ( contents == other.contents );
145 : }
146 :
147 : bool compare( const Cell &other ) const;
148 :
149 : // Is this a printing ISO 8859-1 character?
150 24028354 : static bool isprint_iso8859_1( const wchar_t c )
151 : {
152 24028354 : return ( c <= 0xff && c >= 0xa0 ) || ( c <= 0x7e && c >= 0x20 );
153 : }
154 :
155 0 : static void append_to_str( std::string &dest, const wchar_t c )
156 : {
157 : /* ASCII? Cheat. */
158 0 : if ( static_cast<uint32_t>(c) <= 0x7f ) {
159 0 : dest.push_back( static_cast<char>(c) );
160 0 : return;
161 : }
162 0 : static mbstate_t ps = mbstate_t();
163 0 : char tmp[MB_LEN_MAX];
164 0 : size_t ignore = wcrtomb(NULL, 0, &ps);
165 0 : (void)ignore;
166 0 : size_t len = wcrtomb(tmp, c, &ps);
167 0 : dest.append( tmp, len );
168 : }
169 :
170 24029016 : void append( const wchar_t c )
171 : {
172 : /* ASCII? Cheat. */
173 24029016 : if ( static_cast<uint32_t>(c) <= 0x7f ) {
174 24027822 : contents.push_back( static_cast<char>(c) );
175 24027822 : return;
176 : }
177 1194 : static mbstate_t ps = mbstate_t();
178 1194 : char tmp[MB_LEN_MAX];
179 1194 : size_t ignore = wcrtomb(NULL, 0, &ps);
180 1194 : (void)ignore;
181 1194 : size_t len = wcrtomb(tmp, c, &ps);
182 1194 : contents.insert( contents.end(), tmp, tmp+len );
183 : }
184 :
185 21101133 : void print_grapheme( std::string &output ) const
186 : {
187 21101133 : if ( contents.empty() ) {
188 20083264 : output.append( 1, ' ' );
189 20083264 : return;
190 : }
191 : /*
192 : * cells that begin with combining character get combiner
193 : * attached to no-break space
194 : */
195 1017869 : if ( fallback ) {
196 52 : output.append( "\xC2\xA0" );
197 : }
198 1017869 : output.append( contents );
199 : }
200 :
201 : /* Other accessors */
202 1656511 : const Renditions& get_renditions( void ) const { return renditions; }
203 18 : Renditions& get_renditions( void ) { return renditions; }
204 24028342 : void set_renditions( const Renditions& r ) { renditions = r; }
205 4 : bool get_wide( void ) const { return wide; }
206 24028342 : void set_wide( bool w ) { wide = w; }
207 29611014 : unsigned int get_width( void ) const { return wide + 1; }
208 : bool get_fallback( void ) const { return fallback; }
209 4 : void set_fallback( bool f ) { fallback = f; }
210 385328 : bool get_wrap( void ) const { return wrap; }
211 1876 : void set_wrap( bool f ) { wrap = f; }
212 : };
213 :
214 24048969 : class Row {
215 : public:
216 : typedef std::vector<Cell> cells_type;
217 : cells_type cells;
218 : // gen is a generation counter. It can be used to quickly rule
219 : // out the possibility of two rows being identical; this is useful
220 : // in scrolling.
221 : uint64_t gen;
222 :
223 : private:
224 : Row();
225 : public:
226 : Row( const size_t s_width, const color_type background_color );
227 :
228 : void insert_cell( int col, color_type background_color );
229 : void delete_cell( int col, color_type background_color );
230 :
231 : void reset( color_type background_color );
232 :
233 385758 : bool operator==( const Row &x ) const
234 : {
235 385758 : return ( gen == x.gen && cells == x.cells );
236 : }
237 :
238 385328 : bool get_wrap( void ) const { return cells.back().get_wrap(); }
239 1876 : void set_wrap( bool w ) { cells.back().set_wrap( w ); }
240 :
241 : uint64_t get_gen() const;
242 : };
243 :
244 : class SavedCursor {
245 : public:
246 : int cursor_col, cursor_row;
247 : Renditions renditions;
248 : /* not implemented: character set shift state */
249 : bool auto_wrap_mode;
250 : bool origin_mode;
251 : /* not implemented: state of selective erase */
252 :
253 : SavedCursor();
254 : };
255 :
256 0 : class DrawState {
257 : private:
258 : int width, height;
259 :
260 : void new_grapheme( void );
261 : void snap_cursor_to_border( void );
262 :
263 : int cursor_col, cursor_row;
264 : int combining_char_col, combining_char_row;
265 :
266 : bool default_tabs;
267 : std::vector<bool> tabs;
268 :
269 : void reinitialize_tabs( unsigned int start );
270 :
271 : int scrolling_region_top_row, scrolling_region_bottom_row;
272 :
273 : Renditions renditions;
274 :
275 : SavedCursor save;
276 :
277 : public:
278 : bool next_print_will_wrap;
279 : bool origin_mode;
280 : bool auto_wrap_mode;
281 : bool insert_mode;
282 : bool cursor_visible;
283 : bool reverse_video;
284 : bool bracketed_paste;
285 :
286 : enum MouseReportingMode {
287 : MOUSE_REPORTING_NONE = 0,
288 : MOUSE_REPORTING_X10 = 9,
289 : MOUSE_REPORTING_VT220 = 1000,
290 : MOUSE_REPORTING_VT220_HILIGHT = 1001,
291 : MOUSE_REPORTING_BTN_EVENT = 1002,
292 : MOUSE_REPORTING_ANY_EVENT = 1003
293 : } mouse_reporting_mode;
294 :
295 : bool mouse_focus_event; // 1004
296 : bool mouse_alternate_scroll; // 1007
297 :
298 : enum MouseEncodingMode {
299 : MOUSE_ENCODING_DEFAULT = 0,
300 : MOUSE_ENCODING_UTF8 = 1005,
301 : MOUSE_ENCODING_SGR = 1006,
302 : MOUSE_ENCODING_URXVT = 1015
303 : } mouse_encoding_mode;
304 :
305 : bool application_mode_cursor_keys;
306 :
307 : /* bold, etc. */
308 :
309 : void move_row( int N, bool relative = false );
310 : void move_col( int N, bool relative = false, bool implicit = false );
311 :
312 24076789 : int get_cursor_col( void ) const { return cursor_col; }
313 23997298 : int get_cursor_row( void ) const { return cursor_row; }
314 12 : int get_combining_char_col( void ) const { return combining_char_col; }
315 12 : int get_combining_char_row( void ) const { return combining_char_row; }
316 652357 : int get_width( void ) const { return width; }
317 1272074 : int get_height( void ) const { return height; }
318 :
319 : void set_tab( void );
320 : void clear_tab( int col );
321 0 : void clear_default_tabs( void ) { default_tabs = false; }
322 : /* Default tabs can't be restored without resetting the draw state. */
323 : int get_next_tab( int count ) const;
324 :
325 : void set_scrolling_region( int top, int bottom );
326 :
327 95531190 : int get_scrolling_region_top_row( void ) const { return scrolling_region_top_row; }
328 71666659 : int get_scrolling_region_bottom_row( void ) const { return scrolling_region_bottom_row; }
329 :
330 : int limit_top( void ) const;
331 : int limit_bottom( void ) const;
332 :
333 5200 : void set_foreground_color( int x ) { renditions.set_foreground_color( x ); }
334 5212 : void set_background_color( int x ) { renditions.set_background_color( x ); }
335 14918 : void add_rendition( color_type x ) { renditions.set_rendition( x ); }
336 24199 : const Renditions& get_renditions( void ) const { return renditions; }
337 24028342 : Renditions& get_renditions( void ) { return renditions; }
338 24186798 : int get_background_rendition( void ) const { return renditions.get_background_rendition(); }
339 :
340 : void save_cursor( void );
341 : void restore_cursor( void );
342 0 : void clear_saved_cursor( void ) { save = SavedCursor(); }
343 :
344 : void resize( int s_width, int s_height );
345 :
346 : DrawState( int s_width, int s_height );
347 :
348 16609 : bool operator==( const DrawState &x ) const
349 : {
350 : /* only compare fields that affect display */
351 16609 : return ( width == x.width ) && ( height == x.height ) && ( cursor_col == x.cursor_col )
352 16609 : && ( cursor_row == x.cursor_row ) && ( cursor_visible == x.cursor_visible ) &&
353 15467 : ( reverse_video == x.reverse_video ) && ( renditions == x.renditions ) &&
354 15467 : ( bracketed_paste == x.bracketed_paste ) && ( mouse_reporting_mode == x.mouse_reporting_mode ) &&
355 32076 : ( mouse_focus_event == x.mouse_focus_event ) && ( mouse_alternate_scroll == x.mouse_alternate_scroll) &&
356 15467 : ( mouse_encoding_mode == x.mouse_encoding_mode );
357 : }
358 : };
359 :
360 : class Framebuffer {
361 : // To minimize copying of rows and cells, we use shared_ptr to
362 : // share unchanged rows between multiple Framebuffers. If we
363 : // write to a row in a Framebuffer and it is shared with other
364 : // owners, we copy it first. The shared_ptr naturally manages the
365 : // usage of the actual rows themselves.
366 : //
367 : // We gain a couple of free extras by doing this:
368 : //
369 : // * A quick check for equality between rows in different
370 : // Framebuffers is to simply compare the pointer values. If they
371 : // are equal, then the rows are obviously identical.
372 : // * If no row is shared, the frame has not been modified.
373 : public:
374 : typedef std::vector<wchar_t> title_type;
375 : typedef shared_ptr<Row> row_pointer;
376 : typedef std::vector<row_pointer> rows_type; /* can be either std::vector or std::deque */
377 :
378 : private:
379 : rows_type rows;
380 : title_type icon_name;
381 : title_type window_title;
382 : title_type clipboard;
383 : unsigned int bell_count;
384 : bool title_initialized; /* true if the window title has been set via an OSC */
385 :
386 23865147 : row_pointer newrow( void )
387 : {
388 23865147 : const size_t w = ds.get_width();
389 23865147 : const color_type c = ds.get_background_rendition();
390 23865147 : return make_shared<Row>( w, c );
391 : }
392 :
393 : public:
394 : Framebuffer( int s_width, int s_height );
395 : Framebuffer( const Framebuffer &other );
396 : Framebuffer &operator=( const Framebuffer &other );
397 : DrawState ds;
398 :
399 24199 : const rows_type &get_rows() const { return rows; }
400 :
401 : void scroll( int N );
402 : void move_rows_autoscroll( int rows );
403 :
404 954674 : inline const Row *get_row( int row ) const
405 : {
406 578066 : if ( row == -1 ) row = ds.get_cursor_row();
407 :
408 954674 : return rows.at( row ).get();
409 : }
410 :
411 20759523 : inline const Cell *get_cell( int row = -1, int col = -1 ) const
412 : {
413 20759523 : if ( row == -1 ) row = ds.get_cursor_row();
414 20759523 : if ( col == -1 ) col = ds.get_cursor_col();
415 :
416 20759523 : return &rows.at( row )->cells.at( col );
417 : }
418 :
419 24185988 : Row *get_mutable_row( int row )
420 : {
421 24185988 : if ( row == -1 ) row = ds.get_cursor_row();
422 24185988 : row_pointer &mutable_row = rows.at( row );
423 : // If the row is shared, copy it.
424 24185988 : if (!mutable_row.unique()) {
425 81984 : mutable_row = make_shared<Row>( *mutable_row );
426 : }
427 24185988 : return mutable_row.get();
428 : }
429 :
430 24181242 : Cell *get_mutable_cell( int row = -1, int col = -1 )
431 : {
432 24181242 : if ( row == -1 ) row = ds.get_cursor_row();
433 24181242 : if ( col == -1 ) col = ds.get_cursor_col();
434 :
435 24181242 : return &get_mutable_row( row )->cells.at( col );
436 : }
437 :
438 : Cell *get_combining_cell( void );
439 :
440 : void apply_renditions_to_cell( Cell *cell );
441 :
442 : void insert_line( int before_row, int count );
443 : void delete_line( int row, int count );
444 :
445 : void insert_cell( int row, int col );
446 : void delete_cell( int row, int col );
447 :
448 : void reset( void );
449 : void soft_reset( void );
450 :
451 0 : void set_title_initialized( void ) { title_initialized = true; }
452 24199 : bool is_title_initialized( void ) const { return title_initialized; }
453 0 : void set_icon_name( const title_type &s ) { icon_name = s; }
454 0 : void set_window_title( const title_type &s ) { window_title = s; }
455 0 : void set_clipboard( const title_type &s ) { clipboard = s; }
456 0 : const title_type & get_icon_name( void ) const { return icon_name; }
457 0 : const title_type & get_window_title( void ) const { return window_title; }
458 24199 : const title_type & get_clipboard( void ) const { return clipboard; }
459 :
460 : void prefix_window_title( const title_type &s );
461 :
462 : void resize( int s_width, int s_height );
463 :
464 24179896 : void reset_cell( Cell *c ) { c->reset( ds.get_background_rendition() ); }
465 4454 : void reset_row( Row *r ) { r->reset( ds.get_background_rendition() ); }
466 :
467 0 : void ring_bell( void ) { bell_count++; }
468 24199 : unsigned int get_bell_count( void ) const { return bell_count; }
469 :
470 85482 : bool operator==( const Framebuffer &x ) const
471 : {
472 85482 : return ( rows == x.rows ) && ( window_title == x.window_title ) && ( clipboard == x.clipboard ) && ( bell_count == x.bell_count ) && ( ds == x.ds );
473 : }
474 : };
475 : }
476 :
477 : #endif
|