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