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"
48 struct tsm_screen_attr attr;
64 struct shl_timer *timer;
66 /* default attributes for new cells */
67 struct tsm_screen_attr def_attr;
72 unsigned int margin_top;
73 unsigned int margin_bottom;
74 unsigned int line_num;
77 /* scroll-back buffer */
78 unsigned int sb_count; /* number of lines in sb */
79 struct line *sb_first; /* first line; was moved first */
80 struct line *sb_last; /* last line; was moved last*/
81 unsigned int sb_max; /* max-limit of lines in sb */
82 struct line *sb_pos; /* current position in sb or NULL */
85 unsigned int cursor_x;
86 unsigned int cursor_y;
92 static void cell_init(struct tsm_screen *con, struct cell *cell)
95 memcpy(&cell->attr, &con->def_attr, sizeof(cell->attr));
98 static int line_new(struct tsm_screen *con, struct line **out,
107 line = malloc(sizeof(*line));
114 line->cells = malloc(sizeof(struct cell) * width);
120 for (i = 0; i < width; ++i)
121 cell_init(con, &line->cells[i]);
127 static void line_free(struct line *line)
133 static int line_resize(struct tsm_screen *con, struct line *line,
141 if (line->size < width) {
142 tmp = realloc(line->cells, width * sizeof(struct cell));
149 while (line->size < width) {
150 cell_init(con, &line->cells[line->size]);
158 /* This links the given line into the scrollback-buffer */
159 static void link_to_scrollback(struct tsm_screen *con, struct line *line)
163 if (con->sb_max == 0) {
168 /* Remove a line from the scrollback buffer if it reaches its maximum.
169 * We must take care to correctly keep the current position as the new
170 * line is linked in after we remove the top-most line here.
171 * sb_max == 0 is tested earlier so we can assume sb_max > 0 here. In
172 * other words, buf->sb_first is a valid line if sb_count >= sb_max. */
173 if (con->sb_count >= con->sb_max) {
175 con->sb_first = tmp->next;
177 tmp->next->prev = NULL;
182 /* (position == tmp && !next) means we have sb_max=1 so set
183 * position to the new line. Otherwise, set to new first line.
184 * If position!=tmp and we have a fixed-position then nothing
185 * needs to be done because we can stay at the same line. If we
186 * have no fixed-position, we need to set the position to the
187 * next inserted line, which can be "line", too. */
189 if (con->sb_pos == tmp ||
190 !(con->flags & TSM_SCREEN_FIXED_POS)) {
191 if (con->sb_pos->next)
192 con->sb_pos = con->sb_pos->next;
201 line->prev = con->sb_last;
203 con->sb_last->next = line;
205 con->sb_first = line;
210 /* Unlinks last line from the scrollback buffer, Returns NULL if it is empty */
211 static struct line *get_from_scrollback(struct tsm_screen *con)
219 con->sb_last = line->prev;
221 line->prev->next = NULL;
223 con->sb_first = NULL;
226 /* correctly move the current position if it is set in the sb */
228 if (con->flags & TSM_SCREEN_FIXED_POS ||
229 !con->sb_pos->prev) {
230 if (con->sb_pos == line)
233 con->sb_pos = con->sb_pos->prev;
242 static void screen_scroll_up(struct tsm_screen *con, unsigned int num)
244 unsigned int i, j, max;
250 max = con->margin_bottom + 1 - con->margin_top;
254 /* We cache lines on the stack to speed up the scrolling. However, if
255 * num is too big we might get overflows here so use recursion if num
256 * exceeds a hard-coded limit.
257 * 128 seems to be a sane limit that should never be reached but should
258 * also be small enough so we do not get stack overflows. */
260 screen_scroll_up(con, 128);
261 return screen_scroll_up(con, num - 128);
263 struct line *cache[num];
265 for (i = 0; i < num; ++i) {
266 ret = line_new(con, &cache[i], con->size_x);
268 link_to_scrollback(con,
269 con->lines[con->margin_top + i]);
271 cache[i] = con->lines[con->margin_top + i];
272 for (j = 0; j < con->size_x; ++j)
273 cell_init(con, &cache[i]->cells[j]);
278 memmove(&con->lines[con->margin_top],
279 &con->lines[con->margin_top + num],
280 (max - num) * sizeof(struct line*));
283 memcpy(&con->lines[con->margin_top + (max - num)],
284 cache, num * sizeof(struct line*));
287 static void screen_scroll_down(struct tsm_screen *con, unsigned int num)
289 unsigned int i, j, max;
294 max = con->margin_bottom + 1 - con->margin_top;
298 /* see screen_scroll_up() for an explanation */
300 screen_scroll_down(con, 128);
301 return screen_scroll_down(con, num - 128);
303 struct line *cache[num];
305 for (i = 0; i < num; ++i) {
306 cache[i] = con->lines[con->margin_bottom - i];
307 for (j = 0; j < con->size_x; ++j)
308 cell_init(con, &cache[i]->cells[j]);
312 memmove(&con->lines[con->margin_top + num],
313 &con->lines[con->margin_top],
314 (max - num) * sizeof(struct line*));
317 memcpy(&con->lines[con->margin_top],
318 cache, num * sizeof(struct line*));
321 static void screen_write(struct tsm_screen *con, unsigned int x,
322 unsigned int y, tsm_symbol_t ch,
323 const struct tsm_screen_attr *attr)
327 if (x >= con->size_x || y >= con->size_y) {
328 llog_warn(con, "writing beyond buffer boundary");
332 line = con->lines[y];
334 if ((con->flags & TSM_SCREEN_INSERT_MODE) && x < (con->size_x - 1))
335 memmove(&line->cells[x + 1], &line->cells[x],
336 sizeof(struct cell) * (con->size_x - 1 - x));
337 line->cells[x].ch = ch;
338 memcpy(&line->cells[x].attr, attr, sizeof(*attr));
341 static void screen_erase_region(struct tsm_screen *con,
351 if (y_to >= con->size_y)
352 y_to = con->size_y - 1;
353 if (x_to >= con->size_x)
354 x_to = con->size_x - 1;
356 for ( ; y_from <= y_to; ++y_from) {
357 line = con->lines[y_from];
366 to = con->size_x - 1;
367 for ( ; x_from <= to; ++x_from) {
368 if (protect && line->cells[x_from].attr.protect)
371 cell_init(con, &line->cells[x_from]);
377 static inline unsigned int to_abs_x(struct tsm_screen *con, unsigned int x)
382 static inline unsigned int to_abs_y(struct tsm_screen *con, unsigned int y)
384 if (!(con->flags & TSM_SCREEN_REL_ORIGIN))
387 return con->margin_top + y;
390 int tsm_screen_new(struct tsm_screen **out, tsm_log_t log)
392 struct tsm_screen *con;
399 con = malloc(sizeof(*con));
403 memset(con, 0, sizeof(*con));
406 con->def_attr.fr = 255;
407 con->def_attr.fg = 255;
408 con->def_attr.fb = 255;
410 ret = shl_timer_new(&con->timer);
414 ret = tsm_screen_resize(con, 80, 24);
418 llog_debug(con, "new screen");
424 shl_timer_free(con->timer);
425 for (i = 0; i < con->line_num; ++i)
426 line_free(con->lines[i]);
428 free(con->tab_ruler);
434 void tsm_screen_ref(struct tsm_screen *con)
442 void tsm_screen_unref(struct tsm_screen *con)
446 if (!con || !con->ref || --con->ref)
449 llog_debug(con, "destroying screen");
451 for (i = 0; i < con->line_num; ++i)
452 line_free(con->lines[i]);
454 free(con->tab_ruler);
455 shl_timer_free(con->timer);
459 void tsm_screen_set_opts(struct tsm_screen *scr, unsigned int opts)
467 void tsm_screen_reset_opts(struct tsm_screen *scr, unsigned int opts)
475 unsigned int tsm_screen_get_opts(struct tsm_screen *scr)
483 unsigned int tsm_screen_get_width(struct tsm_screen *con)
491 unsigned int tsm_screen_get_height(struct tsm_screen *con)
499 int tsm_screen_resize(struct tsm_screen *con, unsigned int x,
503 unsigned int i, j, width, diff;
507 if (!con || !x || !y)
510 if (con->size_x == x && con->size_y == y)
513 /* First make sure the line buffer is big enough for our new screen.
514 * That is, allocate all new lines and make sure each line has enough
515 * cells to hold the new screen or the current screen. If we fail, we
516 * can safely return -ENOMEM and the buffer is still valid. We must
517 * allocate the new lines to at least the same size as the current
518 * lines. Otherwise, if this function fails in later turns, we will have
519 * invalid lines in the buffer. */
520 if (y > con->line_num) {
521 cache = realloc(con->lines, sizeof(struct line*) * y);
531 while (con->line_num < y) {
532 ret = line_new(con, &cache[con->line_num], width);
539 /* Resize all lines in the buffer if we increase screen width. This
540 * will guarantee that all lines are big enough so we can resize the
541 * buffer without reallocating them later. */
542 if (x > con->size_x) {
543 tab_ruler = realloc(con->tab_ruler, sizeof(bool) * x);
546 con->tab_ruler = tab_ruler;
548 for (i = 0; i < con->line_num; ++i) {
549 ret = line_resize(con, con->lines[i], x);
555 /* When resizing, we need to reset all the new cells, otherwise, the old
556 * data that was written there will reoccur on the screen.
557 * TODO: we overwrite way to much here; most of it should already be
558 * cleaned. Maybe it does more sense cleaning when _allocating_ or when
559 * _shrinking_, then we never clean twice (for performance reasons). */
560 for (j = 0; j < con->line_num; ++j) {
561 if (j >= con->size_y)
565 if (x < con->lines[j]->size)
568 width = con->lines[j]->size;
569 for (; i < width; ++i)
570 cell_init(con, &con->lines[j]->cells[i]);
573 /* xterm destroys margins on resize, so do we */
575 con->margin_bottom = con->size_y - 1;
578 for (i = 0; i < x; ++i) {
580 con->tab_ruler[i] = true;
582 con->tab_ruler[i] = false;
585 /* We need to adjust x-size first as screen_scroll_up() and friends may
586 * have to reallocate lines. The y-size is adjusted after them to avoid
587 * missing lines when shrinking y-size.
588 * We need to carefully look for the functions that we call here as they
589 * have stronger invariants as when called normally. */
592 if (con->cursor_x >= con->size_x)
593 con->cursor_x = con->size_x - 1;
595 /* scroll buffer if screen height shrinks */
596 if (con->size_y != 0 && y < con->size_y) {
597 diff = con->size_y - y;
598 screen_scroll_up(con, diff);
599 if (con->cursor_y > diff)
600 con->cursor_y -= diff;
606 con->margin_bottom = con->size_y - 1;
607 if (con->cursor_y >= con->size_y)
608 con->cursor_y = con->size_y - 1;
613 int tsm_screen_set_margins(struct tsm_screen *con,
614 unsigned int top, unsigned int bottom)
616 unsigned int upper, lower;
626 lower = con->size_y - 1;
627 } else if (bottom > con->size_y) {
629 lower = con->size_y - 1;
635 con->margin_top = upper;
636 con->margin_bottom = lower;
640 /* set maximum scrollback buffer size */
641 void tsm_screen_set_max_sb(struct tsm_screen *con,
649 while (con->sb_count > max) {
650 line = con->sb_first;
651 con->sb_first = line->next;
653 line->next->prev = NULL;
658 /* We treat fixed/unfixed position the same here because we
659 * remove lines from the TOP of the scrollback buffer. */
660 if (con->sb_pos == line)
661 con->sb_pos = con->sb_first;
669 /* clear scrollback buffer */
670 void tsm_screen_clear_sb(struct tsm_screen *con)
672 struct line *iter, *tmp;
677 for (iter = con->sb_first; iter; ) {
683 con->sb_first = NULL;
689 void tsm_screen_sb_up(struct tsm_screen *con, unsigned int num)
696 if (!con->sb_pos->prev)
699 con->sb_pos = con->sb_pos->prev;
700 } else if (!con->sb_last) {
703 con->sb_pos = con->sb_last;
708 void tsm_screen_sb_down(struct tsm_screen *con, unsigned int num)
715 con->sb_pos = con->sb_pos->next;
724 void tsm_screen_sb_page_up(struct tsm_screen *con, unsigned int num)
729 tsm_screen_sb_up(con, num * con->size_y);
732 void tsm_screen_sb_page_down(struct tsm_screen *con, unsigned int num)
737 tsm_screen_sb_down(con, num * con->size_y);
740 void tsm_screen_sb_reset(struct tsm_screen *con)
748 void tsm_screen_set_def_attr(struct tsm_screen *con,
749 const struct tsm_screen_attr *attr)
754 memcpy(&con->def_attr, attr, sizeof(*attr));
757 void tsm_screen_reset(struct tsm_screen *con)
766 con->margin_bottom = con->size_y - 1;
768 for (i = 0; i < con->size_x; ++i) {
770 con->tab_ruler[i] = true;
772 con->tab_ruler[i] = false;
776 void tsm_screen_set_flags(struct tsm_screen *con, unsigned int flags)
784 void tsm_screen_reset_flags(struct tsm_screen *con, unsigned int flags)
789 con->flags &= ~flags;
792 unsigned int tsm_screen_get_flags(struct tsm_screen *con)
800 unsigned int tsm_screen_get_cursor_x(struct tsm_screen *con)
805 return con->cursor_x;
808 unsigned int tsm_screen_get_cursor_y(struct tsm_screen *con)
813 return con->cursor_y;
816 void tsm_screen_set_tabstop(struct tsm_screen *con)
818 if (!con || con->cursor_x >= con->size_x)
821 con->tab_ruler[con->cursor_x] = true;
824 void tsm_screen_reset_tabstop(struct tsm_screen *con)
826 if (!con || con->cursor_x >= con->size_x)
829 con->tab_ruler[con->cursor_x] = false;
832 void tsm_screen_reset_all_tabstops(struct tsm_screen *con)
839 for (i = 0; i < con->size_x; ++i)
840 con->tab_ruler[i] = false;
843 void tsm_screen_write(struct tsm_screen *con, tsm_symbol_t ch,
844 const struct tsm_screen_attr *attr)
851 if (con->cursor_y <= con->margin_bottom ||
852 con->cursor_y >= con->size_y)
853 last = con->margin_bottom;
855 last = con->size_y - 1;
857 if (con->cursor_x >= con->size_x) {
858 if (con->flags & TSM_SCREEN_AUTO_WRAP) {
862 con->cursor_x = con->size_x - 1;
866 if (con->cursor_y > last) {
867 con->cursor_y = last;
868 screen_scroll_up(con, 1);
871 screen_write(con, con->cursor_x, con->cursor_y, ch, attr);
875 void tsm_screen_newline(struct tsm_screen *con)
880 tsm_screen_move_down(con, 1, true);
881 tsm_screen_move_line_home(con);
884 void tsm_screen_scroll_up(struct tsm_screen *con, unsigned int num)
889 screen_scroll_up(con, num);
892 void tsm_screen_scroll_down(struct tsm_screen *con, unsigned int num)
897 screen_scroll_down(con, num);
900 void tsm_screen_move_to(struct tsm_screen *con, unsigned int x,
908 if (con->flags & TSM_SCREEN_REL_ORIGIN)
909 last = con->margin_bottom;
911 last = con->size_y - 1;
913 con->cursor_x = to_abs_x(con, x);
914 if (con->cursor_x >= con->size_x)
915 con->cursor_x = con->size_x - 1;
917 con->cursor_y = to_abs_y(con, y);
918 if (con->cursor_y > last)
919 con->cursor_y = last;
922 void tsm_screen_move_up(struct tsm_screen *con, unsigned int num,
925 unsigned int diff, size;
930 if (con->cursor_y >= con->margin_top)
931 size = con->margin_top;
935 diff = con->cursor_y - size;
939 screen_scroll_down(con, num);
940 con->cursor_y = size;
942 con->cursor_y -= num;
946 void tsm_screen_move_down(struct tsm_screen *con, unsigned int num,
949 unsigned int diff, size;
954 if (con->cursor_y <= con->margin_bottom)
955 size = con->margin_bottom + 1;
959 diff = size - con->cursor_y - 1;
963 screen_scroll_up(con, num);
964 con->cursor_y = size - 1;
966 con->cursor_y += num;
970 void tsm_screen_move_left(struct tsm_screen *con, unsigned int num)
975 if (num > con->size_x)
978 if (con->cursor_x >= con->size_x)
979 con->cursor_x = con->size_x - 1;
981 if (num > con->cursor_x)
984 con->cursor_x -= num;
987 void tsm_screen_move_right(struct tsm_screen *con, unsigned int num)
992 if (num > con->size_x)
995 if (num + con->cursor_x >= con->size_x)
996 con->cursor_x = con->size_x - 1;
998 con->cursor_x += num;
1001 void tsm_screen_move_line_end(struct tsm_screen *con)
1006 con->cursor_x = con->size_x - 1;
1009 void tsm_screen_move_line_home(struct tsm_screen *con)
1017 void tsm_screen_tab_right(struct tsm_screen *con, unsigned int num)
1024 for (i = 0; i < num; ++i) {
1025 for (j = con->cursor_x + 1; j < con->size_x; ++j) {
1026 if (con->tab_ruler[j])
1031 if (con->cursor_x + 1 >= con->size_x)
1035 /* tabs never cause pending new-lines */
1036 if (con->cursor_x >= con->size_x)
1037 con->cursor_x = con->size_x - 1;
1040 void tsm_screen_tab_left(struct tsm_screen *con, unsigned int num)
1048 for (i = 0; i < num; ++i) {
1049 for (j = con->cursor_x - 1; j > 0; --j) {
1050 if (con->tab_ruler[j])
1062 void tsm_screen_insert_lines(struct tsm_screen *con, unsigned int num)
1064 unsigned int i, j, max;
1069 if (con->cursor_y < con->margin_top ||
1070 con->cursor_y > con->margin_bottom)
1073 max = con->margin_bottom - con->cursor_y + 1;
1077 struct line *cache[num];
1079 for (i = 0; i < num; ++i) {
1080 cache[i] = con->lines[con->margin_bottom - i];
1081 for (j = 0; j < con->size_x; ++j)
1082 cell_init(con, &cache[i]->cells[j]);
1086 memmove(&con->lines[con->cursor_y + num],
1087 &con->lines[con->cursor_y],
1088 (max - num) * sizeof(struct line*));
1090 memcpy(&con->lines[con->cursor_y],
1091 cache, num * sizeof(struct line*));
1097 void tsm_screen_delete_lines(struct tsm_screen *con, unsigned int num)
1099 unsigned int i, j, max;
1104 if (con->cursor_y < con->margin_top ||
1105 con->cursor_y > con->margin_bottom)
1108 max = con->margin_bottom - con->cursor_y + 1;
1112 struct line *cache[num];
1114 for (i = 0; i < num; ++i) {
1115 cache[i] = con->lines[con->cursor_y + i];
1116 for (j = 0; j < con->size_x; ++j)
1117 cell_init(con, &cache[i]->cells[j]);
1121 memmove(&con->lines[con->cursor_y],
1122 &con->lines[con->cursor_y + num],
1123 (max - num) * sizeof(struct line*));
1125 memcpy(&con->lines[con->cursor_y + (max - num)],
1126 cache, num * sizeof(struct line*));
1132 void tsm_screen_insert_chars(struct tsm_screen *con, unsigned int num)
1135 unsigned int max, mv, i;
1137 if (!con || !num || !con->size_y || !con->size_x)
1140 if (con->cursor_x >= con->size_x)
1141 con->cursor_x = con->size_x - 1;
1142 if (con->cursor_y >= con->size_y)
1143 con->cursor_y = con->size_y - 1;
1145 max = con->size_x - con->cursor_x;
1150 cells = con->lines[con->cursor_y]->cells;
1152 memmove(&cells[con->cursor_x + num],
1153 &cells[con->cursor_x],
1154 mv * sizeof(*cells));
1156 for (i = 0; i < num; ++i) {
1157 cell_init(con, &cells[con->cursor_x + i]);
1161 void tsm_screen_delete_chars(struct tsm_screen *con, unsigned int num)
1164 unsigned int max, mv, i;
1166 if (!con || !num || !con->size_y || !con->size_x)
1169 if (con->cursor_x >= con->size_x)
1170 con->cursor_x = con->size_x - 1;
1171 if (con->cursor_y >= con->size_y)
1172 con->cursor_y = con->size_y - 1;
1174 max = con->size_x - con->cursor_x;
1179 cells = con->lines[con->cursor_y]->cells;
1181 memmove(&cells[con->cursor_x],
1182 &cells[con->cursor_x + num],
1183 mv * sizeof(*cells));
1185 for (i = 0; i < num; ++i) {
1186 cell_init(con, &cells[con->cursor_x + mv + i]);
1190 void tsm_screen_erase_cursor(struct tsm_screen *con)
1197 if (con->cursor_x >= con->size_x)
1198 x = con->size_x - 1;
1202 screen_erase_region(con, x, con->cursor_y, x, con->cursor_y, false);
1205 void tsm_screen_erase_chars(struct tsm_screen *con, unsigned int num)
1212 if (con->cursor_x >= con->size_x)
1213 x = con->size_x - 1;
1217 screen_erase_region(con, x, con->cursor_y, x + num - 1, con->cursor_y,
1221 void tsm_screen_erase_cursor_to_end(struct tsm_screen *con,
1229 if (con->cursor_x >= con->size_x)
1230 x = con->size_x - 1;
1234 screen_erase_region(con, x, con->cursor_y, con->size_x - 1,
1235 con->cursor_y, protect);
1238 void tsm_screen_erase_home_to_cursor(struct tsm_screen *con,
1244 screen_erase_region(con, 0, con->cursor_y, con->cursor_x,
1245 con->cursor_y, protect);
1248 void tsm_screen_erase_current_line(struct tsm_screen *con,
1254 screen_erase_region(con, 0, con->cursor_y, con->size_x - 1,
1255 con->cursor_y, protect);
1258 void tsm_screen_erase_screen_to_cursor(struct tsm_screen *con,
1264 screen_erase_region(con, 0, 0, con->cursor_x, con->cursor_y, protect);
1267 void tsm_screen_erase_cursor_to_screen(struct tsm_screen *con,
1275 if (con->cursor_x >= con->size_x)
1276 x = con->size_x - 1;
1280 screen_erase_region(con, x, con->cursor_y, con->size_x - 1,
1281 con->size_y - 1, protect);
1284 void tsm_screen_erase_screen(struct tsm_screen *con, bool protect)
1289 screen_erase_region(con, 0, 0, con->size_x - 1, con->size_y - 1,
1293 void tsm_screen_draw(struct tsm_screen *con,
1294 tsm_screen_prepare_cb prepare_cb,
1295 tsm_screen_draw_cb draw_cb,
1296 tsm_screen_render_cb render_cb,
1299 unsigned int cur_x, cur_y;
1300 unsigned int i, j, k;
1301 struct line *iter, *line = NULL;
1303 struct tsm_screen_attr attr;
1304 bool cursor_done = false;
1305 int ret, warned = 0;
1306 uint64_t time_prep = 0, time_draw = 0, time_rend = 0;
1310 if (!con || !draw_cb)
1313 cur_x = con->cursor_x;
1314 if (con->cursor_x >= con->size_x)
1315 cur_x = con->size_x - 1;
1316 cur_y = con->cursor_y;
1317 if (con->cursor_y >= con->size_y)
1318 cur_y = con->size_y - 1;
1320 /* render preparation */
1323 if (con->opts & TSM_SCREEN_OPT_RENDER_TIMING)
1324 shl_timer_reset(con->timer);
1326 ret = prepare_cb(con, data);
1329 "cannot prepare text-renderer for rendering");
1333 if (con->opts & TSM_SCREEN_OPT_RENDER_TIMING)
1334 time_prep = shl_timer_elapsed(con->timer);
1339 /* push each character into rendering pipeline */
1341 if (con->opts & TSM_SCREEN_OPT_RENDER_TIMING)
1342 shl_timer_reset(con->timer);
1346 for (i = 0; i < con->size_y; ++i) {
1351 line = con->lines[k];
1355 for (j = 0; j < con->size_x; ++j) {
1356 cell = &line->cells[j];
1357 memcpy(&attr, &cell->attr, sizeof(attr));
1359 if (k == cur_y + 1 &&
1362 if (!(con->flags & TSM_SCREEN_HIDE_CURSOR))
1363 attr.inverse = !attr.inverse;
1366 /* TODO: do some more sophisticated inverse here. When
1367 * INVERSE mode is set, we should instead just select
1368 * inverse colors instead of switching background and
1370 if (con->flags & TSM_SCREEN_INVERSE)
1371 attr.inverse = !attr.inverse;
1373 ch = tsm_symbol_get(NULL, &cell->ch, &len);
1374 if (cell->ch == ' ' || cell->ch == 0)
1376 ret = draw_cb(con, cell->ch, ch, len, j, i, &attr,
1378 if (ret && warned++ < 3) {
1380 "cannot draw glyph at %ux%u via text-renderer",
1384 "suppressing further warnings during this rendering round");
1388 if (k == cur_y + 1 && !cursor_done) {
1390 if (!(con->flags & TSM_SCREEN_HIDE_CURSOR)) {
1391 if (!(con->flags & TSM_SCREEN_INVERSE))
1392 attr.inverse = !attr.inverse;
1393 draw_cb(con, 0, NULL, 0, cur_x, i, &attr, data);
1398 if (con->opts & TSM_SCREEN_OPT_RENDER_TIMING)
1399 time_draw = shl_timer_elapsed(con->timer);
1401 /* perform final rendering steps */
1404 if (con->opts & TSM_SCREEN_OPT_RENDER_TIMING)
1405 shl_timer_reset(con->timer);
1407 ret = render_cb(con, data);
1410 "cannot render via text-renderer");
1412 if (con->opts & TSM_SCREEN_OPT_RENDER_TIMING)
1413 time_rend = shl_timer_elapsed(con->timer);
1418 if (con->opts & TSM_SCREEN_OPT_RENDER_TIMING)
1420 "timing: sum: %" PRIu64 " prepare: %" PRIu64 " draw: %" PRIu64 " render: %" PRIu64,
1421 time_prep + time_draw + time_rend,
1422 time_prep, time_draw, time_rend);