[PATCH v3 1/4] kernelbase/console, programs/conhost: support CONSOLE_READCONSOLE_CONTROL in ReadConsoleW

Eric Pouech eric.pouech at gmail.com
Tue Mar 1 02:25:56 CST 2022


Signed-off-by: Eric Pouech <eric.pouech at gmail.com>

---
 dlls/kernelbase/console.c  |   32 +++++++++++++-
 include/wine/condrv.h      |    1 
 programs/conhost/conhost.c |  104 ++++++++++++++++++++++++++++++++++++--------
 programs/conhost/conhost.h |    1 
 server/console.c           |    1 
 5 files changed, 118 insertions(+), 21 deletions(-)

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:




More information about the wine-devel mailing list