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 = ∅
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