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.
41 #include "shl_timer.h"
42 #include "tsm_screen.h"
43 #include "tsm_unicode.h"
45 #define LLOG_SUBSYSTEM "tsm_screen"
50 struct tsm_screen_attr attr;
62 #define SELECTION_TOP -1
63 struct selection_pos {
75 struct shl_timer *timer;
77 /* default attributes for new cells */
78 struct tsm_screen_attr def_attr;
83 unsigned int margin_top;
84 unsigned int margin_bottom;
85 unsigned int line_num;
87 struct line **main_lines;
88 struct line **alt_lines;
90 /* scroll-back buffer */
91 unsigned int sb_count; /* number of lines in sb */
92 struct line *sb_first; /* first line; was moved first */
93 struct line *sb_last; /* last line; was moved last*/
94 unsigned int sb_max; /* max-limit of lines in sb */
95 struct line *sb_pos; /* current position in sb or NULL */
96 uint64_t sb_last_id; /* last id given to sb-line */
99 unsigned int cursor_x;
100 unsigned int cursor_y;
107 struct selection_pos sel_start;
108 struct selection_pos sel_end;
111 static void cell_init(struct tsm_screen *con, struct cell *cell)
115 memcpy(&cell->attr, &con->def_attr, sizeof(cell->attr));
118 static int line_new(struct tsm_screen *con, struct line **out,
127 line = malloc(sizeof(*line));
134 line->cells = malloc(sizeof(struct cell) * width);
140 for (i = 0; i < width; ++i)
141 cell_init(con, &line->cells[i]);
147 static void line_free(struct line *line)
153 static int line_resize(struct tsm_screen *con, struct line *line,
161 if (line->size < width) {
162 tmp = realloc(line->cells, width * sizeof(struct cell));
168 while (line->size < width) {
169 cell_init(con, &line->cells[line->size]);
177 /* This links the given line into the scrollback-buffer */
178 static void link_to_scrollback(struct tsm_screen *con, struct line *line)
182 if (con->sb_max == 0) {
183 if (con->sel_active) {
184 if (con->sel_start.line == line) {
185 con->sel_start.line = NULL;
186 con->sel_start.y = SELECTION_TOP;
188 if (con->sel_end.line == line) {
189 con->sel_end.line = NULL;
190 con->sel_end.y = SELECTION_TOP;
197 /* Remove a line from the scrollback buffer if it reaches its maximum.
198 * We must take care to correctly keep the current position as the new
199 * line is linked in after we remove the top-most line here.
200 * sb_max == 0 is tested earlier so we can assume sb_max > 0 here. In
201 * other words, buf->sb_first is a valid line if sb_count >= sb_max. */
202 if (con->sb_count >= con->sb_max) {
204 con->sb_first = tmp->next;
206 tmp->next->prev = NULL;
211 /* (position == tmp && !next) means we have sb_max=1 so set
212 * position to the new line. Otherwise, set to new first line.
213 * If position!=tmp and we have a fixed-position then nothing
214 * needs to be done because we can stay at the same line. If we
215 * have no fixed-position, we need to set the position to the
216 * next inserted line, which can be "line", too. */
218 if (con->sb_pos == tmp ||
219 !(con->flags & TSM_SCREEN_FIXED_POS)) {
220 if (con->sb_pos->next)
221 con->sb_pos = con->sb_pos->next;
227 if (con->sel_active) {
228 if (con->sel_start.line == tmp) {
229 con->sel_start.line = NULL;
230 con->sel_start.y = SELECTION_TOP;
232 if (con->sel_end.line == tmp) {
233 con->sel_end.line = NULL;
234 con->sel_end.y = SELECTION_TOP;
240 line->sb_id = ++con->sb_last_id;
242 line->prev = con->sb_last;
244 con->sb_last->next = line;
246 con->sb_first = line;
251 static void screen_scroll_up(struct tsm_screen *con, unsigned int num)
253 unsigned int i, j, max, pos;
259 max = con->margin_bottom + 1 - con->margin_top;
263 /* We cache lines on the stack to speed up the scrolling. However, if
264 * num is too big we might get overflows here so use recursion if num
265 * exceeds a hard-coded limit.
266 * 128 seems to be a sane limit that should never be reached but should
267 * also be small enough so we do not get stack overflows. */
269 screen_scroll_up(con, 128);
270 return screen_scroll_up(con, num - 128);
272 struct line *cache[num];
274 for (i = 0; i < num; ++i) {
275 pos = con->margin_top + i;
276 if (!(con->flags & TSM_SCREEN_ALTERNATE))
277 ret = line_new(con, &cache[i], con->size_x);
282 link_to_scrollback(con, con->lines[pos]);
284 cache[i] = con->lines[pos];
285 for (j = 0; j < con->size_x; ++j)
286 cell_init(con, &cache[i]->cells[j]);
291 memmove(&con->lines[con->margin_top],
292 &con->lines[con->margin_top + num],
293 (max - num) * sizeof(struct line*));
296 memcpy(&con->lines[con->margin_top + (max - num)],
297 cache, num * sizeof(struct line*));
299 if (con->sel_active) {
300 if (!con->sel_start.line && con->sel_start.y >= 0) {
301 con->sel_start.y -= num;
302 if (con->sel_start.y < 0) {
303 con->sel_start.line = con->sb_last;
304 while (con->sel_start.line && ++con->sel_start.y < 0)
305 con->sel_start.line = con->sel_start.line->prev;
306 con->sel_start.y = SELECTION_TOP;
309 if (!con->sel_end.line && con->sel_end.y >= 0) {
310 con->sel_end.y -= num;
311 if (con->sel_end.y < 0) {
312 con->sel_end.line = con->sb_last;
313 while (con->sel_end.line && ++con->sel_end.y < 0)
314 con->sel_end.line = con->sel_end.line->prev;
315 con->sel_end.y = SELECTION_TOP;
321 static void screen_scroll_down(struct tsm_screen *con, unsigned int num)
323 unsigned int i, j, max;
328 max = con->margin_bottom + 1 - con->margin_top;
332 /* see screen_scroll_up() for an explanation */
334 screen_scroll_down(con, 128);
335 return screen_scroll_down(con, num - 128);
337 struct line *cache[num];
339 for (i = 0; i < num; ++i) {
340 cache[i] = con->lines[con->margin_bottom - i];
341 for (j = 0; j < con->size_x; ++j)
342 cell_init(con, &cache[i]->cells[j]);
346 memmove(&con->lines[con->margin_top + num],
347 &con->lines[con->margin_top],
348 (max - num) * sizeof(struct line*));
351 memcpy(&con->lines[con->margin_top],
352 cache, num * sizeof(struct line*));
354 if (con->sel_active) {
355 if (!con->sel_start.line && con->sel_start.y >= 0)
356 con->sel_start.y += num;
357 if (!con->sel_end.line && con->sel_end.y >= 0)
358 con->sel_end.y += num;
362 static void screen_write(struct tsm_screen *con, unsigned int x,
363 unsigned int y, tsm_symbol_t ch, unsigned int len,
364 const struct tsm_screen_attr *attr)
372 if (x >= con->size_x || y >= con->size_y) {
373 llog_warn(con, "writing beyond buffer boundary");
377 line = con->lines[y];
379 if ((con->flags & TSM_SCREEN_INSERT_MODE) &&
380 (int)x < ((int)con->size_x - len))
381 memmove(&line->cells[x + len], &line->cells[x],
382 sizeof(struct cell) * (con->size_x - len - x));
384 line->cells[x].ch = ch;
385 line->cells[x].width = len;
386 memcpy(&line->cells[x].attr, attr, sizeof(*attr));
388 for (i = 1; i < len && i + x < con->size_x; ++i)
389 line->cells[x + i].width = 0;
392 static void screen_erase_region(struct tsm_screen *con,
402 if (y_to >= con->size_y)
403 y_to = con->size_y - 1;
404 if (x_to >= con->size_x)
405 x_to = con->size_x - 1;
407 for ( ; y_from <= y_to; ++y_from) {
408 line = con->lines[y_from];
417 to = con->size_x - 1;
418 for ( ; x_from <= to; ++x_from) {
419 if (protect && line->cells[x_from].attr.protect)
422 cell_init(con, &line->cells[x_from]);
428 static inline unsigned int to_abs_x(struct tsm_screen *con, unsigned int x)
433 static inline unsigned int to_abs_y(struct tsm_screen *con, unsigned int y)
435 if (!(con->flags & TSM_SCREEN_REL_ORIGIN))
438 return con->margin_top + y;
442 int tsm_screen_new(struct tsm_screen **out, tsm_log_t log, void *log_data)
444 struct tsm_screen *con;
451 con = malloc(sizeof(*con));
455 memset(con, 0, sizeof(*con));
458 con->llog_data = log_data;
459 con->def_attr.fr = 255;
460 con->def_attr.fg = 255;
461 con->def_attr.fb = 255;
463 ret = shl_timer_new(&con->timer);
467 ret = tsm_screen_resize(con, 80, 24);
471 llog_debug(con, "new screen");
477 shl_timer_free(con->timer);
478 for (i = 0; i < con->line_num; ++i) {
479 line_free(con->main_lines[i]);
480 line_free(con->alt_lines[i]);
482 free(con->main_lines);
483 free(con->alt_lines);
484 free(con->tab_ruler);
491 void tsm_screen_ref(struct tsm_screen *con)
500 void tsm_screen_unref(struct tsm_screen *con)
504 if (!con || !con->ref || --con->ref)
507 llog_debug(con, "destroying screen");
509 for (i = 0; i < con->line_num; ++i) {
510 line_free(con->main_lines[i]);
511 line_free(con->alt_lines[i]);
513 free(con->main_lines);
514 free(con->alt_lines);
515 free(con->tab_ruler);
516 shl_timer_free(con->timer);
521 void tsm_screen_set_opts(struct tsm_screen *scr, unsigned int opts)
530 void tsm_screen_reset_opts(struct tsm_screen *scr, unsigned int opts)
539 unsigned int tsm_screen_get_opts(struct tsm_screen *scr)
548 unsigned int tsm_screen_get_width(struct tsm_screen *con)
557 unsigned int tsm_screen_get_height(struct tsm_screen *con)
566 int tsm_screen_resize(struct tsm_screen *con, unsigned int x,
570 unsigned int i, j, width, diff;
574 if (!con || !x || !y)
577 if (con->size_x == x && con->size_y == y)
580 /* First make sure the line buffer is big enough for our new screen.
581 * That is, allocate all new lines and make sure each line has enough
582 * cells to hold the new screen or the current screen. If we fail, we
583 * can safely return -ENOMEM and the buffer is still valid. We must
584 * allocate the new lines to at least the same size as the current
585 * lines. Otherwise, if this function fails in later turns, we will have
586 * invalid lines in the buffer. */
587 if (y > con->line_num) {
588 /* resize main buffer */
589 cache = realloc(con->main_lines, sizeof(struct line*) * y);
593 if (con->lines == con->main_lines)
595 con->main_lines = cache;
597 /* resize alt buffer */
598 cache = realloc(con->alt_lines, sizeof(struct line*) * y);
602 if (con->lines == con->alt_lines)
604 con->alt_lines = cache;
606 /* allocate new lines */
612 while (con->line_num < y) {
613 ret = line_new(con, &con->main_lines[con->line_num],
618 ret = line_new(con, &con->alt_lines[con->line_num],
621 line_free(con->main_lines[con->line_num]);
629 /* Resize all lines in the buffer if we increase screen width. This
630 * will guarantee that all lines are big enough so we can resize the
631 * buffer without reallocating them later. */
632 if (x > con->size_x) {
633 tab_ruler = realloc(con->tab_ruler, sizeof(bool) * x);
636 con->tab_ruler = tab_ruler;
638 for (i = 0; i < con->line_num; ++i) {
639 ret = line_resize(con, con->main_lines[i], x);
643 ret = line_resize(con, con->alt_lines[i], x);
649 for (j = 0; j < con->line_num; ++j) {
650 if (j >= con->size_y)
655 if (x < con->main_lines[j]->size)
658 width = con->main_lines[j]->size;
659 for (; i < width; ++i)
660 cell_init(con, &con->main_lines[j]->cells[i]);
662 if (x < con->alt_lines[j]->size)
665 width = con->alt_lines[j]->size;
666 for (; i < width; ++i)
667 cell_init(con, &con->alt_lines[j]->cells[i]);
670 /* xterm destroys margins on resize, so do we */
672 con->margin_bottom = con->size_y - 1;
675 for (i = 0; i < x; ++i) {
677 con->tab_ruler[i] = true;
679 con->tab_ruler[i] = false;
682 /* We need to adjust x-size first as screen_scroll_up() and friends may
683 * have to reallocate lines. The y-size is adjusted after them to avoid
684 * missing lines when shrinking y-size.
685 * We need to carefully look for the functions that we call here as they
686 * have stronger invariants as when called normally. */
689 if (con->cursor_x >= con->size_x)
690 con->cursor_x = con->size_x - 1;
692 /* scroll buffer if screen height shrinks */
693 if (con->size_y != 0 && y < con->size_y) {
694 diff = con->size_y - y;
695 screen_scroll_up(con, diff);
696 if (con->cursor_y > diff)
697 con->cursor_y -= diff;
703 con->margin_bottom = con->size_y - 1;
704 if (con->cursor_y >= con->size_y)
705 con->cursor_y = con->size_y - 1;
711 int tsm_screen_set_margins(struct tsm_screen *con,
712 unsigned int top, unsigned int bottom)
714 unsigned int upper, lower;
724 lower = con->size_y - 1;
725 } else if (bottom > con->size_y) {
727 lower = con->size_y - 1;
733 con->margin_top = upper;
734 con->margin_bottom = lower;
738 /* set maximum scrollback buffer size */
740 void tsm_screen_set_max_sb(struct tsm_screen *con,
748 while (con->sb_count > max) {
749 line = con->sb_first;
750 con->sb_first = line->next;
752 line->next->prev = NULL;
757 /* We treat fixed/unfixed position the same here because we
758 * remove lines from the TOP of the scrollback buffer. */
759 if (con->sb_pos == line)
760 con->sb_pos = con->sb_first;
762 if (con->sel_active) {
763 if (con->sel_start.line == line) {
764 con->sel_start.line = NULL;
765 con->sel_start.y = SELECTION_TOP;
767 if (con->sel_end.line == line) {
768 con->sel_end.line = NULL;
769 con->sel_end.y = SELECTION_TOP;
778 /* clear scrollback buffer */
780 void tsm_screen_clear_sb(struct tsm_screen *con)
782 struct line *iter, *tmp;
787 for (iter = con->sb_first; iter; ) {
793 con->sb_first = NULL;
798 if (con->sel_active) {
799 if (con->sel_start.line) {
800 con->sel_start.line = NULL;
801 con->sel_start.y = SELECTION_TOP;
803 if (con->sel_end.line) {
804 con->sel_end.line = NULL;
805 con->sel_end.y = SELECTION_TOP;
811 void tsm_screen_sb_up(struct tsm_screen *con, unsigned int num)
818 if (!con->sb_pos->prev)
821 con->sb_pos = con->sb_pos->prev;
822 } else if (!con->sb_last) {
825 con->sb_pos = con->sb_last;
831 void tsm_screen_sb_down(struct tsm_screen *con, unsigned int num)
838 con->sb_pos = con->sb_pos->next;
848 void tsm_screen_sb_page_up(struct tsm_screen *con, unsigned int num)
853 tsm_screen_sb_up(con, num * con->size_y);
857 void tsm_screen_sb_page_down(struct tsm_screen *con, unsigned int num)
862 tsm_screen_sb_down(con, num * con->size_y);
866 void tsm_screen_sb_reset(struct tsm_screen *con)
875 void tsm_screen_set_def_attr(struct tsm_screen *con,
876 const struct tsm_screen_attr *attr)
881 memcpy(&con->def_attr, attr, sizeof(*attr));
885 void tsm_screen_reset(struct tsm_screen *con)
894 con->margin_bottom = con->size_y - 1;
896 for (i = 0; i < con->size_x; ++i) {
898 con->tab_ruler[i] = true;
900 con->tab_ruler[i] = false;
905 void tsm_screen_set_flags(struct tsm_screen *con, unsigned int flags)
915 if (!(old & TSM_SCREEN_ALTERNATE) && (flags & TSM_SCREEN_ALTERNATE))
916 con->lines = con->alt_lines;
920 void tsm_screen_reset_flags(struct tsm_screen *con, unsigned int flags)
928 con->flags &= ~flags;
930 if ((old & TSM_SCREEN_ALTERNATE) && (flags & TSM_SCREEN_ALTERNATE))
931 con->lines = con->main_lines;
935 unsigned int tsm_screen_get_flags(struct tsm_screen *con)
944 unsigned int tsm_screen_get_cursor_x(struct tsm_screen *con)
949 return con->cursor_x;
953 unsigned int tsm_screen_get_cursor_y(struct tsm_screen *con)
958 return con->cursor_y;
962 void tsm_screen_set_tabstop(struct tsm_screen *con)
964 if (!con || con->cursor_x >= con->size_x)
967 con->tab_ruler[con->cursor_x] = true;
971 void tsm_screen_reset_tabstop(struct tsm_screen *con)
973 if (!con || con->cursor_x >= con->size_x)
976 con->tab_ruler[con->cursor_x] = false;
980 void tsm_screen_reset_all_tabstops(struct tsm_screen *con)
987 for (i = 0; i < con->size_x; ++i)
988 con->tab_ruler[i] = false;
992 void tsm_screen_write(struct tsm_screen *con, tsm_symbol_t ch,
993 const struct tsm_screen_attr *attr)
995 unsigned int last, len;
1000 len = tsm_symbol_get_width(NULL, ch);
1004 if (con->cursor_y <= con->margin_bottom ||
1005 con->cursor_y >= con->size_y)
1006 last = con->margin_bottom;
1008 last = con->size_y - 1;
1010 if (con->cursor_x >= con->size_x) {
1011 if (con->flags & TSM_SCREEN_AUTO_WRAP) {
1015 con->cursor_x = con->size_x - 1;
1019 if (con->cursor_y > last) {
1020 con->cursor_y = last;
1021 screen_scroll_up(con, 1);
1024 screen_write(con, con->cursor_x, con->cursor_y, ch, len, attr);
1025 con->cursor_x += len;
1029 void tsm_screen_newline(struct tsm_screen *con)
1034 tsm_screen_move_down(con, 1, true);
1035 tsm_screen_move_line_home(con);
1039 void tsm_screen_scroll_up(struct tsm_screen *con, unsigned int num)
1044 screen_scroll_up(con, num);
1048 void tsm_screen_scroll_down(struct tsm_screen *con, unsigned int num)
1053 screen_scroll_down(con, num);
1057 void tsm_screen_move_to(struct tsm_screen *con, unsigned int x,
1065 if (con->flags & TSM_SCREEN_REL_ORIGIN)
1066 last = con->margin_bottom;
1068 last = con->size_y - 1;
1070 con->cursor_x = to_abs_x(con, x);
1071 if (con->cursor_x >= con->size_x)
1072 con->cursor_x = con->size_x - 1;
1074 con->cursor_y = to_abs_y(con, y);
1075 if (con->cursor_y > last)
1076 con->cursor_y = last;
1080 void tsm_screen_move_up(struct tsm_screen *con, unsigned int num,
1083 unsigned int diff, size;
1088 if (con->cursor_y >= con->margin_top)
1089 size = con->margin_top;
1093 diff = con->cursor_y - size;
1097 screen_scroll_down(con, num);
1098 con->cursor_y = size;
1100 con->cursor_y -= num;
1105 void tsm_screen_move_down(struct tsm_screen *con, unsigned int num,
1108 unsigned int diff, size;
1113 if (con->cursor_y <= con->margin_bottom)
1114 size = con->margin_bottom + 1;
1118 diff = size - con->cursor_y - 1;
1122 screen_scroll_up(con, num);
1123 con->cursor_y = size - 1;
1125 con->cursor_y += num;
1130 void tsm_screen_move_left(struct tsm_screen *con, unsigned int num)
1135 if (num > con->size_x)
1138 if (con->cursor_x >= con->size_x)
1139 con->cursor_x = con->size_x - 1;
1141 if (num > con->cursor_x)
1144 con->cursor_x -= num;
1148 void tsm_screen_move_right(struct tsm_screen *con, unsigned int num)
1153 if (num > con->size_x)
1156 if (num + con->cursor_x >= con->size_x)
1157 con->cursor_x = con->size_x - 1;
1159 con->cursor_x += num;
1163 void tsm_screen_move_line_end(struct tsm_screen *con)
1168 con->cursor_x = con->size_x - 1;
1172 void tsm_screen_move_line_home(struct tsm_screen *con)
1181 void tsm_screen_tab_right(struct tsm_screen *con, unsigned int num)
1188 for (i = 0; i < num; ++i) {
1189 for (j = con->cursor_x + 1; j < con->size_x; ++j) {
1190 if (con->tab_ruler[j])
1195 if (con->cursor_x + 1 >= con->size_x)
1199 /* tabs never cause pending new-lines */
1200 if (con->cursor_x >= con->size_x)
1201 con->cursor_x = con->size_x - 1;
1205 void tsm_screen_tab_left(struct tsm_screen *con, unsigned int num)
1213 for (i = 0; i < num; ++i) {
1214 for (j = con->cursor_x - 1; j > 0; --j) {
1215 if (con->tab_ruler[j])
1228 void tsm_screen_insert_lines(struct tsm_screen *con, unsigned int num)
1230 unsigned int i, j, max;
1235 if (con->cursor_y < con->margin_top ||
1236 con->cursor_y > con->margin_bottom)
1239 max = con->margin_bottom - con->cursor_y + 1;
1243 struct line *cache[num];
1245 for (i = 0; i < num; ++i) {
1246 cache[i] = con->lines[con->margin_bottom - i];
1247 for (j = 0; j < con->size_x; ++j)
1248 cell_init(con, &cache[i]->cells[j]);
1252 memmove(&con->lines[con->cursor_y + num],
1253 &con->lines[con->cursor_y],
1254 (max - num) * sizeof(struct line*));
1256 memcpy(&con->lines[con->cursor_y],
1257 cache, num * sizeof(struct line*));
1264 void tsm_screen_delete_lines(struct tsm_screen *con, unsigned int num)
1266 unsigned int i, j, max;
1271 if (con->cursor_y < con->margin_top ||
1272 con->cursor_y > con->margin_bottom)
1275 max = con->margin_bottom - con->cursor_y + 1;
1279 struct line *cache[num];
1281 for (i = 0; i < num; ++i) {
1282 cache[i] = con->lines[con->cursor_y + i];
1283 for (j = 0; j < con->size_x; ++j)
1284 cell_init(con, &cache[i]->cells[j]);
1288 memmove(&con->lines[con->cursor_y],
1289 &con->lines[con->cursor_y + num],
1290 (max - num) * sizeof(struct line*));
1292 memcpy(&con->lines[con->cursor_y + (max - num)],
1293 cache, num * sizeof(struct line*));
1300 void tsm_screen_insert_chars(struct tsm_screen *con, unsigned int num)
1303 unsigned int max, mv, i;
1305 if (!con || !num || !con->size_y || !con->size_x)
1308 if (con->cursor_x >= con->size_x)
1309 con->cursor_x = con->size_x - 1;
1310 if (con->cursor_y >= con->size_y)
1311 con->cursor_y = con->size_y - 1;
1313 max = con->size_x - con->cursor_x;
1318 cells = con->lines[con->cursor_y]->cells;
1320 memmove(&cells[con->cursor_x + num],
1321 &cells[con->cursor_x],
1322 mv * sizeof(*cells));
1324 for (i = 0; i < num; ++i) {
1325 cell_init(con, &cells[con->cursor_x + i]);
1330 void tsm_screen_delete_chars(struct tsm_screen *con, unsigned int num)
1333 unsigned int max, mv, i;
1335 if (!con || !num || !con->size_y || !con->size_x)
1338 if (con->cursor_x >= con->size_x)
1339 con->cursor_x = con->size_x - 1;
1340 if (con->cursor_y >= con->size_y)
1341 con->cursor_y = con->size_y - 1;
1343 max = con->size_x - con->cursor_x;
1348 cells = con->lines[con->cursor_y]->cells;
1350 memmove(&cells[con->cursor_x],
1351 &cells[con->cursor_x + num],
1352 mv * sizeof(*cells));
1354 for (i = 0; i < num; ++i) {
1355 cell_init(con, &cells[con->cursor_x + mv + i]);
1360 void tsm_screen_erase_cursor(struct tsm_screen *con)
1367 if (con->cursor_x >= con->size_x)
1368 x = con->size_x - 1;
1372 screen_erase_region(con, x, con->cursor_y, x, con->cursor_y, false);
1376 void tsm_screen_erase_chars(struct tsm_screen *con, unsigned int num)
1383 if (con->cursor_x >= con->size_x)
1384 x = con->size_x - 1;
1388 screen_erase_region(con, x, con->cursor_y, x + num - 1, con->cursor_y,
1393 void tsm_screen_erase_cursor_to_end(struct tsm_screen *con,
1401 if (con->cursor_x >= con->size_x)
1402 x = con->size_x - 1;
1406 screen_erase_region(con, x, con->cursor_y, con->size_x - 1,
1407 con->cursor_y, protect);
1411 void tsm_screen_erase_home_to_cursor(struct tsm_screen *con,
1417 screen_erase_region(con, 0, con->cursor_y, con->cursor_x,
1418 con->cursor_y, protect);
1422 void tsm_screen_erase_current_line(struct tsm_screen *con,
1428 screen_erase_region(con, 0, con->cursor_y, con->size_x - 1,
1429 con->cursor_y, protect);
1433 void tsm_screen_erase_screen_to_cursor(struct tsm_screen *con,
1439 screen_erase_region(con, 0, 0, con->cursor_x, con->cursor_y, protect);
1443 void tsm_screen_erase_cursor_to_screen(struct tsm_screen *con,
1451 if (con->cursor_x >= con->size_x)
1452 x = con->size_x - 1;
1456 screen_erase_region(con, x, con->cursor_y, con->size_x - 1,
1457 con->size_y - 1, protect);
1461 void tsm_screen_erase_screen(struct tsm_screen *con, bool protect)
1466 screen_erase_region(con, 0, 0, con->size_x - 1, con->size_y - 1,
1472 * If a running pty-client does not support mouse-tracking extensions, a
1473 * terminal can manually mark selected areas if it does mouse-tracking itself.
1474 * This tracking is slightly different than the integrated client-tracking:
1476 * Initial state is no-selection. At any time selection_reset() can be called to
1477 * clear the selection and go back to initial state.
1478 * If the user presses a mouse-button, the terminal can calculate the selected
1479 * cell and call selection_start() to notify the terminal that the user started
1480 * the selection. While the mouse-button is held down, the terminal should call
1481 * selection_target() whenever a mouse-event occurs. This will tell the screen
1482 * layer to draw the selection from the initial start up to the last given
1484 * Please note that the selection-start cannot be modified by the terminal
1485 * during a selection. Instead, the screen-layer automatically moves it along
1486 * with any scroll-operations or inserts/deletes. This also means, the terminal
1487 * must _not_ cache the start-position itself as it may change under the hood.
1488 * This selection takes also care of scrollback-buffer selections and correctly
1489 * moves selection state along.
1491 * Please note that this is not the kind of selection that some PTY applications
1492 * support. If the client supports the mouse-protocol, then it can also control
1493 * a separate screen-selection which is always inside of the actual screen. This
1494 * is a totally different selection.
1497 static void selection_set(struct tsm_screen *con, struct selection_pos *sel,
1498 unsigned int x, unsigned int y)
1518 void tsm_screen_selection_reset(struct tsm_screen *con)
1523 con->sel_active = false;
1527 void tsm_screen_selection_start(struct tsm_screen *con,
1534 con->sel_active = true;
1535 selection_set(con, &con->sel_start, posx, posy);
1536 memcpy(&con->sel_end, &con->sel_start, sizeof(con->sel_end));
1540 void tsm_screen_selection_target(struct tsm_screen *con,
1544 if (!con || !con->sel_active)
1547 selection_set(con, &con->sel_end, posx, posy);
1550 /* TODO: tsm_ucs4_to_utf8 expects UCS4 characters, but a cell contains a
1551 * tsm-symbol (which can contain multiple UCS4 chars). Fix this when introducing
1552 * support for combining characters. */
1553 static unsigned int copy_line(struct line *line, char *buf,
1554 unsigned int start, unsigned int len)
1556 unsigned int i, end;
1560 for (i = start; i < line->size && i < end; ++i) {
1561 if (i < line->size || !line->cells[i].ch)
1562 pos += tsm_ucs4_to_utf8(line->cells[i].ch, pos);
1564 pos += tsm_ucs4_to_utf8(' ', pos);
1570 /* TODO: This beast definitely needs some "beautification", however, it's meant
1571 * as a "proof-of-concept" so its enough for now. */
1573 int tsm_screen_selection_copy(struct tsm_screen *con, char **out)
1575 unsigned int len, i;
1576 struct selection_pos *start, *end;
1583 if (!con->sel_active)
1586 /* check whether sel_start or sel_end comes first */
1587 if (!con->sel_start.line && con->sel_start.y == SELECTION_TOP) {
1588 if (!con->sel_end.line && con->sel_end.y == SELECTION_TOP) {
1595 start = &con->sel_start;
1596 end = &con->sel_end;
1597 } else if (!con->sel_end.line && con->sel_end.y == SELECTION_TOP) {
1598 start = &con->sel_end;
1599 end = &con->sel_start;
1600 } else if (con->sel_start.line && con->sel_end.line) {
1601 if (con->sel_start.line->sb_id < con->sel_end.line->sb_id) {
1602 start = &con->sel_start;
1603 end = &con->sel_end;
1604 } else if (con->sel_start.line->sb_id > con->sel_end.line->sb_id) {
1605 start = &con->sel_end;
1606 end = &con->sel_start;
1607 } else if (con->sel_start.x < con->sel_end.x) {
1608 start = &con->sel_start;
1609 end = &con->sel_end;
1611 start = &con->sel_end;
1612 end = &con->sel_start;
1614 } else if (con->sel_start.line) {
1615 start = &con->sel_start;
1616 end = &con->sel_end;
1617 } else if (con->sel_end.line) {
1618 start = &con->sel_end;
1619 end = &con->sel_start;
1620 } else if (con->sel_start.y < con->sel_end.y) {
1621 start = &con->sel_start;
1622 end = &con->sel_end;
1623 } else if (con->sel_start.y > con->sel_end.y) {
1624 start = &con->sel_end;
1625 end = &con->sel_start;
1626 } else if (con->sel_start.x < con->sel_end.x) {
1627 start = &con->sel_start;
1628 end = &con->sel_end;
1630 start = &con->sel_end;
1631 end = &con->sel_start;
1634 /* calculate size of buffer */
1637 if (!iter && start->y == SELECTION_TOP)
1638 iter = con->sb_first;
1641 if (iter == start->line && iter == end->line) {
1642 if (iter->size > start->x) {
1643 if (iter->size > end->x)
1644 len += end->x - start->x + 1;
1646 len += iter->size - start->x;
1649 } else if (iter == start->line) {
1650 if (iter->size > start->x)
1651 len += iter->size - start->x;
1652 } else if (iter == end->line) {
1653 if (iter->size > end->x)
1667 if (start->line || start->y == SELECTION_TOP)
1671 for ( ; i < con->size_y; ++i) {
1672 if (!start->line && start->y == i && end->y == i) {
1673 if (con->size_x > start->x) {
1674 if (con->size_x > end->x)
1675 len += end->x - start->x + 1;
1677 len += con->size_x - start->x;
1680 } else if (!start->line && start->y == i) {
1681 if (con->size_x > start->x)
1682 len += con->size_x - start->x;
1683 } else if (end->y == i) {
1684 if (con->size_x > end->x)
1697 /* allocate buffer */
1705 /* copy data into buffer */
1707 if (!iter && start->y == SELECTION_TOP)
1708 iter = con->sb_first;
1711 if (iter == start->line && iter == end->line) {
1712 if (iter->size > start->x) {
1713 if (iter->size > end->x)
1714 len = end->x - start->x + 1;
1716 len = iter->size - start->x;
1717 pos += copy_line(iter, pos, start->x, len);
1720 } else if (iter == start->line) {
1721 if (iter->size > start->x)
1722 pos += copy_line(iter, pos, start->x,
1723 iter->size - start->x);
1724 } else if (iter == end->line) {
1725 if (iter->size > end->x)
1729 pos += copy_line(iter, pos, 0, len);
1732 pos += copy_line(iter, pos, 0, iter->size);
1740 if (start->line || start->y == SELECTION_TOP)
1744 for ( ; i < con->size_y; ++i) {
1745 iter = con->lines[i];
1746 if (!start->line && start->y == i && end->y == i) {
1747 if (con->size_x > start->x) {
1748 if (con->size_x > end->x)
1749 len = end->x - start->x + 1;
1751 len = con->size_x - start->x;
1752 pos += copy_line(iter, pos, start->x, len);
1755 } else if (!start->line && start->y == i) {
1756 if (con->size_x > start->x)
1757 pos += copy_line(iter, pos, start->x,
1758 con->size_x - start->x);
1759 } else if (end->y == i) {
1760 if (con->size_x > end->x)
1764 pos += copy_line(iter, pos, 0, len);
1767 pos += copy_line(iter, pos, 0, con->size_x);
1781 void tsm_screen_draw(struct tsm_screen *con,
1782 tsm_screen_prepare_cb prepare_cb,
1783 tsm_screen_draw_cb draw_cb,
1784 tsm_screen_render_cb render_cb,
1787 unsigned int cur_x, cur_y;
1788 unsigned int i, j, k;
1789 struct line *iter, *line = NULL;
1791 struct tsm_screen_attr attr;
1792 bool cursor_done = false;
1793 int ret, warned = 0;
1794 uint64_t time_prep = 0, time_draw = 0, time_rend = 0;
1798 bool in_sel = false, sel_start = false, sel_end = false;
1799 bool was_sel = false;
1801 if (!con || !draw_cb)
1804 cell_init(con, &empty);
1806 cur_x = con->cursor_x;
1807 if (con->cursor_x >= con->size_x)
1808 cur_x = con->size_x - 1;
1809 cur_y = con->cursor_y;
1810 if (con->cursor_y >= con->size_y)
1811 cur_y = con->size_y - 1;
1813 /* render preparation */
1816 if (con->opts & TSM_SCREEN_OPT_RENDER_TIMING)
1817 shl_timer_reset(con->timer);
1819 ret = prepare_cb(con, data);
1822 "cannot prepare text-renderer for rendering");
1826 if (con->opts & TSM_SCREEN_OPT_RENDER_TIMING)
1827 time_prep = shl_timer_elapsed(con->timer);
1832 /* push each character into rendering pipeline */
1834 if (con->opts & TSM_SCREEN_OPT_RENDER_TIMING)
1835 shl_timer_reset(con->timer);
1840 if (con->sel_active) {
1841 if (!con->sel_start.line && con->sel_start.y == SELECTION_TOP)
1843 if (!con->sel_end.line && con->sel_end.y == SELECTION_TOP)
1846 if (con->sel_start.line &&
1847 (!iter || con->sel_start.line->sb_id < iter->sb_id))
1849 if (con->sel_end.line &&
1850 (!iter || con->sel_end.line->sb_id < iter->sb_id))
1854 for (i = 0; i < con->size_y; ++i) {
1859 line = con->lines[k];
1863 if (con->sel_active) {
1864 if (con->sel_start.line == line ||
1865 (!con->sel_start.line &&
1866 con->sel_start.y == k - 1))
1870 if (con->sel_end.line == line ||
1871 (!con->sel_end.line &&
1872 con->sel_end.y == k - 1))
1880 for (j = 0; j < con->size_x; ++j) {
1882 cell = &line->cells[j];
1885 memcpy(&attr, &cell->attr, sizeof(attr));
1887 if (con->sel_active) {
1889 j == con->sel_start.x) {
1894 j == con->sel_end.x) {
1900 if (k == cur_y + 1 &&
1903 if (!(con->flags & TSM_SCREEN_HIDE_CURSOR))
1904 attr.inverse = !attr.inverse;
1907 /* TODO: do some more sophisticated inverse here. When
1908 * INVERSE mode is set, we should instead just select
1909 * inverse colors instead of switching background and
1911 if (con->flags & TSM_SCREEN_INVERSE)
1912 attr.inverse = !attr.inverse;
1914 if (in_sel || was_sel) {
1916 attr.inverse = !attr.inverse;
1919 ch = tsm_symbol_get(NULL, &cell->ch, &len);
1920 if (cell->ch == ' ' || cell->ch == 0)
1922 ret = draw_cb(con, cell->ch, ch, len, cell->width,
1924 if (ret && warned++ < 3) {
1926 "cannot draw glyph at %ux%u via text-renderer",
1930 "suppressing further warnings during this rendering round");
1934 if (k == cur_y + 1 && !cursor_done) {
1936 if (!(con->flags & TSM_SCREEN_HIDE_CURSOR)) {
1937 if (!(con->flags & TSM_SCREEN_INVERSE))
1938 attr.inverse = !attr.inverse;
1939 draw_cb(con, 0, NULL, 0, 1,
1940 cur_x, i, &attr, data);
1945 if (con->opts & TSM_SCREEN_OPT_RENDER_TIMING)
1946 time_draw = shl_timer_elapsed(con->timer);
1948 /* perform final rendering steps */
1951 if (con->opts & TSM_SCREEN_OPT_RENDER_TIMING)
1952 shl_timer_reset(con->timer);
1954 ret = render_cb(con, data);
1957 "cannot render via text-renderer");
1959 if (con->opts & TSM_SCREEN_OPT_RENDER_TIMING)
1960 time_rend = shl_timer_elapsed(con->timer);
1965 if (con->opts & TSM_SCREEN_OPT_RENDER_TIMING)
1967 "timing: sum: %" PRIu64 " prepare: %" PRIu64 " draw: %" PRIu64 " render: %" PRIu64,
1968 time_prep + time_draw + time_rend,
1969 time_prep, time_draw, time_rend);