mirror of
https://gitlab.freedesktop.org/wayland/wayland.git
synced 2025-11-03 09:01:42 -05:00
terminal: UTF-8 support
Signed-off-by: Callum Lowcay <callum@callumscode.com>
This commit is contained in:
parent
052d17d4d9
commit
15bdc5d1d3
1 changed files with 175 additions and 25 deletions
|
|
@ -47,10 +47,114 @@ static int option_fullscreen;
|
||||||
#define MOD_ALT 0x02
|
#define MOD_ALT 0x02
|
||||||
#define MOD_CTRL 0x04
|
#define MOD_CTRL 0x04
|
||||||
|
|
||||||
|
union utf8_char {
|
||||||
|
unsigned char byte[4];
|
||||||
|
uint32_t ch;
|
||||||
|
};
|
||||||
|
|
||||||
|
enum utf8_state {
|
||||||
|
utf8state_start,
|
||||||
|
utf8state_accept,
|
||||||
|
utf8state_reject,
|
||||||
|
utf8state_expect3,
|
||||||
|
utf8state_expect2,
|
||||||
|
utf8state_expect1
|
||||||
|
};
|
||||||
|
|
||||||
|
struct utf8_state_machine {
|
||||||
|
enum utf8_state state;
|
||||||
|
int len;
|
||||||
|
union utf8_char s;
|
||||||
|
};
|
||||||
|
|
||||||
|
static void
|
||||||
|
init_state_machine(struct utf8_state_machine *machine)
|
||||||
|
{
|
||||||
|
machine->state = utf8state_start;
|
||||||
|
machine->len = 0;
|
||||||
|
machine->s.ch = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static enum utf8_state
|
||||||
|
utf8_next_char(struct utf8_state_machine *machine, char c)
|
||||||
|
{
|
||||||
|
switch(machine->state) {
|
||||||
|
case utf8state_start:
|
||||||
|
case utf8state_accept:
|
||||||
|
case utf8state_reject:
|
||||||
|
machine->s.ch = 0;
|
||||||
|
machine->len = 0;
|
||||||
|
if(c == 0xC0 || c == 0xC1) {
|
||||||
|
/* overlong encoding, reject */
|
||||||
|
machine->state = utf8state_reject;
|
||||||
|
} else if((c & 0x80) == 0) {
|
||||||
|
/* single byte, accept */
|
||||||
|
machine->s.byte[machine->len++] = c;
|
||||||
|
machine->state = utf8state_accept;
|
||||||
|
} else if((c & 0xC0) == 0x80) {
|
||||||
|
/* parser out of sync, ignore byte */
|
||||||
|
machine->state = utf8state_start;
|
||||||
|
} else if((c & 0xE0) == 0xC0) {
|
||||||
|
/* start of two byte sequence */
|
||||||
|
machine->s.byte[machine->len++] = c;
|
||||||
|
machine->state = utf8state_expect1;
|
||||||
|
} else if((c & 0xF0) == 0xE0) {
|
||||||
|
/* start of three byte sequence */
|
||||||
|
machine->s.byte[machine->len++] = c;
|
||||||
|
machine->state = utf8state_expect2;
|
||||||
|
} else if((c & 0xF8) == 0xF0) {
|
||||||
|
/* start of four byte sequence */
|
||||||
|
machine->s.byte[machine->len++] = c;
|
||||||
|
machine->state = utf8state_expect3;
|
||||||
|
} else {
|
||||||
|
/* overlong encoding, reject */
|
||||||
|
machine->state = utf8state_reject;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case utf8state_expect3:
|
||||||
|
machine->s.byte[machine->len++] = c;
|
||||||
|
if((c & 0xC0) == 0x80) {
|
||||||
|
/* all good, continue */
|
||||||
|
machine->state = utf8state_expect2;
|
||||||
|
} else {
|
||||||
|
/* missing extra byte, reject */
|
||||||
|
machine->state = utf8state_reject;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case utf8state_expect2:
|
||||||
|
machine->s.byte[machine->len++] = c;
|
||||||
|
if((c & 0xC0) == 0x80) {
|
||||||
|
/* all good, continue */
|
||||||
|
machine->state = utf8state_expect1;
|
||||||
|
} else {
|
||||||
|
/* missing extra byte, reject */
|
||||||
|
machine->state = utf8state_reject;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case utf8state_expect1:
|
||||||
|
machine->s.byte[machine->len++] = c;
|
||||||
|
if((c & 0xC0) == 0x80) {
|
||||||
|
/* all good, accept */
|
||||||
|
machine->state = utf8state_accept;
|
||||||
|
} else {
|
||||||
|
/* missing extra byte, reject */
|
||||||
|
machine->state = utf8state_reject;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
machine->state = utf8state_reject;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
return machine->state;
|
||||||
|
}
|
||||||
|
|
||||||
struct terminal {
|
struct terminal {
|
||||||
struct window *window;
|
struct window *window;
|
||||||
struct display *display;
|
struct display *display;
|
||||||
char *data;
|
union utf8_char *data;
|
||||||
|
union utf8_char last_char;
|
||||||
|
int data_pitch; /* The width in bytes of a line */
|
||||||
int width, height, start, row, column;
|
int width, height, start, row, column;
|
||||||
int fd, master;
|
int fd, master;
|
||||||
GIOChannel *channel;
|
GIOChannel *channel;
|
||||||
|
|
@ -58,6 +162,7 @@ struct terminal {
|
||||||
char escape[64];
|
char escape[64];
|
||||||
int escape_length;
|
int escape_length;
|
||||||
int state;
|
int state;
|
||||||
|
struct utf8_state_machine state_machine;
|
||||||
int margin;
|
int margin;
|
||||||
int fullscreen;
|
int fullscreen;
|
||||||
int focused;
|
int focused;
|
||||||
|
|
@ -65,27 +170,29 @@ struct terminal {
|
||||||
cairo_font_extents_t extents;
|
cairo_font_extents_t extents;
|
||||||
};
|
};
|
||||||
|
|
||||||
static char *
|
static union utf8_char *
|
||||||
terminal_get_row(struct terminal *terminal, int row)
|
terminal_get_row(struct terminal *terminal, int row)
|
||||||
{
|
{
|
||||||
int index;
|
int index;
|
||||||
|
|
||||||
index = (row + terminal->start) % terminal->height;
|
index = (row + terminal->start) % terminal->height;
|
||||||
|
|
||||||
return &terminal->data[index * (terminal->width + 1)];
|
return &terminal->data[index * terminal->width];
|
||||||
}
|
}
|
||||||
|
|
||||||
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;
|
union utf8_char *data;
|
||||||
|
int data_pitch;
|
||||||
int i, l, total_rows, start;
|
int i, l, total_rows, start;
|
||||||
|
|
||||||
if (terminal->width == width && terminal->height == height)
|
if (terminal->width == width && terminal->height == height)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
size = (width + 1) * height;
|
data_pitch = width * sizeof(union utf8_char);
|
||||||
|
size = data_pitch * height;
|
||||||
data = malloc(size);
|
data = malloc(size);
|
||||||
memset(data, 0, size);
|
memset(data, 0, size);
|
||||||
if (terminal->data) {
|
if (terminal->data) {
|
||||||
|
|
@ -102,13 +209,16 @@ terminal_resize(struct terminal *terminal, int width, int height)
|
||||||
start = 0;
|
start = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
for (i = 0; i < total_rows; i++)
|
for (i = 0; i < total_rows; i++) {
|
||||||
memcpy(data + (width + 1) * i,
|
memcpy(&data[width * i],
|
||||||
terminal_get_row(terminal, i), l);
|
terminal_get_row(terminal, i),
|
||||||
|
l * sizeof(union utf8_char));
|
||||||
|
}
|
||||||
|
|
||||||
free(terminal->data);
|
free(terminal->data);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
terminal->data_pitch = data_pitch;
|
||||||
terminal->width = width;
|
terminal->width = width;
|
||||||
terminal->height = height;
|
terminal->height = height;
|
||||||
terminal->data = data;
|
terminal->data = data;
|
||||||
|
|
@ -130,10 +240,19 @@ terminal_draw_contents(struct terminal *terminal)
|
||||||
struct rectangle rectangle;
|
struct rectangle rectangle;
|
||||||
cairo_t *cr;
|
cairo_t *cr;
|
||||||
cairo_font_extents_t extents;
|
cairo_font_extents_t extents;
|
||||||
int i, top_margin, side_margin;
|
int top_margin, side_margin;
|
||||||
|
int row, col;
|
||||||
|
union utf8_char *p_row;
|
||||||
|
struct utf8_chars {
|
||||||
|
union utf8_char c;
|
||||||
|
char null;
|
||||||
|
} toShow;
|
||||||
|
int text_x, text_y;
|
||||||
cairo_surface_t *surface;
|
cairo_surface_t *surface;
|
||||||
double d;
|
double d;
|
||||||
|
|
||||||
|
toShow.null = 0;
|
||||||
|
|
||||||
window_get_child_rectangle(terminal->window, &rectangle);
|
window_get_child_rectangle(terminal->window, &rectangle);
|
||||||
|
|
||||||
surface = display_create_surface(terminal->display, &rectangle);
|
surface = display_create_surface(terminal->display, &rectangle);
|
||||||
|
|
@ -161,10 +280,17 @@ terminal_draw_contents(struct terminal *terminal)
|
||||||
side_margin = (rectangle.width - terminal->width * extents.max_x_advance) / 2;
|
side_margin = (rectangle.width - terminal->width * extents.max_x_advance) / 2;
|
||||||
top_margin = (rectangle.height - terminal->height * extents.height) / 2;
|
top_margin = (rectangle.height - terminal->height * extents.height) / 2;
|
||||||
|
|
||||||
for (i = 0; i < terminal->height; i++) {
|
for (row = 0; row < terminal->height; row++) {
|
||||||
cairo_move_to(cr, side_margin,
|
p_row = terminal_get_row(terminal, row);
|
||||||
top_margin + extents.ascent + extents.height * i);
|
for (col = 0; col < terminal->width; col++) {
|
||||||
cairo_show_text(cr, terminal_get_row(terminal, i));
|
/* paint the foreground */
|
||||||
|
text_x = side_margin + col * extents.max_x_advance;
|
||||||
|
text_y = top_margin + extents.ascent + row * extents.height;
|
||||||
|
cairo_move_to(cr, text_x, text_y);
|
||||||
|
|
||||||
|
toShow.c = p_row[col];
|
||||||
|
cairo_show_text(cr, (char *) toShow.c.byte);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
d = terminal->focused ? 0 : 0.5;
|
d = terminal->focused ? 0 : 0.5;
|
||||||
|
|
@ -235,7 +361,8 @@ 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, *p;
|
union utf8_char *row;
|
||||||
|
char *p;
|
||||||
int i, count;
|
int i, count;
|
||||||
int args[10], set[10] = { 0, };
|
int args[10], set[10] = { 0, };
|
||||||
|
|
||||||
|
|
@ -283,9 +410,9 @@ handle_escape(struct terminal *terminal)
|
||||||
break;
|
break;
|
||||||
case 'J':
|
case 'J':
|
||||||
row = terminal_get_row(terminal, terminal->row);
|
row = terminal_get_row(terminal, terminal->row);
|
||||||
memset(&row[terminal->column], 0, terminal->width - terminal->column);
|
memset(&row[terminal->column], 0, (terminal->width - terminal->column) * sizeof(union utf8_char));
|
||||||
for (i = terminal->row + 1; i < terminal->height; i++)
|
for (i = terminal->row + 1; i < terminal->height; i++)
|
||||||
memset(terminal_get_row(terminal, i), 0, terminal->width);
|
memset(terminal_get_row(terminal, i), 0, terminal->width * sizeof(union utf8_char));
|
||||||
break;
|
break;
|
||||||
case 'G':
|
case 'G':
|
||||||
if (set[0])
|
if (set[0])
|
||||||
|
|
@ -298,7 +425,7 @@ handle_escape(struct terminal *terminal)
|
||||||
break;
|
break;
|
||||||
case 'K':
|
case 'K':
|
||||||
row = terminal_get_row(terminal, terminal->row);
|
row = terminal_get_row(terminal, terminal->row);
|
||||||
memset(&row[terminal->column], 0, terminal->width - terminal->column);
|
memset(&row[terminal->column], 0, (terminal->width - terminal->column) * sizeof(union utf8_char));
|
||||||
break;
|
break;
|
||||||
case 'm':
|
case 'm':
|
||||||
/* color, blink, bold etc*/
|
/* color, blink, bold etc*/
|
||||||
|
|
@ -322,20 +449,39 @@ static void
|
||||||
terminal_data(struct terminal *terminal, const char *data, size_t length)
|
terminal_data(struct terminal *terminal, const char *data, size_t length)
|
||||||
{
|
{
|
||||||
int i;
|
int i;
|
||||||
char *row;
|
union utf8_char utf8;
|
||||||
|
enum utf8_state parser_state;
|
||||||
|
union utf8_char *row;
|
||||||
|
|
||||||
for (i = 0; i < length; i++) {
|
for (i = 0; i < length; i++) {
|
||||||
|
parser_state =
|
||||||
|
utf8_next_char(&terminal->state_machine, data[i]);
|
||||||
|
switch(parser_state) {
|
||||||
|
case utf8state_accept:
|
||||||
|
utf8.ch = terminal->state_machine.s.ch;
|
||||||
|
break;
|
||||||
|
case utf8state_reject:
|
||||||
|
/* the unicode replacement character */
|
||||||
|
utf8.byte[0] = 0xEF;
|
||||||
|
utf8.byte[1] = 0xBF;
|
||||||
|
utf8.byte[2] = 0xBD;
|
||||||
|
utf8.byte[3] = 0x00;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
row = terminal_get_row(terminal, terminal->row);
|
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++] = utf8.byte[0];
|
||||||
if (terminal->escape_length == 2 && data[i] != '[') {
|
if (terminal->escape_length == 2 && utf8.byte[0] != '[') {
|
||||||
/* Bad escape sequence. */
|
/* Bad escape sequence. */
|
||||||
terminal->state = STATE_NORMAL;
|
terminal->state = STATE_NORMAL;
|
||||||
goto cancel_escape;
|
goto cancel_escape;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (isalpha(data[i])) {
|
if (isalpha(utf8.byte[0])) {
|
||||||
terminal->state = STATE_NORMAL;
|
terminal->state = STATE_NORMAL;
|
||||||
handle_escape(terminal);
|
handle_escape(terminal);
|
||||||
}
|
}
|
||||||
|
|
@ -343,7 +489,7 @@ terminal_data(struct terminal *terminal, const char *data, size_t length)
|
||||||
}
|
}
|
||||||
|
|
||||||
cancel_escape:
|
cancel_escape:
|
||||||
switch (data[i]) {
|
switch (utf8.byte[0]) {
|
||||||
case '\r':
|
case '\r':
|
||||||
terminal->column = 0;
|
terminal->column = 0;
|
||||||
break;
|
break;
|
||||||
|
|
@ -356,12 +502,12 @@ terminal_data(struct terminal *terminal, const char *data, size_t length)
|
||||||
if (terminal->start == terminal->height)
|
if (terminal->start == terminal->height)
|
||||||
terminal->start = 0;
|
terminal->start = 0;
|
||||||
memset(terminal_get_row(terminal, terminal->row),
|
memset(terminal_get_row(terminal, terminal->row),
|
||||||
0, terminal->width);
|
0, terminal->width * sizeof(union utf8_char));
|
||||||
}
|
}
|
||||||
|
|
||||||
break;
|
break;
|
||||||
case '\t':
|
case '\t':
|
||||||
memset(&row[terminal->column], ' ', -terminal->column & 7);
|
memset(&row[terminal->column], ' ', (-terminal->column & 7) * sizeof(union utf8_char));
|
||||||
terminal->column = (terminal->column + 7) & ~7;
|
terminal->column = (terminal->column + 7) & ~7;
|
||||||
break;
|
break;
|
||||||
case '\e':
|
case '\e':
|
||||||
|
|
@ -378,7 +524,8 @@ terminal_data(struct terminal *terminal, const char *data, size_t length)
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
if (terminal->column < terminal->width)
|
if (terminal->column < terminal->width)
|
||||||
row[terminal->column++] = data[i] < 32 ? data[i] + 64 : data[i];
|
if (utf8.byte[0] < 32) utf8.byte[0] += 64;
|
||||||
|
row[terminal->column++] = utf8;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -465,6 +612,9 @@ terminal_create(struct display *display, int fullscreen)
|
||||||
terminal->color_scheme = &jbarnes_colors;
|
terminal->color_scheme = &jbarnes_colors;
|
||||||
terminal->window = window_create(display, "Wayland Terminal",
|
terminal->window = window_create(display, "Wayland Terminal",
|
||||||
500, 400);
|
500, 400);
|
||||||
|
|
||||||
|
init_state_machine(&terminal->state_machine);
|
||||||
|
|
||||||
terminal->display = display;
|
terminal->display = display;
|
||||||
terminal->margin = 5;
|
terminal->margin = 5;
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue