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