2013-07-06 19:47:53 +00:00
|
|
|
/*
|
|
|
|
* COPYRIGHT: See COPYING in the top level directory
|
|
|
|
* PROJECT: ReactOS Console Server DLL
|
2014-05-02 18:44:26 +00:00
|
|
|
* FILE: frontends/terminal.c
|
|
|
|
* PURPOSE: ConSrv terminal.
|
2013-07-06 19:47:53 +00:00
|
|
|
* PROGRAMMERS: Hermes Belusca-Maito (hermes.belusca@sfr.fr)
|
|
|
|
*/
|
|
|
|
|
|
|
|
/* INCLUDES *******************************************************************/
|
|
|
|
|
2014-02-04 19:54:42 +00:00
|
|
|
#include <consrv.h>
|
2013-07-06 19:47:53 +00:00
|
|
|
|
2014-05-03 01:59:28 +00:00
|
|
|
// #include "frontends/gui/guiterm.h"
|
2014-05-02 18:44:26 +00:00
|
|
|
#ifdef TUITERM_COMPILE
|
|
|
|
#include "frontends/tui/tuiterm.h"
|
|
|
|
#endif
|
|
|
|
|
|
|
|
#define NDEBUG
|
|
|
|
#include <debug.h>
|
|
|
|
|
2014-08-29 19:54:10 +00:00
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/********** HACK HACK HACK HACK HACK HACK HACK HACK HACK HACK HACK ************/
|
|
|
|
|
|
|
|
/* GLOBALS ********************************************************************/
|
|
|
|
|
|
|
|
/*
|
|
|
|
* From MSDN:
|
|
|
|
* "The lpMultiByteStr and lpWideCharStr pointers must not be the same.
|
|
|
|
* If they are the same, the function fails, and GetLastError returns
|
|
|
|
* ERROR_INVALID_PARAMETER."
|
|
|
|
*/
|
|
|
|
#define ConsoleInputUnicodeCharToAnsiChar(Console, dChar, sWChar) \
|
|
|
|
ASSERT((ULONG_PTR)dChar != (ULONG_PTR)sWChar); \
|
|
|
|
WideCharToMultiByte((Console)->InputCodePage, 0, (sWChar), 1, (dChar), 1, NULL, NULL)
|
|
|
|
|
|
|
|
#define ConsoleInputAnsiCharToUnicodeChar(Console, dWChar, sChar) \
|
|
|
|
ASSERT((ULONG_PTR)dWChar != (ULONG_PTR)sChar); \
|
|
|
|
MultiByteToWideChar((Console)->InputCodePage, 0, (sChar), 1, (dWChar), 1)
|
|
|
|
|
|
|
|
typedef struct ConsoleInput_t
|
|
|
|
{
|
|
|
|
LIST_ENTRY ListEntry;
|
|
|
|
INPUT_RECORD InputEvent;
|
|
|
|
} ConsoleInput;
|
|
|
|
|
|
|
|
|
|
|
|
/* PRIVATE FUNCTIONS **********************************************************/
|
|
|
|
|
|
|
|
#if 0
|
|
|
|
|
|
|
|
static VOID
|
|
|
|
ConioInputEventToAnsi(PCONSOLE 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 VOID
|
|
|
|
ConioInputEventToUnicode(PCONSOLE Console, PINPUT_RECORD InputEvent)
|
|
|
|
{
|
|
|
|
if (InputEvent->EventType == KEY_EVENT)
|
|
|
|
{
|
|
|
|
CHAR AsciiChar = InputEvent->Event.KeyEvent.uChar.AsciiChar;
|
|
|
|
InputEvent->Event.KeyEvent.uChar.AsciiChar = 0;
|
|
|
|
ConsoleInputAnsiCharToUnicodeChar(Console,
|
|
|
|
&InputEvent->Event.KeyEvent.uChar.UnicodeChar,
|
|
|
|
&AsciiChar);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
#endif
|
|
|
|
|
|
|
|
/********** HACK HACK HACK HACK HACK HACK HACK HACK HACK HACK HACK ************/
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
2014-05-02 18:44:26 +00:00
|
|
|
/* CONSRV TERMINAL FRONTENDS INTERFACE ****************************************/
|
|
|
|
|
|
|
|
/***************/
|
|
|
|
#ifdef TUITERM_COMPILE
|
|
|
|
NTSTATUS NTAPI
|
|
|
|
TuiLoadFrontEnd(IN OUT PFRONTEND FrontEnd,
|
|
|
|
IN OUT PCONSOLE_INFO ConsoleInfo,
|
|
|
|
IN OUT PVOID ExtraConsoleInfo,
|
|
|
|
IN ULONG ProcessId);
|
|
|
|
NTSTATUS NTAPI
|
|
|
|
TuiUnloadFrontEnd(IN OUT PFRONTEND FrontEnd);
|
|
|
|
#endif
|
|
|
|
|
|
|
|
NTSTATUS NTAPI
|
|
|
|
GuiLoadFrontEnd(IN OUT PFRONTEND FrontEnd,
|
|
|
|
IN OUT PCONSOLE_INFO ConsoleInfo,
|
|
|
|
IN OUT PVOID ExtraConsoleInfo,
|
|
|
|
IN ULONG ProcessId);
|
|
|
|
NTSTATUS NTAPI
|
|
|
|
GuiUnloadFrontEnd(IN OUT PFRONTEND FrontEnd);
|
|
|
|
/***************/
|
|
|
|
|
|
|
|
typedef
|
|
|
|
NTSTATUS (NTAPI *FRONTEND_LOAD)(IN OUT PFRONTEND FrontEnd,
|
|
|
|
IN OUT PCONSOLE_INFO ConsoleInfo,
|
|
|
|
IN OUT PVOID ExtraConsoleInfo,
|
|
|
|
IN ULONG ProcessId);
|
|
|
|
|
|
|
|
typedef
|
|
|
|
NTSTATUS (NTAPI *FRONTEND_UNLOAD)(IN OUT PFRONTEND FrontEnd);
|
|
|
|
|
|
|
|
/*
|
|
|
|
* If we are not in GUI-mode, start the text-mode terminal emulator.
|
|
|
|
* If we fail, try to start the GUI-mode terminal emulator.
|
|
|
|
*
|
|
|
|
* Try to open the GUI-mode terminal emulator. Two cases are possible:
|
|
|
|
* - We are in GUI-mode, therefore GuiMode == TRUE, the previous test-case
|
|
|
|
* failed and we start GUI-mode terminal emulator.
|
|
|
|
* - We are in text-mode, therefore GuiMode == FALSE, the previous test-case
|
|
|
|
* succeeded BUT we failed at starting text-mode terminal emulator.
|
|
|
|
* Then GuiMode was switched to TRUE in order to try to open the GUI-mode
|
|
|
|
* terminal emulator (Win32k will automatically switch to graphical mode,
|
|
|
|
* therefore no additional code is needed).
|
|
|
|
*/
|
|
|
|
|
|
|
|
/*
|
|
|
|
* NOTE: Each entry of the table should be retrieved when loading a front-end
|
|
|
|
* (examples of the CSR servers which register some data for CSRSS).
|
|
|
|
*/
|
|
|
|
struct
|
|
|
|
{
|
|
|
|
CHAR FrontEndName[80];
|
|
|
|
FRONTEND_LOAD FrontEndLoad;
|
|
|
|
FRONTEND_UNLOAD FrontEndUnload;
|
|
|
|
} FrontEndLoadingMethods[] =
|
|
|
|
{
|
|
|
|
#ifdef TUITERM_COMPILE
|
|
|
|
{"TUI", TuiLoadFrontEnd, TuiUnloadFrontEnd},
|
|
|
|
#endif
|
|
|
|
{"GUI", GuiLoadFrontEnd, GuiUnloadFrontEnd},
|
|
|
|
|
|
|
|
// {"Not found", 0, NULL}
|
|
|
|
};
|
|
|
|
|
2014-05-03 01:59:28 +00:00
|
|
|
static NTSTATUS
|
2014-05-02 18:44:26 +00:00
|
|
|
ConSrvLoadFrontEnd(IN OUT PFRONTEND FrontEnd,
|
|
|
|
IN OUT PCONSOLE_INFO ConsoleInfo,
|
|
|
|
IN OUT PVOID ExtraConsoleInfo,
|
|
|
|
IN ULONG ProcessId)
|
|
|
|
{
|
|
|
|
NTSTATUS Status = STATUS_SUCCESS;
|
|
|
|
ULONG i;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Choose an adequate terminal front-end to load, and load it
|
|
|
|
*/
|
|
|
|
for (i = 0; i < sizeof(FrontEndLoadingMethods) / sizeof(FrontEndLoadingMethods[0]); ++i)
|
|
|
|
{
|
|
|
|
DPRINT("CONSRV: Trying to load %s frontend...\n",
|
|
|
|
FrontEndLoadingMethods[i].FrontEndName);
|
|
|
|
Status = FrontEndLoadingMethods[i].FrontEndLoad(FrontEnd,
|
|
|
|
ConsoleInfo,
|
|
|
|
ExtraConsoleInfo,
|
|
|
|
ProcessId);
|
|
|
|
if (NT_SUCCESS(Status))
|
|
|
|
{
|
|
|
|
/* Save the unload callback */
|
|
|
|
FrontEnd->UnloadFrontEnd = FrontEndLoadingMethods[i].FrontEndUnload;
|
|
|
|
|
|
|
|
DPRINT("CONSRV: %s frontend loaded successfully\n",
|
|
|
|
FrontEndLoadingMethods[i].FrontEndName);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
DPRINT1("CONSRV: Loading %s frontend failed, Status = 0x%08lx , continuing...\n",
|
|
|
|
FrontEndLoadingMethods[i].FrontEndName, Status);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return Status;
|
|
|
|
}
|
|
|
|
|
2014-05-03 01:59:28 +00:00
|
|
|
static NTSTATUS
|
2014-05-02 18:44:26 +00:00
|
|
|
ConSrvUnloadFrontEnd(IN PFRONTEND FrontEnd)
|
|
|
|
{
|
|
|
|
if (FrontEnd == NULL) return STATUS_INVALID_PARAMETER;
|
|
|
|
// return FrontEnd->Vtbl->UnloadFrontEnd(FrontEnd);
|
|
|
|
return FrontEnd->UnloadFrontEnd(FrontEnd);
|
|
|
|
}
|
|
|
|
|
2014-05-03 01:59:28 +00:00
|
|
|
// See after...
|
|
|
|
static TERMINAL_VTBL ConSrvTermVtbl;
|
2014-05-02 18:44:26 +00:00
|
|
|
|
2014-05-03 01:59:28 +00:00
|
|
|
NTSTATUS NTAPI
|
|
|
|
ConSrvInitTerminal(IN OUT PTERMINAL Terminal,
|
|
|
|
IN OUT PCONSOLE_INFO ConsoleInfo,
|
|
|
|
IN OUT PVOID ExtraConsoleInfo,
|
|
|
|
IN ULONG ProcessId)
|
|
|
|
{
|
|
|
|
NTSTATUS Status;
|
|
|
|
PFRONTEND FrontEnd;
|
|
|
|
|
|
|
|
/* Load a suitable frontend for the ConSrv terminal */
|
|
|
|
FrontEnd = ConsoleAllocHeap(HEAP_ZERO_MEMORY, sizeof(*FrontEnd));
|
|
|
|
if (!FrontEnd) return STATUS_NO_MEMORY;
|
|
|
|
|
|
|
|
Status = ConSrvLoadFrontEnd(FrontEnd,
|
|
|
|
ConsoleInfo,
|
|
|
|
ExtraConsoleInfo,
|
|
|
|
ProcessId);
|
|
|
|
if (!NT_SUCCESS(Status))
|
|
|
|
{
|
|
|
|
DPRINT1("CONSRV: Failed to initialize a frontend, Status = 0x%08lx\n", Status);
|
|
|
|
ConsoleFreeHeap(FrontEnd);
|
|
|
|
return Status;
|
|
|
|
}
|
|
|
|
DPRINT("CONSRV: Frontend initialized\n");
|
2013-07-06 19:47:53 +00:00
|
|
|
|
2014-05-03 01:59:28 +00:00
|
|
|
/* Initialize the ConSrv terminal */
|
|
|
|
Terminal->Vtbl = &ConSrvTermVtbl;
|
|
|
|
// Terminal->Console will be initialized by ConDrvRegisterTerminal
|
|
|
|
Terminal->Data = FrontEnd; /* We store the frontend pointer in the terminal private data */
|
|
|
|
|
|
|
|
return STATUS_SUCCESS;
|
|
|
|
}
|
|
|
|
|
|
|
|
NTSTATUS NTAPI
|
|
|
|
ConSrvDeinitTerminal(IN OUT PTERMINAL Terminal)
|
|
|
|
{
|
|
|
|
NTSTATUS Status = STATUS_SUCCESS;
|
|
|
|
PFRONTEND FrontEnd = Terminal->Data;
|
|
|
|
|
|
|
|
/* Reset the ConSrv terminal */
|
|
|
|
Terminal->Data = NULL;
|
|
|
|
Terminal->Vtbl = NULL;
|
|
|
|
|
|
|
|
/* Unload the frontend */
|
|
|
|
if (FrontEnd != NULL)
|
|
|
|
{
|
|
|
|
Status = ConSrvUnloadFrontEnd(FrontEnd);
|
|
|
|
ConsoleFreeHeap(FrontEnd);
|
|
|
|
}
|
|
|
|
|
|
|
|
return Status;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/* CONSRV TERMINAL INTERFACE **************************************************/
|
2014-05-02 18:44:26 +00:00
|
|
|
|
2013-07-06 19:47:53 +00:00
|
|
|
static NTSTATUS NTAPI
|
2014-05-03 01:59:28 +00:00
|
|
|
ConSrvTermInitTerminal(IN OUT PTERMINAL This,
|
2013-07-06 19:47:53 +00:00
|
|
|
IN PCONSOLE Console)
|
|
|
|
{
|
2014-05-03 01:59:28 +00:00
|
|
|
NTSTATUS Status;
|
|
|
|
PFRONTEND FrontEnd = This->Data;
|
|
|
|
|
|
|
|
/* Initialize the console pointer for our frontend */
|
|
|
|
FrontEnd->Console = Console;
|
|
|
|
|
|
|
|
/** HACK HACK!! Copy FrontEnd into the console!! **/
|
|
|
|
DPRINT1("Using FrontEndIFace HACK(1), should be removed after proper implementation!\n");
|
|
|
|
Console->FrontEndIFace = *FrontEnd;
|
|
|
|
|
|
|
|
Status = FrontEnd->Vtbl->InitFrontEnd(FrontEnd, FrontEnd->Console);
|
|
|
|
|
|
|
|
/** HACK HACK!! Be sure FrontEndIFace is correctly updated in the console!! **/
|
|
|
|
DPRINT1("Using FrontEndIFace HACK(2), should be removed after proper implementation!\n");
|
|
|
|
Console->FrontEndIFace = *FrontEnd;
|
|
|
|
|
|
|
|
return Status;
|
2013-07-06 19:47:53 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
static VOID NTAPI
|
2014-05-03 01:59:28 +00:00
|
|
|
ConSrvTermDeinitTerminal(IN OUT PTERMINAL This)
|
2013-07-06 19:47:53 +00:00
|
|
|
{
|
2014-05-03 01:59:28 +00:00
|
|
|
PFRONTEND FrontEnd = This->Data;
|
|
|
|
FrontEnd->Vtbl->DeinitFrontEnd(FrontEnd);
|
2013-07-06 19:47:53 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
static VOID NTAPI
|
2014-05-03 01:59:28 +00:00
|
|
|
ConSrvTermDrawRegion(IN OUT PTERMINAL This,
|
2013-07-06 19:47:53 +00:00
|
|
|
SMALL_RECT* Region)
|
|
|
|
{
|
2014-05-03 01:59:28 +00:00
|
|
|
PFRONTEND FrontEnd = This->Data;
|
|
|
|
FrontEnd->Vtbl->DrawRegion(FrontEnd, Region);
|
2013-07-06 19:47:53 +00:00
|
|
|
}
|
|
|
|
|
2014-08-29 19:54:10 +00:00
|
|
|
|
|
|
|
|
|
|
|
/************ Line discipline ***************/
|
|
|
|
|
|
|
|
static NTSTATUS NTAPI
|
|
|
|
ConSrvTermReadStream(IN OUT PTERMINAL This,
|
|
|
|
/**/IN PUNICODE_STRING ExeName /**/OPTIONAL/**/,/**/
|
|
|
|
IN BOOLEAN Unicode,
|
|
|
|
/**PWCHAR Buffer,**/
|
|
|
|
OUT PVOID Buffer,
|
|
|
|
IN OUT PCONSOLE_READCONSOLE_CONTROL ReadControl,
|
|
|
|
IN ULONG NumCharsToRead,
|
|
|
|
OUT PULONG NumCharsRead OPTIONAL)
|
|
|
|
{
|
|
|
|
PFRONTEND FrontEnd = This->Data;
|
|
|
|
PCONSRV_CONSOLE Console = FrontEnd->Console;
|
|
|
|
PCONSOLE_INPUT_BUFFER InputBuffer = &Console->InputBuffer;
|
|
|
|
|
|
|
|
// STATUS_PENDING : Wait if more to read ; STATUS_SUCCESS : Don't wait.
|
|
|
|
NTSTATUS Status = STATUS_PENDING;
|
|
|
|
|
|
|
|
PLIST_ENTRY CurrentEntry;
|
|
|
|
ConsoleInput *Input;
|
|
|
|
ULONG i;
|
|
|
|
|
|
|
|
/* Validity checks */
|
|
|
|
// ASSERT(Console == InputBuffer->Header.Console);
|
|
|
|
ASSERT((Buffer != NULL) || (Buffer == NULL && NumCharsToRead == 0));
|
|
|
|
|
|
|
|
/* We haven't read anything (yet) */
|
|
|
|
i = ReadControl->nInitialChars;
|
|
|
|
|
|
|
|
if (InputBuffer->Mode & ENABLE_LINE_INPUT)
|
|
|
|
{
|
|
|
|
/* COOKED mode, call the line discipline */
|
|
|
|
|
|
|
|
if (Console->LineBuffer == NULL)
|
|
|
|
{
|
|
|
|
/* Starting a new line */
|
|
|
|
Console->LineMaxSize = max(256, NumCharsToRead);
|
|
|
|
|
|
|
|
Console->LineBuffer = ConsoleAllocHeap(0, Console->LineMaxSize * sizeof(WCHAR));
|
|
|
|
if (Console->LineBuffer == NULL) return STATUS_NO_MEMORY;
|
|
|
|
|
|
|
|
Console->LinePos = Console->LineSize = ReadControl->nInitialChars;
|
|
|
|
Console->LineComplete = Console->LineUpPressed = FALSE;
|
|
|
|
Console->LineInsertToggle = Console->InsertMode;
|
|
|
|
Console->LineWakeupMask = ReadControl->dwCtrlWakeupMask;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Pre-filling the buffer is only allowed in the Unicode API,
|
|
|
|
* so we don't need to worry about ANSI <-> Unicode 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(&InputBuffer->InputEvents))
|
|
|
|
{
|
|
|
|
/* Remove input event from queue */
|
|
|
|
CurrentEntry = RemoveHeadList(&InputBuffer->InputEvents);
|
|
|
|
if (IsListEmpty(&InputBuffer->InputEvents))
|
|
|
|
{
|
|
|
|
ResetEvent(InputBuffer->ActiveEvent);
|
|
|
|
}
|
|
|
|
Input = CONTAINING_RECORD(CurrentEntry, ConsoleInput, ListEntry);
|
|
|
|
|
|
|
|
/* Only pay attention to key down */
|
|
|
|
if (Input->InputEvent.EventType == KEY_EVENT &&
|
|
|
|
Input->InputEvent.Event.KeyEvent.bKeyDown)
|
|
|
|
{
|
|
|
|
LineInputKeyDown(Console, ExeName,
|
|
|
|
&Input->InputEvent.Event.KeyEvent);
|
|
|
|
ReadControl->dwControlKeyState = Input->InputEvent.Event.KeyEvent.dwControlKeyState;
|
|
|
|
}
|
|
|
|
ConsoleFreeHeap(Input);
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Check if we have a complete line to read from */
|
|
|
|
if (Console->LineComplete)
|
|
|
|
{
|
|
|
|
while (i < NumCharsToRead && Console->LinePos != Console->LineSize)
|
|
|
|
{
|
|
|
|
WCHAR Char = Console->LineBuffer[Console->LinePos++];
|
|
|
|
|
|
|
|
if (Unicode)
|
|
|
|
{
|
|
|
|
((PWCHAR)Buffer)[i] = Char;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
ConsoleInputUnicodeCharToAnsiChar(Console, &((PCHAR)Buffer)[i], &Char);
|
|
|
|
}
|
|
|
|
++i;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (Console->LinePos == Console->LineSize)
|
|
|
|
{
|
|
|
|
/* Entire line has been read */
|
|
|
|
ConsoleFreeHeap(Console->LineBuffer);
|
|
|
|
Console->LineBuffer = NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
Status = STATUS_SUCCESS;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
/* RAW mode */
|
|
|
|
|
|
|
|
/* Character input */
|
|
|
|
while (i < NumCharsToRead && !IsListEmpty(&InputBuffer->InputEvents))
|
|
|
|
{
|
|
|
|
/* Remove input event from queue */
|
|
|
|
CurrentEntry = RemoveHeadList(&InputBuffer->InputEvents);
|
|
|
|
if (IsListEmpty(&InputBuffer->InputEvents))
|
|
|
|
{
|
|
|
|
ResetEvent(InputBuffer->ActiveEvent);
|
|
|
|
}
|
|
|
|
Input = CONTAINING_RECORD(CurrentEntry, ConsoleInput, ListEntry);
|
|
|
|
|
|
|
|
/* Only pay attention to valid characters, on key down */
|
|
|
|
if (Input->InputEvent.EventType == KEY_EVENT &&
|
|
|
|
Input->InputEvent.Event.KeyEvent.bKeyDown &&
|
|
|
|
Input->InputEvent.Event.KeyEvent.uChar.UnicodeChar != L'\0')
|
|
|
|
{
|
|
|
|
WCHAR Char = Input->InputEvent.Event.KeyEvent.uChar.UnicodeChar;
|
|
|
|
|
|
|
|
if (Unicode)
|
|
|
|
{
|
|
|
|
((PWCHAR)Buffer)[i] = Char;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
ConsoleInputUnicodeCharToAnsiChar(Console, &((PCHAR)Buffer)[i], &Char);
|
|
|
|
}
|
|
|
|
++i;
|
|
|
|
|
|
|
|
/* Did read something */
|
|
|
|
Status = STATUS_SUCCESS;
|
|
|
|
}
|
|
|
|
ConsoleFreeHeap(Input);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// FIXME: Only set if Status == STATUS_SUCCESS ???
|
|
|
|
if (NumCharsRead) *NumCharsRead = i;
|
|
|
|
|
|
|
|
return Status;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/* GLOBALS ********************************************************************/
|
|
|
|
|
|
|
|
#define TAB_WIDTH 8
|
|
|
|
|
|
|
|
// See condrv/text.c
|
|
|
|
/*static*/ VOID
|
|
|
|
ClearLineBuffer(PTEXTMODE_SCREEN_BUFFER Buff);
|
|
|
|
|
|
|
|
static VOID
|
|
|
|
ConioNextLine(PTEXTMODE_SCREEN_BUFFER Buff, PSMALL_RECT UpdateRect, PUINT ScrolledLines)
|
|
|
|
{
|
|
|
|
/* If we hit bottom, slide the viewable screen */
|
|
|
|
if (++Buff->CursorPosition.Y == Buff->ScreenBufferSize.Y)
|
|
|
|
{
|
|
|
|
Buff->CursorPosition.Y--;
|
|
|
|
if (++Buff->VirtualY == Buff->ScreenBufferSize.Y)
|
|
|
|
{
|
|
|
|
Buff->VirtualY = 0;
|
|
|
|
}
|
|
|
|
(*ScrolledLines)++;
|
|
|
|
ClearLineBuffer(Buff);
|
|
|
|
if (UpdateRect->Top != 0)
|
|
|
|
{
|
|
|
|
UpdateRect->Top--;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
UpdateRect->Left = 0;
|
|
|
|
UpdateRect->Right = Buff->ScreenBufferSize.X - 1;
|
|
|
|
UpdateRect->Bottom = Buff->CursorPosition.Y;
|
|
|
|
}
|
|
|
|
|
|
|
|
static NTSTATUS
|
|
|
|
ConioWriteConsole(PFRONTEND FrontEnd,
|
|
|
|
PTEXTMODE_SCREEN_BUFFER Buff,
|
|
|
|
PWCHAR Buffer,
|
|
|
|
DWORD Length,
|
|
|
|
BOOL Attrib)
|
|
|
|
{
|
|
|
|
PCONSRV_CONSOLE Console = FrontEnd->Console;
|
|
|
|
|
|
|
|
UINT i;
|
|
|
|
PCHAR_INFO Ptr;
|
|
|
|
SMALL_RECT UpdateRect;
|
|
|
|
SHORT CursorStartX, CursorStartY;
|
|
|
|
UINT ScrolledLines;
|
|
|
|
|
|
|
|
CursorStartX = Buff->CursorPosition.X;
|
|
|
|
CursorStartY = Buff->CursorPosition.Y;
|
|
|
|
UpdateRect.Left = Buff->ScreenBufferSize.X;
|
|
|
|
UpdateRect.Top = Buff->CursorPosition.Y;
|
|
|
|
UpdateRect.Right = -1;
|
|
|
|
UpdateRect.Bottom = Buff->CursorPosition.Y;
|
|
|
|
ScrolledLines = 0;
|
|
|
|
|
|
|
|
for (i = 0; i < Length; i++)
|
|
|
|
{
|
|
|
|
/*
|
|
|
|
* If we are in processed mode, interpret special characters and
|
|
|
|
* display them correctly. Otherwise, just put them into the buffer.
|
|
|
|
*/
|
|
|
|
if (Buff->Mode & ENABLE_PROCESSED_OUTPUT)
|
|
|
|
{
|
|
|
|
/* --- CR --- */
|
|
|
|
if (Buffer[i] == L'\r')
|
|
|
|
{
|
|
|
|
Buff->CursorPosition.X = 0;
|
|
|
|
UpdateRect.Left = min(UpdateRect.Left, Buff->CursorPosition.X);
|
|
|
|
UpdateRect.Right = max(UpdateRect.Right, Buff->CursorPosition.X);
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
/* --- LF --- */
|
|
|
|
else if (Buffer[i] == L'\n')
|
|
|
|
{
|
|
|
|
Buff->CursorPosition.X = 0;
|
|
|
|
ConioNextLine(Buff, &UpdateRect, &ScrolledLines);
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
/* --- BS --- */
|
|
|
|
else if (Buffer[i] == L'\b')
|
|
|
|
{
|
|
|
|
/* Only handle BS if we're not on the first pos of the first line */
|
|
|
|
if (0 != Buff->CursorPosition.X || 0 != Buff->CursorPosition.Y)
|
|
|
|
{
|
|
|
|
if (0 == Buff->CursorPosition.X)
|
|
|
|
{
|
|
|
|
/* slide virtual position up */
|
|
|
|
Buff->CursorPosition.X = Buff->ScreenBufferSize.X - 1;
|
|
|
|
Buff->CursorPosition.Y--;
|
|
|
|
UpdateRect.Top = min(UpdateRect.Top, Buff->CursorPosition.Y);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
Buff->CursorPosition.X--;
|
|
|
|
}
|
|
|
|
Ptr = ConioCoordToPointer(Buff, Buff->CursorPosition.X, Buff->CursorPosition.Y);
|
|
|
|
Ptr->Char.UnicodeChar = L' ';
|
|
|
|
Ptr->Attributes = Buff->ScreenDefaultAttrib;
|
|
|
|
UpdateRect.Left = min(UpdateRect.Left, Buff->CursorPosition.X);
|
|
|
|
UpdateRect.Right = max(UpdateRect.Right, Buff->CursorPosition.X);
|
|
|
|
}
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
/* --- TAB --- */
|
|
|
|
else if (Buffer[i] == L'\t')
|
|
|
|
{
|
|
|
|
UINT EndX;
|
|
|
|
|
|
|
|
UpdateRect.Left = min(UpdateRect.Left, Buff->CursorPosition.X);
|
|
|
|
EndX = (Buff->CursorPosition.X + TAB_WIDTH) & ~(TAB_WIDTH - 1);
|
|
|
|
EndX = min(EndX, (UINT)Buff->ScreenBufferSize.X);
|
|
|
|
Ptr = ConioCoordToPointer(Buff, Buff->CursorPosition.X, Buff->CursorPosition.Y);
|
|
|
|
while (Buff->CursorPosition.X < EndX)
|
|
|
|
{
|
|
|
|
Ptr->Char.UnicodeChar = L' ';
|
|
|
|
Ptr->Attributes = Buff->ScreenDefaultAttrib;
|
|
|
|
++Ptr;
|
|
|
|
Buff->CursorPosition.X++;
|
|
|
|
}
|
|
|
|
UpdateRect.Right = max(UpdateRect.Right, Buff->CursorPosition.X - 1);
|
|
|
|
if (Buff->CursorPosition.X == Buff->ScreenBufferSize.X)
|
|
|
|
{
|
|
|
|
if (Buff->Mode & ENABLE_WRAP_AT_EOL_OUTPUT)
|
|
|
|
{
|
|
|
|
Buff->CursorPosition.X = 0;
|
|
|
|
ConioNextLine(Buff, &UpdateRect, &ScrolledLines);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
Buff->CursorPosition.X--;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
// /* --- BEL ---*/
|
|
|
|
// else if (Buffer[i] == L'\a')
|
|
|
|
// {
|
|
|
|
// // FIXME: This MUST BE moved to the terminal emulator frontend!!
|
|
|
|
// DPRINT1("Bell\n");
|
|
|
|
// // SendNotifyMessage(Console->hWindow, PM_CONSOLE_BEEP, 0, 0);
|
|
|
|
// continue;
|
|
|
|
// }
|
|
|
|
}
|
|
|
|
UpdateRect.Left = min(UpdateRect.Left, Buff->CursorPosition.X);
|
|
|
|
UpdateRect.Right = max(UpdateRect.Right, Buff->CursorPosition.X);
|
|
|
|
|
|
|
|
Ptr = ConioCoordToPointer(Buff, Buff->CursorPosition.X, Buff->CursorPosition.Y);
|
|
|
|
Ptr->Char.UnicodeChar = Buffer[i];
|
|
|
|
if (Attrib) Ptr->Attributes = Buff->ScreenDefaultAttrib;
|
|
|
|
|
|
|
|
Buff->CursorPosition.X++;
|
|
|
|
if (Buff->CursorPosition.X == Buff->ScreenBufferSize.X)
|
|
|
|
{
|
|
|
|
if (Buff->Mode & ENABLE_WRAP_AT_EOL_OUTPUT)
|
|
|
|
{
|
|
|
|
Buff->CursorPosition.X = 0;
|
|
|
|
ConioNextLine(Buff, &UpdateRect, &ScrolledLines);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
Buff->CursorPosition.X = CursorStartX;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!ConioIsRectEmpty(&UpdateRect) && (PCONSOLE_SCREEN_BUFFER)Buff == Console->ActiveBuffer)
|
|
|
|
{
|
|
|
|
// TermWriteStream(Console, &UpdateRect, CursorStartX, CursorStartY,
|
|
|
|
// ScrolledLines, Buffer, Length);
|
|
|
|
FrontEnd->Vtbl->WriteStream(FrontEnd,
|
|
|
|
&UpdateRect,
|
|
|
|
CursorStartX,
|
|
|
|
CursorStartY,
|
|
|
|
ScrolledLines,
|
|
|
|
Buffer,
|
|
|
|
Length);
|
|
|
|
}
|
|
|
|
|
|
|
|
return STATUS_SUCCESS;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
static NTSTATUS NTAPI
|
2014-05-03 01:59:28 +00:00
|
|
|
ConSrvTermWriteStream(IN OUT PTERMINAL This,
|
2014-08-29 19:54:10 +00:00
|
|
|
PTEXTMODE_SCREEN_BUFFER Buff,
|
|
|
|
PWCHAR Buffer,
|
|
|
|
DWORD Length,
|
|
|
|
BOOL Attrib)
|
2013-07-06 19:47:53 +00:00
|
|
|
{
|
2014-05-03 01:59:28 +00:00
|
|
|
PFRONTEND FrontEnd = This->Data;
|
2014-08-29 19:54:10 +00:00
|
|
|
return ConioWriteConsole(FrontEnd,
|
|
|
|
Buff,
|
|
|
|
Buffer,
|
|
|
|
Length,
|
|
|
|
Attrib);
|
2013-07-06 19:47:53 +00:00
|
|
|
}
|
|
|
|
|
2014-08-29 19:54:10 +00:00
|
|
|
/************ Line discipline ***************/
|
|
|
|
|
|
|
|
|
|
|
|
|
2013-07-06 19:47:53 +00:00
|
|
|
static BOOL NTAPI
|
2014-05-03 01:59:28 +00:00
|
|
|
ConSrvTermSetCursorInfo(IN OUT PTERMINAL This,
|
|
|
|
PCONSOLE_SCREEN_BUFFER ScreenBuffer)
|
2013-07-06 19:47:53 +00:00
|
|
|
{
|
2014-05-03 01:59:28 +00:00
|
|
|
PFRONTEND FrontEnd = This->Data;
|
|
|
|
return FrontEnd->Vtbl->SetCursorInfo(FrontEnd, ScreenBuffer);
|
2013-07-06 19:47:53 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
static BOOL NTAPI
|
2014-05-03 01:59:28 +00:00
|
|
|
ConSrvTermSetScreenInfo(IN OUT PTERMINAL This,
|
|
|
|
PCONSOLE_SCREEN_BUFFER ScreenBuffer,
|
2013-07-06 19:47:53 +00:00
|
|
|
SHORT OldCursorX,
|
|
|
|
SHORT OldCursorY)
|
|
|
|
{
|
2014-05-03 01:59:28 +00:00
|
|
|
PFRONTEND FrontEnd = This->Data;
|
|
|
|
return FrontEnd->Vtbl->SetScreenInfo(FrontEnd,
|
|
|
|
ScreenBuffer,
|
|
|
|
OldCursorX,
|
|
|
|
OldCursorY);
|
2013-07-06 19:47:53 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
static VOID NTAPI
|
2014-05-03 01:59:28 +00:00
|
|
|
ConSrvTermResizeTerminal(IN OUT PTERMINAL This)
|
2013-07-06 19:47:53 +00:00
|
|
|
{
|
2014-05-03 01:59:28 +00:00
|
|
|
PFRONTEND FrontEnd = This->Data;
|
|
|
|
FrontEnd->Vtbl->ResizeTerminal(FrontEnd);
|
2013-07-06 19:47:53 +00:00
|
|
|
}
|
|
|
|
|
2013-10-10 01:16:02 +00:00
|
|
|
static VOID NTAPI
|
2014-05-03 01:59:28 +00:00
|
|
|
ConSrvTermSetActiveScreenBuffer(IN OUT PTERMINAL This)
|
2013-10-10 01:16:02 +00:00
|
|
|
{
|
2014-05-03 01:59:28 +00:00
|
|
|
PFRONTEND FrontEnd = This->Data;
|
|
|
|
FrontEnd->Vtbl->SetActiveScreenBuffer(FrontEnd);
|
2013-10-10 01:16:02 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
static VOID NTAPI
|
2014-05-03 01:59:28 +00:00
|
|
|
ConSrvTermReleaseScreenBuffer(IN OUT PTERMINAL This,
|
2013-10-10 01:16:02 +00:00
|
|
|
IN PCONSOLE_SCREEN_BUFFER ScreenBuffer)
|
|
|
|
{
|
2014-05-03 01:59:28 +00:00
|
|
|
PFRONTEND FrontEnd = This->Data;
|
|
|
|
FrontEnd->Vtbl->ReleaseScreenBuffer(FrontEnd, ScreenBuffer);
|
2013-07-06 19:47:53 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
static VOID NTAPI
|
2014-05-03 01:59:28 +00:00
|
|
|
ConSrvTermChangeTitle(IN OUT PTERMINAL This)
|
2013-07-06 19:47:53 +00:00
|
|
|
{
|
2014-05-03 01:59:28 +00:00
|
|
|
PFRONTEND FrontEnd = This->Data;
|
|
|
|
FrontEnd->Vtbl->ChangeTitle(FrontEnd);
|
2013-07-06 19:47:53 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
static VOID NTAPI
|
2014-05-03 01:59:28 +00:00
|
|
|
ConSrvTermGetLargestConsoleWindowSize(IN OUT PTERMINAL This,
|
2013-07-06 19:47:53 +00:00
|
|
|
PCOORD pSize)
|
|
|
|
{
|
2014-05-03 01:59:28 +00:00
|
|
|
PFRONTEND FrontEnd = This->Data;
|
|
|
|
FrontEnd->Vtbl->GetLargestConsoleWindowSize(FrontEnd, pSize);
|
2013-07-06 19:47:53 +00:00
|
|
|
}
|
|
|
|
|
2014-05-03 01:59:28 +00:00
|
|
|
/*
|
2014-04-22 03:44:13 +00:00
|
|
|
static BOOL NTAPI
|
2014-05-03 01:59:28 +00:00
|
|
|
ConSrvTermGetSelectionInfo(IN OUT PTERMINAL This,
|
2014-04-22 03:44:13 +00:00
|
|
|
PCONSOLE_SELECTION_INFO pSelectionInfo)
|
|
|
|
{
|
2014-05-03 01:59:28 +00:00
|
|
|
PFRONTEND FrontEnd = This->Data;
|
|
|
|
return FrontEnd->Vtbl->GetSelectionInfo(FrontEnd, pSelectionInfo);
|
2014-04-22 03:44:13 +00:00
|
|
|
}
|
2014-05-03 01:59:28 +00:00
|
|
|
*/
|
2014-04-22 03:44:13 +00:00
|
|
|
|
2013-10-12 15:37:50 +00:00
|
|
|
static BOOL NTAPI
|
2014-05-03 01:59:28 +00:00
|
|
|
ConSrvTermSetPalette(IN OUT PTERMINAL This,
|
2013-10-12 15:37:50 +00:00
|
|
|
HPALETTE PaletteHandle,
|
|
|
|
UINT PaletteUsage)
|
|
|
|
{
|
2014-05-03 01:59:28 +00:00
|
|
|
PFRONTEND FrontEnd = This->Data;
|
|
|
|
return FrontEnd->Vtbl->SetPalette(FrontEnd, PaletteHandle, PaletteUsage);
|
2013-07-06 19:47:53 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
static INT NTAPI
|
2014-05-03 01:59:28 +00:00
|
|
|
ConSrvTermShowMouseCursor(IN OUT PTERMINAL This,
|
2013-07-06 19:47:53 +00:00
|
|
|
BOOL Show)
|
|
|
|
{
|
2014-05-03 01:59:28 +00:00
|
|
|
PFRONTEND FrontEnd = This->Data;
|
|
|
|
return FrontEnd->Vtbl->ShowMouseCursor(FrontEnd, Show);
|
2013-07-06 19:47:53 +00:00
|
|
|
}
|
|
|
|
|
2014-05-03 01:59:28 +00:00
|
|
|
static TERMINAL_VTBL ConSrvTermVtbl =
|
2013-07-06 19:47:53 +00:00
|
|
|
{
|
2014-05-03 01:59:28 +00:00
|
|
|
ConSrvTermInitTerminal,
|
|
|
|
ConSrvTermDeinitTerminal,
|
|
|
|
ConSrvTermDrawRegion,
|
2014-08-29 19:54:10 +00:00
|
|
|
|
|
|
|
ConSrvTermReadStream,
|
2014-05-03 01:59:28 +00:00
|
|
|
ConSrvTermWriteStream,
|
2014-08-29 19:54:10 +00:00
|
|
|
|
2014-05-03 01:59:28 +00:00
|
|
|
ConSrvTermSetCursorInfo,
|
|
|
|
ConSrvTermSetScreenInfo,
|
|
|
|
ConSrvTermResizeTerminal,
|
|
|
|
ConSrvTermSetActiveScreenBuffer,
|
|
|
|
ConSrvTermReleaseScreenBuffer,
|
|
|
|
ConSrvTermChangeTitle,
|
|
|
|
ConSrvTermGetLargestConsoleWindowSize,
|
|
|
|
// ConSrvTermGetSelectionInfo,
|
|
|
|
ConSrvTermSetPalette,
|
|
|
|
ConSrvTermShowMouseCursor,
|
2013-07-06 19:47:53 +00:00
|
|
|
};
|
|
|
|
|
2014-05-03 01:59:28 +00:00
|
|
|
#if 0
|
2013-07-06 19:47:53 +00:00
|
|
|
VOID
|
|
|
|
ResetFrontEnd(IN PCONSOLE Console)
|
|
|
|
{
|
|
|
|
if (!Console) return;
|
|
|
|
|
|
|
|
/* Reinitialize the frontend interface */
|
2014-05-02 16:46:13 +00:00
|
|
|
RtlZeroMemory(&Console->FrontEndIFace, sizeof(Console->FrontEndIFace));
|
2014-05-03 01:59:28 +00:00
|
|
|
Console->FrontEndIFace.Vtbl = &ConSrvTermVtbl;
|
2013-07-06 19:47:53 +00:00
|
|
|
}
|
2014-05-02 18:44:26 +00:00
|
|
|
#endif
|
|
|
|
|
2013-07-06 19:47:53 +00:00
|
|
|
/* EOF */
|