@ -52,6 +52,10 @@ static int option_fullscreen;
# define ATTRMASK_BLINK 0x04
# define ATTRMASK_BLINK 0x04
# define ATTRMASK_INVERSE 0x08
# define ATTRMASK_INVERSE 0x08
/* Buffer sizes */
# define MAX_RESPONSE 11
# define MAX_ESCAPE 64
union utf8_char {
union utf8_char {
unsigned char byte [ 4 ] ;
unsigned char byte [ 4 ] ;
uint32_t ch ;
uint32_t ch ;
@ -189,9 +193,10 @@ struct terminal {
int fd , master ;
int fd , master ;
GIOChannel * channel ;
GIOChannel * channel ;
uint32_t modifiers ;
uint32_t modifiers ;
char escape [ 64 ] ;
char escape [ MAX_ESCAPE ] ;
int escape_length ;
int escape_length ;
int state ;
int state ;
int qmark_flag ;
struct utf8_state_machine state_machine ;
struct utf8_state_machine state_machine ;
int margin ;
int margin ;
int fullscreen ;
int fullscreen ;
@ -513,10 +518,15 @@ redraw_handler(struct window *window, void *data)
# define STATE_NORMAL 0
# define STATE_NORMAL 0
# define STATE_ESCAPE 1
# 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 ) ;
static void
handle_char ( struct terminal * terminal , union utf8_char utf8 ) ;
static void
static void
handle_sgr ( struct terminal * terminal , int code ) ;
handle_sgr ( struct terminal * terminal , int code ) ;
@ -629,13 +639,21 @@ handle_escape(struct terminal *terminal)
}
}
break ;
break ;
default :
default :
terminal_data ( terminal ,
fprintf ( stderr , " Unknown CSI escape: %c \n " , * p ) ;
terminal - > escape + 1 ,
terminal - > escape_length - 2 ) ;
break ;
break ;
}
}
}
}
static void
handle_non_csi_escape ( struct terminal * terminal , char code )
{
}
static void
handle_special_escape ( struct terminal * terminal , char special , char code )
{
}
static void
static void
handle_sgr ( struct terminal * terminal , int code )
handle_sgr ( struct terminal * terminal , int code )
{
{
@ -698,58 +716,25 @@ handle_sgr(struct terminal *terminal, int code)
}
}
}
}
static void
/* Returns 1 if c was special, otherwise 0 */
terminal_data ( struct terminal * terminal , const char * data , size_t length )
static int
handle_special_char ( struct terminal * terminal , char c )
{
{
int i ;
union utf8_char utf8 ;
enum utf8_state parser_state ;
union utf8_char * row ;
union utf8_char * row ;
struct attr * attr_row ;
struct attr * attr_row ;
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 ) ;
attr_row = terminal_get_attr_row ( terminal , terminal - > row ) ;
attr_row = terminal_get_attr_row ( terminal , terminal - > row ) ;
if ( terminal - > state = = STATE_ESCAPE ) {
switch ( c ) {
terminal - > escape [ terminal - > escape_length + + ] = utf8 . byte [ 0 ] ;
if ( terminal - > escape_length = = 2 & & utf8 . byte [ 0 ] ! = ' [ ' ) {
/* Bad escape sequence. */
terminal - > state = STATE_NORMAL ;
goto cancel_escape ;
}
if ( isalpha ( utf8 . byte [ 0 ] ) ) {
terminal - > state = STATE_NORMAL ;
handle_escape ( terminal ) ;
}
continue ;
}
cancel_escape :
switch ( utf8 . byte [ 0 ] ) {
case ' \r ' :
case ' \r ' :
terminal - > column = 0 ;
terminal - > column = 0 ;
break ;
break ;
case ' \n ' :
case ' \n ' :
terminal - > column = 0 ;
terminal - > column = 0 ;
/* fallthrough */
case ' \v ' :
case ' \f ' :
if ( terminal - > row + 1 < terminal - > height ) {
if ( terminal - > row + 1 < terminal - > height ) {
terminal - > row + + ;
terminal - > row + + ;
} else {
} else {
@ -768,11 +753,6 @@ terminal_data(struct terminal *terminal, const char *data, size_t length)
attr_init ( & attr_row [ terminal - > column ] , terminal - > curr_attr , - terminal - > column & 7 ) ;
attr_init ( & attr_row [ terminal - > column ] , terminal - > curr_attr , - terminal - > column & 7 ) ;
terminal - > column = ( terminal - > column + 7 ) & ~ 7 ;
terminal - > column = ( terminal - > column + 7 ) & ~ 7 ;
break ;
break ;
case ' \e ' :
terminal - > state = STATE_ESCAPE ;
terminal - > escape [ 0 ] = ' \e ' ;
terminal - > escape_length = 1 ;
break ;
case ' \b ' :
case ' \b ' :
if ( terminal - > column > 0 )
if ( terminal - > column > 0 )
terminal - > column - - ;
terminal - > column - - ;
@ -781,14 +761,133 @@ terminal_data(struct terminal *terminal, const char *data, size_t length)
/* Bell */
/* Bell */
break ;
break ;
default :
default :
if ( terminal - > column < terminal - > width )
return 0 ;
if ( utf8 . byte [ 0 ] < 32 ) utf8 . byte [ 0 ] + = 64 ;
}
return 1 ;
}
static void
handle_char ( struct terminal * terminal , union utf8_char utf8 )
{
union utf8_char * row ;
struct attr * attr_row ;
if ( handle_special_char ( terminal , utf8 . byte [ 0 ] ) ) return ;
/* There are a whole lot of non-characters, control codes,
* and formatting codes that should probably be ignored ,
* for example : */
if ( strncmp ( ( char * ) utf8 . byte , " \xEF \xBB \xBF " , 3 ) = = 0 ) {
/* BOM, ignore */
return ;
}
/* Some of these non-characters should be translated, e.g.: */
if ( utf8 . byte [ 0 ] < 32 ) {
utf8 . byte [ 0 ] = utf8 . byte [ 0 ] + 64 ;
}
/* handle right margin effects */
if ( terminal - > column > = terminal - > width ) {
terminal - > column - - ;
}
row = terminal_get_row ( terminal , terminal - > row ) ;
attr_row = terminal_get_attr_row ( terminal , terminal - > row ) ;
row [ terminal - > column ] = utf8 ;
row [ terminal - > column ] = utf8 ;
attr_row [ terminal - > column + + ] = terminal - > curr_attr ;
attr_row [ terminal - > column + + ] = terminal - > curr_attr ;
if ( utf8 . ch ! = terminal - > last_char . ch )
terminal - > last_char = utf8 ;
}
static void
terminal_data ( struct terminal * terminal , const char * data , size_t length )
{
int i ;
union utf8_char utf8 ;
enum utf8_state parser_state ;
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 ;
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 ;
}
/* assume escape codes never use non-ASCII characters */
if ( terminal - > state = = STATE_ESCAPE ) {
terminal - > escape [ terminal - > escape_length + + ] = utf8 . byte [ 0 ] ;
if ( utf8 . byte [ 0 ] = = ' [ ' ) {
terminal - > state = STATE_ESCAPE_CSI ;
continue ;
} else if ( utf8 . byte [ 0 ] = = ' # ' | | utf8 . byte [ 0 ] = = ' ( ' | |
utf8 . byte [ 0 ] = = ' ) ' )
{
terminal - > state = STATE_ESCAPE_SPECIAL ;
continue ;
} else {
terminal - > state = STATE_NORMAL ;
handle_non_csi_escape ( terminal , utf8 . byte [ 0 ] ) ;
continue ;
}
} 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 ) {
if ( handle_special_char ( terminal , utf8 . byte [ 0 ] ) ! = 0 ) {
/* do nothing */
} else if ( utf8 . byte [ 0 ] = = ' ? ' ) {
terminal - > qmark_flag = 1 ;
} else {
/* Don't overflow the buffer */
if ( terminal - > escape_length < MAX_ESCAPE )
terminal - > escape [ terminal - > escape_length + + ] = utf8 . byte [ 0 ] ;
if ( terminal - > escape_length > = MAX_ESCAPE )
terminal - > state = STATE_NORMAL ;
}
if ( isalpha ( utf8 . byte [ 0 ] ) | | utf8 . byte [ 0 ] = = ' @ ' | |
utf8 . byte [ 0 ] = = ' ` ' )
{
terminal - > state = STATE_NORMAL ;
handle_escape ( terminal ) ;
continue ;
} else {
continue ;
}
}
}
}
/* this is valid, because ASCII characters are never used to
* introduce a multibyte sequence in UTF - 8 */
if ( utf8 . byte [ 0 ] = = ' \e ' ) {
terminal - > state = STATE_ESCAPE ;
terminal - > escape [ 0 ] = ' \e ' ;
terminal - > escape_length = 1 ;
terminal - > qmark_flag = 0 ;
} else {
handle_char ( terminal , utf8 ) ;
} /* if */
} /* for */
window_schedule_redraw ( terminal - > window ) ;
window_schedule_redraw ( terminal - > window ) ;
}
}