[PATCH 0/4] MR9973: Draft: conhost: add basic VT control sequence
This is a minimal approach to running MC in wineconsole. Wineconsole thus supports basic CSI control sequences for: - SGR (Select Graphic Rendition) and - CUP (Cursor Position) {width=900 height=507} -- https://gitlab.winehq.org/wine/wine/-/merge_requests/9973
From: Thomas Csovcsity <thc.fr13nd@gmail.com> Remove Control Sequence Introducer(CSI) commands avoids console scrambling --- programs/conhost/conhost.c | 60 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 60 insertions(+) diff --git a/programs/conhost/conhost.c b/programs/conhost/conhost.c index 8298743553f..6bf81f97407 100644 --- a/programs/conhost/conhost.c +++ b/programs/conhost/conhost.c @@ -2107,6 +2107,9 @@ static NTSTATUS write_console( struct screen_buffer *screen_buffer, const WCHAR { RECT update_rect; size_t i, j; + enum { + ST_START, ST_PARAM, ST_INTER, ST_FINAL + } state; TRACE( "%s\n", debugstr_wn(buffer, len) ); @@ -2143,6 +2146,63 @@ static NTSTATUS write_console( struct screen_buffer *screen_buffer, const WCHAR case '\r': screen_buffer->cursor_x = 0; continue; + case '\e': + if ( buffer[i+1] == '[') + { + FIXME( "CSI sequences not supported fully yet, only skipping control sequences\n" ); + state = ST_PARAM; + i+=2; + } + else + { + ERR("Invalid CSI start sequence\n"); + break; + } + /* intermediate bytes 0x30 - 0x3f (0-?) */ + for (; i<len && (state == ST_PARAM); i++) + { + switch (buffer[i]) + { + case 0x30 ... 0x3b: + continue; + case 0x3c ... 0x3f: /* private for terminal manufacturers */ + WARN("This is a private -terminal manufacturer only- CSI sequence\n"); + continue; + default: + state = ST_INTER; + break; + } + break; + } + /* intermediate bytes 0x20 - 0x2f*/ + for (; i<len && (state == ST_INTER || state == ST_PARAM); i++) + { + switch (buffer[i]) + { + case 0x20 ... 0x2f: + continue; + default: + state = ST_FINAL; + break; + } + break; + } + if ( state == ST_START || state == ST_FINAL ) + { + switch (buffer[i]) + { + case 0x40 ... 0x6F: + break; + case 0x70 ... 0x7E: /* private for terminal manufacturers */ + WARN("This is a private -terminal manufacturer only- CSI sequence\n"); + break; + default: + ERR("This was no valid CSI sequence\n"); + break; + } + i++; + break; + } } } if (screen_buffer->cursor_x == screen_buffer->width && !(screen_buffer->mode & ENABLE_WRAP_AT_EOL_OUTPUT)) -- GitLab https://gitlab.winehq.org/wine/wine/-/merge_requests/9973
From: Thomas Csovcsity <thc.fr13nd@gmail.com> --- programs/conhost/conhost.c | 171 ++++++++++++++++++++++++++++++++++++- 1 file changed, 168 insertions(+), 3 deletions(-) diff --git a/programs/conhost/conhost.c b/programs/conhost/conhost.c index 6bf81f97407..2d4c7f1c011 100644 --- a/programs/conhost/conhost.c +++ b/programs/conhost/conhost.c @@ -2103,6 +2103,163 @@ static NTSTATUS set_output_info( struct screen_buffer *screen_buffer, return STATUS_SUCCESS; } +#define BLACK 0x0000 +#define BLUE 0x0001 +#define GREEN 0x0002 +#define CYAN BLUE | GREEN +#define RED 0x0004 +#define MAGENTA BLUE | RED +#define YELLOW GREEN | RED +#define WHITE BLUE | GREEN | RED + +static unsigned int next_csi_sgr_argument(const WCHAR *seq, size_t len, unsigned int *arg) +{ + unsigned int i=0, value = 0; + TRACE("next SGR arg with %lld length and [%s] as sequence! seq_p %p\n",len, debugstr_wn( seq, len ), seq); + for ( i=0; i<len; i++ ) + { + if ( (seq[i] == ';') || (seq[i]=='m') ) + { + *arg = value; + return i+1; + } + value = 10 * value + seq[i]-'0'; + } + return i; +} + +static void process_csi_sequence_console( struct screen_buffer *screen_buffer, const WCHAR *seq, size_t len ) +{ + unsigned int colors[] = {BLACK, RED, GREEN, YELLOW, BLUE, MAGENTA, CYAN, WHITE}; + unsigned int seq_len = 0, value = 0, *arg = &value; + unsigned int r = 0, g = 0, b = 0; + struct condrv_output_info_params info_params; + + switch (seq[len-1]) + { + case 'm': /* Select Graphic Rendition (SGR) */ + while (len > 0) + { + TRACE("This is an SGR seq with %lld length and [%s] as payload! seq_p %p\n",len, debugstr_wn( seq, len ), seq); + seq_len = next_csi_sgr_argument(seq, len, arg); + switch (value) + { + case 30 ... 37: + { + len -= seq_len; + seq += seq_len; + info_params.info.attr = ( info_params.info.attr & 0xff00 ) | colors[*arg-30]; + break; + } + case 38: + { + len -= seq_len; + seq += seq_len; + seq_len = next_csi_sgr_argument(seq, len, arg); + if ( *arg == 2 ) + { + len -= seq_len; + seq += seq_len; + seq_len = next_csi_sgr_argument(seq, len, &r); + len -= seq_len; + seq += seq_len; + seq_len = next_csi_sgr_argument(seq, len, &g); + len -= seq_len; + seq += seq_len; + seq_len = next_csi_sgr_argument(seq, len, &b); + len -= seq_len; + seq += seq_len; + FIXME("24bit rgb is not supported yet for foreground r[%d]g[%d]b[%d]\n", r, g, b); + info_params.info.attr = ( BLACK < 8 ) | ( WHITE ); /* Fixme set correct color */ + } + if ( *arg == 5 ) + { + len -= seq_len; + seq += seq_len; + seq_len = next_csi_sgr_argument(seq, len, arg); + len -= seq_len; + seq += seq_len; + info_params.info.attr = ( info_params.info.attr & 0xff00 ) | colors[*arg]; + } + break; + } + case 39: + { + len -= seq_len; + seq += seq_len; + info_params.info.attr = ( info_params.info.attr & 0xf0 ) | ( WHITE ); /* default white fg */ + break; + } + case 40 ... 47: + { + len -= seq_len; + seq += seq_len; + info_params.info.attr = ( info_params.info.attr & 0x0f ) | ( colors[*arg-40] << 4 ); + break; + } + case 48: + { + len -= seq_len; + seq += seq_len; + seq_len = next_csi_sgr_argument(seq, len, arg); + if ( *arg == 2 ) + { + len -= seq_len; + seq += seq_len; + seq_len = next_csi_sgr_argument(seq, len, &r); + len -= seq_len; + seq += seq_len; + seq_len = next_csi_sgr_argument(seq, len, &g); + len -= seq_len; + seq += seq_len; + seq_len = next_csi_sgr_argument(seq, len, &b); + len -= seq_len; + seq += seq_len; + FIXME("24bit rgb is not supported yet for background r[%d]g[%d]b[%d]\n", r, g, b); + info_params.info.attr = ( BLACK < 4 ) | ( WHITE ); /* Fixme set correct color */ + } + if ( *arg == 5 ) + { + len -= seq_len; + seq += seq_len; + seq_len = next_csi_sgr_argument(seq, len, arg); + len -= seq_len; + seq += seq_len; + info_params.info.attr = ( info_params.info.attr & 0xf0 ) | colors[*arg]; + } + break; + } + case 49: + { + len -= seq_len; + seq += seq_len; + info_params.info.attr = ( info_params.info.attr & 0x000f ) | ( BLACK < 4 ); /* default black bg */ + break; + } + case 0: + { + len -= seq_len; + seq += seq_len; + info_params.info.attr = ( BLACK < 4 ) | ( WHITE ); //white on black + break; + } + default: + FIXME( "unhandled sgr sequence %s len[%lld]\n", debugstr_wn( seq, len ), len); + info_params.info.attr = ( BLACK < 4 ) | ( WHITE ); //white on black + len = 0; + break; + + } + info_params.mask = SET_CONSOLE_OUTPUT_INFO_ATTR; + set_output_info( screen_buffer, &info_params, sizeof(info_params) ); + } + break; + default: + FIXME( "unhandled sequence %s switch char [%s] len[%lld]\n", debugstr_wn( seq, len ), debugstr_wn( &seq[len], 1 ), len); + break; + } +} + static NTSTATUS write_console( struct screen_buffer *screen_buffer, const WCHAR *buffer, size_t len ) { RECT update_rect; @@ -2110,6 +2267,8 @@ static NTSTATUS write_console( struct screen_buffer *screen_buffer, const WCHAR enum { ST_START, ST_PARAM, ST_INTER, ST_FINAL } state; + const WCHAR *csi_start; + size_t csi_len = 0; TRACE( "%s\n", debugstr_wn(buffer, len) ); @@ -2149,8 +2308,9 @@ static NTSTATUS write_console( struct screen_buffer *screen_buffer, const WCHAR case '\e': if ( buffer[i+1] == '[') { - FIXME( "CSI sequences not supported fully yet, only skipping control sequences\n" ); state = ST_PARAM; + csi_start = &buffer[i+2]; + csi_len = 0; i+=2; } else @@ -2161,6 +2321,7 @@ static NTSTATUS write_console( struct screen_buffer *screen_buffer, const WCHAR /* intermediate bytes 0x30 - 0x3f (0-?) */ for (; i<len && (state == ST_PARAM); i++) { + csi_len++; switch (buffer[i]) { case 0x30 ... 0x3b: @@ -2170,6 +2331,7 @@ static NTSTATUS write_console( struct screen_buffer *screen_buffer, const WCHAR continue; default: state = ST_INTER; + csi_len--; break; } break; @@ -2177,12 +2339,14 @@ static NTSTATUS write_console( struct screen_buffer *screen_buffer, const WCHAR /* intermediate bytes 0x20 - 0x2f*/ for (; i<len && (state == ST_INTER || state == ST_PARAM); i++) { + csi_len++; switch (buffer[i]) { case 0x20 ... 0x2f: continue; default: state = ST_FINAL; + csi_len--; break; } break; @@ -2200,9 +2364,10 @@ static NTSTATUS write_console( struct screen_buffer *screen_buffer, const WCHAR ERR("This was no valid CSI sequence\n"); break; } - i++; - break; + csi_len++; + process_csi_sequence_console(screen_buffer, csi_start, csi_len); } + continue; } } if (screen_buffer->cursor_x == screen_buffer->width && !(screen_buffer->mode & ENABLE_WRAP_AT_EOL_OUTPUT)) -- GitLab https://gitlab.winehq.org/wine/wine/-/merge_requests/9973
From: Thomas Csovcsity <thc.fr13nd@gmail.com> add simple RGB24 to 3bit RGB --- programs/conhost/conhost.c | 30 +++++++++++++++++++++++------- 1 file changed, 23 insertions(+), 7 deletions(-) diff --git a/programs/conhost/conhost.c b/programs/conhost/conhost.c index 2d4c7f1c011..de0f239f204 100644 --- a/programs/conhost/conhost.c +++ b/programs/conhost/conhost.c @@ -2125,7 +2125,7 @@ static unsigned int next_csi_sgr_argument(const WCHAR *seq, size_t len, unsigned } value = 10 * value + seq[i]-'0'; } - return i; + return 0; } static void process_csi_sequence_console( struct screen_buffer *screen_buffer, const WCHAR *seq, size_t len ) @@ -2148,7 +2148,7 @@ static void process_csi_sequence_console( struct screen_buffer *screen_buffer, c { len -= seq_len; seq += seq_len; - info_params.info.attr = ( info_params.info.attr & 0xff00 ) | colors[*arg-30]; + info_params.info.attr = ( info_params.info.attr & 0xf0 ) | colors[*arg-30]; break; } case 38: @@ -2158,6 +2158,7 @@ static void process_csi_sequence_console( struct screen_buffer *screen_buffer, c seq_len = next_csi_sgr_argument(seq, len, arg); if ( *arg == 2 ) { + int color = BLACK; len -= seq_len; seq += seq_len; seq_len = next_csi_sgr_argument(seq, len, &r); @@ -2169,8 +2170,14 @@ static void process_csi_sequence_console( struct screen_buffer *screen_buffer, c seq_len = next_csi_sgr_argument(seq, len, &b); len -= seq_len; seq += seq_len; - FIXME("24bit rgb is not supported yet for foreground r[%d]g[%d]b[%d]\n", r, g, b); - info_params.info.attr = ( BLACK < 8 ) | ( WHITE ); /* Fixme set correct color */ + /* a simple color mapping for basic support */ + if ( r > 127 ) + color |= RED; + if ( g > 127 ) + color |= GREEN; + if ( b > 127 ) + color |= BLUE; + info_params.info.attr = ( info_params.info.attr & 0xf0 ) | color; } if ( *arg == 5 ) { @@ -2179,7 +2186,7 @@ static void process_csi_sequence_console( struct screen_buffer *screen_buffer, c seq_len = next_csi_sgr_argument(seq, len, arg); len -= seq_len; seq += seq_len; - info_params.info.attr = ( info_params.info.attr & 0xff00 ) | colors[*arg]; + info_params.info.attr = ( info_params.info.attr & 0xf0 ) | colors[*arg]; } break; } @@ -2204,6 +2211,7 @@ static void process_csi_sequence_console( struct screen_buffer *screen_buffer, c seq_len = next_csi_sgr_argument(seq, len, arg); if ( *arg == 2 ) { + int color = BLACK; len -= seq_len; seq += seq_len; seq_len = next_csi_sgr_argument(seq, len, &r); @@ -2215,8 +2223,15 @@ static void process_csi_sequence_console( struct screen_buffer *screen_buffer, c seq_len = next_csi_sgr_argument(seq, len, &b); len -= seq_len; seq += seq_len; - FIXME("24bit rgb is not supported yet for background r[%d]g[%d]b[%d]\n", r, g, b); - info_params.info.attr = ( BLACK < 4 ) | ( WHITE ); /* Fixme set correct color */ + /* a simple color mapping for basic support */ + if ( r > 127 ) + color |= RED; + if ( g > 127 ) + color |= GREEN; + if ( b > 127 ) + color |= BLUE; + info_params.info.attr = ( info_params.info.attr & 0x0f ) | ( color << 4 ); + } if ( *arg == 5 ) { @@ -2316,6 +2331,7 @@ static NTSTATUS write_console( struct screen_buffer *screen_buffer, const WCHAR else { ERR("Invalid CSI start sequence\n"); + ERR( "%s\n", debugstr_wn(buffer, len) ); break; } /* intermediate bytes 0x30 - 0x3f (0-?) */ -- GitLab https://gitlab.winehq.org/wine/wine/-/merge_requests/9973
From: Thomas Csovcsity <thc.fr13nd@gmail.com> --- programs/conhost/conhost.c | 40 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 40 insertions(+) diff --git a/programs/conhost/conhost.c b/programs/conhost/conhost.c index de0f239f204..ec84df0ba7e 100644 --- a/programs/conhost/conhost.c +++ b/programs/conhost/conhost.c @@ -2269,6 +2269,46 @@ static void process_csi_sequence_console( struct screen_buffer *screen_buffer, c set_output_info( screen_buffer, &info_params, sizeof(info_params) ); } break; + case 'H': + unsigned int x = 0, y = 0, value = 0, i; + TRACE("This is an CUP seq with %lld length and [%s] as payload! seq_p %p\n",len, debugstr_wn( seq, len ), seq); + + for (i = 0; i < len; i++) + { + if ( seq[i] != ';' && seq[i] != 'H' ) + value = 10 * value + seq[i] - '0'; + else if ( seq[i] == ';' ) + { + if ( value != 0 ) + { + y = value; + value = 0; + } + else + y = 1; + } + else if ( seq[i] == 'H') + { + if ( y == 0 && value == 0) + { + x = 1; + y = 1; + } else if ( y == 0 && value != 0 ) + { + y = value; + x = 1; + } else /* ( x != 0 && value != 0 ) */ + x = value; + } + else + { + ERR("CSI CUP parsing failed, this should not happen!\n"); + break; + } + } + screen_buffer->cursor_x = x-1; + screen_buffer->cursor_y = y-1; + break; default: FIXME( "unhandled sequence %s switch char [%s] len[%lld]\n", debugstr_wn( seq, len ), debugstr_wn( &seq[len], 1 ), len); break; -- GitLab https://gitlab.winehq.org/wine/wine/-/merge_requests/9973
participants (2)
-
Thomas Csovcsity -
Thomas Csovcsity (@thc13)