[PATCH v4 0/4] MR9973: Draft: conhost: add basic VT control sequence
This is a minimal approach to running MC in wineconsole and addresses https://bugs.winehq.org/show_bug.cgi?id=49780. Wineconsole thus supports basic CSI control sequences for: - SGR (Select Graphic Rendition) and - CUP (Cursor Position) {width=900 height=507} **TODO:** - [x] check for ENABLE_VIRTUAL_TERMINAL_PROCESSING - [x] bug states that SetConsoleMode does not return error code and therefore applications fail to handle it correctly - [ ] rewrite, if that is the way to go i will do that, to use newer API. This would allow more colors than ANSI colors only - [ ] test shared: several programs attached to the same console -- v4: conhost: add basic CUP color handling conhost: improve basic SGR color handling conhost: add basic SGR color handling conhost: add basic VT control sequence parsing 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 | 56 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 56 insertions(+) diff --git a/programs/conhost/conhost.c b/programs/conhost/conhost.c index 8298743553f..c472fff9c29 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,59 @@ static NTSTATUS write_console( struct screen_buffer *screen_buffer, const WCHAR case '\r': screen_buffer->cursor_x = 0; continue; + case '\e': + if ((screen_buffer->mode & ENABLE_VIRTUAL_TERMINAL_PROCESSING)) + { + 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++) + { + if (buffer[i] >= 0x30 && buffer[i] <= 0x3b) + continue; + else if (buffer[i] >= 0x3c && buffer[i] <= 0x3f) + { + WARN( "This is a private -terminal manufacturer only- CSI sequence\n" ); + continue; + } + else + { + state = ST_INTER; + break; + } + } + /* intermediate bytes 0x20 - 0x2f*/ + for (; i<len && (state == ST_INTER || state == ST_PARAM); i++) + { + if (buffer[i] >= 0x20 && buffer[i] <= 0x2f) + continue; + else + { + state = ST_FINAL; + break; + } + } + if (state == ST_START || state == ST_FINAL) + { + if (buffer[i] >= 0x70 && buffer[i] <= 0x7e) + WARN("This is a private -terminal manufacturer only- CSI sequence\n"); + else if (!(buffer[i] >= 0x40 && buffer[i] <= 0x6f)) + ERR("This was no valid CSI sequence\n"); + } + } + else + WARN( "ENABLE_VIRTUAL_TERMINAL_PROCESSING is disabled\n" ); + + 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> --- programs/conhost/conhost.c | 188 ++++++++++++++++++++++++++++++++++++- 1 file changed, 185 insertions(+), 3 deletions(-) diff --git a/programs/conhost/conhost.c b/programs/conhost/conhost.c index c472fff9c29..14bc6541d33 100644 --- a/programs/conhost/conhost.c +++ b/programs/conhost/conhost.c @@ -2103,13 +2103,186 @@ 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, unsigned int len, unsigned int *arg ) +{ + unsigned int i=0, value = 0; + TRACE( "next SGR arg with %d 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 0; +} + +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 value = 0, *arg = &value; + size_t seq_len = 0; + 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 %Iu 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: + case 31: + case 32: + case 33: + case 34: + case 35: + case 36: + case 37: + { + len -= seq_len; + seq += seq_len; + info_params.info.attr = ( info_params.info.attr & 0xf0 ) | 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 & 0xf0 ) | 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: + case 41: + case 42: + case 43: + case 44: + case 45: + case 46: + case 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[%Iu]\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[%Iu]\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; - size_t i, j; + size_t csi_len = 0, i, j; enum { ST_START, ST_PARAM, ST_INTER, ST_FINAL } state; + const WCHAR *csi_start; TRACE( "%s\n", debugstr_wn(buffer, len) ); @@ -2151,18 +2324,21 @@ static NTSTATUS write_console( struct screen_buffer *screen_buffer, const WCHAR { 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 { - ERR("Invalid CSI start sequence\n"); + ERR( "Invalid CSI start sequence\n" ); + ERR( "%s\n", debugstr_wn(buffer, len) ); break; } /* intermediate bytes 0x30 - 0x3f (0-?) */ for (; i<len && (state == ST_PARAM); i++) { + csi_len++; if (buffer[i] >= 0x30 && buffer[i] <= 0x3b) continue; else if (buffer[i] >= 0x3c && buffer[i] <= 0x3f) @@ -2173,17 +2349,20 @@ static NTSTATUS write_console( struct screen_buffer *screen_buffer, const WCHAR else { state = ST_INTER; + csi_len--; break; } } /* intermediate bytes 0x20 - 0x2f*/ for (; i<len && (state == ST_INTER || state == ST_PARAM); i++) { + csi_len++; if (buffer[i] >= 0x20 && buffer[i] <= 0x2f) continue; else { state = ST_FINAL; + csi_len--; break; } } @@ -2193,6 +2372,9 @@ static NTSTATUS write_console( struct screen_buffer *screen_buffer, const WCHAR WARN("This is a private -terminal manufacturer only- CSI sequence\n"); else if (!(buffer[i] >= 0x40 && buffer[i] <= 0x6f)) ERR("This was no valid CSI sequence\n"); + + csi_len++; + process_csi_sequence_console(screen_buffer, csi_start, csi_len); } } else -- 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 | 23 +++++++++++++++++++---- 1 file changed, 19 insertions(+), 4 deletions(-) diff --git a/programs/conhost/conhost.c b/programs/conhost/conhost.c index 14bc6541d33..228cd2c1a49 100644 --- a/programs/conhost/conhost.c +++ b/programs/conhost/conhost.c @@ -2166,6 +2166,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); @@ -2177,8 +2178,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) { @@ -2219,6 +2226,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 ); @@ -2230,8 +2238,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) { -- GitLab https://gitlab.winehq.org/wine/wine/-/merge_requests/9973
From: Thomas Csovcsity <thc.fr13nd@gmail.com> --- programs/conhost/conhost.c | 44 ++++++++++++++++++++++++++++++++++++-- 1 file changed, 42 insertions(+), 2 deletions(-) diff --git a/programs/conhost/conhost.c b/programs/conhost/conhost.c index 228cd2c1a49..8581d9bf131 100644 --- a/programs/conhost/conhost.c +++ b/programs/conhost/conhost.c @@ -2187,7 +2187,7 @@ static void process_csi_sequence_console( struct screen_buffer *screen_buffer, c color |= BLUE; info_params.info.attr = ( info_params.info.attr & 0xf0 ) | color; } - if (*arg == 5) + else if (*arg == 5) { len -= seq_len; seq += seq_len; @@ -2248,7 +2248,7 @@ static void process_csi_sequence_console( struct screen_buffer *screen_buffer, c info_params.info.attr = ( info_params.info.attr & 0x0f ) | ( color << 4 ); } - if (*arg == 5) + else if (*arg == 5) { len -= seq_len; seq += seq_len; @@ -2284,6 +2284,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 %Iu 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 /* (y != 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[%Iu]\n", debugstr_wn( seq, len ), debugstr_wn( &seq[len], 1 ), len ); break; -- GitLab https://gitlab.winehq.org/wine/wine/-/merge_requests/9973
As far as i see even win10 cmd only supports the colormap approach with 16 colors as default. Therfore i would suggest to keep going on with the attr part and implement missing parts i actually see, so that mc and w64devkit are usable. @epo What do you mean? -- https://gitlab.winehq.org/wine/wine/-/merge_requests/9973#note_129660
you need to include non regression tests this should get you started, you need to still fill some blanks and extend where needed (cursor position comes to mind, and don't forget that scrolling can take place; but also are 256 colors mapped to 16? or ignored) didn't test if your MR behaves correctly wrt to the tests [conhost.test.patch](/uploads/603a5fb27eb93bc09a7ee0be4c6d9a45/conhost.test.patch) -- https://gitlab.winehq.org/wine/wine/-/merge_requests/9973#note_130961
participants (3)
-
eric pouech (@epo) -
Thomas Csovcsity -
Thomas Csovcsity (@thc13)