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