1 /*
2 * libtsm - Screen Management
3 *
4 * Copyright (c) 2011-2013 David Herrmann <dh.herrmann@gmail.com>
5 *
6 * Permission is hereby granted, free of charge, to any person obtaining
7 * a copy of this software and associated documentation files
8 * (the "Software"), to deal in the Software without restriction, including
9 * without limitation the rights to use, copy, modify, merge, publish,
10 * distribute, sublicense, and/or sell copies of the Software, and to
11 * permit persons to whom the Software is furnished to do so, subject to
12 * the following conditions:
13 *
14 * The above copyright notice and this permission notice shall be included
15 * in all copies or substantial portions of the Software.
16 *
17 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
18 * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
19 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
20 * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
21 * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
22 * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
23 * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
24 */
25
26 /*
27 * Screen Management
28 * This provides the abstracted screen management. It does not do any
29 * terminal-emulation, instead it provides a resizable table of cells. You can
30 * insert, remove and modify the cells freely.
31 * A screen has always a fixed, but changeable, width and height. This defines
32 * the number of columns and rows. The screen doesn't care for pixels, glyphs or
33 * framebuffers. The screen only contains information about each cell.
34 *
35 * Screens are the logical model behind a real screen of a terminal emulator.
36 * Users usually allocate a screen for each terminal-emulator they run. All they
37 * have to do is render the screen onto their widget on each change and forward
38 * any widget-events to the screen.
39 *
40 * The screen object already includes scrollback-buffers, selection support and
41 * more. This simplifies terminal emulators a lot, but also prevents them from
42 * accessing the real screen data. However, terminal emulators should have no
43 * reason to access the data directly. The screen API should provide everything
44 * they need.
45 *
46 * AGEING:
47 * Each cell, line and screen has an "age" field. This field describes when it
48 * was changed the last time. After drawing a screen, the current screen age is
49 * returned. This allows users to skip drawing specific cells, if their
50 * framebuffer was already drawn with a newer age than a given cell.
51 * However, the screen-age might overflow. This is properly detected and causes
52 * drawing functions to return "0" as age. Users must reset all their
53 * framebuffer ages then. Otherwise, further drawing operations might
54 * incorrectly skip cells.
55 * Furthermore, if a cell has age "0", it means it _has_ to be drawn. No ageing
56 * information is available.
57 */
58
59 #include <errno.h>
60 #include <inttypes.h>
61 #include <stdbool.h>
62 #include <stdlib.h>
63 #include <string.h>
64 #include "libtsm.h"
65 #include "libtsm-int.h"
66 #include "shl-llog.h"
67
68 #define LLOG_SUBSYSTEM "tsm-screen"
69
get_cursor_cell(struct tsm_screen * con)70 static struct cell *get_cursor_cell(struct tsm_screen *con)
71 {
72 int cur_x, cur_y;
73
74 cur_x = con->cursor_x;
75 if (cur_x >= con->size_x)
76 cur_x = con->size_x - 1;
77
78 cur_y = con->cursor_y;
79 if (cur_y >= con->size_y)
80 cur_y = con->size_y - 1;
81
82 return &con->lines[cur_y]->cells[cur_x];
83 }
84
move_cursor(struct tsm_screen * con,int x,int y)85 static void move_cursor(struct tsm_screen *con, int x, int y)
86 {
87 struct cell *c;
88
89 /* if cursor is hidden, just move it */
90 if (con->flags & TSM_SCREEN_HIDE_CURSOR) {
91 con->cursor_x = x;
92 con->cursor_y = y;
93 return;
94 }
95
96 /* If cursor is visible, we have to mark the current and the new cell
97 * as changed by resetting their age. We skip it if the cursor-position
98 * didn't actually change. */
99
100 if (con->cursor_x == x && con->cursor_y == y)
101 return;
102
103 c = get_cursor_cell(con);
104 c->age = con->age_cnt;
105
106 con->cursor_x = x;
107 con->cursor_y = y;
108
109 c = get_cursor_cell(con);
110 c->age = con->age_cnt;
111 }
112
screen_cell_init(struct tsm_screen * con,struct cell * cell)113 void screen_cell_init(struct tsm_screen *con, struct cell *cell)
114 {
115 cell->ch = 0;
116 cell->width = 1;
117 cell->age = con->age_cnt;
118 memcpy(&cell->attr, &con->def_attr, sizeof(cell->attr));
119 }
120
line_new(struct tsm_screen * con,struct line ** out,int width)121 static int line_new(struct tsm_screen *con, struct line **out, int width)
122 {
123 struct line *line;
124 int i;
125
126 if (!width)
127 return -EINVAL;
128
129 line = malloc(sizeof(*line));
130 if (!line)
131 return -ENOMEM;
132 line->next = NULL;
133 line->prev = NULL;
134 line->size = width;
135 line->age = con->age_cnt;
136
137 line->cells = malloc(sizeof(struct cell) * width);
138 if (!line->cells) {
139 free(line);
140 return -ENOMEM;
141 }
142
143 for (i = 0; i < width; ++i)
144 screen_cell_init(con, &line->cells[i]);
145
146 *out = line;
147 return 0;
148 }
149
line_free(struct line * line)150 static void line_free(struct line *line)
151 {
152 free(line->cells);
153 free(line);
154 }
155
line_resize(struct tsm_screen * con,struct line * line,int width)156 static int line_resize(struct tsm_screen *con, struct line *line, int width)
157 {
158 struct cell *tmp;
159
160 if (line->size < width) {
161 tmp = realloc(line->cells, width * sizeof(struct cell));
162 if (!tmp)
163 return -ENOMEM;
164
165 line->cells = tmp;
166
167 while (line->size < width) {
168 screen_cell_init(con, &line->cells[line->size]);
169 ++line->size;
170 }
171 }
172
173 return 0;
174 }
175
176 /* This links the given line into the scrollback-buffer */
link_to_scrollback(struct tsm_screen * con,struct line * line)177 static void link_to_scrollback(struct tsm_screen *con, struct line *line)
178 {
179 struct line *tmp;
180
181 /* TODO: more sophisticated ageing */
182 con->age = con->age_cnt;
183
184 if (con->sb_max == 0) {
185 if (con->sel_active) {
186 if (con->sel_start.line == line) {
187 con->sel_start.line = NULL;
188 con->sel_start.y = SELECTION_TOP;
189 }
190 if (con->sel_end.line == line) {
191 con->sel_end.line = NULL;
192 con->sel_end.y = SELECTION_TOP;
193 }
194 }
195 line_free(line);
196 return;
197 }
198
199 /* Remove a line from the scrollback buffer if it reaches its maximum.
200 * We must take care to correctly keep the current position as the new
201 * line is linked in after we remove the top-most line here.
202 * sb_max == 0 is tested earlier so we can assume sb_max > 0 here. In
203 * other words, buf->sb_first is a valid line if sb_count >= sb_max. */
204 if (con->sb_count >= con->sb_max) {
205 tmp = con->sb_first;
206 con->sb_first = tmp->next;
207 if (tmp->next)
208 tmp->next->prev = NULL;
209 else
210 con->sb_last = NULL;
211 --con->sb_count;
212
213 /* (position == tmp && !next) means we have sb_max=1 so set
214 * position to the new line. Otherwise, set to new first line.
215 * If position!=tmp and we have a fixed-position then nothing
216 * needs to be done because we can stay at the same line. If we
217 * have no fixed-position, we need to set the position to the
218 * next inserted line, which can be "line", too. */
219 if (con->sb_pos) {
220 if (con->sb_pos == tmp ||
221 !(con->flags & TSM_SCREEN_FIXED_POS)) {
222 if (con->sb_pos->next)
223 con->sb_pos = con->sb_pos->next;
224 else
225 con->sb_pos = line;
226 }
227 }
228
229 if (con->sel_active) {
230 if (con->sel_start.line == tmp) {
231 con->sel_start.line = NULL;
232 con->sel_start.y = SELECTION_TOP;
233 }
234 if (con->sel_end.line == tmp) {
235 con->sel_end.line = NULL;
236 con->sel_end.y = SELECTION_TOP;
237 }
238 }
239 line_free(tmp);
240 }
241
242 line->sb_id = ++con->sb_last_id;
243 line->next = NULL;
244 line->prev = con->sb_last;
245 if (con->sb_last)
246 con->sb_last->next = line;
247 else
248 con->sb_first = line;
249 con->sb_last = line;
250 ++con->sb_count;
251 }
252
screen_scroll_up(struct tsm_screen * con,int num)253 static void screen_scroll_up(struct tsm_screen *con, int num)
254 {
255 int i, j, max, pos;
256 int ret;
257
258 if (!num)
259 return;
260
261 /* TODO: more sophisticated ageing */
262 con->age = con->age_cnt;
263
264 max = con->margin_bottom + 1 - con->margin_top;
265 if (num > max)
266 num = max;
267
268 /* We cache lines on the stack to speed up the scrolling. However, if
269 * num is too big we might get overflows here so use recursion if num
270 * exceeds a hard-coded limit.
271 * 128 seems to be a sane limit that should never be reached but should
272 * also be small enough so we do not get stack overflows. */
273 if (num > 128) {
274 screen_scroll_up(con, 128);
275 return screen_scroll_up(con, num - 128);
276 }
277 struct line *cache[num];
278
279 for (i = 0; i < num; ++i) {
280 pos = con->margin_top + i;
281 if (!(con->flags & TSM_SCREEN_ALTERNATE))
282 ret = line_new(con, &cache[i], con->size_x);
283 else
284 ret = -EAGAIN;
285
286 if (!ret) {
287 link_to_scrollback(con, con->lines[pos]);
288 } else {
289 cache[i] = con->lines[pos];
290 for (j = 0; j < con->size_x; ++j)
291 screen_cell_init(con, &cache[i]->cells[j]);
292 }
293 con->vanguard--;
294 }
295 if (con->vanguard < 0)
296 con->vanguard = 0;
297
298 if (num < max) {
299 memmove(&con->lines[con->margin_top],
300 &con->lines[con->margin_top + num],
301 (max - num) * sizeof(struct line*));
302 }
303
304 memcpy(&con->lines[con->margin_top + (max - num)],
305 cache, num * sizeof(struct line*));
306
307 if (con->sel_active) {
308 if (!con->sel_start.line && con->sel_start.y >= 0) {
309 con->sel_start.y -= num;
310 if (con->sel_start.y < 0) {
311 con->sel_start.line = con->sb_last;
312 while (con->sel_start.line && ++con->sel_start.y < 0)
313 con->sel_start.line = con->sel_start.line->prev;
314 con->sel_start.y = SELECTION_TOP;
315 }
316 }
317 if (!con->sel_end.line && con->sel_end.y >= 0) {
318 con->sel_end.y -= num;
319 if (con->sel_end.y < 0) {
320 con->sel_end.line = con->sb_last;
321 while (con->sel_end.line && ++con->sel_end.y < 0)
322 con->sel_end.line = con->sel_end.line->prev;
323 con->sel_end.y = SELECTION_TOP;
324 }
325 }
326 }
327 }
328
screen_scroll_down(struct tsm_screen * con,int num)329 static void screen_scroll_down(struct tsm_screen *con, int num)
330 {
331 int i, j, max;
332
333 if (!num)
334 return;
335
336 /* TODO: more sophisticated ageing */
337 con->age = con->age_cnt;
338
339 max = con->margin_bottom + 1 - con->margin_top;
340 if (num > max)
341 num = max;
342
343 /* see screen_scroll_up() for an explanation */
344 if (num > 128) {
345 screen_scroll_down(con, 128);
346 return screen_scroll_down(con, num - 128);
347 }
348 struct line *cache[num];
349
350 for (i = 0; i < num; ++i) {
351 cache[i] = con->lines[con->margin_bottom - i];
352 for (j = 0; j < con->size_x; ++j)
353 screen_cell_init(con, &cache[i]->cells[j]);
354 con->vanguard++;
355 }
356 if (con->vanguard >= con->size_y)
357 con->vanguard = con->size_y - 1;
358
359 if (num < max) {
360 memmove(&con->lines[con->margin_top + num],
361 &con->lines[con->margin_top],
362 (max - num) * sizeof(struct line*));
363 }
364
365 memcpy(&con->lines[con->margin_top],
366 cache, num * sizeof(struct line*));
367
368 if (con->sel_active) {
369 if (!con->sel_start.line && con->sel_start.y >= 0)
370 con->sel_start.y += num;
371 if (!con->sel_end.line && con->sel_end.y >= 0)
372 con->sel_end.y += num;
373 }
374 }
375
screen_write(struct tsm_screen * con,int x,int y,tsm_symbol_t ch,int len,const struct tsm_screen_attr * attr)376 static void screen_write(struct tsm_screen *con, int x, int y, tsm_symbol_t ch,
377 int len, const struct tsm_screen_attr *attr)
378 {
379 struct line *line;
380 int i;
381
382 if (!len)
383 return;
384
385 if (x >= con->size_x || y >= con->size_y) {
386 llog_warning(con, "writing beyond buffer boundary");
387 return;
388 }
389
390 line = con->lines[y];
391
392 if (con->flags & TSM_SCREEN_INSERT_MODE && x < con->size_x - len) {
393 line->age = con->age_cnt;
394 memmove(&line->cells[x + len], &line->cells[x],
395 sizeof(struct cell) * (con->size_x - len - x));
396 }
397
398 line->cells[x].age = con->age_cnt;
399 line->cells[x].ch = ch;
400 line->cells[x].width = len;
401 memcpy(&line->cells[x].attr, attr, sizeof(*attr));
402
403 for (i = 1; i < len && i + x < con->size_x; ++i) {
404 line->cells[x + i].age = con->age_cnt;
405 line->cells[x + i].width = 0;
406 }
407
408 if (y > con->vanguard)
409 con->vanguard = y;
410 }
411
screen_erase_region(struct tsm_screen * con,int x_from,int y_from,int x_to,int y_to,bool protect)412 static void screen_erase_region(struct tsm_screen *con,
413 int x_from, int y_from,
414 int x_to, int y_to,
415 bool protect)
416 {
417 int to;
418 struct line *line;
419
420 /* TODO: more sophisticated ageing */
421 con->age = con->age_cnt;
422
423 if (y_to >= con->size_y)
424 y_to = con->size_y - 1;
425 if (x_to >= con->size_x)
426 x_to = con->size_x - 1;
427
428 for ( ; y_from <= y_to; ++y_from) {
429 line = con->lines[y_from];
430 if (!line) {
431 x_from = 0;
432 continue;
433 }
434
435 if (y_from == y_to)
436 to = x_to;
437 else
438 to = con->size_x - 1;
439 for ( ; x_from <= to; ++x_from) {
440 if (protect && line->cells[x_from].attr.protect)
441 continue;
442
443 screen_cell_init(con, &line->cells[x_from]);
444 }
445 x_from = 0;
446 }
447 }
448
to_abs_x(struct tsm_screen * con,int x)449 static inline int to_abs_x(struct tsm_screen *con, int x)
450 {
451 return x;
452 }
453
to_abs_y(struct tsm_screen * con,int y)454 static inline int to_abs_y(struct tsm_screen *con, int y)
455 {
456 if (!(con->flags & TSM_SCREEN_REL_ORIGIN))
457 return y;
458
459 return con->margin_top + y;
460 }
461
462 SHL_EXPORT
tsm_screen_new(struct tsm_screen ** out)463 int tsm_screen_new(struct tsm_screen **out)
464 {
465 struct tsm_screen *con;
466 int ret;
467 int i;
468
469 con = malloc(sizeof(*con));
470 if (!con)
471 return -ENOMEM;
472
473 memset(con, 0, sizeof(*con));
474 con->ref = 1;
475 con->age_cnt = 1;
476 con->age = con->age_cnt;
477 con->def_attr.fr = 255;
478 con->def_attr.fg = 255;
479 con->def_attr.fb = 255;
480
481 ret = tsm_symbol_table_new(&con->sym_table);
482 if (ret)
483 goto err_free;
484
485 ret = tsm_screen_resize(con, 80, 100);
486 if (ret)
487 goto err_free;
488
489 *out = con;
490
491 return 0;
492
493 err_free:
494 for (i = 0; i < con->line_num; ++i) {
495 line_free(con->main_lines[i]);
496 line_free(con->alt_lines[i]);
497 }
498 free(con->main_lines);
499 free(con->alt_lines);
500 free(con->tab_ruler);
501 tsm_symbol_table_unref(con->sym_table);
502 free(con);
503 return ret;
504 }
505
506 SHL_EXPORT
tsm_screen_ref(struct tsm_screen * con)507 void tsm_screen_ref(struct tsm_screen *con)
508 {
509 ++con->ref;
510 }
511
512 SHL_EXPORT
tsm_screen_unref(struct tsm_screen * con)513 void tsm_screen_unref(struct tsm_screen *con)
514 {
515 int i;
516
517 if (!con->ref || --con->ref)
518 return;
519
520 tsm_screen_clear_sb(con);
521 for (i = 0; i < con->line_num; ++i) {
522 line_free(con->main_lines[i]);
523 line_free(con->alt_lines[i]);
524 }
525 free(con->main_lines);
526 free(con->alt_lines);
527 free(con->tab_ruler);
528 tsm_symbol_table_unref(con->sym_table);
529 free(con);
530 }
531
tsm_screen_set_opts(struct tsm_screen * scr,unsigned int opts)532 void tsm_screen_set_opts(struct tsm_screen *scr, unsigned int opts)
533 {
534 scr->opts |= opts;
535 }
536
tsm_screen_reset_opts(struct tsm_screen * scr,unsigned int opts)537 void tsm_screen_reset_opts(struct tsm_screen *scr, unsigned int opts)
538 {
539 scr->opts &= ~opts;
540 }
541
tsm_screen_get_opts(struct tsm_screen * scr)542 unsigned int tsm_screen_get_opts(struct tsm_screen *scr)
543 {
544 return scr->opts;
545 }
546
547 SHL_EXPORT
tsm_screen_get_width(struct tsm_screen * con)548 int tsm_screen_get_width(struct tsm_screen *con)
549 {
550 return con->size_x;
551 }
552
553 SHL_EXPORT
tsm_screen_get_height(struct tsm_screen * con)554 int tsm_screen_get_height(struct tsm_screen *con)
555 {
556 return con->size_y;
557 }
558
559 SHL_EXPORT
tsm_screen_resize(struct tsm_screen * con,int x,int y)560 int tsm_screen_resize(struct tsm_screen *con, int x, int y)
561 {
562 struct line **cache;
563 int i, j, width, diff, start;
564 int ret;
565 bool *tab_ruler;
566
567 if (con->size_x == x && con->size_y == y)
568 return 0;
569
570 /* First make sure the line buffer is big enough for our new screen.
571 * That is, allocate all new lines and make sure each line has enough
572 * cells to hold the new screen or the current screen. If we fail, we
573 * can safely return -ENOMEM and the buffer is still valid. We must
574 * allocate the new lines to at least the same size as the current
575 * lines. Otherwise, if this function fails in later turns, we will have
576 * invalid lines in the buffer. */
577 if (y > con->line_num) {
578 /* resize main buffer */
579 cache = realloc(con->main_lines, sizeof(struct line*) * y);
580 if (!cache)
581 return -ENOMEM;
582
583 if (con->lines == con->main_lines)
584 con->lines = cache;
585 con->main_lines = cache;
586
587 /* resize alt buffer */
588 cache = realloc(con->alt_lines, sizeof(struct line*) * y);
589 if (!cache)
590 return -ENOMEM;
591
592 if (con->lines == con->alt_lines)
593 con->lines = cache;
594 con->alt_lines = cache;
595
596 /* allocate new lines */
597 if (x > con->size_x)
598 width = x;
599 else
600 width = con->size_x;
601
602 while (con->line_num < y) {
603 ret = line_new(con, &con->main_lines[con->line_num],
604 width);
605 if (ret)
606 return ret;
607
608 ret = line_new(con, &con->alt_lines[con->line_num],
609 width);
610 if (ret) {
611 line_free(con->main_lines[con->line_num]);
612 return ret;
613 }
614
615 ++con->line_num;
616 }
617 }
618
619 /* Resize all lines in the buffer if we increase screen width. This
620 * will guarantee that all lines are big enough so we can resize the
621 * buffer without reallocating them later. */
622 if (x > con->size_x) {
623 tab_ruler = realloc(con->tab_ruler, sizeof(bool) * x);
624 if (!tab_ruler)
625 return -ENOMEM;
626 con->tab_ruler = tab_ruler;
627
628 for (i = 0; i < con->line_num; ++i) {
629 ret = line_resize(con, con->main_lines[i], x);
630 if (ret)
631 return ret;
632
633 ret = line_resize(con, con->alt_lines[i], x);
634 if (ret)
635 return ret;
636 }
637 }
638
639 screen_inc_age(con);
640
641 /* clear expansion/padding area */
642 start = x;
643 if (x > con->size_x)
644 start = con->size_x;
645 for (j = 0; j < con->line_num; ++j) {
646 /* main-lines may go into SB, so clear all cells */
647 i = 0;
648 if (j < con->size_y)
649 i = start;
650
651 for ( ; i < con->main_lines[j]->size; ++i)
652 screen_cell_init(con, &con->main_lines[j]->cells[i]);
653
654 /* alt-lines never go into SB, only clear visible cells */
655 i = 0;
656 if (j < con->size_y)
657 i = con->size_x;
658
659 for ( ; i < x; ++i)
660 screen_cell_init(con, &con->alt_lines[j]->cells[i]);
661 }
662
663 /* xterm destroys margins on resize, so do we */
664 con->margin_top = 0;
665 con->margin_bottom = con->size_y - 1;
666
667 /* reset tabs */
668 for (i = 0; i < x; ++i) {
669 if (i % 8 == 0)
670 con->tab_ruler[i] = true;
671 else
672 con->tab_ruler[i] = false;
673 }
674
675 /* We need to adjust x-size first as screen_scroll_up() and friends may
676 * have to reallocate lines. The y-size is adjusted after them to avoid
677 * missing lines when shrinking y-size.
678 * We need to carefully look for the functions that we call here as they
679 * have stronger invariants as when called normally. */
680
681 con->size_x = x;
682 if (con->cursor_x >= con->size_x)
683 move_cursor(con, con->size_x - 1, con->cursor_y);
684
685 /* scroll buffer if screen height shrinks */
686 diff = (con->size_y - y) - (con->size_y - (con->vanguard + 1));
687 if (diff > 0) {
688 screen_scroll_up(con, diff);
689 if (con->cursor_y > diff)
690 move_cursor(con, con->cursor_x, con->cursor_y - diff);
691 else
692 move_cursor(con, con->cursor_x, 0);
693 }
694
695 con->size_y = y;
696 con->margin_bottom = con->size_y - 1;
697 if (con->cursor_y >= con->size_y)
698 move_cursor(con, con->cursor_x, con->size_y - 1);
699
700 return 0;
701 }
702
703 SHL_EXPORT
tsm_screen_set_margins(struct tsm_screen * con,int top,int bottom)704 int tsm_screen_set_margins(struct tsm_screen *con, int top, int bottom)
705 {
706 int upper, lower;
707
708 if (!top)
709 top = 1;
710
711 if (bottom <= top) {
712 upper = 0;
713 lower = con->size_y - 1;
714 } else if (bottom > con->size_y) {
715 upper = 0;
716 lower = con->size_y - 1;
717 } else {
718 upper = top - 1;
719 lower = bottom - 1;
720 }
721
722 con->margin_top = upper;
723 con->margin_bottom = lower;
724 return 0;
725 }
726
727 /* set maximum scrollback buffer size */
728 SHL_EXPORT
tsm_screen_set_max_sb(struct tsm_screen * con,int max)729 void tsm_screen_set_max_sb(struct tsm_screen *con, int max)
730 {
731 struct line *line;
732
733 screen_inc_age(con);
734 /* TODO: more sophisticated ageing */
735 con->age = con->age_cnt;
736
737 while (con->sb_count > max) {
738 line = con->sb_first;
739 con->sb_first = line->next;
740 if (line->next)
741 line->next->prev = NULL;
742 else
743 con->sb_last = NULL;
744 con->sb_count--;
745
746 /* We treat fixed/unfixed position the same here because we
747 * remove lines from the TOP of the scrollback buffer. */
748 if (con->sb_pos == line)
749 con->sb_pos = con->sb_first;
750
751 if (con->sel_active) {
752 if (con->sel_start.line == line) {
753 con->sel_start.line = NULL;
754 con->sel_start.y = SELECTION_TOP;
755 }
756 if (con->sel_end.line == line) {
757 con->sel_end.line = NULL;
758 con->sel_end.y = SELECTION_TOP;
759 }
760 }
761 line_free(line);
762 }
763
764 con->sb_max = max;
765 }
766
767 /* clear scrollback buffer */
768 SHL_EXPORT
tsm_screen_clear_sb(struct tsm_screen * con)769 void tsm_screen_clear_sb(struct tsm_screen *con)
770 {
771 struct line *iter, *tmp;
772
773 screen_inc_age(con);
774 /* TODO: more sophisticated ageing */
775 con->age = con->age_cnt;
776
777 for (iter = con->sb_first; iter; ) {
778 tmp = iter;
779 iter = iter->next;
780 line_free(tmp);
781 }
782
783 con->sb_first = NULL;
784 con->sb_last = NULL;
785 con->sb_count = 0;
786 con->sb_pos = NULL;
787
788 if (con->sel_active) {
789 if (con->sel_start.line) {
790 con->sel_start.line = NULL;
791 con->sel_start.y = SELECTION_TOP;
792 }
793 if (con->sel_end.line) {
794 con->sel_end.line = NULL;
795 con->sel_end.y = SELECTION_TOP;
796 }
797 }
798 }
799
800 SHL_EXPORT
tsm_screen_sb_up(struct tsm_screen * con,int num)801 void tsm_screen_sb_up(struct tsm_screen *con, int num)
802 {
803 if (!num)
804 return;
805
806 screen_inc_age(con);
807 /* TODO: more sophisticated ageing */
808 con->age = con->age_cnt;
809
810 while (num--) {
811 if (con->sb_pos) {
812 if (!con->sb_pos->prev)
813 return;
814
815 con->sb_pos = con->sb_pos->prev;
816 } else if (!con->sb_last) {
817 return;
818 } else {
819 con->sb_pos = con->sb_last;
820 }
821 }
822 }
823
824 SHL_EXPORT
tsm_screen_sb_down(struct tsm_screen * con,int num)825 void tsm_screen_sb_down(struct tsm_screen *con, int num)
826 {
827 if (!num)
828 return;
829
830 screen_inc_age(con);
831 /* TODO: more sophisticated ageing */
832 con->age = con->age_cnt;
833
834 while (num--) {
835 if (con->sb_pos)
836 con->sb_pos = con->sb_pos->next;
837 else
838 return;
839 }
840 }
841
842 SHL_EXPORT
tsm_screen_sb_page_up(struct tsm_screen * con,int num)843 void tsm_screen_sb_page_up(struct tsm_screen *con, int num)
844 {
845 if (!num)
846 return;
847
848 screen_inc_age(con);
849 tsm_screen_sb_up(con, num * con->size_y);
850 }
851
852 SHL_EXPORT
tsm_screen_sb_page_down(struct tsm_screen * con,int num)853 void tsm_screen_sb_page_down(struct tsm_screen *con, int num)
854 {
855 if (!num)
856 return;
857
858 screen_inc_age(con);
859 tsm_screen_sb_down(con, num * con->size_y);
860 }
861
862 SHL_EXPORT
tsm_screen_sb_reset(struct tsm_screen * con)863 void tsm_screen_sb_reset(struct tsm_screen *con)
864 {
865 if (!con->sb_pos)
866 return;
867
868 screen_inc_age(con);
869 /* TODO: more sophisticated ageing */
870 con->age = con->age_cnt;
871
872 con->sb_pos = NULL;
873 }
874
875 SHL_EXPORT
tsm_screen_set_def_attr(struct tsm_screen * con,const struct tsm_screen_attr * attr)876 void tsm_screen_set_def_attr(struct tsm_screen *con,
877 const struct tsm_screen_attr *attr)
878 {
879 memcpy(&con->def_attr, attr, sizeof(*attr));
880 }
881
882 SHL_EXPORT
tsm_screen_reset(struct tsm_screen * con)883 void tsm_screen_reset(struct tsm_screen *con)
884 {
885 int i;
886
887 screen_inc_age(con);
888 con->age = con->age_cnt;
889
890 con->flags = 0;
891 con->margin_top = 0;
892 con->margin_bottom = con->size_y - 1;
893 con->lines = con->main_lines;
894
895 for (i = 0; i < con->size_x; ++i) {
896 if (i % 8 == 0)
897 con->tab_ruler[i] = true;
898 else
899 con->tab_ruler[i] = false;
900 }
901 }
902
903 SHL_EXPORT
tsm_screen_set_flags(struct tsm_screen * con,unsigned int flags)904 void tsm_screen_set_flags(struct tsm_screen *con, unsigned int flags)
905 {
906 unsigned int old;
907 struct cell *c;
908
909 if (!flags)
910 return;
911
912 screen_inc_age(con);
913
914 old = con->flags;
915 con->flags |= flags;
916
917 if (!(old & TSM_SCREEN_ALTERNATE) && (flags & TSM_SCREEN_ALTERNATE)) {
918 con->age = con->age_cnt;
919 con->lines = con->alt_lines;
920 }
921
922 if (!(old & TSM_SCREEN_HIDE_CURSOR) &&
923 (flags & TSM_SCREEN_HIDE_CURSOR)) {
924 c = get_cursor_cell(con);
925 c->age = con->age_cnt;
926 }
927
928 if (!(old & TSM_SCREEN_INVERSE) && (flags & TSM_SCREEN_INVERSE))
929 con->age = con->age_cnt;
930 }
931
932 SHL_EXPORT
tsm_screen_reset_flags(struct tsm_screen * con,unsigned int flags)933 void tsm_screen_reset_flags(struct tsm_screen *con, unsigned int flags)
934 {
935 unsigned int old;
936 struct cell *c;
937
938 if (!flags)
939 return;
940
941 screen_inc_age(con);
942
943 old = con->flags;
944 con->flags &= ~flags;
945
946 if ((old & TSM_SCREEN_ALTERNATE) && (flags & TSM_SCREEN_ALTERNATE)) {
947 con->age = con->age_cnt;
948 con->lines = con->main_lines;
949 }
950
951 if ((old & TSM_SCREEN_HIDE_CURSOR) &&
952 (flags & TSM_SCREEN_HIDE_CURSOR)) {
953 c = get_cursor_cell(con);
954 c->age = con->age_cnt;
955 }
956
957 if ((old & TSM_SCREEN_INVERSE) && (flags & TSM_SCREEN_INVERSE))
958 con->age = con->age_cnt;
959 }
960
961 SHL_EXPORT
tsm_screen_get_flags(struct tsm_screen * con)962 unsigned int tsm_screen_get_flags(struct tsm_screen *con)
963 {
964 return con->flags;
965 }
966
967 SHL_EXPORT
tsm_screen_get_cursor_x(struct tsm_screen * con)968 int tsm_screen_get_cursor_x(struct tsm_screen *con)
969 {
970 return con->cursor_x;
971 }
972
973 SHL_EXPORT
tsm_screen_get_cursor_y(struct tsm_screen * con)974 int tsm_screen_get_cursor_y(struct tsm_screen *con)
975 {
976 return con->cursor_y;
977 }
978
979 SHL_EXPORT
tsm_screen_set_tabstop(struct tsm_screen * con)980 void tsm_screen_set_tabstop(struct tsm_screen *con)
981 {
982 if (con->cursor_x >= con->size_x)
983 return;
984
985 con->tab_ruler[con->cursor_x] = true;
986 }
987
988 SHL_EXPORT
tsm_screen_reset_tabstop(struct tsm_screen * con)989 void tsm_screen_reset_tabstop(struct tsm_screen *con)
990 {
991 if (con->cursor_x >= con->size_x)
992 return;
993
994 con->tab_ruler[con->cursor_x] = false;
995 }
996
997 SHL_EXPORT
tsm_screen_reset_all_tabstops(struct tsm_screen * con)998 void tsm_screen_reset_all_tabstops(struct tsm_screen *con)
999 {
1000 int i;
1001
1002 for (i = 0; i < con->size_x; ++i)
1003 con->tab_ruler[i] = false;
1004 }
1005
1006 SHL_EXPORT
tsm_screen_write(struct tsm_screen * con,tsm_symbol_t ch,const struct tsm_screen_attr * attr)1007 void tsm_screen_write(struct tsm_screen *con, tsm_symbol_t ch,
1008 const struct tsm_screen_attr *attr)
1009 {
1010 int last;
1011 int len;
1012
1013 len = tsm_symbol_get_width(con->sym_table, ch);
1014 if (!len) {
1015 return;
1016 } else if (len < 0) {
1017 ch = 0x0000fffd;
1018 len = 1;
1019 }
1020
1021 screen_inc_age(con);
1022
1023 if (con->cursor_y <= con->margin_bottom ||
1024 con->cursor_y >= con->size_y)
1025 last = con->margin_bottom;
1026 else
1027 last = con->size_y - 1;
1028
1029 if (con->cursor_x >= con->size_x) {
1030 if (con->flags & TSM_SCREEN_AUTO_WRAP)
1031 move_cursor(con, 0, con->cursor_y + 1);
1032 else
1033 move_cursor(con, con->size_x - 1, con->cursor_y);
1034 }
1035
1036 if (con->cursor_y > last) {
1037 move_cursor(con, con->cursor_x, last);
1038 screen_scroll_up(con, 1);
1039 }
1040
1041 screen_write(con, con->cursor_x, con->cursor_y, ch, len, attr);
1042 move_cursor(con, con->cursor_x + len, con->cursor_y);
1043 }
1044
1045 SHL_EXPORT
tsm_screen_newline(struct tsm_screen * con)1046 void tsm_screen_newline(struct tsm_screen *con)
1047 {
1048 screen_inc_age(con);
1049
1050 tsm_screen_move_down(con, 1, true);
1051 tsm_screen_move_line_home(con);
1052 }
1053
1054 SHL_EXPORT
tsm_screen_scroll_up(struct tsm_screen * con,int num)1055 void tsm_screen_scroll_up(struct tsm_screen *con, int num)
1056 {
1057 if (!num)
1058 return;
1059
1060 screen_inc_age(con);
1061
1062 screen_scroll_up(con, num);
1063 }
1064
1065 SHL_EXPORT
tsm_screen_scroll_down(struct tsm_screen * con,int num)1066 void tsm_screen_scroll_down(struct tsm_screen *con, int num)
1067 {
1068 if (!num)
1069 return;
1070
1071 screen_inc_age(con);
1072
1073 screen_scroll_down(con, num);
1074 }
1075
1076 SHL_EXPORT
tsm_screen_move_to(struct tsm_screen * con,int x,int y)1077 void tsm_screen_move_to(struct tsm_screen *con, int x, int y)
1078 {
1079 int last;
1080
1081 screen_inc_age(con);
1082
1083 if (con->flags & TSM_SCREEN_REL_ORIGIN)
1084 last = con->margin_bottom;
1085 else
1086 last = con->size_y - 1;
1087
1088 x = to_abs_x(con, x);
1089 if (x >= con->size_x)
1090 x = con->size_x - 1;
1091
1092 y = to_abs_y(con, y);
1093 if (y > last)
1094 y = last;
1095
1096 move_cursor(con, x, y);
1097 }
1098
1099 SHL_EXPORT
tsm_screen_move_up(struct tsm_screen * con,int num,bool scroll)1100 void tsm_screen_move_up(struct tsm_screen *con, int num, bool scroll)
1101 {
1102 int diff, size;
1103
1104 if (!num)
1105 return;
1106
1107 screen_inc_age(con);
1108
1109 if (con->cursor_y >= con->margin_top)
1110 size = con->margin_top;
1111 else
1112 size = 0;
1113
1114 diff = con->cursor_y - size;
1115 if (num > diff) {
1116 num -= diff;
1117 if (scroll)
1118 screen_scroll_down(con, num);
1119 move_cursor(con, con->cursor_x, size);
1120 } else {
1121 move_cursor(con, con->cursor_x, con->cursor_y - num);
1122 }
1123 }
1124
1125 SHL_EXPORT
tsm_screen_move_down(struct tsm_screen * con,int num,bool scroll)1126 void tsm_screen_move_down(struct tsm_screen *con, int num, bool scroll)
1127 {
1128 int diff, size;
1129
1130 if (!num)
1131 return;
1132
1133 screen_inc_age(con);
1134
1135 if (con->cursor_y <= con->margin_bottom)
1136 size = con->margin_bottom + 1;
1137 else
1138 size = con->size_y;
1139
1140 diff = size - con->cursor_y - 1;
1141 if (num > diff) {
1142 num -= diff;
1143 if (scroll)
1144 screen_scroll_up(con, num);
1145 move_cursor(con, con->cursor_x, size - 1);
1146 } else {
1147 move_cursor(con, con->cursor_x, con->cursor_y + num);
1148 }
1149 }
1150
1151 SHL_EXPORT
tsm_screen_move_left(struct tsm_screen * con,int num)1152 void tsm_screen_move_left(struct tsm_screen *con, int num)
1153 {
1154 int x;
1155
1156 if (!num)
1157 return;
1158
1159 screen_inc_age(con);
1160
1161 if (num > con->size_x)
1162 num = con->size_x;
1163
1164 x = con->cursor_x;
1165 if (x >= con->size_x)
1166 x = con->size_x - 1;
1167
1168 if (num > x)
1169 move_cursor(con, 0, con->cursor_y);
1170 else
1171 move_cursor(con, x - num, con->cursor_y);
1172 }
1173
1174 SHL_EXPORT
tsm_screen_move_right(struct tsm_screen * con,int num)1175 void tsm_screen_move_right(struct tsm_screen *con, int num)
1176 {
1177 if (!num)
1178 return;
1179
1180 screen_inc_age(con);
1181
1182 if (num > con->size_x)
1183 num = con->size_x;
1184
1185 if (num + con->cursor_x >= con->size_x)
1186 move_cursor(con, con->size_x - 1, con->cursor_y);
1187 else
1188 move_cursor(con, con->cursor_x + num, con->cursor_y);
1189 }
1190
1191 SHL_EXPORT
tsm_screen_move_line_end(struct tsm_screen * con)1192 void tsm_screen_move_line_end(struct tsm_screen *con)
1193 {
1194 screen_inc_age(con);
1195
1196 move_cursor(con, con->size_x - 1, con->cursor_y);
1197 }
1198
1199 SHL_EXPORT
tsm_screen_move_line_home(struct tsm_screen * con)1200 void tsm_screen_move_line_home(struct tsm_screen *con)
1201 {
1202 screen_inc_age(con);
1203
1204 move_cursor(con, 0, con->cursor_y);
1205 }
1206
1207 SHL_EXPORT
tsm_screen_tab_right(struct tsm_screen * con,int num)1208 void tsm_screen_tab_right(struct tsm_screen *con, int num)
1209 {
1210 int i, j, x;
1211
1212 if (!num)
1213 return;
1214
1215 screen_inc_age(con);
1216
1217 x = con->cursor_x;
1218 for (i = 0; i < num; ++i) {
1219 for (j = x + 1; j < con->size_x; ++j) {
1220 if (con->tab_ruler[j])
1221 break;
1222 }
1223
1224 x = j;
1225 if (x + 1 >= con->size_x)
1226 break;
1227 }
1228
1229 /* tabs never cause pending new-lines */
1230 if (x >= con->size_x)
1231 x = con->size_x - 1;
1232
1233 move_cursor(con, x, con->cursor_y);
1234 }
1235
1236 SHL_EXPORT
tsm_screen_tab_left(struct tsm_screen * con,int num)1237 void tsm_screen_tab_left(struct tsm_screen *con, int num)
1238 {
1239 int i, j, x;
1240
1241 if (!num)
1242 return;
1243
1244 screen_inc_age(con);
1245
1246 x = con->cursor_x;
1247 for (i = 0; i < num; ++i) {
1248 for (j = x - 1; j > 0; --j) {
1249 if (con->tab_ruler[j])
1250 break;
1251 }
1252
1253 if (j <= 0) {
1254 x = 0;
1255 break;
1256 }
1257 x = j;
1258 }
1259
1260 move_cursor(con, x, con->cursor_y);
1261 }
1262
1263 SHL_EXPORT
tsm_screen_insert_lines(struct tsm_screen * con,int num)1264 void tsm_screen_insert_lines(struct tsm_screen *con, int num)
1265 {
1266 int i, j, max;
1267
1268 if (!num)
1269 return;
1270
1271 if (con->cursor_y < con->margin_top ||
1272 con->cursor_y > con->margin_bottom)
1273 return;
1274
1275 screen_inc_age(con);
1276 /* TODO: more sophisticated ageing */
1277 con->age = con->age_cnt;
1278
1279 max = con->margin_bottom - con->cursor_y + 1;
1280 if (num > max)
1281 num = max;
1282
1283 struct line *cache[num];
1284
1285 for (i = 0; i < num; ++i) {
1286 cache[i] = con->lines[con->margin_bottom - i];
1287 for (j = 0; j < con->size_x; ++j)
1288 screen_cell_init(con, &cache[i]->cells[j]);
1289 if (con->cursor_y < con->vanguard)
1290 con->vanguard++;
1291 }
1292
1293 if (num < max) {
1294 memmove(&con->lines[con->cursor_y + num],
1295 &con->lines[con->cursor_y],
1296 (max - num) * sizeof(struct line*));
1297
1298 memcpy(&con->lines[con->cursor_y],
1299 cache, num * sizeof(struct line*));
1300 }
1301
1302 con->cursor_x = 0;
1303 }
1304
1305 SHL_EXPORT
tsm_screen_delete_lines(struct tsm_screen * con,int num)1306 void tsm_screen_delete_lines(struct tsm_screen *con, int num)
1307 {
1308 int i, j, max;
1309
1310 if (!num)
1311 return;
1312
1313 if (con->cursor_y < con->margin_top ||
1314 con->cursor_y > con->margin_bottom)
1315 return;
1316
1317 screen_inc_age(con);
1318 /* TODO: more sophisticated ageing */
1319 con->age = con->age_cnt;
1320
1321 max = con->margin_bottom - con->cursor_y + 1;
1322 if (num > max)
1323 num = max;
1324
1325 struct line *cache[num];
1326
1327 for (i = 0; i < num; ++i) {
1328 cache[i] = con->lines[con->cursor_y + i];
1329 for (j = 0; j < con->size_x; ++j)
1330 screen_cell_init(con, &cache[i]->cells[j]);
1331 if (con->cursor_y <= con->vanguard)
1332 con->vanguard--;
1333 }
1334
1335 if (num < max) {
1336 memmove(&con->lines[con->cursor_y],
1337 &con->lines[con->cursor_y + num],
1338 (max - num) * sizeof(struct line*));
1339
1340 memcpy(&con->lines[con->cursor_y + (max - num)],
1341 cache, num * sizeof(struct line*));
1342 }
1343
1344 con->cursor_x = 0;
1345 }
1346
1347 SHL_EXPORT
tsm_screen_insert_chars(struct tsm_screen * con,int num)1348 void tsm_screen_insert_chars(struct tsm_screen *con, int num)
1349 {
1350 struct cell *cells;
1351 int max, mv, i;
1352
1353 if (!num || !con->size_y || !con->size_x)
1354 return;
1355
1356 screen_inc_age(con);
1357 /* TODO: more sophisticated ageing */
1358 con->age = con->age_cnt;
1359
1360 if (con->cursor_x >= con->size_x)
1361 con->cursor_x = con->size_x - 1;
1362 if (con->cursor_y >= con->size_y)
1363 con->cursor_y = con->size_y - 1;
1364
1365 max = con->size_x - con->cursor_x;
1366 if (num > max)
1367 num = max;
1368 mv = max - num;
1369
1370 cells = con->lines[con->cursor_y]->cells;
1371 if (mv)
1372 memmove(&cells[con->cursor_x + num],
1373 &cells[con->cursor_x],
1374 mv * sizeof(*cells));
1375
1376 for (i = 0; i < num; ++i)
1377 screen_cell_init(con, &cells[con->cursor_x + i]);
1378 }
1379
1380 SHL_EXPORT
tsm_screen_delete_chars(struct tsm_screen * con,int num)1381 void tsm_screen_delete_chars(struct tsm_screen *con, int num)
1382 {
1383 struct cell *cells;
1384 int max, mv, i;
1385
1386 if (!num || !con->size_y || !con->size_x)
1387 return;
1388
1389 screen_inc_age(con);
1390 /* TODO: more sophisticated ageing */
1391 con->age = con->age_cnt;
1392
1393 if (con->cursor_x >= con->size_x)
1394 con->cursor_x = con->size_x - 1;
1395 if (con->cursor_y >= con->size_y)
1396 con->cursor_y = con->size_y - 1;
1397
1398 max = con->size_x - con->cursor_x;
1399 if (num > max)
1400 num = max;
1401 mv = max - num;
1402
1403 cells = con->lines[con->cursor_y]->cells;
1404 if (mv)
1405 memmove(&cells[con->cursor_x],
1406 &cells[con->cursor_x + num],
1407 mv * sizeof(*cells));
1408
1409 for (i = 0; i < num; ++i)
1410 screen_cell_init(con, &cells[con->cursor_x + mv + i]);
1411 }
1412
1413 SHL_EXPORT
tsm_screen_erase_cursor(struct tsm_screen * con)1414 void tsm_screen_erase_cursor(struct tsm_screen *con)
1415 {
1416 int x;
1417
1418 screen_inc_age(con);
1419
1420 if (con->cursor_x >= con->size_x)
1421 x = con->size_x - 1;
1422 else
1423 x = con->cursor_x;
1424
1425 screen_erase_region(con, x, con->cursor_y, x, con->cursor_y, false);
1426 }
1427
1428 SHL_EXPORT
tsm_screen_erase_chars(struct tsm_screen * con,int num)1429 void tsm_screen_erase_chars(struct tsm_screen *con, int num)
1430 {
1431 int x;
1432
1433 if (!num)
1434 return;
1435
1436 screen_inc_age(con);
1437
1438 if (con->cursor_x >= con->size_x)
1439 x = con->size_x - 1;
1440 else
1441 x = con->cursor_x;
1442
1443 screen_erase_region(con, x, con->cursor_y, x + num - 1, con->cursor_y,
1444 false);
1445 }
1446
1447 SHL_EXPORT
tsm_screen_erase_cursor_to_end(struct tsm_screen * con,bool protect)1448 void tsm_screen_erase_cursor_to_end(struct tsm_screen *con, bool protect)
1449 {
1450 int x;
1451
1452 screen_inc_age(con);
1453
1454 if (con->cursor_x >= con->size_x)
1455 x = con->size_x - 1;
1456 else
1457 x = con->cursor_x;
1458
1459 screen_erase_region(con, x, con->cursor_y, con->size_x - 1,
1460 con->cursor_y, protect);
1461 }
1462
1463 SHL_EXPORT
tsm_screen_erase_home_to_cursor(struct tsm_screen * con,bool protect)1464 void tsm_screen_erase_home_to_cursor(struct tsm_screen *con, bool protect)
1465 {
1466 screen_inc_age(con);
1467
1468 screen_erase_region(con, 0, con->cursor_y, con->cursor_x,
1469 con->cursor_y, protect);
1470 }
1471
1472 SHL_EXPORT
tsm_screen_erase_current_line(struct tsm_screen * con,bool protect)1473 void tsm_screen_erase_current_line(struct tsm_screen *con, bool protect)
1474 {
1475 screen_inc_age(con);
1476
1477 screen_erase_region(con, 0, con->cursor_y, con->size_x - 1,
1478 con->cursor_y, protect);
1479 if (con->cursor_y == con->vanguard)
1480 con->vanguard--;
1481 }
1482
1483 SHL_EXPORT
tsm_screen_erase_screen_to_cursor(struct tsm_screen * con,bool protect)1484 void tsm_screen_erase_screen_to_cursor(struct tsm_screen *con, bool protect)
1485 {
1486 screen_inc_age(con);
1487
1488 screen_erase_region(con, 0, 0, con->cursor_x, con->cursor_y, protect);
1489 if (con->cursor_y > con->vanguard)
1490 con->vanguard = 0;
1491 }
1492
1493 SHL_EXPORT
tsm_screen_erase_cursor_to_screen(struct tsm_screen * con,bool protect)1494 void tsm_screen_erase_cursor_to_screen(struct tsm_screen *con, bool protect)
1495 {
1496 int x;
1497
1498 screen_inc_age(con);
1499
1500 if (con->cursor_x >= con->size_x)
1501 x = con->size_x - 1;
1502 else
1503 x = con->cursor_x;
1504
1505 screen_erase_region(con, x, con->cursor_y, con->size_x - 1,
1506 con->size_y - 1, protect);
1507 if (con->cursor_y < con->vanguard)
1508 con->vanguard = con->cursor_y;
1509 }
1510
1511 SHL_EXPORT
tsm_screen_erase_screen(struct tsm_screen * con,bool protect)1512 void tsm_screen_erase_screen(struct tsm_screen *con, bool protect)
1513 {
1514 screen_inc_age(con);
1515
1516 screen_erase_region(con, 0, 0, con->size_x - 1, con->size_y - 1,
1517 protect);
1518 con->vanguard = 0;
1519 }
1520