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