Implement a bunch of escape codes.

Bash command line editing and gdb is pretty much there.  Emacs, vi and even less
need more work.
Kristian Høgsberg 16 years ago
parent 73f4e760a2
commit dbd54640f4
  1. 168
      terminal.c

@ -55,7 +55,7 @@ struct terminal {
struct wl_display *display; struct wl_display *display;
int redraw_scheduled, redraw_pending; int redraw_scheduled, redraw_pending;
char *data; char *data;
int width, height, tail, row, column, total_rows; int width, height, start, row, column;
int fd, master; int fd, master;
struct buffer *buffer; struct buffer *buffer;
GIOChannel *channel; GIOChannel *channel;
@ -66,12 +66,22 @@ struct terminal {
int margin; 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 static void
terminal_resize(struct terminal *terminal, int width, int height) terminal_resize(struct terminal *terminal, int width, int height)
{ {
size_t size; size_t size;
char *data; char *data;
int i, l, total_rows, row, tail; int i, l, total_rows, start;
if (terminal->width == width && terminal->height == height) if (terminal->width == width && terminal->height == height)
return; return;
@ -85,34 +95,30 @@ terminal_resize(struct terminal *terminal, int width, int height)
else else
l = width; l = width;
if (terminal->total_rows > height) { if (terminal->height > height) {
total_rows = height; total_rows = height;
tail = terminal->tail + terminal->total_rows - height; start = terminal->height - height;
} else { } else {
total_rows = terminal->total_rows; total_rows = terminal->height;
tail = terminal->tail; start = 0;
} }
for (i = 0; i < total_rows; i++) { for (i = 0; i < total_rows; i++)
row = (tail + i) % terminal->height;
memcpy(data + (width + 1) * i, memcpy(data + (width + 1) * i,
&terminal->data[row * (terminal->width + 1)], l); terminal_get_row(terminal, i), l);
}
free(terminal->data); free(terminal->data);
} else {
total_rows = 1;
} }
terminal->width = width; terminal->width = width;
terminal->height = height; terminal->height = height;
terminal->data = data; terminal->data = data;
terminal->total_rows = total_rows; if (terminal->row >= terminal->height)
terminal->row = total_rows - 1; terminal->row = terminal->height - 1;
if (terminal->column >= terminal->width) if (terminal->column >= terminal->width)
terminal->column = terminal->width - 1; terminal->column = terminal->width - 1;
terminal->tail = 0; terminal->start = 0;
} }
static void static void
@ -122,7 +128,7 @@ terminal_draw_contents(struct terminal *terminal)
cairo_surface_t *surface; cairo_surface_t *surface;
cairo_t *cr; cairo_t *cr;
cairo_font_extents_t extents; cairo_font_extents_t extents;
int i, row; int i;
window_get_child_rectangle(terminal->window, &rectangle); window_get_child_rectangle(terminal->window, &rectangle);
@ -141,11 +147,10 @@ terminal_draw_contents(struct terminal *terminal)
cairo_set_font_size(cr, 14); cairo_set_font_size(cr, 14);
cairo_font_extents(cr, &extents); cairo_font_extents(cr, &extents);
for (i = 0; i < terminal->total_rows; i++) { for (i = 0; i < terminal->height; i++) {
row = (terminal->tail + i) % terminal->height;
cairo_move_to(cr, terminal->margin, cairo_move_to(cr, terminal->margin,
terminal->margin + extents.ascent + extents.height * i); 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); 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 static void
handle_escape(struct terminal *terminal) handle_escape(struct terminal *terminal)
{ {
char *row; char *row, *p;
int i, j; int i, count;
int args[10], set[10] = { 0, };
terminal->escape[terminal->escape_length++] = '\0'; terminal->escape[terminal->escape_length++] = '\0';
if (strcmp(terminal->escape, "\e[J") == 0) { i = 0;
row = &terminal->data[terminal->row * (terminal->width + 1)]; p = &terminal->escape[2];
memset(&row[terminal->column], 0, terminal->width - terminal->column); while ((isdigit(*p) || *p == ';') && i < 10) {
for (i = terminal->total_rows; i < terminal->height; i++) { if (*p == ';') {
p++;
j = terminal->row + i; i++;
if (j >= terminal->height) } else {
j -= terminal->height; args[i] = strtol(p, &p, 10);
set[i] = 1;
row = &terminal->data[j * (terminal->width + 1)];
memset(row, 0, terminal->width);
} }
} 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 static void
@ -252,7 +320,7 @@ terminal_data(struct terminal *terminal, const char *data, size_t length)
char *row; char *row;
for (i = 0; i < length; i++) { 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) { if (terminal->state == STATE_ESCAPE) {
terminal->escape[terminal->escape_length++] = data[i]; terminal->escape[terminal->escape_length++] = data[i];
@ -276,19 +344,16 @@ terminal_data(struct terminal *terminal, const char *data, size_t length)
break; break;
case '\n': case '\n':
terminal->column = 0; terminal->column = 0;
terminal->row++; if (terminal->row + 1 < terminal->height) {
if (terminal->row == terminal->height) terminal->row++;
terminal->row = 0;
if (terminal->total_rows == terminal->height) {
memset(&terminal->data[terminal->row * (terminal->width + 1)],
0, terminal->width);
terminal->tail++;
} else { } 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; break;
case '\t': case '\t':
memset(&row[terminal->column], ' ', -terminal->column & 7); 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[0] = '\e';
terminal->escape_length = 1; terminal->escape_length = 1;
break; break;
case '\b':
if (terminal->column > 0)
terminal->column--;
break;
case '\a':
/* Bell */
break;
default: default:
if (terminal->column < terminal->width) if (terminal->column < terminal->width)
row[terminal->column++] = data[i]; row[terminal->column++] = data[i] < 32 ? data[i] + 64 : data[i];
break; break;
} }
} }

Loading…
Cancel
Save