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