/src/mosh/src/terminal/terminalframebuffer.cc
Line | Count | Source (jump to first uncovered line) |
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 | | #include <cassert> |
34 | | #include <cstdio> |
35 | | #include <cstdlib> |
36 | | |
37 | | #include "src/terminal/terminalframebuffer.h" |
38 | | |
39 | | using namespace Terminal; |
40 | | |
41 | | Cell::Cell( color_type background_color ) |
42 | 238 | : contents(), renditions( background_color ), wide( false ), fallback( false ), wrap( false ) |
43 | 238 | {} |
44 | | |
45 | | void Cell::reset( color_type background_color ) |
46 | 0 | { |
47 | 0 | contents.clear(); |
48 | 0 | renditions = Renditions( background_color ); |
49 | 0 | wide = false; |
50 | 0 | fallback = false; |
51 | 0 | wrap = false; |
52 | 0 | } |
53 | | |
54 | | void DrawState::reinitialize_tabs( unsigned int start ) |
55 | 238 | { |
56 | 238 | assert( default_tabs ); |
57 | 19.2k | for ( unsigned int i = start; i < tabs.size(); i++ ) { |
58 | 19.0k | tabs[i] = ( ( i % 8 ) == 0 ); |
59 | 19.0k | } |
60 | 238 | } |
61 | | |
62 | | DrawState::DrawState( int s_width, int s_height ) |
63 | 238 | : width( s_width ), height( s_height ), cursor_col( 0 ), cursor_row( 0 ), combining_char_col( 0 ), |
64 | 238 | combining_char_row( 0 ), default_tabs( true ), tabs( s_width ), scrolling_region_top_row( 0 ), |
65 | 238 | scrolling_region_bottom_row( height - 1 ), renditions( 0 ), save(), next_print_will_wrap( false ), |
66 | 238 | origin_mode( false ), auto_wrap_mode( true ), insert_mode( false ), cursor_visible( true ), |
67 | 238 | reverse_video( false ), bracketed_paste( false ), mouse_reporting_mode( MOUSE_REPORTING_NONE ), |
68 | 238 | mouse_focus_event( false ), mouse_alternate_scroll( false ), mouse_encoding_mode( MOUSE_ENCODING_DEFAULT ), |
69 | 238 | application_mode_cursor_keys( false ) |
70 | 238 | { |
71 | 238 | reinitialize_tabs( 0 ); |
72 | 238 | } |
73 | | |
74 | | Framebuffer::Framebuffer( int s_width, int s_height ) |
75 | 238 | : rows(), icon_name(), window_title(), clipboard(), bell_count( 0 ), title_initialized( false ), |
76 | 238 | ds( s_width, s_height ) |
77 | 238 | { |
78 | 238 | assert( s_height > 0 ); |
79 | 238 | assert( s_width > 0 ); |
80 | 238 | const size_t w = s_width; |
81 | 238 | const color_type c = 0; |
82 | 238 | rows = rows_type( s_height, row_pointer( std::make_shared<Row>( w, c ) ) ); |
83 | 238 | } |
84 | | |
85 | | Framebuffer::Framebuffer( const Framebuffer& other ) |
86 | 0 | : rows( other.rows ), icon_name( other.icon_name ), window_title( other.window_title ), |
87 | 0 | clipboard( other.clipboard ), bell_count( other.bell_count ), title_initialized( other.title_initialized ), |
88 | 0 | ds( other.ds ) |
89 | 0 | {} |
90 | | |
91 | | Framebuffer& Framebuffer::operator=( const Framebuffer& other ) |
92 | 0 | { |
93 | 0 | if ( this != &other ) { |
94 | 0 | rows = other.rows; |
95 | 0 | icon_name = other.icon_name; |
96 | 0 | window_title = other.window_title; |
97 | 0 | clipboard = other.clipboard; |
98 | 0 | bell_count = other.bell_count; |
99 | 0 | title_initialized = other.title_initialized; |
100 | 0 | ds = other.ds; |
101 | 0 | } |
102 | 0 | return *this; |
103 | 0 | } |
104 | | |
105 | | void Framebuffer::scroll( int N ) |
106 | 0 | { |
107 | 0 | if ( N >= 0 ) { |
108 | 0 | delete_line( ds.get_scrolling_region_top_row(), N ); |
109 | 0 | } else { |
110 | 0 | insert_line( ds.get_scrolling_region_top_row(), -N ); |
111 | 0 | } |
112 | 0 | } |
113 | | |
114 | | void DrawState::new_grapheme( void ) |
115 | 0 | { |
116 | 0 | combining_char_col = cursor_col; |
117 | 0 | combining_char_row = cursor_row; |
118 | 0 | } |
119 | | |
120 | | void DrawState::snap_cursor_to_border( void ) |
121 | 0 | { |
122 | 0 | if ( cursor_row < limit_top() ) |
123 | 0 | cursor_row = limit_top(); |
124 | 0 | if ( cursor_row > limit_bottom() ) |
125 | 0 | cursor_row = limit_bottom(); |
126 | 0 | if ( cursor_col < 0 ) |
127 | 0 | cursor_col = 0; |
128 | 0 | if ( cursor_col >= width ) |
129 | 0 | cursor_col = width - 1; |
130 | 0 | } |
131 | | |
132 | | void DrawState::move_row( int N, bool relative ) |
133 | 0 | { |
134 | 0 | if ( relative ) { |
135 | 0 | cursor_row += N; |
136 | 0 | } else { |
137 | 0 | cursor_row = N + limit_top(); |
138 | 0 | } |
139 | |
|
140 | 0 | snap_cursor_to_border(); |
141 | 0 | new_grapheme(); |
142 | 0 | next_print_will_wrap = false; |
143 | 0 | } |
144 | | |
145 | | void DrawState::move_col( int N, bool relative, bool implicit ) |
146 | 0 | { |
147 | 0 | if ( implicit ) { |
148 | 0 | new_grapheme(); |
149 | 0 | } |
150 | |
|
151 | 0 | if ( relative ) { |
152 | 0 | cursor_col += N; |
153 | 0 | } else { |
154 | 0 | cursor_col = N; |
155 | 0 | } |
156 | |
|
157 | 0 | if ( implicit ) { |
158 | 0 | next_print_will_wrap = ( cursor_col >= width ); |
159 | 0 | } |
160 | |
|
161 | 0 | snap_cursor_to_border(); |
162 | 0 | if ( !implicit ) { |
163 | 0 | new_grapheme(); |
164 | 0 | next_print_will_wrap = false; |
165 | 0 | } |
166 | 0 | } |
167 | | |
168 | | void Framebuffer::move_rows_autoscroll( int rows ) |
169 | 0 | { |
170 | | /* don't scroll if outside the scrolling region */ |
171 | 0 | if ( ( ds.get_cursor_row() < ds.get_scrolling_region_top_row() ) |
172 | 0 | || ( ds.get_cursor_row() > ds.get_scrolling_region_bottom_row() ) ) { |
173 | 0 | ds.move_row( rows, true ); |
174 | 0 | return; |
175 | 0 | } |
176 | | |
177 | 0 | if ( ds.get_cursor_row() + rows > ds.get_scrolling_region_bottom_row() ) { |
178 | 0 | int N = ds.get_cursor_row() + rows - ds.get_scrolling_region_bottom_row(); |
179 | 0 | scroll( N ); |
180 | 0 | ds.move_row( -N, true ); |
181 | 0 | } else if ( ds.get_cursor_row() + rows < ds.get_scrolling_region_top_row() ) { |
182 | 0 | int N = ds.get_cursor_row() + rows - ds.get_scrolling_region_top_row(); |
183 | 0 | scroll( N ); |
184 | 0 | ds.move_row( -N, true ); |
185 | 0 | } |
186 | |
|
187 | 0 | ds.move_row( rows, true ); |
188 | 0 | } |
189 | | |
190 | | Cell* Framebuffer::get_combining_cell( void ) |
191 | 0 | { |
192 | 0 | if ( ( ds.get_combining_char_col() < 0 ) || ( ds.get_combining_char_row() < 0 ) |
193 | 0 | || ( ds.get_combining_char_col() >= ds.get_width() ) |
194 | 0 | || ( ds.get_combining_char_row() >= ds.get_height() ) ) { |
195 | 0 | return NULL; |
196 | 0 | } /* can happen if a resize came in between */ |
197 | | |
198 | 0 | return get_mutable_cell( ds.get_combining_char_row(), ds.get_combining_char_col() ); |
199 | 0 | } |
200 | | |
201 | | void DrawState::set_tab( void ) |
202 | 0 | { |
203 | 0 | tabs[cursor_col] = true; |
204 | 0 | } |
205 | | |
206 | | void DrawState::clear_tab( int col ) |
207 | 0 | { |
208 | 0 | tabs[col] = false; |
209 | 0 | } |
210 | | |
211 | | int DrawState::get_next_tab( int count ) const |
212 | 0 | { |
213 | 0 | if ( count >= 0 ) { |
214 | 0 | for ( int i = cursor_col + 1; i < width; i++ ) { |
215 | 0 | if ( tabs[i] && --count == 0 ) { |
216 | 0 | return i; |
217 | 0 | } |
218 | 0 | } |
219 | 0 | return -1; |
220 | 0 | } |
221 | 0 | for ( int i = cursor_col - 1; i > 0; i-- ) { |
222 | 0 | if ( tabs[i] && ++count == 0 ) { |
223 | 0 | return i; |
224 | 0 | } |
225 | 0 | } |
226 | 0 | return 0; |
227 | 0 | } |
228 | | |
229 | | void DrawState::set_scrolling_region( int top, int bottom ) |
230 | 0 | { |
231 | 0 | if ( height < 1 ) { |
232 | 0 | return; |
233 | 0 | } |
234 | | |
235 | 0 | scrolling_region_top_row = top; |
236 | 0 | scrolling_region_bottom_row = bottom; |
237 | |
|
238 | 0 | if ( scrolling_region_top_row < 0 ) |
239 | 0 | scrolling_region_top_row = 0; |
240 | 0 | if ( scrolling_region_bottom_row >= height ) |
241 | 0 | scrolling_region_bottom_row = height - 1; |
242 | |
|
243 | 0 | if ( scrolling_region_bottom_row < scrolling_region_top_row ) |
244 | 0 | scrolling_region_bottom_row = scrolling_region_top_row; |
245 | | /* real rule requires TWO-line scrolling region */ |
246 | |
|
247 | 0 | if ( origin_mode ) { |
248 | 0 | snap_cursor_to_border(); |
249 | 0 | new_grapheme(); |
250 | 0 | } |
251 | 0 | } |
252 | | |
253 | | int DrawState::limit_top( void ) const |
254 | 0 | { |
255 | 0 | return origin_mode ? scrolling_region_top_row : 0; |
256 | 0 | } |
257 | | |
258 | | int DrawState::limit_bottom( void ) const |
259 | 0 | { |
260 | 0 | return origin_mode ? scrolling_region_bottom_row : height - 1; |
261 | 0 | } |
262 | | |
263 | | void Framebuffer::apply_renditions_to_cell( Cell* cell ) |
264 | 0 | { |
265 | 0 | if ( !cell ) { |
266 | 0 | cell = get_mutable_cell(); |
267 | 0 | } |
268 | 0 | cell->set_renditions( ds.get_renditions() ); |
269 | 0 | } |
270 | | |
271 | | SavedCursor::SavedCursor() |
272 | 238 | : cursor_col( 0 ), cursor_row( 0 ), renditions( 0 ), auto_wrap_mode( true ), origin_mode( false ) |
273 | 238 | {} |
274 | | |
275 | | void DrawState::save_cursor( void ) |
276 | 0 | { |
277 | 0 | save.cursor_col = cursor_col; |
278 | 0 | save.cursor_row = cursor_row; |
279 | 0 | save.renditions = renditions; |
280 | 0 | save.auto_wrap_mode = auto_wrap_mode; |
281 | 0 | save.origin_mode = origin_mode; |
282 | 0 | } |
283 | | |
284 | | void DrawState::restore_cursor( void ) |
285 | 0 | { |
286 | 0 | cursor_col = save.cursor_col; |
287 | 0 | cursor_row = save.cursor_row; |
288 | 0 | renditions = save.renditions; |
289 | 0 | auto_wrap_mode = save.auto_wrap_mode; |
290 | 0 | origin_mode = save.origin_mode; |
291 | |
|
292 | 0 | snap_cursor_to_border(); /* we could have resized in between */ |
293 | 0 | new_grapheme(); |
294 | 0 | } |
295 | | |
296 | | void Framebuffer::insert_line( int before_row, int count ) |
297 | 0 | { |
298 | 0 | if ( ( before_row < ds.get_scrolling_region_top_row() ) |
299 | 0 | || ( before_row > ds.get_scrolling_region_bottom_row() + 1 ) ) { |
300 | 0 | return; |
301 | 0 | } |
302 | | |
303 | 0 | int scroll = ds.get_scrolling_region_bottom_row() + 1 - before_row; |
304 | 0 | if ( count < scroll ) { |
305 | 0 | scroll = count; |
306 | 0 | } |
307 | |
|
308 | 0 | if ( scroll == 0 ) { |
309 | 0 | return; |
310 | 0 | } |
311 | | |
312 | | // delete old rows |
313 | 0 | rows_type::iterator start = rows.begin() + ds.get_scrolling_region_bottom_row() + 1 - scroll; |
314 | 0 | rows.erase( start, start + scroll ); |
315 | | // insert new rows |
316 | 0 | start = rows.begin() + before_row; |
317 | 0 | rows.insert( start, scroll, newrow() ); |
318 | 0 | } |
319 | | |
320 | | void Framebuffer::delete_line( int row, int count ) |
321 | 0 | { |
322 | 0 | if ( ( row < ds.get_scrolling_region_top_row() ) || ( row > ds.get_scrolling_region_bottom_row() ) ) { |
323 | 0 | return; |
324 | 0 | } |
325 | | |
326 | 0 | int scroll = ds.get_scrolling_region_bottom_row() + 1 - row; |
327 | 0 | if ( count < scroll ) { |
328 | 0 | scroll = count; |
329 | 0 | } |
330 | |
|
331 | 0 | if ( scroll == 0 ) { |
332 | 0 | return; |
333 | 0 | } |
334 | | |
335 | | // delete old rows |
336 | 0 | rows_type::iterator start = rows.begin() + row; |
337 | 0 | rows.erase( start, start + scroll ); |
338 | | // insert a block of dummy rows |
339 | 0 | start = rows.begin() + ds.get_scrolling_region_bottom_row() + 1 - scroll; |
340 | 0 | rows.insert( start, scroll, newrow() ); |
341 | 0 | } |
342 | | |
343 | | Row::Row( const size_t s_width, const color_type background_color ) |
344 | 238 | : cells( s_width, Cell( background_color ) ), gen( get_gen() ) |
345 | 238 | {} |
346 | | |
347 | | uint64_t Row::get_gen() const |
348 | 238 | { |
349 | 238 | static uint64_t gen_counter = 0; |
350 | 238 | return gen_counter++; |
351 | 238 | } |
352 | | |
353 | | void Row::insert_cell( int col, color_type background_color ) |
354 | 0 | { |
355 | 0 | cells.insert( cells.begin() + col, Cell( background_color ) ); |
356 | 0 | cells.pop_back(); |
357 | 0 | } |
358 | | |
359 | | void Row::delete_cell( int col, color_type background_color ) |
360 | 0 | { |
361 | 0 | cells.push_back( Cell( background_color ) ); |
362 | 0 | cells.erase( cells.begin() + col ); |
363 | 0 | } |
364 | | |
365 | | void Framebuffer::insert_cell( int row, int col ) |
366 | 0 | { |
367 | 0 | get_mutable_row( row )->insert_cell( col, ds.get_background_rendition() ); |
368 | 0 | } |
369 | | |
370 | | void Framebuffer::delete_cell( int row, int col ) |
371 | 0 | { |
372 | 0 | get_mutable_row( row )->delete_cell( col, ds.get_background_rendition() ); |
373 | 0 | } |
374 | | |
375 | | void Framebuffer::reset( void ) |
376 | 0 | { |
377 | 0 | int width = ds.get_width(), height = ds.get_height(); |
378 | 0 | ds = DrawState( width, height ); |
379 | 0 | rows = rows_type( height, newrow() ); |
380 | 0 | window_title.clear(); |
381 | 0 | clipboard.clear(); |
382 | | /* do not reset bell_count */ |
383 | 0 | } |
384 | | |
385 | | void Framebuffer::soft_reset( void ) |
386 | 0 | { |
387 | 0 | ds.insert_mode = false; |
388 | 0 | ds.origin_mode = false; |
389 | 0 | ds.cursor_visible = true; /* per xterm and gnome-terminal */ |
390 | 0 | ds.application_mode_cursor_keys = false; |
391 | 0 | ds.set_scrolling_region( 0, ds.get_height() - 1 ); |
392 | 0 | ds.add_rendition( 0 ); |
393 | 0 | ds.clear_saved_cursor(); |
394 | 0 | } |
395 | | |
396 | | void Framebuffer::resize( int s_width, int s_height ) |
397 | 0 | { |
398 | 0 | assert( s_width > 0 ); |
399 | 0 | assert( s_height > 0 ); |
400 | | |
401 | 0 | int oldheight = ds.get_height(); |
402 | 0 | int oldwidth = ds.get_width(); |
403 | 0 | ds.resize( s_width, s_height ); |
404 | |
|
405 | 0 | row_pointer blankrow( newrow() ); |
406 | 0 | if ( oldheight != s_height ) { |
407 | 0 | rows.resize( s_height, blankrow ); |
408 | 0 | } |
409 | 0 | if ( oldwidth == s_width ) { |
410 | 0 | return; |
411 | 0 | } |
412 | 0 | for ( rows_type::iterator i = rows.begin(); i != rows.end() && *i != blankrow; i++ ) { |
413 | 0 | *i = std::make_shared<Row>( **i ); |
414 | 0 | ( *i )->set_wrap( false ); |
415 | 0 | ( *i )->cells.resize( s_width, Cell( ds.get_background_rendition() ) ); |
416 | 0 | } |
417 | 0 | } |
418 | | |
419 | | void DrawState::resize( int s_width, int s_height ) |
420 | 0 | { |
421 | 0 | if ( ( width != s_width ) || ( height != s_height ) ) { |
422 | | /* reset entire scrolling region on any resize */ |
423 | | /* xterm and rxvt-unicode do this. gnome-terminal only |
424 | | resets scrolling region if it has to become smaller in resize */ |
425 | 0 | scrolling_region_top_row = 0; |
426 | 0 | scrolling_region_bottom_row = s_height - 1; |
427 | 0 | } |
428 | |
|
429 | 0 | tabs.resize( s_width ); |
430 | 0 | if ( default_tabs ) { |
431 | 0 | reinitialize_tabs( width ); |
432 | 0 | } |
433 | |
|
434 | 0 | width = s_width; |
435 | 0 | height = s_height; |
436 | |
|
437 | 0 | snap_cursor_to_border(); |
438 | | |
439 | | /* saved cursor will be snapped to border on restore */ |
440 | | |
441 | | /* invalidate combining char cell if necessary */ |
442 | 0 | if ( ( combining_char_col >= width ) || ( combining_char_row >= height ) ) { |
443 | 0 | combining_char_col = combining_char_row = -1; |
444 | 0 | } |
445 | 0 | } |
446 | | |
447 | | Renditions::Renditions( color_type s_background ) |
448 | 834 | : foreground_color( 0 ), background_color( s_background ), attributes( 0 ) |
449 | 834 | {} |
450 | | |
451 | | /* This routine cannot be used to set a color beyond the 16-color set. */ |
452 | | void Renditions::set_rendition( color_type num ) |
453 | 0 | { |
454 | 0 | if ( num == 0 ) { |
455 | 0 | clear_attributes(); |
456 | 0 | foreground_color = background_color = 0; |
457 | 0 | return; |
458 | 0 | } |
459 | | |
460 | 0 | if ( num == 39 ) { |
461 | 0 | foreground_color = 0; |
462 | 0 | return; |
463 | 0 | } else if ( num == 49 ) { |
464 | 0 | background_color = 0; |
465 | 0 | return; |
466 | 0 | } |
467 | | |
468 | 0 | if ( ( 30 <= num ) && ( num <= 37 ) ) { /* foreground color in 8-color set */ |
469 | 0 | foreground_color = num; |
470 | 0 | return; |
471 | 0 | } else if ( ( 40 <= num ) && ( num <= 47 ) ) { /* background color in 8-color set */ |
472 | 0 | background_color = num; |
473 | 0 | return; |
474 | 0 | } else if ( ( 90 <= num ) && ( num <= 97 ) ) { /* foreground color in 16-color set */ |
475 | 0 | foreground_color = num - 90 + 38; |
476 | 0 | return; |
477 | 0 | } else if ( ( 100 <= num ) && ( num <= 107 ) ) { /* background color in 16-color set */ |
478 | 0 | background_color = num - 100 + 48; |
479 | 0 | return; |
480 | 0 | } |
481 | | |
482 | 0 | bool value = num < 9; |
483 | 0 | switch ( num ) { |
484 | 0 | case 1: |
485 | 0 | case 22: |
486 | 0 | set_attribute( bold, value ); |
487 | 0 | break; |
488 | 0 | case 3: |
489 | 0 | case 23: |
490 | 0 | set_attribute( italic, value ); |
491 | 0 | break; |
492 | 0 | case 4: |
493 | 0 | case 24: |
494 | 0 | set_attribute( underlined, value ); |
495 | 0 | break; |
496 | 0 | case 5: |
497 | 0 | case 25: |
498 | 0 | set_attribute( blink, value ); |
499 | 0 | break; |
500 | 0 | case 7: |
501 | 0 | case 27: |
502 | 0 | set_attribute( inverse, value ); |
503 | 0 | break; |
504 | 0 | case 8: |
505 | 0 | case 28: |
506 | 0 | set_attribute( invisible, value ); |
507 | 0 | break; |
508 | 0 | default: |
509 | 0 | break; /* ignore unknown rendition */ |
510 | 0 | } |
511 | 0 | } |
512 | | |
513 | | void Renditions::set_foreground_color( int num ) |
514 | 0 | { |
515 | 0 | if ( ( 0 <= num ) && ( num <= 255 ) ) { |
516 | 0 | foreground_color = 30 + num; |
517 | 0 | } else if ( is_true_color( num ) ) { |
518 | 0 | foreground_color = num; |
519 | 0 | } |
520 | 0 | } |
521 | | |
522 | | void Renditions::set_background_color( int num ) |
523 | 0 | { |
524 | 0 | if ( ( 0 <= num ) && ( num <= 255 ) ) { |
525 | 0 | background_color = 40 + num; |
526 | 0 | } else if ( is_true_color( num ) ) { |
527 | 0 | background_color = num; |
528 | 0 | } |
529 | 0 | } |
530 | | |
531 | | std::string Renditions::sgr( void ) const |
532 | 0 | { |
533 | 0 | std::string ret; |
534 | 0 | char col[64]; |
535 | |
|
536 | 0 | ret.append( "\033[0" ); |
537 | 0 | if ( get_attribute( bold ) ) |
538 | 0 | ret.append( ";1" ); |
539 | 0 | if ( get_attribute( italic ) ) |
540 | 0 | ret.append( ";3" ); |
541 | 0 | if ( get_attribute( underlined ) ) |
542 | 0 | ret.append( ";4" ); |
543 | 0 | if ( get_attribute( blink ) ) |
544 | 0 | ret.append( ";5" ); |
545 | 0 | if ( get_attribute( inverse ) ) |
546 | 0 | ret.append( ";7" ); |
547 | 0 | if ( get_attribute( invisible ) ) |
548 | 0 | ret.append( ";8" ); |
549 | |
|
550 | 0 | if ( foreground_color ) { |
551 | | // Since foreground_color is a 25-bit field, it is promoted to an int when |
552 | | // manipulated. (See [conv.prom] in various C++ standards, e.g., |
553 | | // https://timsong-cpp.github.io/cppwp/n4659/conv.prom#5.) The correct |
554 | | // printf format specifier is thus %d. |
555 | 0 | if ( is_true_color( foreground_color ) ) { |
556 | 0 | snprintf( col, |
557 | 0 | sizeof( col ), |
558 | 0 | ";38;2;%d;%d;%d", |
559 | 0 | ( foreground_color >> 16 ) & 0xff, |
560 | 0 | ( foreground_color >> 8 ) & 0xff, |
561 | 0 | foreground_color & 0xff ); |
562 | 0 | } else if ( foreground_color > 37 ) { /* use 256-color set */ |
563 | 0 | snprintf( col, sizeof( col ), ";38;5;%d", foreground_color - 30 ); |
564 | 0 | } else { /* ANSI foreground color */ |
565 | | // Unfortunately, some versions of GCC (notably including GCC 9.3) give |
566 | | // -Wformat warnings when relying on [conv.prom] to promote |
567 | | // foreground_color in calls to printf. Explicitly promote it to silence |
568 | | // the warning. |
569 | 0 | int fg = foreground_color; |
570 | 0 | snprintf( col, sizeof( col ), ";%d", fg ); |
571 | 0 | } |
572 | 0 | ret.append( col ); |
573 | 0 | } |
574 | 0 | if ( background_color ) { |
575 | | // See comment above about bit-field promotion; it applies here as well. |
576 | 0 | if ( is_true_color( background_color ) ) { |
577 | 0 | snprintf( col, |
578 | 0 | sizeof( col ), |
579 | 0 | ";48;2;%d;%d;%d", |
580 | 0 | ( background_color >> 16 ) & 0xff, |
581 | 0 | ( background_color >> 8 ) & 0xff, |
582 | 0 | background_color & 0xff ); |
583 | 0 | } else if ( background_color > 47 ) { /* use 256-color set */ |
584 | 0 | snprintf( col, sizeof( col ), ";48;5;%d", background_color - 40 ); |
585 | 0 | } else { /* ANSI background color */ |
586 | | // See comment above about explicit promotion; it applies here as well. |
587 | 0 | int bg = background_color; |
588 | 0 | snprintf( col, sizeof( col ), ";%d", bg ); |
589 | 0 | } |
590 | 0 | ret.append( col ); |
591 | 0 | } |
592 | 0 | ret.append( "m" ); |
593 | |
|
594 | 0 | return ret; |
595 | 0 | } |
596 | | |
597 | | void Row::reset( color_type background_color ) |
598 | 0 | { |
599 | 0 | gen = get_gen(); |
600 | 0 | for ( cells_type::iterator i = cells.begin(); i != cells.end(); i++ ) { |
601 | 0 | i->reset( background_color ); |
602 | 0 | } |
603 | 0 | } |
604 | | |
605 | | void Framebuffer::prefix_window_title( const title_type& s ) |
606 | 0 | { |
607 | 0 | if ( icon_name == window_title ) { |
608 | | /* preserve equivalence */ |
609 | 0 | icon_name.insert( icon_name.begin(), s.begin(), s.end() ); |
610 | 0 | } |
611 | 0 | window_title.insert( window_title.begin(), s.begin(), s.end() ); |
612 | 0 | } |
613 | | |
614 | | std::string Cell::debug_contents( void ) const |
615 | 0 | { |
616 | 0 | if ( contents.empty() ) { |
617 | 0 | return "'_' ()"; |
618 | 0 | } |
619 | 0 | std::string chars( 1, '\'' ); |
620 | 0 | print_grapheme( chars ); |
621 | 0 | chars.append( "' [" ); |
622 | 0 | const char* lazycomma = ""; |
623 | 0 | char buf[64]; |
624 | 0 | for ( content_type::const_iterator i = contents.begin(); i < contents.end(); i++ ) { |
625 | |
|
626 | 0 | snprintf( buf, sizeof buf, "%s0x%02x", lazycomma, static_cast<uint8_t>( *i ) ); |
627 | 0 | chars.append( buf ); |
628 | 0 | lazycomma = ", "; |
629 | 0 | } |
630 | 0 | chars.append( "]" ); |
631 | 0 | return chars; |
632 | 0 | } |
633 | | |
634 | | bool Cell::compare( const Cell& other ) const |
635 | 0 | { |
636 | 0 | bool ret = false; |
637 | |
|
638 | 0 | std::string grapheme, other_grapheme; |
639 | |
|
640 | 0 | print_grapheme( grapheme ); |
641 | 0 | other.print_grapheme( other_grapheme ); |
642 | |
|
643 | 0 | if ( grapheme != other_grapheme ) { |
644 | 0 | ret = true; |
645 | 0 | fprintf( stderr, "Graphemes: '%s' vs. '%s'\n", grapheme.c_str(), other_grapheme.c_str() ); |
646 | 0 | } |
647 | |
|
648 | 0 | if ( !contents_match( other ) ) { |
649 | | // ret = true; |
650 | 0 | fprintf( stderr, |
651 | 0 | "Contents: %s (%ld) vs. %s (%ld)\n", |
652 | 0 | debug_contents().c_str(), |
653 | 0 | static_cast<long int>( contents.size() ), |
654 | 0 | other.debug_contents().c_str(), |
655 | 0 | static_cast<long int>( other.contents.size() ) ); |
656 | 0 | } |
657 | |
|
658 | 0 | if ( fallback != other.fallback ) { |
659 | | // ret = true; |
660 | | // Since fallback is a 1-bit field, it is promoted to an int when |
661 | | // manipulated. (See [conv.prom] in various C++ standards, e.g., |
662 | | // https://timsong-cpp.github.io/cppwp/n4659/conv.prom#5.) The correct |
663 | | // printf format specifier is thus %d. |
664 | 0 | fprintf( stderr, "fallback: %d vs. %d\n", fallback, other.fallback ); |
665 | 0 | } |
666 | |
|
667 | 0 | if ( wide != other.wide ) { |
668 | 0 | ret = true; |
669 | | // See comment above about bit-field promotion; it applies here as well. |
670 | 0 | fprintf( stderr, "width: %d vs. %d\n", wide, other.wide ); |
671 | 0 | } |
672 | |
|
673 | 0 | if ( !( renditions == other.renditions ) ) { |
674 | 0 | ret = true; |
675 | 0 | fprintf( stderr, "renditions differ\n" ); |
676 | 0 | } |
677 | |
|
678 | 0 | if ( wrap != other.wrap ) { |
679 | 0 | ret = true; |
680 | | // See comment above about bit-field promotion; it applies here as well. |
681 | 0 | fprintf( stderr, "wrap: %d vs. %d\n", wrap, other.wrap ); |
682 | 0 | } |
683 | |
|
684 | 0 | return ret; |
685 | 0 | } |