@ -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 ;
}
}
} else if ( terminal - > state = = STATE_ESCAPE_CSI ) {
continue ;
case escape_state_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 */