diff --git a/dlls/kernelbase/console.c b/dlls/kernelbase/console.c index 1f9eda845a1..1f23292d467 100644 --- a/dlls/kernelbase/console.c +++ b/dlls/kernelbase/console.c @@ -2021,8 +2021,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_CONSOLE_CONTROL, + 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 4515249a673..1000e62e765 100644 --- a/include/wine/condrv.h +++ b/include/wine/condrv.h @@ -44,6 +44,7 @@ #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_GET_PROCESS_LIST CTL_CODE(FILE_DEVICE_CONSOLE, 23, METHOD_BUFFERED, FILE_ANY_ACCESS) +#define IOCTL_CONDRV_READ_CONSOLE_CONTROL 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 ae1f4e35f41..b08d26c2641 100644 --- a/programs/conhost/conhost.c +++ b/programs/conhost/conhost.c @@ -492,8 +492,9 @@ 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_CONSOLE_CONTROL: 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_complete( console, STATUS_SUCCESS, console->read_buffer, out_size, console->record_count != 0 ); read_len = out_size / sizeof(WCHAR); break; case IOCTL_CONDRV_READ_FILE: @@ -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_CONSOLE_CONTROL: case IOCTL_CONDRV_READ_FILE: break; default: @@ -1284,6 +1290,19 @@ 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 (ctx->ctrl_mask && + ir.Event.KeyEvent.uChar.UnicodeChar && + ir.Event.KeyEvent.uChar.UnicodeChar < FIRST_NON_CONTROL_CHAR) + { + if (ctx->ctrl_mask & (1u << ir.Event.KeyEvent.uChar.UnicodeChar)) + { + ctrl_value = ir.Event.KeyEvent.uChar.UnicodeChar; + ctx->status = STATUS_SUCCESS; + TRACE("Found ctrl char in mask: ^%lc %x\n", ir.Event.KeyEvent.uChar.UnicodeChar + '@', ctx->ctrl_mask); + continue; + } + if (ir.Event.KeyEvent.uChar.UnicodeChar == 10) continue; + } func = NULL; for (map = console->edition_mode ? emacs_key_map : win32_key_map; map->entries != NULL; map++) { @@ -1326,7 +1345,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 +1355,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) { - 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 )); + edit_line_insert( console, &ctrl_value, 1 ); } + else + { + 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 +1391,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 *initial, unsigned int initial_len, unsigned int ctrl_mask ) { + struct edit_line *ctx = &console->edit_line; TRACE("\n"); if (out_size > INT_MAX) @@ -1382,11 +1410,37 @@ 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 (initial_len && edit_line_grow( console, initial_len + 1 )) + { + unsigned offset = edit_line_string_width( initial, initial_len ); + if (offset > ctx->home_x) + { + int deltay; + offset -= ctx->home_x; + deltay = offset / console->active->width; + if (ctx->home_y >= deltay) + ctx->home_y -= deltay; + else + { + ctx->home_y = 0; + FIXME("Support for negative ordinates is missing\n"); + } + ctx->home_x = console->active->width - 1 - (offset % console->active->width); + } + else + ctx->home_x -= offset; + ctx->cursor = initial_len; + memcpy( ctx->buf, initial, initial_len * sizeof(WCHAR) ); + ctx->buf[initial_len] = 0; + ctx->len = initial_len; + ctx->end_offset = initial_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 ); @@ -2502,13 +2556,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_CONSOLE_CONTROL: + 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 3cbd2ed2d80..e5f70aa3ca4 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 3fb7d8ab237..5f3f50d006f 100644 --- a/server/console.c +++ b/server/console.c @@ -952,6 +952,7 @@ static int is_blocking_read_ioctl( unsigned int code ) { case IOCTL_CONDRV_READ_INPUT: case IOCTL_CONDRV_READ_CONSOLE: + case IOCTL_CONDRV_READ_CONSOLE_CONTROL: case IOCTL_CONDRV_READ_FILE: return 1; default: