Mercurial > vim
comparison src/libvterm/src/screen.c @ 18267:da6a7491e148 v8.1.2128
patch 8.1.2128: renamed libvterm sources makes merging difficult
Commit: https://github.com/vim/vim/commit/93268054428fe3a6bbe3f89d2def2fec4eabcf5f
Author: Bram Moolenaar <Bram@vim.org>
Date: Thu Oct 10 13:22:54 2019 +0200
patch 8.1.2128: renamed libvterm sources makes merging difficult
Problem: Renamed libvterm sources makes merging difficult.
Solution: Rename back to the original name and only rename the .o files.
Also clean the libvterm build artifacts. (James McCoy,
closes #5027)
author | Bram Moolenaar <Bram@vim.org> |
---|---|
date | Thu, 10 Oct 2019 13:30:04 +0200 |
parents | src/libvterm/src/termscreen.c@9c3347b21b89 |
children | 3be01cf0a632 |
comparison
equal
deleted
inserted
replaced
18266:4af19863e264 | 18267:da6a7491e148 |
---|---|
1 #include "vterm_internal.h" | |
2 | |
3 // vim: set sw=2 : | |
4 #include <stdio.h> | |
5 #include <string.h> | |
6 | |
7 #include "rect.h" | |
8 #include "utf8.h" | |
9 | |
10 #define UNICODE_SPACE 0x20 | |
11 #define UNICODE_LINEFEED 0x0a | |
12 | |
13 // State of the pen at some moment in time, also used in a cell | |
14 typedef struct | |
15 { | |
16 // After the bitfield | |
17 VTermColor fg, bg; | |
18 | |
19 unsigned int bold : 1; | |
20 unsigned int underline : 2; | |
21 unsigned int italic : 1; | |
22 unsigned int blink : 1; | |
23 unsigned int reverse : 1; | |
24 unsigned int strike : 1; | |
25 unsigned int font : 4; // 0 to 9 | |
26 | |
27 // Extra state storage that isn't strictly pen-related | |
28 unsigned int protected_cell : 1; | |
29 unsigned int dwl : 1; // on a DECDWL or DECDHL line | |
30 unsigned int dhl : 2; // on a DECDHL line (1=top 2=bottom) | |
31 } ScreenPen; | |
32 | |
33 // Internal representation of a screen cell | |
34 typedef struct | |
35 { | |
36 uint32_t chars[VTERM_MAX_CHARS_PER_CELL]; | |
37 ScreenPen pen; | |
38 } ScreenCell; | |
39 | |
40 static int vterm_screen_set_cell(VTermScreen *screen, VTermPos pos, const VTermScreenCell *cell); | |
41 | |
42 struct VTermScreen | |
43 { | |
44 VTerm *vt; | |
45 VTermState *state; | |
46 | |
47 const VTermScreenCallbacks *callbacks; | |
48 void *cbdata; | |
49 | |
50 VTermDamageSize damage_merge; | |
51 // start_row == -1 => no damage | |
52 VTermRect damaged; | |
53 VTermRect pending_scrollrect; | |
54 int pending_scroll_downward, pending_scroll_rightward; | |
55 | |
56 int rows; | |
57 int cols; | |
58 int global_reverse; | |
59 | |
60 // Primary and Altscreen. buffers[1] is lazily allocated as needed | |
61 ScreenCell *buffers[2]; | |
62 | |
63 // buffer will == buffers[0] or buffers[1], depending on altscreen | |
64 ScreenCell *buffer; | |
65 | |
66 // buffer for a single screen row used in scrollback storage callbacks | |
67 VTermScreenCell *sb_buffer; | |
68 | |
69 ScreenPen pen; | |
70 }; | |
71 | |
72 static ScreenCell *getcell(const VTermScreen *screen, int row, int col) | |
73 { | |
74 if(row < 0 || row >= screen->rows) | |
75 return NULL; | |
76 if(col < 0 || col >= screen->cols) | |
77 return NULL; | |
78 if (screen->buffer == NULL) | |
79 return NULL; | |
80 return screen->buffer + (screen->cols * row) + col; | |
81 } | |
82 | |
83 static ScreenCell *realloc_buffer(VTermScreen *screen, ScreenCell *buffer, int new_rows, int new_cols) | |
84 { | |
85 ScreenCell *new_buffer = vterm_allocator_malloc(screen->vt, sizeof(ScreenCell) * new_rows * new_cols); | |
86 int row, col; | |
87 | |
88 if (new_buffer == NULL) | |
89 return NULL; | |
90 for(row = 0; row < new_rows; row++) { | |
91 for(col = 0; col < new_cols; col++) { | |
92 ScreenCell *new_cell = new_buffer + row*new_cols + col; | |
93 | |
94 if(buffer && row < screen->rows && col < screen->cols) | |
95 *new_cell = buffer[row * screen->cols + col]; | |
96 else { | |
97 new_cell->chars[0] = 0; | |
98 new_cell->pen = screen->pen; | |
99 } | |
100 } | |
101 } | |
102 | |
103 vterm_allocator_free(screen->vt, buffer); | |
104 | |
105 return new_buffer; | |
106 } | |
107 | |
108 static void damagerect(VTermScreen *screen, VTermRect rect) | |
109 { | |
110 VTermRect emit; | |
111 | |
112 switch(screen->damage_merge) { | |
113 case VTERM_DAMAGE_CELL: | |
114 // Always emit damage event | |
115 emit = rect; | |
116 break; | |
117 | |
118 case VTERM_DAMAGE_ROW: | |
119 // Emit damage longer than one row. Try to merge with existing damage in | |
120 // the same row | |
121 if(rect.end_row > rect.start_row + 1) { | |
122 // Bigger than 1 line - flush existing, emit this | |
123 vterm_screen_flush_damage(screen); | |
124 emit = rect; | |
125 } | |
126 else if(screen->damaged.start_row == -1) { | |
127 // None stored yet | |
128 screen->damaged = rect; | |
129 return; | |
130 } | |
131 else if(rect.start_row == screen->damaged.start_row) { | |
132 // Merge with the stored line | |
133 if(screen->damaged.start_col > rect.start_col) | |
134 screen->damaged.start_col = rect.start_col; | |
135 if(screen->damaged.end_col < rect.end_col) | |
136 screen->damaged.end_col = rect.end_col; | |
137 return; | |
138 } | |
139 else { | |
140 // Emit the currently stored line, store a new one | |
141 emit = screen->damaged; | |
142 screen->damaged = rect; | |
143 } | |
144 break; | |
145 | |
146 case VTERM_DAMAGE_SCREEN: | |
147 case VTERM_DAMAGE_SCROLL: | |
148 // Never emit damage event | |
149 if(screen->damaged.start_row == -1) | |
150 screen->damaged = rect; | |
151 else { | |
152 rect_expand(&screen->damaged, &rect); | |
153 } | |
154 return; | |
155 | |
156 default: | |
157 DEBUG_LOG1("TODO: Maybe merge damage for level %d\n", screen->damage_merge); | |
158 return; | |
159 } | |
160 | |
161 if(screen->callbacks && screen->callbacks->damage) | |
162 (*screen->callbacks->damage)(emit, screen->cbdata); | |
163 } | |
164 | |
165 static void damagescreen(VTermScreen *screen) | |
166 { | |
167 VTermRect rect = {0,0,0,0}; | |
168 rect.end_row = screen->rows; | |
169 rect.end_col = screen->cols; | |
170 | |
171 damagerect(screen, rect); | |
172 } | |
173 | |
174 static int putglyph(VTermGlyphInfo *info, VTermPos pos, void *user) | |
175 { | |
176 int i; | |
177 int col; | |
178 VTermRect rect; | |
179 | |
180 VTermScreen *screen = user; | |
181 ScreenCell *cell = getcell(screen, pos.row, pos.col); | |
182 | |
183 if(!cell) | |
184 return 0; | |
185 | |
186 for(i = 0; i < VTERM_MAX_CHARS_PER_CELL && info->chars[i]; i++) { | |
187 cell->chars[i] = info->chars[i]; | |
188 cell->pen = screen->pen; | |
189 } | |
190 if(i < VTERM_MAX_CHARS_PER_CELL) | |
191 cell->chars[i] = 0; | |
192 | |
193 for(col = 1; col < info->width; col++) | |
194 getcell(screen, pos.row, pos.col + col)->chars[0] = (uint32_t)-1; | |
195 | |
196 rect.start_row = pos.row; | |
197 rect.end_row = pos.row+1; | |
198 rect.start_col = pos.col; | |
199 rect.end_col = pos.col+info->width; | |
200 | |
201 cell->pen.protected_cell = info->protected_cell; | |
202 cell->pen.dwl = info->dwl; | |
203 cell->pen.dhl = info->dhl; | |
204 | |
205 damagerect(screen, rect); | |
206 | |
207 return 1; | |
208 } | |
209 | |
210 static int moverect_internal(VTermRect dest, VTermRect src, void *user) | |
211 { | |
212 VTermScreen *screen = user; | |
213 | |
214 if(screen->callbacks && screen->callbacks->sb_pushline && | |
215 dest.start_row == 0 && dest.start_col == 0 && // starts top-left corner | |
216 dest.end_col == screen->cols && // full width | |
217 screen->buffer == screen->buffers[0]) { // not altscreen | |
218 VTermPos pos; | |
219 for(pos.row = 0; pos.row < src.start_row; pos.row++) { | |
220 for(pos.col = 0; pos.col < screen->cols; pos.col++) | |
221 (void)vterm_screen_get_cell(screen, pos, screen->sb_buffer + pos.col); | |
222 | |
223 (screen->callbacks->sb_pushline)(screen->cols, screen->sb_buffer, screen->cbdata); | |
224 } | |
225 } | |
226 | |
227 { | |
228 int cols = src.end_col - src.start_col; | |
229 int downward = src.start_row - dest.start_row; | |
230 int init_row, test_row, inc_row; | |
231 int row; | |
232 | |
233 if(downward < 0) { | |
234 init_row = dest.end_row - 1; | |
235 test_row = dest.start_row - 1; | |
236 inc_row = -1; | |
237 } | |
238 else { | |
239 init_row = dest.start_row; | |
240 test_row = dest.end_row; | |
241 inc_row = +1; | |
242 } | |
243 | |
244 for(row = init_row; row != test_row; row += inc_row) | |
245 memmove(getcell(screen, row, dest.start_col), | |
246 getcell(screen, row + downward, src.start_col), | |
247 cols * sizeof(ScreenCell)); | |
248 } | |
249 | |
250 return 1; | |
251 } | |
252 | |
253 static int moverect_user(VTermRect dest, VTermRect src, void *user) | |
254 { | |
255 VTermScreen *screen = user; | |
256 | |
257 if(screen->callbacks && screen->callbacks->moverect) { | |
258 if(screen->damage_merge != VTERM_DAMAGE_SCROLL) | |
259 // Avoid an infinite loop | |
260 vterm_screen_flush_damage(screen); | |
261 | |
262 if((*screen->callbacks->moverect)(dest, src, screen->cbdata)) | |
263 return 1; | |
264 } | |
265 | |
266 damagerect(screen, dest); | |
267 | |
268 return 1; | |
269 } | |
270 | |
271 static int erase_internal(VTermRect rect, int selective, void *user) | |
272 { | |
273 VTermScreen *screen = user; | |
274 int row, col; | |
275 | |
276 for(row = rect.start_row; row < screen->state->rows && row < rect.end_row; row++) { | |
277 const VTermLineInfo *info = vterm_state_get_lineinfo(screen->state, row); | |
278 | |
279 for(col = rect.start_col; col < rect.end_col; col++) { | |
280 ScreenCell *cell = getcell(screen, row, col); | |
281 | |
282 if(selective && cell->pen.protected_cell) | |
283 continue; | |
284 | |
285 cell->chars[0] = 0; | |
286 cell->pen = screen->pen; | |
287 cell->pen.dwl = info->doublewidth; | |
288 cell->pen.dhl = info->doubleheight; | |
289 } | |
290 } | |
291 | |
292 return 1; | |
293 } | |
294 | |
295 static int erase_user(VTermRect rect, int selective UNUSED, void *user) | |
296 { | |
297 VTermScreen *screen = user; | |
298 | |
299 damagerect(screen, rect); | |
300 | |
301 return 1; | |
302 } | |
303 | |
304 static int erase(VTermRect rect, int selective, void *user) | |
305 { | |
306 erase_internal(rect, selective, user); | |
307 return erase_user(rect, 0, user); | |
308 } | |
309 | |
310 static int scrollrect(VTermRect rect, int downward, int rightward, void *user) | |
311 { | |
312 VTermScreen *screen = user; | |
313 | |
314 if(screen->damage_merge != VTERM_DAMAGE_SCROLL) { | |
315 vterm_scroll_rect(rect, downward, rightward, | |
316 moverect_internal, erase_internal, screen); | |
317 | |
318 vterm_screen_flush_damage(screen); | |
319 | |
320 vterm_scroll_rect(rect, downward, rightward, | |
321 moverect_user, erase_user, screen); | |
322 | |
323 return 1; | |
324 } | |
325 | |
326 if(screen->damaged.start_row != -1 && | |
327 !rect_intersects(&rect, &screen->damaged)) { | |
328 vterm_screen_flush_damage(screen); | |
329 } | |
330 | |
331 if(screen->pending_scrollrect.start_row == -1) { | |
332 screen->pending_scrollrect = rect; | |
333 screen->pending_scroll_downward = downward; | |
334 screen->pending_scroll_rightward = rightward; | |
335 } | |
336 else if(rect_equal(&screen->pending_scrollrect, &rect) && | |
337 ((screen->pending_scroll_downward == 0 && downward == 0) || | |
338 (screen->pending_scroll_rightward == 0 && rightward == 0))) { | |
339 screen->pending_scroll_downward += downward; | |
340 screen->pending_scroll_rightward += rightward; | |
341 } | |
342 else { | |
343 vterm_screen_flush_damage(screen); | |
344 | |
345 screen->pending_scrollrect = rect; | |
346 screen->pending_scroll_downward = downward; | |
347 screen->pending_scroll_rightward = rightward; | |
348 } | |
349 | |
350 vterm_scroll_rect(rect, downward, rightward, | |
351 moverect_internal, erase_internal, screen); | |
352 | |
353 if(screen->damaged.start_row == -1) | |
354 return 1; | |
355 | |
356 if(rect_contains(&rect, &screen->damaged)) { | |
357 // Scroll region entirely contains the damage; just move it | |
358 vterm_rect_move(&screen->damaged, -downward, -rightward); | |
359 rect_clip(&screen->damaged, &rect); | |
360 } | |
361 // There are a number of possible cases here, but lets restrict this to only | |
362 // the common case where we might actually gain some performance by | |
363 // optimising it. Namely, a vertical scroll that neatly cuts the damage | |
364 // region in half. | |
365 else if(rect.start_col <= screen->damaged.start_col && | |
366 rect.end_col >= screen->damaged.end_col && | |
367 rightward == 0) { | |
368 if(screen->damaged.start_row >= rect.start_row && | |
369 screen->damaged.start_row < rect.end_row) { | |
370 screen->damaged.start_row -= downward; | |
371 if(screen->damaged.start_row < rect.start_row) | |
372 screen->damaged.start_row = rect.start_row; | |
373 if(screen->damaged.start_row > rect.end_row) | |
374 screen->damaged.start_row = rect.end_row; | |
375 } | |
376 if(screen->damaged.end_row >= rect.start_row && | |
377 screen->damaged.end_row < rect.end_row) { | |
378 screen->damaged.end_row -= downward; | |
379 if(screen->damaged.end_row < rect.start_row) | |
380 screen->damaged.end_row = rect.start_row; | |
381 if(screen->damaged.end_row > rect.end_row) | |
382 screen->damaged.end_row = rect.end_row; | |
383 } | |
384 } | |
385 else { | |
386 DEBUG_LOG2("TODO: Just flush and redo damaged=" STRFrect " rect=" STRFrect "\n", | |
387 ARGSrect(screen->damaged), ARGSrect(rect)); | |
388 } | |
389 | |
390 return 1; | |
391 } | |
392 | |
393 static int movecursor(VTermPos pos, VTermPos oldpos, int visible, void *user) | |
394 { | |
395 VTermScreen *screen = user; | |
396 | |
397 if(screen->callbacks && screen->callbacks->movecursor) | |
398 return (*screen->callbacks->movecursor)(pos, oldpos, visible, screen->cbdata); | |
399 | |
400 return 0; | |
401 } | |
402 | |
403 static int setpenattr(VTermAttr attr, VTermValue *val, void *user) | |
404 { | |
405 VTermScreen *screen = user; | |
406 | |
407 switch(attr) { | |
408 case VTERM_ATTR_BOLD: | |
409 screen->pen.bold = val->boolean; | |
410 return 1; | |
411 case VTERM_ATTR_UNDERLINE: | |
412 screen->pen.underline = val->number; | |
413 return 1; | |
414 case VTERM_ATTR_ITALIC: | |
415 screen->pen.italic = val->boolean; | |
416 return 1; | |
417 case VTERM_ATTR_BLINK: | |
418 screen->pen.blink = val->boolean; | |
419 return 1; | |
420 case VTERM_ATTR_REVERSE: | |
421 screen->pen.reverse = val->boolean; | |
422 return 1; | |
423 case VTERM_ATTR_STRIKE: | |
424 screen->pen.strike = val->boolean; | |
425 return 1; | |
426 case VTERM_ATTR_FONT: | |
427 screen->pen.font = val->number; | |
428 return 1; | |
429 case VTERM_ATTR_FOREGROUND: | |
430 screen->pen.fg = val->color; | |
431 return 1; | |
432 case VTERM_ATTR_BACKGROUND: | |
433 screen->pen.bg = val->color; | |
434 return 1; | |
435 | |
436 case VTERM_N_ATTRS: | |
437 return 0; | |
438 } | |
439 | |
440 return 0; | |
441 } | |
442 | |
443 static int settermprop(VTermProp prop, VTermValue *val, void *user) | |
444 { | |
445 VTermScreen *screen = user; | |
446 | |
447 switch(prop) { | |
448 case VTERM_PROP_ALTSCREEN: | |
449 if(val->boolean && !screen->buffers[1]) | |
450 return 0; | |
451 | |
452 screen->buffer = val->boolean ? screen->buffers[1] : screen->buffers[0]; | |
453 // only send a damage event on disable; because during enable there's an | |
454 // erase that sends a damage anyway | |
455 if(!val->boolean) | |
456 damagescreen(screen); | |
457 break; | |
458 case VTERM_PROP_REVERSE: | |
459 screen->global_reverse = val->boolean; | |
460 damagescreen(screen); | |
461 break; | |
462 default: | |
463 ; // ignore | |
464 } | |
465 | |
466 if(screen->callbacks && screen->callbacks->settermprop) | |
467 return (*screen->callbacks->settermprop)(prop, val, screen->cbdata); | |
468 | |
469 return 1; | |
470 } | |
471 | |
472 static int bell(void *user) | |
473 { | |
474 VTermScreen *screen = user; | |
475 | |
476 if(screen->callbacks && screen->callbacks->bell) | |
477 return (*screen->callbacks->bell)(screen->cbdata); | |
478 | |
479 return 0; | |
480 } | |
481 | |
482 static int resize(int new_rows, int new_cols, VTermPos *delta, void *user) | |
483 { | |
484 VTermScreen *screen = user; | |
485 | |
486 int is_altscreen = (screen->buffers[1] && screen->buffer == screen->buffers[1]); | |
487 | |
488 int old_rows = screen->rows; | |
489 int old_cols = screen->cols; | |
490 int first_blank_row; | |
491 | |
492 if(!is_altscreen && new_rows < old_rows) { | |
493 // Fewer rows - determine if we're going to scroll at all, and if so, push | |
494 // those lines to scrollback | |
495 VTermPos pos = { 0, 0 }; | |
496 VTermPos cursor = screen->state->pos; | |
497 // Find the first blank row after the cursor. | |
498 for(pos.row = old_rows - 1; pos.row >= new_rows; pos.row--) | |
499 if(!vterm_screen_is_eol(screen, pos) || cursor.row == pos.row) | |
500 break; | |
501 | |
502 first_blank_row = pos.row + 1; | |
503 if(first_blank_row > new_rows) { | |
504 VTermRect rect = {0,0,0,0}; | |
505 rect.end_row = old_rows; | |
506 rect.end_col = old_cols; | |
507 scrollrect(rect, first_blank_row - new_rows, 0, user); | |
508 vterm_screen_flush_damage(screen); | |
509 | |
510 delta->row -= first_blank_row - new_rows; | |
511 } | |
512 } | |
513 | |
514 screen->buffers[0] = realloc_buffer(screen, screen->buffers[0], new_rows, new_cols); | |
515 if(screen->buffers[1]) | |
516 screen->buffers[1] = realloc_buffer(screen, screen->buffers[1], new_rows, new_cols); | |
517 | |
518 screen->buffer = is_altscreen ? screen->buffers[1] : screen->buffers[0]; | |
519 | |
520 screen->rows = new_rows; | |
521 screen->cols = new_cols; | |
522 | |
523 vterm_allocator_free(screen->vt, screen->sb_buffer); | |
524 | |
525 screen->sb_buffer = vterm_allocator_malloc(screen->vt, sizeof(VTermScreenCell) * new_cols); | |
526 | |
527 if(new_cols > old_cols) { | |
528 VTermRect rect; | |
529 rect.start_row = 0; | |
530 rect.end_row = old_rows; | |
531 rect.start_col = old_cols; | |
532 rect.end_col = new_cols; | |
533 damagerect(screen, rect); | |
534 } | |
535 | |
536 if(new_rows > old_rows) { | |
537 if(!is_altscreen && screen->callbacks && screen->callbacks->sb_popline) { | |
538 int rows = new_rows - old_rows; | |
539 while(rows) { | |
540 VTermRect rect = {0,0,0,0}; | |
541 VTermPos pos = { 0, 0 }; | |
542 if(!(screen->callbacks->sb_popline(screen->cols, screen->sb_buffer, screen->cbdata))) | |
543 break; | |
544 | |
545 rect.end_row = screen->rows; | |
546 rect.end_col = screen->cols; | |
547 scrollrect(rect, -1, 0, user); | |
548 | |
549 for(pos.col = 0; pos.col < screen->cols; pos.col += screen->sb_buffer[pos.col].width) | |
550 vterm_screen_set_cell(screen, pos, screen->sb_buffer + pos.col); | |
551 | |
552 rect.end_row = 1; | |
553 damagerect(screen, rect); | |
554 | |
555 vterm_screen_flush_damage(screen); | |
556 | |
557 rows--; | |
558 delta->row++; | |
559 } | |
560 } | |
561 | |
562 { | |
563 VTermRect rect; | |
564 rect.start_row = old_rows; | |
565 rect.end_row = new_rows; | |
566 rect.start_col = 0; | |
567 rect.end_col = new_cols; | |
568 damagerect(screen, rect); | |
569 } | |
570 } | |
571 | |
572 if(screen->callbacks && screen->callbacks->resize) | |
573 return (*screen->callbacks->resize)(new_rows, new_cols, screen->cbdata); | |
574 | |
575 return 1; | |
576 } | |
577 | |
578 static int setlineinfo(int row, const VTermLineInfo *newinfo, const VTermLineInfo *oldinfo, void *user) | |
579 { | |
580 VTermScreen *screen = user; | |
581 int col; | |
582 VTermRect rect; | |
583 | |
584 if(newinfo->doublewidth != oldinfo->doublewidth || | |
585 newinfo->doubleheight != oldinfo->doubleheight) { | |
586 for(col = 0; col < screen->cols; col++) { | |
587 ScreenCell *cell = getcell(screen, row, col); | |
588 cell->pen.dwl = newinfo->doublewidth; | |
589 cell->pen.dhl = newinfo->doubleheight; | |
590 } | |
591 | |
592 rect.start_row = row; | |
593 rect.end_row = row + 1; | |
594 rect.start_col = 0; | |
595 rect.end_col = newinfo->doublewidth ? screen->cols / 2 : screen->cols; | |
596 damagerect(screen, rect); | |
597 | |
598 if(newinfo->doublewidth) { | |
599 rect.start_col = screen->cols / 2; | |
600 rect.end_col = screen->cols; | |
601 | |
602 erase_internal(rect, 0, user); | |
603 } | |
604 } | |
605 | |
606 return 1; | |
607 } | |
608 | |
609 static VTermStateCallbacks state_cbs = { | |
610 &putglyph, // putglyph | |
611 &movecursor, // movecursor | |
612 &scrollrect, // scrollrect | |
613 NULL, // moverect | |
614 &erase, // erase | |
615 NULL, // initpen | |
616 &setpenattr, // setpenattr | |
617 &settermprop, // settermprop | |
618 &bell, // bell | |
619 &resize, // resize | |
620 &setlineinfo // setlineinfo | |
621 }; | |
622 | |
623 /* | |
624 * Allocate a new screen and return it. | |
625 * Return NULL when out of memory. | |
626 */ | |
627 static VTermScreen *screen_new(VTerm *vt) | |
628 { | |
629 VTermState *state = vterm_obtain_state(vt); | |
630 VTermScreen *screen; | |
631 int rows, cols; | |
632 | |
633 if (state == NULL) | |
634 return NULL; | |
635 screen = vterm_allocator_malloc(vt, sizeof(VTermScreen)); | |
636 if (screen == NULL) | |
637 return NULL; | |
638 | |
639 vterm_get_size(vt, &rows, &cols); | |
640 | |
641 screen->vt = vt; | |
642 screen->state = state; | |
643 | |
644 screen->damage_merge = VTERM_DAMAGE_CELL; | |
645 screen->damaged.start_row = -1; | |
646 screen->pending_scrollrect.start_row = -1; | |
647 | |
648 screen->rows = rows; | |
649 screen->cols = cols; | |
650 | |
651 screen->callbacks = NULL; | |
652 screen->cbdata = NULL; | |
653 | |
654 screen->buffers[0] = realloc_buffer(screen, NULL, rows, cols); | |
655 screen->buffer = screen->buffers[0]; | |
656 screen->sb_buffer = vterm_allocator_malloc(screen->vt, sizeof(VTermScreenCell) * cols); | |
657 if (screen->buffer == NULL || screen->sb_buffer == NULL) | |
658 { | |
659 vterm_screen_free(screen); | |
660 return NULL; | |
661 } | |
662 | |
663 vterm_state_set_callbacks(screen->state, &state_cbs, screen); | |
664 | |
665 return screen; | |
666 } | |
667 | |
668 INTERNAL void vterm_screen_free(VTermScreen *screen) | |
669 { | |
670 vterm_allocator_free(screen->vt, screen->buffers[0]); | |
671 vterm_allocator_free(screen->vt, screen->buffers[1]); | |
672 vterm_allocator_free(screen->vt, screen->sb_buffer); | |
673 vterm_allocator_free(screen->vt, screen); | |
674 } | |
675 | |
676 void vterm_screen_reset(VTermScreen *screen, int hard) | |
677 { | |
678 screen->damaged.start_row = -1; | |
679 screen->pending_scrollrect.start_row = -1; | |
680 vterm_state_reset(screen->state, hard); | |
681 vterm_screen_flush_damage(screen); | |
682 } | |
683 | |
684 static size_t _get_chars(const VTermScreen *screen, const int utf8, void *buffer, size_t len, const VTermRect rect) | |
685 { | |
686 size_t outpos = 0; | |
687 int padding = 0; | |
688 int row, col; | |
689 | |
690 #define PUT(c) \ | |
691 if(utf8) { \ | |
692 size_t thislen = utf8_seqlen(c); \ | |
693 if(buffer && outpos + thislen <= len) \ | |
694 outpos += fill_utf8((c), (char *)buffer + outpos); \ | |
695 else \ | |
696 outpos += thislen; \ | |
697 } \ | |
698 else { \ | |
699 if(buffer && outpos + 1 <= len) \ | |
700 ((uint32_t*)buffer)[outpos++] = (c); \ | |
701 else \ | |
702 outpos++; \ | |
703 } | |
704 | |
705 for(row = rect.start_row; row < rect.end_row; row++) { | |
706 for(col = rect.start_col; col < rect.end_col; col++) { | |
707 ScreenCell *cell = getcell(screen, row, col); | |
708 int i; | |
709 | |
710 if(cell->chars[0] == 0) | |
711 // Erased cell, might need a space | |
712 padding++; | |
713 else if(cell->chars[0] == (uint32_t)-1) | |
714 // Gap behind a double-width char, do nothing | |
715 ; | |
716 else { | |
717 while(padding) { | |
718 PUT(UNICODE_SPACE); | |
719 padding--; | |
720 } | |
721 for(i = 0; i < VTERM_MAX_CHARS_PER_CELL && cell->chars[i]; i++) { | |
722 PUT(cell->chars[i]); | |
723 } | |
724 } | |
725 } | |
726 | |
727 if(row < rect.end_row - 1) { | |
728 PUT(UNICODE_LINEFEED); | |
729 padding = 0; | |
730 } | |
731 } | |
732 | |
733 return outpos; | |
734 } | |
735 | |
736 size_t vterm_screen_get_chars(const VTermScreen *screen, uint32_t *chars, size_t len, const VTermRect rect) | |
737 { | |
738 return _get_chars(screen, 0, chars, len, rect); | |
739 } | |
740 | |
741 size_t vterm_screen_get_text(const VTermScreen *screen, char *str, size_t len, const VTermRect rect) | |
742 { | |
743 return _get_chars(screen, 1, str, len, rect); | |
744 } | |
745 | |
746 // Copy internal to external representation of a screen cell | |
747 int vterm_screen_get_cell(const VTermScreen *screen, VTermPos pos, VTermScreenCell *cell) | |
748 { | |
749 ScreenCell *intcell = getcell(screen, pos.row, pos.col); | |
750 int i; | |
751 | |
752 if(!intcell) | |
753 return 0; | |
754 | |
755 for(i = 0; ; i++) { | |
756 cell->chars[i] = intcell->chars[i]; | |
757 if(!intcell->chars[i]) | |
758 break; | |
759 } | |
760 | |
761 cell->attrs.bold = intcell->pen.bold; | |
762 cell->attrs.underline = intcell->pen.underline; | |
763 cell->attrs.italic = intcell->pen.italic; | |
764 cell->attrs.blink = intcell->pen.blink; | |
765 cell->attrs.reverse = intcell->pen.reverse ^ screen->global_reverse; | |
766 cell->attrs.strike = intcell->pen.strike; | |
767 cell->attrs.font = intcell->pen.font; | |
768 | |
769 cell->attrs.dwl = intcell->pen.dwl; | |
770 cell->attrs.dhl = intcell->pen.dhl; | |
771 | |
772 cell->fg = intcell->pen.fg; | |
773 cell->bg = intcell->pen.bg; | |
774 | |
775 if(vterm_get_special_pty_type() == 2) { | |
776 /* Get correct cell width from cell information contained in line buffer */ | |
777 if(pos.col < (screen->cols - 1) && | |
778 getcell(screen, pos.row, pos.col + 1)->chars[0] == (uint32_t)-1) { | |
779 if(getcell(screen, pos.row, pos.col)->chars[0] == 0x20) { | |
780 getcell(screen, pos.row, pos.col)->chars[0] = 0; | |
781 cell->width = 2; | |
782 } else if(getcell(screen, pos.row, pos.col)->chars[0] == 0) { | |
783 getcell(screen, pos.row, pos.col + 1)->chars[0] = 0; | |
784 cell->width = 1; | |
785 } else { | |
786 cell->width = 2; | |
787 } | |
788 } else | |
789 cell->width = 1; | |
790 } else { | |
791 if(pos.col < (screen->cols - 1) && | |
792 getcell(screen, pos.row, pos.col + 1)->chars[0] == (uint32_t)-1) | |
793 cell->width = 2; | |
794 else | |
795 cell->width = 1; | |
796 } | |
797 | |
798 return 1; | |
799 } | |
800 | |
801 // Copy external to internal representation of a screen cell | |
802 /* static because it's only used internally for sb_popline during resize */ | |
803 static int vterm_screen_set_cell(VTermScreen *screen, VTermPos pos, const VTermScreenCell *cell) | |
804 { | |
805 ScreenCell *intcell = getcell(screen, pos.row, pos.col); | |
806 int i; | |
807 | |
808 if(!intcell) | |
809 return 0; | |
810 | |
811 for(i = 0; ; i++) { | |
812 intcell->chars[i] = cell->chars[i]; | |
813 if(!cell->chars[i]) | |
814 break; | |
815 } | |
816 | |
817 intcell->pen.bold = cell->attrs.bold; | |
818 intcell->pen.underline = cell->attrs.underline; | |
819 intcell->pen.italic = cell->attrs.italic; | |
820 intcell->pen.blink = cell->attrs.blink; | |
821 intcell->pen.reverse = cell->attrs.reverse ^ screen->global_reverse; | |
822 intcell->pen.strike = cell->attrs.strike; | |
823 intcell->pen.font = cell->attrs.font; | |
824 | |
825 intcell->pen.fg = cell->fg; | |
826 intcell->pen.bg = cell->bg; | |
827 | |
828 if(cell->width == 2) | |
829 getcell(screen, pos.row, pos.col + 1)->chars[0] = (uint32_t)-1; | |
830 | |
831 return 1; | |
832 } | |
833 | |
834 int vterm_screen_is_eol(const VTermScreen *screen, VTermPos pos) | |
835 { | |
836 // This cell is EOL if this and every cell to the right is black | |
837 for(; pos.col < screen->cols; pos.col++) { | |
838 ScreenCell *cell = getcell(screen, pos.row, pos.col); | |
839 if(cell->chars[0] != 0) | |
840 return 0; | |
841 } | |
842 | |
843 return 1; | |
844 } | |
845 | |
846 VTermScreen *vterm_obtain_screen(VTerm *vt) | |
847 { | |
848 if(!vt->screen) | |
849 vt->screen = screen_new(vt); | |
850 return vt->screen; | |
851 } | |
852 | |
853 void vterm_screen_enable_altscreen(VTermScreen *screen, int altscreen) | |
854 { | |
855 | |
856 if(!screen->buffers[1] && altscreen) { | |
857 int rows, cols; | |
858 vterm_get_size(screen->vt, &rows, &cols); | |
859 | |
860 screen->buffers[1] = realloc_buffer(screen, NULL, rows, cols); | |
861 } | |
862 } | |
863 | |
864 void vterm_screen_set_callbacks(VTermScreen *screen, const VTermScreenCallbacks *callbacks, void *user) | |
865 { | |
866 screen->callbacks = callbacks; | |
867 screen->cbdata = user; | |
868 } | |
869 | |
870 void *vterm_screen_get_cbdata(VTermScreen *screen) | |
871 { | |
872 return screen->cbdata; | |
873 } | |
874 | |
875 void vterm_screen_set_unrecognised_fallbacks(VTermScreen *screen, const VTermParserCallbacks *fallbacks, void *user) | |
876 { | |
877 vterm_state_set_unrecognised_fallbacks(screen->state, fallbacks, user); | |
878 } | |
879 | |
880 void *vterm_screen_get_unrecognised_fbdata(VTermScreen *screen) | |
881 { | |
882 return vterm_state_get_unrecognised_fbdata(screen->state); | |
883 } | |
884 | |
885 void vterm_screen_flush_damage(VTermScreen *screen) | |
886 { | |
887 if(screen->pending_scrollrect.start_row != -1) { | |
888 vterm_scroll_rect(screen->pending_scrollrect, screen->pending_scroll_downward, screen->pending_scroll_rightward, | |
889 moverect_user, erase_user, screen); | |
890 | |
891 screen->pending_scrollrect.start_row = -1; | |
892 } | |
893 | |
894 if(screen->damaged.start_row != -1) { | |
895 if(screen->callbacks && screen->callbacks->damage) | |
896 (*screen->callbacks->damage)(screen->damaged, screen->cbdata); | |
897 | |
898 screen->damaged.start_row = -1; | |
899 } | |
900 } | |
901 | |
902 void vterm_screen_set_damage_merge(VTermScreen *screen, VTermDamageSize size) | |
903 { | |
904 vterm_screen_flush_damage(screen); | |
905 screen->damage_merge = size; | |
906 } | |
907 | |
908 static int attrs_differ(VTermAttrMask attrs, ScreenCell *a, ScreenCell *b) | |
909 { | |
910 if((attrs & VTERM_ATTR_BOLD_MASK) && (a->pen.bold != b->pen.bold)) | |
911 return 1; | |
912 if((attrs & VTERM_ATTR_UNDERLINE_MASK) && (a->pen.underline != b->pen.underline)) | |
913 return 1; | |
914 if((attrs & VTERM_ATTR_ITALIC_MASK) && (a->pen.italic != b->pen.italic)) | |
915 return 1; | |
916 if((attrs & VTERM_ATTR_BLINK_MASK) && (a->pen.blink != b->pen.blink)) | |
917 return 1; | |
918 if((attrs & VTERM_ATTR_REVERSE_MASK) && (a->pen.reverse != b->pen.reverse)) | |
919 return 1; | |
920 if((attrs & VTERM_ATTR_STRIKE_MASK) && (a->pen.strike != b->pen.strike)) | |
921 return 1; | |
922 if((attrs & VTERM_ATTR_FONT_MASK) && (a->pen.font != b->pen.font)) | |
923 return 1; | |
924 if((attrs & VTERM_ATTR_FOREGROUND_MASK) && !vterm_color_equal(a->pen.fg, b->pen.fg)) | |
925 return 1; | |
926 if((attrs & VTERM_ATTR_BACKGROUND_MASK) && !vterm_color_equal(a->pen.bg, b->pen.bg)) | |
927 return 1; | |
928 | |
929 return 0; | |
930 } | |
931 | |
932 int vterm_screen_get_attrs_extent(const VTermScreen *screen, VTermRect *extent, VTermPos pos, VTermAttrMask attrs) | |
933 { | |
934 int col; | |
935 | |
936 ScreenCell *target = getcell(screen, pos.row, pos.col); | |
937 | |
938 // TODO: bounds check | |
939 extent->start_row = pos.row; | |
940 extent->end_row = pos.row + 1; | |
941 | |
942 if(extent->start_col < 0) | |
943 extent->start_col = 0; | |
944 if(extent->end_col < 0) | |
945 extent->end_col = screen->cols; | |
946 | |
947 for(col = pos.col - 1; col >= extent->start_col; col--) | |
948 if(attrs_differ(attrs, target, getcell(screen, pos.row, col))) | |
949 break; | |
950 extent->start_col = col + 1; | |
951 | |
952 for(col = pos.col + 1; col < extent->end_col; col++) | |
953 if(attrs_differ(attrs, target, getcell(screen, pos.row, col))) | |
954 break; | |
955 extent->end_col = col - 1; | |
956 | |
957 return 1; | |
958 } |