END_TEST START_TEST (test_line_flip) { line_coordsAtT (j, -1.1, &f.x, &f.y); line_coordsAtT (j, 4.3, &g.x, &g.y); line_resize (j, 4.3, -1.1); line_coordsAtT (j, 0, &h.x, &h.y); line_coordsAtT (j, 1, &i.x, &i.y); fail_unless ( vector_cmp (&f, &i) == true && vector_cmp (&g, &h) == true, "A new t-0 that is smaller than the t-1 is allowed, and should flip the line's direction as well as changing its length." ); }
END_TEST START_TEST (test_line_shrink) { line_coordsAtT (j, 0, &f.x, &f.y); line_coordsAtT (j, 1, &g.x, &g.y); fail_unless ( line_resize (j, 5, 5) == false, "The same t-value is not allowed as both the t-0 point and the t-1 point." ); line_coordsAtT (j, 0, &h.x, &h.y); line_coordsAtT (j, 1, &i.x, &i.y); fail_unless ( vector_cmp (&f, &h) == true && vector_cmp (&g, &i) == true, "An invalid resize attempt cannot alter the line's dimensions" ); }
SHL_EXPORT int tsm_screen_resize(struct tsm_screen *con, unsigned int x, unsigned int y) { struct line **cache; unsigned int i, j, width, diff, start; int ret; bool *tab_ruler; if (!con || !x || !y) return -EINVAL; if (con->size_x == x && con->size_y == y) return 0; /* First make sure the line buffer is big enough for our new screen. * That is, allocate all new lines and make sure each line has enough * cells to hold the new screen or the current screen. If we fail, we * can safely return -ENOMEM and the buffer is still valid. We must * allocate the new lines to at least the same size as the current * lines. Otherwise, if this function fails in later turns, we will have * invalid lines in the buffer. */ if (y > con->line_num) { /* resize main buffer */ cache = realloc(con->main_lines, sizeof(struct line*) * y); if (!cache) return -ENOMEM; if (con->lines == con->main_lines) con->lines = cache; con->main_lines = cache; /* resize alt buffer */ cache = realloc(con->alt_lines, sizeof(struct line*) * y); if (!cache) return -ENOMEM; if (con->lines == con->alt_lines) con->lines = cache; con->alt_lines = cache; /* allocate new lines */ if (x > con->size_x) width = x; else width = con->size_x; while (con->line_num < y) { ret = line_new(con, &con->main_lines[con->line_num], width); if (ret) return ret; ret = line_new(con, &con->alt_lines[con->line_num], width); if (ret) { line_free(con->main_lines[con->line_num]); return ret; } ++con->line_num; } } /* Resize all lines in the buffer if we increase screen width. This * will guarantee that all lines are big enough so we can resize the * buffer without reallocating them later. */ if (x > con->size_x) { tab_ruler = realloc(con->tab_ruler, sizeof(bool) * x); if (!tab_ruler) return -ENOMEM; con->tab_ruler = tab_ruler; for (i = 0; i < con->line_num; ++i) { ret = line_resize(con, con->main_lines[i], x); if (ret) return ret; ret = line_resize(con, con->alt_lines[i], x); if (ret) return ret; } } screen_inc_age(con); /* clear expansion/padding area */ start = x; if (x > con->size_x) start = con->size_x; for (j = 0; j < con->line_num; ++j) { /* main-lines may go into SB, so clear all cells */ i = 0; if (j < con->size_y) i = start; for ( ; i < con->main_lines[j]->size; ++i) screen_cell_init(con, &con->main_lines[j]->cells[i]); /* alt-lines never go into SB, only clear visible cells */ i = 0; if (j < con->size_y) i = con->size_x; for ( ; i < x; ++i) screen_cell_init(con, &con->alt_lines[j]->cells[i]); } /* xterm destroys margins on resize, so do we */ con->margin_top = 0; con->margin_bottom = con->size_y - 1; /* reset tabs */ for (i = 0; i < x; ++i) { if (i % 8 == 0) con->tab_ruler[i] = true; else con->tab_ruler[i] = false; } /* We need to adjust x-size first as screen_scroll_up() and friends may * have to reallocate lines. The y-size is adjusted after them to avoid * missing lines when shrinking y-size. * We need to carefully look for the functions that we call here as they * have stronger invariants as when called normally. */ con->size_x = x; if (con->cursor_x >= con->size_x) move_cursor(con, con->size_x - 1, con->cursor_y); /* scroll buffer if screen height shrinks */ if (y < con->size_y) { diff = con->size_y - y; screen_scroll_up(con, diff); if (con->cursor_y > diff) move_cursor(con, con->cursor_x, con->cursor_y - diff); else move_cursor(con, con->cursor_x, 0); } con->size_y = y; con->margin_bottom = con->size_y - 1; if (con->cursor_y >= con->size_y) move_cursor(con, con->cursor_x, con->size_y - 1); return 0; }
int tsm_screen_resize(struct tsm_screen *con, unsigned int x, unsigned int y) { struct line **cache; unsigned int i, j, width, diff; int ret; bool *tab_ruler; if (!con || !x || !y) return -EINVAL; if (con->size_x == x && con->size_y == y) return 0; /* First make sure the line buffer is big enough for our new screen. * That is, allocate all new lines and make sure each line has enough * cells to hold the new screen or the current screen. If we fail, we * can safely return -ENOMEM and the buffer is still valid. We must * allocate the new lines to at least the same size as the current * lines. Otherwise, if this function fails in later turns, we will have * invalid lines in the buffer. */ if (y > con->line_num) { cache = realloc(con->lines, sizeof(struct line*) * y); if (!cache) return -ENOMEM; con->lines = cache; if (x > con->size_x) width = x; else width = con->size_x; while (con->line_num < y) { ret = line_new(con, &cache[con->line_num], width); if (ret) return ret; ++con->line_num; } } /* Resize all lines in the buffer if we increase screen width. This * will guarantee that all lines are big enough so we can resize the * buffer without reallocating them later. */ if (x > con->size_x) { tab_ruler = realloc(con->tab_ruler, sizeof(bool) * x); if (!tab_ruler) return -ENOMEM; con->tab_ruler = tab_ruler; for (i = 0; i < con->line_num; ++i) { ret = line_resize(con, con->lines[i], x); if (ret) return ret; } } /* When resizing, we need to reset all the new cells, otherwise, the old * data that was written there will reoccur on the screen. * TODO: we overwrite way to much here; most of it should already be * cleaned. Maybe it does more sense cleaning when _allocating_ or when * _shrinking_, then we never clean twice (for performance reasons). */ for (j = 0; j < con->line_num; ++j) { if (j >= con->size_y) i = 0; else i = con->size_x; if (x < con->lines[j]->size) width = x; else width = con->lines[j]->size; for (; i < width; ++i) cell_init(con, &con->lines[j]->cells[i]); } /* xterm destroys margins on resize, so do we */ con->margin_top = 0; con->margin_bottom = con->size_y - 1; /* reset tabs */ for (i = 0; i < x; ++i) { if (i % 8 == 0) con->tab_ruler[i] = true; else con->tab_ruler[i] = false; } /* We need to adjust x-size first as screen_scroll_up() and friends may * have to reallocate lines. The y-size is adjusted after them to avoid * missing lines when shrinking y-size. * We need to carefully look for the functions that we call here as they * have stronger invariants as when called normally. */ con->size_x = x; if (con->cursor_x >= con->size_x) con->cursor_x = con->size_x - 1; /* scroll buffer if screen height shrinks */ if (con->size_y != 0 && y < con->size_y) { diff = con->size_y - y; screen_scroll_up(con, diff); if (con->cursor_y > diff) con->cursor_y -= diff; else con->cursor_y = 0; } con->size_y = y; con->margin_bottom = con->size_y - 1; if (con->cursor_y >= con->size_y) con->cursor_y = con->size_y - 1; return 0; }
static void test_term_line_ops(void) { term_line *l; term_attr attr_regular = { }; term_attr attr_bold = { .bold = true }; term_attr attr_italic = { .italic = true }; assert_se(term_line_new(&l) >= 0); line_resize(l, 8, NULL, 0); assert_se(l->n_cells == 8); LINE_ASSERT(l, "| | | | | | | | |", 0); term_line_write(l, 4, TERM_CHAR_NULL, 0, NULL, TERM_AGE_NULL, 0); LINE_ASSERT(l, "| | | | | | | | |", 5); term_line_write(l, 1, PACK1('A'), 1, NULL, TERM_AGE_NULL, 0); LINE_ASSERT(l, "| |A| | | | | | |", 5); term_line_write(l, 8, PACK2('A', 'B'), 1, NULL, TERM_AGE_NULL, 0); LINE_ASSERT(l, "| |A| | | | | | |", 5); term_line_write(l, 7, PACK2('A', 'B'), 1, &attr_regular, 10, 0); LINE_ASSERT(l, "| |A| | | | | | 10 AB |", 8); term_line_write(l, 7, PACK2('A', 'B'), 1, &attr_bold, 10, 0); LINE_ASSERT(l, "| |A| | | | | | 10 *AB* |", 8); term_line_reset(l, NULL, TERM_AGE_NULL); LINE_ASSERT(l, "| | | | | | | | |", 0); line_set(l, 2, "*wxyz* 8", 0); line_set(l, 3, "/wxyz/ 8", 0); LINE_ASSERT(l, "| | | *wxyz* 8 | /wxyz/ 8 | | | | |", 4); line_set(l, 2, "*abc* 9", true); LINE_ASSERT(l, "| | | *abc* 9 | *wxyz* 9 | /wxyz/ 9 | 9 | 9 | 9 |", 5); line_set(l, 7, "*abc* 10", true); LINE_ASSERT(l, "| | | *abc* 9 | *wxyz* 9 | /wxyz/ 9 | 9 | 9 | *abc* 10 |", 8); term_line_erase(l, 6, 1, NULL, 11, 0); LINE_ASSERT(l, "| | | *abc* 9 | *wxyz* 9 | /wxyz/ 9 | 9 | 11 | *abc* 10 |", 8); term_line_erase(l, 6, 2, &attr_italic, 12, 0); LINE_ASSERT(l, "| | | *abc* 9 | *wxyz* 9 | /wxyz/ 9 | 9 | 12 // | 12 // |", 6); term_line_erase(l, 7, 2, &attr_regular, 13, 0); LINE_ASSERT(l, "| | | *abc* 9 | *wxyz* 9 | /wxyz/ 9 | 9 | 12 // | 13 |", 6); term_line_delete(l, 1, 3, &attr_bold, 14); LINE_ASSERT(l, "| | /wxyz/ 14 | 14 | 14 // | 14 | 14 ** | 14 ** | 14 ** |", 3); term_line_insert(l, 2, 2, &attr_regular, 15); LINE_ASSERT(l, "| | /wxyz/ 14 | 15 | 15 | 15 | 15 // | 15 | 15 ** |", 5); assert_se(!term_line_free(l)); } int main(int argc, char *argv[]) { test_term_char_misc(); test_term_char_packing(); test_term_char_allocating(); test_term_line_misc(); test_term_line_ops(); return 0; }