| /* $Id: screen.c,v 1.11 2007-09-21 19:24:37 nicm Exp $ */ |
| |
| /* |
| * Copyright (c) 2007 Nicholas Marriott <nicm@users.sourceforge.net> |
| * |
| * Permission to use, copy, modify, and distribute this software for any |
| * purpose with or without fee is hereby granted, provided that the above |
| * copyright notice and this permission notice appear in all copies. |
| * |
| * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES |
| * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF |
| * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR |
| * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES |
| * WHATSOEVER RESULTING FROM LOSS OF MIND, USE, DATA OR PROFITS, WHETHER |
| * IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING |
| * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. |
| */ |
| |
| #include <sys/types.h> |
| |
| #include <string.h> |
| |
| #include "tmux.h" |
| |
| /* |
| * Virtual screen and basic terminal emulator. |
| * |
| * XXX Much of this file sucks. |
| */ |
| |
| size_t screen_store_attributes(struct buffer *, u_char); |
| size_t screen_store_colours(struct buffer *, u_char); |
| void screen_free_lines(struct screen *, u_int, u_int); |
| void screen_make_lines(struct screen *, u_int, u_int); |
| void screen_move_lines(struct screen *, u_int, u_int, u_int); |
| void screen_fill_lines( |
| struct screen *, u_int, u_int, u_char, u_char, u_char); |
| uint16_t screen_extract(u_char *); |
| void screen_write_character(struct screen *, u_char); |
| void screen_cursor_up_scroll(struct screen *); |
| void screen_cursor_down_scroll(struct screen *); |
| void screen_scroll_region_up(struct screen *); |
| void screen_scroll_region_down(struct screen *); |
| void screen_scroll_up(struct screen *, u_int); |
| void screen_scroll_down(struct screen *, u_int); |
| void screen_fill_screen(struct screen *, u_char, u_char, u_char); |
| void screen_fill_line(struct screen *, u_int, u_char, u_char, u_char); |
| void screen_fill_end_of_screen( |
| struct screen *, u_int, u_int, u_char, u_char, u_char); |
| void screen_fill_end_of_line( |
| struct screen *, u_int, u_int, u_char, u_char, u_char); |
| void screen_fill_start_of_line( |
| struct screen *, u_int, u_int, u_char, u_char, u_char); |
| void screen_insert_lines(struct screen *, u_int, u_int); |
| void screen_delete_lines(struct screen *, u_int, u_int); |
| void screen_insert_characters(struct screen *, u_int, u_int, u_int); |
| void screen_delete_characters(struct screen *, u_int, u_int, u_int); |
| |
| #define SCREEN_DEFDATA ' ' |
| #define SCREEN_DEFATTR 0 |
| #define SCREEN_DEFCOLR 0x88 |
| |
| #define screen_last_y(s) ((s)->sy - 1) |
| #define screen_last_x(s) ((s)->sx - 1) |
| |
| #define screen_range_y(lx, rx) (((rx) - (lx)) + 1) |
| #define screen_range_x(ux, lx) (((lx) - (ux)) + 1) |
| |
| #define screen_offset_y(py, ny) ((py) + (ny) - 1) |
| #define screen_offset_x(px, nx) ((px) + (nx) - 1) |
| |
| /* Create a new screen. */ |
| void |
| screen_create(struct screen *s, u_int sx, u_int sy) |
| { |
| s->sx = sx; |
| s->sy = sy; |
| s->cx = 0; |
| s->cy = 0; |
| |
| s->ry_upper = 0; |
| s->ry_lower = screen_last_y(s); |
| |
| s->attr = SCREEN_DEFATTR; |
| s->colr = SCREEN_DEFCOLR; |
| |
| s->mode = MODE_CURSOR; |
| *s->title = '\0'; |
| |
| s->grid_data = xmalloc(sy * (sizeof *s->grid_data)); |
| s->grid_attr = xmalloc(sy * (sizeof *s->grid_attr)); |
| s->grid_colr = xmalloc(sy * (sizeof *s->grid_colr)); |
| screen_make_lines(s, 0, sy); |
| screen_fill_screen(s, SCREEN_DEFDATA, 0, SCREEN_DEFCOLR); |
| } |
| |
| /* Resize screen. */ |
| void |
| screen_resize(struct screen *s, u_int sx, u_int sy) |
| { |
| u_int i, ox, oy, ny; |
| |
| if (sx == s->sx || sy == s->sy) |
| return; |
| |
| if (sx < 1) |
| sx = 1; |
| if (sy < 1) |
| sy = 1; |
| |
| ox = s->sx; |
| oy = s->sy; |
| s->sx = sx; |
| s->sy = sy; |
| |
| log_debug("resizing screen (%u, %u) -> (%u, %u)", ox, oy, sx, sy); |
| |
| if (sy < oy) { |
| ny = oy - sy; |
| if (ny > s->cy) |
| ny = s->cy; |
| |
| if (ny != 0) { |
| log_debug("removing %u lines from top", ny); |
| for (i = 0; i < ny; i++) { |
| log_debug("freeing line %u", i); |
| xfree(s->grid_data[i]); |
| xfree(s->grid_attr[i]); |
| xfree(s->grid_colr[i]); |
| } |
| memmove(s->grid_data, s->grid_data + ny, |
| (oy - ny) * (sizeof *s->grid_data)); |
| memmove(s->grid_attr, s->grid_attr + ny, |
| (oy - ny) * (sizeof *s->grid_attr)); |
| memmove(s->grid_colr, s->grid_colr + ny, |
| (oy - ny) * (sizeof *s->grid_colr)); |
| s->cy -= ny; |
| } |
| if (ny < oy - sy) { |
| log_debug( |
| "removing %u lines from bottom", oy - sy - ny); |
| for (i = sy; i < oy - ny; i++) { |
| log_debug("freeing line %u", i); |
| xfree(s->grid_data[i]); |
| xfree(s->grid_attr[i]); |
| xfree(s->grid_colr[i]); |
| } |
| if (s->cy >= sy) |
| s->cy = sy - 1; |
| } |
| } |
| if (sy != oy) { |
| s->grid_data = xrealloc(s->grid_data, sy, sizeof *s->grid_data); |
| s->grid_attr = xrealloc(s->grid_attr, sy, sizeof *s->grid_attr); |
| s->grid_colr = xrealloc(s->grid_colr, sy, sizeof *s->grid_colr); |
| } |
| if (sy > oy) { |
| for (i = oy; i < sy; i++) { |
| log_debug("allocating line %u", i); |
| s->grid_data[i] = xmalloc(sx); |
| s->grid_attr[i] = xmalloc(sx); |
| s->grid_colr[i] = xmalloc(sx); |
| screen_fill_line(s, i, |
| SCREEN_DEFDATA, SCREEN_DEFATTR, SCREEN_DEFCOLR); |
| } |
| sy = oy; |
| } |
| |
| if (sx != ox) { |
| for (i = 0; i < sy; i++) { |
| log_debug("adjusting line %u to %u", i, sx); |
| s->grid_data[i] = xrealloc(s->grid_data[i], sx, 1); |
| s->grid_attr[i] = xrealloc(s->grid_attr[i], sx, 1); |
| s->grid_colr[i] = xrealloc(s->grid_colr[i], sx, 1); |
| if (sx > ox) { |
| screen_fill_end_of_line(s, ox, i, |
| SCREEN_DEFDATA, SCREEN_DEFATTR, |
| SCREEN_DEFCOLR); |
| } |
| } |
| if (s->cx >= sx) |
| s->cx = sx - 1; |
| } |
| } |
| |
| /* Draw a set of lines on the screen. */ |
| void |
| screen_draw(struct screen *s, struct buffer *b, u_int uy, u_int ly) |
| { |
| u_char attr, colr; |
| size_t size; |
| u_int i, j; |
| uint16_t n; |
| |
| if (uy > screen_last_y(s) || ly > screen_last_y(s) || ly < uy) |
| fatalx("bad range"); |
| |
| /* XXX. This is naive and rough right now. */ |
| attr = 0; |
| colr = SCREEN_DEFCOLR; |
| |
| input_store_zero(b, CODE_CURSOROFF); |
| |
| input_store_one(b, CODE_ATTRIBUTES, 0); |
| |
| for (j = uy; j <= ly; j++) { |
| input_store_two(b, CODE_CURSORMOVE, j + 1, 1); |
| |
| for (i = 0; i <= screen_last_x(s); i++) { |
| |
| size = BUFFER_USED(b); |
| input_store_one(b, CODE_ATTRIBUTES, 0); |
| |
| n = 0; |
| if (s->grid_attr[j][i] != attr) { |
| attr = s->grid_attr[j][i]; |
| n += screen_store_attributes(b, attr); |
| if (attr == 0) |
| colr = SCREEN_DEFCOLR; |
| } |
| if (s->grid_colr[j][i] != colr) { |
| colr = s->grid_colr[j][i]; |
| n += screen_store_colours(b, colr); |
| } |
| if (n == 0) |
| buffer_reverse_add(b, 4); |
| else { |
| size = BUFFER_USED(b) - size; |
| memcpy(BUFFER_IN(b) - size + 2, &n, 2); |
| } |
| |
| input_store8(b, s->grid_data[j][i]); |
| } |
| } |
| |
| size = BUFFER_USED(b); |
| input_store_one(b, CODE_ATTRIBUTES, 0); |
| n = screen_store_attributes(b, s->attr); |
| n += screen_store_colours(b, s->colr); |
| size = BUFFER_USED(b) - size; |
| memcpy(BUFFER_IN(b) - size + 2, &n, 2); |
| |
| input_store_two(b, CODE_CURSORMOVE, s->cy + 1, s->cx + 1); |
| |
| if (s->mode & MODE_CURSOR) |
| input_store_zero(b, CODE_CURSORON); |
| } |
| |
| /* Store screen atttributes in buffer. */ |
| size_t |
| screen_store_attributes(struct buffer *b, u_char attr) |
| { |
| size_t n; |
| |
| if (attr == 0) { |
| input_store16(b, 0); |
| return (1); |
| } |
| |
| n = 0; |
| if (attr & ATTR_BRIGHT) { |
| input_store16(b, 1); |
| n++; |
| } |
| if (attr & ATTR_DIM) { |
| input_store16(b, 2); |
| n++; |
| } |
| if (attr & ATTR_ITALICS) { |
| input_store16(b, 3); |
| n++; |
| } |
| if (attr & ATTR_UNDERSCORE) { |
| input_store16(b, 4); |
| n++; |
| } |
| if (attr & ATTR_BLINK) { |
| input_store16(b, 5); |
| n++; |
| } |
| if (attr & ATTR_REVERSE) { |
| input_store16(b, 7); |
| n++; |
| } |
| if (attr & ATTR_HIDDEN) { |
| input_store16(b, 8); |
| n++; |
| } |
| return (n); |
| } |
| |
| /* Store screen colours in buffer. */ |
| size_t |
| screen_store_colours(struct buffer *b, u_char colr) |
| { |
| uint16_t v; |
| |
| v = colr >> 4; |
| if (v == 8) |
| v = 9; |
| input_store16(b, 30 + v); |
| v = colr & 0xf; |
| if (v == 8) |
| v = 9; |
| input_store16(b, 40 + v); |
| |
| return (2); |
| } |
| |
| /* Make a range of lines. */ |
| void |
| screen_make_lines(struct screen *s, u_int py, u_int ny) |
| { |
| u_int i; |
| |
| log_debug("making lines %u,%u", py, ny); |
| |
| if (py > screen_last_y(s) || py + ny - 1 > screen_last_y(s)) |
| fatalx("bad range"); |
| |
| for (i = py; i < py + ny; i++) { |
| s->grid_data[i] = xmalloc(s->sx); |
| s->grid_attr[i] = xmalloc(s->sx); |
| s->grid_colr[i] = xmalloc(s->sx); |
| } |
| } |
| |
| /* Free a range of lines. */ |
| void |
| screen_free_lines(struct screen *s, u_int py, u_int ny) |
| { |
| u_int i; |
| |
| log_debug("freeing lines %u,%u", py, ny); |
| |
| if (py > screen_last_y(s) || py + ny - 1 > screen_last_y(s)) |
| fatalx("bad range"); |
| |
| for (i = py; i < py + ny; i++) { |
| xfree(s->grid_data[i]); |
| xfree(s->grid_attr[i]); |
| xfree(s->grid_colr[i]); |
| } |
| } |
| |
| /* Move a range of lines. */ |
| void |
| screen_move_lines(struct screen *s, u_int dy, u_int py, u_int ny) |
| { |
| log_debug("moving lines %u,%u to %u", py, ny, dy); |
| |
| if (py > screen_last_y(s) || py + ny - 1 > screen_last_y(s)) |
| fatalx("bad range"); |
| if (dy > screen_last_y(s) || dy == py) |
| fatalx("bad destination"); |
| if (dy + ny - 1 > screen_last_y(s)) |
| fatalx("bad size"); |
| |
| memmove( |
| &s->grid_data[dy], &s->grid_data[py], ny * (sizeof *s->grid_data)); |
| memmove( |
| &s->grid_attr[dy], &s->grid_attr[py], ny * (sizeof *s->grid_attr)); |
| memmove( |
| &s->grid_colr[dy], &s->grid_colr[py], ny * (sizeof *s->grid_colr)); |
| } |
| |
| /* Fill a range of lines. */ |
| void |
| screen_fill_lines( |
| struct screen *s, u_int py, u_int ny, u_char data, u_char attr, u_char colr) |
| { |
| u_int i; |
| |
| log_debug("filling lines %u,%u", py, ny); |
| |
| if (py > screen_last_y(s) || py + ny - 1 > screen_last_y(s)) |
| fatalx("bad range"); |
| |
| for (i = py; i < py + ny; i++) |
| screen_fill_line(s, i, data, attr, colr); |
| } |
| |
| /* Update screen with character. */ |
| void |
| screen_character(struct screen *s, u_char ch) |
| { |
| switch (ch) { |
| case '\n': /* LF */ |
| screen_cursor_down_scroll(s); |
| break; |
| case '\r': /* CR */ |
| s->cx = 0; |
| break; |
| case '\010': /* BS */ |
| if (s->cx > 0) |
| s->cx--; |
| break; |
| case '\177': /* DC */ |
| /* XXX */ |
| break; |
| default: |
| if (ch < ' ') |
| fatalx("bad control"); |
| screen_write_character(s, ch); |
| break; |
| } |
| } |
| |
| /* Extract 16-bit value from pointer. */ |
| uint16_t |
| screen_extract(u_char *ptr) |
| { |
| uint16_t n; |
| |
| memcpy(&n, ptr, sizeof n); |
| return (n); |
| } |
| |
| /* Update screen with escape sequence. */ |
| void |
| screen_sequence(struct screen *s, u_char *ptr) |
| { |
| uint16_t ua, ub; |
| |
| ptr++; |
| log_debug("processing code %hhu", *ptr); |
| switch (*ptr++) { |
| case CODE_CURSORUP: |
| ua = screen_extract(ptr); |
| if (ua > s->cy) |
| ua = s->cy; |
| s->cy -= ua; |
| break; |
| case CODE_CURSORDOWN: |
| ua = screen_extract(ptr); |
| if (s->cy + ua > screen_last_y(s)) |
| ua = screen_last_y(s) - s->cy; |
| s->cy += ua; |
| break; |
| case CODE_CURSORLEFT: |
| ua = screen_extract(ptr); |
| if (ua > s->cx) |
| ua = s->cx; |
| s->cx -= ua; |
| break; |
| case CODE_CURSORRIGHT: |
| ua = screen_extract(ptr); |
| if (s->cx + ua > screen_last_x(s)) |
| ua = screen_last_x(s) - s->cx; |
| s->cx += ua; |
| break; |
| case CODE_CURSORMOVE: |
| ua = screen_extract(ptr); |
| ptr += 2; |
| ub = screen_extract(ptr); |
| if (ub > s->sx) |
| ub = s->sx; |
| s->cx = ub - 1; |
| if (ua > s->sy) |
| ua = s->sy; |
| s->cy = ua - 1; |
| break; |
| case CODE_CLEARENDOFSCREEN: |
| screen_fill_end_of_screen( |
| s, s->cx, s->cy, SCREEN_DEFDATA, s->attr, s->colr); |
| break; |
| case CODE_CLEARSCREEN: |
| screen_fill_screen(s, SCREEN_DEFDATA, s->attr, s->colr); |
| break; |
| case CODE_CLEARENDOFLINE: |
| screen_fill_end_of_line( |
| s, s->cx, s->cy, SCREEN_DEFDATA, s->attr, s->colr); |
| break; |
| case CODE_CLEARSTARTOFLINE: |
| screen_fill_start_of_line( |
| s, s->cx, s->cy, SCREEN_DEFDATA, s->attr, s->colr); |
| break; |
| case CODE_CLEARLINE: |
| screen_fill_line(s, s->cy, SCREEN_DEFDATA, s->attr, s->colr); |
| break; |
| case CODE_INSERTLINE: |
| ua = screen_extract(ptr); |
| screen_insert_lines(s, s->cy, ua); |
| break; |
| case CODE_DELETELINE: |
| ua = screen_extract(ptr); |
| screen_delete_lines(s, s->cy, ua); |
| break; |
| case CODE_INSERTCHARACTER: |
| ua = screen_extract(ptr); |
| screen_insert_characters(s, s->cx, s->cy, ua); |
| break; |
| case CODE_DELETECHARACTER: |
| ua = screen_extract(ptr); |
| screen_delete_characters(s, s->cx, s->cy, ua); |
| break; |
| case CODE_CURSORON: |
| s->mode |= MODE_CURSOR; |
| break; |
| case CODE_CURSOROFF: |
| s->mode &= ~MODE_CURSOR; |
| break; |
| case CODE_REVERSEINDEX: |
| screen_cursor_up_scroll(s); |
| break; |
| case CODE_SCROLLREGION: |
| ua = screen_extract(ptr); |
| ptr += 2; |
| ub = screen_extract(ptr); |
| if (ua > s->sy) |
| ua = s->sy; |
| s->ry_upper = ua - 1; |
| if (ub > s->sy) |
| ub = s->sy; |
| s->ry_lower = ub - 1; |
| break; |
| case CODE_INSERTOFF: |
| s->mode &= ~MODE_INSERT; |
| break; |
| case CODE_INSERTON: |
| s->mode |= MODE_INSERT; |
| break; |
| case CODE_KCURSOROFF: |
| s->mode &= ~MODE_KCURSOR; |
| break; |
| case CODE_KCURSORON: |
| s->mode |= MODE_KCURSOR; |
| break; |
| case CODE_KKEYPADOFF: |
| s->mode &= ~MODE_KKEYPAD; |
| break; |
| case CODE_KKEYPADON: |
| s->mode |= MODE_KKEYPAD; |
| break; |
| case CODE_TITLE: |
| ua = screen_extract(ptr); |
| ptr += 2; |
| log_debug("new title: %u:%.*s", ua, (int) ua, ptr); |
| if (ua > (sizeof s->title) - 1) |
| ua = (sizeof s->title) - 1; |
| memcpy(s->title, ptr, ua); |
| s->title[ua] = '\0'; |
| break; |
| case CODE_ATTRIBUTES: |
| ua = screen_extract(ptr); |
| if (ua == 0) { |
| s->attr = 0; |
| s->colr = SCREEN_DEFCOLR; |
| break; |
| } |
| |
| while (ua-- > 0) { |
| ptr += 2; |
| ub = screen_extract(ptr); |
| switch (ub) { |
| case 0: |
| case 10: |
| s->attr = 0; |
| s->colr = SCREEN_DEFCOLR; |
| break; |
| case 1: |
| s->attr |= ATTR_BRIGHT; |
| break; |
| case 2: |
| s->attr |= ATTR_DIM; |
| break; |
| case 3: |
| s->attr |= ATTR_ITALICS; |
| break; |
| case 4: |
| s->attr |= ATTR_UNDERSCORE; |
| break; |
| case 5: |
| s->attr |= ATTR_BLINK; |
| break; |
| case 7: |
| s->attr |= ATTR_REVERSE; |
| break; |
| case 8: |
| s->attr |= ATTR_HIDDEN; |
| break; |
| case 23: |
| s->attr &= ~ATTR_ITALICS; |
| break; |
| case 24: |
| s->attr &= ~ATTR_UNDERSCORE; |
| break; |
| case 30: |
| case 31: |
| case 32: |
| case 33: |
| case 34: |
| case 35: |
| case 36: |
| case 37: |
| s->colr &= 0x0f; |
| s->colr |= (ub - 30) << 4; |
| break; |
| case 39: |
| s->colr &= 0x0f; |
| s->colr |= 0x80; |
| break; |
| case 40: |
| case 41: |
| case 42: |
| case 43: |
| case 44: |
| case 45: |
| case 46: |
| case 47: |
| s->colr &= 0xf0; |
| s->colr |= ub - 40; |
| break; |
| case 49: |
| s->colr &= 0xf0; |
| s->colr |= 0x08; |
| break; |
| } |
| } |
| } |
| } |
| |
| /* Write a single character to the screen at the cursor and move forward. */ |
| void |
| screen_write_character(struct screen *s, u_char ch) |
| { |
| if (s->cx > screen_last_x(s)) { |
| s->cx = 0; |
| screen_cursor_down_scroll(s); |
| } |
| |
| s->grid_data[s->cy][s->cx] = ch; |
| s->grid_attr[s->cy][s->cx] = s->attr; |
| s->grid_colr[s->cy][s->cx] = s->colr; |
| |
| s->cx++; |
| } |
| |
| /* Move cursor up and scroll if necessary. */ |
| void |
| screen_cursor_up_scroll(struct screen *s) |
| { |
| if (s->cy == s->ry_upper) |
| screen_scroll_region_down(s); |
| else if (s->cy > 0) |
| s->cy--; |
| } |
| |
| /* Move cursor down and scroll if necessary. */ |
| void |
| screen_cursor_down_scroll(struct screen *s) |
| { |
| if (s->cy == s->ry_lower) |
| screen_scroll_region_up(s); |
| else if (s->cy < screen_last_y(s)) |
| s->cy++; |
| } |
| |
| /* Scroll region up. */ |
| void |
| screen_scroll_region_up(struct screen *s) |
| { |
| log_debug("scrolling region up: %u:%u", s->ry_upper, s->ry_lower); |
| |
| /* |
| * Scroll scrolling region up: |
| * - delete ry_upper |
| * - move ry_upper + 1 to ry_lower to ry_upper |
| * - make new line at ry_lower |
| * |
| * Example: region is 12 to 24. |
| * ry_lower = 24, ry_upper = 12 |
| * screen_free_lines(s, 12, 1); |
| * screen_move_lines(s, 12, 13, 12); |
| * screen_make_lines(s, 24, 1); |
| */ |
| |
| screen_free_lines(s, s->ry_upper, 1); |
| |
| if (s->ry_upper != s->ry_lower) { |
| screen_move_lines(s, |
| s->ry_upper, s->ry_upper + 1, s->ry_lower - s->ry_upper); |
| } |
| |
| screen_make_lines(s, s->ry_lower, 1); |
| screen_fill_lines( |
| s, s->ry_lower, 1, SCREEN_DEFDATA, SCREEN_DEFATTR, SCREEN_DEFCOLR); |
| } |
| |
| /* Scroll region down. */ |
| void |
| screen_scroll_region_down(struct screen *s) |
| { |
| log_debug("scrolling region down: %u:%u", s->ry_upper, s->ry_lower); |
| |
| /* |
| * Scroll scrolling region down: |
| * - delete ry_lower |
| * - move ry_upper to ry_lower - 1 to ry_upper + 1 |
| * - make new line at ry_upper |
| * |
| * Example: region is 12 to 24. |
| * ry_lower = 24, ry_upper = 12 |
| * screen_free_lines(s, 24, 1); |
| * screen_move_lines(s, 13, 12, 12); |
| * screen_make_lines(s, 12, 1); |
| */ |
| |
| screen_free_lines(s, s->ry_lower, 1); |
| |
| if (s->ry_upper != s->ry_lower) { |
| screen_move_lines(s, |
| s->ry_upper + 1, s->ry_upper, s->ry_lower - s->ry_upper); |
| } |
| |
| screen_make_lines(s, s->ry_upper, 1); |
| screen_fill_lines( |
| s, s->ry_upper, 1, SCREEN_DEFDATA, SCREEN_DEFATTR, SCREEN_DEFCOLR); |
| } |
| |
| /* Scroll screen up. */ |
| void |
| screen_scroll_up(struct screen *s, u_int ny) |
| { |
| screen_delete_lines(s, 0, ny); |
| } |
| |
| /* Scroll screen down. */ |
| void |
| screen_scroll_down(struct screen *s, u_int ny) |
| { |
| screen_insert_lines(s, 0, ny); |
| } |
| |
| /* Fill entire screen. */ |
| void |
| screen_fill_screen(struct screen *s, u_char data, u_char attr, u_char colr) |
| { |
| screen_fill_end_of_screen(s, 0, 0, data, attr, colr); |
| } |
| |
| /* Fill single line. */ |
| void |
| screen_fill_line( |
| struct screen *s, u_int py, u_char data, u_char attr, u_char colr) |
| { |
| screen_fill_end_of_line(s, 0, py, data, attr, colr); |
| } |
| |
| /* Fill to end of screen. */ |
| void |
| screen_fill_end_of_screen( |
| struct screen *s, u_int px, u_int py, u_char data, u_char attr, u_char colr) |
| { |
| if (py > screen_last_y(s)) |
| return; |
| |
| if (px != 0) { |
| screen_fill_end_of_line(s, px, py, data, attr, colr); |
| if (py++ > screen_last_y(s)) |
| return; |
| } |
| |
| while (py <= screen_last_y(s)) { |
| screen_fill_line(s, py, data, attr, colr); |
| py++; |
| } |
| } |
| |
| /* Fill to end of line. */ |
| void |
| screen_fill_end_of_line( |
| struct screen *s, u_int px, u_int py, u_char data, u_char attr, u_char colr) |
| { |
| if (px > screen_last_x(s)) |
| return; |
| if (py > screen_last_y(s)) |
| return; |
| |
| memset(&s->grid_data[py][px], data, s->sx - px); |
| memset(&s->grid_attr[py][px], attr, s->sx - px); |
| memset(&s->grid_colr[py][px], colr, s->sx - px); |
| } |
| |
| /* Fill to start of line. */ |
| void |
| screen_fill_start_of_line( |
| struct screen *s, u_int px, u_int py, u_char data, u_char attr, u_char colr) |
| { |
| if (px > screen_last_x(s)) |
| return; |
| if (py > screen_last_y(s)) |
| return; |
| |
| memset(s->grid_data[py], data, px); |
| memset(s->grid_attr[py], attr, px); |
| memset(s->grid_colr[py], colr, px); |
| } |
| |
| /* Insert lines. */ |
| void |
| screen_insert_lines(struct screen *s, u_int py, u_int ny) |
| { |
| if (py > screen_last_y(s)) |
| return; |
| |
| if (py + ny > screen_last_y(s)) |
| ny = screen_last_y(s) - py; |
| log_debug("inserting lines: %u,%u", py, ny); |
| |
| /* |
| * Insert range of ny lines at py: |
| * - Free ny lines from end of screen. |
| * - Move from py to end of screen - ny to py + ny. |
| * - Create ny lines at py. |
| * |
| * Example: insert 2 lines at 4. |
| * sy = 10, py = 4, ny = 2 |
| * screen_free_lines(s, 8, 2); - delete lines 8,9 |
| * screen_move_lines(s, 6, 4, 4); - move 4,5,6,7 to 6,7,8,9 |
| * screen_make_lines(s, 4, 2); - make lines 4,5 |
| */ |
| |
| screen_free_lines(s, s->sy - ny, ny); |
| |
| if (py != screen_last_y(s)) |
| screen_move_lines(s, py + ny, py, s->sy - py - ny); |
| |
| screen_make_lines(s, py, ny); |
| screen_fill_lines( |
| s, py, ny, SCREEN_DEFDATA, SCREEN_DEFATTR, SCREEN_DEFCOLR); |
| } |
| |
| /* Delete lines. */ |
| void |
| screen_delete_lines(struct screen *s, u_int py, u_int ny) |
| { |
| if (py > screen_last_y(s)) |
| return; |
| |
| if (py + ny > screen_last_y(s)) |
| ny = screen_last_y(s) - py; |
| log_debug("deleting lines: %u,%u", py, ny); |
| |
| /* |
| * Delete range of ny lines at py: |
| * - Free ny lines at py. |
| * - Move from py + ny to end of screen to py. |
| * - Free and recreate last ny lines. |
| * |
| * Example: delete lines 3,4. |
| * sy = 10, py = 3, ny = 2 |
| * screen_free_lines(s, 3, 2); - delete lines 3,4 |
| * screen_move_lines(s, 3, 5, 5); - move 5,6,7,8,9 to 3 |
| * screen_make_lines(s, 8, 2); - make lines 8,9 |
| */ |
| |
| screen_free_lines(s, py, ny); |
| |
| if (py != screen_last_y(s)) |
| screen_move_lines(s, py, py + ny, s->sy - py - ny); |
| |
| screen_make_lines(s, s->sy - ny, ny); |
| screen_fill_lines( |
| s, s->sy - ny, ny, SCREEN_DEFDATA, SCREEN_DEFATTR, SCREEN_DEFCOLR); |
| } |
| |
| /* Insert characters. */ |
| void |
| screen_insert_characters(struct screen *s, u_int px, u_int py, u_int nx) |
| { |
| u_int lx, rx; |
| |
| if (px > screen_last_x(s) || py > screen_last_y(s)) |
| return; |
| |
| lx = px; |
| rx = screen_offset_x(px, nx); |
| if (rx > screen_last_x(s)) |
| rx = screen_last_x(s); |
| |
| /* |
| * Inserting a range from lx to rx, inclusive. |
| * |
| * - If rx is not the last x, move from lx to rx + 1. |
| * - Clear the range from lx to rx. |
| */ |
| if (rx != screen_last_x(s)) { |
| nx = screen_range_x(rx + 1, screen_last_x(s)); |
| memmove(&s->grid_data[py][rx + 1], &s->grid_data[py][lx], nx); |
| memmove(&s->grid_attr[py][rx + 1], &s->grid_attr[py][lx], nx); |
| memmove(&s->grid_colr[py][rx + 1], &s->grid_colr[py][lx], nx); |
| } |
| memset(&s->grid_data[py][lx], SCREEN_DEFDATA, screen_range_x(lx, rx)); |
| memset(&s->grid_attr[py][lx], SCREEN_DEFATTR, screen_range_x(lx, rx)); |
| memset(&s->grid_colr[py][lx], SCREEN_DEFCOLR, screen_range_x(lx, rx)); |
| } |
| |
| /* Delete characters. */ |
| void |
| screen_delete_characters(struct screen *s, u_int px, u_int py, u_int nx) |
| { |
| u_int lx, rx; |
| |
| if (px > screen_last_x(s) || py > screen_last_y(s)) |
| return; |
| |
| lx = px; |
| rx = screen_offset_x(px, nx); |
| if (rx > screen_last_x(s)) |
| rx = screen_last_x(s); |
| |
| /* |
| * Deleting the range from lx to rx, inclusive. |
| * |
| * - If rx is not the last x, move the range from rx + 1 to lx. |
| * - Clear the range from the last x - (rx - lx) to the last x. |
| */ |
| |
| if (rx != screen_last_x(s)) { |
| nx = screen_range_x(rx + 1, screen_last_x(s)); |
| memmove(&s->grid_data[py][lx], &s->grid_data[py][rx + 1], nx); |
| memmove(&s->grid_attr[py][lx], &s->grid_attr[py][rx + 1], nx); |
| memmove(&s->grid_colr[py][lx], &s->grid_colr[py][rx + 1], nx); |
| } |
| |
| /* If lx == rx, then nx = 1. */ |
| nx = screen_range_x(lx, rx); |
| memset(&s->grid_data[py][s->sx - nx], SCREEN_DEFDATA, nx); |
| memset(&s->grid_attr[py][s->sx - nx], SCREEN_DEFATTR, nx); |
| memset(&s->grid_colr[py][s->sx - nx], SCREEN_DEFCOLR, nx); |
| } |