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