1 /*
2  * libtsm - Screen Management
3  *
4  * Copyright (c) 2011-2013 David Herrmann <dh.herrmann@gmail.com>
5  * Copyright (c) 2016-2017 Bjorn Stahl <contact@arcan-fe.com>
6  *
7  * Permission is hereby granted, free of charge, to any person obtaining
8  * a copy of this software and associated documentation files
9  * (the "Software"), to deal in the Software without restriction, including
10  * without limitation the rights to use, copy, modify, merge, publish,
11  * distribute, sublicense, and/or sell copies of the Software, and to
12  * permit persons to whom the Software is furnished to do so, subject to
13  * the following conditions:
14  *
15  * The above copyright notice and this permission notice shall be included
16  * in all copies or substantial portions of the Software.
17  *
18  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
19  * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
20  * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
21  * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
22  * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
23  * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
24  * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
25  */
26 
27 /*
28  * Screen Management
29  * This provides the abstracted screen management. It does not do any
30  * terminal-emulation, instead it provides a resizable table of cells. You can
31  * insert, remove and modify the cells freely.
32  * A screen has always a fixed, but changeable, width and height. This defines
33  * the number of columns and rows. The screen doesn't care for pixels, glyphs or
34  * framebuffers. The screen only contains information about each cell.
35  *
36  * Screens are the logical model behind a real screen of a terminal emulator.
37  * Users usually allocate a screen for each terminal-emulator they run. All they
38  * have to do is render the screen onto their widget on each change and forward
39  * any widget-events to the screen.
40  *
41  * The screen object already includes scrollback-buffers, selection support and
42  * more. This simplifies terminal emulators a lot, but also prevents them from
43  * accessing the real screen data. However, terminal emulators should have no
44  * reason to access the data directly. The screen API should provide everything
45  * they need.
46  *
47  * AGEING:
48  * Each cell, line and screen has an "age" field. This field describes when it
49  * was changed the last time. After drawing a screen, the current screen age is
50  * returned. This allows users to skip drawing specific cells, if their
51  * framebuffer was already drawn with a newer age than a given cell.
52  * However, the screen-age might overflow. This is properly detected and causes
53  * drawing functions to return "0" as age. Users must reset all their
54  * framebuffer ages then. Otherwise, further drawing operations might
55  * incorrectly skip cells.
56  * Furthermore, if a cell has age "0", it means it _has_ to be drawn. No ageing
57  * information is available.
58  *
59  * Simplifications:
60  * Design mismatches between tui and screen features exist in some places, and
61  * as a result, parts of this could be simplified somewhat.
62  *
63  * 1. Drop age:ing (cell front-back buffers removes that need)
64  * 2. Drop selection (the code here is murky anyhow)
65  * 3. Drop alt-screen/alt-lines, there is already support for multiple screens
66  *    on the tui side
67  * 4. Drop the hash table / combiner glyph tracking, glyph transformations are
68  *    made as part of shaping
69  */
70 
71 #include <errno.h>
72 #include <inttypes.h>
73 #include <stdbool.h>
74 #include <stdlib.h>
75 #include <stdio.h>
76 #include <string.h>
77 #include <ctype.h>
78 #include "../../arcan_shmif.h"
79 #include "../../arcan_tui.h"
80 #include "libtsm.h"
81 
82 typedef void* TTF_Font;
83 #include "libtsm_int.h"
84 
inc_age(struct tsm_screen * con)85 static void inc_age(struct tsm_screen *con)
86 {
87 	if (!++con->age_cnt) {
88 		con->age_reset = 1;
89 		++con->age_cnt;
90 	}
91 }
92 
tsm_screen_inc_age(struct tsm_screen * con)93 void tsm_screen_inc_age(struct tsm_screen *con)
94 {
95 	if (!++con->age_cnt) {
96 		con->age_reset = 1;
97 		++con->age_cnt;
98 	}
99 }
100 
get_cursor_cell(struct tsm_screen * con)101 static struct cell *get_cursor_cell(struct tsm_screen *con)
102 {
103 	unsigned int cur_x, cur_y;
104 
105 	cur_x = con->cursor_x;
106 	if (cur_x >= con->size_x)
107 		cur_x = con->size_x - 1;
108 
109 	cur_y = con->cursor_y;
110 	if (cur_y >= con->size_y)
111 		cur_y = con->size_y - 1;
112 
113 	return &con->lines[cur_y]->cells[cur_x];
114 }
115 
move_cursor(struct tsm_screen * con,unsigned int x,unsigned int y)116 static void move_cursor(struct tsm_screen *con, unsigned int x, unsigned int y)
117 {
118 	struct cell *c;
119 
120 	con->cursor_x = x;
121 	con->cursor_y = y;
122 }
123 
124 /*
125  * this is to not age cells where there isn't a difference, saves some poor
126  * use of _clear screen + refresh even when a small portion has changed
127  */
cell_init_chg(struct tsm_screen * con,struct cell * cell)128 static void cell_init_chg(struct tsm_screen *con, struct cell *cell)
129 {
130 	if (cell->ch == 0 && tui_attr_equal(cell->attr, con->def_attr)){
131 		return;
132 	}
133 
134 	cell->ch = 0;
135 	cell->width = 1;
136 	cell->age = con->age_cnt;
137 	memcpy(&cell->attr, &con->def_attr, sizeof(cell->attr));
138 }
139 
cell_init(struct tsm_screen * con,struct cell * cell)140 static void cell_init(struct tsm_screen *con, struct cell *cell)
141 {
142 	cell->ch = 0;
143 	cell->width = 1;
144 	cell->age = con->age_cnt;
145 	memcpy(&cell->attr, &con->def_attr, sizeof(cell->attr));
146 }
147 
line_new(struct tsm_screen * con,struct line ** out,unsigned int width)148 static int line_new(struct tsm_screen *con, struct line **out,
149 		    unsigned int width)
150 {
151 	struct line *line;
152 	unsigned int i;
153 
154 	if (!width)
155 		return -EINVAL;
156 
157 	line = malloc(sizeof(*line));
158 	if (!line)
159 		return -ENOMEM;
160 	line->next = NULL;
161 	line->prev = NULL;
162 	line->size = width;
163 	line->age = con->age_cnt;
164 
165 	line->cells = malloc(sizeof(struct cell) * width);
166 	if (!line->cells) {
167 		free(line);
168 		return -ENOMEM;
169 	}
170 
171 	for (i = 0; i < width; ++i)
172 		cell_init(con, &line->cells[i]);
173 
174 	*out = line;
175 	return 0;
176 }
177 
line_free(struct line * line)178 static void line_free(struct line *line)
179 {
180 	free(line->cells);
181 	free(line);
182 }
183 
line_resize(struct tsm_screen * con,struct line * line,unsigned int width)184 static int line_resize(struct tsm_screen *con, struct line *line,
185 		       unsigned int width)
186 {
187 	struct cell *tmp;
188 
189 	if (!line || !width)
190 		return -EINVAL;
191 
192 	if (line->size < width) {
193 		tmp = realloc(line->cells, width * sizeof(struct cell));
194 		if (!tmp)
195 			return -ENOMEM;
196 
197 		line->cells = tmp;
198 
199 		while (line->size < width) {
200 			cell_init(con, &line->cells[line->size]);
201 			++line->size;
202 		}
203 	}
204 
205 	return 0;
206 }
207 
208 /* This links the given line into the scrollback-buffer */
link_to_scrollback(struct tsm_screen * con,struct line * line)209 static void link_to_scrollback(struct tsm_screen *con, struct line *line)
210 {
211 	struct line *tmp;
212 
213 	con->age = con->age_cnt;
214 
215 	if (con->sb_max == 0) {
216 		if (con->sel_active) {
217 			if (con->sel_start.line == line) {
218 				con->sel_start.line = NULL;
219 				con->sel_start.y = SELECTION_TOP;
220 			}
221 			if (con->sel_end.line == line) {
222 				con->sel_end.line = NULL;
223 				con->sel_end.y = SELECTION_TOP;
224 			}
225 		}
226 		line_free(line);
227 		return;
228 	}
229 
230 	/* Remove a line from the scrollback buffer if it reaches its maximum.
231 	 * We must take care to correctly keep the current position as the new
232 	 * line is linked in after we remove the top-most line here.
233 	 * sb_max == 0 is tested earlier so we can assume sb_max > 0 here. In
234 	 * other words, buf->sb_first is a valid line if sb_count >= sb_max. */
235 	if (con->sb_count >= con->sb_max) {
236 		tmp = con->sb_first;
237 		con->sb_first = tmp->next;
238 		if (tmp->next)
239 			tmp->next->prev = NULL;
240 		else
241 			con->sb_last = NULL;
242 		--con->sb_count;
243 
244 		/* (position == tmp && !next) means we have sb_max=1 so set
245 		 * position to the new line. Otherwise, set to new first line.
246 		 * If position!=tmp and we have a fixed-position then nothing
247 		 * needs to be done because we can stay at the same line. If we
248 		 * have no fixed-position, we need to set the position to the
249 		 * next inserted line, which can be "line", too. */
250 		if (con->sb_pos) {
251 			if (con->sb_pos == tmp ||
252 			    !(con->flags & TSM_SCREEN_FIXED_POS)) {
253 				if (con->sb_pos->next)
254 					con->sb_pos = con->sb_pos->next;
255 				else
256 					con->sb_pos = line;
257 			}
258 		}
259 
260 		if (con->sel_active) {
261 			if (con->sel_start.line == tmp) {
262 				con->sel_start.line = NULL;
263 				con->sel_start.y = SELECTION_TOP;
264 			}
265 			if (con->sel_end.line == tmp) {
266 				con->sel_end.line = NULL;
267 				con->sel_end.y = SELECTION_TOP;
268 			}
269 		}
270 		line_free(tmp);
271 	}
272 
273 	line->sb_id = ++con->sb_last_id;
274 	line->next = NULL;
275 	line->prev = con->sb_last;
276 	if (con->sb_last)
277 		con->sb_last->next = line;
278 	else
279 		con->sb_first = line;
280 	con->sb_last = line;
281 	++con->sb_count;
282 }
283 
screen_scroll_up(struct tsm_screen * con,unsigned int num)284 static int screen_scroll_up(struct tsm_screen *con, unsigned int num)
285 {
286 	unsigned int i, j, max, pos;
287 	int ret;
288 
289 	if (!num)
290 		return 0;
291 
292 	con->age = con->age_cnt;
293 
294 	max = con->margin_bottom + 1 - con->margin_top;
295 	if (num > max)
296 		num = max;
297 
298 	/* We cache lines on the stack to speed up the scrolling. However, if
299 	 * num is too big we might get overflows here so use recursion if num
300 	 * exceeds a hard-coded limit.
301 	 * 128 seems to be a sane limit that should never be reached but should
302 	 * also be small enough so we do not get stack overflows. */
303 	if (num > 128) {
304 		screen_scroll_up(con, 128);
305 		return screen_scroll_up(con, num - 128);
306 	}
307 	struct line *cache[num];
308 
309 	for (i = 0; i < num; ++i) {
310 		pos = con->margin_top + i;
311 		if (!(con->flags & TSM_SCREEN_ALTERNATE))
312 			ret = line_new(con, &cache[i], con->size_x);
313 		else
314 			ret = -EAGAIN;
315 
316 		if (!ret) {
317 			link_to_scrollback(con, con->lines[pos]);
318 		} else {
319 			cache[i] = con->lines[pos];
320 			for (j = 0; j < con->size_x; ++j)
321 				cell_init(con, &cache[i]->cells[j]);
322 		}
323 		con->vanguard--;
324 	}
325 
326 	if (con->vanguard < 0)
327 		con->vanguard = 0;
328 
329 	if (num < max) {
330 		memmove(&con->lines[con->margin_top],
331 			&con->lines[con->margin_top + num],
332 			(max - num) * sizeof(struct line*));
333 	}
334 
335 	memcpy(&con->lines[con->margin_top + (max - num)],
336 	       cache, num * sizeof(struct line*));
337 
338 	if (con->sel_active) {
339 		if (!con->sel_start.line && con->sel_start.y >= 0) {
340 			con->sel_start.y -= num;
341 			if (con->sel_start.y < 0) {
342 				con->sel_start.line = con->sb_last;
343 				while (con->sel_start.line && ++con->sel_start.y < 0)
344 					con->sel_start.line = con->sel_start.line->prev;
345 				con->sel_start.y = SELECTION_TOP;
346 			}
347 		}
348 		if (!con->sel_end.line && con->sel_end.y >= 0) {
349 			con->sel_end.y -= num;
350 			if (con->sel_end.y < 0) {
351 				con->sel_end.line = con->sb_last;
352 				while (con->sel_end.line && ++con->sel_end.y < 0)
353 					con->sel_end.line = con->sel_end.line->prev;
354 				con->sel_end.y = SELECTION_TOP;
355 			}
356 		}
357 	}
358 	return num;
359 }
360 
screen_scroll_down(struct tsm_screen * con,unsigned int num)361 static int screen_scroll_down(struct tsm_screen *con, unsigned int num)
362 {
363 	unsigned int i, j, max;
364 
365 	if (!num)
366 		return 0;
367 
368 	con->age = con->age_cnt;
369 
370 	max = con->margin_bottom + 1 - con->margin_top;
371 	if (num > max)
372 		num = max;
373 
374 	/* see screen_scroll_up() for an explanation */
375 	if (num > 128) {
376 		screen_scroll_down(con, 128);
377 		return screen_scroll_down(con, num - 128);
378 	}
379 	struct line *cache[num];
380 
381 	for (i = 0; i < num; ++i) {
382 		cache[i] = con->lines[con->margin_bottom - i];
383 		for (j = 0; j < con->size_x; ++j)
384 			cell_init(con, &cache[i]->cells[j]);
385 		con->vanguard++;
386 	}
387 
388 	if (con->vanguard >= con->size_y)
389 		con->vanguard = con->size_y - 1;
390 
391 	if (num < max) {
392 		memmove(&con->lines[con->margin_top + num],
393 			&con->lines[con->margin_top],
394 			(max - num) * sizeof(struct line*));
395 	}
396 
397 	memcpy(&con->lines[con->margin_top],
398 	       cache, num * sizeof(struct line*));
399 
400 	if (con->sel_active) {
401 		if (!con->sel_start.line && con->sel_start.y >= 0)
402 			con->sel_start.y += num;
403 		if (!con->sel_end.line && con->sel_end.y >= 0)
404 			con->sel_end.y += num;
405 	}
406 	return num;
407 }
408 
screen_write(struct tsm_screen * con,unsigned int x,unsigned int y,tsm_symbol_t ch,unsigned int len,const struct tui_screen_attr * attr)409 static void screen_write(struct tsm_screen *con, unsigned int x,
410 			  unsigned int y, tsm_symbol_t ch, unsigned int len,
411 			  const struct tui_screen_attr *attr)
412 {
413 	struct line *line;
414 	int i;
415 
416 	if (!len)
417 		return;
418 
419 	if (x >= con->size_x || y >= con->size_y) {
420 		return;
421 	}
422 
423 	line = con->lines[y];
424 
425 	if ((con->flags & TSM_SCREEN_INSERT_MODE) &&
426 	    (int)x < ((int)con->size_x - len)) {
427 		line->age = con->age_cnt;
428 		memmove(&line->cells[x + len], &line->cells[x],
429 			sizeof(struct cell) * (con->size_x - len - x));
430 	}
431 
432 	line->cells[x].age = con->age_cnt;
433 	line->cells[x].ch = ch;
434 	line->cells[x].width = len;
435 	memcpy(&line->cells[x].attr, attr, sizeof(*attr));
436 
437 	for (i = 1; i < len && i + x < con->size_x; ++i) {
438 		line->cells[x + i].age = con->age_cnt;
439 		line->cells[x + i].width = 0;
440 	}
441 
442 	if (y > con->vanguard)
443 		con->vanguard = y;
444 }
445 
tsm_screen_erase_region(struct tsm_screen * con,unsigned int x_from,unsigned int y_from,unsigned int x_to,unsigned int y_to,bool protect)446 void tsm_screen_erase_region(struct tsm_screen *con,
447 				 unsigned int x_from,
448 				 unsigned int y_from,
449 				 unsigned int x_to,
450 				 unsigned int y_to,
451 				 bool protect)
452 {
453 	unsigned int to;
454 	struct line *line;
455 
456 	inc_age(con);
457 	con->age = con->age_cnt;
458 
459 	if (y_to >= con->size_y)
460 		y_to = con->size_y - 1;
461 	if (x_to >= con->size_x)
462 		x_to = con->size_x - 1;
463 
464 	for ( ; y_from <= y_to; ++y_from) {
465 		line = con->lines[y_from];
466 		if (!line) {
467 			x_from = 0;
468 			continue;
469 		}
470 
471 		if (y_from == y_to)
472 			to = x_to;
473 		else
474 			to = con->size_x - 1;
475 		for ( ; x_from <= to; ++x_from) {
476 			if (protect && TUI_HAS_ATTR(line->cells[x_from].attr, TUI_ATTR_PROTECT))
477 				continue;
478 
479 			cell_init_chg(con, &line->cells[x_from]);
480 		}
481 		x_from = 0;
482 	}
483 }
484 
to_abs_x(struct tsm_screen * con,unsigned int x)485 static inline unsigned int to_abs_x(struct tsm_screen *con, unsigned int x)
486 {
487 	return x;
488 }
489 
to_abs_y(struct tsm_screen * con,unsigned int y)490 static inline unsigned int to_abs_y(struct tsm_screen *con, unsigned int y)
491 {
492 	if (!(con->flags & TSM_SCREEN_REL_ORIGIN))
493 		return y;
494 
495 	return con->margin_top + y;
496 }
497 
498 SHL_EXPORT
tsm_screen_new(struct tsm_screen ** out,tsm_log_t log,void * log_data)499 int tsm_screen_new(struct tsm_screen **out, tsm_log_t log, void *log_data)
500 {
501 	struct tsm_screen *con;
502 	int ret;
503 	unsigned int i;
504 
505 	if (!out)
506 		return -EINVAL;
507 
508 	con = malloc(sizeof(*con));
509 	if (!con)
510 		return -ENOMEM;
511 
512 	memset(con, 0, sizeof(*con));
513 	con->ref = 1;
514 	con->age_cnt = 1;
515 	con->age = con->age_cnt;
516 	con->def_attr.aflags = TUI_ATTR_COLOR_INDEXED;
517 	con->def_attr.fr = TUI_COL_TEXT;
518 	con->def_attr.br = TUI_COL_TEXT;
519 
520 	ret = tsm_symbol_table_new(&con->sym_table);
521 	if (ret)
522 		goto err_free;
523 
524 	ret = tsm_screen_resize(con, 80, 24);
525 	if (ret)
526 		goto err_free;
527 
528 	*out = con;
529 
530 	return 0;
531 
532 err_free:
533 	for (i = 0; i < con->line_num; ++i) {
534 		line_free(con->main_lines[i]);
535 		line_free(con->alt_lines[i]);
536 	}
537 	free(con->main_lines);
538 	free(con->alt_lines);
539 	free(con->tab_ruler);
540 	tsm_symbol_table_unref(con->sym_table);
541 	free(con);
542 	return ret;
543 }
544 
545 SHL_EXPORT
tsm_screen_ref(struct tsm_screen * con)546 void tsm_screen_ref(struct tsm_screen *con)
547 {
548 	if (!con)
549 		return;
550 
551 	++con->ref;
552 }
553 
554 SHL_EXPORT
tsm_screen_unref(struct tsm_screen * con)555 void tsm_screen_unref(struct tsm_screen *con)
556 {
557 	unsigned int i;
558 
559 	if (!con || !con->ref || --con->ref)
560 		return;
561 
562 	tsm_screen_clear_sb(con);
563 
564 	for (i = 0; i < con->line_num; ++i) {
565 		line_free(con->main_lines[i]);
566 		line_free(con->alt_lines[i]);
567 	}
568 	free(con->main_lines);
569 	free(con->alt_lines);
570 	free(con->tab_ruler);
571 	tsm_symbol_table_unref(con->sym_table);
572 	free(con);
573 }
574 
575 SHL_EXPORT
tsm_screen_get_width(struct tsm_screen * con)576 unsigned int tsm_screen_get_width(struct tsm_screen *con)
577 {
578 	if (!con)
579 		return 0;
580 
581 	return con->size_x;
582 }
583 
584 SHL_EXPORT
tsm_screen_get_height(struct tsm_screen * con)585 unsigned int tsm_screen_get_height(struct tsm_screen *con)
586 {
587 	if (!con)
588 		return 0;
589 
590 	return con->size_y;
591 }
592 
593 SHL_EXPORT
tsm_screen_resize(struct tsm_screen * con,unsigned int x,unsigned int y)594 int tsm_screen_resize(
595 	struct tsm_screen *con, unsigned int x, unsigned int y)
596 {
597 	struct line **cache;
598 	unsigned int i, j, width, start;
599 	int ret, diff;
600 	bool *tab_ruler;
601 
602 	if (!con || !x || !y)
603 		return -EINVAL;
604 
605 	inc_age(con);
606 
607 	if (con->size_x == x && con->size_y == y)
608 		return 0;
609 
610 	/* First make sure the line buffer is big enough for our new screen.
611 	 * That is, allocate all new lines and make sure each line has enough
612 	 * cells to hold the new screen or the current screen. If we fail, we
613 	 * can safely return -ENOMEM and the buffer is still valid. We must
614 	 * allocate the new lines to at least the same size as the current
615 	 * lines. Otherwise, if this function fails in later turns, we will have
616 	 * invalid lines in the buffer. */
617 	if (y > con->line_num) {
618 		/* resize main buffer */
619 		cache = realloc(con->main_lines, sizeof(struct line*) * y);
620 		if (!cache)
621 			return -ENOMEM;
622 
623 		if (con->lines == con->main_lines)
624 			con->lines = cache;
625 		con->main_lines = cache;
626 
627 		/* resize alt buffer */
628 		cache = realloc(con->alt_lines, sizeof(struct line*) * y);
629 		if (!cache)
630 			return -ENOMEM;
631 
632 		if (con->lines == con->alt_lines)
633 			con->lines = cache;
634 		con->alt_lines = cache;
635 
636 		/* allocate new lines */
637 		if (x > con->size_x)
638 			width = x;
639 		else
640 			width = con->size_x;
641 
642 		while (con->line_num < y) {
643 			ret = line_new(con, &con->main_lines[con->line_num],
644 				       width);
645 			if (ret)
646 				return ret;
647 
648 			ret = line_new(con, &con->alt_lines[con->line_num],
649 				       width);
650 			if (ret) {
651 				line_free(con->main_lines[con->line_num]);
652 				return ret;
653 			}
654 
655 			++con->line_num;
656 		}
657 	}
658 
659 	/* Resize all lines in the buffer if we increase screen width. This
660 	 * will guarantee that all lines are big enough so we can resize the
661 	 * buffer without reallocating them later. */
662 	if (x > con->size_x) {
663 		tab_ruler = realloc(con->tab_ruler, sizeof(bool) * x);
664 		if (!tab_ruler)
665 			return -ENOMEM;
666 		con->tab_ruler = tab_ruler;
667 
668 		for (i = 0; i < con->line_num; ++i) {
669 			ret = line_resize(con, con->main_lines[i], x);
670 			if (ret)
671 				return ret;
672 
673 			ret = line_resize(con, con->alt_lines[i], x);
674 			if (ret)
675 				return ret;
676 		}
677 	}
678 
679 	/* clear expansion/padding area */
680 	start = x;
681 	if (x > con->size_x)
682 		start = con->size_x;
683 	for (j = 0; j < con->line_num; ++j) {
684 		/* main-lines may go into SB, so clear all cells */
685 		i = 0;
686 		if (j < con->size_y)
687 			i = start;
688 
689 		for ( ; i < con->main_lines[j]->size; ++i)
690 			cell_init(con, &con->main_lines[j]->cells[i]);
691 
692 		/* alt-lines never go into SB, only clear visible cells */
693 		i = 0;
694 		if (j < con->size_y)
695 			i = con->size_x;
696 
697 		for ( ; i < x; ++i)
698 			cell_init(con, &con->alt_lines[j]->cells[i]);
699 	}
700 
701 	/* xterm destroys margins on resize, so do we */
702 	con->margin_top = 0;
703 	con->margin_bottom = con->size_y - 1;
704 
705 	/* reset tabs */
706 	for (i = 0; i < x; ++i) {
707 		if (i % 8 == 0)
708 			con->tab_ruler[i] = true;
709 		else
710 			con->tab_ruler[i] = false;
711 	}
712 
713 	/* We need to adjust x-size first as screen_scroll_up() and friends may
714 	 * have to reallocate lines. The y-size is adjusted after them to avoid
715 	 * missing lines when shrinking y-size.
716 	 * We need to carefully look for the functions that we call here as they
717 	 * have stronger invariants as when called normally. */
718 
719 	con->size_x = x;
720 	if (con->cursor_x >= con->size_x)
721 		move_cursor(con, con->size_x - 1, con->cursor_y);
722 
723 	/* scroll buffer if screen height shrinks */
724 	diff = ((int)con->size_y - (int)y) - ((int)con->size_y - (con->vanguard + 1));
725 	if (diff > 0){
726 		screen_scroll_up(con, diff);
727 		if (con->cursor_y > diff)
728 			move_cursor(con, con->cursor_x, con->cursor_y - diff);
729 		else
730 			move_cursor(con, con->cursor_x, 0);
731 	}
732 
733 	con->size_y = y;
734 	con->margin_bottom = con->size_y - 1;
735 	if (con->cursor_y >= con->size_y)
736 		move_cursor(con, con->cursor_x, con->size_y - 1);
737 
738 	return 0;
739 }
740 
ascii_test(struct tsm_screen * con,tsm_symbol_t inch)741 static int ascii_test(struct tsm_screen *con, tsm_symbol_t inch)
742 {
743 	size_t len;
744 	const uint32_t *ch = tsm_symbol_get(con->sym_table, &inch, &len);
745 
746 	size_t u8w = tsm_ucs4_get_width(*ch)+1;
747 	char u8_ch[u8w];
748 	size_t nch = tsm_ucs4_to_utf8(*ch, u8_ch);
749 
750 	return !(nch == 1 && isspace(u8_ch[0]));
751 }
752 
tsm_attr_at_cursor(struct tsm_screen * con,tsm_symbol_t * out)753 struct tui_screen_attr tsm_attr_at_cursor(
754 	struct tsm_screen *con, tsm_symbol_t* out)
755 {
756 	struct cell* cell = get_cursor_cell(con);
757 	*out = 0;
758 
759 	if (!cell)
760 		return con->def_attr;
761 
762 	*out = cell->ch;
763 
764 	return con->def_attr;
765 }
766 
767 SHL_EXPORT
tsm_screen_get_word(struct tsm_screen * con,unsigned x,unsigned y,unsigned * sx,unsigned * sy,unsigned * ex,unsigned * ey)768 int tsm_screen_get_word(struct tsm_screen *con,
769 								unsigned x, unsigned y,
770 								unsigned *sx, unsigned *sy,
771 								unsigned *ex, unsigned *ey)
772 {
773 	if (y > con->size_y-1)
774 		return -EINVAL;
775 
776 	struct line *cur = con->lines[y];
777 
778 	int cy = y;
779 
780 	if (!cur || x >= cur->size)
781 		return -EINVAL;
782 
783 	*sx = x; *sy = y; *ex = x; *ey = y;
784 
785 	struct line *wl = cur;
786 	if (!ascii_test(con, wl->cells[*sx].ch))
787 		return -EINVAL;
788 
789 /* scan left */
790 	for(;;){
791 		int tx = *sx;
792 /* wrap around back */
793 		if (tx == 0){
794 			wl = wl->prev;
795 			if (!wl || !ascii_test(con, wl->cells[wl->size-1].ch))
796 				break;
797 
798 			*sy--;
799 			*sx = wl->size - 1;
800 			continue;
801 		}
802 		else{
803 			tx = tx - 1;
804 			if (!ascii_test(con, wl->cells[tx].ch))
805 				break;
806 			*sx = tx;
807 		}
808 	}
809 
810 	wl = cur;
811 /* scan right */
812 	for(;;){
813 		int tx = *ex;
814 		if (tx == wl->size-1){
815 			wl = wl->next;
816 			if (!wl || !ascii_test(con, wl->cells[0].ch))
817 				break;
818 
819 			*ey++;
820 			*ex = 0;
821 		}
822 		else{
823 			tx = tx+1;
824 			if (!ascii_test(con, wl->cells[tx].ch))
825 				break;
826 			*ex = tx;
827 		}
828 	}
829 
830 	return (*sx != *ex || *sy != *ey) ? 0 : -EINVAL;
831 }
832 
833 /* use line_num and sb_count to figure out where we are */
834 
835 SHL_EXPORT
tsm_screen_set_margins(struct tsm_screen * con,unsigned int top,unsigned int bottom)836 int tsm_screen_set_margins(struct tsm_screen *con,
837 			       unsigned int top, unsigned int bottom)
838 {
839 	unsigned int upper, lower;
840 
841 	if (!con)
842 		return -EINVAL;
843 
844 	if (!top)
845 		top = 1;
846 
847 	if (bottom <= top) {
848 		upper = 0;
849 		lower = con->size_y - 1;
850 	} else if (bottom > con->size_y) {
851 		upper = 0;
852 		lower = con->size_y - 1;
853 	} else {
854 		upper = top - 1;
855 		lower = bottom - 1;
856 	}
857 
858 	con->margin_top = upper;
859 	con->margin_bottom = lower;
860 	return 0;
861 }
862 
863 /* set maximum scrollback buffer size */
864 SHL_EXPORT
tsm_screen_set_max_sb(struct tsm_screen * con,unsigned int max)865 void tsm_screen_set_max_sb(struct tsm_screen *con,
866 			       unsigned int max)
867 {
868 	struct line *line;
869 
870 	if (!con)
871 		return;
872 
873 	inc_age(con);
874 	con->age = con->age_cnt;
875 
876 	while (con->sb_count > max) {
877 		line = con->sb_first;
878 		con->sb_first = line->next;
879 		if (line->next)
880 			line->next->prev = NULL;
881 		else
882 			con->sb_last = NULL;
883 		con->sb_count--;
884 
885 		/* We treat fixed/unfixed position the same here because we
886 		 * remove lines from the TOP of the scrollback buffer. */
887 		if (con->sb_pos == line)
888 			con->sb_pos = con->sb_first;
889 
890 		if (con->sel_active) {
891 			if (con->sel_start.line == line) {
892 				con->sel_start.line = NULL;
893 				con->sel_start.y = SELECTION_TOP;
894 			}
895 			if (con->sel_end.line == line) {
896 				con->sel_end.line = NULL;
897 				con->sel_end.y = SELECTION_TOP;
898 			}
899 		}
900 		line_free(line);
901 	}
902 
903 	con->sb_max = max;
904 }
905 
906 /* clear scrollback buffer */
907 SHL_EXPORT
tsm_screen_clear_sb(struct tsm_screen * con)908 void tsm_screen_clear_sb(struct tsm_screen *con)
909 {
910 	struct line *iter, *tmp;
911 
912 	if (!con)
913 		return;
914 
915 	inc_age(con);
916 	con->age = con->age_cnt;
917 
918 	for (iter = con->sb_first; iter; ) {
919 		tmp = iter;
920 		iter = iter->next;
921 		line_free(tmp);
922 	}
923 
924 	con->sb_first = NULL;
925 	con->sb_last = NULL;
926 	con->sb_count = 0;
927 	con->sb_pos = NULL;
928 
929 	if (con->sel_active) {
930 		if (con->sel_start.line) {
931 			con->sel_start.line = NULL;
932 			con->sel_start.y = SELECTION_TOP;
933 		}
934 		if (con->sel_end.line) {
935 			con->sel_end.line = NULL;
936 			con->sel_end.y = SELECTION_TOP;
937 		}
938 	}
939 }
940 
941 SHL_EXPORT
tsm_screen_sb_up(struct tsm_screen * con,unsigned int num)942 int tsm_screen_sb_up(struct tsm_screen *con, unsigned int num)
943 {
944 	if (!con || !num)
945 		return 0;
946 
947 	unsigned num2 = num;
948 	inc_age(con);
949 	con->age = con->age_cnt;
950 
951 	while (num2--) {
952 		if (con->sb_pos) {
953 			if (!con->sb_pos->prev)
954 				return 0;
955 
956 			con->sb_pos = con->sb_pos->prev;
957 		} else if (!con->sb_last) {
958 			return -(num - num2);
959 		} else {
960 			con->sb_pos = con->sb_last;
961 		}
962 	}
963 	return -num;
964 }
965 
966 SHL_EXPORT
tsm_screen_sb_down(struct tsm_screen * con,unsigned int num)967 int tsm_screen_sb_down(struct tsm_screen *con, unsigned int num)
968 {
969 	if (!con || !num)
970 		return 0;
971 
972 	unsigned num2 = num;
973 	inc_age(con);
974 	con->age = con->age_cnt;
975 
976 	while (num2--) {
977 		if (con->sb_pos)
978 			con->sb_pos = con->sb_pos->next;
979 		else
980 			return (num - num2);
981 	}
982 	return num;
983 }
984 
985 SHL_EXPORT
tsm_screen_sb_page_up(struct tsm_screen * con,unsigned int num)986 int tsm_screen_sb_page_up(struct tsm_screen *con, unsigned int num)
987 {
988 	if (!con || !num)
989 		return 0;
990 
991 	inc_age(con);
992 	return tsm_screen_sb_up(con, num * con->size_y);
993 }
994 
995 SHL_EXPORT
tsm_screen_sb_page_down(struct tsm_screen * con,unsigned int num)996 int tsm_screen_sb_page_down(struct tsm_screen *con, unsigned int num)
997 {
998 	if (!con || !num)
999 		return 0;
1000 
1001 	inc_age(con);
1002 	return tsm_screen_sb_down(con, num * con->size_y);
1003 }
1004 
1005 SHL_EXPORT
tsm_screen_sb_reset(struct tsm_screen * con)1006 void tsm_screen_sb_reset(struct tsm_screen *con)
1007 {
1008 	if (!con || !con->sb_pos)
1009 		return;
1010 
1011 	inc_age(con);
1012 	con->age = con->age_cnt;
1013 
1014 	con->sb_pos = NULL;
1015 }
1016 
1017 SHL_EXPORT
tsm_screen_set_def_attr(struct tsm_screen * con,const struct tui_screen_attr * attr)1018 void tsm_screen_set_def_attr(struct tsm_screen *con,
1019 				 const struct tui_screen_attr *attr)
1020 {
1021 	if (!con || !attr)
1022 		return;
1023 
1024 	memcpy(&con->def_attr, attr, sizeof(*attr));
1025 }
1026 
1027 SHL_EXPORT
tsm_screen_get_def_attr(struct tsm_screen * con)1028 struct tui_screen_attr tsm_screen_get_def_attr(struct tsm_screen* con)
1029 {
1030 	return con->def_attr;
1031 }
1032 
1033 SHL_EXPORT
tsm_screen_reset(struct tsm_screen * con)1034 void tsm_screen_reset(struct tsm_screen *con)
1035 {
1036 	unsigned int i;
1037 
1038 	if (!con)
1039 		return;
1040 
1041 	inc_age(con);
1042 	con->age = con->age_cnt;
1043 
1044 	con->flags = 0;
1045 	con->margin_top = 0;
1046 	con->margin_bottom = con->size_y - 1;
1047 	con->lines = con->main_lines;
1048 
1049 	for (i = 0; i < con->size_x; ++i) {
1050 		if (i % 8 == 0)
1051 			con->tab_ruler[i] = true;
1052 		else
1053 			con->tab_ruler[i] = false;
1054 	}
1055 }
1056 
1057 SHL_EXPORT
tsm_screen_set_flags(struct tsm_screen * con,unsigned int flags)1058 void tsm_screen_set_flags(struct tsm_screen *con, unsigned int flags)
1059 {
1060 	unsigned int old;
1061 	struct cell *c;
1062 
1063 	if (!con || !flags)
1064 		return;
1065 
1066 	inc_age(con);
1067 
1068 	old = con->flags;
1069 	con->flags |= flags;
1070 
1071 	if (!(old & TSM_SCREEN_ALTERNATE) && (flags & TSM_SCREEN_ALTERNATE)) {
1072 		con->age = con->age_cnt;
1073 		con->lines = con->alt_lines;
1074 	}
1075 
1076 	if (!(old & TSM_SCREEN_INVERSE) && (flags & TSM_SCREEN_INVERSE))
1077 		con->age = con->age_cnt;
1078 }
1079 
1080 SHL_EXPORT
tsm_screen_reset_flags(struct tsm_screen * con,unsigned int flags)1081 void tsm_screen_reset_flags(struct tsm_screen *con, unsigned int flags)
1082 {
1083 	unsigned int old;
1084 	struct cell *c;
1085 
1086 	if (!con || !flags)
1087 		return;
1088 
1089 	inc_age(con);
1090 
1091 	old = con->flags;
1092 	con->flags &= ~flags;
1093 
1094 	if ((old & TSM_SCREEN_ALTERNATE) && (flags & TSM_SCREEN_ALTERNATE)) {
1095 		con->age = con->age_cnt;
1096 		con->lines = con->main_lines;
1097 	}
1098 
1099 	if ((old & TSM_SCREEN_INVERSE) && (flags & TSM_SCREEN_INVERSE))
1100 		con->age = con->age_cnt;
1101 }
1102 
1103 SHL_EXPORT
tsm_screen_get_flags(struct tsm_screen * con)1104 unsigned int tsm_screen_get_flags(struct tsm_screen *con)
1105 {
1106 	if (!con)
1107 		return 0;
1108 
1109 	return con->flags;
1110 }
1111 
1112 SHL_EXPORT
tsm_screen_get_cursor_x(struct tsm_screen * con)1113 unsigned int tsm_screen_get_cursor_x(struct tsm_screen *con)
1114 {
1115 	if (!con)
1116 		return 0;
1117 
1118 	return con->cursor_x;
1119 }
1120 
1121 SHL_EXPORT
tsm_screen_get_cursor_y(struct tsm_screen * con)1122 unsigned int tsm_screen_get_cursor_y(struct tsm_screen *con)
1123 {
1124 	if (!con)
1125 		return 0;
1126 
1127 	return con->cursor_y;
1128 }
1129 
1130 SHL_EXPORT
tsm_screen_set_tabstop(struct tsm_screen * con)1131 void tsm_screen_set_tabstop(struct tsm_screen *con)
1132 {
1133 	if (!con || con->cursor_x >= con->size_x)
1134 		return;
1135 
1136 	con->tab_ruler[con->cursor_x] = true;
1137 }
1138 
1139 SHL_EXPORT
tsm_screen_reset_tabstop(struct tsm_screen * con)1140 void tsm_screen_reset_tabstop(struct tsm_screen *con)
1141 {
1142 	if (!con || con->cursor_x >= con->size_x)
1143 		return;
1144 
1145 	con->tab_ruler[con->cursor_x] = false;
1146 }
1147 
1148 SHL_EXPORT
tsm_screen_reset_all_tabstops(struct tsm_screen * con)1149 void tsm_screen_reset_all_tabstops(struct tsm_screen *con)
1150 {
1151 	unsigned int i;
1152 
1153 	if (!con)
1154 		return;
1155 
1156 	for (i = 0; i < con->size_x; ++i)
1157 		con->tab_ruler[i] = false;
1158 }
1159 
1160 SHL_EXPORT
tsm_screen_write(struct tsm_screen * con,tsm_symbol_t ch,const struct tui_screen_attr * attr)1161 void tsm_screen_write(struct tsm_screen *con, tsm_symbol_t ch,
1162 			  const struct tui_screen_attr *attr)
1163 {
1164 	int last, len;
1165 
1166 	if (!con)
1167 		return;
1168 
1169 	len = tsm_symbol_get_width(con->sym_table, ch);
1170 	if (!len)
1171 		return;
1172 		else if (len < 0) {
1173 			ch = 0x0000fffd;
1174 			len = 1;
1175 		}
1176 
1177 	inc_age(con);
1178 
1179 	if (con->cursor_y <= con->margin_bottom ||
1180 	    con->cursor_y >= con->size_y)
1181 		last = con->margin_bottom;
1182 	else
1183 		last = con->size_y - 1;
1184 
1185 	if (con->cursor_x >= con->size_x) {
1186 		if (con->flags & TSM_SCREEN_AUTO_WRAP)
1187 			move_cursor(con, 0, con->cursor_y + 1);
1188 		else
1189 			move_cursor(con, con->size_x - 1, con->cursor_y);
1190 	}
1191 
1192 	if (con->cursor_y > last) {
1193 		move_cursor(con, con->cursor_x, last);
1194 		screen_scroll_up(con, 1);
1195 		return;
1196 	}
1197 
1198 	screen_write(con,
1199 		con->cursor_x, con->cursor_y, ch, len, attr ? attr : &con->def_attr);
1200 	move_cursor(con, con->cursor_x + len, con->cursor_y);
1201 
1202 	return;
1203 }
1204 
1205 struct export_metadata {
1206 	uint8_t magic[4];
1207 	uint32_t sb_count;
1208 	uint16_t columns, rows;
1209 	uint16_t margin_top;
1210 	uint16_t margin_bottom;
1211 	uint32_t flags;
1212 };
1213 
1214 struct export_cell {
1215 	struct tui_screen_attr attr;
1216 	uint32_t ch;
1217 };
1218 
1219 SHL_EXPORT
tsm_screen_save(struct tsm_screen * src,bool sb,struct tsm_save_buf ** out)1220 bool tsm_screen_save(struct tsm_screen* src, bool sb, struct tsm_save_buf** out)
1221 {
1222 	if (!src || !out)
1223 		return false;
1224 
1225 	if (!tsm_screen_save_sub(src, out, 0, 0, src->size_x, src->size_y))
1226 		return false;
1227 
1228 /* _sub guarantees alignment */
1229 	struct export_metadata* md = (struct export_metadata*)((*out)->metadata);
1230 
1231 /* take the buffer, complement with scrollback and more metadata */
1232 	md->margin_top = src->margin_top;
1233 	md->margin_bottom = src->margin_bottom;
1234 	md->flags = src->flags;
1235 
1236 /* missing:
1237  * tab-ruler, selection state (likely uninteresting)
1238  */
1239 
1240 	if (sb){
1241 /* sb_count, sb_first, sb_last, sb_max, sb_pos, sb_last_id */
1242 		fprintf(stderr, "scrollback save/restore missing\n");
1243 	}
1244 
1245 	return true;
1246 }
1247 
1248 SHL_EXPORT
tsm_screen_save_sub(struct tsm_screen * src,struct tsm_save_buf ** out,size_t x,size_t y,size_t w,size_t h)1249 bool tsm_screen_save_sub(struct tsm_screen* src,
1250 	struct tsm_save_buf** out, size_t x, size_t y, size_t w, size_t h)
1251 {
1252 	if (x > src->size_x || x+w > src->size_x)
1253 		return false;
1254 
1255 	if (y > src->size_y || y+h > src->size_y)
1256 		return false;
1257 
1258 	struct tsm_save_buf* buf = malloc(sizeof(struct tsm_save_buf));
1259 	*buf = (struct tsm_save_buf){0};
1260 	buf->metadata_sz = sizeof(struct export_metadata);
1261 
1262 	struct export_metadata* md = malloc(buf->metadata_sz);
1263 	buf->metadata = (uint8_t*) md;
1264 	*md = (struct export_metadata){
1265 		.magic = {'a', 't', 'u', 'i'},
1266 		.columns = w,
1267 		.rows = h
1268 	};
1269 
1270 	buf->screen_sz = sizeof(struct export_cell) * w * h;
1271 	buf->screen = malloc(buf->screen_sz);
1272 	size_t ofs = 0;
1273 
1274 /* the _resize call makes sure that lines actually fit the current size */
1275 	for (size_t row = y; row < h; row++)
1276 		for (size_t col = x; col < w; col++, ofs++){
1277 			struct export_cell cell = {
1278 				.attr = src->lines[row]->cells[col].attr,
1279 				.ch = src->lines[row]->cells[col].ch
1280 			};
1281 
1282 			memcpy(
1283 			&buf->screen[ofs * sizeof(struct export_cell)],
1284 				&cell,
1285 				sizeof(struct export_cell)
1286 			);
1287 		}
1288 
1289 	*out = buf;
1290 	return true;
1291 }
1292 
1293 SHL_EXPORT
tsm_screen_load(struct tsm_screen * dst,struct tsm_save_buf * in,size_t start_x,size_t start_y,int mode)1294 bool tsm_screen_load(struct tsm_screen* dst,
1295 	struct tsm_save_buf* in, size_t start_x, size_t start_y, int mode)
1296 {
1297 	struct export_metadata md;
1298 	if (in->metadata_sz != sizeof(struct export_metadata))
1299 		return false;
1300 
1301 	memcpy(&md, in->metadata, sizeof(struct export_metadata));
1302 
1303 	if (!in->screen ||
1304 		md.magic[0] != 'a' || md.magic[1] != 't' ||
1305 		md.magic[2] != 'u' || md.magic[3] != 'i')
1306 		return false;
1307 
1308 	if (mode & TSM_LOAD_RESIZE){
1309 		if (md.columns > dst->size_x || md.rows > dst->size_y){
1310 			tsm_screen_resize(dst, md.columns, md.rows);
1311 		}
1312 		tsm_screen_erase_screen(dst, false);
1313 	}
1314 
1315 	size_t csz = sizeof(struct export_cell);
1316 	if (mode & TSM_LOAD_APPEND){
1317 		tsm_screen_move_to(dst, start_x, start_y);
1318 		for (size_t pos = 0; pos < md.rows * md.columns * csz; pos += csz){
1319 			struct export_cell unp;
1320 			memcpy(&unp, &in->screen[pos], csz);
1321 			tsm_screen_write(dst, unp.ch, &unp.attr);
1322 		}
1323 		tsm_screen_move_to(dst, start_x, start_y+md.rows+1);
1324 	}
1325 	else{
1326 /* replace screen contents with as much as possible */
1327 		for (size_t y = start_y; y < dst->size_y && y - start_y < md.rows; y++)
1328 			for (size_t x = start_x; x < dst->size_x && x - start_x < md.columns;x++){
1329 				struct export_cell unp;
1330 				memcpy(&unp, &in->screen[ csz *
1331 					((y-start_y) * md.columns + (x-start_x))], csz);
1332 				tsm_screen_move_to(dst, x, y);
1333 				tsm_screen_write(dst, unp.ch, &unp.attr);
1334 		}
1335 	}
1336 
1337 	return true;
1338 }
1339 
1340 SHL_EXPORT
tsm_screen_newline(struct tsm_screen * con)1341 int tsm_screen_newline(struct tsm_screen *con)
1342 {
1343 	if (!con)
1344 		return 0;
1345 
1346 	inc_age(con);
1347 
1348 	int rv = tsm_screen_move_down(con, 1, true);
1349 	tsm_screen_move_line_home(con);
1350 	return rv;
1351 }
1352 
1353 SHL_EXPORT
tsm_screen_scroll_up(struct tsm_screen * con,unsigned int num)1354 int tsm_screen_scroll_up(struct tsm_screen *con, unsigned int num)
1355 {
1356 	if (!con || !num)
1357 		return 0;
1358 
1359 	inc_age(con);
1360 
1361 	return screen_scroll_up(con, num);
1362 }
1363 
1364 SHL_EXPORT
tsm_screen_scroll_down(struct tsm_screen * con,unsigned int num)1365 int tsm_screen_scroll_down(struct tsm_screen *con, unsigned int num)
1366 {
1367 	if (!con || !num)
1368 		return 0;
1369 
1370 	inc_age(con);
1371 
1372 	return screen_scroll_down(con, num);
1373 }
1374 
1375 SHL_EXPORT
tsm_screen_move_to(struct tsm_screen * con,unsigned int x,unsigned int y)1376 void tsm_screen_move_to(struct tsm_screen *con, unsigned int x,
1377 			    unsigned int y)
1378 {
1379 	unsigned int last;
1380 
1381 	if (!con)
1382 		return;
1383 
1384 	inc_age(con);
1385 
1386 	if (con->flags & TSM_SCREEN_REL_ORIGIN)
1387 		last = con->margin_bottom;
1388 	else
1389 		last = con->size_y - 1;
1390 
1391 	x = to_abs_x(con, x);
1392 	if (x >= con->size_x)
1393 		x = con->size_x - 1;
1394 
1395 	y = to_abs_y(con, y);
1396 	if (y > last)
1397 		y = last;
1398 
1399 	move_cursor(con, x, y);
1400 }
1401 
1402 SHL_EXPORT
tsm_screen_move_up(struct tsm_screen * con,unsigned int num,bool scroll)1403 int tsm_screen_move_up(struct tsm_screen *con, unsigned int num,
1404 			    bool scroll)
1405 {
1406 	unsigned int diff, size;
1407 
1408 	if (!con || !num)
1409 		return 0;
1410 
1411 	inc_age(con);
1412 
1413 	if (con->cursor_y >= con->margin_top)
1414 		size = con->margin_top;
1415 	else
1416 		size = 0;
1417 
1418 	diff = con->cursor_y - size;
1419 	if (num > diff) {
1420 		num -= diff;
1421 		if (scroll){
1422 			screen_scroll_down(con, num);
1423 			num = 0;
1424 		}
1425 		move_cursor(con, con->cursor_x, size);
1426 		return -num;
1427 	} else {
1428 		move_cursor(con, con->cursor_x, con->cursor_y - num);
1429 	}
1430 	return 0;
1431 }
1432 
1433 SHL_EXPORT
tsm_screen_move_down(struct tsm_screen * con,unsigned int num,bool scroll)1434 int tsm_screen_move_down(struct tsm_screen *con, unsigned int num,
1435 			      bool scroll)
1436 {
1437 	unsigned int diff, size;
1438 
1439 	if (!con || !num)
1440 		return 0;
1441 
1442 	inc_age(con);
1443 
1444 	if (con->cursor_y <= con->margin_bottom)
1445 		size = con->margin_bottom + 1;
1446 	else
1447 		size = con->size_y;
1448 
1449 	int res = 0;
1450 	diff = size - con->cursor_y - 1;
1451 	if (num > diff) {
1452 		num -= diff;
1453 		if (scroll){
1454 			res = screen_scroll_up(con, num);
1455 		}
1456 		else
1457 			res = 0;
1458 		move_cursor(con, con->cursor_x, size - 1);
1459 		return res;
1460 	} else {
1461 		move_cursor(con, con->cursor_x, con->cursor_y + num);
1462 	}
1463 	return res;
1464 }
1465 
1466 SHL_EXPORT
tsm_screen_move_left(struct tsm_screen * con,unsigned int num)1467 void tsm_screen_move_left(struct tsm_screen *con, unsigned int num)
1468 {
1469 	unsigned int x;
1470 
1471 	if (!con || !num)
1472 		return;
1473 
1474 	inc_age(con);
1475 
1476 	if (num > con->size_x)
1477 		num = con->size_x;
1478 
1479 	x = con->cursor_x;
1480 	if (x >= con->size_x)
1481 		x = con->size_x - 1;
1482 
1483 	if (num > x)
1484 		move_cursor(con, 0, con->cursor_y);
1485 	else
1486 		move_cursor(con, x - num, con->cursor_y);
1487 }
1488 
1489 SHL_EXPORT
tsm_screen_move_right(struct tsm_screen * con,unsigned int num)1490 void tsm_screen_move_right(struct tsm_screen *con, unsigned int num)
1491 {
1492 	if (!con || !num)
1493 		return;
1494 
1495 	inc_age(con);
1496 
1497 	if (num > con->size_x)
1498 		num = con->size_x;
1499 
1500 	if (num + con->cursor_x >= con->size_x)
1501 		move_cursor(con, con->size_x - 1, con->cursor_y);
1502 	else
1503 		move_cursor(con, con->cursor_x + num, con->cursor_y);
1504 }
1505 
1506 SHL_EXPORT
tsm_screen_move_line_end(struct tsm_screen * con)1507 void tsm_screen_move_line_end(struct tsm_screen *con)
1508 {
1509 	if (!con)
1510 		return;
1511 
1512 	inc_age(con);
1513 
1514 	move_cursor(con, con->size_x - 1, con->cursor_y);
1515 }
1516 
1517 SHL_EXPORT
tsm_screen_move_line_home(struct tsm_screen * con)1518 void tsm_screen_move_line_home(struct tsm_screen *con)
1519 {
1520 	if (!con)
1521 		return;
1522 
1523 	inc_age(con);
1524 
1525 	move_cursor(con, 0, con->cursor_y);
1526 }
1527 
1528 SHL_EXPORT
tsm_screen_tab_right(struct tsm_screen * con,unsigned int num)1529 void tsm_screen_tab_right(struct tsm_screen *con, unsigned int num)
1530 {
1531 	unsigned int i, j, x;
1532 
1533 	if (!con || !num)
1534 		return;
1535 
1536 	inc_age(con);
1537 
1538 	x = con->cursor_x;
1539 	for (i = 0; i < num; ++i) {
1540 		for (j = x + 1; j < con->size_x; ++j) {
1541 			if (con->tab_ruler[j])
1542 				break;
1543 		}
1544 
1545 		x = j;
1546 		if (x + 1 >= con->size_x)
1547 			break;
1548 	}
1549 
1550 	/* tabs never cause pending new-lines */
1551 	if (x >= con->size_x)
1552 		x = con->size_x - 1;
1553 
1554 	move_cursor(con, x, con->cursor_y);
1555 }
1556 
1557 SHL_EXPORT
tsm_screen_tab_left(struct tsm_screen * con,unsigned int num)1558 void tsm_screen_tab_left(struct tsm_screen *con, unsigned int num)
1559 {
1560 	unsigned int i, x;
1561 	int j;
1562 
1563 	if (!con || !num)
1564 		return;
1565 
1566 	inc_age(con);
1567 
1568 	x = con->cursor_x;
1569 	for (i = 0; i < num; ++i) {
1570 		for (j = x - 1; j > 0; --j) {
1571 			if (con->tab_ruler[j])
1572 				break;
1573 		}
1574 
1575 		if (j <= 0) {
1576 			x = 0;
1577 			break;
1578 		}
1579 		x = j;
1580 	}
1581 
1582 	move_cursor(con, x, con->cursor_y);
1583 }
1584 
1585 SHL_EXPORT
tsm_screen_insert_lines(struct tsm_screen * con,unsigned int num)1586 void tsm_screen_insert_lines(struct tsm_screen *con, unsigned int num)
1587 {
1588 	unsigned int i, j, max;
1589 
1590 	if (!con || !num)
1591 		return;
1592 
1593 	if (con->cursor_y < con->margin_top ||
1594 	    con->cursor_y > con->margin_bottom)
1595 		return;
1596 
1597 	inc_age(con);
1598 	con->age = con->age_cnt;
1599 
1600 	max = con->margin_bottom - con->cursor_y + 1;
1601 	if (num > max)
1602 		num = max;
1603 
1604 	struct line *cache[num];
1605 
1606 	for (i = 0; i < num; ++i) {
1607 		cache[i] = con->lines[con->margin_bottom - i];
1608 		for (j = 0; j < con->size_x; ++j)
1609 			cell_init(con, &cache[i]->cells[j]);
1610 		if (con->cursor_y < con->vanguard)
1611 			con->vanguard++;
1612 	}
1613 
1614 	if (num < max) {
1615 		memmove(&con->lines[con->cursor_y + num],
1616 			&con->lines[con->cursor_y],
1617 			(max - num) * sizeof(struct line*));
1618 
1619 		memcpy(&con->lines[con->cursor_y],
1620 		       cache, num * sizeof(struct line*));
1621 	}
1622 
1623 	con->cursor_x = 0;
1624 }
1625 
1626 SHL_EXPORT
tsm_screen_delete_lines(struct tsm_screen * con,unsigned int num)1627 void tsm_screen_delete_lines(struct tsm_screen *con, unsigned int num)
1628 {
1629 	unsigned int i, j, max;
1630 
1631 	if (!con || !num)
1632 		return;
1633 
1634 	if (con->cursor_y < con->margin_top ||
1635 	    con->cursor_y > con->margin_bottom)
1636 		return;
1637 
1638 	inc_age(con);
1639 	con->age = con->age_cnt;
1640 
1641 	max = con->margin_bottom - con->cursor_y + 1;
1642 	if (num > max)
1643 		num = max;
1644 
1645 	struct line *cache[num];
1646 
1647 	for (i = 0; i < num; ++i) {
1648 		cache[i] = con->lines[con->cursor_y + i];
1649 		for (j = 0; j < con->size_x; ++j)
1650 			cell_init(con, &cache[i]->cells[j]);
1651 		if (con->cursor_y <= con->vanguard)
1652 			con->vanguard--;
1653 	}
1654 
1655 	if (num < max) {
1656 		memmove(&con->lines[con->cursor_y],
1657 			&con->lines[con->cursor_y + num],
1658 			(max - num) * sizeof(struct line*));
1659 
1660 		memcpy(&con->lines[con->cursor_y + (max - num)],
1661 		       cache, num * sizeof(struct line*));
1662 	}
1663 
1664 	con->cursor_x = 0;
1665 }
1666 
1667 SHL_EXPORT
tsm_screen_insert_chars(struct tsm_screen * con,unsigned int num)1668 void tsm_screen_insert_chars(struct tsm_screen *con, unsigned int num)
1669 {
1670 	struct cell *cells;
1671 	unsigned int max, mv, i;
1672 
1673 	if (!con || !num || !con->size_y || !con->size_x)
1674 		return;
1675 
1676 	inc_age(con);
1677 	con->age = con->age_cnt;
1678 
1679 	if (con->cursor_x >= con->size_x)
1680 		con->cursor_x = con->size_x - 1;
1681 	if (con->cursor_y >= con->size_y)
1682 		con->cursor_y = con->size_y - 1;
1683 
1684 	max = con->size_x - con->cursor_x;
1685 	if (num > max)
1686 		num = max;
1687 	mv = max - num;
1688 
1689 	cells = con->lines[con->cursor_y]->cells;
1690 	if (mv)
1691 		memmove(&cells[con->cursor_x + num],
1692 			&cells[con->cursor_x],
1693 			mv * sizeof(*cells));
1694 
1695 	for (i = 0; i < num; ++i)
1696 		cell_init(con, &cells[con->cursor_x + i]);
1697 }
1698 
1699 SHL_EXPORT
tsm_screen_delete_chars(struct tsm_screen * con,unsigned int num)1700 void tsm_screen_delete_chars(struct tsm_screen *con, unsigned int num)
1701 {
1702 	struct cell *cells;
1703 	unsigned int max, mv, i;
1704 
1705 	if (!con || !num || !con->size_y || !con->size_x)
1706 		return;
1707 
1708 	inc_age(con);
1709 	con->age = con->age_cnt;
1710 
1711 	if (con->cursor_x >= con->size_x)
1712 		con->cursor_x = con->size_x - 1;
1713 	if (con->cursor_y >= con->size_y)
1714 		con->cursor_y = con->size_y - 1;
1715 
1716 	max = con->size_x - con->cursor_x;
1717 	if (num > max)
1718 		num = max;
1719 	mv = max - num;
1720 
1721 	cells = con->lines[con->cursor_y]->cells;
1722 	if (mv)
1723 		memmove(&cells[con->cursor_x],
1724 			&cells[con->cursor_x + num],
1725 			mv * sizeof(*cells));
1726 
1727 	for (i = 0; i < num; ++i)
1728 		cell_init(con, &cells[con->cursor_x + mv + i]);
1729 }
1730 
1731 SHL_EXPORT
tsm_screen_erase_cursor(struct tsm_screen * con)1732 void tsm_screen_erase_cursor(struct tsm_screen *con)
1733 {
1734 	unsigned int x;
1735 
1736 	if (!con)
1737 		return;
1738 
1739 	if (con->cursor_x >= con->size_x)
1740 		x = con->size_x - 1;
1741 	else
1742 		x = con->cursor_x;
1743 
1744 	tsm_screen_erase_region(con, x, con->cursor_y, x, con->cursor_y, false);
1745 }
1746 
1747 SHL_EXPORT
tsm_screen_erase_chars(struct tsm_screen * con,unsigned int num)1748 void tsm_screen_erase_chars(struct tsm_screen *con, unsigned int num)
1749 {
1750 	unsigned int x;
1751 
1752 	if (!con || !num)
1753 		return;
1754 
1755 	if (con->cursor_x >= con->size_x)
1756 		x = con->size_x - 1;
1757 	else
1758 		x = con->cursor_x;
1759 
1760 	tsm_screen_erase_region(
1761 		con, x, con->cursor_y, x + num - 1, con->cursor_y, false);
1762 }
1763 
1764 SHL_EXPORT
tsm_screen_erase_cursor_to_end(struct tsm_screen * con,bool protect)1765 void tsm_screen_erase_cursor_to_end(struct tsm_screen *con,
1766 				        bool protect)
1767 {
1768 	unsigned int x;
1769 
1770 	if (!con)
1771 		return;
1772 
1773 	if (con->cursor_x >= con->size_x)
1774 		x = con->size_x - 1;
1775 	else
1776 		x = con->cursor_x;
1777 
1778 	tsm_screen_erase_region(con,
1779 		x, con->cursor_y, con->size_x - 1, con->cursor_y, protect);
1780 }
1781 
1782 SHL_EXPORT
tsm_screen_erase_home_to_cursor(struct tsm_screen * con,bool protect)1783 void tsm_screen_erase_home_to_cursor(struct tsm_screen *con, bool protect)
1784 {
1785 	if (!con)
1786 		return;
1787 
1788 	tsm_screen_erase_region(con,
1789 		0, con->cursor_y, con->cursor_x, con->cursor_y, protect);
1790 }
1791 
1792 SHL_EXPORT
tsm_screen_erase_current_line(struct tsm_screen * con,bool protect)1793 void tsm_screen_erase_current_line(struct tsm_screen *con,
1794 				       bool protect)
1795 {
1796 	if (!con)
1797 		return;
1798 
1799 	tsm_screen_erase_region(con,
1800 		0, con->cursor_y, con->size_x - 1, con->cursor_y, protect);
1801 	if (con->cursor_y == con->vanguard)
1802 		con->vanguard--;
1803 }
1804 
1805 SHL_EXPORT
tsm_screen_erase_screen_to_cursor(struct tsm_screen * con,bool protect)1806 void tsm_screen_erase_screen_to_cursor(struct tsm_screen *con,
1807 					   bool protect)
1808 {
1809 	if (!con)
1810 		return;
1811 
1812 	tsm_screen_erase_region(con, 0, 0, con->cursor_x, con->cursor_y, protect);
1813 	if (con->cursor_y > con->vanguard)
1814 		con->vanguard = 0;
1815 }
1816 
1817 SHL_EXPORT
tsm_screen_erase_cursor_to_screen(struct tsm_screen * con,bool protect)1818 void tsm_screen_erase_cursor_to_screen(struct tsm_screen *con,
1819 					   bool protect)
1820 {
1821 	unsigned int x;
1822 
1823 	if (!con)
1824 		return;
1825 
1826 	if (con->cursor_x >= con->size_x)
1827 		x = con->size_x - 1;
1828 	else
1829 		x = con->cursor_x;
1830 
1831 	tsm_screen_erase_region(con,
1832 		x, con->cursor_y, con->size_x - 1, con->size_y - 1, protect);
1833 }
1834 
1835 SHL_EXPORT
tsm_screen_erase_screen(struct tsm_screen * con,bool protect)1836 void tsm_screen_erase_screen(struct tsm_screen *con, bool protect)
1837 {
1838 	if (!con)
1839 		return;
1840 
1841 	tsm_screen_erase_region(con,
1842 		0, 0, con->size_x - 1, con->size_y - 1, protect);
1843 	con->vanguard = 0;
1844 }
1845 
1846 /*
1847  * Selection Code
1848  * If a running pty-client does not support mouse-tracking extensions, a
1849  * terminal can manually mark selected areas if it does mouse-tracking itself.
1850  * This tracking is slightly different than the integrated client-tracking:
1851  *
1852  * Initial state is no-selection. At any time selection_reset() can be called to
1853  * clear the selection and go back to initial state.
1854  * If the user presses a mouse-button, the terminal can calculate the selected
1855  * cell and call selection_start() to notify the terminal that the user started
1856  * the selection. While the mouse-button is held down, the terminal should call
1857  * selection_target() whenever a mouse-event occurs. This will tell the screen
1858  * layer to draw the selection from the initial start up to the last given
1859  * target.
1860  * Please note that the selection-start cannot be modified by the terminal
1861  * during a selection. Instead, the screen-layer automatically moves it along
1862  * with any scroll-operations or inserts/deletes. This also means, the terminal
1863  * must _not_ cache the start-position itself as it may change under the hood.
1864  * This selection takes also care of scrollback-buffer selections and correctly
1865  * moves selection state along.
1866  *
1867  * Please note that this is not the kind of selection that some PTY applications
1868  * support. If the client supports the mouse-protocol, then it can also control
1869  * a separate screen-selection which is always inside of the actual screen. This
1870  * is a totally different selection.
1871  */
1872 
selection_set(struct tsm_screen * con,struct selection_pos * sel,unsigned int x,unsigned int y)1873 static void selection_set(struct tsm_screen *con, struct selection_pos *sel,
1874 			  unsigned int x, unsigned int y)
1875 {
1876 	struct line *pos;
1877 
1878 	sel->line = NULL;
1879 	pos = con->sb_pos;
1880 
1881 	while (y && pos) {
1882 		--y;
1883 		pos = pos->next;
1884 	}
1885 
1886 	if (pos)
1887 		sel->line = pos;
1888 
1889 	sel->x = x;
1890 	sel->y = y;
1891 }
1892 
1893 SHL_EXPORT
tsm_screen_selection_reset(struct tsm_screen * con)1894 void tsm_screen_selection_reset(struct tsm_screen *con)
1895 {
1896 	if (!con)
1897 		return;
1898 
1899 	inc_age(con);
1900 	con->age = con->age_cnt;
1901 
1902 	con->sel_active = false;
1903 }
1904 
1905 SHL_EXPORT
tsm_screen_selection_start(struct tsm_screen * con,unsigned int posx,unsigned int posy)1906 void tsm_screen_selection_start(struct tsm_screen *con,
1907 				unsigned int posx,
1908 				unsigned int posy)
1909 {
1910 	if (!con)
1911 		return;
1912 
1913 	inc_age(con);
1914 	con->age = con->age_cnt;
1915 
1916 	con->sel_active = true;
1917 	selection_set(con, &con->sel_start, posx, posy);
1918 	memcpy(&con->sel_end, &con->sel_start, sizeof(con->sel_end));
1919 }
1920 
1921 SHL_EXPORT
tsm_screen_selection_target(struct tsm_screen * con,unsigned int posx,unsigned int posy)1922 void tsm_screen_selection_target(struct tsm_screen *con,
1923 				 unsigned int posx,
1924 				 unsigned int posy)
1925 {
1926 	if (!con || !con->sel_active)
1927 		return;
1928 
1929 	inc_age(con);
1930 	con->age = con->age_cnt;
1931 
1932 	selection_set(con, &con->sel_end, posx, posy);
1933 }
1934 
copy_line(struct line * line,char * buf,unsigned int start,unsigned int len,bool conv)1935 static unsigned int copy_line(struct line *line, char *buf,
1936 			      unsigned int start, unsigned int len, bool conv)
1937 {
1938 	unsigned int i, end;
1939 	char *pos = buf;
1940 
1941 	end = start + len;
1942 	for (i = start; i < line->size && i < end; ++i) {
1943 		if (i < line->size || !line->cells[i].ch){
1944 			if (!conv){
1945 				memcpy(pos, &line->cells[i].ch, 4);
1946 				pos += 4;
1947 			}
1948 			else
1949 				pos += tsm_ucs4_to_utf8(line->cells[i].ch, pos);
1950 		}
1951 		else{
1952 			if (!conv){
1953 				uint32_t ch = ' ';
1954 				memcpy(pos, &ch, 4);
1955 				pos += 4;
1956 			}
1957 			else
1958 				pos += tsm_ucs4_to_utf8(' ', pos);
1959 		}
1960 	}
1961 
1962 	return pos - buf;
1963 }
1964 
1965 /* TODO: This beast definitely needs some "beautification", however, it's meant
1966  * as a "proof-of-concept" so its enough for now. */
1967 SHL_EXPORT
tsm_screen_selection_copy(struct tsm_screen * con,char ** out,bool conv)1968 int tsm_screen_selection_copy(struct tsm_screen *con, char **out, bool conv)
1969 {
1970 	unsigned int len, i;
1971 	struct selection_pos *start, *end;
1972 	struct line *iter;
1973 	char *str, *pos;
1974 
1975 	if (!con || !out)
1976 		return -EINVAL;
1977 
1978 	if (!con->sel_active)
1979 		return -ENOENT;
1980 
1981 	/* check whether sel_start or sel_end comes first */
1982 	if (!con->sel_start.line && con->sel_start.y == SELECTION_TOP) {
1983 		if (!con->sel_end.line && con->sel_end.y == SELECTION_TOP) {
1984 			str = strdup("");
1985 			if (!str)
1986 				return -ENOMEM;
1987 			*out = str;
1988 			return 0;
1989 		}
1990 		start = &con->sel_start;
1991 		end = &con->sel_end;
1992 	} else if (!con->sel_end.line && con->sel_end.y == SELECTION_TOP) {
1993 		start = &con->sel_end;
1994 		end = &con->sel_start;
1995 	} else if (con->sel_start.line && con->sel_end.line) {
1996 		if (con->sel_start.line->sb_id < con->sel_end.line->sb_id) {
1997 			start = &con->sel_start;
1998 			end = &con->sel_end;
1999 		} else if (con->sel_start.line->sb_id > con->sel_end.line->sb_id) {
2000 			start = &con->sel_end;
2001 			end = &con->sel_start;
2002 		} else if (con->sel_start.x < con->sel_end.x) {
2003 			start = &con->sel_start;
2004 			end = &con->sel_end;
2005 		} else {
2006 			start = &con->sel_end;
2007 			end = &con->sel_start;
2008 		}
2009 	} else if (con->sel_start.line) {
2010 		start = &con->sel_start;
2011 		end = &con->sel_end;
2012 	} else if (con->sel_end.line) {
2013 		start = &con->sel_end;
2014 		end = &con->sel_start;
2015 	} else if (con->sel_start.y < con->sel_end.y) {
2016 		start = &con->sel_start;
2017 		end = &con->sel_end;
2018 	} else if (con->sel_start.y > con->sel_end.y) {
2019 		start = &con->sel_end;
2020 		end = &con->sel_start;
2021 	} else if (con->sel_start.x < con->sel_end.x) {
2022 		start = &con->sel_start;
2023 		end = &con->sel_end;
2024 	} else {
2025 		start = &con->sel_end;
2026 		end = &con->sel_start;
2027 	}
2028 
2029 	/* calculate size of buffer */
2030 	len = 0;
2031 	iter = start->line;
2032 	if (!iter && start->y == SELECTION_TOP)
2033 		iter = con->sb_first;
2034 
2035 	while (iter) {
2036 		if (iter == start->line && iter == end->line) {
2037 			if (iter->size > start->x) {
2038 				if (iter->size > end->x)
2039 					len += end->x - start->x + 1;
2040 				else
2041 					len += iter->size - start->x;
2042 			}
2043 			break;
2044 		} else if (iter == start->line) {
2045 			if (iter->size > start->x)
2046 				len += iter->size - start->x;
2047 		} else if (iter == end->line) {
2048 			if (iter->size > end->x)
2049 				len += end->x + 1;
2050 			else
2051 				len += iter->size;
2052 			break;
2053 		} else {
2054 			len += iter->size;
2055 		}
2056 
2057 		++len;
2058 		iter = iter->next;
2059 	}
2060 
2061 	if (!end->line) {
2062 		if (start->line || start->y == SELECTION_TOP)
2063 			i = 0;
2064 		else
2065 			i = start->y;
2066 		for ( ; i < con->size_y; ++i) {
2067 			if (!start->line && start->y == i && end->y == i) {
2068 				if (con->size_x > start->x) {
2069 					if (con->size_x > end->x)
2070 						len += end->x - start->x + 1;
2071 					else
2072 						len += con->size_x - start->x;
2073 				}
2074 				break;
2075 			} else if (!start->line && start->y == i) {
2076 				if (con->size_x > start->x)
2077 					len += con->size_x - start->x;
2078 			} else if (end->y == i) {
2079 				if (con->size_x > end->x)
2080 					len += end->x + 1;
2081 				else
2082 					len += con->size_x;
2083 				break;
2084 			} else {
2085 				len += con->size_x;
2086 			}
2087 
2088 			++len;
2089 		}
2090 	}
2091 
2092 	/* allocate buffer */
2093 	len *= 4;
2094 	++len;
2095 	str = malloc(len);
2096 	if (!str)
2097 		return -ENOMEM;
2098 	pos = str;
2099 
2100 	/* copy data into buffer */
2101 	iter = start->line;
2102 	if (!iter && start->y == SELECTION_TOP)
2103 		iter = con->sb_first;
2104 
2105 	while (iter) {
2106 		if (iter == start->line && iter == end->line) {
2107 			if (iter->size > start->x) {
2108 				if (iter->size > end->x)
2109 					len = end->x - start->x + 1;
2110 				else
2111 					len = iter->size - start->x;
2112 				pos += copy_line(iter, pos, start->x, len, conv);
2113 			}
2114 			break;
2115 		} else if (iter == start->line) {
2116 			if (iter->size > start->x)
2117 				pos += copy_line(iter, pos, start->x,
2118 						 iter->size - start->x, conv);
2119 		} else if (iter == end->line) {
2120 			if (iter->size > end->x)
2121 				len = end->x + 1;
2122 			else
2123 				len = iter->size;
2124 			pos += copy_line(iter, pos, 0, len, conv);
2125 			break;
2126 		} else {
2127 			pos += copy_line(iter, pos, 0, iter->size, conv);
2128 		}
2129 
2130 		if (conv){
2131 			*pos++ = '\n';
2132 		}
2133 		else {
2134 			uint32_t ch = '\n';
2135 			memcpy(pos, &ch, 4);
2136 			pos += 4;
2137 		}
2138 		iter = iter->next;
2139 	}
2140 
2141 	if (!end->line) {
2142 		if (start->line || start->y == SELECTION_TOP)
2143 			i = 0;
2144 		else
2145 			i = start->y;
2146 		for ( ; i < con->size_y; ++i) {
2147 			iter = con->lines[i];
2148 			if (!start->line && start->y == i && end->y == i) {
2149 				if (con->size_x > start->x) {
2150 					if (con->size_x > end->x)
2151 						len = end->x - start->x + 1;
2152 					else
2153 						len = con->size_x - start->x;
2154 					pos += copy_line(iter, pos, start->x, len, conv);
2155 				}
2156 				break;
2157 			} else if (!start->line && start->y == i) {
2158 				if (con->size_x > start->x)
2159 					pos += copy_line(iter, pos, start->x,
2160 							 con->size_x - start->x, conv);
2161 			} else if (end->y == i) {
2162 				if (con->size_x > end->x)
2163 					len = end->x + 1;
2164 				else
2165 					len = con->size_x;
2166 				pos += copy_line(iter, pos, 0, len, conv);
2167 				break;
2168 			} else {
2169 				pos += copy_line(iter, pos, 0, con->size_x, conv);
2170 			}
2171 
2172 			if (conv){
2173 				*pos++ = '\n';
2174 			}
2175 			else {
2176 				uint32_t ch = '\n';
2177 				memcpy(pos, &ch, 4);
2178 				pos += 4;
2179 			}
2180 		}
2181 	}
2182 
2183 	/* return buffer */
2184 	*pos = 0;
2185 	*out = str;
2186 	return pos - str;
2187 }
2188 
2189 SHL_EXPORT
tsm_screen_draw(struct tsm_screen * con,tsm_screen_draw_cb draw_cb,void * data)2190 tsm_age_t tsm_screen_draw(struct tsm_screen *con, tsm_screen_draw_cb draw_cb,
2191 			  void *data)
2192 {
2193 	unsigned int i, j, k;
2194 	struct line *iter, *line = NULL;
2195 	struct cell *cell, empty;
2196 	struct tui_screen_attr attr;
2197 	const uint32_t *ch;
2198 	size_t len;
2199 	bool in_sel = false, sel_start = false, sel_end = false;
2200 	bool was_sel = false;
2201 	tsm_age_t age;
2202 
2203 	if (!con || !draw_cb)
2204 		return 0;
2205 
2206 	cell_init(con, &empty);
2207 
2208 	/* push ech character into rendering pipeline */
2209 
2210 	iter = con->sb_pos;
2211 	k = 0;
2212 
2213 	if (con->sel_active) {
2214 		if (!con->sel_start.line && con->sel_start.y == SELECTION_TOP)
2215 			in_sel = !in_sel;
2216 		if (!con->sel_end.line && con->sel_end.y == SELECTION_TOP)
2217 			in_sel = !in_sel;
2218 
2219 		if (con->sel_start.line &&
2220 		    (!iter || con->sel_start.line->sb_id < iter->sb_id))
2221 			in_sel = !in_sel;
2222 		if (con->sel_end.line &&
2223 		    (!iter || con->sel_end.line->sb_id < iter->sb_id))
2224 			in_sel = !in_sel;
2225 	}
2226 
2227 	for (i = 0; i < con->size_y; ++i) {
2228 		if (iter) {
2229 			line = iter;
2230 			iter = iter->next;
2231 		} else {
2232 			line = con->lines[k];
2233 			k++;
2234 		}
2235 
2236 		if (con->sel_active) {
2237 			if (con->sel_start.line == line ||
2238 			    (!con->sel_start.line &&
2239 			     con->sel_start.y == k - 1))
2240 				sel_start = true;
2241 			else
2242 				sel_start = false;
2243 			if (con->sel_end.line == line ||
2244 			    (!con->sel_end.line &&
2245 			     con->sel_end.y == k - 1))
2246 				sel_end = true;
2247 			else
2248 				sel_end = false;
2249 
2250 			was_sel = false;
2251 		}
2252 
2253 		for (j = 0; j < con->size_x; ++j) {
2254 			if (j < line->size)
2255 				cell = &line->cells[j];
2256 			else
2257 				cell = &empty;
2258 			memcpy(&attr, &cell->attr, sizeof(attr));
2259 
2260 			if (con->sel_active) {
2261 				if (sel_start &&
2262 				    j == con->sel_start.x) {
2263 					was_sel = in_sel;
2264 					in_sel = !in_sel;
2265 				}
2266 				if (sel_end &&
2267 				    j == con->sel_end.x) {
2268 					was_sel = in_sel;
2269 					in_sel = !in_sel;
2270 				}
2271 			}
2272 
2273 /* actual inverse logic is handled in the renderer */
2274 			if (con->flags & TSM_SCREEN_INVERSE)
2275 				attr.aflags ^= TUI_ATTR_INVERSE;
2276 
2277 			if (in_sel || was_sel) {
2278 				was_sel = false;
2279 				attr.aflags ^= TUI_ATTR_INVERSE;
2280 			}
2281 
2282 			if (con->age_reset) {
2283 				age = 0;
2284 			} else {
2285 				age = cell->age;
2286 				if (line->age > age)
2287 					age = line->age;
2288 				if (con->age > age)
2289 					age = con->age;
2290 			}
2291 
2292 			ch = tsm_symbol_get(con->sym_table, &cell->ch, &len);
2293 			if (cell->ch == ' ' || cell->ch == 0 || cell->ch == 0xa0)
2294 				len = 0;
2295 
2296 			draw_cb(con, cell->ch,
2297 				ch, len, cell->width, j, i, &attr, age, data);
2298 		}
2299 	}
2300 
2301 	if (con->age_reset) {
2302 		con->age_reset = 0;
2303 		return 0;
2304 	} else {
2305 		return con->age_cnt;
2306 	}
2307 }
2308