diff --git a/terminal.c b/terminal.c index 7e81555d..3091a9da 100644 --- a/terminal.c +++ b/terminal.c @@ -55,7 +55,7 @@ struct terminal { struct wl_display *display; int redraw_scheduled, redraw_pending; char *data; - int width, height, tail, row, column, total_rows; + int width, height, start, row, column; int fd, master; struct buffer *buffer; GIOChannel *channel; @@ -66,12 +66,22 @@ struct terminal { int margin; }; +static char * +terminal_get_row(struct terminal *terminal, int row) +{ + int index; + + index = (row + terminal->start) % terminal->height; + + return &terminal->data[index * (terminal->width + 1)]; +} + static void terminal_resize(struct terminal *terminal, int width, int height) { size_t size; char *data; - int i, l, total_rows, row, tail; + int i, l, total_rows, start; if (terminal->width == width && terminal->height == height) return; @@ -85,34 +95,30 @@ terminal_resize(struct terminal *terminal, int width, int height) else l = width; - if (terminal->total_rows > height) { + if (terminal->height > height) { total_rows = height; - tail = terminal->tail + terminal->total_rows - height; + start = terminal->height - height; } else { - total_rows = terminal->total_rows; - tail = terminal->tail; + total_rows = terminal->height; + start = 0; } - for (i = 0; i < total_rows; i++) { - row = (tail + i) % terminal->height; + for (i = 0; i < total_rows; i++) memcpy(data + (width + 1) * i, - &terminal->data[row * (terminal->width + 1)], l); - } + terminal_get_row(terminal, i), l); free(terminal->data); - } else { - total_rows = 1; } terminal->width = width; terminal->height = height; terminal->data = data; - terminal->total_rows = total_rows; - terminal->row = total_rows - 1; + if (terminal->row >= terminal->height) + terminal->row = terminal->height - 1; if (terminal->column >= terminal->width) terminal->column = terminal->width - 1; - terminal->tail = 0; + terminal->start = 0; } static void @@ -122,7 +128,7 @@ terminal_draw_contents(struct terminal *terminal) cairo_surface_t *surface; cairo_t *cr; cairo_font_extents_t extents; - int i, row; + int i; window_get_child_rectangle(terminal->window, &rectangle); @@ -141,11 +147,10 @@ terminal_draw_contents(struct terminal *terminal) cairo_set_font_size(cr, 14); cairo_font_extents(cr, &extents); - for (i = 0; i < terminal->total_rows; i++) { - row = (terminal->tail + i) % terminal->height; + for (i = 0; i < terminal->height; i++) { cairo_move_to(cr, terminal->margin, terminal->margin + extents.ascent + extents.height * i); - cairo_show_text(cr, &terminal->data[row * (terminal->width + 1)]); + cairo_show_text(cr, terminal_get_row(terminal, i)); } cairo_destroy(cr); @@ -219,30 +224,93 @@ terminal_schedule_redraw(struct terminal *terminal) } } +static void +terminal_data(struct terminal *terminal, const char *data, size_t length); + static void handle_escape(struct terminal *terminal) { - char *row; - int i, j; + char *row, *p; + int i, count; + int args[10], set[10] = { 0, }; terminal->escape[terminal->escape_length++] = '\0'; - if (strcmp(terminal->escape, "\e[J") == 0) { - row = &terminal->data[terminal->row * (terminal->width + 1)]; - memset(&row[terminal->column], 0, terminal->width - terminal->column); - for (i = terminal->total_rows; i < terminal->height; i++) { - - j = terminal->row + i; - if (j >= terminal->height) - j -= terminal->height; - - row = &terminal->data[j * (terminal->width + 1)]; - memset(row, 0, terminal->width); + i = 0; + p = &terminal->escape[2]; + while ((isdigit(*p) || *p == ';') && i < 10) { + if (*p == ';') { + p++; + i++; + } else { + args[i] = strtol(p, &p, 10); + set[i] = 1; } - } else if (strcmp(terminal->escape, "\e[H") == 0) { - terminal->row = terminal->tail; - terminal->total_rows = 1; - terminal->column = 0; } + + switch (*p) { + case 'A': + count = set[0] ? args[0] : 1; + if (terminal->row - count >= 0) + terminal->row -= count; + else + terminal->row = 0; + break; + case 'B': + count = set[0] ? args[0] : 1; + if (terminal->row + count < terminal->height) + terminal->row += count; + else + terminal->row = terminal->height; + break; + case 'C': + count = set[0] ? args[0] : 1; + if (terminal->column + count < terminal->width) + terminal->column += count; + else + terminal->column = terminal->width; + break; + case 'D': + count = set[0] ? args[0] : 1; + if (terminal->column - count >= 0) + terminal->column -= count; + else + terminal->column = 0; + break; + case 'J': + row = terminal_get_row(terminal, terminal->row); + memset(&row[terminal->column], 0, terminal->width - terminal->column); + for (i = terminal->row + 1; i < terminal->height; i++) + memset(terminal_get_row(terminal, i), 0, terminal->width); + break; + case 'G': + if (set[0]) + terminal->column = args[0] - 1; + break; + case 'H': + case 'f': + terminal->row = set[0] ? args[0] - 1 : 0; + terminal->column = set[1] ? args[1] - 1 : 0; + break; + case 'K': + row = terminal_get_row(terminal, terminal->row); + memset(&row[terminal->column], 0, terminal->width - terminal->column); + break; + case 'm': + /* color, blink, bold etc*/ + break; + case '?': + if (strcmp(p, "?25l") == 0) { + /* hide cursor */ + } else if (strcmp(p, "?25h") == 0) { + /* show cursor */ + } + break; + default: + terminal_data(terminal, + terminal->escape + 1, + terminal->escape_length - 2); + break; + } } static void @@ -252,7 +320,7 @@ terminal_data(struct terminal *terminal, const char *data, size_t length) char *row; for (i = 0; i < length; i++) { - row = &terminal->data[terminal->row * (terminal->width + 1)]; + row = terminal_get_row(terminal, terminal->row); if (terminal->state == STATE_ESCAPE) { terminal->escape[terminal->escape_length++] = data[i]; @@ -276,19 +344,16 @@ terminal_data(struct terminal *terminal, const char *data, size_t length) break; case '\n': terminal->column = 0; - terminal->row++; - if (terminal->row == terminal->height) - terminal->row = 0; - if (terminal->total_rows == terminal->height) { - memset(&terminal->data[terminal->row * (terminal->width + 1)], - 0, terminal->width); - terminal->tail++; + if (terminal->row + 1 < terminal->height) { + terminal->row++; } else { - terminal->total_rows++; + terminal->start++; + if (terminal->start == terminal->height) + terminal->start = 0; + memset(terminal_get_row(terminal, terminal->row), + 0, terminal->width); } - if (terminal->tail == terminal->height) - terminal->tail = 0; break; case '\t': memset(&row[terminal->column], ' ', -terminal->column & 7); @@ -299,9 +364,16 @@ terminal_data(struct terminal *terminal, const char *data, size_t length) terminal->escape[0] = '\e'; terminal->escape_length = 1; break; + case '\b': + if (terminal->column > 0) + terminal->column--; + break; + case '\a': + /* Bell */ + break; default: if (terminal->column < terminal->width) - row[terminal->column++] = data[i]; + row[terminal->column++] = data[i] < 32 ? data[i] + 64 : data[i]; break; } }