LF OS
Hobby operating system for amd64 with high ambitions
Loading...
Searching...
No Matches
screen.c
Go to the documentation of this file.
1#include "vterm_internal.h"
2
3#include <stdio.h>
4#include <string.h>
5
6#include "rect.h"
7#include "utf8.h"
8
9#define UNICODE_SPACE 0x20
10#define UNICODE_LINEFEED 0x0a
11
12#undef DEBUG_REFLOW
13
14/* State of the pen at some moment in time, also used in a cell */
15typedef struct
16{
17 /* After the bitfield */
19
20 unsigned int bold : 1;
21 unsigned int underline : 2;
22 unsigned int italic : 1;
23 unsigned int blink : 1;
24 unsigned int reverse : 1;
25 unsigned int conceal : 1;
26 unsigned int strike : 1;
27 unsigned int font : 4; /* 0 to 9 */
28 unsigned int small : 1;
29 unsigned int baseline : 2;
30
31 /* Extra state storage that isn't strictly pen-related */
32 unsigned int protected_cell : 1;
33 unsigned int dwl : 1; /* on a DECDWL or DECDHL line */
34 unsigned int dhl : 2; /* on a DECDHL line (1=top 2=bottom) */
35} ScreenPen;
36
37/* Internal representation of a screen cell */
43
45{
48
50 void *cbdata;
51
53 /* start_row == -1 => no damage */
57
58 int rows;
59 int cols;
60
61 unsigned int global_reverse : 1;
62 unsigned int reflow : 1;
63
64 /* Primary and Altscreen. buffers[1] is lazily allocated as needed */
66
67 /* buffer will == buffers[0] or buffers[1], depending on altscreen */
69
70 /* buffer for a single screen row used in scrollback storage callbacks */
72
74};
75
76static inline void clearcell(const VTermScreen *screen, ScreenCell *cell)
77{
78 cell->chars[0] = 0;
79 cell->pen = screen->pen;
80}
81
82static inline ScreenCell *getcell(const VTermScreen *screen, int row, int col)
83{
84 if(row < 0 || row >= screen->rows)
85 return NULL;
86 if(col < 0 || col >= screen->cols)
87 return NULL;
88 return screen->buffer + (screen->cols * row) + col;
89}
90
92{
93 ScreenCell *new_buffer = vterm_allocator_malloc(screen->vt, sizeof(ScreenCell) * rows * cols);
94
95 for(int row = 0; row < rows; row++) {
96 for(int col = 0; col < cols; col++) {
97 clearcell(screen, &new_buffer[row * cols + col]);
98 }
99 }
100
101 return new_buffer;
102}
103
105{
106 VTermRect emit;
107
108 switch(screen->damage_merge) {
110 /* Always emit damage event */
111 emit = rect;
112 break;
113
114 case VTERM_DAMAGE_ROW:
115 /* Emit damage longer than one row. Try to merge with existing damage in
116 * the same row */
117 if(rect.end_row > rect.start_row + 1) {
118 // Bigger than 1 line - flush existing, emit this
120 emit = rect;
121 }
122 else if(screen->damaged.start_row == -1) {
123 // None stored yet
124 screen->damaged = rect;
125 return;
126 }
127 else if(rect.start_row == screen->damaged.start_row) {
128 // Merge with the stored line
131 if(screen->damaged.end_col < rect.end_col)
133 return;
134 }
135 else {
136 // Emit the currently stored line, store a new one
137 emit = screen->damaged;
138 screen->damaged = rect;
139 }
140 break;
141
144 /* Never emit damage event */
145 if(screen->damaged.start_row == -1)
146 screen->damaged = rect;
147 else {
148 rect_expand(&screen->damaged, &rect);
149 }
150 return;
151
152 default:
153 DEBUG_LOG("TODO: Maybe merge damage for level %d\n", screen->damage_merge);
154 return;
155 }
156
158 (*screen->callbacks->damage)(emit, screen->cbdata);
159}
160
162{
163 VTermRect rect = {
164 .start_row = 0,
165 .end_row = screen->rows,
166 .start_col = 0,
167 .end_col = screen->cols,
168 };
169
170 damagerect(screen, rect);
171}
172
173static int putglyph(VTermGlyphInfo *info, VTermPos pos, void *user)
174{
175 VTermScreen *screen = user;
176 ScreenCell *cell = getcell(screen, pos.row, pos.col);
177
178 if(!cell)
179 return 0;
180
181 int i;
182 for(i = 0; i < VTERM_MAX_CHARS_PER_CELL && info->chars[i]; i++) {
183 cell->chars[i] = info->chars[i];
184 cell->pen = screen->pen;
185 }
187 cell->chars[i] = 0;
188
189 for(int col = 1; col < info->width; col++)
190 getcell(screen, pos.row, pos.col + col)->chars[0] = (uint32_t)-1;
191
192 VTermRect rect = {
193 .start_row = pos.row,
194 .end_row = pos.row+1,
195 .start_col = pos.col,
196 .end_col = pos.col+info->width,
197 };
198
199 cell->pen.protected_cell = info->protected_cell;
200 cell->pen.dwl = info->dwl;
201 cell->pen.dhl = info->dhl;
202
203 damagerect(screen, rect);
204
205 return 1;
206}
207
209{
210 VTermPos pos = { .row = row };
211 for(pos.col = 0; pos.col < screen->cols; pos.col++)
213
215}
216
217static int moverect_internal(VTermRect dest, VTermRect src, void *user)
218{
219 VTermScreen *screen = user;
220
222 dest.start_row == 0 && dest.start_col == 0 && // starts top-left corner
223 dest.end_col == screen->cols && // full width
224 screen->buffer == screen->buffers[BUFIDX_PRIMARY]) { // not altscreen
225 for(int row = 0; row < src.start_row; row++)
227 }
228
229 int cols = src.end_col - src.start_col;
230 int downward = src.start_row - dest.start_row;
231
232 int init_row, test_row, inc_row;
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(int 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 return 1;
250}
251
252static int moverect_user(VTermRect dest, VTermRect src, void *user)
253{
254 VTermScreen *screen = user;
255
258 // Avoid an infinite loop
260
261 if((*screen->callbacks->moverect)(dest, src, screen->cbdata))
262 return 1;
263 }
264
265 damagerect(screen, dest);
266
267 return 1;
268}
269
270static int erase_internal(VTermRect rect, int selective, void *user)
271{
272 VTermScreen *screen = user;
273
274 for(int row = rect.start_row; row < screen->state->rows && row < rect.end_row; row++) {
276
277 for(int col = rect.start_col; col < rect.end_col; col++) {
278 ScreenCell *cell = getcell(screen, row, col);
279
280 if(selective && cell->pen.protected_cell)
281 continue;
282
283 cell->chars[0] = 0;
284 cell->pen = (ScreenPen){
285 /* Only copy .fg and .bg; leave things like rv in reset state */
286 .fg = screen->pen.fg,
287 .bg = screen->pen.bg,
288 };
289 cell->pen.dwl = info->doublewidth;
290 cell->pen.dhl = info->doubleheight;
291 }
292 }
293
294 return 1;
295}
296
297static int erase_user(VTermRect rect, int selective, void *user)
298{
299 VTermScreen *screen = user;
300
301 damagerect(screen, rect);
302
303 return 1;
304}
305
306static int erase(VTermRect rect, int selective, void *user)
307{
308 erase_internal(rect, selective, user);
309 return erase_user(rect, 0, user);
310}
311
312static int scrollrect(VTermRect rect, int downward, int rightward, void *user)
313{
314 VTermScreen *screen = user;
315
317 vterm_scroll_rect(rect, downward, rightward,
319
321
322 vterm_scroll_rect(rect, downward, rightward,
324
325 return 1;
326 }
327
328 if(screen->damaged.start_row != -1 &&
329 !rect_intersects(&rect, &screen->damaged)) {
331 }
332
336 screen->pending_scroll_rightward = rightward;
337 }
338 else if(rect_equal(&screen->pending_scrollrect, &rect) &&
339 ((screen->pending_scroll_downward == 0 && downward == 0) ||
340 (screen->pending_scroll_rightward == 0 && rightward == 0))) {
341 screen->pending_scroll_downward += downward;
342 screen->pending_scroll_rightward += rightward;
343 }
344 else {
346
349 screen->pending_scroll_rightward = rightward;
350 }
351
352 vterm_scroll_rect(rect, downward, rightward,
354
355 if(screen->damaged.start_row == -1)
356 return 1;
357
358 if(rect_contains(&rect, &screen->damaged)) {
359 /* Scroll region entirely contains the damage; just move it */
360 vterm_rect_move(&screen->damaged, -downward, -rightward);
361 rect_clip(&screen->damaged, &rect);
362 }
363 /* There are a number of possible cases here, but lets restrict this to only
364 * the common case where we might actually gain some performance by
365 * optimising it. Namely, a vertical scroll that neatly cuts the damage
366 * region in half.
367 */
368 else if(rect.start_col <= screen->damaged.start_col &&
369 rect.end_col >= screen->damaged.end_col &&
370 rightward == 0) {
371 if(screen->damaged.start_row >= rect.start_row &&
373 screen->damaged.start_row -= downward;
376 if(screen->damaged.start_row > rect.end_row)
378 }
379 if(screen->damaged.end_row >= rect.start_row &&
380 screen->damaged.end_row < rect.end_row) {
381 screen->damaged.end_row -= downward;
382 if(screen->damaged.end_row < rect.start_row)
384 if(screen->damaged.end_row > rect.end_row)
386 }
387 }
388 else {
389 DEBUG_LOG("TODO: Just flush and redo damaged=" STRFrect " rect=" STRFrect "\n",
391 }
392
393 return 1;
394}
395
396static int movecursor(VTermPos pos, VTermPos oldpos, int visible, void *user)
397{
398 VTermScreen *screen = user;
399
401 return (*screen->callbacks->movecursor)(pos, oldpos, visible, screen->cbdata);
402
403 return 0;
404}
405
406static int setpenattr(VTermAttr attr, VTermValue *val, void *user)
407{
408 VTermScreen *screen = user;
409
410 switch(attr) {
411 case VTERM_ATTR_BOLD:
412 screen->pen.bold = val->boolean;
413 return 1;
415 screen->pen.underline = val->number;
416 return 1;
418 screen->pen.italic = val->boolean;
419 return 1;
420 case VTERM_ATTR_BLINK:
421 screen->pen.blink = val->boolean;
422 return 1;
424 screen->pen.reverse = val->boolean;
425 return 1;
427 screen->pen.conceal = val->boolean;
428 return 1;
430 screen->pen.strike = val->boolean;
431 return 1;
432 case VTERM_ATTR_FONT:
433 screen->pen.font = val->number;
434 return 1;
436 screen->pen.fg = val->color;
437 return 1;
439 screen->pen.bg = val->color;
440 return 1;
441 case VTERM_ATTR_SMALL:
442 screen->pen.small = val->boolean;
443 return 1;
445 screen->pen.baseline = val->number;
446 return 1;
447
448 case VTERM_N_ATTRS:
449 return 0;
450 }
451
452 return 0;
453}
454
455static int settermprop(VTermProp prop, VTermValue *val, void *user)
456{
457 VTermScreen *screen = user;
458
459 switch(prop) {
462 return 0;
463
465 /* only send a damage event on disable; because during enable there's an
466 * erase that sends a damage anyway
467 */
468 if(!val->boolean)
470 break;
474 break;
475 default:
476 ; /* ignore */
477 }
478
480 return (*screen->callbacks->settermprop)(prop, val, screen->cbdata);
481
482 return 1;
483}
484
485static int bell(void *user)
486{
487 VTermScreen *screen = user;
488
490 return (*screen->callbacks->bell)(screen->cbdata);
491
492 return 0;
493}
494
495/* How many cells are non-blank
496 * Returns the position of the first blank cell in the trailing blank end */
497static int line_popcount(ScreenCell *buffer, int row, int rows, int cols)
498{
499 int col = cols - 1;
500 while(col >= 0 && buffer[row * cols + col].chars[0] == 0)
501 col--;
502 return col + 1;
503}
504
505#define REFLOW (screen->reflow)
506
507static void resize_buffer(VTermScreen *screen, int bufidx, int new_rows, int new_cols, bool active, VTermStateFields *statefields)
508{
509 int old_rows = screen->rows;
510 int old_cols = screen->cols;
511
512 ScreenCell *old_buffer = screen->buffers[bufidx];
513 VTermLineInfo *old_lineinfo = statefields->lineinfos[bufidx];
514
515 ScreenCell *new_buffer = vterm_allocator_malloc(screen->vt, sizeof(ScreenCell) * new_rows * new_cols);
516 VTermLineInfo *new_lineinfo = vterm_allocator_malloc(screen->vt, sizeof(new_lineinfo[0]) * new_rows);
517
518 int old_row = old_rows - 1;
519 int new_row = new_rows - 1;
520
521 VTermPos old_cursor = statefields->pos;
522 VTermPos new_cursor = { -1, -1 };
523
524#ifdef DEBUG_REFLOW
525 fprintf(stderr, "Resizing from %dx%d to %dx%d; cursor was at (%d,%d)\n",
526 old_cols, old_rows, new_cols, new_rows, old_cursor.col, old_cursor.row);
527#endif
528
529 /* Keep track of the final row that is knonw to be blank, so we know what
530 * spare space we have for scrolling into
531 */
532 int final_blank_row = new_rows;
533
534 while(old_row >= 0) {
535 int old_row_end = old_row;
536 /* TODO: Stop if dwl or dhl */
537 while(REFLOW && old_lineinfo && old_row >= 0 && old_lineinfo[old_row].continuation)
538 old_row--;
539 int old_row_start = old_row;
540
541 int width = 0;
542 for(int row = old_row_start; row <= old_row_end; row++) {
543 if(REFLOW && row < (old_rows - 1) && old_lineinfo[row + 1].continuation)
544 width += old_cols;
545 else
546 width += line_popcount(old_buffer, row, old_rows, old_cols);
547 }
548
549 if(final_blank_row == (new_row + 1) && width == 0)
550 final_blank_row = new_row;
551
552 int new_height = REFLOW
553 ? width ? (width + new_cols - 1) / new_cols : 1
554 : 1;
555
556 int new_row_end = new_row;
557 int new_row_start = new_row - new_height + 1;
558
559 old_row = old_row_start;
560 int old_col = 0;
561
562 int spare_rows = new_rows - final_blank_row;
563
564 if(new_row_start < 0 && /* we'd fall off the top */
565 spare_rows >= 0 && /* we actually have spare rows */
566 (!active || new_cursor.row == -1 || (new_cursor.row - new_row_start) < new_rows))
567 {
568 /* Attempt to scroll content down into the blank rows at the bottom to
569 * make it fit
570 */
571 int downwards = -new_row_start;
572 if(downwards > spare_rows)
573 downwards = spare_rows;
574 int rowcount = new_rows - downwards;
575
576#ifdef DEBUG_REFLOW
577 fprintf(stderr, " scroll %d rows +%d downwards\n", rowcount, downwards);
578#endif
579
580 memmove(&new_buffer[downwards * new_cols], &new_buffer[0], rowcount * new_cols * sizeof(ScreenCell));
581 memmove(&new_lineinfo[downwards], &new_lineinfo[0], rowcount * sizeof(new_lineinfo[0]));
582
583 new_row += downwards;
584 new_row_start += downwards;
585 new_row_end += downwards;
586
587 if(new_cursor.row >= 0)
588 new_cursor.row += downwards;
589
590 final_blank_row += downwards;
591 }
592
593#ifdef DEBUG_REFLOW
594 fprintf(stderr, " rows [%d..%d] <- [%d..%d] width=%d\n",
595 new_row_start, new_row_end, old_row_start, old_row_end, width);
596#endif
597
598 if(new_row_start < 0) {
599 if(old_row_start <= old_cursor.row && old_cursor.row < old_row_end) {
600 new_cursor.row = 0;
601 new_cursor.col = old_cursor.col;
602 if(new_cursor.col >= new_cols)
603 new_cursor.col = new_cols-1;
604 }
605 break;
606 }
607
608 for(new_row = new_row_start, old_row = old_row_start; new_row <= new_row_end; new_row++) {
609 int count = width >= new_cols ? new_cols : width;
610 width -= count;
611
612 int new_col = 0;
613
614 while(count) {
615 /* TODO: This could surely be done a lot faster by memcpy()'ing the entire range */
616 new_buffer[new_row * new_cols + new_col] = old_buffer[old_row * old_cols + old_col];
617
618 if(old_cursor.row == old_row && old_cursor.col == old_col)
619 new_cursor.row = new_row, new_cursor.col = new_col;
620
621 old_col++;
622 if(old_col == old_cols) {
623 old_row++;
624
625 if(!REFLOW) {
626 new_col++;
627 break;
628 }
629 old_col = 0;
630 }
631
632 new_col++;
633 count--;
634 }
635
636 if(old_cursor.row == old_row && old_cursor.col >= old_col) {
637 new_cursor.row = new_row, new_cursor.col = (old_cursor.col - old_col + new_col);
638 if(new_cursor.col >= new_cols)
639 new_cursor.col = new_cols-1;
640 }
641
642 while(new_col < new_cols) {
643 clearcell(screen, &new_buffer[new_row * new_cols + new_col]);
644 new_col++;
645 }
646
647 new_lineinfo[new_row].continuation = (new_row > new_row_start);
648 }
649
650 old_row = old_row_start - 1;
651 new_row = new_row_start - 1;
652 }
653
654 if(old_cursor.row <= old_row) {
655 /* cursor would have moved entirely off the top of the screen; lets just
656 * bring it within range */
657 new_cursor.row = 0, new_cursor.col = old_cursor.col;
658 if(new_cursor.col >= new_cols)
659 new_cursor.col = new_cols-1;
660 }
661
662 /* We really expect the cursor position to be set by now */
663 if(active && (new_cursor.row == -1 || new_cursor.col == -1)) {
664 fprintf(stderr, "screen_resize failed to update cursor position\n");
665 abort();
666 }
667
668 if(old_row >= 0 && bufidx == BUFIDX_PRIMARY) {
669 /* Push spare lines to scrollback buffer */
671 for(int row = 0; row <= old_row; row++)
673 if(active)
674 statefields->pos.row -= (old_row + 1);
675 }
676 if(new_row >= 0 && bufidx == BUFIDX_PRIMARY &&
678 /* Try to backfill rows by popping scrollback buffer */
679 while(new_row >= 0) {
681 break;
682
683 VTermPos pos = { .row = new_row };
684 for(pos.col = 0; pos.col < old_cols && pos.col < new_cols; pos.col += screen->sb_buffer[pos.col].width) {
685 VTermScreenCell *src = &screen->sb_buffer[pos.col];
686 ScreenCell *dst = &new_buffer[pos.row * new_cols + pos.col];
687
688 for(int i = 0; i < VTERM_MAX_CHARS_PER_CELL; i++) {
689 dst->chars[i] = src->chars[i];
690 if(!src->chars[i])
691 break;
692 }
693
694 dst->pen.bold = src->attrs.bold;
695 dst->pen.underline = src->attrs.underline;
696 dst->pen.italic = src->attrs.italic;
697 dst->pen.blink = src->attrs.blink;
699 dst->pen.conceal = src->attrs.conceal;
700 dst->pen.strike = src->attrs.strike;
701 dst->pen.font = src->attrs.font;
702 dst->pen.small = src->attrs.small;
703 dst->pen.baseline = src->attrs.baseline;
704
705 dst->pen.fg = src->fg;
706 dst->pen.bg = src->bg;
707
708 if(src->width == 2 && pos.col < (new_cols-1))
709 (dst + 1)->chars[0] = (uint32_t) -1;
710 }
711 for( ; pos.col < new_cols; pos.col++)
712 clearcell(screen, &new_buffer[pos.row * new_cols + pos.col]);
713 new_row--;
714
715 if(active)
716 statefields->pos.row++;
717 }
718 }
719 if(new_row >= 0) {
720 /* Scroll new rows back up to the top and fill in blanks at the bottom */
721 int moverows = new_rows - new_row - 1;
722 memmove(&new_buffer[0], &new_buffer[(new_row + 1) * new_cols], moverows * new_cols * sizeof(ScreenCell));
723 memmove(&new_lineinfo[0], &new_lineinfo[new_row + 1], moverows * sizeof(new_lineinfo[0]));
724
725 new_cursor.row -= (new_row + 1);
726
727 for(new_row = moverows; new_row < new_rows; new_row++) {
728 for(int col = 0; col < new_cols; col++)
729 clearcell(screen, &new_buffer[new_row * new_cols + col]);
730 new_lineinfo[new_row] = (VTermLineInfo){ 0 };
731 }
732 }
733
734 vterm_allocator_free(screen->vt, old_buffer);
735 screen->buffers[bufidx] = new_buffer;
736
737 vterm_allocator_free(screen->vt, old_lineinfo);
738 statefields->lineinfos[bufidx] = new_lineinfo;
739
740 if(active)
741 statefields->pos = new_cursor;
742
743 return;
744}
745
746static int resize(int new_rows, int new_cols, VTermStateFields *fields, void *user)
747{
748 VTermScreen *screen = user;
749
750 int altscreen_active = (screen->buffers[BUFIDX_ALTSCREEN] && screen->buffer == screen->buffers[BUFIDX_ALTSCREEN]);
751
752 int old_rows = screen->rows;
753 int old_cols = screen->cols;
754
755 if(new_cols > old_cols) {
756 /* Ensure that ->sb_buffer is large enough for a new or and old row */
757 if(screen->sb_buffer)
759
761 }
762
763 resize_buffer(screen, 0, new_rows, new_cols, !altscreen_active, fields);
765 resize_buffer(screen, 1, new_rows, new_cols, altscreen_active, fields);
766 else if(new_rows != old_rows) {
767 /* We don't need a full resize of the altscreen because it isn't enabled
768 * but we should at least keep the lineinfo the right size */
770
771 VTermLineInfo *new_lineinfo = vterm_allocator_malloc(screen->vt, sizeof(new_lineinfo[0]) * new_rows);
772 for(int row = 0; row < new_rows; row++)
773 new_lineinfo[row] = (VTermLineInfo){ 0 };
774
775 fields->lineinfos[BUFIDX_ALTSCREEN] = new_lineinfo;
776 }
777
779
780 screen->rows = new_rows;
781 screen->cols = new_cols;
782
783 if(new_cols <= old_cols) {
784 if(screen->sb_buffer)
786
788 }
789
790 /* TODO: Maaaaybe we can optimise this if there's no reflow happening */
792
794 return (*screen->callbacks->resize)(new_rows, new_cols, screen->cbdata);
795
796 return 1;
797}
798
799static int setlineinfo(int row, const VTermLineInfo *newinfo, const VTermLineInfo *oldinfo, void *user)
800{
801 VTermScreen *screen = user;
802
803 if(newinfo->doublewidth != oldinfo->doublewidth ||
804 newinfo->doubleheight != oldinfo->doubleheight) {
805 for(int col = 0; col < screen->cols; col++) {
806 ScreenCell *cell = getcell(screen, row, col);
807 cell->pen.dwl = newinfo->doublewidth;
808 cell->pen.dhl = newinfo->doubleheight;
809 }
810
811 VTermRect rect = {
812 .start_row = row,
813 .end_row = row + 1,
814 .start_col = 0,
815 .end_col = newinfo->doublewidth ? screen->cols / 2 : screen->cols,
816 };
817 damagerect(screen, rect);
818
819 if(newinfo->doublewidth) {
820 rect.start_col = screen->cols / 2;
821 rect.end_col = screen->cols;
822
823 erase_internal(rect, 0, user);
824 }
825 }
826
827 return 1;
828}
829
830static int sb_clear(void *user) {
831 VTermScreen *screen = user;
832
835 return 1;
836
837 return 0;
838}
839
841 .putglyph = &putglyph,
842 .movecursor = &movecursor,
843 .scrollrect = &scrollrect,
844 .erase = &erase,
845 .setpenattr = &setpenattr,
846 .settermprop = &settermprop,
847 .bell = &bell,
848 .resize = &resize,
849 .setlineinfo = &setlineinfo,
850 .sb_clear = &sb_clear,
851};
852
854{
856 if(!state)
857 return NULL;
858
860 int rows, cols;
861
863
864 screen->vt = vt;
865 screen->state = state;
866
870
871 screen->rows = rows;
872 screen->cols = cols;
873
874 screen->global_reverse = false;
875 screen->reflow = false;
876
877 screen->callbacks = NULL;
878 screen->cbdata = NULL;
879
881
883
885
887
888 return screen;
889}
890
901
909
910static size_t _get_chars(const VTermScreen *screen, const int utf8, void *buffer, size_t len, const VTermRect rect)
911{
912 size_t outpos = 0;
913 int padding = 0;
914
915#define PUT(c) \
916 if(utf8) { \
917 size_t thislen = utf8_seqlen(c); \
918 if(buffer && outpos + thislen <= len) \
919 outpos += fill_utf8((c), (char *)buffer + outpos); \
920 else \
921 outpos += thislen; \
922 } \
923 else { \
924 if(buffer && outpos + 1 <= len) \
925 ((uint32_t*)buffer)[outpos++] = (c); \
926 else \
927 outpos++; \
928 }
929
930 for(int row = rect.start_row; row < rect.end_row; row++) {
931 for(int col = rect.start_col; col < rect.end_col; col++) {
932 ScreenCell *cell = getcell(screen, row, col);
933
934 if(cell->chars[0] == 0)
935 // Erased cell, might need a space
936 padding++;
937 else if(cell->chars[0] == (uint32_t)-1)
938 // Gap behind a double-width char, do nothing
939 ;
940 else {
941 while(padding) {
943 padding--;
944 }
945 for(int i = 0; i < VTERM_MAX_CHARS_PER_CELL && cell->chars[i]; i++) {
946 PUT(cell->chars[i]);
947 }
948 }
949 }
950
951 if(row < rect.end_row - 1) {
953 padding = 0;
954 }
955 }
956
957 return outpos;
958}
959
960size_t vterm_screen_get_chars(const VTermScreen *screen, uint32_t *chars, size_t len, const VTermRect rect)
961{
962 return _get_chars(screen, 0, chars, len, rect);
963}
964
965size_t vterm_screen_get_text(const VTermScreen *screen, char *str, size_t len, const VTermRect rect)
966{
967 return _get_chars(screen, 1, str, len, rect);
968}
969
970/* Copy internal to external representation of a screen cell */
972{
973 ScreenCell *intcell = getcell(screen, pos.row, pos.col);
974 if(!intcell)
975 return 0;
976
977 for(int i = 0; i < VTERM_MAX_CHARS_PER_CELL; i++) {
978 cell->chars[i] = intcell->chars[i];
979 if(!intcell->chars[i])
980 break;
981 }
982
983 cell->attrs.bold = intcell->pen.bold;
984 cell->attrs.underline = intcell->pen.underline;
985 cell->attrs.italic = intcell->pen.italic;
986 cell->attrs.blink = intcell->pen.blink;
987 cell->attrs.reverse = intcell->pen.reverse ^ screen->global_reverse;
988 cell->attrs.conceal = intcell->pen.conceal;
989 cell->attrs.strike = intcell->pen.strike;
990 cell->attrs.font = intcell->pen.font;
991 cell->attrs.small = intcell->pen.small;
992 cell->attrs.baseline = intcell->pen.baseline;
993
994 cell->attrs.dwl = intcell->pen.dwl;
995 cell->attrs.dhl = intcell->pen.dhl;
996
997 cell->fg = intcell->pen.fg;
998 cell->bg = intcell->pen.bg;
999
1000 if(pos.col < (screen->cols - 1) &&
1001 getcell(screen, pos.row, pos.col + 1)->chars[0] == (uint32_t)-1)
1002 cell->width = 2;
1003 else
1004 cell->width = 1;
1005
1006 return 1;
1007}
1008
1010{
1011 /* This cell is EOL if this and every cell to the right is black */
1012 for(; pos.col < screen->cols; pos.col++) {
1013 ScreenCell *cell = getcell(screen, pos.row, pos.col);
1014 if(cell->chars[0] != 0)
1015 return 0;
1016 }
1017
1018 return 1;
1019}
1020
1022{
1023 if(vt->screen)
1024 return vt->screen;
1025
1027 vt->screen = screen;
1028
1029 return screen;
1030}
1031
1033{
1034 screen->reflow = reflow;
1035}
1036
1037#undef vterm_screen_set_reflow
1039{
1041}
1042
1044{
1045 if(!screen->buffers[BUFIDX_ALTSCREEN] && altscreen) {
1046 int rows, cols;
1048
1050 }
1051}
1052
1054{
1055 screen->callbacks = callbacks;
1056 screen->cbdata = user;
1057}
1058
1060{
1061 return screen->cbdata;
1062}
1063
1068
1073
1090
1096
1098{
1099 if((attrs & VTERM_ATTR_BOLD_MASK) && (a->pen.bold != b->pen.bold))
1100 return 1;
1101 if((attrs & VTERM_ATTR_UNDERLINE_MASK) && (a->pen.underline != b->pen.underline))
1102 return 1;
1103 if((attrs & VTERM_ATTR_ITALIC_MASK) && (a->pen.italic != b->pen.italic))
1104 return 1;
1105 if((attrs & VTERM_ATTR_BLINK_MASK) && (a->pen.blink != b->pen.blink))
1106 return 1;
1107 if((attrs & VTERM_ATTR_REVERSE_MASK) && (a->pen.reverse != b->pen.reverse))
1108 return 1;
1109 if((attrs & VTERM_ATTR_CONCEAL_MASK) && (a->pen.conceal != b->pen.conceal))
1110 return 1;
1111 if((attrs & VTERM_ATTR_STRIKE_MASK) && (a->pen.strike != b->pen.strike))
1112 return 1;
1113 if((attrs & VTERM_ATTR_FONT_MASK) && (a->pen.font != b->pen.font))
1114 return 1;
1115 if((attrs & VTERM_ATTR_FOREGROUND_MASK) && !vterm_color_is_equal(&a->pen.fg, &b->pen.fg))
1116 return 1;
1117 if((attrs & VTERM_ATTR_BACKGROUND_MASK) && !vterm_color_is_equal(&a->pen.bg, &b->pen.bg))
1118 return 1;
1119 if((attrs & VTERM_ATTR_SMALL_MASK) && (a->pen.small != b->pen.small))
1120 return 1;
1121 if((attrs & VTERM_ATTR_BASELINE_MASK) && (a->pen.baseline != b->pen.baseline))
1122 return 1;
1123
1124 return 0;
1125}
1126
1128{
1129 ScreenCell *target = getcell(screen, pos.row, pos.col);
1130
1131 // TODO: bounds check
1132 extent->start_row = pos.row;
1133 extent->end_row = pos.row + 1;
1134
1135 if(extent->start_col < 0)
1136 extent->start_col = 0;
1137 if(extent->end_col < 0)
1138 extent->end_col = screen->cols;
1139
1140 int col;
1141
1142 for(col = pos.col - 1; col >= extent->start_col; col--)
1143 if(attrs_differ(attrs, target, getcell(screen, pos.row, col)))
1144 break;
1145 extent->start_col = col + 1;
1146
1147 for(col = pos.col + 1; col < extent->end_col; col++)
1148 if(attrs_differ(attrs, target, getcell(screen, pos.row, col)))
1149 break;
1150 extent->end_col = col - 1;
1151
1152 return 1;
1153}
1154
1159
1161{
1162 for(int row = 0; row <= screen->rows - 1; row++)
1163 for(int col = 0; col <= screen->cols - 1; col++) {
1164 ScreenCell *cell = &buffer[row * screen->cols + col];
1165 if(VTERM_COLOR_IS_DEFAULT_FG(&cell->pen.fg))
1166 cell->pen.fg = screen->pen.fg;
1167 if(VTERM_COLOR_IS_DEFAULT_BG(&cell->pen.bg))
1168 cell->pen.bg = screen->pen.bg;
1169 }
1170}
1171
1172void vterm_screen_set_default_colors(VTermScreen *screen, const VTermColor *default_fg, const VTermColor *default_bg)
1173{
1174 vterm_state_set_default_colors(screen->state, default_fg, default_bg);
1175
1176 if(default_fg && VTERM_COLOR_IS_DEFAULT_FG(&screen->pen.fg)) {
1177 screen->pen.fg = *default_fg;
1178 screen->pen.fg.type = (screen->pen.fg.type & ~VTERM_COLOR_DEFAULT_MASK)
1180 }
1181
1182 if(default_bg && VTERM_COLOR_IS_DEFAULT_BG(&screen->pen.bg)) {
1183 screen->pen.bg = *default_bg;
1184 screen->pen.bg.type = (screen->pen.bg.type & ~VTERM_COLOR_DEFAULT_MASK)
1186 }
1187
1189 if(screen->buffers[1])
1191}
unsigned int uint32_t
Definition arch.h:11
static VTermScreen * screen
Definition harness.c:106
static VTermState * state
Definition harness.c:105
static VTermStateFallbacks fallbacks
Definition harness.c:297
void * memmove(void *dest, const void *source, size_t size)
Definition string.c:106
uint32_t info
Definition elf.h:7
uint16_t size
Size of the loaded file.
Definition loader.h:5
static int rect_equal(VTermRect *a, VTermRect *b)
Definition rect.h:30
#define ARGSrect(r)
Definition rect.h:6
static void rect_clip(VTermRect *dst, VTermRect *bounds)
Definition rect.h:18
static void rect_expand(VTermRect *dst, VTermRect *src)
Definition rect.h:9
static int rect_contains(VTermRect *big, VTermRect *small)
Definition rect.h:39
static int rect_intersects(VTermRect *a, VTermRect *b)
Definition rect.h:49
#define STRFrect
Definition rect.h:5
#define PUT(c)
unsigned int dwl
Definition screen.c:33
static ScreenCell * getcell(const VTermScreen *screen, int row, int col)
Definition screen.c:82
VTerm * vt
Definition screen.c:46
static int scrollrect(VTermRect rect, int downward, int rightward, void *user)
Definition screen.c:312
int cols
Definition screen.c:59
static VTermScreen * screen_new(VTerm *vt)
Definition screen.c:853
#define UNICODE_SPACE
Definition screen.c:9
void vterm_screen_enable_reflow(VTermScreen *screen, bool reflow)
Definition screen.c:1032
void vterm_screen_flush_damage(VTermScreen *screen)
Definition screen.c:1074
unsigned int global_reverse
Definition screen.c:61
static int attrs_differ(VTermAttrMask attrs, ScreenCell *a, ScreenCell *b)
Definition screen.c:1097
static void reset_default_colours(VTermScreen *screen, ScreenCell *buffer)
Definition screen.c:1160
void vterm_screen_set_default_colors(VTermScreen *screen, const VTermColor *default_fg, const VTermColor *default_bg)
Definition screen.c:1172
static int moverect_user(VTermRect dest, VTermRect src, void *user)
Definition screen.c:252
unsigned int conceal
Definition screen.c:25
void * vterm_screen_get_cbdata(VTermScreen *screen)
Definition screen.c:1059
VTermScreenCell * sb_buffer
Definition screen.c:71
ScreenCell * buffers[2]
Definition screen.c:65
void vterm_screen_reset(VTermScreen *screen, int hard)
Definition screen.c:902
static void resize_buffer(VTermScreen *screen, int bufidx, int new_rows, int new_cols, bool active, VTermStateFields *statefields)
Definition screen.c:507
static int moverect_internal(VTermRect dest, VTermRect src, void *user)
Definition screen.c:217
size_t vterm_screen_get_chars(const VTermScreen *screen, uint32_t *chars, size_t len, const VTermRect rect)
Definition screen.c:960
void vterm_screen_convert_color_to_rgb(const VTermScreen *screen, VTermColor *col)
Definition screen.c:1155
VTermRect damaged
Definition screen.c:54
unsigned int baseline
Definition screen.c:29
static ScreenCell * alloc_buffer(VTermScreen *screen, int rows, int cols)
Definition screen.c:91
VTermState * state
Definition screen.c:47
static int sb_clear(void *user)
Definition screen.c:830
void vterm_screen_set_unrecognised_fallbacks(VTermScreen *screen, const VTermStateFallbacks *fallbacks, void *user)
Definition screen.c:1064
INTERNAL void vterm_screen_free(VTermScreen *screen)
Definition screen.c:891
static int erase(VTermRect rect, int selective, void *user)
Definition screen.c:306
static size_t _get_chars(const VTermScreen *screen, const int utf8, void *buffer, size_t len, const VTermRect rect)
Definition screen.c:910
static void damagescreen(VTermScreen *screen)
Definition screen.c:161
int vterm_screen_is_eol(const VTermScreen *screen, VTermPos pos)
Definition screen.c:1009
static void sb_pushline_from_row(VTermScreen *screen, int row)
Definition screen.c:208
static void clearcell(const VTermScreen *screen, ScreenCell *cell)
Definition screen.c:76
ScreenPen pen
Definition screen.c:73
static int movecursor(VTermPos pos, VTermPos oldpos, int visible, void *user)
Definition screen.c:396
int vterm_screen_get_cell(const VTermScreen *screen, VTermPos pos, VTermScreenCell *cell)
Definition screen.c:971
static int setlineinfo(int row, const VTermLineInfo *newinfo, const VTermLineInfo *oldinfo, void *user)
Definition screen.c:799
int rows
Definition screen.c:58
int pending_scroll_downward
Definition screen.c:56
void * cbdata
Definition screen.c:50
unsigned int protected_cell
Definition screen.c:32
uint32_t chars[VTERM_MAX_CHARS_PER_CELL]
Definition screen.c:40
ScreenPen pen
Definition screen.c:41
static int bell(void *user)
Definition screen.c:485
VTermColor bg
Definition screen.c:18
ScreenCell * buffer
Definition screen.c:68
size_t vterm_screen_get_text(const VTermScreen *screen, char *str, size_t len, const VTermRect rect)
Definition screen.c:965
void vterm_screen_set_callbacks(VTermScreen *screen, const VTermScreenCallbacks *callbacks, void *user)
Definition screen.c:1053
#define REFLOW
Definition screen.c:505
static VTermStateCallbacks state_cbs
Definition screen.c:840
unsigned int dhl
Definition screen.c:34
unsigned int underline
Definition screen.c:21
#define UNICODE_LINEFEED
Definition screen.c:10
VTermDamageSize damage_merge
Definition screen.c:52
static int erase_internal(VTermRect rect, int selective, void *user)
Definition screen.c:270
unsigned int italic
Definition screen.c:22
unsigned int bold
Definition screen.c:20
unsigned int reflow
Definition screen.c:62
static void damagerect(VTermScreen *screen, VTermRect rect)
Definition screen.c:104
static int settermprop(VTermProp prop, VTermValue *val, void *user)
Definition screen.c:455
unsigned int font
Definition screen.c:27
VTermScreen * vterm_obtain_screen(VTerm *vt)
Definition screen.c:1021
unsigned int small
Definition screen.c:28
void vterm_screen_enable_altscreen(VTermScreen *screen, int altscreen)
Definition screen.c:1043
static int line_popcount(ScreenCell *buffer, int row, int rows, int cols)
Definition screen.c:497
static int putglyph(VTermGlyphInfo *info, VTermPos pos, void *user)
Definition screen.c:173
static int erase_user(VTermRect rect, int selective, void *user)
Definition screen.c:297
static int resize(int new_rows, int new_cols, VTermStateFields *fields, void *user)
Definition screen.c:746
unsigned int blink
Definition screen.c:23
int pending_scroll_rightward
Definition screen.c:56
int vterm_screen_get_attrs_extent(const VTermScreen *screen, VTermRect *extent, VTermPos pos, VTermAttrMask attrs)
Definition screen.c:1127
VTermColor fg
Definition screen.c:18
const VTermScreenCallbacks * callbacks
Definition screen.c:49
void * vterm_screen_get_unrecognised_fbdata(VTermScreen *screen)
Definition screen.c:1069
static int setpenattr(VTermAttr attr, VTermValue *val, void *user)
Definition screen.c:406
unsigned int reverse
Definition screen.c:24
unsigned int strike
Definition screen.c:26
VTermRect pending_scrollrect
Definition screen.c:55
void vterm_screen_set_damage_merge(VTermScreen *screen, VTermDamageSize size)
Definition screen.c:1091
int(* moverect)(VTermRect dest, VTermRect src, void *user)
Definition vterm.h:536
int(* damage)(VTermRect rect, void *user)
Definition vterm.h:535
int(* movecursor)(VTermPos pos, VTermPos oldpos, int visible, void *user)
Definition vterm.h:537
int(* sb_popline)(int cols, VTermScreenCell *cells, void *user)
Definition vterm.h:542
int(* resize)(int rows, int cols, void *user)
Definition vterm.h:540
int(* bell)(void *user)
Definition vterm.h:539
int(* settermprop)(VTermProp prop, VTermValue *val, void *user)
Definition vterm.h:538
int(* sb_pushline)(int cols, const VTermScreenCell *cells, void *user)
Definition vterm.h:541
int(* sb_clear)(void *user)
Definition vterm.h:543
int(* putglyph)(VTermGlyphInfo *info, VTermPos pos, void *user)
Definition vterm.h:428
static uint16_t * width
Definition syscalls.h:104
static int rows
Definition unterm.c:31
static int cols
Definition unterm.c:30
static VTerm * vt
Definition unterm.c:27
INTERNAL void * vterm_allocator_malloc(VTerm *vt, size_t size)
Definition vterm.c:98
INTERNAL void vterm_allocator_free(VTerm *vt, void *ptr)
Definition vterm.c:103
#define VTERM_MAX_CHARS_PER_CELL
Definition vterm.h:24
static void vterm_rect_move(VTermRect *rect, int row_delta, int col_delta)
Definition vterm.h:58
unsigned int underline
Definition vterm.h:501
void vterm_state_set_callbacks(VTermState *state, const VTermStateCallbacks *callbacks, void *user)
Definition state.c:2144
unsigned int bold
Definition vterm.h:500
unsigned int italic
Definition vterm.h:502
VTermColor fg
Definition vterm.h:531
VTermAttrMask
Definition vterm.h:579
@ VTERM_ATTR_UNDERLINE_MASK
Definition vterm.h:581
@ VTERM_ATTR_CONCEAL_MASK
Definition vterm.h:589
@ VTERM_ATTR_BOLD_MASK
Definition vterm.h:580
@ VTERM_ATTR_ITALIC_MASK
Definition vterm.h:582
@ VTERM_ATTR_FOREGROUND_MASK
Definition vterm.h:587
@ VTERM_ATTR_BASELINE_MASK
Definition vterm.h:591
@ VTERM_ATTR_SMALL_MASK
Definition vterm.h:590
@ VTERM_ATTR_REVERSE_MASK
Definition vterm.h:584
@ VTERM_ATTR_FONT_MASK
Definition vterm.h:586
@ VTERM_ATTR_STRIKE_MASK
Definition vterm.h:585
@ VTERM_ATTR_BACKGROUND_MASK
Definition vterm.h:588
@ VTERM_ATTR_BLINK_MASK
Definition vterm.h:583
int boolean
Definition vterm.h:228
VTermDamageSize
Definition vterm.h:561
@ VTERM_DAMAGE_SCREEN
Definition vterm.h:564
@ VTERM_DAMAGE_SCROLL
Definition vterm.h:565
@ VTERM_DAMAGE_CELL
Definition vterm.h:562
@ VTERM_DAMAGE_ROW
Definition vterm.h:563
VTermLineInfo * lineinfos[2]
Definition vterm.h:313
unsigned int doubleheight
Definition vterm.h:302
void * vterm_state_get_unrecognised_fbdata(VTermState *state)
Definition state.c:2176
unsigned int dwl
Definition vterm.h:508
VTermPos pos
Definition vterm.h:312
uint8_t type
Definition vterm.h:149
VTermColor bg
Definition vterm.h:531
int end_row
Definition vterm.h:45
unsigned int small
Definition vterm.h:510
void vterm_state_reset(VTermState *state, int hard)
Definition state.c:2072
int col
Definition vterm.h:32
uint32_t chars[VTERM_MAX_CHARS_PER_CELL]
Definition vterm.h:528
@ VTERM_COLOR_DEFAULT_BG
Definition vterm.h:97
@ VTERM_COLOR_DEFAULT_FG
Definition vterm.h:89
#define VTERM_COLOR_IS_DEFAULT_FG(col)
Definition vterm.h:124
int end_col
Definition vterm.h:47
unsigned int font
Definition vterm.h:507
int row
Definition vterm.h:31
void vterm_scroll_rect(VTermRect rect, int downward, int rightward, int(*moverect)(VTermRect src, VTermRect dest, void *user), int(*eraserect)(VTermRect rect, int selective, void *user), void *user)
Definition vterm.c:305
int number
Definition vterm.h:229
unsigned int continuation
Definition vterm.h:303
void vterm_state_convert_color_to_rgb(const VTermState *state, VTermColor *col)
Definition pen.c:263
void vterm_state_set_default_colors(VTermState *state, const VTermColor *default_fg, const VTermColor *default_bg)
Definition pen.c:242
const VTermLineInfo * vterm_state_get_lineinfo(const VTermState *state, int row)
Definition state.c:2251
VTermScreenCellAttrs attrs
Definition vterm.h:530
void vterm_get_size(const VTerm *vt, int *rowsp, int *colsp)
Definition vterm.c:108
int start_row
Definition vterm.h:44
VTermState * vterm_obtain_state(VTerm *vt)
Definition state.c:2059
unsigned int dhl
Definition vterm.h:509
unsigned int conceal
Definition vterm.h:505
unsigned int strike
Definition vterm.h:506
int start_col
Definition vterm.h:46
unsigned int doublewidth
Definition vterm.h:301
unsigned int reverse
Definition vterm.h:504
VTermAttr
Definition vterm.h:234
@ VTERM_ATTR_BOLD
Definition vterm.h:236
@ VTERM_N_ATTRS
Definition vterm.h:249
@ VTERM_ATTR_FOREGROUND
Definition vterm.h:244
@ VTERM_ATTR_UNDERLINE
Definition vterm.h:237
@ VTERM_ATTR_ITALIC
Definition vterm.h:238
@ VTERM_ATTR_BACKGROUND
Definition vterm.h:245
@ VTERM_ATTR_FONT
Definition vterm.h:243
@ VTERM_ATTR_BLINK
Definition vterm.h:239
@ VTERM_ATTR_STRIKE
Definition vterm.h:242
@ VTERM_ATTR_SMALL
Definition vterm.h:246
@ VTERM_ATTR_REVERSE
Definition vterm.h:240
@ VTERM_ATTR_BASELINE
Definition vterm.h:247
@ VTERM_ATTR_CONCEAL
Definition vterm.h:241
VTermColor color
Definition vterm.h:231
#define VTERM_COLOR_IS_DEFAULT_BG(col)
Definition vterm.h:132
void vterm_state_set_unrecognised_fallbacks(VTermState *state, const VTermStateFallbacks *fallbacks, void *user)
Definition state.c:2164
#define vterm_screen_set_reflow
Definition vterm.h:557
VTermProp
Definition vterm.h:252
@ VTERM_PROP_REVERSE
Definition vterm.h:259
@ VTERM_PROP_ALTSCREEN
Definition vterm.h:256
int vterm_color_is_equal(const VTermColor *a, const VTermColor *b)
Definition pen.c:211
unsigned int blink
Definition vterm.h:503
unsigned int baseline
Definition vterm.h:511
#define INTERNAL
#define DEBUG_LOG(...)
#define BUFIDX_ALTSCREEN
VTermScreen * screen
#define BUFIDX_PRIMARY