reactos/win32ss/user/consrv/coninput.c
Hermès Bélusca-Maïto 608c1e0428 [CONSRV/WIN32CSR]
- Move all the console subsystem from win32csr to a new server dll called consrv.dll (instead of putting it directly to winsrv.dll). It's because I want to rework on this later on.
Many things must be done in order to achieve the moving (renaming functions, etc...). Tables & symbol names come from http://j00ru.vexillium.org/csrss_list/api_list.html as usual,
but here I took the Win 2k3 ones and I added few Win 7 functions which already existed in our codebase. The other table names are guessed based on what originally existed in csrsrv
and so on. Many stubs are also missing.
Cultural remark: consrv.dll originally existed in Beta releases of Windows NT 3.1 (e.g. the October 1991 one), but was soon merged with winsrv.dll and thus, in subsequent versions of
Windows, the console subsystem is also found in winsrv.dll. See http://www.logotypes.se/NT310CSRSS.txt and http://j00ru.vexillium.org/?p=349#comment-1883. One has to wait until
Windows 7 to see it moving again to conhost.exe.

[CSRSRV]
- Add an explanation comment on what the ValidTable member in the CSR_SERVER_DLL structure is for.

svn path=/branches/ros-csrss/; revision=57597
2012-10-22 23:55:51 +00:00

656 lines
21 KiB
C

