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) {
 | 
					 | 
				
			||||||
			terminal->escape[terminal->escape_length++] = utf8.byte[0];
 | 
					 | 
				
			||||||
			terminal->state = STATE_NORMAL;
 | 
					 | 
				
			||||||
			if (isdigit(utf8.byte[0]) || isalpha(utf8.byte[0])) {
 | 
					 | 
				
			||||||
				handle_special_escape(terminal, terminal->escape[1],
 | 
					 | 
				
			||||||
				                      utf8.byte[0]);
 | 
					 | 
				
			||||||
			continue;
 | 
								continue;
 | 
				
			||||||
			}
 | 
							case escape_state_csi:
 | 
				
			||||||
		} 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