From 9f7057e9f25710d73db18dddbf7e658e04f5377c Mon Sep 17 00:00:00 2001 From: Jeffrey Morlan Date: Tue, 8 Jun 2010 06:38:14 +0000 Subject: [PATCH] [KERNEL32], [WIN32CSR] - Implement ReadConsoleW's CONSOLE_READCONSOLE_CONTROL structure. - Make pressing Insert during line input work svn path=/trunk/; revision=47693 --- reactos/dll/win32/kernel32/misc/console.c | 83 +++++++++---------- reactos/include/reactos/subsys/csrss/csrss.h | 9 +- .../win32/csrss/win32csr/coninput.c | 41 ++++++--- .../subsystems/win32/csrss/win32csr/conio.h | 3 + .../win32/csrss/win32csr/conoutput.c | 10 +++ .../win32/csrss/win32csr/guiconsole.c | 6 +- .../win32/csrss/win32csr/lineinput.c | 29 ++++++- .../win32/csrss/win32csr/tuiconsole.c | 7 +- 8 files changed, 120 insertions(+), 68 deletions(-) diff --git a/reactos/dll/win32/kernel32/misc/console.c b/reactos/dll/win32/kernel32/misc/console.c index 34b1e84a6aa..d1e8a97038e 100644 --- a/reactos/dll/win32/kernel32/misc/console.c +++ b/reactos/dll/win32/kernel32/misc/console.c @@ -1698,37 +1698,45 @@ IntReadConsole(HANDLE hConsoleInput, PVOID lpBuffer, DWORD nNumberOfCharsToRead, LPDWORD lpNumberOfCharsRead, - PCONSOLE_READCONSOLE_CONTROL lpReserved, + PCONSOLE_READCONSOLE_CONTROL pInputControl, BOOL bUnicode) { - PCSR_API_MESSAGE Request; - ULONG CsrRequest; - NTSTATUS Status; - ULONG CharSize, CharsRead = 0; + CSR_API_MESSAGE Request; + PCSR_CAPTURE_BUFFER CaptureBuffer; + ULONG CsrRequest = MAKE_CSR_API(READ_CONSOLE, CSR_CONSOLE); + NTSTATUS Status = STATUS_SUCCESS; + ULONG CharSize = (bUnicode ? sizeof(WCHAR) : sizeof(CHAR)); - CharSize = (bUnicode ? sizeof(WCHAR) : sizeof(CHAR)); - Request = RtlAllocateHeap(RtlGetProcessHeap(), - 0, - max(sizeof(CSR_API_MESSAGE), - CSR_API_MESSAGE_HEADER_SIZE(CSRSS_READ_CONSOLE) + min(nNumberOfCharsToRead, - CSRSS_MAX_READ_CONSOLE / CharSize) * CharSize)); - if (Request == NULL) + CaptureBuffer = CsrAllocateCaptureBuffer(1, nNumberOfCharsToRead * CharSize); + if (CaptureBuffer == NULL) { SetLastError(ERROR_NOT_ENOUGH_MEMORY); return FALSE; } - Request->Status = STATUS_SUCCESS; - Request->Data.ReadConsoleRequest.ConsoleHandle = hConsoleInput; - Request->Data.ReadConsoleRequest.Unicode = bUnicode; - Request->Data.ReadConsoleRequest.FullReadSize = (WORD)nNumberOfCharsToRead; - CsrRequest = MAKE_CSR_API(READ_CONSOLE, CSR_CONSOLE); + CsrAllocateMessagePointer(CaptureBuffer, + nNumberOfCharsToRead * CharSize, + &Request.Data.ReadConsoleRequest.Buffer); + + Request.Data.ReadConsoleRequest.ConsoleHandle = hConsoleInput; + Request.Data.ReadConsoleRequest.Unicode = bUnicode; + Request.Data.ReadConsoleRequest.NrCharactersToRead = (WORD)nNumberOfCharsToRead; + Request.Data.ReadConsoleRequest.NrCharactersRead = 0; + Request.Data.ReadConsoleRequest.CtrlWakeupMask = 0; + if (pInputControl && pInputControl->nLength == sizeof(CONSOLE_READCONSOLE_CONTROL)) + { + Request.Data.ReadConsoleRequest.NrCharactersRead = pInputControl->nInitialChars; + memcpy(Request.Data.ReadConsoleRequest.Buffer, + lpBuffer, + pInputControl->nInitialChars * sizeof(WCHAR)); + Request.Data.ReadConsoleRequest.CtrlWakeupMask = pInputControl->dwCtrlWakeupMask; + } do { - if (Request->Status == STATUS_PENDING) + if (Status == STATUS_PENDING) { - Status = NtWaitForSingleObject(Request->Data.ReadConsoleRequest.EventHandle, + Status = NtWaitForSingleObject(Request.Data.ReadConsoleRequest.EventHandle, FALSE, 0); if (!NT_SUCCESS(Status)) @@ -1738,37 +1746,28 @@ IntReadConsole(HANDLE hConsoleInput, } } - Request->Data.ReadConsoleRequest.NrCharactersToRead = (WORD)min(nNumberOfCharsToRead, CSRSS_MAX_READ_CONSOLE / CharSize); - - Status = CsrClientCallServer(Request, - NULL, - CsrRequest, - max(sizeof(CSR_API_MESSAGE), - CSR_API_MESSAGE_HEADER_SIZE(CSRSS_READ_CONSOLE) - + Request->Data.ReadConsoleRequest.NrCharactersToRead * CharSize)); - - if (!NT_SUCCESS(Status) || !NT_SUCCESS(Status = Request->Status)) + Status = CsrClientCallServer(&Request, CaptureBuffer, CsrRequest, sizeof(CSR_API_MESSAGE)); + if (!NT_SUCCESS(Status) || !NT_SUCCESS(Status = Request.Status)) { DPRINT1("CSR returned error in ReadConsole\n"); - RtlFreeHeap(RtlGetProcessHeap(), 0, Request); + CsrFreeCaptureBuffer(CaptureBuffer); SetLastErrorByStatus(Status); return FALSE; } - - nNumberOfCharsToRead -= Request->Data.ReadConsoleRequest.NrCharactersRead; - memcpy((PVOID)((ULONG_PTR)lpBuffer + (ULONG_PTR)(CharsRead * CharSize)), - Request->Data.ReadConsoleRequest.Buffer, - Request->Data.ReadConsoleRequest.NrCharactersRead * CharSize); - CharsRead += Request->Data.ReadConsoleRequest.NrCharactersRead; } - while (Request->Status == STATUS_PENDING && nNumberOfCharsToRead > 0); + while (Status == STATUS_PENDING); + + memcpy(lpBuffer, + Request.Data.ReadConsoleRequest.Buffer, + Request.Data.ReadConsoleRequest.NrCharactersRead * CharSize); if (lpNumberOfCharsRead != NULL) - { - *lpNumberOfCharsRead = CharsRead; - } + *lpNumberOfCharsRead = Request.Data.ReadConsoleRequest.NrCharactersRead; - RtlFreeHeap(RtlGetProcessHeap(), 0, Request); + if (pInputControl && pInputControl->nLength == sizeof(CONSOLE_READCONSOLE_CONTROL)) + pInputControl->dwControlKeyState = Request.Data.ReadConsoleRequest.ControlKeyState; + + CsrFreeCaptureBuffer(CaptureBuffer); return TRUE; } @@ -1791,7 +1790,7 @@ ReadConsoleA(HANDLE hConsoleInput, lpBuffer, nNumberOfCharsToRead, lpNumberOfCharsRead, - pInputControl, + NULL, FALSE); } diff --git a/reactos/include/reactos/subsys/csrss/csrss.h b/reactos/include/reactos/subsys/csrss/csrss.h index 1f8628ef712..1156d39bd49 100644 --- a/reactos/include/reactos/subsys/csrss/csrss.h +++ b/reactos/include/reactos/subsys/csrss/csrss.h @@ -70,11 +70,13 @@ typedef struct { HANDLE ConsoleHandle; BOOL Unicode; - WORD FullReadSize; WORD NrCharactersToRead; + WORD NrCharactersRead; HANDLE EventHandle; - ULONG NrCharactersRead; - BYTE Buffer[0]; + PVOID Buffer; + UNICODE_STRING ExeName; + DWORD CtrlWakeupMask; + DWORD ControlKeyState; } CSRSS_READ_CONSOLE, *PCSRSS_READ_CONSOLE; typedef struct @@ -515,7 +517,6 @@ typedef struct #define CSRSS_MAX_WRITE_CONSOLE (LPC_MAX_DATA_LENGTH - CSR_API_MESSAGE_HEADER_SIZE(CSRSS_WRITE_CONSOLE)) #define CSRSS_MAX_WRITE_CONSOLE_OUTPUT_CHAR (LPC_MAX_DATA_LENGTH - CSR_API_MESSAGE_HEADER_SIZE(CSRSS_WRITE_CONSOLE_OUTPUT_CHAR)) #define CSRSS_MAX_WRITE_CONSOLE_OUTPUT_ATTRIB (LPC_MAX_DATA_LENGTH - CSR_API_MESSAGE_HEADER_SIZE(CSRSS_WRITE_CONSOLE_OUTPUT_ATTRIB)) -#define CSRSS_MAX_READ_CONSOLE (LPC_MAX_DATA_LENGTH - CSR_API_MESSAGE_HEADER_SIZE(CSRSS_READ_CONSOLE)) #define CSRSS_MAX_READ_CONSOLE_OUTPUT_CHAR (LPC_MAX_DATA_LENGTH - CSR_API_MESSAGE_HEADER_SIZE(CSRSS_READ_CONSOLE_OUTPUT_CHAR)) #define CSRSS_MAX_READ_CONSOLE_OUTPUT_ATTRIB (LPC_MAX_DATA_LENGTH - CSR_API_MESSAGE_HEADER_SIZE(CSRSS_READ_CONSOLE_OUTPUT_ATTRIB)) diff --git a/reactos/subsystems/win32/csrss/win32csr/coninput.c b/reactos/subsystems/win32/csrss/win32csr/coninput.c index fa770aaade3..83aa6e7c71d 100644 --- a/reactos/subsystems/win32/csrss/win32csr/coninput.c +++ b/reactos/subsystems/win32/csrss/win32csr/coninput.c @@ -37,13 +37,18 @@ CSR_API(CsrReadConsole) CharSize = (Request->Data.ReadConsoleRequest.Unicode ? sizeof(WCHAR) : sizeof(CHAR)); - /* truncate length to CSRSS_MAX_READ_CONSOLE_REQUEST */ - nNumberOfCharsToRead = min(Request->Data.ReadConsoleRequest.NrCharactersToRead, CSRSS_MAX_READ_CONSOLE / CharSize); + nNumberOfCharsToRead = Request->Data.ReadConsoleRequest.NrCharactersToRead; Request->Header.u1.s1.TotalLength = sizeof(CSR_API_MESSAGE); Request->Header.u1.s1.DataLength = sizeof(CSR_API_MESSAGE) - sizeof(PORT_MESSAGE); Buffer = (PCHAR)Request->Data.ReadConsoleRequest.Buffer; UnicodeBuffer = (PWCHAR)Buffer; + if (!Win32CsrValidateBuffer(ProcessData, Buffer, nNumberOfCharsToRead, CharSize)) + return STATUS_ACCESS_VIOLATION; + + if (Request->Data.ReadConsoleRequest.NrCharactersRead * sizeof(WCHAR) > nNumberOfCharsToRead * CharSize) + return STATUS_INVALID_PARAMETER; + Status = ConioLockConsole(ProcessData, Request->Data.ReadConsoleRequest.ConsoleHandle, &Console, GENERIC_READ); if (! NT_SUCCESS(Status)) @@ -58,7 +63,7 @@ CSR_API(CsrReadConsole) if (Console->LineBuffer == NULL) { /* Starting a new line */ - Console->LineMaxSize = max(256, Request->Data.ReadConsoleRequest.FullReadSize); + Console->LineMaxSize = max(256, nNumberOfCharsToRead); Console->LineBuffer = HeapAlloc(Win32CsrApiHeap, 0, Console->LineMaxSize * sizeof(WCHAR)); if (Console->LineBuffer == NULL) { @@ -66,9 +71,19 @@ CSR_API(CsrReadConsole) goto done; } Console->LineComplete = FALSE; - Console->LineSize = 0; - Console->LinePos = 0; Console->LineUpPressed = FALSE; + Console->LineInsertToggle = 0; + Console->LineWakeupMask = Request->Data.ReadConsoleRequest.CtrlWakeupMask; + Console->LineSize = Request->Data.ReadConsoleRequest.NrCharactersRead; + Console->LinePos = Console->LineSize; + /* pre-filling the buffer is only allowed in the Unicode API, + * so we don't need to worry about conversion */ + memcpy(Console->LineBuffer, Buffer, Console->LineSize * sizeof(WCHAR)); + if (Console->LineSize == Console->LineMaxSize) + { + Console->LineComplete = TRUE; + Console->LinePos = 0; + } } /* If we don't have a complete line yet, process the pending input */ @@ -87,6 +102,7 @@ CSR_API(CsrReadConsole) && Input->InputEvent.Event.KeyEvent.bKeyDown) { LineInputKeyDown(Console, &Input->InputEvent.Event.KeyEvent); + Request->Data.ReadConsoleRequest.ControlKeyState = Input->InputEvent.Event.KeyEvent.dwControlKeyState; } HeapFree(Win32CsrApiHeap, 0, Input); } @@ -104,10 +120,11 @@ CSR_API(CsrReadConsole) } if (Console->LinePos == Console->LineSize) { + /* Entire line has been read */ HeapFree(Win32CsrApiHeap, 0, Console->LineBuffer); Console->LineBuffer = NULL; - Status = STATUS_SUCCESS; /* Entire line has been read */ } + Status = STATUS_SUCCESS; } } else @@ -142,12 +159,6 @@ done: Request->Data.ReadConsoleRequest.NrCharactersRead = i; ConioUnlockConsole(Console); - if (CSR_API_MESSAGE_HEADER_SIZE(CSRSS_READ_CONSOLE) + i * CharSize > sizeof(CSR_API_MESSAGE)) - { - Request->Header.u1.s1.TotalLength = CSR_API_MESSAGE_HEADER_SIZE(CSRSS_READ_CONSOLE) + i * CharSize; - Request->Header.u1.s1.DataLength = Request->Header.u1.s1.TotalLength - sizeof(PORT_MESSAGE); - } - return Status; } @@ -355,6 +366,12 @@ ConioProcessKey(MSG *msg, PCSRSS_CONSOLE Console, BOOL TextMode) current_entry = current_entry->Flink; ConioConsoleCtrlEvent((DWORD)CTRL_C_EVENT, current); } + if (Console->LineBuffer && !Console->LineComplete) + { + /* Line input is in progress; end it */ + Console->LinePos = Console->LineSize = 0; + Console->LineComplete = TRUE; + } return; } diff --git a/reactos/subsystems/win32/csrss/win32csr/conio.h b/reactos/subsystems/win32/csrss/win32csr/conio.h index bd66928bddd..40e3dd55ce7 100644 --- a/reactos/subsystems/win32/csrss/win32csr/conio.h +++ b/reactos/subsystems/win32/csrss/win32csr/conio.h @@ -81,6 +81,8 @@ typedef struct tagCSRSS_CONSOLE WORD LinePos; /* current position within line */ BOOLEAN LineComplete; /* user pressed enter, ready to send back to client */ BOOLEAN LineUpPressed; + BOOLEAN LineInsertToggle; /* replace character over cursor instead of inserting */ + ULONG LineWakeupMask; /* bitmap of which control characters will end line input */ LIST_ENTRY HistoryBuffers; WORD HistoryBufferSize; /* size for newly created history buffers */ WORD NumberOfHistoryBuffers; /* maximum number of history buffers allowed */ @@ -192,6 +194,7 @@ NTSTATUS FASTCALL ConioWriteConsole(PCSRSS_CONSOLE Console, PCSRSS_SCREEN_BUFFER CHAR *Buffer, DWORD Length, BOOL Attrib); NTSTATUS FASTCALL CsrInitConsoleScreenBuffer(PCSRSS_CONSOLE Console, PCSRSS_SCREEN_BUFFER Buffer); VOID WINAPI ConioDeleteScreenBuffer(PCSRSS_SCREEN_BUFFER Buffer); +DWORD FASTCALL ConioEffectiveCursorSize(PCSRSS_CONSOLE Console, DWORD Scale); CSR_API(CsrWriteConsole); CSR_API(CsrGetScreenBufferInfo); diff --git a/reactos/subsystems/win32/csrss/win32csr/conoutput.c b/reactos/subsystems/win32/csrss/win32csr/conoutput.c index 6b2a3a38358..e1b6215f7b2 100644 --- a/reactos/subsystems/win32/csrss/win32csr/conoutput.c +++ b/reactos/subsystems/win32/csrss/win32csr/conoutput.c @@ -865,6 +865,16 @@ CSR_API(CsrFillOutputAttrib) return STATUS_SUCCESS; } +DWORD FASTCALL +ConioEffectiveCursorSize(PCSRSS_CONSOLE Console, DWORD Scale) +{ + DWORD Size = (Console->ActiveBuffer->CursorInfo.dwSize * Scale + 99) / 100; + /* If line input in progress, perhaps adjust for insert toggle */ + if (Console->LineBuffer && !Console->LineComplete && Console->LineInsertToggle) + return (Size * 2 <= Scale) ? (Size * 2) : (Size / 2); + return Size; +} + CSR_API(CsrGetCursorInfo) { PCSRSS_SCREEN_BUFFER Buff; diff --git a/reactos/subsystems/win32/csrss/win32csr/guiconsole.c b/reactos/subsystems/win32/csrss/win32csr/guiconsole.c index d46d9cb4428..ca7388d6f8d 100644 --- a/reactos/subsystems/win32/csrss/win32csr/guiconsole.c +++ b/reactos/subsystems/win32/csrss/win32csr/guiconsole.c @@ -946,11 +946,7 @@ GuiConsolePaint(PCSRSS_CONSOLE Console, if (LeftChar <= CursorX && CursorX <= RightChar && TopLine <= CursorY && CursorY <= BottomLine) { - CursorHeight = (GuiData->CharHeight * Buff->CursorInfo.dwSize) / 100; - if (CursorHeight < 1) - { - CursorHeight = 1; - } + CursorHeight = ConioEffectiveCursorSize(Console, GuiData->CharHeight); From = ConioCoordToPointer(Buff, Buff->CurrentX, Buff->CurrentY) + 1; if (*From != DEFAULT_ATTRIB) diff --git a/reactos/subsystems/win32/csrss/win32csr/lineinput.c b/reactos/subsystems/win32/csrss/win32csr/lineinput.c index 957cb07cbfe..78cbe27cfb1 100644 --- a/reactos/subsystems/win32/csrss/win32csr/lineinput.c +++ b/reactos/subsystems/win32/csrss/win32csr/lineinput.c @@ -459,6 +459,11 @@ LineInputKeyDown(PCSRSS_CONSOLE Console, KEY_EVENT_RECORD *KeyEvent) LineInputEdit(Console, 0, 1, &Entry.Buffer[Pos]); } return; + case VK_INSERT: + /* Toggle between insert and overstrike */ + Console->LineInsertToggle = !Console->LineInsertToggle; + ConioSetCursorInfo(Console, Console->ActiveBuffer); + return; case VK_DELETE: /* Remove character to right of cursor */ if (Pos != Console->LineSize) @@ -555,7 +560,12 @@ LineInputKeyDown(PCSRSS_CONSOLE Console, KEY_EVENT_RECORD *KeyEvent) Console->LineBuffer[Console->LineSize++] = L'\r'; if (Console->Mode & ENABLE_ECHO_INPUT) ConioWriteConsole(Console, Console->ActiveBuffer, "\r", 1, TRUE); - if (Console->Mode & ENABLE_PROCESSED_INPUT) + + /* Add \n if processed input. There should usually be room for it, + * but an exception to the rule exists: the buffer could have been + * pre-filled with LineMaxSize - 1 characters. */ + if (Console->Mode & ENABLE_PROCESSED_INPUT && + Console->LineSize < Console->LineMaxSize) { Console->LineBuffer[Console->LineSize++] = L'\n'; if (Console->Mode & ENABLE_ECHO_INPUT) @@ -566,8 +576,21 @@ LineInputKeyDown(PCSRSS_CONSOLE Console, KEY_EVENT_RECORD *KeyEvent) } else if (KeyEvent->uChar.UnicodeChar != L'\0') { - /* Normal character */ - LineInputEdit(Console, 0, 1, &KeyEvent->uChar.UnicodeChar); + if (KeyEvent->uChar.UnicodeChar < 0x20 && + Console->LineWakeupMask & (1 << KeyEvent->uChar.UnicodeChar)) + { + /* Control key client wants to handle itself (e.g. for tab completion) */ + Console->LineBuffer[Console->LineSize++] = L' '; + Console->LineBuffer[Console->LinePos] = KeyEvent->uChar.UnicodeChar; + Console->LineComplete = TRUE; + Console->LinePos = 0; + } + else + { + /* Normal character */ + BOOL Overstrike = Console->LineInsertToggle && Console->LinePos != Console->LineSize; + LineInputEdit(Console, Overstrike, 1, &KeyEvent->uChar.UnicodeChar); + } } } diff --git a/reactos/subsystems/win32/csrss/win32csr/tuiconsole.c b/reactos/subsystems/win32/csrss/win32csr/tuiconsole.c index f078fe1e2a6..e767d48c202 100644 --- a/reactos/subsystems/win32/csrss/win32csr/tuiconsole.c +++ b/reactos/subsystems/win32/csrss/win32csr/tuiconsole.c @@ -208,6 +208,7 @@ TuiWriteStream(PCSRSS_CONSOLE Console, SMALL_RECT *Region, LONG CursorStartX, LO static BOOL WINAPI TuiSetCursorInfo(PCSRSS_CONSOLE Console, PCSRSS_SCREEN_BUFFER Buff) { + CONSOLE_CURSOR_INFO Info; DWORD BytesReturned; if (ActiveConsole->ActiveBuffer != Buff) @@ -215,9 +216,11 @@ TuiSetCursorInfo(PCSRSS_CONSOLE Console, PCSRSS_SCREEN_BUFFER Buff) return TRUE; } + Info.dwSize = ConioEffectiveCursorSize(Console, 100); + Info.bVisible = Buff->CursorInfo.bVisible; + if (! DeviceIoControl(ConsoleDeviceHandle, IOCTL_CONSOLE_SET_CURSOR_INFO, - &Buff->CursorInfo, sizeof(Buff->CursorInfo), NULL, 0, - &BytesReturned, NULL)) + &Info, sizeof(Info), NULL, 0, &BytesReturned, NULL)) { DPRINT1( "Failed to set cursor info\n" ); return FALSE;