Signed-off-by: Eric Pouech eric.pouech@gmail.com
--- dlls/kernelbase/console.c | 32 ++++++++++++++- include/wine/condrv.h | 1 programs/conhost/conhost.c | 93 +++++++++++++++++++++++++++++++++++--------- programs/conhost/conhost.h | 1 server/console.c | 1 5 files changed, 107 insertions(+), 21 deletions(-)
diff --git a/dlls/kernelbase/console.c b/dlls/kernelbase/console.c index a7eeb439232..52949ab7541 100644 --- a/dlls/kernelbase/console.c +++ b/dlls/kernelbase/console.c @@ -1598,8 +1598,36 @@ BOOL WINAPI ReadConsoleW( HANDLE handle, void *buffer, DWORD length, DWORD *coun return FALSE; }
- ret = console_ioctl( handle, IOCTL_CONDRV_READ_CONSOLE, NULL, 0, buffer, - length * sizeof(WCHAR), count ); + if (reserved) + { + CONSOLE_READCONSOLE_CONTROL* crc = reserved; + char *tmp; + + if (crc->nLength != sizeof(*crc) || crc->nInitialChars >= length) + { + SetLastError( ERROR_INVALID_PARAMETER ); + return FALSE; + } + if (!(tmp = HeapAlloc( GetProcessHeap(), 0, sizeof(DWORD) + crc->nInitialChars * sizeof(WCHAR) ))) + { + SetLastError( ERROR_NOT_ENOUGH_MEMORY ); + return FALSE; + } + + memcpy( tmp, &crc->dwCtrlWakeupMask, sizeof(DWORD) ); + memcpy( tmp + sizeof(DWORD), buffer, crc->nInitialChars * sizeof(WCHAR) ); + ret = console_ioctl( handle, IOCTL_CONDRV_READ_COMPLETION, + tmp, sizeof(DWORD) + crc->nInitialChars * sizeof(WCHAR), + buffer, length * sizeof(WCHAR), + count ); + crc->dwConsoleKeyState = 0; + HeapFree( GetProcessHeap(), 0, tmp ); + } + else + { + ret = console_ioctl( handle, IOCTL_CONDRV_READ_CONSOLE, NULL, 0, buffer, + length * sizeof(WCHAR), count ); + } if (ret) *count /= sizeof(WCHAR); return ret; } diff --git a/include/wine/condrv.h b/include/wine/condrv.h index 4d2332a1ee9..610fb859511 100644 --- a/include/wine/condrv.h +++ b/include/wine/condrv.h @@ -43,6 +43,7 @@ #define IOCTL_CONDRV_BEEP CTL_CODE(FILE_DEVICE_CONSOLE, 20, METHOD_BUFFERED, FILE_ANY_ACCESS) #define IOCTL_CONDRV_FLUSH CTL_CODE(FILE_DEVICE_CONSOLE, 21, METHOD_BUFFERED, FILE_ANY_ACCESS) #define IOCTL_CONDRV_GET_WINDOW CTL_CODE(FILE_DEVICE_CONSOLE, 22, METHOD_BUFFERED, FILE_ANY_ACCESS) +#define IOCTL_CONDRV_READ_COMPLETION CTL_CODE(FILE_DEVICE_CONSOLE, 24, METHOD_BUFFERED, FILE_READ_ACCESS)
/* console output ioctls */ #define IOCTL_CONDRV_WRITE_CONSOLE CTL_CODE(FILE_DEVICE_CONSOLE, 30, METHOD_BUFFERED, FILE_WRITE_ACCESS) diff --git a/programs/conhost/conhost.c b/programs/conhost/conhost.c index 2b137c40fb0..b7f59a25e0d 100644 --- a/programs/conhost/conhost.c +++ b/programs/conhost/conhost.c @@ -492,6 +492,7 @@ static void read_from_buffer( struct console *console, size_t out_size ) switch( console->read_ioctl ) { case IOCTL_CONDRV_READ_CONSOLE: + case IOCTL_CONDRV_READ_COMPLETION: out_size = min( out_size, console->read_buffer_count * sizeof(WCHAR) ); read_complete( console, STATUS_SUCCESS, console->read_buffer, out_size, console->record_count != 0 ); read_len = out_size / sizeof(WCHAR); @@ -1155,7 +1156,7 @@ static unsigned int edit_line_string_width( const WCHAR *str, unsigned int len) return offset; }
-static void update_read_output( struct console *console ) +static void update_read_output( struct console *console, BOOL newline ) { struct screen_buffer *screen_buffer = console->active; struct edit_line *ctx = &console->edit_line; @@ -1203,7 +1204,7 @@ static void update_read_output( struct console *console ) } }
- if (!ctx->status) + if (newline) { offset = edit_line_string_width( ctx->buf, ctx->len ); screen_buffer->cursor_x = 0; @@ -1241,10 +1242,14 @@ static void update_read_output( struct console *console ) update_window_config( screen_buffer->console, TRUE ); }
+/* can end on any ctrl-character: from 0x00 up to 0x1F) */ +#define FIRST_NON_CONTROL_CHAR (L' ') + static NTSTATUS process_console_input( struct console *console ) { struct edit_line *ctx = &console->edit_line; unsigned int i; + WCHAR ctrl_value = FIRST_NON_CONTROL_CHAR;
switch (console->read_ioctl) { @@ -1252,6 +1257,7 @@ static NTSTATUS process_console_input( struct console *console ) if (console->record_count) read_console_input( console, console->pending_read ); return STATUS_SUCCESS; case IOCTL_CONDRV_READ_CONSOLE: + case IOCTL_CONDRV_READ_COMPLETION: case IOCTL_CONDRV_READ_FILE: break; default: @@ -1264,7 +1270,7 @@ static NTSTATUS process_console_input( struct console *console ) ctx->update_begin = ctx->len + 1; ctx->update_end = 0;
- for (i = 0; i < console->record_count && ctx->status == STATUS_PENDING; i++) + for (i = 0; i < console->record_count && ctx->status == STATUS_PENDING && ctrl_value == FIRST_NON_CONTROL_CHAR; i++) { void (*func)( struct console *console ) = NULL; INPUT_RECORD ir = console->records[i]; @@ -1284,6 +1290,14 @@ static NTSTATUS process_console_input( struct console *console ) /* mask out some bits which don't interest us */ state = ir.Event.KeyEvent.dwControlKeyState & ~(NUMLOCK_ON|SCROLLLOCK_ON|CAPSLOCK_ON|ENHANCED_KEY);
+ if (ir.Event.KeyEvent.uChar.UnicodeChar && + ir.Event.KeyEvent.uChar.UnicodeChar < FIRST_NON_CONTROL_CHAR && + (ctx->ctrl_mask & (1u << ir.Event.KeyEvent.uChar.UnicodeChar))) + { + ctrl_value = ir.Event.KeyEvent.uChar.UnicodeChar; + ctx->status = STATUS_SUCCESS; + continue; + } func = NULL; for (map = console->edition_mode ? emacs_key_map : win32_key_map; map->entries != NULL; map++) { @@ -1326,7 +1340,7 @@ static NTSTATUS process_console_input( struct console *console ) } else if (ctx->len >= console->pending_read / sizeof(WCHAR)) ctx->status = STATUS_SUCCESS; - } + } }
if (console->record_count > i) memmove( console->records, console->records + i, @@ -1336,19 +1350,26 @@ static NTSTATUS process_console_input( struct console *console ) if (ctx->status == STATUS_PENDING && !(console->mode & ENABLE_LINE_INPUT) && ctx->len) ctx->status = STATUS_SUCCESS;
- if (console->mode & ENABLE_ECHO_INPUT) update_read_output( console ); + if (console->mode & ENABLE_ECHO_INPUT) update_read_output( console, !ctx->status && ctrl_value == FIRST_NON_CONTROL_CHAR ); if (ctx->status == STATUS_PENDING) return STATUS_SUCCESS;
if (!ctx->status && (console->mode & ENABLE_LINE_INPUT)) { - if (ctx->len) append_input_history( console, ctx->buf, ctx->len * sizeof(WCHAR) ); - if (edit_line_grow(console, 2)) + if (ctrl_value < FIRST_NON_CONTROL_CHAR) + { + edit_line_insert( console, &ctrl_value, 1 ); + } + else { - ctx->buf[ctx->len++] = '\r'; - ctx->buf[ctx->len++] = '\n'; - ctx->buf[ctx->len] = 0; - TRACE( "return %s\n", debugstr_wn( ctx->buf, ctx->len )); + if (ctx->len) append_input_history( console, ctx->buf, ctx->len * sizeof(WCHAR) ); + if (edit_line_grow(console, 2)) + { + ctx->buf[ctx->len++] = '\r'; + ctx->buf[ctx->len++] = '\n'; + ctx->buf[ctx->len] = 0; + } } + TRACE( "return %s\n", debugstr_wn( ctx->buf, ctx->len )); }
console->read_buffer = ctx->buf; @@ -1365,8 +1386,10 @@ static NTSTATUS process_console_input( struct console *console ) return STATUS_SUCCESS; }
-static NTSTATUS read_console( struct console *console, unsigned int ioctl, size_t out_size ) +static NTSTATUS read_console( struct console *console, unsigned int ioctl, size_t out_size, + const WCHAR *keep, unsigned int keep_len, unsigned int ctrl_mask ) { + struct edit_line *ctx = &console->edit_line; TRACE("\n");
if (out_size > INT_MAX) @@ -1382,11 +1405,31 @@ static NTSTATUS read_console( struct console *console, unsigned int ioctl, size_ return STATUS_SUCCESS; }
- console->edit_line.history_index = console->history_index; - console->edit_line.home_x = console->active->cursor_x; - console->edit_line.home_y = console->active->cursor_y; - console->edit_line.status = STATUS_PENDING; - if (edit_line_grow( console, 1 )) console->edit_line.buf[0] = 0; + ctx->history_index = console->history_index; + ctx->home_x = console->active->cursor_x; + ctx->home_y = console->active->cursor_y; + ctx->status = STATUS_PENDING; + if (keep_len && edit_line_grow( console, keep_len + 1 )) + { + unsigned offset = edit_line_string_width( keep, keep_len ); + if (offset > ctx->home_x) + { + int deltay; + offset -= ctx->home_x; + deltay = offset / console->active->width; + ctx->home_y = (deltay >= ctx->home_y) ? ctx->home_y - deltay : 0; + ctx->home_x = console->active->width - 1 - (offset % console->active->width); + } + else + ctx->home_x -= offset; + ctx->cursor = keep_len; + memcpy( ctx->buf, keep, keep_len * sizeof(WCHAR) ); + ctx->buf[keep_len] = 0; + ctx->len = keep_len; + ctx->end_offset = keep_len; + } + else if (edit_line_grow( console, 1 )) ctx->buf[0] = 0; + ctx->ctrl_mask = ctrl_mask;
console->pending_read = out_size; return process_console_input( console ); @@ -2477,13 +2520,25 @@ static NTSTATUS console_input_ioctl( struct console *console, unsigned int code, case IOCTL_CONDRV_READ_CONSOLE: if (in_size || *out_size % sizeof(WCHAR)) return STATUS_INVALID_PARAMETER; ensure_tty_input_thread( console ); - status = read_console( console, code, *out_size ); + status = read_console( console, code, *out_size, NULL, 0, 0 ); + *out_size = 0; + return status; + + case IOCTL_CONDRV_READ_COMPLETION: + if ((in_size < sizeof(DWORD)) || ((in_size - sizeof(DWORD)) % sizeof(WCHAR)) || + (*out_size % sizeof(WCHAR))) + return STATUS_INVALID_PARAMETER; + ensure_tty_input_thread( console ); + status = read_console( console, code, *out_size, + (const WCHAR*)((const char*)in_data + sizeof(DWORD)), + (in_size - sizeof(DWORD)) / sizeof(WCHAR), + *(DWORD*)in_data ); *out_size = 0; return status;
case IOCTL_CONDRV_READ_FILE: ensure_tty_input_thread( console ); - status = read_console( console, code, *out_size ); + status = read_console( console, code, *out_size, NULL, 0, 0 ); *out_size = 0; return status;
diff --git a/programs/conhost/conhost.h b/programs/conhost/conhost.h index 5e9b999380c..d150fd0acad 100644 --- a/programs/conhost/conhost.h +++ b/programs/conhost/conhost.h @@ -69,6 +69,7 @@ struct edit_line unsigned int end_offset; /* offset of the last written char */ unsigned int home_x; /* home position */ unsigned int home_y; + unsigned int ctrl_mask; /* mask for ctrl characters for completion */ };
struct console diff --git a/server/console.c b/server/console.c index 5407fba1411..609362e2d5d 100644 --- a/server/console.c +++ b/server/console.c @@ -931,6 +931,7 @@ static int is_blocking_read_ioctl( unsigned int code ) { case IOCTL_CONDRV_READ_INPUT: case IOCTL_CONDRV_READ_CONSOLE: + case IOCTL_CONDRV_READ_COMPLETION: case IOCTL_CONDRV_READ_FILE: return 1; default: