mirror of
https://gitlab.freedesktop.org/wayland/wayland.git
synced 2025-11-03 09:01:42 -05:00
terminal: Escape sequences with string parameters
Adjust escape parsing code to handle OSC, DSC, and other escape sequences with string parameters. Signed-off-by: Callum Lowcay <callum@callumscode.com>
This commit is contained in:
parent
1b091dd411
commit
67a201da6f
1 changed files with 147 additions and 40 deletions
|
|
@ -336,6 +336,25 @@ attr_init(struct attr *data_attr, struct attr attr, int n)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
enum escape_state {
|
||||||
|
escape_state_normal = 0,
|
||||||
|
escape_state_escape,
|
||||||
|
escape_state_dcs,
|
||||||
|
escape_state_csi,
|
||||||
|
escape_state_osc,
|
||||||
|
escape_state_inner_escape,
|
||||||
|
escape_state_ignore,
|
||||||
|
escape_state_special
|
||||||
|
};
|
||||||
|
|
||||||
|
#define ESC_FLAG_WHAT 0x01
|
||||||
|
#define ESC_FLAG_GT 0x02
|
||||||
|
#define ESC_FLAG_BANG 0x04
|
||||||
|
#define ESC_FLAG_CASH 0x08
|
||||||
|
#define ESC_FLAG_SQUOTE 0x10
|
||||||
|
#define ESC_FLAG_DQUOTE 0x20
|
||||||
|
#define ESC_FLAG_SPACE 0x40
|
||||||
|
|
||||||
struct terminal {
|
struct terminal {
|
||||||
struct window *window;
|
struct window *window;
|
||||||
struct display *display;
|
struct display *display;
|
||||||
|
|
@ -360,8 +379,9 @@ struct terminal {
|
||||||
uint32_t modifiers;
|
uint32_t modifiers;
|
||||||
char escape[MAX_ESCAPE];
|
char escape[MAX_ESCAPE];
|
||||||
int escape_length;
|
int escape_length;
|
||||||
int state;
|
enum escape_state state;
|
||||||
int qmark_flag;
|
enum escape_state outer_state;
|
||||||
|
int escape_flags;
|
||||||
struct utf8_state_machine state_machine;
|
struct utf8_state_machine state_machine;
|
||||||
int margin;
|
int margin;
|
||||||
int fullscreen;
|
int fullscreen;
|
||||||
|
|
@ -860,11 +880,6 @@ redraw_handler(struct window *window, void *data)
|
||||||
terminal_draw(terminal);
|
terminal_draw(terminal);
|
||||||
}
|
}
|
||||||
|
|
||||||
#define STATE_NORMAL 0
|
|
||||||
#define STATE_ESCAPE 1
|
|
||||||
#define STATE_ESCAPE_SPECIAL 2
|
|
||||||
#define STATE_ESCAPE_CSI 3
|
|
||||||
|
|
||||||
static void
|
static void
|
||||||
terminal_data(struct terminal *terminal, const char *data, size_t length);
|
terminal_data(struct terminal *terminal, const char *data, size_t length);
|
||||||
|
|
||||||
|
|
@ -879,7 +894,7 @@ handle_term_parameter(struct terminal *terminal, int code, int sr)
|
||||||
{
|
{
|
||||||
int i;
|
int i;
|
||||||
|
|
||||||
if (terminal->qmark_flag) {
|
if (terminal->escape_flags & ESC_FLAG_WHAT) {
|
||||||
switch(code) {
|
switch(code) {
|
||||||
case 1: /* DECCKM */
|
case 1: /* DECCKM */
|
||||||
if (sr) terminal->key_mode = KM_APPLICATION;
|
if (sr) terminal->key_mode = KM_APPLICATION;
|
||||||
|
|
@ -959,6 +974,16 @@ handle_term_parameter(struct terminal *terminal, int code, int sr)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
handle_dcs(struct terminal *terminal)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
handle_osc(struct terminal *terminal)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
static void
|
static void
|
||||||
handle_escape(struct terminal *terminal)
|
handle_escape(struct terminal *terminal)
|
||||||
{
|
{
|
||||||
|
|
@ -1600,6 +1625,26 @@ handle_char(struct terminal *terminal, union utf8_char utf8)
|
||||||
terminal->last_char = utf8;
|
terminal->last_char = utf8;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
escape_append_utf8(struct terminal *terminal, union utf8_char utf8)
|
||||||
|
{
|
||||||
|
int len, i;
|
||||||
|
|
||||||
|
if ((utf8.byte[0] & 0x80) == 0x00) len = 1;
|
||||||
|
else if ((utf8.byte[0] & 0xE0) == 0xC0) len = 2;
|
||||||
|
else if ((utf8.byte[0] & 0xF0) == 0xE0) len = 3;
|
||||||
|
else if ((utf8.byte[0] & 0xF8) == 0xF0) len = 4;
|
||||||
|
else len = 1; /* Invalid, cannot happen */
|
||||||
|
|
||||||
|
if (terminal->escape_length + len <= MAX_ESCAPE) {
|
||||||
|
for (i = 0; i < len; i++)
|
||||||
|
terminal->escape[terminal->escape_length + i] = utf8.byte[i];
|
||||||
|
terminal->escape_length += len;
|
||||||
|
} else if (terminal->escape_length < MAX_ESCAPE) {
|
||||||
|
terminal->escape[terminal->escape_length++] = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
static void
|
static void
|
||||||
terminal_data(struct terminal *terminal, const char *data, size_t length)
|
terminal_data(struct terminal *terminal, const char *data, size_t length)
|
||||||
{
|
{
|
||||||
|
|
@ -1626,60 +1671,122 @@ terminal_data(struct terminal *terminal, const char *data, size_t length)
|
||||||
}
|
}
|
||||||
|
|
||||||
/* assume escape codes never use non-ASCII characters */
|
/* assume escape codes never use non-ASCII characters */
|
||||||
if (terminal->state == STATE_ESCAPE) {
|
switch (terminal->state) {
|
||||||
terminal->escape[terminal->escape_length++] = utf8.byte[0];
|
case escape_state_escape:
|
||||||
if (utf8.byte[0] == '[') {
|
escape_append_utf8(terminal, utf8);
|
||||||
terminal->state = STATE_ESCAPE_CSI;
|
switch (utf8.byte[0]) {
|
||||||
continue;
|
case 'P': /* DCS */
|
||||||
} else if (utf8.byte[0] == '#' || utf8.byte[0] == '(' ||
|
terminal->state = escape_state_dcs;
|
||||||
utf8.byte[0] == ')')
|
break;
|
||||||
{
|
case '[': /* CSI */
|
||||||
terminal->state = STATE_ESCAPE_SPECIAL;
|
terminal->state = escape_state_csi;
|
||||||
continue;
|
break;
|
||||||
} else {
|
case ']': /* OSC */
|
||||||
terminal->state = STATE_NORMAL;
|
terminal->state = escape_state_osc;
|
||||||
|
break;
|
||||||
|
case '#':
|
||||||
|
case '(':
|
||||||
|
case ')': /* special */
|
||||||
|
terminal->state = escape_state_special;
|
||||||
|
break;
|
||||||
|
case '^': /* PM (not implemented) */
|
||||||
|
case '_': /* APC (not implemented) */
|
||||||
|
terminal->state = escape_state_ignore;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
terminal->state = escape_state_normal;
|
||||||
handle_non_csi_escape(terminal, utf8.byte[0]);
|
handle_non_csi_escape(terminal, utf8.byte[0]);
|
||||||
continue;
|
break;
|
||||||
}
|
}
|
||||||
} else if (terminal->state == STATE_ESCAPE_SPECIAL) {
|
continue;
|
||||||
terminal->escape[terminal->escape_length++] = utf8.byte[0];
|
case escape_state_csi:
|
||||||
terminal->state = STATE_NORMAL;
|
|
||||||
if (isdigit(utf8.byte[0]) || isalpha(utf8.byte[0])) {
|
|
||||||
handle_special_escape(terminal, terminal->escape[1],
|
|
||||||
utf8.byte[0]);
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
} else if (terminal->state == STATE_ESCAPE_CSI) {
|
|
||||||
if (handle_special_char(terminal, utf8.byte[0]) != 0) {
|
if (handle_special_char(terminal, utf8.byte[0]) != 0) {
|
||||||
/* do nothing */
|
/* do nothing */
|
||||||
} else if (utf8.byte[0] == '?') {
|
} else if (utf8.byte[0] == '?') {
|
||||||
terminal->qmark_flag = 1;
|
terminal->escape_flags |= ESC_FLAG_WHAT;
|
||||||
|
} else if (utf8.byte[0] == '>') {
|
||||||
|
terminal->escape_flags |= ESC_FLAG_GT;
|
||||||
|
} else if (utf8.byte[0] == '!') {
|
||||||
|
terminal->escape_flags |= ESC_FLAG_BANG;
|
||||||
|
} else if (utf8.byte[0] == '$') {
|
||||||
|
terminal->escape_flags |= ESC_FLAG_CASH;
|
||||||
|
} else if (utf8.byte[0] == '\'') {
|
||||||
|
terminal->escape_flags |= ESC_FLAG_SQUOTE;
|
||||||
|
} else if (utf8.byte[0] == '"') {
|
||||||
|
terminal->escape_flags |= ESC_FLAG_DQUOTE;
|
||||||
|
} else if (utf8.byte[0] == ' ') {
|
||||||
|
terminal->escape_flags |= ESC_FLAG_SPACE;
|
||||||
} else {
|
} else {
|
||||||
/* Don't overflow the buffer */
|
escape_append_utf8(terminal, utf8);
|
||||||
if (terminal->escape_length < MAX_ESCAPE)
|
|
||||||
terminal->escape[terminal->escape_length++] = utf8.byte[0];
|
|
||||||
if (terminal->escape_length >= MAX_ESCAPE)
|
if (terminal->escape_length >= MAX_ESCAPE)
|
||||||
terminal->state = STATE_NORMAL;
|
terminal->state = escape_state_normal;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (isalpha(utf8.byte[0]) || utf8.byte[0] == '@' ||
|
if (isalpha(utf8.byte[0]) || utf8.byte[0] == '@' ||
|
||||||
utf8.byte[0] == '`')
|
utf8.byte[0] == '`')
|
||||||
{
|
{
|
||||||
terminal->state = STATE_NORMAL;
|
terminal->state = escape_state_normal;
|
||||||
handle_escape(terminal);
|
handle_escape(terminal);
|
||||||
continue;
|
|
||||||
} else {
|
} else {
|
||||||
continue;
|
|
||||||
}
|
}
|
||||||
|
continue;
|
||||||
|
case escape_state_inner_escape:
|
||||||
|
if (utf8.byte[0] == '\\') {
|
||||||
|
terminal->state = escape_state_normal;
|
||||||
|
if (terminal->outer_state == escape_state_dcs) {
|
||||||
|
handle_dcs(terminal);
|
||||||
|
} else if (terminal->outer_state == escape_state_osc) {
|
||||||
|
handle_osc(terminal);
|
||||||
|
}
|
||||||
|
} else if (utf8.byte[0] == '\e') {
|
||||||
|
terminal->state = terminal->outer_state;
|
||||||
|
escape_append_utf8(terminal, utf8);
|
||||||
|
if (terminal->escape_length >= MAX_ESCAPE)
|
||||||
|
terminal->state = escape_state_normal;
|
||||||
|
} else {
|
||||||
|
terminal->state = terminal->outer_state;
|
||||||
|
if (terminal->escape_length < MAX_ESCAPE)
|
||||||
|
terminal->escape[terminal->escape_length++] = '\e';
|
||||||
|
escape_append_utf8(terminal, utf8);
|
||||||
|
if (terminal->escape_length >= MAX_ESCAPE)
|
||||||
|
terminal->state = escape_state_normal;
|
||||||
|
}
|
||||||
|
continue;
|
||||||
|
case escape_state_dcs:
|
||||||
|
case escape_state_osc:
|
||||||
|
case escape_state_ignore:
|
||||||
|
if (utf8.byte[0] == '\e') {
|
||||||
|
terminal->outer_state = terminal->state;
|
||||||
|
terminal->state = escape_state_inner_escape;
|
||||||
|
} else if (utf8.byte[0] == '\a' && terminal->state == escape_state_osc) {
|
||||||
|
terminal->state = escape_state_normal;
|
||||||
|
handle_osc(terminal);
|
||||||
|
} else {
|
||||||
|
escape_append_utf8(terminal, utf8);
|
||||||
|
if (terminal->escape_length >= MAX_ESCAPE)
|
||||||
|
terminal->state = escape_state_normal;
|
||||||
|
}
|
||||||
|
continue;
|
||||||
|
case escape_state_special:
|
||||||
|
escape_append_utf8(terminal, utf8);
|
||||||
|
terminal->state = escape_state_normal;
|
||||||
|
if (isdigit(utf8.byte[0]) || isalpha(utf8.byte[0])) {
|
||||||
|
handle_special_escape(terminal, terminal->escape[1],
|
||||||
|
utf8.byte[0]);
|
||||||
|
}
|
||||||
|
continue;
|
||||||
|
default:
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* this is valid, because ASCII characters are never used to
|
/* this is valid, because ASCII characters are never used to
|
||||||
* introduce a multibyte sequence in UTF-8 */
|
* introduce a multibyte sequence in UTF-8 */
|
||||||
if (utf8.byte[0] == '\e') {
|
if (utf8.byte[0] == '\e') {
|
||||||
terminal->state = STATE_ESCAPE;
|
terminal->state = escape_state_escape;
|
||||||
|
terminal->outer_state = escape_state_normal;
|
||||||
terminal->escape[0] = '\e';
|
terminal->escape[0] = '\e';
|
||||||
terminal->escape_length = 1;
|
terminal->escape_length = 1;
|
||||||
terminal->qmark_flag = 0;
|
terminal->escape_flags = 0;
|
||||||
} else {
|
} else {
|
||||||
handle_char(terminal, utf8);
|
handle_char(terminal, utf8);
|
||||||
} /* if */
|
} /* if */
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue