2 * TSM - Screen Management
4 * Copyright (c) 2011-2012 David Herrmann <dh.herrmann@googlemail.com>
5 * Copyright (c) 2011 University of Tuebingen
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:
15 * The above copyright notice and this permission notice shall be included
16 * in all copies or substantial portions of the Software.
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.
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.
40 #include "shl_timer.h"
41 #include "tsm_screen.h"
42 #include "tsm_unicode.h"
44 #define LLOG_SUBSYSTEM "tsm_screen"
49 struct tsm_screen_attr attr;
61 #define SELECTION_TOP -1
62 struct selection_pos {
73 struct shl_timer *timer;
75 /* default attributes for new cells */
76 struct tsm_screen_attr def_attr;
81 unsigned int margin_top;
82 unsigned int margin_bottom;
83 unsigned int line_num;
85 struct line **main_lines;
86 struct line **alt_lines;
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 */
97 unsigned int cursor_x;
98 unsigned int cursor_y;
105 struct selection_pos sel_start;
106 struct selection_pos sel_end;
109 static void cell_init(struct tsm_screen *con, struct cell *cell)
113 memcpy(&cell->attr, &con->def_attr, sizeof(cell->attr));
116 static int line_new(struct tsm_screen *con, struct line **out,
125 line = malloc(sizeof(*line));
132 line->cells = malloc(sizeof(struct cell) * width);
138 for (i = 0; i < width; ++i)
139 cell_init(con, &line->cells[i]);
145 static void line_free(struct line *line)
151 static int line_resize(struct tsm_screen *con, struct line *line,
159 if (line->size < width) {
160 tmp = realloc(line->cells, width * sizeof(struct cell));
166 while (line->size < width) {
167 cell_init(con, &line->cells[line->size]);
175 /* This links the given line into the scrollback-buffer */
176 static void link_to_scrollback(struct tsm_screen *con, struct line *line)
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;
186 if (con->sel_end.line == line) {
187 con->sel_end.line = NULL;
188 con->sel_end.y = SELECTION_TOP;
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) {
202 con->sb_first = tmp->next;
204 tmp->next->prev = NULL;
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. */
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;
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;
230 if (con->sel_end.line == tmp) {
231 con->sel_end.line = NULL;
232 con->sel_end.y = SELECTION_TOP;
238 line->sb_id = ++con->sb_last_id;
240 line->prev = con->sb_last;
242 con->sb_last->next = line;
244 con->sb_first = line;
249 static void screen_scroll_up(struct tsm_screen *con, unsigned int num)
251 unsigned int i, j, max, pos;
257 max = con->margin_bottom + 1 - con->margin_top;
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. */
267 screen_scroll_up(con, 128);
268 return screen_scroll_up(con, num - 128);
270 struct line *cache[num];
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);
280 link_to_scrollback(con, con->lines[pos]);
282 cache[i] = con->lines[pos];
283 for (j = 0; j < con->size_x; ++j)
284 cell_init(con, &cache[i]->cells[j]);
289 memmove(&con->lines[con->margin_top],
290 &con->lines[con->margin_top + num],
291 (max - num) * sizeof(struct line*));
294 memcpy(&con->lines[con->margin_top + (max - num)],
295 cache, num * sizeof(struct line*));
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;
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;
319 static void screen_scroll_down(struct tsm_screen *con, unsigned int num)
321 unsigned int i, j, max;
326 max = con->margin_bottom + 1 - con->margin_top;
330 /* see screen_scroll_up() for an explanation */
332 screen_scroll_down(con, 128);
333 return screen_scroll_down(con, num - 128);
335 struct line *cache[num];
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]);
344 memmove(&con->lines[con->margin_top + num],
345 &con->lines[con->margin_top],
346 (max - num) * sizeof(struct line*));
349 memcpy(&con->lines[con->margin_top],
350 cache, num * sizeof(struct line*));
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;
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)
370 if (x >= con->size_x || y >= con->size_y) {
371 llog_warn(con, "writing beyond buffer boundary");
375 line = con->lines[y];
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));
382 line->cells[x].ch = ch;
383 line->cells[x].width = len;
384 memcpy(&line->cells[x].attr, attr, sizeof(*attr));
386 for (i = 1; i < len && i + x < con->size_x; ++i)
387 line->cells[x + i].width = 0;
390 static void screen_erase_region(struct tsm_screen *con,
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;
405 for ( ; y_from <= y_to; ++y_from) {
406 line = con->lines[y_from];
415 to = con->size_x - 1;
416 for ( ; x_from <= to; ++x_from) {
417 if (protect && line->cells[x_from].attr.protect)
420 cell_init(con, &line->cells[x_from]);
426 static inline unsigned int to_abs_x(struct tsm_screen *con, unsigned int x)
431 static inline unsigned int to_abs_y(struct tsm_screen *con, unsigned int y)
433 if (!(con->flags & TSM_SCREEN_REL_ORIGIN))
436 return con->margin_top + y;
439 int tsm_screen_new(struct tsm_screen **out, tsm_log_t log)
441 struct tsm_screen *con;
448 con = malloc(sizeof(*con));
452 memset(con, 0, sizeof(*con));
455 con->def_attr.fr = 255;
456 con->def_attr.fg = 255;
457 con->def_attr.fb = 255;
459 ret = shl_timer_new(&con->timer);
463 ret = tsm_screen_resize(con, 80, 24);
467 llog_debug(con, "new screen");
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]);
478 free(con->main_lines);
479 free(con->alt_lines);
480 free(con->tab_ruler);
486 void tsm_screen_ref(struct tsm_screen *con)
494 void tsm_screen_unref(struct tsm_screen *con)
498 if (!con || !con->ref || --con->ref)
501 llog_debug(con, "destroying screen");
503 for (i = 0; i < con->line_num; ++i) {
504 line_free(con->main_lines[i]);
505 line_free(con->alt_lines[i]);
507 free(con->main_lines);
508 free(con->alt_lines);
509 free(con->tab_ruler);
510 shl_timer_free(con->timer);
514 void tsm_screen_set_opts(struct tsm_screen *scr, unsigned int opts)
522 void tsm_screen_reset_opts(struct tsm_screen *scr, unsigned int opts)
530 unsigned int tsm_screen_get_opts(struct tsm_screen *scr)
538 unsigned int tsm_screen_get_width(struct tsm_screen *con)
546 unsigned int tsm_screen_get_height(struct tsm_screen *con)
554 int tsm_screen_resize(struct tsm_screen *con, unsigned int x,
558 unsigned int i, j, width, diff;
562 if (!con || !x || !y)
565 if (con->size_x == x && con->size_y == y)
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);
581 if (con->lines == con->main_lines)
583 con->main_lines = cache;
585 /* resize alt buffer */
586 cache = realloc(con->alt_lines, sizeof(struct line*) * y);
590 if (con->lines == con->alt_lines)
592 con->alt_lines = cache;
594 /* allocate new lines */
600 while (con->line_num < y) {
601 ret = line_new(con, &con->main_lines[con->line_num],
606 ret = line_new(con, &con->alt_lines[con->line_num],
609 line_free(con->main_lines[con->line_num]);
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);
624 con->tab_ruler = tab_ruler;
626 for (i = 0; i < con->line_num; ++i) {
627 ret = line_resize(con, con->main_lines[i], x);
631 ret = line_resize(con, con->alt_lines[i], x);
637 for (j = 0; j < con->line_num; ++j) {
638 if (j >= con->size_y)
643 if (x < con->main_lines[j]->size)
646 width = con->main_lines[j]->size;
647 for (; i < width; ++i)
648 cell_init(con, &con->main_lines[j]->cells[i]);
650 if (x < con->alt_lines[j]->size)
653 width = con->alt_lines[j]->size;
654 for (; i < width; ++i)
655 cell_init(con, &con->alt_lines[j]->cells[i]);
658 /* xterm destroys margins on resize, so do we */
660 con->margin_bottom = con->size_y - 1;
663 for (i = 0; i < x; ++i) {
665 con->tab_ruler[i] = true;
667 con->tab_ruler[i] = false;
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. */
677 if (con->cursor_x >= con->size_x)
678 con->cursor_x = con->size_x - 1;
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;
691 con->margin_bottom = con->size_y - 1;
692 if (con->cursor_y >= con->size_y)
693 con->cursor_y = con->size_y - 1;
698 int tsm_screen_set_margins(struct tsm_screen *con,
699 unsigned int top, unsigned int bottom)
701 unsigned int upper, lower;
711 lower = con->size_y - 1;
712 } else if (bottom > con->size_y) {
714 lower = con->size_y - 1;
720 con->margin_top = upper;
721 con->margin_bottom = lower;
725 /* set maximum scrollback buffer size */
726 void tsm_screen_set_max_sb(struct tsm_screen *con,
734 while (con->sb_count > max) {
735 line = con->sb_first;
736 con->sb_first = line->next;
738 line->next->prev = NULL;
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;
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;
753 if (con->sel_end.line == line) {
754 con->sel_end.line = NULL;
755 con->sel_end.y = SELECTION_TOP;
764 /* clear scrollback buffer */
765 void tsm_screen_clear_sb(struct tsm_screen *con)
767 struct line *iter, *tmp;
772 for (iter = con->sb_first; iter; ) {
778 con->sb_first = NULL;
783 if (con->sel_active) {
784 if (con->sel_start.line) {
785 con->sel_start.line = NULL;
786 con->sel_start.y = SELECTION_TOP;
788 if (con->sel_end.line) {
789 con->sel_end.line = NULL;
790 con->sel_end.y = SELECTION_TOP;
795 void tsm_screen_sb_up(struct tsm_screen *con, unsigned int num)
802 if (!con->sb_pos->prev)
805 con->sb_pos = con->sb_pos->prev;
806 } else if (!con->sb_last) {
809 con->sb_pos = con->sb_last;
814 void tsm_screen_sb_down(struct tsm_screen *con, unsigned int num)
821 con->sb_pos = con->sb_pos->next;
830 void tsm_screen_sb_page_up(struct tsm_screen *con, unsigned int num)
835 tsm_screen_sb_up(con, num * con->size_y);
838 void tsm_screen_sb_page_down(struct tsm_screen *con, unsigned int num)
843 tsm_screen_sb_down(con, num * con->size_y);
846 void tsm_screen_sb_reset(struct tsm_screen *con)
854 void tsm_screen_set_def_attr(struct tsm_screen *con,
855 const struct tsm_screen_attr *attr)
860 memcpy(&con->def_attr, attr, sizeof(*attr));
863 void tsm_screen_reset(struct tsm_screen *con)
872 con->margin_bottom = con->size_y - 1;
874 for (i = 0; i < con->size_x; ++i) {
876 con->tab_ruler[i] = true;
878 con->tab_ruler[i] = false;
882 void tsm_screen_set_flags(struct tsm_screen *con, unsigned int flags)
892 if (!(old & TSM_SCREEN_ALTERNATE) && (flags & TSM_SCREEN_ALTERNATE))
893 con->lines = con->alt_lines;
896 void tsm_screen_reset_flags(struct tsm_screen *con, unsigned int flags)
904 con->flags &= ~flags;
906 if ((old & TSM_SCREEN_ALTERNATE) && (flags & TSM_SCREEN_ALTERNATE))
907 con->lines = con->main_lines;
910 unsigned int tsm_screen_get_flags(struct tsm_screen *con)
918 unsigned int tsm_screen_get_cursor_x(struct tsm_screen *con)
923 return con->cursor_x;
926 unsigned int tsm_screen_get_cursor_y(struct tsm_screen *con)
931 return con->cursor_y;
934 void tsm_screen_set_tabstop(struct tsm_screen *con)
936 if (!con || con->cursor_x >= con->size_x)
939 con->tab_ruler[con->cursor_x] = true;
942 void tsm_screen_reset_tabstop(struct tsm_screen *con)
944 if (!con || con->cursor_x >= con->size_x)
947 con->tab_ruler[con->cursor_x] = false;
950 void tsm_screen_reset_all_tabstops(struct tsm_screen *con)
957 for (i = 0; i < con->size_x; ++i)
958 con->tab_ruler[i] = false;
961 void tsm_screen_write(struct tsm_screen *con, tsm_symbol_t ch,
962 const struct tsm_screen_attr *attr)
964 unsigned int last, len;
969 len = tsm_symbol_get_width(NULL, ch);
973 if (con->cursor_y <= con->margin_bottom ||
974 con->cursor_y >= con->size_y)
975 last = con->margin_bottom;
977 last = con->size_y - 1;
979 if (con->cursor_x >= con->size_x) {
980 if (con->flags & TSM_SCREEN_AUTO_WRAP) {
984 con->cursor_x = con->size_x - 1;
988 if (con->cursor_y > last) {
989 con->cursor_y = last;
990 screen_scroll_up(con, 1);
993 screen_write(con, con->cursor_x, con->cursor_y, ch, len, attr);
994 con->cursor_x += len;
997 void tsm_screen_newline(struct tsm_screen *con)
1002 tsm_screen_move_down(con, 1, true);
1003 tsm_screen_move_line_home(con);
1006 void tsm_screen_scroll_up(struct tsm_screen *con, unsigned int num)
1011 screen_scroll_up(con, num);
1014 void tsm_screen_scroll_down(struct tsm_screen *con, unsigned int num)
1019 screen_scroll_down(con, num);
1022 void tsm_screen_move_to(struct tsm_screen *con, unsigned int x,
1030 if (con->flags & TSM_SCREEN_REL_ORIGIN)
1031 last = con->margin_bottom;
1033 last = con->size_y - 1;
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;
1039 con->cursor_y = to_abs_y(con, y);
1040 if (con->cursor_y > last)
1041 con->cursor_y = last;
1044 void tsm_screen_move_up(struct tsm_screen *con, unsigned int num,
1047 unsigned int diff, size;
1052 if (con->cursor_y >= con->margin_top)
1053 size = con->margin_top;
1057 diff = con->cursor_y - size;
1061 screen_scroll_down(con, num);
1062 con->cursor_y = size;
1064 con->cursor_y -= num;
1068 void tsm_screen_move_down(struct tsm_screen *con, unsigned int num,
1071 unsigned int diff, size;
1076 if (con->cursor_y <= con->margin_bottom)
1077 size = con->margin_bottom + 1;
1081 diff = size - con->cursor_y - 1;
1085 screen_scroll_up(con, num);
1086 con->cursor_y = size - 1;
1088 con->cursor_y += num;
1092 void tsm_screen_move_left(struct tsm_screen *con, unsigned int num)
1097 if (num > con->size_x)
1100 if (con->cursor_x >= con->size_x)
1101 con->cursor_x = con->size_x - 1;
1103 if (num > con->cursor_x)
1106 con->cursor_x -= num;
1109 void tsm_screen_move_right(struct tsm_screen *con, unsigned int num)
1114 if (num > con->size_x)
1117 if (num + con->cursor_x >= con->size_x)
1118 con->cursor_x = con->size_x - 1;
1120 con->cursor_x += num;
1123 void tsm_screen_move_line_end(struct tsm_screen *con)
1128 con->cursor_x = con->size_x - 1;
1131 void tsm_screen_move_line_home(struct tsm_screen *con)
1139 void tsm_screen_tab_right(struct tsm_screen *con, unsigned int num)
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])
1153 if (con->cursor_x + 1 >= con->size_x)
1157 /* tabs never cause pending new-lines */
1158 if (con->cursor_x >= con->size_x)
1159 con->cursor_x = con->size_x - 1;
1162 void tsm_screen_tab_left(struct tsm_screen *con, unsigned int num)
1170 for (i = 0; i < num; ++i) {
1171 for (j = con->cursor_x - 1; j > 0; --j) {
1172 if (con->tab_ruler[j])
1184 void tsm_screen_insert_lines(struct tsm_screen *con, unsigned int num)
1186 unsigned int i, j, max;
1191 if (con->cursor_y < con->margin_top ||
1192 con->cursor_y > con->margin_bottom)
1195 max = con->margin_bottom - con->cursor_y + 1;
1199 struct line *cache[num];
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]);
1208 memmove(&con->lines[con->cursor_y + num],
1209 &con->lines[con->cursor_y],
1210 (max - num) * sizeof(struct line*));
1212 memcpy(&con->lines[con->cursor_y],
1213 cache, num * sizeof(struct line*));
1219 void tsm_screen_delete_lines(struct tsm_screen *con, unsigned int num)
1221 unsigned int i, j, max;
1226 if (con->cursor_y < con->margin_top ||
1227 con->cursor_y > con->margin_bottom)
1230 max = con->margin_bottom - con->cursor_y + 1;
1234 struct line *cache[num];
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]);
1243 memmove(&con->lines[con->cursor_y],
1244 &con->lines[con->cursor_y + num],
1245 (max - num) * sizeof(struct line*));
1247 memcpy(&con->lines[con->cursor_y + (max - num)],
1248 cache, num * sizeof(struct line*));
1254 void tsm_screen_insert_chars(struct tsm_screen *con, unsigned int num)
1257 unsigned int max, mv, i;
1259 if (!con || !num || !con->size_y || !con->size_x)
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;
1267 max = con->size_x - con->cursor_x;
1272 cells = con->lines[con->cursor_y]->cells;
1274 memmove(&cells[con->cursor_x + num],
1275 &cells[con->cursor_x],
1276 mv * sizeof(*cells));
1278 for (i = 0; i < num; ++i) {
1279 cell_init(con, &cells[con->cursor_x + i]);
1283 void tsm_screen_delete_chars(struct tsm_screen *con, unsigned int num)
1286 unsigned int max, mv, i;
1288 if (!con || !num || !con->size_y || !con->size_x)
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;
1296 max = con->size_x - con->cursor_x;
1301 cells = con->lines[con->cursor_y]->cells;
1303 memmove(&cells[con->cursor_x],
1304 &cells[con->cursor_x + num],
1305 mv * sizeof(*cells));
1307 for (i = 0; i < num; ++i) {
1308 cell_init(con, &cells[con->cursor_x + mv + i]);
1312 void tsm_screen_erase_cursor(struct tsm_screen *con)
1319 if (con->cursor_x >= con->size_x)
1320 x = con->size_x - 1;
1324 screen_erase_region(con, x, con->cursor_y, x, con->cursor_y, false);
1327 void tsm_screen_erase_chars(struct tsm_screen *con, unsigned int num)
1334 if (con->cursor_x >= con->size_x)
1335 x = con->size_x - 1;
1339 screen_erase_region(con, x, con->cursor_y, x + num - 1, con->cursor_y,
1343 void tsm_screen_erase_cursor_to_end(struct tsm_screen *con,
1351 if (con->cursor_x >= con->size_x)
1352 x = con->size_x - 1;
1356 screen_erase_region(con, x, con->cursor_y, con->size_x - 1,
1357 con->cursor_y, protect);
1360 void tsm_screen_erase_home_to_cursor(struct tsm_screen *con,
1366 screen_erase_region(con, 0, con->cursor_y, con->cursor_x,
1367 con->cursor_y, protect);
1370 void tsm_screen_erase_current_line(struct tsm_screen *con,
1376 screen_erase_region(con, 0, con->cursor_y, con->size_x - 1,
1377 con->cursor_y, protect);
1380 void tsm_screen_erase_screen_to_cursor(struct tsm_screen *con,
1386 screen_erase_region(con, 0, 0, con->cursor_x, con->cursor_y, protect);
1389 void tsm_screen_erase_cursor_to_screen(struct tsm_screen *con,
1397 if (con->cursor_x >= con->size_x)
1398 x = con->size_x - 1;
1402 screen_erase_region(con, x, con->cursor_y, con->size_x - 1,
1403 con->size_y - 1, protect);
1406 void tsm_screen_erase_screen(struct tsm_screen *con, bool protect)
1411 screen_erase_region(con, 0, 0, con->size_x - 1, con->size_y - 1,
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:
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
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.
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.
1442 static void selection_set(struct tsm_screen *con, struct selection_pos *sel,
1443 unsigned int x, unsigned int y)
1462 void tsm_screen_selection_reset(struct tsm_screen *con)
1467 con->sel_active = false;
1470 void tsm_screen_selection_start(struct tsm_screen *con,
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));
1482 void tsm_screen_selection_target(struct tsm_screen *con,
1486 if (!con || !con->sel_active)
1489 selection_set(con, &con->sel_end, posx, posy);
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)
1498 unsigned int i, end;
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);
1506 pos += tsm_ucs4_to_utf8(' ', pos);
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)
1516 unsigned int len, i;
1517 struct selection_pos *start, *end;
1524 if (!con->sel_active)
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) {
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;
1552 start = &con->sel_end;
1553 end = &con->sel_start;
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;
1571 start = &con->sel_end;
1572 end = &con->sel_start;
1575 /* calculate size of buffer */
1578 if (!iter && start->y == SELECTION_TOP)
1579 iter = con->sb_first;
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;
1587 len += iter->size - start->x;
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)
1608 if (start->line || start->y == SELECTION_TOP)
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;
1618 len += con->size_x - start->x;
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)
1638 /* allocate buffer */
1646 /* copy data into buffer */
1648 if (!iter && start->y == SELECTION_TOP)
1649 iter = con->sb_first;
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;
1657 len = iter->size - start->x;
1658 pos += copy_line(iter, pos, start->x, len);
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)
1670 pos += copy_line(iter, pos, 0, len);
1673 pos += copy_line(iter, pos, 0, iter->size);
1681 if (start->line || start->y == SELECTION_TOP)
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;
1692 len = con->size_x - start->x;
1693 pos += copy_line(iter, pos, start->x, len);
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)
1705 pos += copy_line(iter, pos, 0, len);
1708 pos += copy_line(iter, pos, 0, con->size_x);
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,
1727 unsigned int cur_x, cur_y;
1728 unsigned int i, j, k;
1729 struct line *iter, *line = NULL;
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;
1738 bool in_sel = false, sel_start = false, sel_end = false;
1739 bool was_sel = false;
1741 if (!con || !draw_cb)
1744 cell_init(con, &empty);
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;
1753 /* render preparation */
1756 if (con->opts & TSM_SCREEN_OPT_RENDER_TIMING)
1757 shl_timer_reset(con->timer);
1759 ret = prepare_cb(con, data);
1762 "cannot prepare text-renderer for rendering");
1766 if (con->opts & TSM_SCREEN_OPT_RENDER_TIMING)
1767 time_prep = shl_timer_elapsed(con->timer);
1772 /* push each character into rendering pipeline */
1774 if (con->opts & TSM_SCREEN_OPT_RENDER_TIMING)
1775 shl_timer_reset(con->timer);
1780 if (con->sel_active) {
1781 if (!con->sel_start.line && con->sel_start.y == SELECTION_TOP)
1783 if (!con->sel_end.line && con->sel_end.y == SELECTION_TOP)
1786 if (con->sel_start.line &&
1787 (!iter || con->sel_start.line->sb_id < iter->sb_id))
1789 if (con->sel_end.line &&
1790 (!iter || con->sel_end.line->sb_id < iter->sb_id))
1794 for (i = 0; i < con->size_y; ++i) {
1799 line = con->lines[k];
1803 if (con->sel_active) {
1804 if (con->sel_start.line == line ||
1805 (!con->sel_start.line &&
1806 con->sel_start.y == k - 1))
1810 if (con->sel_end.line == line ||
1811 (!con->sel_end.line &&
1812 con->sel_end.y == k - 1))
1820 for (j = 0; j < con->size_x; ++j) {
1822 cell = &line->cells[j];
1825 memcpy(&attr, &cell->attr, sizeof(attr));
1827 if (con->sel_active) {
1829 j == con->sel_start.x) {
1834 j == con->sel_end.x) {
1840 if (k == cur_y + 1 &&
1843 if (!(con->flags & TSM_SCREEN_HIDE_CURSOR))
1844 attr.inverse = !attr.inverse;
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
1851 if (con->flags & TSM_SCREEN_INVERSE)
1852 attr.inverse = !attr.inverse;
1854 if (in_sel || was_sel) {
1856 attr.inverse = !attr.inverse;
1859 ch = tsm_symbol_get(NULL, &cell->ch, &len);
1860 if (cell->ch == ' ' || cell->ch == 0)
1862 ret = draw_cb(con, cell->ch, ch, len, cell->width,
1864 if (ret && warned++ < 3) {
1866 "cannot draw glyph at %ux%u via text-renderer",
1870 "suppressing further warnings during this rendering round");
1874 if (k == cur_y + 1 && !cursor_done) {
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);
1885 if (con->opts & TSM_SCREEN_OPT_RENDER_TIMING)
1886 time_draw = shl_timer_elapsed(con->timer);
1888 /* perform final rendering steps */
1891 if (con->opts & TSM_SCREEN_OPT_RENDER_TIMING)
1892 shl_timer_reset(con->timer);
1894 ret = render_cb(con, data);
1897 "cannot render via text-renderer");
1899 if (con->opts & TSM_SCREEN_OPT_RENDER_TIMING)
1900 time_rend = shl_timer_elapsed(con->timer);
1905 if (con->opts & TSM_SCREEN_OPT_RENDER_TIMING)
1907 "timing: sum: %" PRIu64 " prepare: %" PRIu64 " draw: %" PRIu64 " render: %" PRIu64,
1908 time_prep + time_draw + time_rend,
1909 time_prep, time_draw, time_rend);