/*
* reactos/win32ss/user/consrv/conio.c
*
* Console I/O functions
*
* ReactOS Operating System
*/
/* INCLUDES ******************************************************************/
#define NDEBUG
#include "w32csr.h"
#include <debug.h>
/* GLOBALS *******************************************************************/
#define ConsoleInputUnicodeCharToAnsiChar(Console, dChar, sWChar) \
WideCharToMultiByte((Console)->CodePage, 0, (sWChar), 1, (dChar), 1, NULL, NULL)
#define ConsoleInputAnsiCharToUnicodeChar(Console, dWChar, sChar) \
MultiByteToWideChar((Console)->CodePage, 0, (sChar), 1, (dWChar), 1)
/* FUNCTIONS *****************************************************************/
CSR_API(SrvReadConsole)
{
PLIST_ENTRY CurrentEntry;
ConsoleInput *Input;
PCHAR Buffer;
PWCHAR UnicodeBuffer;
ULONG i = 0;
ULONG nNumberOfCharsToRead, CharSize;
PCSR_PROCESS ProcessData = CsrGetClientThread()->Process;
PCSRSS_CONSOLE Console;
NTSTATUS Status;
DPRINT("SrvReadConsole\n");
CharSize = (ApiMessage->Data.ReadConsoleRequest.Unicode ? sizeof(WCHAR) : sizeof(CHAR));
nNumberOfCharsToRead = ApiMessage->Data.ReadConsoleRequest.NrCharactersToRead;
Buffer = (PCHAR)ApiMessage->Data.ReadConsoleRequest.Buffer;
UnicodeBuffer = (PWCHAR)Buffer;
if (!Win32CsrValidateBuffer(ProcessData, Buffer, nNumberOfCharsToRead, CharSize))
return STATUS_ACCESS_VIOLATION;
if (ApiMessage->Data.ReadConsoleRequest.NrCharactersRead * sizeof(WCHAR) > nNumberOfCharsToRead * CharSize)
return STATUS_INVALID_PARAMETER;
Status = ConioLockConsole(ProcessData, ApiMessage->Data.ReadConsoleRequest.ConsoleHandle,
&Console, GENERIC_READ);
if (! NT_SUCCESS(Status))
{
return Status;
}
ApiMessage->Data.ReadConsoleRequest.EventHandle = ProcessData->ConsoleEvent;
Status = STATUS_PENDING; /* we haven't read anything (yet) */
if (Console->Mode & ENABLE_LINE_INPUT)
{
if (Console->LineBuffer == NULL)
{
/* Starting a new line */
Console->LineMaxSize = max(256, nNumberOfCharsToRead);
Console->LineBuffer = HeapAlloc(ConSrvHeap, 0, Console->LineMaxSize * sizeof(WCHAR));
if (Console->LineBuffer == NULL)
{
Status = STATUS_NO_MEMORY;
goto done;
}
Console->LineComplete = FALSE;
Console->LineUpPressed = FALSE;
Console->LineInsertToggle = 0;
Console->LineWakeupMask = ApiMessage->Data.ReadConsoleRequest.CtrlWakeupMask;
Console->LineSize = ApiMessage->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 */
while (!Console->LineComplete && !IsListEmpty(&Console->InputEvents))
{
/* remove input event from queue */
CurrentEntry = RemoveHeadList(&Console->InputEvents);
if (IsListEmpty(&Console->InputEvents))
{
ResetEvent(Console->ActiveEvent);
}
Input = CONTAINING_RECORD(CurrentEntry, ConsoleInput, ListEntry);
/* only pay attention to key down */
if (KEY_EVENT == Input->InputEvent.EventType
&& Input->InputEvent.Event.KeyEvent.bKeyDown)
{
LineInputKeyDown(Console, &Input->InputEvent.Event.KeyEvent);
ApiMessage->Data.ReadConsoleRequest.ControlKeyState = Input->InputEvent.Event.KeyEvent.dwControlKeyState;
}
HeapFree(ConSrvHeap, 0, Input);
}
/* Check if we have a complete line to read from */
if (Console->LineComplete)
{
while (i < nNumberOfCharsToRead && Console->LinePos != Console->LineSize)
{
WCHAR Char = Console->LineBuffer[Console->LinePos++];
if (ApiMessage->Data.ReadConsoleRequest.Unicode)
UnicodeBuffer[i++] = Char;
else
ConsoleInputUnicodeCharToAnsiChar(Console, &Buffer[i++], &Char);
}
if (Console->LinePos == Console->LineSize)
{
/* Entire line has been read */
HeapFree(ConSrvHeap, 0, Console->LineBuffer);
Console->LineBuffer = NULL;
}
Status = STATUS_SUCCESS;
}
}
else
{
/* Character input */
while (i < nNumberOfCharsToRead && !IsListEmpty(&Console->InputEvents))
{
/* remove input event from queue */
CurrentEntry = RemoveHeadList(&Console->InputEvents);
if (IsListEmpty(&Console->InputEvents))
{
ResetEvent(Console->ActiveEvent);
}
Input = CONTAINING_RECORD(CurrentEntry, ConsoleInput, ListEntry);
/* only pay attention to valid ascii chars, on key down */
if (KEY_EVENT == Input->InputEvent.EventType
&& Input->InputEvent.Event.KeyEvent.bKeyDown
&& Input->InputEvent.Event.KeyEvent.uChar.UnicodeChar != L'\0')
{
WCHAR Char = Input->InputEvent.Event.KeyEvent.uChar.UnicodeChar;
if (ApiMessage->Data.ReadConsoleRequest.Unicode)
UnicodeBuffer[i++] = Char;
else
ConsoleInputUnicodeCharToAnsiChar(Console, &Buffer[i++], &Char);
Status = STATUS_SUCCESS; /* did read something */
}
HeapFree(ConSrvHeap, 0, Input);
}
}
done:
ApiMessage->Data.ReadConsoleRequest.NrCharactersRead = i;
ConioUnlockConsole(Console);
return Status;
}
static VOID FASTCALL
ConioInputEventToAnsi(PCSRSS_CONSOLE Console, PINPUT_RECORD InputEvent)
{
if (InputEvent->EventType == KEY_EVENT)
{
WCHAR UnicodeChar = InputEvent->Event.KeyEvent.uChar.UnicodeChar;
InputEvent->Event.KeyEvent.uChar.UnicodeChar = 0;
ConsoleInputUnicodeCharToAnsiChar(Console,
&InputEvent->Event.KeyEvent.uChar.AsciiChar,
&UnicodeChar);
}
}
static NTSTATUS FASTCALL
ConioProcessChar(PCSRSS_CONSOLE Console,
PINPUT_RECORD InputEvent)
{
ConsoleInput *ConInRec;
/* Check for pause or unpause */
if (InputEvent->EventType == KEY_EVENT && InputEvent->Event.KeyEvent.bKeyDown)
{
WORD vk = InputEvent->Event.KeyEvent.wVirtualKeyCode;
if (!(Console->PauseFlags & PAUSED_FROM_KEYBOARD))
{
DWORD cks = InputEvent->Event.KeyEvent.dwControlKeyState;
if (Console->Mode & ENABLE_LINE_INPUT &&
(vk == VK_PAUSE || (vk == 'S' &&
(cks & (LEFT_CTRL_PRESSED | RIGHT_CTRL_PRESSED)) &&
!(cks & (LEFT_ALT_PRESSED | RIGHT_ALT_PRESSED)))))
{
ConioPause(Console, PAUSED_FROM_KEYBOARD);
return STATUS_SUCCESS;
}
}
else
{
if ((vk < VK_SHIFT || vk > VK_CAPITAL) && vk != VK_LWIN &&
vk != VK_RWIN && vk != VK_NUMLOCK && vk != VK_SCROLL)
{
ConioUnpause(Console, PAUSED_FROM_KEYBOARD);
return STATUS_SUCCESS;
}
}
}
/* add event to the queue */
ConInRec = RtlAllocateHeap(ConSrvHeap, 0, sizeof(ConsoleInput));
if (ConInRec == NULL)
return STATUS_INSUFFICIENT_RESOURCES;
ConInRec->InputEvent = *InputEvent;
InsertTailList(&Console->InputEvents, &ConInRec->ListEntry);
SetEvent(Console->ActiveEvent);
return STATUS_SUCCESS;
}
static DWORD FASTCALL
ConioGetShiftState(PBYTE KeyState)
{
DWORD ssOut = 0;
if (KeyState[VK_CAPITAL] & 1)
ssOut |= CAPSLOCK_ON;
if (KeyState[VK_NUMLOCK] & 1)
ssOut |= NUMLOCK_ON;
if (KeyState[VK_SCROLL] & 1)
ssOut |= SCROLLLOCK_ON;
if (KeyState[VK_SHIFT] & 0x80)
ssOut |= SHIFT_PRESSED;
if (KeyState[VK_LCONTROL] & 0x80)
ssOut |= LEFT_CTRL_PRESSED;
if (KeyState[VK_RCONTROL] & 0x80)
ssOut |= RIGHT_CTRL_PRESSED;
if (KeyState[VK_LMENU] & 0x80)
ssOut |= LEFT_ALT_PRESSED;
if (KeyState[VK_RMENU] & 0x80)
ssOut |= RIGHT_ALT_PRESSED;
return ssOut;
}
VOID WINAPI
ConioProcessKey(MSG *msg, PCSRSS_CONSOLE Console, BOOL TextMode)
{
static BYTE KeyState[256] = { 0 };
/* MSDN mentions that you should use the last virtual key code received
* when putting a virtual key identity to a WM_CHAR message since multiple
* or translated keys may be involved. */
static UINT LastVirtualKey = 0;
DWORD ShiftState;
UINT RepeatCount;
WCHAR UnicodeChar;
UINT VirtualKeyCode;
UINT VirtualScanCode;
BOOL Down = FALSE;
INPUT_RECORD er;
BOOLEAN Fake; // synthesized, not a real event
BOOLEAN NotChar; // message should not be used to return a character
RepeatCount = 1;
VirtualScanCode = (msg->lParam >> 16) & 0xff;
Down = msg->message == WM_KEYDOWN || msg->message == WM_CHAR ||
msg->message == WM_SYSKEYDOWN || msg->message == WM_SYSCHAR;
GetKeyboardState(KeyState);
ShiftState = ConioGetShiftState(KeyState);
if (msg->message == WM_CHAR || msg->message == WM_SYSCHAR)
{
VirtualKeyCode = LastVirtualKey;
UnicodeChar = msg->wParam;
}
else
{
WCHAR Chars[2];
INT RetChars = 0;
VirtualKeyCode = msg->wParam;
RetChars = ToUnicodeEx(VirtualKeyCode,
VirtualScanCode,
KeyState,
Chars,
2,
0,
0);
UnicodeChar = (1 == RetChars ? Chars[0] : 0);
}
er.EventType = KEY_EVENT;
er.Event.KeyEvent.bKeyDown = Down;
er.Event.KeyEvent.wRepeatCount = RepeatCount;
er.Event.KeyEvent.uChar.UnicodeChar = UnicodeChar;
er.Event.KeyEvent.dwControlKeyState = ShiftState;
er.Event.KeyEvent.wVirtualKeyCode = VirtualKeyCode;
er.Event.KeyEvent.wVirtualScanCode = VirtualScanCode;
if (TextMode)
{
if (0 != (ShiftState & (RIGHT_ALT_PRESSED | LEFT_ALT_PRESSED))
&& VK_TAB == VirtualKeyCode)
{
if (Down)
{
TuiSwapConsole(ShiftState & SHIFT_PRESSED ? -1 : 1);
}
return;
}
else if (VK_MENU == VirtualKeyCode && ! Down)
{
if (TuiSwapConsole(0))
{
return;
}
}
}
else
{
if ((ShiftState & (RIGHT_ALT_PRESSED | LEFT_ALT_PRESSED) || KeyState[VK_MENU] & 0x80) &&
(VirtualKeyCode == VK_ESCAPE || VirtualKeyCode == VK_TAB || VirtualKeyCode == VK_SPACE))
{
DefWindowProcW( msg->hwnd, msg->message, msg->wParam, msg->lParam);
return;
}
}
if (NULL == Console)
{
DPRINT1("No Active Console!\n");
return;
}
Fake = UnicodeChar &&
(msg->message != WM_CHAR && msg->message != WM_SYSCHAR &&
msg->message != WM_KEYUP && msg->message != WM_SYSKEYUP);
NotChar = (msg->message != WM_CHAR && msg->message != WM_SYSCHAR);
if (NotChar)
LastVirtualKey = msg->wParam;
DPRINT ("csrss: %s %s %s %s %02x %02x '%lc' %04x\n",
Down ? "down" : "up ",
(msg->message == WM_CHAR || msg->message == WM_SYSCHAR) ?
"char" : "key ",
Fake ? "fake" : "real",
NotChar ? "notc" : "char",
VirtualScanCode,
VirtualKeyCode,
(UnicodeChar >= L' ') ? UnicodeChar : L'.',
ShiftState);
if (Fake)
return;
/* process Ctrl-C and Ctrl-Break */
if (Console->Mode & ENABLE_PROCESSED_INPUT &&
er.Event.KeyEvent.bKeyDown &&
((er.Event.KeyEvent.wVirtualKeyCode == VK_PAUSE) ||
(er.Event.KeyEvent.wVirtualKeyCode == 'C')) &&
(er.Event.KeyEvent.dwControlKeyState & (LEFT_CTRL_PRESSED | RIGHT_CTRL_PRESSED) || KeyState[VK_CONTROL] & 0x80))
{
PCSR_PROCESS current;
PLIST_ENTRY current_entry;
DPRINT1("Console_Api Ctrl-C\n");
current_entry = Console->ProcessList.Flink;
while (current_entry != &Console->ProcessList)
{
current = CONTAINING_RECORD(current_entry, CSR_PROCESS, ConsoleLink);
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;
}
if (0 != (er.Event.KeyEvent.dwControlKeyState
& (RIGHT_ALT_PRESSED | LEFT_ALT_PRESSED))
&& (VK_UP == er.Event.KeyEvent.wVirtualKeyCode
|| VK_DOWN == er.Event.KeyEvent.wVirtualKeyCode))
{
if (er.Event.KeyEvent.bKeyDown)
{
/* scroll up or down */
if (VK_UP == er.Event.KeyEvent.wVirtualKeyCode)
{
/* only scroll up if there is room to scroll up into */
if (Console->ActiveBuffer->CurrentY != Console->ActiveBuffer->MaxY - 1)
{
Console->ActiveBuffer->VirtualY = (Console->ActiveBuffer->VirtualY +
Console->ActiveBuffer->MaxY - 1) %
Console->ActiveBuffer->MaxY;
Console->ActiveBuffer->CurrentY++;
}
}
else
{
/* only scroll down if there is room to scroll down into */
if (Console->ActiveBuffer->CurrentY != 0)
{
Console->ActiveBuffer->VirtualY = (Console->ActiveBuffer->VirtualY + 1) %
Console->ActiveBuffer->MaxY;
Console->ActiveBuffer->CurrentY--;
}
}
ConioDrawConsole(Console);
}
return;
}
ConioProcessChar(Console, &er);
}
CSR_API(CsrReadInputEvent)
{
PLIST_ENTRY CurrentEntry;
PCSR_PROCESS ProcessData = CsrGetClientThread()->Process;
PCSRSS_CONSOLE Console;
NTSTATUS Status;
BOOLEAN Done = FALSE;
ConsoleInput *Input;
DPRINT("CsrReadInputEvent\n");
ApiMessage->Data.ReadInputRequest.Event = ProcessData->ConsoleEvent;
Status = ConioLockConsole(ProcessData, ApiMessage->Data.ReadInputRequest.ConsoleHandle, &Console, GENERIC_READ);
if (! NT_SUCCESS(Status))
{
return Status;
}
/* only get input if there is any */
CurrentEntry = Console->InputEvents.Flink;
while (CurrentEntry != &Console->InputEvents)
{
Input = CONTAINING_RECORD(CurrentEntry, ConsoleInput, ListEntry);
CurrentEntry = CurrentEntry->Flink;
if (Done)
{
ApiMessage->Data.ReadInputRequest.MoreEvents = TRUE;
break;
}
RemoveEntryList(&Input->ListEntry);
if (!Done)
{
ApiMessage->Data.ReadInputRequest.Input = Input->InputEvent;
if (ApiMessage->Data.ReadInputRequest.Unicode == FALSE)
{
ConioInputEventToAnsi(Console, &ApiMessage->Data.ReadInputRequest.Input);
}
Done = TRUE;
}
HeapFree(ConSrvHeap, 0, Input);
}
if (Done)
Status = STATUS_SUCCESS;
else
Status = STATUS_PENDING;
if (IsListEmpty(&Console->InputEvents))
{
ResetEvent(Console->ActiveEvent);
}
ConioUnlockConsole(Console);
return Status;
}
CSR_API(SrvFlushConsoleInputBuffer)
{
PLIST_ENTRY CurrentEntry;
PCSRSS_CONSOLE Console;
ConsoleInput* Input;
NTSTATUS Status;
DPRINT("SrvFlushConsoleInputBuffer\n");
Status = ConioLockConsole(CsrGetClientThread()->Process,
ApiMessage->Data.FlushInputBufferRequest.ConsoleInput,
&Console,
GENERIC_WRITE);
if(! NT_SUCCESS(Status))
{
return Status;
}
/* Discard all entries in the input event queue */
while (!IsListEmpty(&Console->InputEvents))
{
CurrentEntry = RemoveHeadList(&Console->InputEvents);
Input = CONTAINING_RECORD(CurrentEntry, ConsoleInput, ListEntry);
/* Destroy the event */
HeapFree(ConSrvHeap, 0, Input);
}
ResetEvent(Console->ActiveEvent);
ConioUnlockConsole(Console);
return STATUS_SUCCESS;
}
CSR_API(SrvGetConsoleNumberOfInputEvents)
{
NTSTATUS Status;
PCSRSS_CONSOLE Console;
PLIST_ENTRY CurrentItem;
DWORD NumEvents;
DPRINT("SrvGetConsoleNumberOfInputEvents\n");
Status = ConioLockConsole(CsrGetClientThread()->Process, ApiMessage->Data.GetNumInputEventsRequest.ConsoleHandle, &Console, GENERIC_READ);
if (! NT_SUCCESS(Status))
{
return Status;
}
CurrentItem = Console->InputEvents.Flink;
NumEvents = 0;
/* If there are any events ... */
while (CurrentItem != &Console->InputEvents)
{
CurrentItem = CurrentItem->Flink;
NumEvents++;
}
ConioUnlockConsole(Console);
ApiMessage->Data.GetNumInputEventsRequest.NumInputEvents = NumEvents;
return STATUS_SUCCESS;
}
CSR_API(SrvGetConsoleInput)
{
NTSTATUS Status;
PCSR_PROCESS ProcessData = CsrGetClientThread()->Process;
PCSRSS_CONSOLE Console;
DWORD Length;
PLIST_ENTRY CurrentItem;
PINPUT_RECORD InputRecord;
ConsoleInput* Item;
UINT NumItems;
DPRINT("SrvGetConsoleInput\n");
Status = ConioLockConsole(ProcessData, ApiMessage->Data.GetNumInputEventsRequest.ConsoleHandle, &Console, GENERIC_READ);
if(! NT_SUCCESS(Status))
{
return Status;
}
InputRecord = ApiMessage->Data.PeekConsoleInputRequest.InputRecord;
Length = ApiMessage->Data.PeekConsoleInputRequest.Length;
if (!Win32CsrValidateBuffer(ProcessData, InputRecord, Length, sizeof(INPUT_RECORD)))
{
ConioUnlockConsole(Console);
return STATUS_ACCESS_VIOLATION;
}
NumItems = 0;
if (! IsListEmpty(&Console->InputEvents))
{
CurrentItem = Console->InputEvents.Flink;
while (CurrentItem != &Console->InputEvents && NumItems < Length)
{
Item = CONTAINING_RECORD(CurrentItem, ConsoleInput, ListEntry);
++NumItems;
*InputRecord = Item->InputEvent;
if (ApiMessage->Data.PeekConsoleInputRequest.Unicode == FALSE)
{
ConioInputEventToAnsi(Console, InputRecord);
}
InputRecord++;
CurrentItem = CurrentItem->Flink;
}
}
ConioUnlockConsole(Console);
ApiMessage->Data.PeekConsoleInputRequest.Length = NumItems;
return STATUS_SUCCESS;
}
CSR_API(SrvWriteConsoleInput)
{
PINPUT_RECORD InputRecord;
PCSR_PROCESS ProcessData = CsrGetClientThread()->Process;
PCSRSS_CONSOLE Console;
NTSTATUS Status;
DWORD Length;
DWORD i;
DPRINT("SrvWriteConsoleInput\n");
Status = ConioLockConsole(ProcessData, ApiMessage->Data.WriteConsoleInputRequest.ConsoleHandle, &Console, GENERIC_WRITE);
if (! NT_SUCCESS(Status))
{
return Status;
}
InputRecord = ApiMessage->Data.WriteConsoleInputRequest.InputRecord;
Length = ApiMessage->Data.WriteConsoleInputRequest.Length;
if (!Win32CsrValidateBuffer(ProcessData, InputRecord, Length, sizeof(INPUT_RECORD)))
{
ConioUnlockConsole(Console);
return STATUS_ACCESS_VIOLATION;
}
for (i = 0; i < Length && NT_SUCCESS(Status); i++)
{
if (!ApiMessage->Data.WriteConsoleInputRequest.Unicode &&
InputRecord->EventType == KEY_EVENT)
{
CHAR AsciiChar = InputRecord->Event.KeyEvent.uChar.AsciiChar;
ConsoleInputAnsiCharToUnicodeChar(Console,
&InputRecord->Event.KeyEvent.uChar.UnicodeChar,
&AsciiChar);
}
Status = ConioProcessChar(Console, InputRecord++);
}
ConioUnlockConsole(Console);
ApiMessage->Data.WriteConsoleInputRequest.Length = i;
return Status;
}
/* EOF */