diff --git a/reactos/win32ss/user/winsrv/consrv_new/alias.c b/reactos/win32ss/user/winsrv/consrv_new/alias.c new file mode 100644 index 00000000000..d96ab0a872b --- /dev/null +++ b/reactos/win32ss/user/winsrv/consrv_new/alias.c @@ -0,0 +1,587 @@ +/* + * LICENSE: GPL - See COPYING in the top level directory + * PROJECT: ReactOS Console Server DLL + * FILE: win32ss/user/winsrv/consrv/alias.c + * PURPOSE: Alias support functions + * PROGRAMMERS: Christoph Wittich + * Johannes Anderwald + */ + +/* INCLUDES *******************************************************************/ + +#include "consrv.h" +#include "console.h" +#include "include/conio.h" + +#define NDEBUG +#include + + +/* TYPES **********************************************************************/ + +typedef struct _ALIAS_ENTRY +{ + LPCWSTR lpSource; + LPCWSTR lpTarget; + struct _ALIAS_ENTRY* Next; +} ALIAS_ENTRY, *PALIAS_ENTRY; + +typedef struct _ALIAS_HEADER +{ + LPCWSTR lpExeName; + PALIAS_ENTRY Data; + struct _ALIAS_HEADER* Next; +} ALIAS_HEADER, *PALIAS_HEADER; + + +/* PRIVATE FUNCTIONS **********************************************************/ + +static +PALIAS_HEADER +IntFindAliasHeader(PALIAS_HEADER RootHeader, LPCWSTR lpExeName) +{ + while (RootHeader) + { + INT diff = _wcsicmp(RootHeader->lpExeName, lpExeName); + if (!diff) return RootHeader; + if (diff > 0) break; + + RootHeader = RootHeader->Next; + } + return NULL; +} + +PALIAS_HEADER +IntCreateAliasHeader(LPCWSTR lpExeName) +{ + PALIAS_HEADER Entry; + UINT dwLength = wcslen(lpExeName) + 1; + + Entry = ConsoleAllocHeap(0, sizeof(ALIAS_HEADER) + sizeof(WCHAR) * dwLength); + if (!Entry) return Entry; + + Entry->lpExeName = (LPCWSTR)(Entry + 1); + wcscpy((PWCHAR)Entry->lpExeName, lpExeName); + Entry->Data = NULL; + Entry->Next = NULL; + return Entry; +} + +VOID +IntInsertAliasHeader(PALIAS_HEADER * RootHeader, PALIAS_HEADER NewHeader) +{ + PALIAS_HEADER CurrentHeader; + PALIAS_HEADER *LastLink = RootHeader; + + while ((CurrentHeader = *LastLink) != NULL) + { + INT Diff = _wcsicmp(NewHeader->lpExeName, CurrentHeader->lpExeName); + if (Diff < 0) break; + + LastLink = &CurrentHeader->Next; + } + + *LastLink = NewHeader; + NewHeader->Next = CurrentHeader; +} + +PALIAS_ENTRY +IntGetAliasEntry(PALIAS_HEADER Header, LPCWSTR lpSrcName) +{ + PALIAS_ENTRY RootHeader; + + if (Header == NULL) return NULL; + + RootHeader = Header->Data; + while (RootHeader) + { + INT diff; + DPRINT("IntGetAliasEntry->lpSource %S\n", RootHeader->lpSource); + diff = _wcsicmp(RootHeader->lpSource, lpSrcName); + if (!diff) return RootHeader; + if (diff > 0) break; + + RootHeader = RootHeader->Next; + } + return NULL; +} + +VOID +IntInsertAliasEntry(PALIAS_HEADER Header, PALIAS_ENTRY NewEntry) +{ + PALIAS_ENTRY CurrentEntry; + PALIAS_ENTRY *LastLink = &Header->Data; + + while ((CurrentEntry = *LastLink) != NULL) + { + INT Diff = _wcsicmp(NewEntry->lpSource, CurrentEntry->lpSource); + if (Diff < 0) break; + + LastLink = &CurrentEntry->Next; + } + + *LastLink = NewEntry; + NewEntry->Next = CurrentEntry; +} + +PALIAS_ENTRY +IntCreateAliasEntry(LPCWSTR lpSource, LPCWSTR lpTarget) +{ + UINT dwSource; + UINT dwTarget; + PALIAS_ENTRY Entry; + + dwSource = wcslen(lpSource) + 1; + dwTarget = wcslen(lpTarget) + 1; + + Entry = ConsoleAllocHeap(0, sizeof(ALIAS_ENTRY) + sizeof(WCHAR) * (dwSource + dwTarget)); + if (!Entry) return Entry; + + Entry->lpSource = (LPCWSTR)(Entry + 1); + wcscpy((LPWSTR)Entry->lpSource, lpSource); + Entry->lpTarget = Entry->lpSource + dwSource; + wcscpy((LPWSTR)Entry->lpTarget, lpTarget); + Entry->Next = NULL; + + return Entry; +} + +UINT +IntGetConsoleAliasesExesLength(PALIAS_HEADER RootHeader) +{ + UINT length = 0; + + while (RootHeader) + { + length += (wcslen(RootHeader->lpExeName) + 1) * sizeof(WCHAR); + RootHeader = RootHeader->Next; + } + if (length) + length += sizeof(WCHAR); // last entry entry is terminated with 2 zero bytes + + return length; +} + +UINT +IntGetConsoleAliasesExes(PALIAS_HEADER RootHeader, LPWSTR TargetBuffer, UINT TargetBufferSize) +{ + UINT Offset = 0; + UINT Length; + + TargetBufferSize /= sizeof(WCHAR); + while (RootHeader) + { + Length = wcslen(RootHeader->lpExeName) + 1; + if (TargetBufferSize > Offset + Length) + { + wcscpy(&TargetBuffer[Offset], RootHeader->lpExeName); + Offset += Length; + } + else + { + break; + } + RootHeader = RootHeader->Next; + } + Length = min(Offset+1, TargetBufferSize); + TargetBuffer[Length] = L'\0'; + return Length * sizeof(WCHAR); +} + +UINT +IntGetAllConsoleAliasesLength(PALIAS_HEADER Header) +{ + UINT Length = 0; + PALIAS_ENTRY CurEntry = Header->Data; + + while (CurEntry) + { + Length += wcslen(CurEntry->lpSource); + Length += wcslen(CurEntry->lpTarget); + Length += 2; // zero byte and '=' + CurEntry = CurEntry->Next; + } + + if (Length) + { + return (Length+1) * sizeof(WCHAR); + } + return 0; +} + +UINT +IntGetAllConsoleAliases(PALIAS_HEADER Header, LPWSTR TargetBuffer, UINT TargetBufferLength) +{ + PALIAS_ENTRY CurEntry = Header->Data; + UINT Offset = 0; + UINT SrcLength, TargetLength; + + TargetBufferLength /= sizeof(WCHAR); + while (CurEntry) + { + SrcLength = wcslen(CurEntry->lpSource) + 1; + TargetLength = wcslen(CurEntry->lpTarget) + 1; + if (Offset + TargetLength + SrcLength >= TargetBufferLength) + break; + + wcscpy(&TargetBuffer[Offset], CurEntry->lpSource); + Offset += SrcLength; + TargetBuffer[Offset] = L'='; + wcscpy(&TargetBuffer[Offset], CurEntry->lpTarget); + Offset += TargetLength; + + CurEntry = CurEntry->Next; + } + TargetBuffer[Offset] = L'\0'; + return Offset * sizeof(WCHAR); +} + +VOID +IntDeleteAliasEntry(PALIAS_HEADER Header, PALIAS_ENTRY Entry) +{ + PALIAS_ENTRY *LastLink = &Header->Data; + PALIAS_ENTRY CurEntry; + + while ((CurEntry = *LastLink) != NULL) + { + if (CurEntry == Entry) + { + *LastLink = Entry->Next; + ConsoleFreeHeap(Entry); + return; + } + LastLink = &CurEntry->Next; + } +} + +VOID +IntDeleteAllAliases(PCONSOLE Console) +{ + PALIAS_HEADER Header, NextHeader; + PALIAS_ENTRY Entry, NextEntry; + + for (Header = Console->Aliases; Header; Header = NextHeader) + { + NextHeader = Header->Next; + for (Entry = Header->Data; Entry; Entry = NextEntry) + { + NextEntry = Entry->Next; + ConsoleFreeHeap(Entry); + } + ConsoleFreeHeap(Header); + } +} + + +/* PUBLIC SERVER APIS *********************************************************/ + +CSR_API(SrvAddConsoleAlias) +{ + PCONSOLE_ADDGETALIAS ConsoleAliasRequest = &((PCONSOLE_API_MESSAGE)ApiMessage)->Data.ConsoleAliasRequest; + PCONSOLE Console; + PALIAS_HEADER Header; + PALIAS_ENTRY Entry; + LPWSTR lpSource, lpTarget, lpExeName; + + DPRINT("SrvAddConsoleAlias entered ApiMessage %p\n", ApiMessage); + + if ( !CsrValidateMessageBuffer(ApiMessage, + (PVOID*)&ConsoleAliasRequest->Source, + ConsoleAliasRequest->SourceLength, + sizeof(BYTE)) || + !CsrValidateMessageBuffer(ApiMessage, + (PVOID*)&ConsoleAliasRequest->Target, + ConsoleAliasRequest->TargetLength, + sizeof(BYTE)) || + !CsrValidateMessageBuffer(ApiMessage, + (PVOID*)&ConsoleAliasRequest->Exe, + ConsoleAliasRequest->ExeLength, + sizeof(BYTE)) ) + { + return STATUS_INVALID_PARAMETER; + } + + lpSource = ConsoleAliasRequest->Source; + lpTarget = (ConsoleAliasRequest->TargetLength != 0 ? ConsoleAliasRequest->Target : NULL); + lpExeName = ConsoleAliasRequest->Exe; + + DPRINT("SrvAddConsoleAlias lpSource %p lpExeName %p lpTarget %p\n", lpSource, lpExeName, lpTarget); + + if (lpExeName == NULL || lpSource == NULL) + { + return STATUS_INVALID_PARAMETER; + } + + ApiMessage->Status = ConSrvGetConsole(ConsoleGetPerProcessData(CsrGetClientThread()->Process), &Console, TRUE); + if (!NT_SUCCESS(ApiMessage->Status)) + { + return ApiMessage->Status; + } + + Header = IntFindAliasHeader(Console->Aliases, lpExeName); + if (!Header && lpTarget != NULL) + { + Header = IntCreateAliasHeader(lpExeName); + if (!Header) + { + ConSrvReleaseConsole(Console, TRUE); + return STATUS_INSUFFICIENT_RESOURCES; + } + IntInsertAliasHeader(&Console->Aliases, Header); + } + + if (lpTarget == NULL) // Delete the entry + { + Entry = IntGetAliasEntry(Header, lpSource); + if (Entry) + { + IntDeleteAliasEntry(Header, Entry); + ApiMessage->Status = STATUS_SUCCESS; + } + else + { + ApiMessage->Status = STATUS_INVALID_PARAMETER; + } + ConSrvReleaseConsole(Console, TRUE); + return ApiMessage->Status; + } + + Entry = IntCreateAliasEntry(lpSource, lpTarget); + + if (!Entry) + { + ConSrvReleaseConsole(Console, TRUE); + return STATUS_INSUFFICIENT_RESOURCES; + } + + IntInsertAliasEntry(Header, Entry); + ConSrvReleaseConsole(Console, TRUE); + return STATUS_SUCCESS; +} + +CSR_API(SrvGetConsoleAlias) +{ + PCONSOLE_ADDGETALIAS ConsoleAliasRequest = &((PCONSOLE_API_MESSAGE)ApiMessage)->Data.ConsoleAliasRequest; + PCONSOLE Console; + PALIAS_HEADER Header; + PALIAS_ENTRY Entry; + UINT Length; + LPWSTR lpSource, lpTarget, lpExeName; + + DPRINT("SrvGetConsoleAlias entered ApiMessage %p\n", ApiMessage); + + if ( !CsrValidateMessageBuffer(ApiMessage, + (PVOID*)&ConsoleAliasRequest->Source, + ConsoleAliasRequest->SourceLength, + sizeof(BYTE)) || + !CsrValidateMessageBuffer(ApiMessage, + (PVOID*)&ConsoleAliasRequest->Target, + ConsoleAliasRequest->TargetLength, + sizeof(BYTE)) || + !CsrValidateMessageBuffer(ApiMessage, + (PVOID*)&ConsoleAliasRequest->Exe, + ConsoleAliasRequest->ExeLength, + sizeof(BYTE)) ) + { + return STATUS_INVALID_PARAMETER; + } + + lpSource = ConsoleAliasRequest->Source; + lpTarget = ConsoleAliasRequest->Target; + lpExeName = ConsoleAliasRequest->Exe; + + DPRINT("SrvGetConsoleAlias lpExeName %p lpSource %p TargetBuffer %p TargetLength %u\n", + lpExeName, lpSource, lpTarget, ConsoleAliasRequest->TargetLength); + + if (ConsoleAliasRequest->ExeLength == 0 || lpTarget == NULL || + ConsoleAliasRequest->TargetLength == 0 || ConsoleAliasRequest->SourceLength == 0) + { + return STATUS_INVALID_PARAMETER; + } + + ApiMessage->Status = ConSrvGetConsole(ConsoleGetPerProcessData(CsrGetClientThread()->Process), &Console, TRUE); + if (!NT_SUCCESS(ApiMessage->Status)) + { + return ApiMessage->Status; + } + + Header = IntFindAliasHeader(Console->Aliases, lpExeName); + if (!Header) + { + ConSrvReleaseConsole(Console, TRUE); + return STATUS_INVALID_PARAMETER; + } + + Entry = IntGetAliasEntry(Header, lpSource); + if (!Entry) + { + ConSrvReleaseConsole(Console, TRUE); + return STATUS_INVALID_PARAMETER; + } + + Length = (wcslen(Entry->lpTarget) + 1) * sizeof(WCHAR); + if (Length > ConsoleAliasRequest->TargetLength) + { + ConSrvReleaseConsole(Console, TRUE); + return STATUS_BUFFER_TOO_SMALL; + } + + wcscpy(lpTarget, Entry->lpTarget); + ConsoleAliasRequest->TargetLength = Length; + ConSrvReleaseConsole(Console, TRUE); + return STATUS_SUCCESS; +} + +CSR_API(SrvGetConsoleAliases) +{ + PCONSOLE_GETALLALIASES GetAllAliasesRequest = &((PCONSOLE_API_MESSAGE)ApiMessage)->Data.GetAllAliasesRequest; + PCONSOLE Console; + ULONG BytesWritten; + PALIAS_HEADER Header; + + if ( !CsrValidateMessageBuffer(ApiMessage, + (PVOID)&GetAllAliasesRequest->ExeName, + GetAllAliasesRequest->ExeLength, + sizeof(BYTE)) || + !CsrValidateMessageBuffer(ApiMessage, + (PVOID)&GetAllAliasesRequest->AliasesBuffer, + GetAllAliasesRequest->AliasesBufferLength, + sizeof(BYTE)) ) + { + return STATUS_INVALID_PARAMETER; + } + + if (GetAllAliasesRequest->ExeName == NULL) + { + return STATUS_INVALID_PARAMETER; + } + + ApiMessage->Status = ConSrvGetConsole(ConsoleGetPerProcessData(CsrGetClientThread()->Process), &Console, TRUE); + if (!NT_SUCCESS(ApiMessage->Status)) + { + return ApiMessage->Status; + } + + Header = IntFindAliasHeader(Console->Aliases, GetAllAliasesRequest->ExeName); + if (!Header) + { + ConSrvReleaseConsole(Console, TRUE); + return STATUS_INVALID_PARAMETER; + } + + if (IntGetAllConsoleAliasesLength(Header) > GetAllAliasesRequest->AliasesBufferLength) + { + ConSrvReleaseConsole(Console, TRUE); + return STATUS_BUFFER_OVERFLOW; + } + + BytesWritten = IntGetAllConsoleAliases(Header, + GetAllAliasesRequest->AliasesBuffer, + GetAllAliasesRequest->AliasesBufferLength); + + GetAllAliasesRequest->AliasesBufferLength = BytesWritten; + ConSrvReleaseConsole(Console, TRUE); + return STATUS_SUCCESS; +} + +CSR_API(SrvGetConsoleAliasesLength) +{ + PCONSOLE_GETALLALIASESLENGTH GetAllAliasesLengthRequest = &((PCONSOLE_API_MESSAGE)ApiMessage)->Data.GetAllAliasesLengthRequest; + PCONSOLE Console; + PALIAS_HEADER Header; + UINT Length; + + if (!CsrValidateMessageBuffer(ApiMessage, + (PVOID)&GetAllAliasesLengthRequest->ExeName, + GetAllAliasesLengthRequest->ExeLength, + sizeof(BYTE))) + { + return STATUS_INVALID_PARAMETER; + } + + if (GetAllAliasesLengthRequest->ExeName == NULL) + { + return STATUS_INVALID_PARAMETER; + } + + ApiMessage->Status = ConSrvGetConsole(ConsoleGetPerProcessData(CsrGetClientThread()->Process), &Console, TRUE); + if (!NT_SUCCESS(ApiMessage->Status)) + { + return ApiMessage->Status; + } + + Header = IntFindAliasHeader(Console->Aliases, GetAllAliasesLengthRequest->ExeName); + if (!Header) + { + ConSrvReleaseConsole(Console, TRUE); + return STATUS_INVALID_PARAMETER; + } + + Length = IntGetAllConsoleAliasesLength(Header); + GetAllAliasesLengthRequest->Length = Length; + ConSrvReleaseConsole(Console, TRUE); + return STATUS_SUCCESS; +} + +CSR_API(SrvGetConsoleAliasExes) +{ + PCONSOLE_GETALIASESEXES GetAliasesExesRequest = &((PCONSOLE_API_MESSAGE)ApiMessage)->Data.GetAliasesExesRequest; + PCONSOLE Console; + UINT BytesWritten; + UINT ExesLength; + + DPRINT("SrvGetConsoleAliasExes entered\n"); + + if (!CsrValidateMessageBuffer(ApiMessage, + (PVOID)&GetAliasesExesRequest->ExeNames, + GetAliasesExesRequest->Length, + sizeof(BYTE))) + { + return STATUS_INVALID_PARAMETER; + } + + ApiMessage->Status = ConSrvGetConsole(ConsoleGetPerProcessData(CsrGetClientThread()->Process), &Console, TRUE); + if (!NT_SUCCESS(ApiMessage->Status)) + { + return ApiMessage->Status; + } + + ExesLength = IntGetConsoleAliasesExesLength(Console->Aliases); + + if (ExesLength > GetAliasesExesRequest->Length) + { + ConSrvReleaseConsole(Console, TRUE); + return STATUS_BUFFER_OVERFLOW; + } + + if (GetAliasesExesRequest->ExeNames == NULL) + { + ConSrvReleaseConsole(Console, TRUE); + return STATUS_INVALID_PARAMETER; + } + + BytesWritten = IntGetConsoleAliasesExes(Console->Aliases, + GetAliasesExesRequest->ExeNames, + GetAliasesExesRequest->Length); + + GetAliasesExesRequest->Length = BytesWritten; + ConSrvReleaseConsole(Console, TRUE); + return STATUS_SUCCESS; +} + +CSR_API(SrvGetConsoleAliasExesLength) +{ + PCONSOLE_GETALIASESEXESLENGTH GetAliasesExesLengthRequest = &((PCONSOLE_API_MESSAGE)ApiMessage)->Data.GetAliasesExesLengthRequest; + PCONSOLE Console; + DPRINT("SrvGetConsoleAliasExesLength entered\n"); + + ApiMessage->Status = ConSrvGetConsole(ConsoleGetPerProcessData(CsrGetClientThread()->Process), &Console, TRUE); + if (NT_SUCCESS(ApiMessage->Status)) + { + GetAliasesExesLengthRequest->Length = IntGetConsoleAliasesExesLength(Console->Aliases); + ConSrvReleaseConsole(Console, TRUE); + } + return ApiMessage->Status; +} + +/* EOF */ diff --git a/reactos/win32ss/user/winsrv/consrv_new/alias.h b/reactos/win32ss/user/winsrv/consrv_new/alias.h new file mode 100644 index 00000000000..19e3b98a871 --- /dev/null +++ b/reactos/win32ss/user/winsrv/consrv_new/alias.h @@ -0,0 +1,14 @@ +/* + * LICENSE: GPL - See COPYING in the top level directory + * PROJECT: ReactOS Console Server DLL + * FILE: win32ss/user/winsrv/consrv/alias.h + * PURPOSE: Alias support functions + * PROGRAMMERS: Christoph Wittich + * Johannes Anderwald + */ + +#pragma once + +VOID IntDeleteAllAliases(PCONSOLE Console); + +/* EOF */ diff --git a/reactos/win32ss/user/winsrv/consrv_new/api.h b/reactos/win32ss/user/winsrv/consrv_new/api.h new file mode 100644 index 00000000000..a7f340bc742 --- /dev/null +++ b/reactos/win32ss/user/winsrv/consrv_new/api.h @@ -0,0 +1,84 @@ +/* + * COPYRIGHT: See COPYING in the top level directory + * PROJECT: ReactOS Console Server DLL + * FILE: win32ss/user/winsrv/consrv/api.h + * PURPOSE: Public server APIs definitions + * PROGRAMMERS: Hermes Belusca-Maito (hermes.belusca@sfr.fr) + */ + +#pragma once + +/* alias.c */ +CSR_API(SrvAddConsoleAlias); +CSR_API(SrvGetConsoleAlias); +CSR_API(SrvGetConsoleAliases); +CSR_API(SrvGetConsoleAliasesLength); +CSR_API(SrvGetConsoleAliasExes); +CSR_API(SrvGetConsoleAliasExesLength); + +/* coninput.c */ +CSR_API(SrvReadConsole); +CSR_API(SrvGetConsoleInput); +CSR_API(SrvWriteConsoleInput); +CSR_API(SrvFlushConsoleInputBuffer); +CSR_API(SrvGetConsoleNumberOfInputEvents); + +/* conoutput.c */ +CSR_API(SrvInvalidateBitMapRect); +CSR_API(SrvReadConsoleOutput); +CSR_API(SrvWriteConsole); +CSR_API(SrvWriteConsoleOutput); +CSR_API(SrvReadConsoleOutputString); +CSR_API(SrvWriteConsoleOutputString); +CSR_API(SrvFillConsoleOutput); +CSR_API(SrvGetConsoleCursorInfo); +CSR_API(SrvSetConsoleCursorInfo); +CSR_API(SrvSetConsoleCursorPosition); +CSR_API(SrvSetConsoleTextAttribute); +CSR_API(SrvCreateConsoleScreenBuffer); +CSR_API(SrvGetConsoleScreenBufferInfo); +CSR_API(SrvSetConsoleActiveScreenBuffer); +CSR_API(SrvScrollConsoleScreenBuffer); +CSR_API(SrvSetConsoleScreenBufferSize); + +/* console.c */ +CSR_API(SrvAllocConsole); +CSR_API(SrvAttachConsole); +CSR_API(SrvFreeConsole); +CSR_API(SrvGetConsoleMode); +CSR_API(SrvSetConsoleMode); +CSR_API(SrvGetConsoleTitle); +CSR_API(SrvSetConsoleTitle); +CSR_API(SrvGetConsoleHardwareState); +CSR_API(SrvSetConsoleHardwareState); +CSR_API(SrvGetConsoleDisplayMode); +CSR_API(SrvSetConsoleDisplayMode); +CSR_API(SrvGetLargestConsoleWindowSize); +CSR_API(SrvShowConsoleCursor); +CSR_API(SrvSetConsoleCursor); +CSR_API(SrvConsoleMenuControl); +CSR_API(SrvSetConsoleMenuClose); +CSR_API(SrvSetConsoleWindowInfo); +CSR_API(SrvGetConsoleWindow); +CSR_API(SrvSetConsoleIcon); +CSR_API(SrvGetConsoleCP); +CSR_API(SrvSetConsoleCP); +CSR_API(SrvGetConsoleProcessList); +CSR_API(SrvGenerateConsoleCtrlEvent); +CSR_API(SrvGetConsoleSelectionInfo); + +/* handle.c */ +CSR_API(SrvOpenConsole); +CSR_API(SrvCloseHandle); +CSR_API(SrvVerifyConsoleIoHandle); +CSR_API(SrvDuplicateHandle); + +/* lineinput.c */ +CSR_API(SrvGetConsoleCommandHistory); +CSR_API(SrvGetConsoleCommandHistoryLength); +CSR_API(SrvExpungeConsoleCommandHistory); +CSR_API(SrvSetConsoleNumberOfCommands); +CSR_API(SrvGetConsoleHistory); +CSR_API(SrvSetConsoleHistory); + +/* EOF */ diff --git a/reactos/win32ss/user/winsrv/consrv_new/condrv/coninput.c b/reactos/win32ss/user/winsrv/consrv_new/condrv/coninput.c new file mode 100644 index 00000000000..38031304921 --- /dev/null +++ b/reactos/win32ss/user/winsrv/consrv_new/condrv/coninput.c @@ -0,0 +1,514 @@ +/* + * COPYRIGHT: See COPYING in the top level directory + * PROJECT: ReactOS Console Driver DLL + * FILE: win32ss/user/winsrv/consrv/condrv/coninput.c + * PURPOSE: Console Input functions + * PROGRAMMERS: Jeffrey Morlan + * Hermes Belusca-Maito (hermes.belusca@sfr.fr) + */ + +/* INCLUDES *******************************************************************/ + +#include "consrv.h" +#include "include/conio.h" +#include "include/conio2.h" +#include "handle.h" +#include "lineinput.h" + +#define NDEBUG +#include + + +/* GLOBALS ********************************************************************/ + +#define ConSrvGetInputBuffer(ProcessData, Handle, Ptr, Access, LockConsole) \ + ConSrvGetObject((ProcessData), (Handle), (PCONSOLE_IO_OBJECT*)(Ptr), NULL, \ + (Access), (LockConsole), INPUT_BUFFER) +#define ConSrvGetInputBufferAndHandleEntry(ProcessData, Handle, Ptr, Entry, Access, LockConsole) \ + ConSrvGetObject((ProcessData), (Handle), (PCONSOLE_IO_OBJECT*)(Ptr), (Entry), \ + (Access), (LockConsole), INPUT_BUFFER) +#define ConSrvReleaseInputBuffer(Buff, IsConsoleLocked) \ + ConSrvReleaseObject(&(Buff)->Header, (IsConsoleLocked)) + + +#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) + +typedef struct ConsoleInput_t +{ + LIST_ENTRY ListEntry; + INPUT_RECORD InputEvent; +} ConsoleInput; + + +/* PRIVATE FUNCTIONS **********************************************************/ + +static VOID FASTCALL +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); + } +} + +NTSTATUS FASTCALL +ConioProcessInputEvent(PCONSOLE 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->InputBuffer.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 = ConsoleAllocHeap(0, sizeof(ConsoleInput)); + if (ConInRec == NULL) return STATUS_INSUFFICIENT_RESOURCES; + + ConInRec->InputEvent = *InputEvent; + InsertTailList(&Console->InputBuffer.InputEvents, &ConInRec->ListEntry); + + SetEvent(Console->InputBuffer.ActiveEvent); + CsrNotifyWait(&Console->InputBuffer.ReadWaitQueue, + WaitAny, + NULL, + NULL); + if (!IsListEmpty(&Console->InputBuffer.ReadWaitQueue)) + { + CsrDereferenceWait(&Console->InputBuffer.ReadWaitQueue); + } + + return STATUS_SUCCESS; +} + +VOID FASTCALL +PurgeInputBuffer(PCONSOLE Console) +{ + PLIST_ENTRY CurrentEntry; + ConsoleInput* Event; + + while (!IsListEmpty(&Console->InputBuffer.InputEvents)) + { + CurrentEntry = RemoveHeadList(&Console->InputBuffer.InputEvents); + Event = CONTAINING_RECORD(CurrentEntry, ConsoleInput, ListEntry); + ConsoleFreeHeap(Event); + } + + CloseHandle(Console->InputBuffer.ActiveEvent); +} + +VOID NTAPI +ConDrvProcessKey(IN PCONSOLE Console, + IN BOOLEAN Down, + IN UINT VirtualKeyCode, + IN UINT VirtualScanCode, + IN WCHAR UnicodeChar, + IN ULONG ShiftState, + IN BYTE KeyStateCtrl) +{ + INPUT_RECORD er; + + /* process Ctrl-C and Ctrl-Break */ + if ( Console->InputBuffer.Mode & ENABLE_PROCESSED_INPUT && + Down && (VirtualKeyCode == VK_PAUSE || VirtualKeyCode == 'C') && + (ShiftState & (LEFT_CTRL_PRESSED | RIGHT_CTRL_PRESSED) || KeyStateCtrl & 0x80) ) + { + DPRINT1("Console_Api Ctrl-C\n"); + ConDrvConsoleProcessCtrlEvent(Console, 0, CTRL_C_EVENT); + + if (Console->LineBuffer && !Console->LineComplete) + { + /* Line input is in progress; end it */ + Console->LinePos = Console->LineSize = 0; + Console->LineComplete = TRUE; + } + return; + } + + if ( (ShiftState & (RIGHT_ALT_PRESSED | LEFT_ALT_PRESSED)) != 0 && + (VK_UP == VirtualKeyCode || VK_DOWN == VirtualKeyCode) ) + { + if (!Down) return; + + /* scroll up or down */ + if (VK_UP == VirtualKeyCode) + { + /* only scroll up if there is room to scroll up into */ + if (Console->ActiveBuffer->CursorPosition.Y != Console->ActiveBuffer->ScreenBufferSize.Y - 1) + { + Console->ActiveBuffer->VirtualY = (Console->ActiveBuffer->VirtualY + + Console->ActiveBuffer->ScreenBufferSize.Y - 1) % + Console->ActiveBuffer->ScreenBufferSize.Y; + Console->ActiveBuffer->CursorPosition.Y++; + } + } + else + { + /* only scroll down if there is room to scroll down into */ + if (Console->ActiveBuffer->CursorPosition.Y != 0) + { + Console->ActiveBuffer->VirtualY = (Console->ActiveBuffer->VirtualY + 1) % + Console->ActiveBuffer->ScreenBufferSize.Y; + Console->ActiveBuffer->CursorPosition.Y--; + } + } + + ConioDrawConsole(Console); + return; + } + + er.EventType = KEY_EVENT; + er.Event.KeyEvent.bKeyDown = Down; + er.Event.KeyEvent.wRepeatCount = 1; + er.Event.KeyEvent.wVirtualKeyCode = VirtualKeyCode; + er.Event.KeyEvent.wVirtualScanCode = VirtualScanCode; + er.Event.KeyEvent.uChar.UnicodeChar = UnicodeChar; + er.Event.KeyEvent.dwControlKeyState = ShiftState; + + ConioProcessInputEvent(Console, &er); +} + + +/* PUBLIC DRIVER APIS *********************************************************/ + +NTSTATUS NTAPI +ConDrvReadConsole(IN PCONSOLE Console, + IN PCONSOLE_INPUT_BUFFER InputBuffer, + IN BOOLEAN Unicode, + OUT PVOID Buffer, + IN OUT PCONSOLE_READCONSOLE_CONTROL ReadControl, + IN ULONG NumCharsToRead, + OUT PULONG NumCharsRead OPTIONAL) +{ + // STATUS_PENDING : Wait if more to read ; STATUS_SUCCESS : Don't wait. + NTSTATUS Status = STATUS_PENDING; + PLIST_ENTRY CurrentEntry; + ConsoleInput *Input; + ULONG i = ReadControl->nInitialChars; + + if (Console == NULL || InputBuffer == NULL || /* Buffer == NULL || */ + ReadControl == NULL || ReadControl->nLength != sizeof(CONSOLE_READCONSOLE_CONTROL)) + { + return STATUS_INVALID_PARAMETER; + } + + /* Validity checks */ + ASSERT(Console == InputBuffer->Header.Console); + ASSERT( (Buffer != NULL && NumCharsToRead >= 0) || + (Buffer == NULL && NumCharsToRead == 0) ); + + /* We haven't read anything (yet) */ + + if (InputBuffer->Mode & ENABLE_LINE_INPUT) + { + if (Console->LineBuffer == NULL) + { + /* Starting a new line */ + Console->LineMaxSize = (WORD)max(256, NumCharsToRead); + + Console->LineBuffer = ConsoleAllocHeap(0, Console->LineMaxSize * sizeof(WCHAR)); + if (Console->LineBuffer == NULL) return STATUS_NO_MEMORY; + + Console->LineComplete = FALSE; + Console->LineUpPressed = FALSE; + Console->LineInsertToggle = 0; + Console->LineWakeupMask = ReadControl->dwCtrlWakeupMask; + Console->LineSize = ReadControl->nInitialChars; + Console->LinePos = Console->LineSize; + + /* + * 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, &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 + { + /* 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 ASCII chars, 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); + } + } + + if (NumCharsRead) *NumCharsRead = i; + + return Status; +} + +NTSTATUS NTAPI +ConDrvGetConsoleInput(IN PCONSOLE Console, + IN PCONSOLE_INPUT_BUFFER InputBuffer, + IN BOOLEAN WaitForMoreEvents, + IN BOOLEAN Unicode, + OUT PINPUT_RECORD InputRecord, + IN ULONG NumEventsToRead, + OUT PULONG NumEventsRead OPTIONAL) +{ + PLIST_ENTRY CurrentInput; + ConsoleInput* Input; + ULONG i = 0; + + if (Console == NULL || InputBuffer == NULL /* || InputRecord == NULL */) + return STATUS_INVALID_PARAMETER; + + /* Validity checks */ + ASSERT(Console == InputBuffer->Header.Console); + ASSERT( (InputRecord != NULL && NumEventsToRead >= 0) || + (InputRecord == NULL && NumEventsToRead == 0) ); + + // Do NOT do that !! Use the existing number of events already read, if any... + // if (NumEventsRead) *NumEventsRead = 0; + + if (IsListEmpty(&InputBuffer->InputEvents)) + { + /* + * No input is available. Wait for more input if requested, + * otherwise, we don't wait, so we return success. + */ + return (WaitForMoreEvents ? STATUS_PENDING : STATUS_SUCCESS); + } + + /* Only get input if there is any */ + CurrentInput = InputBuffer->InputEvents.Flink; + if (NumEventsRead) i = *NumEventsRead; // We will read the remaining events... + + while ((CurrentInput != &InputBuffer->InputEvents) && (i < NumEventsToRead)) + { + Input = CONTAINING_RECORD(CurrentInput, ConsoleInput, ListEntry); + + *InputRecord = Input->InputEvent; + + if (!Unicode) + { + ConioInputEventToAnsi(InputBuffer->Header.Console, InputRecord); + } + + ++InputRecord; + ++i; + CurrentInput = CurrentInput->Flink; + + if (WaitForMoreEvents) // TRUE --> Read, we remove inputs from the buffer ; FALSE --> Peek, we keep inputs. + { + RemoveEntryList(&Input->ListEntry); + ConsoleFreeHeap(Input); + } + } + + if (NumEventsRead) *NumEventsRead = i; + + if (IsListEmpty(&InputBuffer->InputEvents)) + { + ResetEvent(InputBuffer->ActiveEvent); + } + + /* We read all the inputs available, we return success */ + return STATUS_SUCCESS; +} + +NTSTATUS NTAPI +ConDrvWriteConsoleInput(IN PCONSOLE Console, + IN PCONSOLE_INPUT_BUFFER InputBuffer, + IN BOOLEAN Unicode, + IN PINPUT_RECORD InputRecord, + IN ULONG NumEventsToWrite, + OUT PULONG NumEventsWritten OPTIONAL) +{ + NTSTATUS Status = STATUS_SUCCESS; + ULONG i; + + if (Console == NULL || InputBuffer == NULL /* || InputRecord == NULL */) + return STATUS_INVALID_PARAMETER; + + /* Validity checks */ + ASSERT(Console == InputBuffer->Header.Console); + ASSERT( (InputRecord != NULL && NumEventsToWrite >= 0) || + (InputRecord == NULL && NumEventsToWrite == 0) ); + + // Do NOT do that !! Use the existing number of events already written, if any... + // if (NumEventsWritten) *NumEventsWritten = 0; + + for (i = (NumEventsWritten ? *NumEventsWritten : 0); i < NumEventsToWrite && NT_SUCCESS(Status); ++i) + { + if (InputRecord->EventType == KEY_EVENT && !Unicode) + { + CHAR AsciiChar = InputRecord->Event.KeyEvent.uChar.AsciiChar; + ConsoleInputAnsiCharToUnicodeChar(Console, + &InputRecord->Event.KeyEvent.uChar.UnicodeChar, + &AsciiChar); + } + + Status = ConioProcessInputEvent(Console, InputRecord++); + } + + if (NumEventsWritten) *NumEventsWritten = i; + + return Status; +} + +NTSTATUS NTAPI +ConDrvFlushConsoleInputBuffer(IN PCONSOLE Console, + IN PCONSOLE_INPUT_BUFFER InputBuffer) +{ + PLIST_ENTRY CurrentEntry; + ConsoleInput* Event; + + if (Console == NULL || InputBuffer == NULL) + return STATUS_INVALID_PARAMETER; + + /* Validity check */ + ASSERT(Console == InputBuffer->Header.Console); + + /* Discard all entries in the input event queue */ + while (!IsListEmpty(&InputBuffer->InputEvents)) + { + CurrentEntry = RemoveHeadList(&InputBuffer->InputEvents); + Event = CONTAINING_RECORD(CurrentEntry, ConsoleInput, ListEntry); + ConsoleFreeHeap(Event); + } + ResetEvent(InputBuffer->ActiveEvent); + + return STATUS_SUCCESS; +} + +NTSTATUS NTAPI +ConDrvGetConsoleNumberOfInputEvents(IN PCONSOLE Console, + IN PCONSOLE_INPUT_BUFFER InputBuffer, + OUT PULONG NumEvents) +{ + PLIST_ENTRY CurrentInput; + + if (Console == NULL || InputBuffer == NULL || NumEvents == NULL) + return STATUS_INVALID_PARAMETER; + + /* Validity check */ + ASSERT(Console == InputBuffer->Header.Console); + + *NumEvents = 0; + + /* If there are any events ... */ + CurrentInput = InputBuffer->InputEvents.Flink; + while (CurrentInput != &InputBuffer->InputEvents) + { + CurrentInput = CurrentInput->Flink; + (*NumEvents)++; + } + + return STATUS_SUCCESS; +} + +/* EOF */ diff --git a/reactos/win32ss/user/winsrv/consrv_new/condrv/conoutput.c b/reactos/win32ss/user/winsrv/consrv_new/condrv/conoutput.c new file mode 100644 index 00000000000..03d5745f754 --- /dev/null +++ b/reactos/win32ss/user/winsrv/consrv_new/condrv/conoutput.c @@ -0,0 +1,287 @@ +/* + * COPYRIGHT: See COPYING in the top level directory + * PROJECT: ReactOS Console Driver DLL + * FILE: win32ss/user/winsrv/consrv/condrv/conoutput.c + * PURPOSE: General Console Output Functions + * PROGRAMMERS: Jeffrey Morlan + * Hermes Belusca-Maito (hermes.belusca@sfr.fr) + */ + +/* INCLUDES *******************************************************************/ + +#include "consrv.h" +#include "console.h" +#include "include/conio.h" +#include "include/conio2.h" +#include "conoutput.h" +#include "handle.h" + +#define NDEBUG +#include + + +/* PRIVATE FUNCTIONS **********************************************************/ + +NTSTATUS +TEXTMODE_BUFFER_Initialize(OUT PCONSOLE_SCREEN_BUFFER* Buffer, + IN OUT PCONSOLE Console, + IN PTEXTMODE_BUFFER_INFO TextModeInfo); +NTSTATUS +GRAPHICS_BUFFER_Initialize(OUT PCONSOLE_SCREEN_BUFFER* Buffer, + IN OUT PCONSOLE Console, + IN PGRAPHICS_BUFFER_INFO GraphicsInfo); + +VOID +TEXTMODE_BUFFER_Destroy(IN OUT PCONSOLE_SCREEN_BUFFER Buffer); +VOID +GRAPHICS_BUFFER_Destroy(IN OUT PCONSOLE_SCREEN_BUFFER Buffer); + + +NTSTATUS +CONSOLE_SCREEN_BUFFER_Initialize(OUT PCONSOLE_SCREEN_BUFFER* Buffer, + IN OUT PCONSOLE Console, + IN SIZE_T Size) +{ + if (Buffer == NULL || Console == NULL) + return STATUS_INVALID_PARAMETER; + + *Buffer = ConsoleAllocHeap(HEAP_ZERO_MEMORY, max(sizeof(CONSOLE_SCREEN_BUFFER), Size)); + if (*Buffer == NULL) return STATUS_INSUFFICIENT_RESOURCES; + + /* Initialize the header with the default type */ + ConSrvInitObject(&(*Buffer)->Header, SCREEN_BUFFER, Console); + (*Buffer)->Vtbl = NULL; + return STATUS_SUCCESS; +} + +VOID +CONSOLE_SCREEN_BUFFER_Destroy(IN OUT PCONSOLE_SCREEN_BUFFER Buffer) +{ + if (Buffer->Header.Type == TEXTMODE_BUFFER) + TEXTMODE_BUFFER_Destroy(Buffer); + else if (Buffer->Header.Type == GRAPHICS_BUFFER) + GRAPHICS_BUFFER_Destroy(Buffer); + else if (Buffer->Header.Type == SCREEN_BUFFER) + ConsoleFreeHeap(Buffer); + // else + // do_nothing; +} + +// ConDrvCreateConsoleScreenBuffer +NTSTATUS FASTCALL +ConDrvCreateScreenBuffer(OUT PCONSOLE_SCREEN_BUFFER* Buffer, + IN OUT PCONSOLE Console, + IN ULONG BufferType, + IN PVOID ScreenBufferInfo) +{ + NTSTATUS Status = STATUS_SUCCESS; + + if ( Console == NULL || Buffer == NULL || + (BufferType != CONSOLE_TEXTMODE_BUFFER && BufferType != CONSOLE_GRAPHICS_BUFFER) ) + { + return STATUS_INVALID_PARAMETER; + } + + if (BufferType == CONSOLE_TEXTMODE_BUFFER) + { + Status = TEXTMODE_BUFFER_Initialize(Buffer, + Console, + (PTEXTMODE_BUFFER_INFO)ScreenBufferInfo); + } + else if (BufferType == CONSOLE_GRAPHICS_BUFFER) + { + Status = GRAPHICS_BUFFER_Initialize(Buffer, + Console, + (PGRAPHICS_BUFFER_INFO)ScreenBufferInfo); + } + else + { + /* Never ever go there!! */ + ASSERT(FALSE); + } + + /* Insert the newly created screen buffer into the list, if succeeded */ + if (NT_SUCCESS(Status)) InsertHeadList(&Console->BufferList, &(*Buffer)->ListEntry); + + return Status; +} + +static VOID +ConioSetActiveScreenBuffer(PCONSOLE_SCREEN_BUFFER Buffer); + +VOID WINAPI +ConioDeleteScreenBuffer(PCONSOLE_SCREEN_BUFFER Buffer) +{ + PCONSOLE Console = Buffer->Header.Console; + PCONSOLE_SCREEN_BUFFER NewBuffer; + + RemoveEntryList(&Buffer->ListEntry); + if (Buffer == Console->ActiveBuffer) + { + /* Delete active buffer; switch to most recently created */ + Console->ActiveBuffer = NULL; + if (!IsListEmpty(&Console->BufferList)) + { + NewBuffer = CONTAINING_RECORD(Console->BufferList.Flink, + CONSOLE_SCREEN_BUFFER, + ListEntry); + ConioSetActiveScreenBuffer(NewBuffer); + } + } + + CONSOLE_SCREEN_BUFFER_Destroy(Buffer); +} + +VOID FASTCALL +ConioDrawConsole(PCONSOLE Console) +{ + SMALL_RECT Region; + PCONSOLE_SCREEN_BUFFER ActiveBuffer = Console->ActiveBuffer; + + if (ActiveBuffer) + { + ConioInitRect(&Region, 0, 0, ActiveBuffer->ViewSize.Y - 1, ActiveBuffer->ViewSize.X - 1); + ConioDrawRegion(Console, &Region); + } +} + +static VOID +ConioSetActiveScreenBuffer(PCONSOLE_SCREEN_BUFFER Buffer) +{ + PCONSOLE Console = Buffer->Header.Console; + Console->ActiveBuffer = Buffer; + ConioResizeTerminal(Console); + // ConioDrawConsole(Console); +} + +NTSTATUS NTAPI +ConDrvSetConsoleActiveScreenBuffer(IN PCONSOLE Console, + IN PCONSOLE_SCREEN_BUFFER Buffer) +{ + if (Console == NULL || Buffer == NULL) + return STATUS_INVALID_PARAMETER; + + /* Validity check */ + ASSERT(Console == Buffer->Header.Console); + + if (Buffer == Console->ActiveBuffer) return STATUS_SUCCESS; + + /* If old buffer has no handles, it's now unreferenced */ + if (Console->ActiveBuffer->Header.HandleCount == 0) + { + ConioDeleteScreenBuffer(Console->ActiveBuffer); + } + + /* Tie console to new buffer */ + ConioSetActiveScreenBuffer(Buffer); + + return STATUS_SUCCESS; +} + +PCONSOLE_SCREEN_BUFFER +ConDrvGetActiveScreenBuffer(IN PCONSOLE Console) +{ + return (Console ? Console->ActiveBuffer : NULL); +} + +/* PUBLIC DRIVER APIS *********************************************************/ + +NTSTATUS NTAPI +ConDrvInvalidateBitMapRect(IN PCONSOLE Console, + IN PCONSOLE_SCREEN_BUFFER Buffer, + IN PSMALL_RECT Region) +{ + if (Console == NULL || Buffer == NULL || Region == NULL) + return STATUS_INVALID_PARAMETER; + + /* Validity check */ + ASSERT(Console == Buffer->Header.Console); + + /* If the output buffer is the current one, redraw the correct portion of the screen */ + if (Buffer == Console->ActiveBuffer) ConioDrawRegion(Console, Region); + + return STATUS_SUCCESS; +} + +NTSTATUS NTAPI +ConDrvGetConsoleCursorInfo(IN PCONSOLE Console, + IN PTEXTMODE_SCREEN_BUFFER Buffer, + OUT PCONSOLE_CURSOR_INFO CursorInfo) +{ + if (Console == NULL || Buffer == NULL || CursorInfo == NULL) + return STATUS_INVALID_PARAMETER; + + /* Validity check */ + ASSERT(Console == Buffer->Header.Console); + + *CursorInfo = Buffer->CursorInfo; + // CursorInfo->bVisible = Buffer->CursorInfo.bVisible; + // CursorInfo->dwSize = Buffer->CursorInfo.dwSize; + + return STATUS_SUCCESS; +} + +NTSTATUS NTAPI +ConDrvSetConsoleCursorInfo(IN PCONSOLE Console, + IN PTEXTMODE_SCREEN_BUFFER Buffer, + IN PCONSOLE_CURSOR_INFO CursorInfo) +{ + ULONG Size; + BOOLEAN Visible, Success = TRUE; + + if (Console == NULL || Buffer == NULL || CursorInfo == NULL) + return STATUS_INVALID_PARAMETER; + + /* Validity check */ + ASSERT(Console == Buffer->Header.Console); + + Size = min(max(CursorInfo->dwSize, 1), 100); + Visible = CursorInfo->bVisible; + + if ( (Size != Buffer->CursorInfo.dwSize) || + (Visible && !Buffer->CursorInfo.bVisible) || + (!Visible && Buffer->CursorInfo.bVisible) ) + { + Buffer->CursorInfo.dwSize = Size; + Buffer->CursorInfo.bVisible = Visible; + + Success = ConioSetCursorInfo(Console, (PCONSOLE_SCREEN_BUFFER)Buffer); + } + + return (Success ? STATUS_SUCCESS : STATUS_UNSUCCESSFUL); +} + +NTSTATUS NTAPI +ConDrvSetConsoleCursorPosition(IN PCONSOLE Console, + IN PTEXTMODE_SCREEN_BUFFER Buffer, + IN PCOORD Position) +{ + SHORT OldCursorX, OldCursorY; + + if (Console == NULL || Buffer == NULL || Position == NULL) + return STATUS_INVALID_PARAMETER; + + /* Validity check */ + ASSERT(Console == Buffer->Header.Console); + + if ( Position->X < 0 || Position->X >= Buffer->ScreenBufferSize.X || + Position->Y < 0 || Position->Y >= Buffer->ScreenBufferSize.Y ) + { + return STATUS_INVALID_PARAMETER; + } + + OldCursorX = Buffer->CursorPosition.X; + OldCursorY = Buffer->CursorPosition.Y; + Buffer->CursorPosition = *Position; + // Buffer->CursorPosition.X = Position->X; + // Buffer->CursorPosition.Y = Position->Y; + if ( ((PCONSOLE_SCREEN_BUFFER)Buffer == Console->ActiveBuffer) && + (!ConioSetScreenInfo(Console, (PCONSOLE_SCREEN_BUFFER)Buffer, OldCursorX, OldCursorY)) ) + { + return STATUS_UNSUCCESSFUL; + } + + return STATUS_SUCCESS; +} + +/* EOF */ diff --git a/reactos/win32ss/user/winsrv/consrv_new/condrv/console.c b/reactos/win32ss/user/winsrv/consrv_new/condrv/console.c new file mode 100644 index 00000000000..861c4721f74 --- /dev/null +++ b/reactos/win32ss/user/winsrv/consrv_new/condrv/console.c @@ -0,0 +1,1106 @@ +/* + * COPYRIGHT: See COPYING in the top level directory + * PROJECT: ReactOS Console Driver DLL + * FILE: win32ss/user/winsrv/consrv/condrv/console.c + * PURPOSE: Console Management Functions + * PROGRAMMERS: Gé van Geldorp + * Jeffrey Morlan + * Hermes Belusca-Maito (hermes.belusca@sfr.fr) + */ + +/* INCLUDES *******************************************************************/ + +#include "consrv.h" +#include "include/conio.h" +#include "include/conio2.h" +#include "handle.h" +#include "procinit.h" +#include "alias.h" +#include "coninput.h" +#include "conoutput.h" +#include "lineinput.h" +#include "include/settings.h" + +#include "include/console.h" +#include "console.h" +#include "resource.h" + +#define NDEBUG +#include + +// FIXME: Add this prototype to winternl.h / rtlfuncs.h / ... +NTSTATUS NTAPI RtlGetLastNtStatus(VOID); + + +/* GLOBALS ********************************************************************/ + +static ULONG ConsoleListSize; +static PCONSOLE* ConsoleList; /* The list of all the allocated consoles */ +static RTL_RESOURCE ListLock; + +#define ConDrvLockConsoleListExclusive() \ + RtlAcquireResourceExclusive(&ListLock, TRUE) + +#define ConDrvLockConsoleListShared() \ + RtlAcquireResourceShared(&ListLock, TRUE) + +#define ConDrvUnlockConsoleList() \ + RtlReleaseResource(&ListLock) + +// Adapted from reactos/lib/rtl/unicode.c, RtlCreateUnicodeString line 2180 +static BOOLEAN +ConsoleCreateUnicodeString(IN OUT PUNICODE_STRING UniDest, + IN PCWSTR Source) +{ + SIZE_T Size = (wcslen(Source) + 1) * sizeof(WCHAR); + if (Size > MAXUSHORT) return FALSE; + + UniDest->Buffer = ConsoleAllocHeap(HEAP_ZERO_MEMORY, Size); + if (UniDest->Buffer == NULL) return FALSE; + + RtlCopyMemory(UniDest->Buffer, Source, Size); + UniDest->MaximumLength = (USHORT)Size; + UniDest->Length = (USHORT)Size - sizeof(WCHAR); + + return TRUE; +} + +// Adapted from reactos/lib/rtl/unicode.c, RtlFreeUnicodeString line 431 +static VOID +ConsoleFreeUnicodeString(IN PUNICODE_STRING UnicodeString) +{ + if (UnicodeString->Buffer) + { + ConsoleFreeHeap(UnicodeString->Buffer); + RtlZeroMemory(UnicodeString, sizeof(UNICODE_STRING)); + } +} + + +static NTSTATUS +InsertConsole(OUT PHANDLE Handle, + IN PCONSOLE Console) +{ +#define CONSOLE_HANDLES_INCREMENT 2 * 3 + + NTSTATUS Status = STATUS_SUCCESS; + ULONG i = 0; + PCONSOLE* Block; + + ASSERT( (ConsoleList == NULL && ConsoleListSize == 0) || + (ConsoleList != NULL && ConsoleListSize != 0) ); + + /* All went right, so add the console to the list */ + ConDrvLockConsoleListExclusive(); + DPRINT1("Insert in the list\n"); + + if (ConsoleList) + { + for (i = 0; i < ConsoleListSize; i++) + { + if (ConsoleList[i] == NULL) break; + } + } + + if (i >= ConsoleListSize) + { + DPRINT1("Creation of a new handles table\n"); + /* Allocate a new handles table */ + Block = ConsoleAllocHeap(HEAP_ZERO_MEMORY, + (ConsoleListSize + + CONSOLE_HANDLES_INCREMENT) * sizeof(PCONSOLE)); + if (Block == NULL) + { + Status = STATUS_UNSUCCESSFUL; + goto Quit; + } + + /* If we previously had a handles table, free it and use the new one */ + if (ConsoleList) + { + /* Copy the handles from the old table to the new one */ + RtlCopyMemory(Block, + ConsoleList, + ConsoleListSize * sizeof(PCONSOLE)); + ConsoleFreeHeap(ConsoleList); + } + ConsoleList = Block; + ConsoleListSize += CONSOLE_HANDLES_INCREMENT; + } + + ConsoleList[i] = Console; + *Handle = ULongToHandle((i << 2) | 0x3); + +Quit: + /* Unlock the console list and return status */ + ConDrvUnlockConsoleList(); + return Status; +} + +/* Unused */ +#if 0 +static NTSTATUS +RemoveConsoleByHandle(IN HANDLE Handle) +{ + NTSTATUS Status = STATUS_SUCCESS; + ULONG Index = HandleToULong(Handle) >> 2; + PCONSOLE Console; + + ASSERT( (ConsoleList == NULL && ConsoleListSize == 0) || + (ConsoleList != NULL && ConsoleListSize != 0) ); + + /* Remove the console from the list */ + ConDrvLockConsoleListExclusive(); + + if (Index >= ConsoleListSize || + (Console = ConsoleList[Index]) == NULL) + { + Status = STATUS_INVALID_HANDLE; + goto Quit; + } + + ConsoleList[Index] = NULL; + +Quit: + /* Unlock the console list and return status */ + ConDrvUnlockConsoleList(); + return Status; +} +#endif + +static NTSTATUS +RemoveConsoleByPointer(IN PCONSOLE Console) +{ + ULONG i = 0; + + if (!Console) return STATUS_INVALID_PARAMETER; + + ASSERT( (ConsoleList == NULL && ConsoleListSize == 0) || + (ConsoleList != NULL && ConsoleListSize != 0) ); + + /* Remove the console from the list */ + ConDrvLockConsoleListExclusive(); + + if (ConsoleList) + { + for (i = 0; i < ConsoleListSize; i++) + { + if (ConsoleList[i] == Console) ConsoleList[i] = NULL; + } + } + + /* Unlock the console list */ + ConDrvUnlockConsoleList(); + + return STATUS_SUCCESS; +} + + +/* For resetting the frontend - defined in dummyfrontend.c */ +VOID ResetFrontEnd(IN PCONSOLE Console); + + +/* PRIVATE FUNCTIONS **********************************************************/ + +static NTSTATUS +ConDrvConsoleCtrlEventTimeout(IN ULONG Event, + IN PCONSOLE_PROCESS_DATA ProcessData, + IN ULONG Timeout) +{ + NTSTATUS Status = STATUS_SUCCESS; + + DPRINT("ConDrvConsoleCtrlEventTimeout Parent ProcessId = %x\n", ProcessData->Process->ClientId.UniqueProcess); + + if (ProcessData->CtrlDispatcher) + { + _SEH2_TRY + { + HANDLE Thread = NULL; + + _SEH2_TRY + { + Thread = CreateRemoteThread(ProcessData->Process->ProcessHandle, NULL, 0, + ProcessData->CtrlDispatcher, + UlongToPtr(Event), 0, NULL); + if (NULL == Thread) + { + Status = RtlGetLastNtStatus(); + DPRINT1("Failed thread creation, Status = 0x%08lx\n", Status); + } + else + { + DPRINT("ProcessData->CtrlDispatcher remote thread creation succeeded, ProcessId = %x, Process = 0x%p\n", ProcessData->Process->ClientId.UniqueProcess, ProcessData->Process); + WaitForSingleObject(Thread, Timeout); + } + } + _SEH2_FINALLY + { + CloseHandle(Thread); + } + _SEH2_END; + } + _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER) + { + Status = _SEH2_GetExceptionCode(); + DPRINT1("ConDrvConsoleCtrlEventTimeout - Caught an exception, Status = 0x%08lx\n", Status); + } + _SEH2_END; + } + + return Status; +} + +static NTSTATUS +ConDrvConsoleCtrlEvent(IN ULONG Event, + IN PCONSOLE_PROCESS_DATA ProcessData) +{ + return ConDrvConsoleCtrlEventTimeout(Event, ProcessData, 0); +} + +VOID FASTCALL +ConioPause(PCONSOLE Console, UINT Flags) +{ + Console->PauseFlags |= Flags; + if (!Console->UnpauseEvent) + Console->UnpauseEvent = CreateEvent(NULL, TRUE, FALSE, NULL); +} + +VOID FASTCALL +ConioUnpause(PCONSOLE Console, UINT Flags) +{ + Console->PauseFlags &= ~Flags; + + // if ((Console->PauseFlags & (PAUSED_FROM_KEYBOARD | PAUSED_FROM_SCROLLBAR | PAUSED_FROM_SELECTION)) == 0) + if (Console->PauseFlags == 0 && Console->UnpauseEvent) + { + SetEvent(Console->UnpauseEvent); + CloseHandle(Console->UnpauseEvent); + Console->UnpauseEvent = NULL; + + CsrNotifyWait(&Console->WriteWaitQueue, + WaitAll, + NULL, + NULL); + if (!IsListEmpty(&Console->WriteWaitQueue)) + { + CsrDereferenceWait(&Console->WriteWaitQueue); + } + } +} + + +/* + * Console accessibility check helpers + */ + +BOOLEAN NTAPI +ConDrvValidateConsoleState(IN PCONSOLE Console, + IN CONSOLE_STATE ExpectedState) +{ + // if (!Console) return FALSE; + + /* The console must be locked */ + // ASSERT(Console_locked); + + return (Console->State == ExpectedState); +} + +BOOLEAN NTAPI +ConDrvValidateConsoleUnsafe(IN PCONSOLE Console, + IN CONSOLE_STATE ExpectedState, + IN BOOLEAN LockConsole) +{ + if (!Console) return FALSE; + + /* + * Lock the console to forbid possible console's state changes + * (which must be done when the console is already locked). + * If we don't want to lock it, it's because the lock is already + * held. So there must be no problems. + */ + if (LockConsole) EnterCriticalSection(&Console->Lock); + + // ASSERT(Console_locked); + + /* Check whether the console's state is what we expect */ + if (!ConDrvValidateConsoleState(Console, ExpectedState)) + { + if (LockConsole) LeaveCriticalSection(&Console->Lock); + return FALSE; + } + + return TRUE; +} + +BOOLEAN NTAPI +ConDrvValidateConsole(OUT PCONSOLE* Console, + IN HANDLE ConsoleHandle, + IN CONSOLE_STATE ExpectedState, + IN BOOLEAN LockConsole) +{ + BOOLEAN RetVal = FALSE; + + ULONG Index = HandleToULong(ConsoleHandle) >> 2; + PCONSOLE ValidatedConsole; + + if (!Console) return FALSE; + *Console = NULL; + + /* + * Forbid creation or deletion of consoles when + * checking for the existence of a console. + */ + ConDrvLockConsoleListShared(); + + if (Index >= ConsoleListSize || + (ValidatedConsole = ConsoleList[Index]) == NULL) + { + /* Unlock the console list */ + ConDrvUnlockConsoleList(); + + return FALSE; + } + + ValidatedConsole = ConsoleList[Index]; + + /* Unlock the console list and return */ + ConDrvUnlockConsoleList(); + + RetVal = ConDrvValidateConsoleUnsafe(ValidatedConsole, + ExpectedState, + LockConsole); + if (RetVal) *Console = ValidatedConsole; + + return RetVal; +} + +NTSTATUS NTAPI +ConDrvGetConsole(OUT PCONSOLE* Console, + IN HANDLE ConsoleHandle, + IN BOOLEAN LockConsole) +{ + NTSTATUS Status = STATUS_INVALID_HANDLE; + PCONSOLE GrabConsole; + + if (Console == NULL) return STATUS_INVALID_PARAMETER; + *Console = NULL; + + if (ConDrvValidateConsole(&GrabConsole, + ConsoleHandle, + CONSOLE_RUNNING, + LockConsole)) + { + InterlockedIncrement(&GrabConsole->ReferenceCount); + *Console = GrabConsole; + Status = STATUS_SUCCESS; + } + + return Status; +} + +VOID NTAPI +ConDrvReleaseConsole(IN PCONSOLE Console, + IN BOOLEAN WasConsoleLocked) +{ + LONG RefCount = 0; + + if (!Console) return; + // if (Console->ReferenceCount == 0) return; // This shouldn't happen + ASSERT(Console->ReferenceCount > 0); + + /* The console must be locked */ + // ASSERT(Console_locked); + + /* + * Decrement the reference count. Save the new value too, + * because Console->ReferenceCount might be modified after + * the console gets unlocked but before we check whether we + * can destroy it. + */ + RefCount = _InterlockedDecrement(&Console->ReferenceCount); + + /* Unlock the console if needed */ + if (WasConsoleLocked) LeaveCriticalSection(&Console->Lock); + + /* Delete the console if needed */ + if (RefCount <= 0) ConDrvDeleteConsole(Console); +} + + +/* CONSOLE INITIALIZATION FUNCTIONS *******************************************/ + +VOID NTAPI +ConDrvInitConsoleSupport(VOID) +{ + DPRINT("CONSRV: ConDrvInitConsoleSupport()\n"); + + /* Initialize the console list and its lock */ + ConsoleListSize = 0; + ConsoleList = NULL; + RtlInitializeResource(&ListLock); + + /* Should call LoadKeyboardLayout */ +} + +NTSTATUS NTAPI +ConDrvInitConsole(OUT PHANDLE NewConsoleHandle, + OUT PCONSOLE* NewConsole, + IN PCONSOLE_INFO ConsoleInfo, + IN ULONG ConsoleLeaderProcessId) +{ + NTSTATUS Status; + SECURITY_ATTRIBUTES SecurityAttributes; + // CONSOLE_INFO CapturedConsoleInfo; + TEXTMODE_BUFFER_INFO ScreenBufferInfo; + HANDLE ConsoleHandle; + PCONSOLE Console; + PCONSOLE_SCREEN_BUFFER NewBuffer; + // WCHAR DefaultTitle[128]; + + if (NewConsoleHandle == NULL || NewConsole == NULL || ConsoleInfo == NULL) + return STATUS_INVALID_PARAMETER; + + *NewConsoleHandle = NULL; + *NewConsole = NULL; + + /* + * Allocate a console structure + */ + Console = ConsoleAllocHeap(HEAP_ZERO_MEMORY, sizeof(CONSOLE)); + if (NULL == Console) + { + DPRINT1("Not enough memory for console creation.\n"); + return STATUS_NO_MEMORY; + } + + /* + * Load the console settings + */ + + /* 1. Load the default settings */ + // ConSrvGetDefaultSettings(ConsoleInfo, ProcessId); + + // /* 2. Get the title of the console (initialize ConsoleInfo.ConsoleTitle) */ + // Length = min(wcslen(ConsoleStartInfo->ConsoleTitle), + // sizeof(ConsoleInfo.ConsoleTitle) / sizeof(ConsoleInfo.ConsoleTitle[0]) - 1); + // wcsncpy(ConsoleInfo.ConsoleTitle, ConsoleStartInfo->ConsoleTitle, Length); + // ConsoleInfo.ConsoleTitle[Length] = L'\0'; + + /* + * 4. Load the remaining console settings via the registry. + */ +#if 0 + if ((ConsoleStartInfo->dwStartupFlags & STARTF_TITLEISLINKNAME) == 0) + { + /* + * Either we weren't created by an app launched via a shell-link, + * or we failed to load shell-link console properties. + * Therefore, load the console infos for the application from the registry. + */ + ConSrvReadUserSettings(ConsoleInfo, ProcessId); + + /* + * Now, update them with the properties the user might gave to us + * via the STARTUPINFO structure before calling CreateProcess + * (and which was transmitted via the ConsoleStartInfo structure). + * We therefore overwrite the values read in the registry. + */ + if (ConsoleStartInfo->dwStartupFlags & STARTF_USEFILLATTRIBUTE) + { + ConsoleInfo->ScreenAttrib = (USHORT)ConsoleStartInfo->FillAttribute; + } + if (ConsoleStartInfo->dwStartupFlags & STARTF_USECOUNTCHARS) + { + ConsoleInfo->ScreenBufferSize = ConsoleStartInfo->ScreenBufferSize; + } + if (ConsoleStartInfo->dwStartupFlags & STARTF_USESIZE) + { + // ConsoleInfo->ConsoleSize = ConsoleStartInfo->ConsoleWindowSize; + ConsoleInfo->ConsoleSize.X = (SHORT)ConsoleStartInfo->ConsoleWindowSize.cx; + ConsoleInfo->ConsoleSize.Y = (SHORT)ConsoleStartInfo->ConsoleWindowSize.cy; + } + } +#endif + + /* + * Fix the screen buffer size if needed. The rule is: + * ScreenBufferSize >= ConsoleSize + */ + if (ConsoleInfo->ScreenBufferSize.X < ConsoleInfo->ConsoleSize.X) + ConsoleInfo->ScreenBufferSize.X = ConsoleInfo->ConsoleSize.X; + if (ConsoleInfo->ScreenBufferSize.Y < ConsoleInfo->ConsoleSize.Y) + ConsoleInfo->ScreenBufferSize.Y = ConsoleInfo->ConsoleSize.Y; + + /* + * Initialize the console + */ + Console->State = CONSOLE_INITIALIZING; + Console->ReferenceCount = 0; + InitializeCriticalSection(&Console->Lock); + InitializeListHead(&Console->ProcessList); + + /* Initialize the frontend interface */ + ResetFrontEnd(Console); + + memcpy(Console->Colors, ConsoleInfo->Colors, sizeof(ConsoleInfo->Colors)); + Console->ConsoleSize = ConsoleInfo->ConsoleSize; + Console->FixedSize = FALSE; // Value by default; is reseted by the front-ends if needed. + + /* + * Initialize the input buffer + */ + ConSrvInitObject(&Console->InputBuffer.Header, INPUT_BUFFER, Console); + + SecurityAttributes.nLength = sizeof(SECURITY_ATTRIBUTES); + SecurityAttributes.lpSecurityDescriptor = NULL; + SecurityAttributes.bInheritHandle = TRUE; + Console->InputBuffer.ActiveEvent = CreateEventW(&SecurityAttributes, TRUE, FALSE, NULL); + if (NULL == Console->InputBuffer.ActiveEvent) + { + DeleteCriticalSection(&Console->Lock); + ConsoleFreeHeap(Console); + return STATUS_UNSUCCESSFUL; + } + + Console->InputBuffer.InputBufferSize = 0; // FIXME! + InitializeListHead(&Console->InputBuffer.InputEvents); + InitializeListHead(&Console->InputBuffer.ReadWaitQueue); + Console->InputBuffer.Mode = ENABLE_PROCESSED_INPUT | ENABLE_LINE_INPUT | + ENABLE_ECHO_INPUT | ENABLE_MOUSE_INPUT; + + Console->QuickEdit = ConsoleInfo->QuickEdit; + Console->InsertMode = ConsoleInfo->InsertMode; + Console->LineBuffer = NULL; + Console->LineMaxSize = Console->LineSize = Console->LinePos = 0; + Console->LineComplete = Console->LineUpPressed = Console->LineInsertToggle = FALSE; + // LineWakeupMask + + // FIXME: This is terminal-specific !! VV + RtlZeroMemory(&Console->Selection, sizeof(CONSOLE_SELECTION_INFO)); + Console->Selection.dwFlags = CONSOLE_NO_SELECTION; + // dwSelectionCursor + + /* Set-up the code page */ + Console->CodePage = Console->OutputCodePage = ConsoleInfo->CodePage; + + /* Initialize a new text-mode screen buffer with default settings */ + ScreenBufferInfo.ScreenBufferSize = ConsoleInfo->ScreenBufferSize; + ScreenBufferInfo.ScreenAttrib = ConsoleInfo->ScreenAttrib; + ScreenBufferInfo.PopupAttrib = ConsoleInfo->PopupAttrib; + ScreenBufferInfo.IsCursorVisible = TRUE; + ScreenBufferInfo.CursorSize = ConsoleInfo->CursorSize; + + InitializeListHead(&Console->BufferList); + Status = ConDrvCreateScreenBuffer(&NewBuffer, + Console, + CONSOLE_TEXTMODE_BUFFER, + &ScreenBufferInfo); + if (!NT_SUCCESS(Status)) + { + DPRINT1("ConDrvCreateScreenBuffer: failed, Status = 0x%08lx\n", Status); + CloseHandle(Console->InputBuffer.ActiveEvent); + DeleteCriticalSection(&Console->Lock); + ConsoleFreeHeap(Console); + return Status; + } + /* Make the new screen buffer active */ + Console->ActiveBuffer = NewBuffer; + InitializeListHead(&Console->WriteWaitQueue); + Console->PauseFlags = 0; + Console->UnpauseEvent = NULL; + + /* + * Initialize the alias and history buffers + */ + Console->Aliases = NULL; + InitializeListHead(&Console->HistoryBuffers); + Console->HistoryBufferSize = ConsoleInfo->HistoryBufferSize; + Console->NumberOfHistoryBuffers = ConsoleInfo->NumberOfHistoryBuffers; + Console->HistoryNoDup = ConsoleInfo->HistoryNoDup; + + /* Initialize the console title */ + ConsoleCreateUnicodeString(&Console->OriginalTitle, ConsoleInfo->ConsoleTitle); + // if (ConsoleInfo.ConsoleTitle[0] == L'\0') + // { + // if (LoadStringW(ConSrvDllInstance, IDS_CONSOLE_TITLE, DefaultTitle, sizeof(DefaultTitle) / sizeof(DefaultTitle[0]))) + // { + // ConsoleCreateUnicodeString(&Console->Title, DefaultTitle); + // } + // else + // { + // ConsoleCreateUnicodeString(&Console->Title, L"ReactOS Console"); + // } + // } + // else + // { + ConsoleCreateUnicodeString(&Console->Title, ConsoleInfo->ConsoleTitle); + // } + + /* Lock the console until its initialization is finished */ + // EnterCriticalSection(&Console->Lock); + + DPRINT("Console initialized\n"); + + /* All went right, so add the console to the list */ + Status = InsertConsole(&ConsoleHandle, Console); + if (!NT_SUCCESS(Status)) + { + /* Fail */ + ConDrvDeleteConsole(Console); + return Status; + } + + /* The initialization is finished */ + DPRINT("Change state\n"); + Console->State = CONSOLE_RUNNING; + + /* Unlock the console */ + // LeaveCriticalSection(&Console->Lock); + + /* Return the newly created console to the caller and a success code too */ + *NewConsoleHandle = ConsoleHandle; + *NewConsole = Console; + return STATUS_SUCCESS; +} + +NTSTATUS NTAPI +ConDrvRegisterFrontEnd(IN PCONSOLE Console, + IN PFRONTEND FrontEnd) +{ + NTSTATUS Status; + + if (Console == NULL || FrontEnd == NULL) + return STATUS_INVALID_PARAMETER; + + /* FIXME: Lock the console before ?? */ + + /* + * Attach the frontend to the console. Use now the TermIFace of the console, + * and not the user-defined temporary FrontEnd pointer. + */ + Console->TermIFace = *FrontEnd; + Console->TermIFace.Console = Console; + + /* Initialize the frontend AFTER having attached it to the console */ + DPRINT("Finish initialization of frontend\n"); + Status = Console->TermIFace.Vtbl->InitFrontEnd(&Console->TermIFace, Console); + if (!NT_SUCCESS(Status)) + { + DPRINT1("FrontEnd initialization failed, Status = 0x%08lx\n", Status); + + /* We failed, detach the frontend from the console */ + FrontEnd->Console = NULL; // For the caller + ResetFrontEnd(Console); + + return Status; + } + + /* Copy buffer contents to screen */ + // FrontEnd.Draw(); + // ConioDrawConsole(Console); + DPRINT("Console drawn\n"); + + DPRINT("Terminal FrontEnd initialization done\n"); + return STATUS_SUCCESS; +} + +NTSTATUS NTAPI +ConDrvDeregisterFrontEnd(IN PCONSOLE Console) +{ + if (Console == NULL) return STATUS_INVALID_PARAMETER; + + /* FIXME: Lock the console before ?? */ + + /* Deinitialize the frontend BEFORE detaching it from the console */ + Console->TermIFace.Vtbl->DeinitFrontEnd(&Console->TermIFace/*, Console*/); + + /* + * Detach the frontend from the console: + * reinitialize the frontend interface. + */ + ResetFrontEnd(Console); + + DPRINT("Terminal FrontEnd unregistered\n"); + return STATUS_SUCCESS; +} + +VOID NTAPI +ConDrvDeleteConsole(IN PCONSOLE Console) +{ + DPRINT("ConDrvDeleteConsole(0x%p)\n", Console); + + /* + * Forbid validation of any console by other threads + * during the deletion of this console. + */ + ConDrvLockConsoleListExclusive(); + + /* + * If the console is already being destroyed, i.e. not running + * or finishing to be initialized, just return. + */ + if (!ConDrvValidateConsoleUnsafe(Console, CONSOLE_RUNNING, TRUE) && + !ConDrvValidateConsoleUnsafe(Console, CONSOLE_INITIALIZING, TRUE)) + { + /* Unlock the console list and return */ + ConDrvUnlockConsoleList(); + return; + } + + /* + * We are about to be destroyed. Signal it to other people + * so that they can terminate what they are doing, and that + * they cannot longer validate the console. + */ + Console->State = CONSOLE_TERMINATING; + + /* + * Allow other threads to finish their job: basically, unlock + * all other calls to EnterCriticalSection(&Console->Lock); by + * ConDrvValidateConsole(Unsafe) functions so that they just see + * that we are not in CONSOLE_RUNNING state anymore, or unlock + * other concurrent calls to ConDrvDeleteConsole so that they + * can see that we are in fact already deleting the console. + */ + LeaveCriticalSection(&Console->Lock); + ConDrvUnlockConsoleList(); + + /* FIXME: Send a terminate message to all the processes owning this console */ + + /* Cleanup the UI-oriented part */ + DPRINT("Deregister console\n"); + ConDrvDeregisterFrontEnd(Console); + DPRINT("Console deregistered\n"); + + /*** + * Check that the console is in terminating state before continuing + * (the cleanup code must not change the state of the console... + * ...unless to cancel console deletion ?). + ***/ + + ConDrvLockConsoleListExclusive(); + + if (!ConDrvValidateConsoleUnsafe(Console, CONSOLE_TERMINATING, TRUE)) + { + ConDrvUnlockConsoleList(); + return; + } + + /* We are now in destruction */ + Console->State = CONSOLE_IN_DESTRUCTION; + + /* We really delete the console. Reset the count to be sure. */ + Console->ReferenceCount = 0; + + /* Remove the console from the list */ + RemoveConsoleByPointer(Console); + + /* Discard all entries in the input event queue */ + PurgeInputBuffer(Console); + + if (Console->LineBuffer) ConsoleFreeHeap(Console->LineBuffer); + + IntDeleteAllAliases(Console); + HistoryDeleteBuffers(Console); + + ConioDeleteScreenBuffer(Console->ActiveBuffer); + Console->ActiveBuffer = NULL; + if (!IsListEmpty(&Console->BufferList)) + { + DPRINT1("BUG: screen buffer list not empty\n"); + ASSERT(FALSE); + } + + /**/ CloseHandle(Console->InputBuffer.ActiveEvent); /**/ + if (Console->UnpauseEvent) CloseHandle(Console->UnpauseEvent); + + ConsoleFreeUnicodeString(&Console->OriginalTitle); + ConsoleFreeUnicodeString(&Console->Title); + + DPRINT("ConDrvDeleteConsole - Unlocking\n"); + LeaveCriticalSection(&Console->Lock); + DPRINT("ConDrvDeleteConsole - Destroying lock\n"); + DeleteCriticalSection(&Console->Lock); + DPRINT("ConDrvDeleteConsole - Lock destroyed ; freeing console\n"); + + ConsoleFreeHeap(Console); + DPRINT("ConDrvDeleteConsole - Console destroyed\n"); + + /* Unlock the console list and return */ + ConDrvUnlockConsoleList(); +} + + +/* PUBLIC DRIVER APIS *********************************************************/ + +NTSTATUS NTAPI +ConDrvGetConsoleMode(IN PCONSOLE Console, + IN PCONSOLE_IO_OBJECT Object, + OUT PULONG ConsoleMode) +{ + NTSTATUS Status = STATUS_SUCCESS; + + if (Console == NULL || Object == NULL || ConsoleMode == NULL) + return STATUS_INVALID_PARAMETER; + + /* Validity check */ + ASSERT(Console == Object->Console); + + /*** FIXME: */ *ConsoleMode = 0; /***/ + + if (INPUT_BUFFER == Object->Type) + { + PCONSOLE_INPUT_BUFFER InputBuffer = (PCONSOLE_INPUT_BUFFER)Object; + + *ConsoleMode = InputBuffer->Mode; + + if (Console->QuickEdit || Console->InsertMode) + { + // Windows does this, even if it's not documented on MSDN + *ConsoleMode |= ENABLE_EXTENDED_FLAGS; + + if (Console->QuickEdit ) *ConsoleMode |= ENABLE_QUICK_EDIT_MODE; + if (Console->InsertMode) *ConsoleMode |= ENABLE_INSERT_MODE; + } + } + else if (TEXTMODE_BUFFER == Object->Type || GRAPHICS_BUFFER == Object->Type) + { + PCONSOLE_SCREEN_BUFFER Buffer = (PCONSOLE_SCREEN_BUFFER)Object; + *ConsoleMode = Buffer->Mode; + } + else + { + Status = STATUS_INVALID_HANDLE; + } + + return Status; +} + +NTSTATUS NTAPI +ConDrvSetConsoleMode(IN PCONSOLE Console, + IN PCONSOLE_IO_OBJECT Object, + IN ULONG ConsoleMode) +{ +#define CONSOLE_VALID_CONTROL_MODES ( ENABLE_EXTENDED_FLAGS | ENABLE_INSERT_MODE | ENABLE_QUICK_EDIT_MODE ) +#define CONSOLE_VALID_INPUT_MODES ( ENABLE_PROCESSED_INPUT | ENABLE_LINE_INPUT | \ + ENABLE_ECHO_INPUT | ENABLE_WINDOW_INPUT | \ + ENABLE_MOUSE_INPUT ) +#define CONSOLE_VALID_OUTPUT_MODES ( ENABLE_PROCESSED_OUTPUT | ENABLE_WRAP_AT_EOL_OUTPUT ) + + NTSTATUS Status = STATUS_SUCCESS; + + if (Console == NULL || Object == NULL) + return STATUS_INVALID_PARAMETER; + + /* Validity check */ + ASSERT(Console == Object->Console); + + if (INPUT_BUFFER == Object->Type) + { + PCONSOLE_INPUT_BUFFER InputBuffer = (PCONSOLE_INPUT_BUFFER)Object; + + DPRINT("SetConsoleMode(Input, %d)\n", ConsoleMode); + + /* + * 1. Only the presence of valid mode flags is allowed. + */ + if (ConsoleMode & ~(CONSOLE_VALID_INPUT_MODES | CONSOLE_VALID_CONTROL_MODES)) + { + Status = STATUS_INVALID_PARAMETER; + goto Quit; + } + + /* + * 2. If we use control mode flags without ENABLE_EXTENDED_FLAGS, + * then consider the flags invalid. + * + if ( (ConsoleMode & CONSOLE_VALID_CONTROL_MODES) && + (ConsoleMode & ENABLE_EXTENDED_FLAGS) == 0 ) + { + Status = STATUS_INVALID_PARAMETER; + goto Quit; + } + */ + + /* + * 3. Now we can continue. + */ + if (ConsoleMode & CONSOLE_VALID_CONTROL_MODES) + { + Console->QuickEdit = !!(ConsoleMode & ENABLE_QUICK_EDIT_MODE); + Console->InsertMode = !!(ConsoleMode & ENABLE_INSERT_MODE); + } + InputBuffer->Mode = (ConsoleMode & CONSOLE_VALID_INPUT_MODES); + } + else if (TEXTMODE_BUFFER == Object->Type || GRAPHICS_BUFFER == Object->Type) + { + PCONSOLE_SCREEN_BUFFER Buffer = (PCONSOLE_SCREEN_BUFFER)Object; + + DPRINT("SetConsoleMode(Output, %d)\n", ConsoleMode); + + if (ConsoleMode & ~CONSOLE_VALID_OUTPUT_MODES) + { + Status = STATUS_INVALID_PARAMETER; + } + else + { + Buffer->Mode = (ConsoleMode & CONSOLE_VALID_OUTPUT_MODES); + } + } + else + { + Status = STATUS_INVALID_HANDLE; + } + +Quit: + return Status; +} + +NTSTATUS NTAPI +ConDrvGetConsoleTitle(IN PCONSOLE Console, + IN OUT PWCHAR Title, + IN OUT PULONG BufLength) +{ + ULONG Length; + + if (Console == NULL || Title == NULL || BufLength == NULL) + return STATUS_INVALID_PARAMETER; + + /* Copy title of the console to the user title buffer */ + if (*BufLength >= sizeof(WCHAR)) + { + Length = min(*BufLength - sizeof(WCHAR), Console->Title.Length); + RtlCopyMemory(Title, Console->Title.Buffer, Length); + Title[Length / sizeof(WCHAR)] = L'\0'; + } + + *BufLength = Console->Title.Length; + + return STATUS_SUCCESS; +} + +NTSTATUS NTAPI +ConDrvSetConsoleTitle(IN PCONSOLE Console, + IN PWCHAR Title, + IN ULONG BufLength) +{ + PWCHAR Buffer; + + if (Console == NULL || Title == NULL) + return STATUS_INVALID_PARAMETER; + + /* Allocate a new buffer to hold the new title (NULL-terminated) */ + Buffer = ConsoleAllocHeap(0, BufLength + sizeof(WCHAR)); + if (!Buffer) return STATUS_NO_MEMORY; + + /* Free the old title */ + ConsoleFreeUnicodeString(&Console->Title); + + /* Copy title to console */ + Console->Title.Buffer = Buffer; + Console->Title.Length = BufLength; + Console->Title.MaximumLength = Console->Title.Length + sizeof(WCHAR); + RtlCopyMemory(Console->Title.Buffer, Title, Console->Title.Length); + Console->Title.Buffer[Console->Title.Length / sizeof(WCHAR)] = L'\0'; + + // ConioChangeTitle(Console); + return STATUS_SUCCESS; +} + +NTSTATUS NTAPI +ConDrvGetConsoleCP(IN PCONSOLE Console, + OUT PUINT CodePage, + IN BOOLEAN InputCP) +{ + if (Console == NULL || CodePage == NULL) + return STATUS_INVALID_PARAMETER; + + *CodePage = (InputCP ? Console->CodePage : Console->OutputCodePage); + + return STATUS_SUCCESS; +} + +NTSTATUS NTAPI +ConDrvSetConsoleCP(IN PCONSOLE Console, + IN UINT CodePage, + IN BOOLEAN InputCP) +{ + if (Console == NULL || !IsValidCodePage(CodePage)) + return STATUS_INVALID_PARAMETER; + + if (InputCP) + Console->CodePage = CodePage; + else + Console->OutputCodePage = CodePage; + + return STATUS_SUCCESS; +} + +NTSTATUS NTAPI +ConDrvGetConsoleProcessList(IN PCONSOLE Console, + IN OUT PULONG ProcessIdsList, + IN ULONG MaxIdListItems, + OUT PULONG ProcessIdsTotal) +{ + PCONSOLE_PROCESS_DATA current; + PLIST_ENTRY current_entry; + + if (Console == NULL || ProcessIdsList == NULL || ProcessIdsTotal == NULL) + return STATUS_INVALID_PARAMETER; + + *ProcessIdsTotal = 0; + + for (current_entry = Console->ProcessList.Flink; + current_entry != &Console->ProcessList; + current_entry = current_entry->Flink) + { + current = CONTAINING_RECORD(current_entry, CONSOLE_PROCESS_DATA, ConsoleLink); + if (++(*ProcessIdsTotal) <= MaxIdListItems) + { + *ProcessIdsList++ = HandleToUlong(current->Process->ClientId.UniqueProcess); + } + } + + return STATUS_SUCCESS; +} + +// ConDrvGenerateConsoleCtrlEvent +NTSTATUS NTAPI +ConDrvConsoleProcessCtrlEvent(IN PCONSOLE Console, + IN ULONG ProcessGroupId, + IN ULONG Event) +{ + NTSTATUS Status = STATUS_SUCCESS; + PLIST_ENTRY current_entry; + PCONSOLE_PROCESS_DATA current; + + /* If the console is already being destroyed, just return */ + if (!ConDrvValidateConsoleState(Console, CONSOLE_RUNNING)) + return STATUS_UNSUCCESSFUL; + + /* + * Loop through the process list, from the most recent process + * (the active one) to the oldest one (the first created, i.e. + * the console leader process), and for each, send an event + * (new processes are inserted at the head of the console process list). + */ + current_entry = Console->ProcessList.Flink; + while (current_entry != &Console->ProcessList) + { + current = CONTAINING_RECORD(current_entry, CONSOLE_PROCESS_DATA, ConsoleLink); + current_entry = current_entry->Flink; + + /* + * Only processes belonging to the same process group are signaled. + * If the process group ID is zero, then all the processes are signaled. + */ + if (ProcessGroupId == 0 || current->Process->ProcessGroupId == ProcessGroupId) + { + Status = ConDrvConsoleCtrlEvent(Event, current); + } + } + + return Status; +} + +/* EOF */ diff --git a/reactos/win32ss/user/winsrv/consrv_new/condrv/dummyfrontend.c b/reactos/win32ss/user/winsrv/consrv_new/condrv/dummyfrontend.c new file mode 100644 index 00000000000..fe8c43fc36c --- /dev/null +++ b/reactos/win32ss/user/winsrv/consrv_new/condrv/dummyfrontend.c @@ -0,0 +1,185 @@ +/* + * COPYRIGHT: See COPYING in the top level directory + * PROJECT: ReactOS Console Server DLL + * FILE: win32ss/user/winsrv/consrv/condrv/dummyfrontend.c + * PURPOSE: Dummy Terminal Front-End used when no frontend + * is attached to the specified console. + * PROGRAMMERS: Hermes Belusca-Maito (hermes.belusca@sfr.fr) + */ + +/* INCLUDES *******************************************************************/ + +#include "consrv.h" +#include "include/conio.h" + + +/* DUMMY FRONTEND INTERFACE ***************************************************/ + +static NTSTATUS NTAPI +DummyInitFrontEnd(IN OUT PFRONTEND This, + IN PCONSOLE Console) +{ + /* Load some settings ?? */ + return STATUS_SUCCESS; +} + +static VOID NTAPI +DummyDeinitFrontEnd(IN OUT PFRONTEND This) +{ + /* Free some settings ?? */ +} + +static VOID NTAPI +DummyDrawRegion(IN OUT PFRONTEND This, + SMALL_RECT* Region) +{ +} + +static VOID NTAPI +DummyWriteStream(IN OUT PFRONTEND This, + SMALL_RECT* Region, + SHORT CursorStartX, + SHORT CursorStartY, + UINT ScrolledLines, + PWCHAR Buffer, + UINT Length) +{ +} + +static BOOL NTAPI +DummySetCursorInfo(IN OUT PFRONTEND This, + PCONSOLE_SCREEN_BUFFER Buff) +{ + return TRUE; +} + +static BOOL NTAPI +DummySetScreenInfo(IN OUT PFRONTEND This, + PCONSOLE_SCREEN_BUFFER Buff, + SHORT OldCursorX, + SHORT OldCursorY) +{ + return TRUE; +} + +static VOID NTAPI +DummyResizeTerminal(IN OUT PFRONTEND This) +{ +} + +static BOOL NTAPI +DummyProcessKeyCallback(IN OUT PFRONTEND This, + MSG* msg, + BYTE KeyStateMenu, + DWORD ShiftState, + UINT VirtualKeyCode, + BOOL Down) +{ + return FALSE; +} + +static VOID NTAPI +DummyRefreshInternalInfo(IN OUT PFRONTEND This) +{ +} + +static VOID NTAPI +DummyChangeTitle(IN OUT PFRONTEND This) +{ +} + +static BOOL NTAPI +DummyChangeIcon(IN OUT PFRONTEND This, + HICON hWindowIcon) +{ + return TRUE; +} + +static HWND NTAPI +DummyGetConsoleWindowHandle(IN OUT PFRONTEND This) +{ + return NULL; +} + +static VOID NTAPI +DummyGetLargestConsoleWindowSize(IN OUT PFRONTEND This, + PCOORD pSize) +{ +} + +static ULONG NTAPI +DummyGetDisplayMode(IN OUT PFRONTEND This) +{ + return 0; +} + +static BOOL NTAPI +DummySetDisplayMode(IN OUT PFRONTEND This, + ULONG NewMode) +{ + return TRUE; +} + +static INT NTAPI +DummyShowMouseCursor(IN OUT PFRONTEND This, + BOOL Show) +{ + return 0; +} + +static BOOL NTAPI +DummySetMouseCursor(IN OUT PFRONTEND This, + HCURSOR hCursor) +{ + return TRUE; +} + +static HMENU NTAPI +DummyMenuControl(IN OUT PFRONTEND This, + UINT cmdIdLow, + UINT cmdIdHigh) +{ + return NULL; +} + +static BOOL NTAPI +DummySetMenuClose(IN OUT PFRONTEND This, + BOOL Enable) +{ + return TRUE; +} + +static FRONTEND_VTBL DummyVtbl = +{ + DummyInitFrontEnd, + DummyDeinitFrontEnd, + DummyDrawRegion, + DummyWriteStream, + DummySetCursorInfo, + DummySetScreenInfo, + DummyResizeTerminal, + DummyProcessKeyCallback, + DummyRefreshInternalInfo, + DummyChangeTitle, + DummyChangeIcon, + DummyGetConsoleWindowHandle, + DummyGetLargestConsoleWindowSize, + DummyGetDisplayMode, + DummySetDisplayMode, + DummyShowMouseCursor, + DummySetMouseCursor, + DummyMenuControl, + DummySetMenuClose, +}; + +VOID +ResetFrontEnd(IN PCONSOLE Console) +{ + if (!Console) return; + + /* Reinitialize the frontend interface */ + RtlZeroMemory(&Console->TermIFace, sizeof(Console->TermIFace)); + Console->TermIFace.Vtbl = &DummyVtbl; +} + +/* EOF */ diff --git a/reactos/win32ss/user/winsrv/consrv_new/condrv/graphics.c b/reactos/win32ss/user/winsrv/consrv_new/condrv/graphics.c new file mode 100644 index 00000000000..7c1f070c734 --- /dev/null +++ b/reactos/win32ss/user/winsrv/consrv_new/condrv/graphics.c @@ -0,0 +1,274 @@ +/* + * COPYRIGHT: See COPYING in the top level directory + * PROJECT: ReactOS Console Driver DLL + * FILE: win32ss/user/winsrv/consrv/condrv/graphics.c + * PURPOSE: Console Output Functions for graphics-mode screen-buffers + * PROGRAMMERS: Hermes Belusca-Maito (hermes.belusca@sfr.fr) + * + * NOTE: See http://blog.airesoft.co.uk/2012/10/things-ms-can-do-that-they-dont-tell-you-about-console-graphics/ + * for more information. + */ + +/* INCLUDES *******************************************************************/ + +#include "consrv.h" +#include "include/conio.h" +#include "include/conio2.h" +#include "conoutput.h" +#include "handle.h" + +#define NDEBUG +#include + + +/* PRIVATE FUNCTIONS **********************************************************/ + +CONSOLE_IO_OBJECT_TYPE +GRAPHICS_BUFFER_GetType(PCONSOLE_SCREEN_BUFFER This) +{ + // return This->Header.Type; + return GRAPHICS_BUFFER; +} + +static CONSOLE_SCREEN_BUFFER_VTBL GraphicsVtbl = +{ + GRAPHICS_BUFFER_GetType, +}; + + +NTSTATUS +CONSOLE_SCREEN_BUFFER_Initialize(OUT PCONSOLE_SCREEN_BUFFER* Buffer, + IN OUT PCONSOLE Console, + IN SIZE_T Size); +VOID +CONSOLE_SCREEN_BUFFER_Destroy(IN OUT PCONSOLE_SCREEN_BUFFER Buffer); + + +NTSTATUS +GRAPHICS_BUFFER_Initialize(OUT PCONSOLE_SCREEN_BUFFER* Buffer, + IN OUT PCONSOLE Console, + IN PGRAPHICS_BUFFER_INFO GraphicsInfo) +{ + NTSTATUS Status = STATUS_SUCCESS; + PGRAPHICS_SCREEN_BUFFER NewBuffer = NULL; + + LARGE_INTEGER SectionSize; + ULONG ViewSize = 0; + HANDLE ProcessHandle; + + if (Buffer == NULL || Console == NULL || GraphicsInfo == NULL) + return STATUS_INVALID_PARAMETER; + + *Buffer = NULL; + + Status = CONSOLE_SCREEN_BUFFER_Initialize((PCONSOLE_SCREEN_BUFFER*)&NewBuffer, + Console, + sizeof(GRAPHICS_SCREEN_BUFFER)); + if (!NT_SUCCESS(Status)) return Status; + NewBuffer->Header.Type = GRAPHICS_BUFFER; + NewBuffer->Vtbl = &GraphicsVtbl; + + /* + * Remember the handle to the process so that we can close or unmap + * correctly the allocated resources when the client releases the + * screen buffer. + */ + ProcessHandle = CsrGetClientThread()->Process->ProcessHandle; + NewBuffer->ClientProcess = ProcessHandle; + + /* Get infos from the graphics buffer information structure */ + NewBuffer->BitMapInfoLength = GraphicsInfo->Info.dwBitMapInfoLength; + + NewBuffer->BitMapInfo = ConsoleAllocHeap(HEAP_ZERO_MEMORY, NewBuffer->BitMapInfoLength); + if (NewBuffer->BitMapInfo == NULL) + { + CONSOLE_SCREEN_BUFFER_Destroy((PCONSOLE_SCREEN_BUFFER)NewBuffer); + return STATUS_INSUFFICIENT_RESOURCES; + } + + /* Adjust the bitmap height if needed (bottom-top vs. top-bottom). Use always bottom-up. */ + if (GraphicsInfo->Info.lpBitMapInfo->bmiHeader.biHeight > 0) + GraphicsInfo->Info.lpBitMapInfo->bmiHeader.biHeight = -GraphicsInfo->Info.lpBitMapInfo->bmiHeader.biHeight; + + /* We do not use anything else than uncompressed bitmaps */ + if (GraphicsInfo->Info.lpBitMapInfo->bmiHeader.biCompression != BI_RGB) + { + DPRINT1("biCompression == %d != BI_RGB, correct that!\n", GraphicsInfo->Info.lpBitMapInfo->bmiHeader.biCompression); + GraphicsInfo->Info.lpBitMapInfo->bmiHeader.biCompression = BI_RGB; + } + + RtlCopyMemory(NewBuffer->BitMapInfo, + GraphicsInfo->Info.lpBitMapInfo, + GraphicsInfo->Info.dwBitMapInfoLength); + + NewBuffer->BitMapUsage = GraphicsInfo->Info.dwUsage; + + /* Set the screen buffer size. Fight against overflows. */ + if ( GraphicsInfo->Info.lpBitMapInfo->bmiHeader.biWidth <= 0xFFFF && + -GraphicsInfo->Info.lpBitMapInfo->bmiHeader.biHeight <= 0xFFFF ) + { + /* Be careful about the sign of biHeight */ + NewBuffer->ScreenBufferSize.X = (SHORT)GraphicsInfo->Info.lpBitMapInfo->bmiHeader.biWidth ; + NewBuffer->ScreenBufferSize.Y = (SHORT)-GraphicsInfo->Info.lpBitMapInfo->bmiHeader.biHeight; + + NewBuffer->OldViewSize = NewBuffer->ViewSize = + NewBuffer->OldScreenBufferSize = NewBuffer->ScreenBufferSize; + } + else + { + Status = STATUS_INSUFFICIENT_RESOURCES; + ConsoleFreeHeap(NewBuffer->BitMapInfo); + CONSOLE_SCREEN_BUFFER_Destroy((PCONSOLE_SCREEN_BUFFER)NewBuffer); + goto Quit; + } + + /* + * Create a mutex to synchronize bitmap memory access + * between ourselves and the client. + */ + Status = NtCreateMutant(&NewBuffer->Mutex, MUTANT_ALL_ACCESS, NULL, FALSE); + if (!NT_SUCCESS(Status)) + { + DPRINT1("NtCreateMutant() failed: %lu\n", Status); + ConsoleFreeHeap(NewBuffer->BitMapInfo); + CONSOLE_SCREEN_BUFFER_Destroy((PCONSOLE_SCREEN_BUFFER)NewBuffer); + goto Quit; + } + + /* + * Duplicate the Mutex for the client. We must keep a trace of it + * so that we can close it when the client releases the screen buffer. + */ + Status = NtDuplicateObject(NtCurrentProcess(), + NewBuffer->Mutex, + ProcessHandle, + &NewBuffer->ClientMutex, + 0, 0, DUPLICATE_SAME_ACCESS); + if (!NT_SUCCESS(Status)) + { + DPRINT1("NtDuplicateObject() failed: %lu\n", Status); + NtClose(NewBuffer->Mutex); + ConsoleFreeHeap(NewBuffer->BitMapInfo); + CONSOLE_SCREEN_BUFFER_Destroy((PCONSOLE_SCREEN_BUFFER)NewBuffer); + goto Quit; + } + + /* + * Create a memory section for the bitmap area, to share with the client. + */ + SectionSize.QuadPart = NewBuffer->BitMapInfo->bmiHeader.biSizeImage; + Status = NtCreateSection(&NewBuffer->hSection, + SECTION_ALL_ACCESS, + NULL, + &SectionSize, + PAGE_READWRITE, + SEC_COMMIT, + NULL); + if (!NT_SUCCESS(Status)) + { + DPRINT1("Error: Impossible to create a shared section ; Status = %lu\n", Status); + NtClose(NewBuffer->ClientMutex); + NtClose(NewBuffer->Mutex); + ConsoleFreeHeap(NewBuffer->BitMapInfo); + CONSOLE_SCREEN_BUFFER_Destroy((PCONSOLE_SCREEN_BUFFER)NewBuffer); + goto Quit; + } + + /* + * Create a view for our needs. + */ + ViewSize = 0; + NewBuffer->BitMap = NULL; + Status = NtMapViewOfSection(NewBuffer->hSection, + NtCurrentProcess(), + (PVOID*)&NewBuffer->BitMap, + 0, + 0, + NULL, + &ViewSize, + ViewUnmap, + 0, + PAGE_READWRITE); + if (!NT_SUCCESS(Status)) + { + DPRINT1("Error: Impossible to map the shared section ; Status = %lu\n", Status); + NtClose(NewBuffer->hSection); + NtClose(NewBuffer->ClientMutex); + NtClose(NewBuffer->Mutex); + ConsoleFreeHeap(NewBuffer->BitMapInfo); + CONSOLE_SCREEN_BUFFER_Destroy((PCONSOLE_SCREEN_BUFFER)NewBuffer); + goto Quit; + } + + /* + * Create a view for the client. We must keep a trace of it so that + * we can unmap it when the client releases the screen buffer. + */ + ViewSize = 0; + NewBuffer->ClientBitMap = NULL; + Status = NtMapViewOfSection(NewBuffer->hSection, + ProcessHandle, + (PVOID*)&NewBuffer->ClientBitMap, + 0, + 0, + NULL, + &ViewSize, + ViewUnmap, + 0, + PAGE_READWRITE); + if (!NT_SUCCESS(Status)) + { + DPRINT1("Error: Impossible to map the shared section ; Status = %lu\n", Status); + NtUnmapViewOfSection(NtCurrentProcess(), NewBuffer->BitMap); + NtClose(NewBuffer->hSection); + NtClose(NewBuffer->ClientMutex); + NtClose(NewBuffer->Mutex); + ConsoleFreeHeap(NewBuffer->BitMapInfo); + CONSOLE_SCREEN_BUFFER_Destroy((PCONSOLE_SCREEN_BUFFER)NewBuffer); + goto Quit; + } + + NewBuffer->ViewOrigin.X = NewBuffer->ViewOrigin.Y = 0; + NewBuffer->VirtualY = 0; + + NewBuffer->CursorBlinkOn = FALSE; + NewBuffer->ForceCursorOff = TRUE; + NewBuffer->CursorInfo.bVisible = FALSE; + NewBuffer->CursorInfo.dwSize = 0; + NewBuffer->CursorPosition.X = NewBuffer->CursorPosition.Y = 0; + + NewBuffer->Mode = 0; + + *Buffer = (PCONSOLE_SCREEN_BUFFER)NewBuffer; + Status = STATUS_SUCCESS; + +Quit: + return Status; +} + +VOID +GRAPHICS_BUFFER_Destroy(IN OUT PCONSOLE_SCREEN_BUFFER Buffer) +{ + PGRAPHICS_SCREEN_BUFFER Buff = (PGRAPHICS_SCREEN_BUFFER)Buffer; + + /* + * IMPORTANT !! Reinitialize the type so that we don't enter a recursive + * infinite loop when calling CONSOLE_SCREEN_BUFFER_Destroy. + */ + Buffer->Header.Type = SCREEN_BUFFER; + + /* + * Uninitialize the graphics screen buffer + * in the reverse way we initialized it. + */ + NtUnmapViewOfSection(Buff->ClientProcess, Buff->ClientBitMap); + NtUnmapViewOfSection(NtCurrentProcess(), Buff->BitMap); + NtClose(Buff->hSection); + NtClose(Buff->ClientMutex); + NtClose(Buff->Mutex); + ConsoleFreeHeap(Buff->BitMapInfo); + + CONSOLE_SCREEN_BUFFER_Destroy(Buffer); +} + +/* EOF */ diff --git a/reactos/win32ss/user/winsrv/consrv_new/condrv/text.c b/reactos/win32ss/user/winsrv/consrv_new/condrv/text.c new file mode 100644 index 00000000000..f6b3b507b4a --- /dev/null +++ b/reactos/win32ss/user/winsrv/consrv_new/condrv/text.c @@ -0,0 +1,1346 @@ +/* + * COPYRIGHT: See COPYING in the top level directory + * PROJECT: ReactOS Console Driver DLL + * FILE: win32ss/user/winsrv/consrv/condrv/text.c + * PURPOSE: Console Output Functions for text-mode screen-buffers + * PROGRAMMERS: Jeffrey Morlan + * Hermes Belusca-Maito (hermes.belusca@sfr.fr) + */ + +/* INCLUDES *******************************************************************/ + +#include "consrv.h" +#include "include/conio.h" +#include "include/conio2.h" +#include "conoutput.h" +#include "handle.h" + +#define NDEBUG +#include + + +/* GLOBALS ********************************************************************/ + +#define TAB_WIDTH 8 + + +/* PRIVATE FUNCTIONS **********************************************************/ + +CONSOLE_IO_OBJECT_TYPE +TEXTMODE_BUFFER_GetType(PCONSOLE_SCREEN_BUFFER This) +{ + // return This->Header.Type; + return TEXTMODE_BUFFER; +} + +static CONSOLE_SCREEN_BUFFER_VTBL TextVtbl = +{ + TEXTMODE_BUFFER_GetType, +}; + + +static VOID FASTCALL +ClearLineBuffer(PTEXTMODE_SCREEN_BUFFER Buff); + + +NTSTATUS +CONSOLE_SCREEN_BUFFER_Initialize(OUT PCONSOLE_SCREEN_BUFFER* Buffer, + IN OUT PCONSOLE Console, + IN SIZE_T Size); +VOID +CONSOLE_SCREEN_BUFFER_Destroy(IN OUT PCONSOLE_SCREEN_BUFFER Buffer); + + +NTSTATUS +TEXTMODE_BUFFER_Initialize(OUT PCONSOLE_SCREEN_BUFFER* Buffer, + IN OUT PCONSOLE Console, + IN PTEXTMODE_BUFFER_INFO TextModeInfo) +{ + NTSTATUS Status = STATUS_SUCCESS; + PTEXTMODE_SCREEN_BUFFER NewBuffer = NULL; + + if (Console == NULL || Buffer == NULL || TextModeInfo == NULL) + return STATUS_INVALID_PARAMETER; + + *Buffer = NULL; + + Status = CONSOLE_SCREEN_BUFFER_Initialize((PCONSOLE_SCREEN_BUFFER*)&NewBuffer, + Console, + sizeof(TEXTMODE_SCREEN_BUFFER)); + if (!NT_SUCCESS(Status)) return Status; + NewBuffer->Header.Type = TEXTMODE_BUFFER; + NewBuffer->Vtbl = &TextVtbl; + + NewBuffer->Buffer = ConsoleAllocHeap(HEAP_ZERO_MEMORY, + TextModeInfo->ScreenBufferSize.X * + TextModeInfo->ScreenBufferSize.Y * + sizeof(CHAR_INFO)); + if (NewBuffer->Buffer == NULL) + { + CONSOLE_SCREEN_BUFFER_Destroy((PCONSOLE_SCREEN_BUFFER)NewBuffer); + return STATUS_INSUFFICIENT_RESOURCES; + } + + NewBuffer->ScreenBufferSize = NewBuffer->OldScreenBufferSize + = TextModeInfo->ScreenBufferSize; + NewBuffer->ViewSize = NewBuffer->OldViewSize + = Console->ConsoleSize; + + NewBuffer->ViewOrigin.X = NewBuffer->ViewOrigin.Y = 0; + NewBuffer->VirtualY = 0; + + NewBuffer->CursorBlinkOn = NewBuffer->ForceCursorOff = FALSE; + NewBuffer->CursorInfo.bVisible = (TextModeInfo->IsCursorVisible && (TextModeInfo->CursorSize != 0)); + NewBuffer->CursorInfo.dwSize = min(max(TextModeInfo->CursorSize, 0), 100); + + NewBuffer->ScreenDefaultAttrib = TextModeInfo->ScreenAttrib; + NewBuffer->PopupDefaultAttrib = TextModeInfo->PopupAttrib; + + /* Initialize buffer to be empty with default attributes */ + for (NewBuffer->CursorPosition.Y = 0 ; NewBuffer->CursorPosition.Y < NewBuffer->ScreenBufferSize.Y; NewBuffer->CursorPosition.Y++) + { + ClearLineBuffer(NewBuffer); + } + NewBuffer->CursorPosition.X = NewBuffer->CursorPosition.Y = 0; + + NewBuffer->Mode = ENABLE_PROCESSED_OUTPUT | ENABLE_WRAP_AT_EOL_OUTPUT; + + *Buffer = (PCONSOLE_SCREEN_BUFFER)NewBuffer; + return STATUS_SUCCESS; +} + +VOID +TEXTMODE_BUFFER_Destroy(IN OUT PCONSOLE_SCREEN_BUFFER Buffer) +{ + PTEXTMODE_SCREEN_BUFFER Buff = (PTEXTMODE_SCREEN_BUFFER)Buffer; + + /* + * IMPORTANT !! Reinitialize the type so that we don't enter a recursive + * infinite loop when calling CONSOLE_SCREEN_BUFFER_Destroy. + */ + Buffer->Header.Type = SCREEN_BUFFER; + + ConsoleFreeHeap(Buff->Buffer); + + CONSOLE_SCREEN_BUFFER_Destroy(Buffer); +} + + +PCHAR_INFO +ConioCoordToPointer(PTEXTMODE_SCREEN_BUFFER Buff, ULONG X, ULONG Y) +{ + return &Buff->Buffer[((Y + Buff->VirtualY) % Buff->ScreenBufferSize.Y) * Buff->ScreenBufferSize.X + X]; +} + +static VOID FASTCALL +ClearLineBuffer(PTEXTMODE_SCREEN_BUFFER Buff) +{ + PCHAR_INFO Ptr = ConioCoordToPointer(Buff, 0, Buff->CursorPosition.Y); + SHORT Pos; + + for (Pos = 0; Pos < Buff->ScreenBufferSize.X; Pos++, Ptr++) + { + /* Fill the cell */ + Ptr->Char.UnicodeChar = L' '; + Ptr->Attributes = Buff->ScreenDefaultAttrib; + } +} + +static __inline BOOLEAN +ConioGetIntersection(OUT PSMALL_RECT Intersection, + IN PSMALL_RECT Rect1, + IN PSMALL_RECT Rect2) +{ + if ( ConioIsRectEmpty(Rect1) || + ConioIsRectEmpty(Rect2) || + (Rect1->Top > Rect2->Bottom) || + (Rect1->Left > Rect2->Right) || + (Rect1->Bottom < Rect2->Top) || + (Rect1->Right < Rect2->Left) ) + { + /* The rectangles do not intersect */ + ConioInitRect(Intersection, 0, -1, 0, -1); + return FALSE; + } + + ConioInitRect(Intersection, + max(Rect1->Top , Rect2->Top ), + max(Rect1->Left , Rect2->Left ), + min(Rect1->Bottom, Rect2->Bottom), + min(Rect1->Right , Rect2->Right )); + + return TRUE; +} + +static __inline BOOLEAN +ConioGetUnion(OUT PSMALL_RECT Union, + IN PSMALL_RECT Rect1, + IN PSMALL_RECT Rect2) +{ + if (ConioIsRectEmpty(Rect1)) + { + if (ConioIsRectEmpty(Rect2)) + { + ConioInitRect(Union, 0, -1, 0, -1); + return FALSE; + } + else + { + *Union = *Rect2; + } + } + else if (ConioIsRectEmpty(Rect2)) + { + *Union = *Rect1; + } + else + { + ConioInitRect(Union, + min(Rect1->Top , Rect2->Top ), + min(Rect1->Left , Rect2->Left ), + max(Rect1->Bottom, Rect2->Bottom), + max(Rect1->Right , Rect2->Right )); + } + + return TRUE; +} + +static VOID FASTCALL +ConioComputeUpdateRect(IN PTEXTMODE_SCREEN_BUFFER Buff, + IN OUT PSMALL_RECT UpdateRect, + IN PCOORD Start, + IN UINT Length) +{ + if (Buff->ScreenBufferSize.X <= Start->X + Length) + { + UpdateRect->Left = 0; + } + else + { + UpdateRect->Left = Start->X; + } + if (Buff->ScreenBufferSize.X <= Start->X + Length) + { + UpdateRect->Right = Buff->ScreenBufferSize.X - 1; + } + else + { + UpdateRect->Right = Start->X + Length - 1; + } + UpdateRect->Top = Start->Y; + UpdateRect->Bottom = Start->Y + (Start->X + Length - 1) / Buff->ScreenBufferSize.X; + if (Buff->ScreenBufferSize.Y <= UpdateRect->Bottom) + { + UpdateRect->Bottom = Buff->ScreenBufferSize.Y - 1; + } +} + +/* + * Move from one rectangle to another. We must be careful about the order that + * this is done, to avoid overwriting parts of the source before they are moved. + */ +static VOID FASTCALL +ConioMoveRegion(PTEXTMODE_SCREEN_BUFFER ScreenBuffer, + PSMALL_RECT SrcRegion, + PSMALL_RECT DstRegion, + PSMALL_RECT ClipRegion, + CHAR_INFO FillChar) +{ + int Width = ConioRectWidth(SrcRegion); + int Height = ConioRectHeight(SrcRegion); + int SX, SY; + int DX, DY; + int XDelta, YDelta; + int i, j; + + SY = SrcRegion->Top; + DY = DstRegion->Top; + YDelta = 1; + if (SY < DY) + { + /* Moving down: work from bottom up */ + SY = SrcRegion->Bottom; + DY = DstRegion->Bottom; + YDelta = -1; + } + for (i = 0; i < Height; i++) + { + PCHAR_INFO SRow = ConioCoordToPointer(ScreenBuffer, 0, SY); + PCHAR_INFO DRow = ConioCoordToPointer(ScreenBuffer, 0, DY); + + SX = SrcRegion->Left; + DX = DstRegion->Left; + XDelta = 1; + if (SX < DX) + { + /* Moving right: work from right to left */ + SX = SrcRegion->Right; + DX = DstRegion->Right; + XDelta = -1; + } + for (j = 0; j < Width; j++) + { + CHAR_INFO Cell = SRow[SX]; + if (SX >= ClipRegion->Left && SX <= ClipRegion->Right && + SY >= ClipRegion->Top && SY <= ClipRegion->Bottom) + { + SRow[SX] = FillChar; + } + if (DX >= ClipRegion->Left && DX <= ClipRegion->Right && + DY >= ClipRegion->Top && DY <= ClipRegion->Bottom) + { + DRow[DX] = Cell; + } + SX += XDelta; + DX += XDelta; + } + SY += YDelta; + DY += YDelta; + } +} + +DWORD FASTCALL +ConioEffectiveCursorSize(PCONSOLE 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; +} + +NTSTATUS +ConioResizeBuffer(PCONSOLE Console, + PTEXTMODE_SCREEN_BUFFER ScreenBuffer, + COORD Size) +{ + PCHAR_INFO Buffer; + DWORD Offset = 0; + PCHAR_INFO ptr; + WORD CurrentAttribute; + USHORT CurrentY; + PCHAR_INFO OldBuffer; + DWORD i; + DWORD diff; + + /* Buffer size is not allowed to be smaller than the view size */ + if (Size.X < ScreenBuffer->ViewSize.X || Size.Y < ScreenBuffer->ViewSize.Y) + return STATUS_INVALID_PARAMETER; + + if (Size.X == ScreenBuffer->ScreenBufferSize.X && Size.Y == ScreenBuffer->ScreenBufferSize.Y) + { + // FIXME: Trigger a buffer resize event ?? + return STATUS_SUCCESS; + } + + if (Console->FixedSize) + { + /* + * The console is in fixed-size mode, so we cannot resize anything + * at the moment. However, keep those settings somewhere so that + * we can try to set them up when we will be allowed to do so. + */ + ScreenBuffer->OldScreenBufferSize = Size; + return STATUS_NOT_SUPPORTED; // STATUS_SUCCESS + } + + Buffer = ConsoleAllocHeap(HEAP_ZERO_MEMORY, Size.X * Size.Y * sizeof(CHAR_INFO)); + if (!Buffer) return STATUS_NO_MEMORY; + + DPRINT1("Resizing (%d,%d) to (%d,%d)\n", ScreenBuffer->ScreenBufferSize.X, ScreenBuffer->ScreenBufferSize.Y, Size.X, Size.Y); + OldBuffer = ScreenBuffer->Buffer; + + for (CurrentY = 0; CurrentY < ScreenBuffer->ScreenBufferSize.Y && CurrentY < Size.Y; CurrentY++) + { + ptr = ConioCoordToPointer(ScreenBuffer, 0, CurrentY); + if (Size.X <= ScreenBuffer->ScreenBufferSize.X) + { + /* Reduce size */ + RtlCopyMemory(Buffer + Offset, ptr, Size.X * sizeof(CHAR_INFO)); + Offset += Size.X; + } + else + { + /* Enlarge size */ + RtlCopyMemory(Buffer + Offset, ptr, ScreenBuffer->ScreenBufferSize.X * sizeof(CHAR_INFO)); + Offset += ScreenBuffer->ScreenBufferSize.X; + + /* The attribute to be used is the one of the last cell of the current line */ + CurrentAttribute = ConioCoordToPointer(ScreenBuffer, + ScreenBuffer->ScreenBufferSize.X - 1, + CurrentY)->Attributes; + + diff = Size.X - ScreenBuffer->ScreenBufferSize.X; + + /* Zero-out the new part of the buffer */ + for (i = 0; i < diff; i++) + { + ptr = Buffer + Offset; + ptr->Char.UnicodeChar = L' '; + ptr->Attributes = CurrentAttribute; + ++Offset; + } + } + } + + if (Size.Y > ScreenBuffer->ScreenBufferSize.Y) + { + diff = Size.X * (Size.Y - ScreenBuffer->ScreenBufferSize.Y); + + /* Zero-out the new part of the buffer */ + for (i = 0; i < diff; i++) + { + ptr = Buffer + Offset; + ptr->Char.UnicodeChar = L' '; + ptr->Attributes = ScreenBuffer->ScreenDefaultAttrib; + ++Offset; + } + } + + (void)InterlockedExchangePointer((PVOID volatile*)&ScreenBuffer->Buffer, Buffer); + ConsoleFreeHeap(OldBuffer); + ScreenBuffer->ScreenBufferSize = ScreenBuffer->OldScreenBufferSize = Size; + ScreenBuffer->VirtualY = 0; + + /* Ensure cursor and window are within buffer */ + if (ScreenBuffer->CursorPosition.X >= Size.X) + ScreenBuffer->CursorPosition.X = Size.X - 1; + if (ScreenBuffer->CursorPosition.Y >= Size.Y) + ScreenBuffer->CursorPosition.Y = Size.Y - 1; + if (ScreenBuffer->ViewOrigin.X > Size.X - ScreenBuffer->ViewSize.X) + ScreenBuffer->ViewOrigin.X = Size.X - ScreenBuffer->ViewSize.X; + if (ScreenBuffer->ViewOrigin.Y > Size.Y - ScreenBuffer->ViewSize.Y) + ScreenBuffer->ViewOrigin.Y = Size.Y - ScreenBuffer->ViewSize.Y; + + /* + * Trigger a buffer resize event + */ + if (Console->InputBuffer.Mode & ENABLE_WINDOW_INPUT) + { + INPUT_RECORD er; + + er.EventType = WINDOW_BUFFER_SIZE_EVENT; + er.Event.WindowBufferSizeEvent.dwSize = ScreenBuffer->ScreenBufferSize; + + ConioProcessInputEvent(Console, &er); + } + + return STATUS_SUCCESS; +} + +static VOID FASTCALL +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; +} + +NTSTATUS +ConioWriteConsole(PCONSOLE Console, + PTEXTMODE_SCREEN_BUFFER Buff, + PWCHAR Buffer, + DWORD Length, + BOOL Attrib) +{ + 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) + { + ConioWriteStream(Console, &UpdateRect, CursorStartX, CursorStartY, + ScrolledLines, Buffer, Length); + } + + return STATUS_SUCCESS; +} + + +/* PUBLIC DRIVER APIS *********************************************************/ + +NTSTATUS NTAPI +ConDrvReadConsoleOutput(IN PCONSOLE Console, + IN PTEXTMODE_SCREEN_BUFFER Buffer, + IN BOOLEAN Unicode, + OUT PCHAR_INFO CharInfo/*Buffer*/, + IN PCOORD BufferSize, + IN PCOORD BufferCoord, + IN OUT PSMALL_RECT ReadRegion) +{ + PCHAR_INFO CurCharInfo; + SHORT SizeX, SizeY; + SMALL_RECT CapturedReadRegion; + SMALL_RECT ScreenRect; + DWORD i; + PCHAR_INFO Ptr; + LONG X, Y; + UINT CodePage; + + if (Console == NULL || Buffer == NULL || CharInfo == NULL || + BufferSize == NULL || BufferCoord == NULL || ReadRegion == NULL) + { + return STATUS_INVALID_PARAMETER; + } + + /* Validity check */ + ASSERT(Console == Buffer->Header.Console); + + CapturedReadRegion = *ReadRegion; + + /* FIXME: Is this correct? */ + CodePage = Console->OutputCodePage; + + SizeX = min(BufferSize->X - BufferCoord->X, ConioRectWidth(&CapturedReadRegion)); + SizeY = min(BufferSize->Y - BufferCoord->Y, ConioRectHeight(&CapturedReadRegion)); + CapturedReadRegion.Right = CapturedReadRegion.Left + SizeX; + CapturedReadRegion.Bottom = CapturedReadRegion.Top + SizeY; + + ConioInitRect(&ScreenRect, 0, 0, Buffer->ScreenBufferSize.Y, Buffer->ScreenBufferSize.X); + if (!ConioGetIntersection(&CapturedReadRegion, &ScreenRect, &CapturedReadRegion)) + { + return STATUS_SUCCESS; + } + + for (i = 0, Y = CapturedReadRegion.Top; Y < CapturedReadRegion.Bottom; ++i, ++Y) + { + CurCharInfo = CharInfo + (i * BufferSize->X); + + Ptr = ConioCoordToPointer(Buffer, CapturedReadRegion.Left, Y); + for (X = CapturedReadRegion.Left; X < CapturedReadRegion.Right; ++X) + { + if (Unicode) + { + CurCharInfo->Char.UnicodeChar = Ptr->Char.UnicodeChar; + } + else + { + // ConsoleUnicodeCharToAnsiChar(Console, &CurCharInfo->Char.AsciiChar, &Ptr->Char.UnicodeChar); + WideCharToMultiByte(CodePage, 0, &Ptr->Char.UnicodeChar, 1, + &CurCharInfo->Char.AsciiChar, 1, NULL, NULL); + } + CurCharInfo->Attributes = Ptr->Attributes; + ++Ptr; + ++CurCharInfo; + } + } + + ReadRegion->Left = CapturedReadRegion.Left; + ReadRegion->Top = CapturedReadRegion.Top ; + ReadRegion->Right = CapturedReadRegion.Left + SizeX - 1; + ReadRegion->Bottom = CapturedReadRegion.Top + SizeY - 1; + + return STATUS_SUCCESS; +} + +NTSTATUS NTAPI +ConDrvWriteConsoleOutput(IN PCONSOLE Console, + IN PTEXTMODE_SCREEN_BUFFER Buffer, + IN BOOLEAN Unicode, + IN PCHAR_INFO CharInfo/*Buffer*/, + IN PCOORD BufferSize, + IN PCOORD BufferCoord, + IN OUT PSMALL_RECT WriteRegion) +{ + SHORT i, X, Y, SizeX, SizeY; + SMALL_RECT ScreenBuffer; + PCHAR_INFO CurCharInfo; + SMALL_RECT CapturedWriteRegion; + PCHAR_INFO Ptr; + + if (Console == NULL || Buffer == NULL || CharInfo == NULL || + BufferSize == NULL || BufferCoord == NULL || WriteRegion == NULL) + { + return STATUS_INVALID_PARAMETER; + } + + /* Validity check */ + ASSERT(Console == Buffer->Header.Console); + + CapturedWriteRegion = *WriteRegion; + + SizeX = min(BufferSize->X - BufferCoord->X, ConioRectWidth(&CapturedWriteRegion)); + SizeY = min(BufferSize->Y - BufferCoord->Y, ConioRectHeight(&CapturedWriteRegion)); + CapturedWriteRegion.Right = CapturedWriteRegion.Left + SizeX - 1; + CapturedWriteRegion.Bottom = CapturedWriteRegion.Top + SizeY - 1; + + /* Make sure WriteRegion is inside the screen buffer */ + ConioInitRect(&ScreenBuffer, 0, 0, Buffer->ScreenBufferSize.Y - 1, Buffer->ScreenBufferSize.X - 1); + if (!ConioGetIntersection(&CapturedWriteRegion, &ScreenBuffer, &CapturedWriteRegion)) + { + /* + * It is okay to have a WriteRegion completely outside + * the screen buffer. No data is written then. + */ + return STATUS_SUCCESS; + } + + for (i = 0, Y = CapturedWriteRegion.Top; Y <= CapturedWriteRegion.Bottom; i++, Y++) + { + CurCharInfo = CharInfo + (i + BufferCoord->Y) * BufferSize->X + BufferCoord->X; + + Ptr = ConioCoordToPointer(Buffer, CapturedWriteRegion.Left, Y); + for (X = CapturedWriteRegion.Left; X <= CapturedWriteRegion.Right; X++) + { + if (Unicode) + { + Ptr->Char.UnicodeChar = CurCharInfo->Char.UnicodeChar; + } + else + { + ConsoleAnsiCharToUnicodeChar(Console, &Ptr->Char.UnicodeChar, &CurCharInfo->Char.AsciiChar); + } + Ptr->Attributes = CurCharInfo->Attributes; + ++Ptr; + ++CurCharInfo; + } + } + + ConioDrawRegion(Console, &CapturedWriteRegion); + + WriteRegion->Left = CapturedWriteRegion.Left; + WriteRegion->Top = CapturedWriteRegion.Top ; + WriteRegion->Right = CapturedWriteRegion.Left + SizeX - 1; + WriteRegion->Bottom = CapturedWriteRegion.Top + SizeY - 1; + + return STATUS_SUCCESS; +} + +NTSTATUS NTAPI +ConDrvWriteConsole(IN PCONSOLE Console, + IN PTEXTMODE_SCREEN_BUFFER ScreenBuffer, + IN BOOLEAN Unicode, + IN PVOID StringBuffer, + IN ULONG NumCharsToWrite, + OUT PULONG NumCharsWritten OPTIONAL) +{ + NTSTATUS Status = STATUS_SUCCESS; + PWCHAR Buffer = NULL; + ULONG Written = 0; + ULONG Length; + + if (Console == NULL || ScreenBuffer == NULL /* || StringBuffer == NULL */) + return STATUS_INVALID_PARAMETER; + + /* Validity checks */ + ASSERT(Console == ScreenBuffer->Header.Console); + ASSERT( (StringBuffer != NULL && NumCharsToWrite >= 0) || + (StringBuffer == NULL && NumCharsToWrite == 0) ); + + // if (Console->PauseFlags & (PAUSED_FROM_KEYBOARD | PAUSED_FROM_SCROLLBAR | PAUSED_FROM_SELECTION)) + if (Console->PauseFlags && Console->UnpauseEvent != NULL) + { + return STATUS_PENDING; + } + + if (Unicode) + { + Buffer = StringBuffer; + } + else + { + Length = MultiByteToWideChar(Console->OutputCodePage, 0, + (PCHAR)StringBuffer, + NumCharsToWrite, + NULL, 0); + Buffer = RtlAllocateHeap(RtlGetProcessHeap(), 0, Length * sizeof(WCHAR)); + if (Buffer) + { + MultiByteToWideChar(Console->OutputCodePage, 0, + (PCHAR)StringBuffer, + NumCharsToWrite, + (PWCHAR)Buffer, Length); + } + else + { + Status = STATUS_NO_MEMORY; + } + } + + if (Buffer) + { + if (NT_SUCCESS(Status)) + { + Status = ConioWriteConsole(Console, + ScreenBuffer, + Buffer, + NumCharsToWrite, + TRUE); + if (NT_SUCCESS(Status)) + { + Written = NumCharsToWrite; + } + } + + if (!Unicode) RtlFreeHeap(RtlGetProcessHeap(), 0, Buffer); + } + + if (NumCharsWritten) *NumCharsWritten = Written; + + return Status; +} + +NTSTATUS NTAPI +ConDrvReadConsoleOutputString(IN PCONSOLE Console, + IN PTEXTMODE_SCREEN_BUFFER Buffer, + IN CODE_TYPE CodeType, + OUT PVOID StringBuffer, + IN ULONG NumCodesToRead, + IN PCOORD ReadCoord, + OUT PCOORD EndCoord, + OUT PULONG CodesRead) +{ + SHORT Xpos, Ypos; + PVOID ReadBuffer; + ULONG i; + ULONG CodeSize; + PCHAR_INFO Ptr; + + if (Console == NULL || Buffer == NULL || + ReadCoord == NULL || EndCoord == NULL || CodesRead == NULL) + { + return STATUS_INVALID_PARAMETER; + } + + /* Validity checks */ + ASSERT(Console == Buffer->Header.Console); + ASSERT( (StringBuffer != NULL && NumCodesToRead >= 0) || + (StringBuffer == NULL && NumCodesToRead == 0) ); + + switch (CodeType) + { + case CODE_ASCII: + CodeSize = sizeof(CHAR); + break; + + case CODE_UNICODE: + CodeSize = sizeof(WCHAR); + break; + + case CODE_ATTRIBUTE: + CodeSize = sizeof(WORD); + break; + + default: + return STATUS_INVALID_PARAMETER; + } + + ReadBuffer = StringBuffer; + Xpos = ReadCoord->X; + Ypos = (ReadCoord->Y + Buffer->VirtualY) % Buffer->ScreenBufferSize.Y; + + /* + * MSDN (ReadConsoleOutputAttribute and ReadConsoleOutputCharacter) : + * + * If the number of attributes (resp. characters) to be read from extends + * beyond the end of the specified screen buffer row, attributes (resp. + * characters) are read from the next row. If the number of attributes + * (resp. characters) to be read from extends beyond the end of the console + * screen buffer, attributes (resp. characters) up to the end of the console + * screen buffer are read. + * + * TODO: Do NOT loop up to NumCodesToRead, but stop before + * if we are going to overflow... + */ + // Ptr = ConioCoordToPointer(Buffer, Xpos, Ypos); // Doesn't work + for (i = 0; i < min(NumCodesToRead, Buffer->ScreenBufferSize.X * Buffer->ScreenBufferSize.Y); ++i) + { + // Ptr = ConioCoordToPointer(Buffer, Xpos, Ypos); // Doesn't work either + Ptr = &Buffer->Buffer[Xpos + Ypos * Buffer->ScreenBufferSize.X]; + + switch (CodeType) + { + case CODE_ASCII: + ConsoleUnicodeCharToAnsiChar(Console, (PCHAR)ReadBuffer, &Ptr->Char.UnicodeChar); + break; + + case CODE_UNICODE: + *(PWCHAR)ReadBuffer = Ptr->Char.UnicodeChar; + break; + + case CODE_ATTRIBUTE: + *(PWORD)ReadBuffer = Ptr->Attributes; + break; + } + ReadBuffer = (PVOID)((ULONG_PTR)ReadBuffer + CodeSize); + // ++Ptr; + + Xpos++; + + if (Xpos == Buffer->ScreenBufferSize.X) + { + Xpos = 0; + Ypos++; + + if (Ypos == Buffer->ScreenBufferSize.Y) + { + Ypos = 0; + } + } + } + + // switch (CodeType) + // { + // case CODE_UNICODE: + // *(PWCHAR)ReadBuffer = 0; + // break; + + // case CODE_ASCII: + // *(PCHAR)ReadBuffer = 0; + // break; + + // case CODE_ATTRIBUTE: + // *(PWORD)ReadBuffer = 0; + // break; + // } + + EndCoord->X = Xpos; + EndCoord->Y = (Ypos - Buffer->VirtualY + Buffer->ScreenBufferSize.Y) % Buffer->ScreenBufferSize.Y; + + *CodesRead = (ULONG)((ULONG_PTR)ReadBuffer - (ULONG_PTR)StringBuffer) / CodeSize; + // <= NumCodesToRead + + return STATUS_SUCCESS; +} + +NTSTATUS NTAPI +ConDrvWriteConsoleOutputString(IN PCONSOLE Console, + IN PTEXTMODE_SCREEN_BUFFER Buffer, + IN CODE_TYPE CodeType, + IN PVOID StringBuffer, + IN ULONG NumCodesToWrite, + IN PCOORD WriteCoord /*, + OUT PCOORD EndCoord, + OUT PULONG CodesWritten */) +{ + NTSTATUS Status = STATUS_SUCCESS; + PVOID WriteBuffer = NULL; + PWCHAR tmpString = NULL; + DWORD X, Y, Length; // , Written = 0; + ULONG CodeSize; + SMALL_RECT UpdateRect; + PCHAR_INFO Ptr; + + if (Console == NULL || Buffer == NULL || + WriteCoord == NULL /* || EndCoord == NULL || CodesWritten == NULL */) + { + return STATUS_INVALID_PARAMETER; + } + + /* Validity checks */ + ASSERT(Console == Buffer->Header.Console); + ASSERT( (StringBuffer != NULL && NumCodesToWrite >= 0) || + (StringBuffer == NULL && NumCodesToWrite == 0) ); + + switch (CodeType) + { + case CODE_ASCII: + CodeSize = sizeof(CHAR); + break; + + case CODE_UNICODE: + CodeSize = sizeof(WCHAR); + break; + + case CODE_ATTRIBUTE: + CodeSize = sizeof(WORD); + break; + + default: + return STATUS_INVALID_PARAMETER; + } + + if (CodeType == CODE_ASCII) + { + /* Convert the ASCII string into Unicode before writing it to the console */ + Length = MultiByteToWideChar(Console->OutputCodePage, 0, + (PCHAR)StringBuffer, + NumCodesToWrite, + NULL, 0); + tmpString = WriteBuffer = RtlAllocateHeap(RtlGetProcessHeap(), 0, Length * sizeof(WCHAR)); + if (WriteBuffer) + { + MultiByteToWideChar(Console->OutputCodePage, 0, + (PCHAR)StringBuffer, + NumCodesToWrite, + (PWCHAR)WriteBuffer, Length); + } + else + { + Status = STATUS_NO_MEMORY; + } + } + else + { + /* For CODE_UNICODE or CODE_ATTRIBUTE, we are already OK */ + WriteBuffer = StringBuffer; + } + + if (WriteBuffer == NULL || !NT_SUCCESS(Status)) goto Cleanup; + + X = WriteCoord->X; + Y = (WriteCoord->Y + Buffer->VirtualY) % Buffer->ScreenBufferSize.Y; + Length = NumCodesToWrite; + // Ptr = ConioCoordToPointer(Buffer, X, Y); // Doesn't work + // Ptr = &Buffer->Buffer[X + Y * Buffer->ScreenBufferSize.X]; // May work + + while (Length--) + { + // Ptr = ConioCoordToPointer(Buffer, X, Y); // Doesn't work either + Ptr = &Buffer->Buffer[X + Y * Buffer->ScreenBufferSize.X]; + + switch (CodeType) + { + case CODE_ASCII: + case CODE_UNICODE: + Ptr->Char.UnicodeChar = *(PWCHAR)WriteBuffer; + break; + + case CODE_ATTRIBUTE: + Ptr->Attributes = *(PWORD)WriteBuffer; + break; + } + WriteBuffer = (PVOID)((ULONG_PTR)WriteBuffer + CodeSize); + // ++Ptr; + + // Written++; + if (++X == Buffer->ScreenBufferSize.X) + { + X = 0; + + if (++Y == Buffer->ScreenBufferSize.Y) + { + Y = 0; + } + } + } + + if ((PCONSOLE_SCREEN_BUFFER)Buffer == Console->ActiveBuffer) + { + ConioComputeUpdateRect(Buffer, &UpdateRect, WriteCoord, NumCodesToWrite); + ConioDrawRegion(Console, &UpdateRect); + } + + // EndCoord->X = X; + // EndCoord->Y = (Y + Buffer->ScreenBufferSize.Y - Buffer->VirtualY) % Buffer->ScreenBufferSize.Y; + +Cleanup: + if (tmpString) RtlFreeHeap(RtlGetProcessHeap(), 0, tmpString); + + // CodesWritten = Written; + return Status; +} + +NTSTATUS NTAPI +ConDrvFillConsoleOutput(IN PCONSOLE Console, + IN PTEXTMODE_SCREEN_BUFFER Buffer, + IN CODE_TYPE CodeType, + IN PVOID Code, + IN ULONG NumCodesToWrite, + IN PCOORD WriteCoord /*, + OUT PULONG CodesWritten */) +{ + DWORD X, Y, Length; // , Written = 0; + PCHAR_INFO Ptr; + SMALL_RECT UpdateRect; + + if (Console == NULL || Buffer == NULL || Code == NULL || + WriteCoord == NULL /* || CodesWritten == NULL */) + { + return STATUS_INVALID_PARAMETER; + } + + /* Validity check */ + ASSERT(Console == Buffer->Header.Console); + +#if 0 + switch (CodeType) + { + case CODE_ASCII: + /* On-place conversion from the ASCII char to the UNICODE char */ + ConsoleAnsiCharToUnicodeChar(Console, &Code->UnicodeChar, &Code->AsciiChar); + /* Fall through */ + case CODE_UNICODE: + Code = &Code->UnicodeChar; + break; + + case CODE_ATTRIBUTE: + Code = &Code->Attribute; + break; + } +#else + if (CodeType == CODE_ASCII) + { + /* On-place conversion from the ASCII char to the UNICODE char */ + // FIXME: What if Code points to an invalid memory zone ?? + ConsoleAnsiCharToUnicodeChar(Console, (PWCHAR)Code, (PCHAR)Code); + } +#endif + + X = WriteCoord->X; + Y = (WriteCoord->Y + Buffer->VirtualY) % Buffer->ScreenBufferSize.Y; + Length = NumCodesToWrite; + // Ptr = ConioCoordToPointer(Buffer, X, Y); // Doesn't work + // Ptr = &Buffer->Buffer[X + Y * Buffer->ScreenBufferSize.X]; // May work + + while (Length--) + { + // Ptr = ConioCoordToPointer(Buffer, X, Y); // Doesn't work either + Ptr = &Buffer->Buffer[X + Y * Buffer->ScreenBufferSize.X]; + + switch (CodeType) + { + case CODE_ASCII: + case CODE_UNICODE: + Ptr->Char.UnicodeChar = *(PWCHAR)Code; + break; + + case CODE_ATTRIBUTE: + Ptr->Attributes = *(PWORD)Code; + break; + } + // ++Ptr; + + // Written++; + if (++X == Buffer->ScreenBufferSize.X) + { + X = 0; + + if (++Y == Buffer->ScreenBufferSize.Y) + { + Y = 0; + } + } + } + + if ((PCONSOLE_SCREEN_BUFFER)Buffer == Console->ActiveBuffer) + { + ConioComputeUpdateRect(Buffer, &UpdateRect, WriteCoord, NumCodesToWrite); + ConioDrawRegion(Console, &UpdateRect); + } + + // CodesWritten = Written; // NumCodesToWrite; + return STATUS_SUCCESS; +} + +NTSTATUS NTAPI +ConDrvGetConsoleScreenBufferInfo(IN PCONSOLE Console, + IN PTEXTMODE_SCREEN_BUFFER Buffer, + OUT PCONSOLE_SCREEN_BUFFER_INFO ScreenBufferInfo) +{ + if (Console == NULL || Buffer == NULL || ScreenBufferInfo == NULL) + return STATUS_INVALID_PARAMETER; + + /* Validity check */ + ASSERT(Console == Buffer->Header.Console); + + ScreenBufferInfo->dwSize = Buffer->ScreenBufferSize; + ScreenBufferInfo->dwCursorPosition = Buffer->CursorPosition; + ScreenBufferInfo->wAttributes = Buffer->ScreenDefaultAttrib; + ScreenBufferInfo->srWindow.Left = Buffer->ViewOrigin.X; + ScreenBufferInfo->srWindow.Top = Buffer->ViewOrigin.Y; + ScreenBufferInfo->srWindow.Right = Buffer->ViewOrigin.X + Buffer->ViewSize.X - 1; + ScreenBufferInfo->srWindow.Bottom = Buffer->ViewOrigin.Y + Buffer->ViewSize.Y - 1; + + // FIXME: Refine the computation + ScreenBufferInfo->dwMaximumWindowSize = Buffer->ScreenBufferSize; + + return STATUS_SUCCESS; +} + +NTSTATUS NTAPI +ConDrvSetConsoleTextAttribute(IN PCONSOLE Console, + IN PTEXTMODE_SCREEN_BUFFER Buffer, + IN WORD Attribute) +{ + if (Console == NULL || Buffer == NULL) + return STATUS_INVALID_PARAMETER; + + /* Validity check */ + ASSERT(Console == Buffer->Header.Console); + + Buffer->ScreenDefaultAttrib = Attribute; + return STATUS_SUCCESS; +} + +NTSTATUS NTAPI +ConDrvSetConsoleScreenBufferSize(IN PCONSOLE Console, + IN PTEXTMODE_SCREEN_BUFFER Buffer, + IN PCOORD Size) +{ + NTSTATUS Status; + + if (Console == NULL || Buffer == NULL || Size == NULL) + return STATUS_INVALID_PARAMETER; + + /* Validity check */ + ASSERT(Console == Buffer->Header.Console); + + Status = ConioResizeBuffer(Console, Buffer, *Size); + if (NT_SUCCESS(Status)) ConioResizeTerminal(Console); + + return Status; +} + +NTSTATUS NTAPI +ConDrvScrollConsoleScreenBuffer(IN PCONSOLE Console, + IN PTEXTMODE_SCREEN_BUFFER Buffer, + IN BOOLEAN Unicode, + IN PSMALL_RECT ScrollRectangle, + IN BOOLEAN UseClipRectangle, + IN PSMALL_RECT ClipRectangle OPTIONAL, + IN PCOORD DestinationOrigin, + IN CHAR_INFO FillChar) +{ + COORD CapturedDestinationOrigin; + SMALL_RECT ScreenBuffer; + SMALL_RECT SrcRegion; + SMALL_RECT DstRegion; + SMALL_RECT UpdateRegion; + SMALL_RECT CapturedClipRectangle; + + if (Console == NULL || Buffer == NULL || ScrollRectangle == NULL || + (UseClipRectangle ? ClipRectangle == NULL : FALSE) || DestinationOrigin == NULL) + { + return STATUS_INVALID_PARAMETER; + } + + /* Validity check */ + ASSERT(Console == Buffer->Header.Console); + + CapturedDestinationOrigin = *DestinationOrigin; + + /* Make sure the source rectangle is inside the screen buffer */ + ConioInitRect(&ScreenBuffer, 0, 0, Buffer->ScreenBufferSize.Y - 1, Buffer->ScreenBufferSize.X - 1); + if (!ConioGetIntersection(&SrcRegion, &ScreenBuffer, ScrollRectangle)) + { + return STATUS_SUCCESS; + } + + /* If the source was clipped on the left or top, adjust the destination accordingly */ + if (ScrollRectangle->Left < 0) + { + CapturedDestinationOrigin.X -= ScrollRectangle->Left; + } + if (ScrollRectangle->Top < 0) + { + CapturedDestinationOrigin.Y -= ScrollRectangle->Top; + } + + if (UseClipRectangle) + { + CapturedClipRectangle = *ClipRectangle; + if (!ConioGetIntersection(&CapturedClipRectangle, &CapturedClipRectangle, &ScreenBuffer)) + { + return STATUS_SUCCESS; + } + } + else + { + CapturedClipRectangle = ScreenBuffer; + } + + ConioInitRect(&DstRegion, + CapturedDestinationOrigin.Y, + CapturedDestinationOrigin.X, + CapturedDestinationOrigin.Y + ConioRectHeight(&SrcRegion) - 1, + CapturedDestinationOrigin.X + ConioRectWidth(&SrcRegion ) - 1); + + if (!Unicode) + ConsoleAnsiCharToUnicodeChar(Console, &FillChar.Char.UnicodeChar, &FillChar.Char.AsciiChar); + + ConioMoveRegion(Buffer, &SrcRegion, &DstRegion, &CapturedClipRectangle, FillChar); + + if ((PCONSOLE_SCREEN_BUFFER)Buffer == Console->ActiveBuffer) + { + ConioGetUnion(&UpdateRegion, &SrcRegion, &DstRegion); + if (ConioGetIntersection(&UpdateRegion, &UpdateRegion, &CapturedClipRectangle)) + { + /* Draw update region */ + ConioDrawRegion(Console, &UpdateRegion); + } + } + + return STATUS_SUCCESS; +} + +NTSTATUS NTAPI +ConDrvSetConsoleWindowInfo(IN PCONSOLE Console, + IN PTEXTMODE_SCREEN_BUFFER Buffer, + IN BOOLEAN Absolute, + IN PSMALL_RECT WindowRect) +{ + SMALL_RECT CapturedWindowRect; + + if (Console == NULL || Buffer == NULL || WindowRect == NULL) + return STATUS_INVALID_PARAMETER; + + /* Validity check */ + ASSERT(Console == Buffer->Header.Console); + + CapturedWindowRect = *WindowRect; + + if (Absolute == FALSE) + { + /* Relative positions given. Transform them to absolute ones */ + CapturedWindowRect.Left += Buffer->ViewOrigin.X; + CapturedWindowRect.Top += Buffer->ViewOrigin.Y; + CapturedWindowRect.Right += Buffer->ViewOrigin.X + Buffer->ViewSize.X - 1; + CapturedWindowRect.Bottom += Buffer->ViewOrigin.Y + Buffer->ViewSize.Y - 1; + } + + /* See MSDN documentation on SetConsoleWindowInfo about the performed checks */ + if ( (CapturedWindowRect.Left < 0) || (CapturedWindowRect.Top < 0) || + (CapturedWindowRect.Right >= Buffer->ScreenBufferSize.X) || + (CapturedWindowRect.Bottom >= Buffer->ScreenBufferSize.Y) || + (CapturedWindowRect.Right <= CapturedWindowRect.Left) || + (CapturedWindowRect.Bottom <= CapturedWindowRect.Top) ) + { + return STATUS_INVALID_PARAMETER; + } + + Buffer->ViewOrigin.X = CapturedWindowRect.Left; + Buffer->ViewOrigin.Y = CapturedWindowRect.Top; + + Buffer->ViewSize.X = CapturedWindowRect.Right - CapturedWindowRect.Left + 1; + Buffer->ViewSize.Y = CapturedWindowRect.Bottom - CapturedWindowRect.Top + 1; + + return STATUS_SUCCESS; +} + +/* EOF */ diff --git a/reactos/win32ss/user/winsrv/consrv_new/coninput.c b/reactos/win32ss/user/winsrv/consrv_new/coninput.c new file mode 100644 index 00000000000..7143a322c5f --- /dev/null +++ b/reactos/win32ss/user/winsrv/consrv_new/coninput.c @@ -0,0 +1,479 @@ +/* + * COPYRIGHT: See COPYING in the top level directory + * PROJECT: ReactOS Console Server DLL + * FILE: win32ss/user/winsrv/consrv/coninput.c + * PURPOSE: Console Input functions + * PROGRAMMERS: Jeffrey Morlan + * Hermes Belusca-Maito (hermes.belusca@sfr.fr) + */ + +/* INCLUDES *******************************************************************/ + +#include "consrv.h" +#include "include/conio.h" +#include "include/conio2.h" +#include "handle.h" +#include "lineinput.h" + +#define NDEBUG +#include + + +/* GLOBALS ********************************************************************/ + +#define ConSrvGetInputBuffer(ProcessData, Handle, Ptr, Access, LockConsole) \ + ConSrvGetObject((ProcessData), (Handle), (PCONSOLE_IO_OBJECT*)(Ptr), NULL, \ + (Access), (LockConsole), INPUT_BUFFER) +#define ConSrvGetInputBufferAndHandleEntry(ProcessData, Handle, Ptr, Entry, Access, LockConsole) \ + ConSrvGetObject((ProcessData), (Handle), (PCONSOLE_IO_OBJECT*)(Ptr), (Entry), \ + (Access), (LockConsole), INPUT_BUFFER) +#define ConSrvReleaseInputBuffer(Buff, IsConsoleLocked) \ + ConSrvReleaseObject(&(Buff)->Header, (IsConsoleLocked)) + + +typedef struct _GET_INPUT_INFO +{ + PCSR_THREAD CallingThread; // The thread which called the input API. + PVOID HandleEntry; // The handle data associated with the wait thread. + PCONSOLE_INPUT_BUFFER InputBuffer; // The input buffer corresponding to the handle. +} GET_INPUT_INFO, *PGET_INPUT_INFO; + + +/* PRIVATE FUNCTIONS **********************************************************/ + +static NTSTATUS +WaitBeforeReading(IN PGET_INPUT_INFO InputInfo, + IN PCSR_API_MESSAGE ApiMessage, + IN CSR_WAIT_FUNCTION WaitFunction OPTIONAL, + IN BOOL CreateWaitBlock OPTIONAL) +{ + if (CreateWaitBlock) + { + PGET_INPUT_INFO CapturedInputInfo; + + CapturedInputInfo = ConsoleAllocHeap(0, sizeof(GET_INPUT_INFO)); + if (!CapturedInputInfo) return STATUS_NO_MEMORY; + + RtlMoveMemory(CapturedInputInfo, InputInfo, sizeof(GET_INPUT_INFO)); + + if (!CsrCreateWait(&InputInfo->InputBuffer->ReadWaitQueue, + WaitFunction, + InputInfo->CallingThread, + ApiMessage, + CapturedInputInfo, + NULL)) + { + ConsoleFreeHeap(CapturedInputInfo); + return STATUS_NO_MEMORY; + } + } + + /* Wait for input */ + return STATUS_PENDING; +} + +static NTSTATUS +ReadChars(IN PGET_INPUT_INFO InputInfo, + IN PCSR_API_MESSAGE ApiMessage, + IN BOOL CreateWaitBlock OPTIONAL); + +// Wait function CSR_WAIT_FUNCTION +static BOOLEAN +ReadCharsThread(IN PLIST_ENTRY WaitList, + IN PCSR_THREAD WaitThread, + IN PCSR_API_MESSAGE WaitApiMessage, + IN PVOID WaitContext, + IN PVOID WaitArgument1, + IN PVOID WaitArgument2, + IN ULONG WaitFlags) +{ + NTSTATUS Status; + PGET_INPUT_INFO InputInfo = (PGET_INPUT_INFO)WaitContext; + + PVOID InputHandle = WaitArgument2; + + DPRINT("ReadCharsThread - WaitContext = 0x%p, WaitArgument1 = 0x%p, WaitArgument2 = 0x%p, WaitFlags = %lu\n", WaitContext, WaitArgument1, WaitArgument2, WaitFlags); + + /* + * If we are notified of the process termination via a call + * to CsrNotifyWaitBlock triggered by CsrDestroyProcess or + * CsrDestroyThread, just return. + */ + if (WaitFlags & CsrProcessTerminating) + { + Status = STATUS_THREAD_IS_TERMINATING; + goto Quit; + } + + /* + * Somebody is closing a handle to this input buffer, + * by calling ConSrvCloseHandleEntry. + * See whether we are linked to that handle (ie. we + * are a waiter for this handle), and if so, return. + * Otherwise, ignore the call and continue waiting. + */ + if (InputHandle != NULL) + { + Status = (InputHandle == InputInfo->HandleEntry ? STATUS_ALERTED + : STATUS_PENDING); + goto Quit; + } + + /* + * If we go there, that means we are notified for some new input. + * The console is therefore already locked. + */ + Status = ReadChars(InputInfo, + WaitApiMessage, + FALSE); + +Quit: + if (Status != STATUS_PENDING) + { + WaitApiMessage->Status = Status; + ConsoleFreeHeap(InputInfo); + } + + return (Status == STATUS_PENDING ? FALSE : TRUE); +} + +NTSTATUS NTAPI +ConDrvReadConsole(IN PCONSOLE Console, + IN PCONSOLE_INPUT_BUFFER InputBuffer, + IN BOOLEAN Unicode, + OUT PVOID Buffer, + IN OUT PCONSOLE_READCONSOLE_CONTROL ReadControl, + IN ULONG NumCharsToRead, + OUT PULONG NumCharsRead OPTIONAL); +static NTSTATUS +ReadChars(IN PGET_INPUT_INFO InputInfo, + IN PCSR_API_MESSAGE ApiMessage, + IN BOOL CreateWaitBlock OPTIONAL) +{ + NTSTATUS Status; + PCONSOLE_READCONSOLE ReadConsoleRequest = &((PCONSOLE_API_MESSAGE)ApiMessage)->Data.ReadConsoleRequest; + PCONSOLE_INPUT_BUFFER InputBuffer = InputInfo->InputBuffer; + CONSOLE_READCONSOLE_CONTROL ReadControl; + + ReadControl.nLength = sizeof(CONSOLE_READCONSOLE_CONTROL); + ReadControl.nInitialChars = ReadConsoleRequest->NrCharactersRead; + ReadControl.dwCtrlWakeupMask = ReadConsoleRequest->CtrlWakeupMask; + ReadControl.dwControlKeyState = ReadConsoleRequest->ControlKeyState; + + Status = ConDrvReadConsole(InputBuffer->Header.Console, + InputBuffer, + ReadConsoleRequest->Unicode, + ReadConsoleRequest->Buffer, + &ReadControl, + ReadConsoleRequest->NrCharactersToRead, + &ReadConsoleRequest->NrCharactersRead); + + ReadConsoleRequest->ControlKeyState = ReadControl.dwControlKeyState; + + if (Status == STATUS_PENDING) + { + /* We haven't completed a read, so start a wait */ + return WaitBeforeReading(InputInfo, + ApiMessage, + ReadCharsThread, + CreateWaitBlock); + } + else + { + /* We read all what we wanted, we return the error code we were given */ + return Status; + // return STATUS_SUCCESS; + } +} + +static NTSTATUS +ReadInputBuffer(IN PGET_INPUT_INFO InputInfo, + IN BOOL Wait, + IN PCSR_API_MESSAGE ApiMessage, + IN BOOL CreateWaitBlock OPTIONAL); + +// Wait function CSR_WAIT_FUNCTION +static BOOLEAN +ReadInputBufferThread(IN PLIST_ENTRY WaitList, + IN PCSR_THREAD WaitThread, + IN PCSR_API_MESSAGE WaitApiMessage, + IN PVOID WaitContext, + IN PVOID WaitArgument1, + IN PVOID WaitArgument2, + IN ULONG WaitFlags) +{ + NTSTATUS Status; + PCONSOLE_GETINPUT GetInputRequest = &((PCONSOLE_API_MESSAGE)WaitApiMessage)->Data.GetInputRequest; + PGET_INPUT_INFO InputInfo = (PGET_INPUT_INFO)WaitContext; + + PVOID InputHandle = WaitArgument2; + + DPRINT("ReadInputBufferThread - WaitContext = 0x%p, WaitArgument1 = 0x%p, WaitArgument2 = 0x%p, WaitFlags = %lu\n", WaitContext, WaitArgument1, WaitArgument2, WaitFlags); + + /* + * If we are notified of the process termination via a call + * to CsrNotifyWaitBlock triggered by CsrDestroyProcess or + * CsrDestroyThread, just return. + */ + if (WaitFlags & CsrProcessTerminating) + { + Status = STATUS_THREAD_IS_TERMINATING; + goto Quit; + } + + /* + * Somebody is closing a handle to this input buffer, + * by calling ConSrvCloseHandleEntry. + * See whether we are linked to that handle (ie. we + * are a waiter for this handle), and if so, return. + * Otherwise, ignore the call and continue waiting. + */ + if (InputHandle != NULL) + { + Status = (InputHandle == InputInfo->HandleEntry ? STATUS_ALERTED + : STATUS_PENDING); + goto Quit; + } + + /* + * If we go there, that means we are notified for some new input. + * The console is therefore already locked. + */ + Status = ReadInputBuffer(InputInfo, + GetInputRequest->bRead, + WaitApiMessage, + FALSE); + +Quit: + if (Status != STATUS_PENDING) + { + WaitApiMessage->Status = Status; + ConsoleFreeHeap(InputInfo); + } + + return (Status == STATUS_PENDING ? FALSE : TRUE); +} + +NTSTATUS NTAPI +ConDrvGetConsoleInput(IN PCONSOLE Console, + IN PCONSOLE_INPUT_BUFFER InputBuffer, + IN BOOLEAN WaitForMoreEvents, + IN BOOLEAN Unicode, + OUT PINPUT_RECORD InputRecord, + IN ULONG NumEventsToRead, + OUT PULONG NumEventsRead); +static NTSTATUS +ReadInputBuffer(IN PGET_INPUT_INFO InputInfo, + IN BOOL Wait, // TRUE --> Read ; FALSE --> Peek + IN PCSR_API_MESSAGE ApiMessage, + IN BOOL CreateWaitBlock OPTIONAL) +{ + NTSTATUS Status; + PCONSOLE_GETINPUT GetInputRequest = &((PCONSOLE_API_MESSAGE)ApiMessage)->Data.GetInputRequest; + PCONSOLE_INPUT_BUFFER InputBuffer = InputInfo->InputBuffer; + + // GetInputRequest->InputsRead = 0; + + Status = ConDrvGetConsoleInput(InputBuffer->Header.Console, + InputBuffer, + Wait, + GetInputRequest->Unicode, + GetInputRequest->InputRecord, + GetInputRequest->Length, + &GetInputRequest->InputsRead); + + if (Status == STATUS_PENDING) + { + /* We haven't completed a read, so start a wait */ + return WaitBeforeReading(InputInfo, + ApiMessage, + ReadInputBufferThread, + CreateWaitBlock); + } + else + { + /* We read all what we wanted, we return the error code we were given */ + return Status; + // return STATUS_SUCCESS; + } +} + + +/* PUBLIC SERVER APIS *********************************************************/ + +CSR_API(SrvReadConsole) +{ + NTSTATUS Status; + PCONSOLE_READCONSOLE ReadConsoleRequest = &((PCONSOLE_API_MESSAGE)ApiMessage)->Data.ReadConsoleRequest; + PCONSOLE_PROCESS_DATA ProcessData = ConsoleGetPerProcessData(CsrGetClientThread()->Process); + PVOID HandleEntry; + PCONSOLE_INPUT_BUFFER InputBuffer; + GET_INPUT_INFO InputInfo; + + DPRINT("SrvReadConsole\n"); + + if (!CsrValidateMessageBuffer(ApiMessage, + (PVOID*)&ReadConsoleRequest->Buffer, + ReadConsoleRequest->BufferSize, + sizeof(BYTE))) + { + return STATUS_INVALID_PARAMETER; + } + + if (ReadConsoleRequest->NrCharactersRead > ReadConsoleRequest->NrCharactersToRead) + { + return STATUS_INVALID_PARAMETER; + } + + Status = ConSrvGetInputBufferAndHandleEntry(ProcessData, ReadConsoleRequest->InputHandle, &InputBuffer, &HandleEntry, GENERIC_READ, TRUE); + if (!NT_SUCCESS(Status)) return Status; + + // This member is set by the caller (IntReadConsole in kernel32) + // ReadConsoleRequest->NrCharactersRead = 0; + + InputInfo.CallingThread = CsrGetClientThread(); + InputInfo.HandleEntry = HandleEntry; + InputInfo.InputBuffer = InputBuffer; + + Status = ReadChars(&InputInfo, + ApiMessage, + TRUE); + + ConSrvReleaseInputBuffer(InputBuffer, TRUE); + + if (Status == STATUS_PENDING) *ReplyCode = CsrReplyPending; + + return Status; +} + +CSR_API(SrvGetConsoleInput) +{ + NTSTATUS Status; + PCONSOLE_GETINPUT GetInputRequest = &((PCONSOLE_API_MESSAGE)ApiMessage)->Data.GetInputRequest; + PCONSOLE_PROCESS_DATA ProcessData = ConsoleGetPerProcessData(CsrGetClientThread()->Process); + PVOID HandleEntry; + PCONSOLE_INPUT_BUFFER InputBuffer; + GET_INPUT_INFO InputInfo; + + DPRINT("SrvGetConsoleInput\n"); + + if (!CsrValidateMessageBuffer(ApiMessage, + (PVOID*)&GetInputRequest->InputRecord, + GetInputRequest->Length, + sizeof(INPUT_RECORD))) + { + return STATUS_INVALID_PARAMETER; + } + + Status = ConSrvGetInputBufferAndHandleEntry(ProcessData, GetInputRequest->InputHandle, &InputBuffer, &HandleEntry, GENERIC_READ, TRUE); + if (!NT_SUCCESS(Status)) return Status; + + GetInputRequest->InputsRead = 0; + + InputInfo.CallingThread = CsrGetClientThread(); + InputInfo.HandleEntry = HandleEntry; + InputInfo.InputBuffer = InputBuffer; + + Status = ReadInputBuffer(&InputInfo, + GetInputRequest->bRead, + ApiMessage, + TRUE); + + ConSrvReleaseInputBuffer(InputBuffer, TRUE); + + if (Status == STATUS_PENDING) *ReplyCode = CsrReplyPending; + + return Status; +} + +NTSTATUS NTAPI +ConDrvWriteConsoleInput(IN PCONSOLE Console, + IN PCONSOLE_INPUT_BUFFER InputBuffer, + IN BOOLEAN Unicode, + IN PINPUT_RECORD InputRecord, + IN ULONG NumEventsToWrite, + OUT PULONG NumEventsWritten OPTIONAL); +CSR_API(SrvWriteConsoleInput) +{ + NTSTATUS Status; + PCONSOLE_WRITEINPUT WriteInputRequest = &((PCONSOLE_API_MESSAGE)ApiMessage)->Data.WriteInputRequest; + PCONSOLE_INPUT_BUFFER InputBuffer; + ULONG NumEventsWritten; + + DPRINT("SrvWriteConsoleInput\n"); + + if (!CsrValidateMessageBuffer(ApiMessage, + (PVOID*)&WriteInputRequest->InputRecord, + WriteInputRequest->Length, + sizeof(INPUT_RECORD))) + { + return STATUS_INVALID_PARAMETER; + } + + Status = ConSrvGetInputBuffer(ConsoleGetPerProcessData(CsrGetClientThread()->Process), + WriteInputRequest->InputHandle, + &InputBuffer, GENERIC_WRITE, TRUE); + if (!NT_SUCCESS(Status)) return Status; + + NumEventsWritten = 0; + Status = ConDrvWriteConsoleInput(InputBuffer->Header.Console, + InputBuffer, + WriteInputRequest->Unicode, + WriteInputRequest->InputRecord, + WriteInputRequest->Length, + &NumEventsWritten); + WriteInputRequest->Length = NumEventsWritten; + + ConSrvReleaseInputBuffer(InputBuffer, TRUE); + return Status; +} + +NTSTATUS NTAPI +ConDrvFlushConsoleInputBuffer(IN PCONSOLE Console, + IN PCONSOLE_INPUT_BUFFER InputBuffer); +CSR_API(SrvFlushConsoleInputBuffer) +{ + NTSTATUS Status; + PCONSOLE_FLUSHINPUTBUFFER FlushInputBufferRequest = &((PCONSOLE_API_MESSAGE)ApiMessage)->Data.FlushInputBufferRequest; + PCONSOLE_INPUT_BUFFER InputBuffer; + + DPRINT("SrvFlushConsoleInputBuffer\n"); + + Status = ConSrvGetInputBuffer(ConsoleGetPerProcessData(CsrGetClientThread()->Process), + FlushInputBufferRequest->InputHandle, + &InputBuffer, GENERIC_WRITE, TRUE); + if (!NT_SUCCESS(Status)) return Status; + + Status = ConDrvFlushConsoleInputBuffer(InputBuffer->Header.Console, + InputBuffer); + + ConSrvReleaseInputBuffer(InputBuffer, TRUE); + return Status; +} + +NTSTATUS NTAPI +ConDrvGetConsoleNumberOfInputEvents(IN PCONSOLE Console, + IN PCONSOLE_INPUT_BUFFER InputBuffer, + OUT PULONG NumEvents); +CSR_API(SrvGetConsoleNumberOfInputEvents) +{ + NTSTATUS Status; + PCONSOLE_GETNUMINPUTEVENTS GetNumInputEventsRequest = &((PCONSOLE_API_MESSAGE)ApiMessage)->Data.GetNumInputEventsRequest; + PCONSOLE_INPUT_BUFFER InputBuffer; + + DPRINT("SrvGetConsoleNumberOfInputEvents\n"); + + Status = ConSrvGetInputBuffer(ConsoleGetPerProcessData(CsrGetClientThread()->Process), + GetNumInputEventsRequest->InputHandle, + &InputBuffer, GENERIC_READ, TRUE); + if (!NT_SUCCESS(Status)) return Status; + + Status = ConDrvGetConsoleNumberOfInputEvents(InputBuffer->Header.Console, + InputBuffer, + &GetNumInputEventsRequest->NumInputEvents); + + ConSrvReleaseInputBuffer(InputBuffer, TRUE); + return Status; +} + +/* EOF */ diff --git a/reactos/win32ss/user/winsrv/consrv_new/coninput.h b/reactos/win32ss/user/winsrv/consrv_new/coninput.h new file mode 100644 index 00000000000..43e4f54e2b8 --- /dev/null +++ b/reactos/win32ss/user/winsrv/consrv_new/coninput.h @@ -0,0 +1,23 @@ +/* + * COPYRIGHT: See COPYING in the top level directory + * PROJECT: ReactOS Console Server DLL + * FILE: win32ss/user/winsrv/consrv/coninput.h + * PURPOSE: Console Input functions + * PROGRAMMERS: Jeffrey Morlan + * Hermes Belusca-Maito (hermes.belusca@sfr.fr) + */ + +#pragma once + +VOID FASTCALL PurgeInputBuffer(PCONSOLE Console); + +VOID NTAPI +ConDrvProcessKey(IN PCONSOLE Console, + IN BOOLEAN Down, + IN UINT VirtualKeyCode, + IN UINT VirtualScanCode, + IN WCHAR UnicodeChar, + IN ULONG ShiftState, + IN BYTE KeyStateCtrl); + +/* EOF */ diff --git a/reactos/win32ss/user/winsrv/consrv_new/conoutput.c b/reactos/win32ss/user/winsrv/consrv_new/conoutput.c new file mode 100644 index 00000000000..223f6caf781 --- /dev/null +++ b/reactos/win32ss/user/winsrv/consrv_new/conoutput.c @@ -0,0 +1,793 @@ +/* + * COPYRIGHT: See COPYING in the top level directory + * PROJECT: ReactOS Console Server DLL + * FILE: win32ss/user/winsrv/consrv/conoutput.c + * PURPOSE: General Console Output Functions + * PROGRAMMERS: Jeffrey Morlan + * Hermes Belusca-Maito (hermes.belusca@sfr.fr) + */ + +/* INCLUDES *******************************************************************/ + +#include "consrv.h" +#include "console.h" +#include "include/conio.h" +#include "include/conio2.h" +#include "conoutput.h" +#include "handle.h" + +#define NDEBUG +#include + + +/* PRIVATE FUNCTIONS **********************************************************/ + + +/* PUBLIC SERVER APIS *********************************************************/ + +NTSTATUS NTAPI +ConDrvInvalidateBitMapRect(IN PCONSOLE Console, + IN PCONSOLE_SCREEN_BUFFER Buffer, + IN PSMALL_RECT Region); +CSR_API(SrvInvalidateBitMapRect) +{ + NTSTATUS Status; + PCONSOLE_INVALIDATEDIBITS InvalidateDIBitsRequest = &((PCONSOLE_API_MESSAGE)ApiMessage)->Data.InvalidateDIBitsRequest; + PCONSOLE_SCREEN_BUFFER Buffer; + + DPRINT("SrvInvalidateBitMapRect\n"); + + Status = ConSrvGetScreenBuffer(ConsoleGetPerProcessData(CsrGetClientThread()->Process), + InvalidateDIBitsRequest->OutputHandle, + &Buffer, GENERIC_READ, TRUE); + if (!NT_SUCCESS(Status)) return Status; + + Status = ConDrvInvalidateBitMapRect(Buffer->Header.Console, + Buffer, + &InvalidateDIBitsRequest->Region); + + ConSrvReleaseScreenBuffer(Buffer, TRUE); + return Status; +} + +NTSTATUS NTAPI +ConDrvGetConsoleCursorInfo(IN PCONSOLE Console, + IN PTEXTMODE_SCREEN_BUFFER Buffer, + OUT PCONSOLE_CURSOR_INFO CursorInfo); +CSR_API(SrvGetConsoleCursorInfo) +{ + NTSTATUS Status; + PCONSOLE_GETSETCURSORINFO CursorInfoRequest = &((PCONSOLE_API_MESSAGE)ApiMessage)->Data.CursorInfoRequest; + PTEXTMODE_SCREEN_BUFFER Buffer; + + DPRINT("SrvGetConsoleCursorInfo\n"); + + Status = ConSrvGetTextModeBuffer(ConsoleGetPerProcessData(CsrGetClientThread()->Process), + CursorInfoRequest->OutputHandle, + &Buffer, GENERIC_READ, TRUE); + if (!NT_SUCCESS(Status)) return Status; + + Status = ConDrvGetConsoleCursorInfo(Buffer->Header.Console, + Buffer, + &CursorInfoRequest->Info); + + ConSrvReleaseScreenBuffer(Buffer, TRUE); + return Status; +} + +NTSTATUS NTAPI +ConDrvSetConsoleCursorInfo(IN PCONSOLE Console, + IN PTEXTMODE_SCREEN_BUFFER Buffer, + IN PCONSOLE_CURSOR_INFO CursorInfo); +CSR_API(SrvSetConsoleCursorInfo) +{ + NTSTATUS Status; + PCONSOLE_GETSETCURSORINFO CursorInfoRequest = &((PCONSOLE_API_MESSAGE)ApiMessage)->Data.CursorInfoRequest; + PTEXTMODE_SCREEN_BUFFER Buffer; + + DPRINT("SrvSetConsoleCursorInfo\n"); + + Status = ConSrvGetTextModeBuffer(ConsoleGetPerProcessData(CsrGetClientThread()->Process), + CursorInfoRequest->OutputHandle, + &Buffer, GENERIC_WRITE, TRUE); + if (!NT_SUCCESS(Status)) return Status; + + Status = ConDrvSetConsoleCursorInfo(Buffer->Header.Console, + Buffer, + &CursorInfoRequest->Info); + + ConSrvReleaseScreenBuffer(Buffer, TRUE); + return Status; +} + +NTSTATUS NTAPI +ConDrvSetConsoleCursorPosition(IN PCONSOLE Console, + IN PTEXTMODE_SCREEN_BUFFER Buffer, + IN PCOORD Position); +CSR_API(SrvSetConsoleCursorPosition) +{ + NTSTATUS Status; + PCONSOLE_SETCURSORPOSITION SetCursorPositionRequest = &((PCONSOLE_API_MESSAGE)ApiMessage)->Data.SetCursorPositionRequest; + PTEXTMODE_SCREEN_BUFFER Buffer; + + DPRINT("SrvSetConsoleCursorPosition\n"); + + Status = ConSrvGetTextModeBuffer(ConsoleGetPerProcessData(CsrGetClientThread()->Process), + SetCursorPositionRequest->OutputHandle, + &Buffer, GENERIC_WRITE, TRUE); + if (!NT_SUCCESS(Status)) return Status; + + Status = ConDrvSetConsoleCursorPosition(Buffer->Header.Console, + Buffer, + &SetCursorPositionRequest->Position); + + ConSrvReleaseScreenBuffer(Buffer, TRUE); + return Status; +} + +CSR_API(SrvCreateConsoleScreenBuffer) +{ + NTSTATUS Status = STATUS_INVALID_PARAMETER; + PCONSOLE_CREATESCREENBUFFER CreateScreenBufferRequest = &((PCONSOLE_API_MESSAGE)ApiMessage)->Data.CreateScreenBufferRequest; + PCONSOLE_PROCESS_DATA ProcessData = ConsoleGetPerProcessData(CsrGetClientThread()->Process); + PCONSOLE Console; + PCONSOLE_SCREEN_BUFFER Buff; + + PVOID ScreenBufferInfo = NULL; + TEXTMODE_BUFFER_INFO TextModeInfo = {{80, 25}, + DEFAULT_SCREEN_ATTRIB, + DEFAULT_POPUP_ATTRIB , + TRUE, + CSR_DEFAULT_CURSOR_SIZE}; + GRAPHICS_BUFFER_INFO GraphicsInfo; + GraphicsInfo.Info = CreateScreenBufferRequest->GraphicsBufferInfo; // HACK for MSVC + + DPRINT("SrvCreateConsoleScreenBuffer\n"); + + Status = ConSrvGetConsole(ProcessData, &Console, TRUE); + if (!NT_SUCCESS(Status)) return Status; + + if (CreateScreenBufferRequest->ScreenBufferType == CONSOLE_TEXTMODE_BUFFER) + { + ScreenBufferInfo = &TextModeInfo; + + /* + if (Console->ActiveBuffer) + { + TextModeInfo.ScreenBufferSize = Console->ActiveBuffer->ScreenBufferSize; + if (TextModeInfo.ScreenBufferSize.X == 0) TextModeInfo.ScreenBufferSize.X = 80; + if (TextModeInfo.ScreenBufferSize.Y == 0) TextModeInfo.ScreenBufferSize.Y = 25; + + TextModeInfo.ScreenAttrib = Console->ActiveBuffer->ScreenBuffer.TextBuffer.ScreenDefaultAttrib; + TextModeInfo.PopupAttrib = Console->ActiveBuffer->ScreenBuffer.TextBuffer.PopupDefaultAttrib; + + TextModeInfo.IsCursorVisible = Console->ActiveBuffer->CursorInfo.bVisible; + TextModeInfo.CursorSize = Console->ActiveBuffer->CursorInfo.dwSize; + } + */ + + /* + * This is Windows' behaviour + */ + + /* Use the current console size. Regularize it if needed. */ + TextModeInfo.ScreenBufferSize = Console->ConsoleSize; + if (TextModeInfo.ScreenBufferSize.X == 0) TextModeInfo.ScreenBufferSize.X = 1; + if (TextModeInfo.ScreenBufferSize.Y == 0) TextModeInfo.ScreenBufferSize.Y = 1; + + /* If we have an active screen buffer, use its attributes as the new ones */ + if (Console->ActiveBuffer && GetType(Console->ActiveBuffer) == TEXTMODE_BUFFER) + { + PTEXTMODE_SCREEN_BUFFER Buffer = (PTEXTMODE_SCREEN_BUFFER)Console->ActiveBuffer; + + TextModeInfo.ScreenAttrib = Buffer->ScreenDefaultAttrib; + TextModeInfo.PopupAttrib = Buffer->PopupDefaultAttrib; + + TextModeInfo.IsCursorVisible = Buffer->CursorInfo.bVisible; + TextModeInfo.CursorSize = Buffer->CursorInfo.dwSize; + } + } + else if (CreateScreenBufferRequest->ScreenBufferType == CONSOLE_GRAPHICS_BUFFER) + { + /* Get infos from the graphics buffer information structure */ + if (!CsrValidateMessageBuffer(ApiMessage, + (PVOID*)&CreateScreenBufferRequest->GraphicsBufferInfo.lpBitMapInfo, + 1, + CreateScreenBufferRequest->GraphicsBufferInfo.dwBitMapInfoLength)) + { + Status = STATUS_INVALID_PARAMETER; + goto Quit; + } + + ScreenBufferInfo = &GraphicsInfo; + + /* Initialize shared variables */ + CreateScreenBufferRequest->GraphicsBufferInfo.hMutex = GraphicsInfo.Info.hMutex = INVALID_HANDLE_VALUE; + CreateScreenBufferRequest->GraphicsBufferInfo.lpBitMap = GraphicsInfo.Info.lpBitMap = NULL; + + /* A graphics screen buffer is never inheritable */ + CreateScreenBufferRequest->Inheritable = FALSE; + } + + Status = ConDrvCreateScreenBuffer(&Buff, + Console, + CreateScreenBufferRequest->ScreenBufferType, + ScreenBufferInfo); + if (!NT_SUCCESS(Status)) goto Quit; + + /* Insert the new handle inside the process handles table */ + RtlEnterCriticalSection(&ProcessData->HandleTableLock); + + Status = ConSrvInsertObject(ProcessData, + &CreateScreenBufferRequest->OutputHandle, + &Buff->Header, + CreateScreenBufferRequest->Access, + CreateScreenBufferRequest->Inheritable, + CreateScreenBufferRequest->ShareMode); + + RtlLeaveCriticalSection(&ProcessData->HandleTableLock); + + if (!NT_SUCCESS(Status)) goto Quit; + + if (CreateScreenBufferRequest->ScreenBufferType == CONSOLE_GRAPHICS_BUFFER) + { + PGRAPHICS_SCREEN_BUFFER Buffer = (PGRAPHICS_SCREEN_BUFFER)Buff; + /* + * Initialize the graphics buffer information structure + * and give it back to the client. + */ + CreateScreenBufferRequest->GraphicsBufferInfo.hMutex = Buffer->ClientMutex; + CreateScreenBufferRequest->GraphicsBufferInfo.lpBitMap = Buffer->ClientBitMap; + } + +Quit: + ConSrvReleaseConsole(Console, TRUE); + return Status; +} + +NTSTATUS NTAPI +ConDrvSetConsoleActiveScreenBuffer(IN PCONSOLE Console, + IN PCONSOLE_SCREEN_BUFFER Buffer); +CSR_API(SrvSetConsoleActiveScreenBuffer) +{ + NTSTATUS Status; + PCONSOLE_SETACTIVESCREENBUFFER SetScreenBufferRequest = &((PCONSOLE_API_MESSAGE)ApiMessage)->Data.SetScreenBufferRequest; + PCONSOLE_SCREEN_BUFFER Buffer; + + DPRINT("SrvSetConsoleActiveScreenBuffer\n"); + + Status = ConSrvGetScreenBuffer(ConsoleGetPerProcessData(CsrGetClientThread()->Process), + SetScreenBufferRequest->OutputHandle, + &Buffer, GENERIC_WRITE, TRUE); + if (!NT_SUCCESS(Status)) return Status; + + Status = ConDrvSetConsoleActiveScreenBuffer(Buffer->Header.Console, + Buffer); + + ConSrvReleaseScreenBuffer(Buffer, TRUE); + return Status; +} + + +/* CSR THREADS FOR WriteConsole ***********************************************/ + +static NTSTATUS +DoWriteConsole(IN PCSR_API_MESSAGE ApiMessage, + IN PCSR_THREAD ClientThread, + IN BOOL CreateWaitBlock OPTIONAL); + +// Wait function CSR_WAIT_FUNCTION +static BOOLEAN +WriteConsoleThread(IN PLIST_ENTRY WaitList, + IN PCSR_THREAD WaitThread, + IN PCSR_API_MESSAGE WaitApiMessage, + IN PVOID WaitContext, + IN PVOID WaitArgument1, + IN PVOID WaitArgument2, + IN ULONG WaitFlags) +{ + NTSTATUS Status; + + DPRINT("WriteConsoleThread - WaitContext = 0x%p, WaitArgument1 = 0x%p, WaitArgument2 = 0x%p, WaitFlags = %lu\n", WaitContext, WaitArgument1, WaitArgument2, WaitFlags); + + /* + * If we are notified of the process termination via a call + * to CsrNotifyWaitBlock triggered by CsrDestroyProcess or + * CsrDestroyThread, just return. + */ + if (WaitFlags & CsrProcessTerminating) + { + Status = STATUS_THREAD_IS_TERMINATING; + goto Quit; + } + + Status = DoWriteConsole(WaitApiMessage, + WaitThread, + FALSE); + +Quit: + if (Status != STATUS_PENDING) + { + WaitApiMessage->Status = Status; + } + + return (Status == STATUS_PENDING ? FALSE : TRUE); +} + +NTSTATUS NTAPI +ConDrvWriteConsole(IN PCONSOLE Console, + IN PTEXTMODE_SCREEN_BUFFER ScreenBuffer, + IN BOOLEAN Unicode, + IN PVOID StringBuffer, + IN ULONG NumCharsToWrite, + OUT PULONG NumCharsWritten OPTIONAL); +static NTSTATUS +DoWriteConsole(IN PCSR_API_MESSAGE ApiMessage, + IN PCSR_THREAD ClientThread, + IN BOOL CreateWaitBlock OPTIONAL) +{ + NTSTATUS Status; + PCONSOLE_WRITECONSOLE WriteConsoleRequest = &((PCONSOLE_API_MESSAGE)ApiMessage)->Data.WriteConsoleRequest; + PTEXTMODE_SCREEN_BUFFER ScreenBuffer; + + Status = ConSrvGetTextModeBuffer(ConsoleGetPerProcessData(ClientThread->Process), + WriteConsoleRequest->OutputHandle, + &ScreenBuffer, GENERIC_WRITE, FALSE); + if (!NT_SUCCESS(Status)) return Status; + + Status = ConDrvWriteConsole(ScreenBuffer->Header.Console, + ScreenBuffer, + WriteConsoleRequest->Unicode, + WriteConsoleRequest->Buffer, + WriteConsoleRequest->NrCharactersToWrite, + &WriteConsoleRequest->NrCharactersWritten); + + if (Status == STATUS_PENDING) + { + if (CreateWaitBlock) + { + if (!CsrCreateWait(&ScreenBuffer->Header.Console->WriteWaitQueue, + WriteConsoleThread, + ClientThread, + ApiMessage, + NULL, + NULL)) + { + /* Fail */ + Status = STATUS_NO_MEMORY; + goto Quit; + } + } + + /* Wait until we un-pause the console */ + // Status = STATUS_PENDING; + } + +Quit: + ConSrvReleaseScreenBuffer(ScreenBuffer, FALSE); + return Status; +} + + +/* TEXT OUTPUT APIS ***********************************************************/ + +NTSTATUS NTAPI +ConDrvReadConsoleOutput(IN PCONSOLE Console, + IN PTEXTMODE_SCREEN_BUFFER Buffer, + IN BOOLEAN Unicode, + OUT PCHAR_INFO CharInfo/*Buffer*/, + IN PCOORD BufferSize, + IN PCOORD BufferCoord, + IN OUT PSMALL_RECT ReadRegion); +CSR_API(SrvReadConsoleOutput) +{ + NTSTATUS Status; + PCONSOLE_READOUTPUT ReadOutputRequest = &((PCONSOLE_API_MESSAGE)ApiMessage)->Data.ReadOutputRequest; + PTEXTMODE_SCREEN_BUFFER Buffer; + + DPRINT("SrvReadConsoleOutput\n"); + + if (!CsrValidateMessageBuffer(ApiMessage, + (PVOID*)&ReadOutputRequest->CharInfo, + ReadOutputRequest->BufferSize.X * ReadOutputRequest->BufferSize.Y, + sizeof(CHAR_INFO))) + { + return STATUS_INVALID_PARAMETER; + } + + Status = ConSrvGetTextModeBuffer(ConsoleGetPerProcessData(CsrGetClientThread()->Process), + ReadOutputRequest->OutputHandle, + &Buffer, GENERIC_READ, TRUE); + if (!NT_SUCCESS(Status)) return Status; + + Status = ConDrvReadConsoleOutput(Buffer->Header.Console, + Buffer, + ReadOutputRequest->Unicode, + ReadOutputRequest->CharInfo, + &ReadOutputRequest->BufferSize, + &ReadOutputRequest->BufferCoord, + &ReadOutputRequest->ReadRegion); + + ConSrvReleaseScreenBuffer(Buffer, TRUE); + return Status; +} + +NTSTATUS NTAPI +ConDrvWriteConsoleOutput(IN PCONSOLE Console, + IN PTEXTMODE_SCREEN_BUFFER Buffer, + IN BOOLEAN Unicode, + IN PCHAR_INFO CharInfo/*Buffer*/, + IN PCOORD BufferSize, + IN PCOORD BufferCoord, + IN OUT PSMALL_RECT WriteRegion); +CSR_API(SrvWriteConsoleOutput) +{ + NTSTATUS Status; + PCONSOLE_WRITEOUTPUT WriteOutputRequest = &((PCONSOLE_API_MESSAGE)ApiMessage)->Data.WriteOutputRequest; + PTEXTMODE_SCREEN_BUFFER Buffer; + + DPRINT("SrvWriteConsoleOutput\n"); + + if (!CsrValidateMessageBuffer(ApiMessage, + (PVOID*)&WriteOutputRequest->CharInfo, + WriteOutputRequest->BufferSize.X * WriteOutputRequest->BufferSize.Y, + sizeof(CHAR_INFO))) + { + return STATUS_INVALID_PARAMETER; + } + + Status = ConSrvGetTextModeBuffer(ConsoleGetPerProcessData(CsrGetClientThread()->Process), + WriteOutputRequest->OutputHandle, + &Buffer, GENERIC_WRITE, TRUE); + if (!NT_SUCCESS(Status)) return Status; + + Status = ConDrvWriteConsoleOutput(Buffer->Header.Console, + Buffer, + WriteOutputRequest->Unicode, + WriteOutputRequest->CharInfo, + &WriteOutputRequest->BufferSize, + &WriteOutputRequest->BufferCoord, + &WriteOutputRequest->WriteRegion); + + ConSrvReleaseScreenBuffer(Buffer, TRUE); + return Status; +} + +CSR_API(SrvWriteConsole) +{ + NTSTATUS Status; + PCONSOLE_WRITECONSOLE WriteConsoleRequest = &((PCONSOLE_API_MESSAGE)ApiMessage)->Data.WriteConsoleRequest; + + DPRINT("SrvWriteConsole\n"); + + if (!CsrValidateMessageBuffer(ApiMessage, + (PVOID)&WriteConsoleRequest->Buffer, + WriteConsoleRequest->BufferSize, + sizeof(BYTE))) + { + return STATUS_INVALID_PARAMETER; + } + + Status = DoWriteConsole(ApiMessage, + CsrGetClientThread(), + TRUE); + + if (Status == STATUS_PENDING) *ReplyCode = CsrReplyPending; + + return Status; +} + +NTSTATUS NTAPI +ConDrvReadConsoleOutputString(IN PCONSOLE Console, + IN PTEXTMODE_SCREEN_BUFFER Buffer, + IN CODE_TYPE CodeType, + OUT PVOID StringBuffer, + IN ULONG NumCodesToRead, + IN PCOORD ReadCoord, + OUT PCOORD EndCoord, + OUT PULONG CodesRead); +CSR_API(SrvReadConsoleOutputString) +{ + NTSTATUS Status; + PCONSOLE_READOUTPUTCODE ReadOutputCodeRequest = &((PCONSOLE_API_MESSAGE)ApiMessage)->Data.ReadOutputCodeRequest; + PTEXTMODE_SCREEN_BUFFER Buffer; + ULONG CodeSize; + + DPRINT("SrvReadConsoleOutputString\n"); + + switch (ReadOutputCodeRequest->CodeType) + { + case CODE_ASCII: + CodeSize = sizeof(CHAR); + break; + + case CODE_UNICODE: + CodeSize = sizeof(WCHAR); + break; + + case CODE_ATTRIBUTE: + CodeSize = sizeof(WORD); + break; + + default: + return STATUS_INVALID_PARAMETER; + } + + if (!CsrValidateMessageBuffer(ApiMessage, + (PVOID*)&ReadOutputCodeRequest->pCode.pCode, + ReadOutputCodeRequest->NumCodesToRead, + CodeSize)) + { + return STATUS_INVALID_PARAMETER; + } + + Status = ConSrvGetTextModeBuffer(ConsoleGetPerProcessData(CsrGetClientThread()->Process), + ReadOutputCodeRequest->OutputHandle, + &Buffer, GENERIC_READ, TRUE); + if (!NT_SUCCESS(Status)) return Status; + + Status = ConDrvReadConsoleOutputString(Buffer->Header.Console, + Buffer, + ReadOutputCodeRequest->CodeType, + ReadOutputCodeRequest->pCode.pCode, + ReadOutputCodeRequest->NumCodesToRead, + &ReadOutputCodeRequest->ReadCoord, + &ReadOutputCodeRequest->EndCoord, + &ReadOutputCodeRequest->CodesRead); + + ConSrvReleaseScreenBuffer(Buffer, TRUE); + return Status; +} + +NTSTATUS NTAPI +ConDrvWriteConsoleOutputString(IN PCONSOLE Console, + IN PTEXTMODE_SCREEN_BUFFER Buffer, + IN CODE_TYPE CodeType, + IN PVOID StringBuffer, + IN ULONG NumCodesToWrite, + IN PCOORD WriteCoord /*, + OUT PCOORD EndCoord, + OUT PULONG CodesWritten */); +CSR_API(SrvWriteConsoleOutputString) +{ + NTSTATUS Status; + PCONSOLE_WRITEOUTPUTCODE WriteOutputCodeRequest = &((PCONSOLE_API_MESSAGE)ApiMessage)->Data.WriteOutputCodeRequest; + PTEXTMODE_SCREEN_BUFFER Buffer; + ULONG CodeSize; + + DPRINT("SrvWriteConsoleOutputString\n"); + + switch (WriteOutputCodeRequest->CodeType) + { + case CODE_ASCII: + CodeSize = sizeof(CHAR); + break; + + case CODE_UNICODE: + CodeSize = sizeof(WCHAR); + break; + + case CODE_ATTRIBUTE: + CodeSize = sizeof(WORD); + break; + + default: + return STATUS_INVALID_PARAMETER; + } + + if (!CsrValidateMessageBuffer(ApiMessage, + (PVOID*)&WriteOutputCodeRequest->pCode.pCode, + WriteOutputCodeRequest->Length, + CodeSize)) + { + return STATUS_INVALID_PARAMETER; + } + + Status = ConSrvGetTextModeBuffer(ConsoleGetPerProcessData(CsrGetClientThread()->Process), + WriteOutputCodeRequest->OutputHandle, + &Buffer, GENERIC_WRITE, TRUE); + if (!NT_SUCCESS(Status)) return Status; + + Status = ConDrvWriteConsoleOutputString(Buffer->Header.Console, + Buffer, + WriteOutputCodeRequest->CodeType, + WriteOutputCodeRequest->pCode.pCode, + WriteOutputCodeRequest->Length, // NumCodesToWrite, + &WriteOutputCodeRequest->Coord /*, // WriteCoord, + &WriteOutputCodeRequest->EndCoord, + &WriteOutputCodeRequest->NrCharactersWritten */); + + // WriteOutputCodeRequest->NrCharactersWritten = Written; + + ConSrvReleaseScreenBuffer(Buffer, TRUE); + return Status; +} + +NTSTATUS NTAPI +ConDrvFillConsoleOutput(IN PCONSOLE Console, + IN PTEXTMODE_SCREEN_BUFFER Buffer, + IN CODE_TYPE CodeType, + IN PVOID Code, + IN ULONG NumCodesToWrite, + IN PCOORD WriteCoord /*, + OUT PULONG CodesWritten */); +CSR_API(SrvFillConsoleOutput) +{ + NTSTATUS Status; + PCONSOLE_FILLOUTPUTCODE FillOutputRequest = &((PCONSOLE_API_MESSAGE)ApiMessage)->Data.FillOutputRequest; + PTEXTMODE_SCREEN_BUFFER Buffer; + USHORT CodeType = FillOutputRequest->CodeType; + + DPRINT("SrvFillConsoleOutput\n"); + + if ( (CodeType != CODE_ASCII ) && + (CodeType != CODE_UNICODE ) && + (CodeType != CODE_ATTRIBUTE) ) + { + return STATUS_INVALID_PARAMETER; + } + + Status = ConSrvGetTextModeBuffer(ConsoleGetPerProcessData(CsrGetClientThread()->Process), + FillOutputRequest->OutputHandle, + &Buffer, GENERIC_WRITE, TRUE); + if (!NT_SUCCESS(Status)) return Status; + + Status = ConDrvFillConsoleOutput(Buffer->Header.Console, + Buffer, + CodeType, + &FillOutputRequest->Code, + FillOutputRequest->Length, // NumCodesToWrite, + &FillOutputRequest->Coord /*, // WriteCoord, + &FillOutputRequest->NrCharactersWritten */); + + // FillOutputRequest->NrCharactersWritten = Written; + + ConSrvReleaseScreenBuffer(Buffer, TRUE); + return Status; +} + +NTSTATUS NTAPI +ConDrvGetConsoleScreenBufferInfo(IN PCONSOLE Console, + IN PTEXTMODE_SCREEN_BUFFER Buffer, + OUT PCONSOLE_SCREEN_BUFFER_INFO ScreenBufferInfo); +CSR_API(SrvGetConsoleScreenBufferInfo) +{ + NTSTATUS Status; + PCONSOLE_GETSCREENBUFFERINFO ScreenBufferInfoRequest = &((PCONSOLE_API_MESSAGE)ApiMessage)->Data.ScreenBufferInfoRequest; + PTEXTMODE_SCREEN_BUFFER Buffer; + + DPRINT("SrvGetConsoleScreenBufferInfo\n"); + + Status = ConSrvGetTextModeBuffer(ConsoleGetPerProcessData(CsrGetClientThread()->Process), + ScreenBufferInfoRequest->OutputHandle, + &Buffer, GENERIC_READ, TRUE); + if (!NT_SUCCESS(Status)) return Status; + + Status = ConDrvGetConsoleScreenBufferInfo(Buffer->Header.Console, + Buffer, + &ScreenBufferInfoRequest->Info); + + ConSrvReleaseScreenBuffer(Buffer, TRUE); + return Status; +} + +NTSTATUS NTAPI +ConDrvSetConsoleTextAttribute(IN PCONSOLE Console, + IN PTEXTMODE_SCREEN_BUFFER Buffer, + IN WORD Attribute); +CSR_API(SrvSetConsoleTextAttribute) +{ + NTSTATUS Status; + PCONSOLE_SETTEXTATTRIB SetTextAttribRequest = &((PCONSOLE_API_MESSAGE)ApiMessage)->Data.SetTextAttribRequest; + PTEXTMODE_SCREEN_BUFFER Buffer; + + DPRINT("SrvSetConsoleTextAttribute\n"); + + Status = ConSrvGetTextModeBuffer(ConsoleGetPerProcessData(CsrGetClientThread()->Process), + SetTextAttribRequest->OutputHandle, + &Buffer, GENERIC_WRITE, TRUE); + if (!NT_SUCCESS(Status)) return Status; + + Status = ConDrvSetConsoleTextAttribute(Buffer->Header.Console, + Buffer, + SetTextAttribRequest->Attrib); + + ConSrvReleaseScreenBuffer(Buffer, TRUE); + return Status; +} + +NTSTATUS NTAPI +ConDrvSetConsoleScreenBufferSize(IN PCONSOLE Console, + IN PTEXTMODE_SCREEN_BUFFER Buffer, + IN PCOORD Size); +CSR_API(SrvSetConsoleScreenBufferSize) +{ + NTSTATUS Status; + PCONSOLE_SETSCREENBUFFERSIZE SetScreenBufferSizeRequest = &((PCONSOLE_API_MESSAGE)ApiMessage)->Data.SetScreenBufferSizeRequest; + PTEXTMODE_SCREEN_BUFFER Buffer; + + DPRINT("SrvSetConsoleScreenBufferSize\n"); + + Status = ConSrvGetTextModeBuffer(ConsoleGetPerProcessData(CsrGetClientThread()->Process), + SetScreenBufferSizeRequest->OutputHandle, + &Buffer, GENERIC_WRITE, TRUE); + if (!NT_SUCCESS(Status)) return Status; + + Status = ConDrvSetConsoleScreenBufferSize(Buffer->Header.Console, + Buffer, + &SetScreenBufferSizeRequest->Size); + + ConSrvReleaseScreenBuffer(Buffer, TRUE); + return Status; +} + +NTSTATUS NTAPI +ConDrvScrollConsoleScreenBuffer(IN PCONSOLE Console, + IN PTEXTMODE_SCREEN_BUFFER Buffer, + IN BOOLEAN Unicode, + IN PSMALL_RECT ScrollRectangle, + IN BOOLEAN UseClipRectangle, + IN PSMALL_RECT ClipRectangle OPTIONAL, + IN PCOORD DestinationOrigin, + IN CHAR_INFO FillChar); +CSR_API(SrvScrollConsoleScreenBuffer) +{ + NTSTATUS Status; + PCONSOLE_SCROLLSCREENBUFFER ScrollScreenBufferRequest = &((PCONSOLE_API_MESSAGE)ApiMessage)->Data.ScrollScreenBufferRequest; + PTEXTMODE_SCREEN_BUFFER Buffer; + + DPRINT("SrvScrollConsoleScreenBuffer\n"); + + Status = ConSrvGetTextModeBuffer(ConsoleGetPerProcessData(CsrGetClientThread()->Process), + ScrollScreenBufferRequest->OutputHandle, + &Buffer, GENERIC_WRITE, TRUE); + if (!NT_SUCCESS(Status)) return Status; + + Status = ConDrvScrollConsoleScreenBuffer(Buffer->Header.Console, + Buffer, + ScrollScreenBufferRequest->Unicode, + &ScrollScreenBufferRequest->ScrollRectangle, + ScrollScreenBufferRequest->UseClipRectangle, + &ScrollScreenBufferRequest->ClipRectangle, + &ScrollScreenBufferRequest->DestinationOrigin, + ScrollScreenBufferRequest->Fill); + + ConSrvReleaseScreenBuffer(Buffer, TRUE); + return Status; +} + +NTSTATUS NTAPI +ConDrvSetConsoleWindowInfo(IN PCONSOLE Console, + IN PTEXTMODE_SCREEN_BUFFER Buffer, + IN BOOLEAN Absolute, + IN PSMALL_RECT WindowRect); +CSR_API(SrvSetConsoleWindowInfo) +{ + NTSTATUS Status; + PCONSOLE_SETWINDOWINFO SetWindowInfoRequest = &((PCONSOLE_API_MESSAGE)ApiMessage)->Data.SetWindowInfoRequest; + // PCONSOLE_SCREEN_BUFFER Buffer; + PTEXTMODE_SCREEN_BUFFER Buffer; + + DPRINT1("SrvSetConsoleWindowInfo(0x%08x, %d, {L%d, T%d, R%d, B%d}) called\n", + SetWindowInfoRequest->OutputHandle, SetWindowInfoRequest->Absolute, + SetWindowInfoRequest->WindowRect.Left , + SetWindowInfoRequest->WindowRect.Top , + SetWindowInfoRequest->WindowRect.Right, + SetWindowInfoRequest->WindowRect.Bottom); + + // ConSrvGetScreenBuffer + Status = ConSrvGetTextModeBuffer(ConsoleGetPerProcessData(CsrGetClientThread()->Process), + SetWindowInfoRequest->OutputHandle, + &Buffer, GENERIC_READ, TRUE); + if (!NT_SUCCESS(Status)) return Status; + + Status = ConDrvSetConsoleWindowInfo(Buffer->Header.Console, + Buffer, + SetWindowInfoRequest->Absolute, + &SetWindowInfoRequest->WindowRect); + + ConSrvReleaseScreenBuffer(Buffer, TRUE); + return Status; +} + +/* EOF */ diff --git a/reactos/win32ss/user/winsrv/consrv_new/conoutput.h b/reactos/win32ss/user/winsrv/consrv_new/conoutput.h new file mode 100644 index 00000000000..966a6abcca6 --- /dev/null +++ b/reactos/win32ss/user/winsrv/consrv_new/conoutput.h @@ -0,0 +1,46 @@ +/* + * COPYRIGHT: See COPYING in the top level directory + * PROJECT: ReactOS Console Server DLL + * FILE: win32ss/user/winsrv/consrv/conoutput.h + * PURPOSE: Console Output functions + * PROGRAMMERS: Jeffrey Morlan + * Hermes Belusca-Maito (hermes.belusca@sfr.fr) + */ + +#pragma once + +#define ConSrvGetTextModeBuffer(ProcessData, Handle, Ptr, Access, LockConsole) \ + ConSrvGetObject((ProcessData), (Handle), (PCONSOLE_IO_OBJECT*)(Ptr), NULL, \ + (Access), (LockConsole), TEXTMODE_BUFFER) +#define ConSrvGetTextModeBufferAndHandleEntry(ProcessData, Handle, Ptr, Entry, Access, LockConsole) \ + ConSrvGetObject((ProcessData), (Handle), (PCONSOLE_IO_OBJECT*)(Ptr), (Entry), \ + (Access), (LockConsole), TEXTMODE_BUFFER) + +#define ConSrvGetGraphicsBuffer(ProcessData, Handle, Ptr, Access, LockConsole) \ + ConSrvGetObject((ProcessData), (Handle), (PCONSOLE_IO_OBJECT*)(Ptr), NULL, \ + (Access), (LockConsole), GRAPHICS_BUFFER) +#define ConSrvGetGraphicsBufferAndHandleEntry(ProcessData, Handle, Ptr, Entry, Access, LockConsole) \ + ConSrvGetObject((ProcessData), (Handle), (PCONSOLE_IO_OBJECT*)(Ptr), (Entry), \ + (Access), (LockConsole), GRAPHICS_BUFFER) + +#define ConSrvGetScreenBuffer(ProcessData, Handle, Ptr, Access, LockConsole) \ + ConSrvGetObject((ProcessData), (Handle), (PCONSOLE_IO_OBJECT*)(Ptr), NULL, \ + (Access), (LockConsole), SCREEN_BUFFER) +#define ConSrvGetScreenBufferAndHandleEntry(ProcessData, Handle, Ptr, Entry, Access, LockConsole) \ + ConSrvGetObject((ProcessData), (Handle), (PCONSOLE_IO_OBJECT*)(Ptr), (Entry), \ + (Access), (LockConsole), SCREEN_BUFFER) + +#define ConSrvReleaseScreenBuffer(Buff, IsConsoleLocked) \ + ConSrvReleaseObject(&(Buff)->Header, (IsConsoleLocked)) + +NTSTATUS FASTCALL ConDrvCreateScreenBuffer(OUT PCONSOLE_SCREEN_BUFFER* Buffer, + IN OUT PCONSOLE Console, + IN ULONG BufferType, + IN PVOID ScreenBufferInfo); +VOID WINAPI ConioDeleteScreenBuffer(PCONSOLE_SCREEN_BUFFER Buffer); +// VOID FASTCALL ConioSetActiveScreenBuffer(PCONSOLE_SCREEN_BUFFER Buffer); + +PCONSOLE_SCREEN_BUFFER +ConDrvGetActiveScreenBuffer(IN PCONSOLE Console); + +/* EOF */ diff --git a/reactos/win32ss/user/winsrv/consrv_new/console.c b/reactos/win32ss/user/winsrv/consrv_new/console.c new file mode 100644 index 00000000000..9403daa081f --- /dev/null +++ b/reactos/win32ss/user/winsrv/consrv_new/console.c @@ -0,0 +1,652 @@ +/* + * COPYRIGHT: See COPYING in the top level directory + * PROJECT: ReactOS Console Server DLL + * FILE: win32ss/user/winsrv/consrv/console.c + * PURPOSE: Console Management Functions + * PROGRAMMERS: Gé van Geldorp + * Jeffrey Morlan + * Hermes Belusca-Maito (hermes.belusca@sfr.fr) + */ + +/* INCLUDES *******************************************************************/ + +#include "consrv.h" +#include "include/conio.h" +#include "include/conio2.h" +#include "handle.h" +#include "procinit.h" +#include "alias.h" +#include "coninput.h" +#include "conoutput.h" +#include "lineinput.h" +#include "include/settings.h" + +#include "frontends/gui/guiterm.h" +#ifdef TUITERM_COMPILE +#include "frontends/tui/tuiterm.h" +#endif + +#include "include/console.h" +#include "console.h" +#include "resource.h" + +#define NDEBUG +#include + + +/* GLOBALS ********************************************************************/ + +/***************/ +#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} +}; + + +/* PRIVATE FUNCTIONS **********************************************************/ + +#if 0000 +VOID FASTCALL +ConioPause(PCONSOLE Console, UINT Flags) +{ + Console->PauseFlags |= Flags; + if (!Console->UnpauseEvent) + Console->UnpauseEvent = CreateEvent(NULL, TRUE, FALSE, NULL); +} + +VOID FASTCALL +ConioUnpause(PCONSOLE Console, UINT Flags) +{ + Console->PauseFlags &= ~Flags; + + // if ((Console->PauseFlags & (PAUSED_FROM_KEYBOARD | PAUSED_FROM_SCROLLBAR | PAUSED_FROM_SELECTION)) == 0) + if (Console->PauseFlags == 0 && Console->UnpauseEvent) + { + SetEvent(Console->UnpauseEvent); + CloseHandle(Console->UnpauseEvent); + Console->UnpauseEvent = NULL; + + CsrNotifyWait(&Console->WriteWaitQueue, + WaitAll, + NULL, + NULL); + if (!IsListEmpty(&Console->WriteWaitQueue)) + { + CsrDereferenceWait(&Console->WriteWaitQueue); + } + } +} +#endif + + +NTSTATUS FASTCALL +ConSrvGetConsole(PCONSOLE_PROCESS_DATA ProcessData, + PCONSOLE* Console, + BOOL LockConsole) +{ + NTSTATUS Status = STATUS_SUCCESS; + PCONSOLE ProcessConsole; + + ASSERT(Console); + *Console = NULL; + + // RtlEnterCriticalSection(&ProcessData->HandleTableLock); + + Status = ConDrvGetConsole(&ProcessConsole, ProcessData->ConsoleHandle, LockConsole); + if (NT_SUCCESS(Status)) *Console = ProcessConsole; + + // RtlLeaveCriticalSection(&ProcessData->HandleTableLock); + return Status; +} + +VOID FASTCALL +ConSrvReleaseConsole(PCONSOLE Console, + BOOL WasConsoleLocked) +{ + /* Just call the driver*/ + ConDrvReleaseConsole(Console, WasConsoleLocked); +} + + +NTSTATUS WINAPI +ConSrvInitConsole(OUT PHANDLE NewConsoleHandle, + OUT PCONSOLE* NewConsole, + IN OUT PCONSOLE_START_INFO ConsoleStartInfo, + IN ULONG ConsoleLeaderProcessId) +{ + NTSTATUS Status; + HANDLE ConsoleHandle; + PCONSOLE Console; + CONSOLE_INFO ConsoleInfo; + SIZE_T Length = 0; + ULONG i = 0; + FRONTEND FrontEnd; + + if (NewConsole == NULL || ConsoleStartInfo == NULL) + return STATUS_INVALID_PARAMETER; + + *NewConsole = NULL; + + /* + * Load the console settings + */ + + /* 1. Load the default settings */ + ConSrvGetDefaultSettings(&ConsoleInfo, ConsoleLeaderProcessId); + + /* 2. Get the title of the console (initialize ConsoleInfo.ConsoleTitle) */ + Length = min(wcslen(ConsoleStartInfo->ConsoleTitle), + sizeof(ConsoleInfo.ConsoleTitle) / sizeof(ConsoleInfo.ConsoleTitle[0]) - 1); + wcsncpy(ConsoleInfo.ConsoleTitle, ConsoleStartInfo->ConsoleTitle, Length); + ConsoleInfo.ConsoleTitle[Length] = L'\0'; + + + /* + * Choose an adequate terminal front-end to load, and load it + */ + Status = STATUS_SUCCESS; + for (i = 0; i < sizeof(FrontEndLoadingMethods) / sizeof(FrontEndLoadingMethods[0]); ++i) + { + DPRINT("CONSRV: Trying to load %s terminal emulator...\n", FrontEndLoadingMethods[i].FrontEndName); + Status = FrontEndLoadingMethods[i].FrontEndLoad(&FrontEnd, + &ConsoleInfo, + ConsoleStartInfo, + ConsoleLeaderProcessId); + if (NT_SUCCESS(Status)) + { + DPRINT("CONSRV: %s terminal emulator loaded successfully\n", FrontEndLoadingMethods[i].FrontEndName); + break; + } + else + { + DPRINT1("CONSRV: Loading %s terminal emulator failed, Status = 0x%08lx , continuing...\n", FrontEndLoadingMethods[i].FrontEndName, Status); + } + } + + if (!NT_SUCCESS(Status)) + { + DPRINT1("CONSRV: Failed to initialize a frontend, Status = 0x%08lx\n", Status); + return Status; + } + + DPRINT("CONSRV: Frontend initialized\n"); + + +/******************************************************************************/ + /* + * 4. Load the remaining console settings via the registry. + */ + if ((ConsoleStartInfo->dwStartupFlags & STARTF_TITLEISLINKNAME) == 0) + { + /* + * Either we weren't created by an app launched via a shell-link, + * or we failed to load shell-link console properties. + * Therefore, load the console infos for the application from the registry. + */ + ConSrvReadUserSettings(&ConsoleInfo, ConsoleLeaderProcessId); + + /* + * Now, update them with the properties the user might gave to us + * via the STARTUPINFO structure before calling CreateProcess + * (and which was transmitted via the ConsoleStartInfo structure). + * We therefore overwrite the values read in the registry. + */ + if (ConsoleStartInfo->dwStartupFlags & STARTF_USEFILLATTRIBUTE) + { + ConsoleInfo.ScreenAttrib = (USHORT)ConsoleStartInfo->FillAttribute; + } + if (ConsoleStartInfo->dwStartupFlags & STARTF_USECOUNTCHARS) + { + ConsoleInfo.ScreenBufferSize = ConsoleStartInfo->ScreenBufferSize; + } + if (ConsoleStartInfo->dwStartupFlags & STARTF_USESIZE) + { + // ConsoleInfo.ConsoleSize = ConsoleStartInfo->ConsoleWindowSize; + ConsoleInfo.ConsoleSize.X = (SHORT)ConsoleStartInfo->ConsoleWindowSize.cx; + ConsoleInfo.ConsoleSize.Y = (SHORT)ConsoleStartInfo->ConsoleWindowSize.cy; + } + } + + /* Set-up the code page */ + ConsoleInfo.CodePage = GetOEMCP(); +/******************************************************************************/ + + Status = ConDrvInitConsole(&ConsoleHandle, + &Console, + &ConsoleInfo, + ConsoleLeaderProcessId); + if (!NT_SUCCESS(Status)) + { + DPRINT1("Creating a new console failed, Status = 0x%08lx\n", Status); + FrontEndLoadingMethods[i].FrontEndUnload(&FrontEnd); + return Status; + } + + ASSERT(Console); + DPRINT("Console initialized\n"); + + Status = ConDrvRegisterFrontEnd(Console, &FrontEnd); + if (!NT_SUCCESS(Status)) + { + DPRINT1("Failed to register frontend to the given console, Status = 0x%08lx\n", Status); + ConDrvDeleteConsole(Console); + FrontEndLoadingMethods[i].FrontEndUnload(&FrontEnd); + return Status; + } + DPRINT("FrontEnd registered\n"); + + /* Return the newly created console to the caller and a success code too */ + *NewConsoleHandle = ConsoleHandle; + *NewConsole = Console; + return STATUS_SUCCESS; +} + +VOID WINAPI +ConSrvDeleteConsole(PCONSOLE Console) +{ + DPRINT("ConSrvDeleteConsole\n"); + + /* Just call the driver. ConSrvDeregisterFrontEnd is called on-demand. */ + ConDrvDeleteConsole(Console); +} + + +/* PUBLIC SERVER APIS *********************************************************/ + +CSR_API(SrvAllocConsole) +{ + NTSTATUS Status = STATUS_SUCCESS; + PCONSOLE_ALLOCCONSOLE AllocConsoleRequest = &((PCONSOLE_API_MESSAGE)ApiMessage)->Data.AllocConsoleRequest; + PCSR_PROCESS CsrProcess = CsrGetClientThread()->Process; + PCONSOLE_PROCESS_DATA ProcessData = ConsoleGetPerProcessData(CsrProcess); + + if (ProcessData->ConsoleHandle != NULL) + { + DPRINT1("Process already has a console\n"); + return STATUS_ACCESS_DENIED; + } + + if (!CsrValidateMessageBuffer(ApiMessage, + (PVOID*)&AllocConsoleRequest->ConsoleStartInfo, + 1, + sizeof(CONSOLE_START_INFO))) + { + return STATUS_INVALID_PARAMETER; + } + + /* Initialize a new Console owned by the Console Leader Process */ + Status = ConSrvAllocateConsole(ProcessData, + &AllocConsoleRequest->InputHandle, + &AllocConsoleRequest->OutputHandle, + &AllocConsoleRequest->ErrorHandle, + AllocConsoleRequest->ConsoleStartInfo); + if (!NT_SUCCESS(Status)) + { + DPRINT1("Console allocation failed\n"); + return Status; + } + + /* Return the console handle and the input wait handle to the caller */ + AllocConsoleRequest->ConsoleHandle = ProcessData->ConsoleHandle; + AllocConsoleRequest->InputWaitHandle = ProcessData->ConsoleEvent; + + /* Set the Property-Dialog and Control-Dispatcher handlers */ + ProcessData->PropDispatcher = AllocConsoleRequest->PropDispatcher; + ProcessData->CtrlDispatcher = AllocConsoleRequest->CtrlDispatcher; + + return STATUS_SUCCESS; +} + +CSR_API(SrvAttachConsole) +{ + NTSTATUS Status = STATUS_SUCCESS; + PCONSOLE_ATTACHCONSOLE AttachConsoleRequest = &((PCONSOLE_API_MESSAGE)ApiMessage)->Data.AttachConsoleRequest; + PCSR_PROCESS SourceProcess = NULL; // The parent process. + PCSR_PROCESS TargetProcess = CsrGetClientThread()->Process; // Ourselves. + HANDLE ProcessId = ULongToHandle(AttachConsoleRequest->ProcessId); + PCONSOLE_PROCESS_DATA SourceProcessData, TargetProcessData; + + TargetProcessData = ConsoleGetPerProcessData(TargetProcess); + + if (TargetProcessData->ConsoleHandle != NULL) + { + DPRINT1("Process already has a console\n"); + return STATUS_ACCESS_DENIED; + } + + /* Check whether we try to attach to the parent's console */ + if (ProcessId == ULongToHandle(ATTACH_PARENT_PROCESS)) + { + PROCESS_BASIC_INFORMATION ProcessInfo; + ULONG Length = sizeof(ProcessInfo); + + /* Get the real parent's ID */ + + Status = NtQueryInformationProcess(TargetProcess->ProcessHandle, + ProcessBasicInformation, + &ProcessInfo, + Length, &Length); + if (!NT_SUCCESS(Status)) + { + DPRINT1("SrvAttachConsole - Cannot retrieve basic process info, Status = %lu\n", Status); + return Status; + } + + ProcessId = ULongToHandle(ProcessInfo.InheritedFromUniqueProcessId); + } + + /* Lock the source process via its PID */ + Status = CsrLockProcessByClientId(ProcessId, &SourceProcess); + if (!NT_SUCCESS(Status)) return Status; + + SourceProcessData = ConsoleGetPerProcessData(SourceProcess); + + if (SourceProcessData->ConsoleHandle == NULL) + { + Status = STATUS_INVALID_HANDLE; + goto Quit; + } + + /* + * Inherit the console from the parent, + * if any, otherwise return an error. + */ + Status = ConSrvInheritConsole(TargetProcessData, + SourceProcessData->ConsoleHandle, + TRUE, + &AttachConsoleRequest->InputHandle, + &AttachConsoleRequest->OutputHandle, + &AttachConsoleRequest->ErrorHandle); + if (!NT_SUCCESS(Status)) + { + DPRINT1("Console inheritance failed\n"); + goto Quit; + } + + /* Return the console handle and the input wait handle to the caller */ + AttachConsoleRequest->ConsoleHandle = TargetProcessData->ConsoleHandle; + AttachConsoleRequest->InputWaitHandle = TargetProcessData->ConsoleEvent; + + /* Set the Property-Dialog and Control-Dispatcher handlers */ + TargetProcessData->PropDispatcher = AttachConsoleRequest->PropDispatcher; + TargetProcessData->CtrlDispatcher = AttachConsoleRequest->CtrlDispatcher; + + Status = STATUS_SUCCESS; + +Quit: + /* Unlock the "source" process and exit */ + CsrUnlockProcess(SourceProcess); + return Status; +} + +CSR_API(SrvFreeConsole) +{ + ConSrvRemoveConsole(ConsoleGetPerProcessData(CsrGetClientThread()->Process)); + return STATUS_SUCCESS; +} + +NTSTATUS NTAPI +ConDrvGetConsoleMode(IN PCONSOLE Console, + IN PCONSOLE_IO_OBJECT Object, + OUT PULONG ConsoleMode); +CSR_API(SrvGetConsoleMode) +{ + NTSTATUS Status; + PCONSOLE_GETSETCONSOLEMODE ConsoleModeRequest = &((PCONSOLE_API_MESSAGE)ApiMessage)->Data.ConsoleModeRequest; + PCONSOLE_IO_OBJECT Object; + + Status = ConSrvGetObject(ConsoleGetPerProcessData(CsrGetClientThread()->Process), + ConsoleModeRequest->ConsoleHandle, + &Object, NULL, GENERIC_READ, TRUE, 0); + if (!NT_SUCCESS(Status)) return Status; + + Status = ConDrvGetConsoleMode(Object->Console, Object, + &ConsoleModeRequest->ConsoleMode); + + ConSrvReleaseObject(Object, TRUE); + return Status; +} + +NTSTATUS NTAPI +ConDrvSetConsoleMode(IN PCONSOLE Console, + IN PCONSOLE_IO_OBJECT Object, + IN ULONG ConsoleMode); +CSR_API(SrvSetConsoleMode) +{ + NTSTATUS Status; + PCONSOLE_GETSETCONSOLEMODE ConsoleModeRequest = &((PCONSOLE_API_MESSAGE)ApiMessage)->Data.ConsoleModeRequest; + PCONSOLE_IO_OBJECT Object; + + Status = ConSrvGetObject(ConsoleGetPerProcessData(CsrGetClientThread()->Process), + ConsoleModeRequest->ConsoleHandle, + &Object, NULL, GENERIC_WRITE, TRUE, 0); + if (!NT_SUCCESS(Status)) return Status; + + Status = ConDrvSetConsoleMode(Object->Console, Object, + ConsoleModeRequest->ConsoleMode); + + ConSrvReleaseObject(Object, TRUE); + return Status; +} + +NTSTATUS NTAPI +ConDrvGetConsoleTitle(IN PCONSOLE Console, + IN OUT PWCHAR Title, + IN OUT PULONG BufLength); +CSR_API(SrvGetConsoleTitle) +{ + NTSTATUS Status; + PCONSOLE_GETSETCONSOLETITLE TitleRequest = &((PCONSOLE_API_MESSAGE)ApiMessage)->Data.TitleRequest; + PCONSOLE Console; + + if (!CsrValidateMessageBuffer(ApiMessage, + (PVOID)&TitleRequest->Title, + TitleRequest->Length, + sizeof(BYTE))) + { + return STATUS_INVALID_PARAMETER; + } + + Status = ConSrvGetConsole(ConsoleGetPerProcessData(CsrGetClientThread()->Process), &Console, TRUE); + if (!NT_SUCCESS(Status)) + { + DPRINT1("Can't get console\n"); + return Status; + } + + Status = ConDrvGetConsoleTitle(Console, + TitleRequest->Title, + &TitleRequest->Length); + + ConSrvReleaseConsole(Console, TRUE); + return Status; +} + +NTSTATUS NTAPI +ConDrvSetConsoleTitle(IN PCONSOLE Console, + IN PWCHAR Title, + IN ULONG BufLength); +CSR_API(SrvSetConsoleTitle) +{ + NTSTATUS Status; + PCONSOLE_GETSETCONSOLETITLE TitleRequest = &((PCONSOLE_API_MESSAGE)ApiMessage)->Data.TitleRequest; + PCONSOLE Console; + + if (!CsrValidateMessageBuffer(ApiMessage, + (PVOID)&TitleRequest->Title, + TitleRequest->Length, + sizeof(BYTE))) + { + return STATUS_INVALID_PARAMETER; + } + + Status = ConSrvGetConsole(ConsoleGetPerProcessData(CsrGetClientThread()->Process), &Console, TRUE); + if (!NT_SUCCESS(Status)) + { + DPRINT1("Can't get console\n"); + return Status; + } + + Status = ConDrvSetConsoleTitle(Console, + TitleRequest->Title, + TitleRequest->Length); + + if (NT_SUCCESS(Status)) ConioChangeTitle(Console); + + ConSrvReleaseConsole(Console, TRUE); + return Status; +} + +NTSTATUS NTAPI +ConDrvGetConsoleCP(IN PCONSOLE Console, + OUT PUINT CodePage, + IN BOOLEAN InputCP); +CSR_API(SrvGetConsoleCP) +{ + NTSTATUS Status; + PCONSOLE_GETSETINPUTOUTPUTCP ConsoleCPRequest = &((PCONSOLE_API_MESSAGE)ApiMessage)->Data.ConsoleCPRequest; + PCONSOLE Console; + + DPRINT("SrvGetConsoleCP, getting %s Code Page\n", + ConsoleCPRequest->InputCP ? "Input" : "Output"); + + Status = ConSrvGetConsole(ConsoleGetPerProcessData(CsrGetClientThread()->Process), &Console, TRUE); + if (!NT_SUCCESS(Status)) return Status; + + Status = ConDrvGetConsoleCP(Console, + &ConsoleCPRequest->CodePage, + ConsoleCPRequest->InputCP); + + ConSrvReleaseConsole(Console, TRUE); + return Status; +} + +NTSTATUS NTAPI +ConDrvSetConsoleCP(IN PCONSOLE Console, + IN UINT CodePage, + IN BOOLEAN InputCP); +CSR_API(SrvSetConsoleCP) +{ + NTSTATUS Status = STATUS_INVALID_PARAMETER; + PCONSOLE_GETSETINPUTOUTPUTCP ConsoleCPRequest = &((PCONSOLE_API_MESSAGE)ApiMessage)->Data.ConsoleCPRequest; + PCONSOLE Console; + + DPRINT("SrvSetConsoleCP, setting %s Code Page\n", + ConsoleCPRequest->InputCP ? "Input" : "Output"); + + Status = ConSrvGetConsole(ConsoleGetPerProcessData(CsrGetClientThread()->Process), &Console, TRUE); + if (!NT_SUCCESS(Status)) return Status; + + Status = ConDrvSetConsoleCP(Console, + ConsoleCPRequest->CodePage, + ConsoleCPRequest->InputCP); + + ConSrvReleaseConsole(Console, TRUE); + return Status; +} + +NTSTATUS NTAPI +ConDrvGetConsoleProcessList(IN PCONSOLE Console, + IN OUT PULONG ProcessIdsList, + IN ULONG MaxIdListItems, + OUT PULONG ProcessIdsTotal); +CSR_API(SrvGetConsoleProcessList) +{ + NTSTATUS Status; + PCONSOLE_GETPROCESSLIST GetProcessListRequest = &((PCONSOLE_API_MESSAGE)ApiMessage)->Data.GetProcessListRequest; + PCONSOLE Console; + + if (!CsrValidateMessageBuffer(ApiMessage, + (PVOID)&GetProcessListRequest->pProcessIds, + GetProcessListRequest->nMaxIds, + sizeof(DWORD))) + { + return STATUS_INVALID_PARAMETER; + } + + Status = ConSrvGetConsole(ConsoleGetPerProcessData(CsrGetClientThread()->Process), &Console, TRUE); + if (!NT_SUCCESS(Status)) return Status; + + Status = ConDrvGetConsoleProcessList(Console, + GetProcessListRequest->pProcessIds, + GetProcessListRequest->nMaxIds, + &GetProcessListRequest->nProcessIdsTotal); + + ConSrvReleaseConsole(Console, TRUE); + return Status; +} + +CSR_API(SrvGenerateConsoleCtrlEvent) +{ + NTSTATUS Status; + PCONSOLE_GENERATECTRLEVENT GenerateCtrlEventRequest = &((PCONSOLE_API_MESSAGE)ApiMessage)->Data.GenerateCtrlEventRequest; + PCONSOLE Console; + + Status = ConSrvGetConsole(ConsoleGetPerProcessData(CsrGetClientThread()->Process), &Console, TRUE); + if (!NT_SUCCESS(Status)) return Status; + + Status = ConDrvConsoleProcessCtrlEvent(Console, + GenerateCtrlEventRequest->ProcessGroup, + GenerateCtrlEventRequest->Event); + + ConSrvReleaseConsole(Console, TRUE); + return Status; +} + +/* EOF */ diff --git a/reactos/win32ss/user/winsrv/consrv_new/console.h b/reactos/win32ss/user/winsrv/consrv_new/console.h new file mode 100644 index 00000000000..b617da888c7 --- /dev/null +++ b/reactos/win32ss/user/winsrv/consrv_new/console.h @@ -0,0 +1,27 @@ +/* + * COPYRIGHT: See COPYING in the top level directory + * PROJECT: ReactOS Console Server DLL + * FILE: win32ss/user/winsrv/consrv/console.h + * PURPOSE: Console Initialization Functions + * PROGRAMMERS: Hermes Belusca-Maito (hermes.belusca@sfr.fr) + */ + +#pragma once + +// FIXME: Fix compilation +struct _CONSOLE; + +NTSTATUS WINAPI +ConSrvInitConsole(OUT PHANDLE NewConsoleHandle, + OUT struct _CONSOLE** /* PCONSOLE* */ NewConsole, + IN OUT PCONSOLE_START_INFO ConsoleStartInfo, + IN ULONG ConsoleLeaderProcessId); +VOID WINAPI ConSrvDeleteConsole(struct _CONSOLE* /* PCONSOLE */ Console); + +NTSTATUS FASTCALL ConSrvGetConsole(PCONSOLE_PROCESS_DATA ProcessData, + struct _CONSOLE** /* PCONSOLE* */ Console, + BOOL LockConsole); +VOID FASTCALL ConSrvReleaseConsole(struct _CONSOLE* /* PCONSOLE */ Console, + BOOL WasConsoleLocked); + +/* EOF */ diff --git a/reactos/win32ss/user/winsrv/consrv_new/consrv.h b/reactos/win32ss/user/winsrv/consrv_new/consrv.h new file mode 100644 index 00000000000..7a07e3bafb8 --- /dev/null +++ b/reactos/win32ss/user/winsrv/consrv_new/consrv.h @@ -0,0 +1,79 @@ +/* + * COPYRIGHT: See COPYING in the top level directory + * PROJECT: ReactOS Console Server DLL + * FILE: win32ss/user/winsrv/consrv/consrv.h + * PURPOSE: Main header - Definitions + * PROGRAMMERS: Hermes Belusca-Maito (hermes.belusca@sfr.fr) + */ + +#ifndef __CONSRV_H__ +#define __CONSRV_H__ + +#pragma once + +/* PSDK/NDK Headers */ +#include +#define WIN32_NO_STATUS +#define _INC_WINDOWS +#define COM_NO_WINDOWS_H +#include +#include +#include +#include +#include +#include +#include +#define NTOS_MODE_USER +#include +#include +#include +#include +#include +#include +#include + +/* Public Win32K Headers */ +#include + +/* PSEH for SEH Support */ +#include + +/* CSRSS Header */ +#include + +/* CONSOLE Headers */ +#include +#include + + +/* Heap Helpers */ +#include "heap.h" + +/* Globals */ +extern HINSTANCE ConSrvDllInstance; + +#define ConsoleGetPerProcessData(Process) \ + ((PCONSOLE_PROCESS_DATA)((Process)->ServerData[CONSRV_SERVERDLL_INDEX])) + +typedef struct _CONSOLE_PROCESS_DATA +{ + LIST_ENTRY ConsoleLink; + PCSR_PROCESS Process; // Process owning this structure. + HANDLE ConsoleEvent; + + HANDLE ConsoleHandle; + HANDLE ParentConsoleHandle; + + BOOL ConsoleApp; // TRUE if it is a CUI app, FALSE otherwise. + + RTL_CRITICAL_SECTION HandleTableLock; + ULONG HandleTableSize; + struct _CONSOLE_IO_HANDLE* /* PCONSOLE_IO_HANDLE */ HandleTable; // Length-varying table + + LPTHREAD_START_ROUTINE CtrlDispatcher; + LPTHREAD_START_ROUTINE PropDispatcher; // We hold the property dialog handler there, till all the GUI thingie moves out from CSRSS. +} CONSOLE_PROCESS_DATA, *PCONSOLE_PROCESS_DATA; + +#endif // __CONSRV_H__ + +/* EOF */ diff --git a/reactos/win32ss/user/winsrv/consrv_new/consrv.rc b/reactos/win32ss/user/winsrv/consrv_new/consrv.rc new file mode 100644 index 00000000000..3e6479868de --- /dev/null +++ b/reactos/win32ss/user/winsrv/consrv_new/consrv.rc @@ -0,0 +1,6 @@ +#include +#include +#include "resource.h" + +#include "rsrc.rc" +#include "frontends/frontends.rc" diff --git a/reactos/win32ss/user/winsrv/consrv_new/frontendctl.c b/reactos/win32ss/user/winsrv/consrv_new/frontendctl.c new file mode 100644 index 00000000000..92c147a5a36 --- /dev/null +++ b/reactos/win32ss/user/winsrv/consrv_new/frontendctl.c @@ -0,0 +1,331 @@ +/* + * COPYRIGHT: See COPYING in the top level directory + * PROJECT: ReactOS Console Server DLL + * FILE: win32ss/user/winsrv/consrv/frontendctl.c + * PURPOSE: Terminal Front-Ends Control + * PROGRAMMERS: Hermes Belusca-Maito (hermes.belusca@sfr.fr) + */ + +/* INCLUDES *******************************************************************/ + +#include "consrv.h" +#include "include/conio.h" +#include "include/conio2.h" +#include "conoutput.h" +#include "console.h" +#include "handle.h" + +#define NDEBUG +#include + + +/* PRIVATE FUNCTIONS **********************************************************/ + + +/* PUBLIC SERVER APIS *********************************************************/ + +/********************************************************************** + * HardwareStateProperty + * + * DESCRIPTION + * Set/Get the value of the HardwareState and switch + * between direct video buffer ouput and GDI windowed + * output. + * ARGUMENTS + * Client hands us a CONSOLE_GETSETHWSTATE object. + * We use the same object to Request. + * NOTE + * ConsoleHwState has the correct size to be compatible + * with NT's, but values are not. + */ +#if 0 +static NTSTATUS FASTCALL +SetConsoleHardwareState(PCONSOLE Console, ULONG ConsoleHwState) +{ + DPRINT1("Console Hardware State: %d\n", ConsoleHwState); + + if ((CONSOLE_HARDWARE_STATE_GDI_MANAGED == ConsoleHwState) + ||(CONSOLE_HARDWARE_STATE_DIRECT == ConsoleHwState)) + { + if (Console->HardwareState != ConsoleHwState) + { + /* TODO: implement switching from full screen to windowed mode */ + /* TODO: or back; now simply store the hardware state */ + Console->HardwareState = ConsoleHwState; + } + + return STATUS_SUCCESS; + } + + return STATUS_INVALID_PARAMETER_3; /* Client: (handle, set_get, [mode]) */ +} +#endif + +CSR_API(SrvGetConsoleHardwareState) +{ +#if 0 + NTSTATUS Status; + PCONSOLE_GETSETHWSTATE HardwareStateRequest = &((PCONSOLE_API_MESSAGE)ApiMessage)->Data.HardwareStateRequest; + PCONSOLE_SCREEN_BUFFER Buff; + PCONSOLE Console; + + Status = ConSrvGetTextModeBuffer(ConsoleGetPerProcessData(CsrGetClientThread()->Process), + HardwareStateRequest->OutputHandle, + &Buff, + GENERIC_READ, + TRUE); + if (!NT_SUCCESS(Status)) + { + DPRINT1("Failed to get console handle in SrvGetConsoleHardwareState\n"); + return Status; + } + + Console = Buff->Header.Console; + HardwareStateRequest->State = Console->HardwareState; + + ConSrvReleaseScreenBuffer(Buff, TRUE); + return Status; +#else + UNIMPLEMENTED; + return STATUS_NOT_IMPLEMENTED; +#endif +} + +CSR_API(SrvSetConsoleHardwareState) +{ +#if 0 + NTSTATUS Status; + PCONSOLE_GETSETHWSTATE HardwareStateRequest = &((PCONSOLE_API_MESSAGE)ApiMessage)->Data.HardwareStateRequest; + PCONSOLE_SCREEN_BUFFER Buff; + PCONSOLE Console; + + Status = ConSrvGetTextModeBuffer(ConsoleGetPerProcessData(CsrGetClientThread()->Process), + HardwareStateRequest->OutputHandle, + &Buff, + GENERIC_WRITE, + TRUE); + if (!NT_SUCCESS(Status)) + { + DPRINT1("Failed to get console handle in SrvSetConsoleHardwareState\n"); + return Status; + } + + DPRINT("Setting console hardware state.\n"); + Console = Buff->Header.Console; + Status = SetConsoleHardwareState(Console, HardwareStateRequest->State); + + ConSrvReleaseScreenBuffer(Buff, TRUE); + return Status; +#else + UNIMPLEMENTED; + return STATUS_NOT_IMPLEMENTED; +#endif +} + +CSR_API(SrvGetConsoleDisplayMode) +{ + NTSTATUS Status; + PCONSOLE_GETDISPLAYMODE GetDisplayModeRequest = &((PCONSOLE_API_MESSAGE)ApiMessage)->Data.GetDisplayModeRequest; + PCONSOLE Console; + + Status = ConSrvGetConsole(ConsoleGetPerProcessData(CsrGetClientThread()->Process), + &Console, TRUE); + if (!NT_SUCCESS(Status)) return Status; + + GetDisplayModeRequest->DisplayMode = ConioGetDisplayMode(Console); + + ConSrvReleaseConsole(Console, TRUE); + return STATUS_SUCCESS; +} + +CSR_API(SrvSetConsoleDisplayMode) +{ + NTSTATUS Status; + PCONSOLE_SETDISPLAYMODE SetDisplayModeRequest = &((PCONSOLE_API_MESSAGE)ApiMessage)->Data.SetDisplayModeRequest; + PCONSOLE Console; + PCONSOLE_SCREEN_BUFFER Buff; + + Status = ConSrvGetScreenBuffer(ConsoleGetPerProcessData(CsrGetClientThread()->Process), + SetDisplayModeRequest->OutputHandle, + &Buff, + GENERIC_WRITE, + TRUE); + if (!NT_SUCCESS(Status)) return Status; + + Console = Buff->Header.Console; + + if (ConioSetDisplayMode(Console, SetDisplayModeRequest->DisplayMode)) + { + SetDisplayModeRequest->NewSBDim = Buff->ScreenBufferSize; + Status = STATUS_SUCCESS; + } + else + { + Status = STATUS_INVALID_PARAMETER; + } + + ConSrvReleaseScreenBuffer(Buff, TRUE); + return Status; +} + +CSR_API(SrvGetLargestConsoleWindowSize) +{ + NTSTATUS Status; + PCONSOLE_GETLARGESTWINDOWSIZE GetLargestWindowSizeRequest = &((PCONSOLE_API_MESSAGE)ApiMessage)->Data.GetLargestWindowSizeRequest; + PCONSOLE_SCREEN_BUFFER Buff; + PCONSOLE Console; + + Status = ConSrvGetTextModeBuffer(ConsoleGetPerProcessData(CsrGetClientThread()->Process), + GetLargestWindowSizeRequest->OutputHandle, + &Buff, + GENERIC_READ, + TRUE); + if (!NT_SUCCESS(Status)) return Status; + + Console = Buff->Header.Console; + ConioGetLargestConsoleWindowSize(Console, &GetLargestWindowSizeRequest->Size); + + ConSrvReleaseScreenBuffer(Buff, TRUE); + return STATUS_SUCCESS; +} + +CSR_API(SrvShowConsoleCursor) +{ + NTSTATUS Status; + PCONSOLE_SHOWCURSOR ShowCursorRequest = &((PCONSOLE_API_MESSAGE)ApiMessage)->Data.ShowCursorRequest; + PCONSOLE Console; + PCONSOLE_SCREEN_BUFFER Buff; + + Status = ConSrvGetScreenBuffer(ConsoleGetPerProcessData(CsrGetClientThread()->Process), + ShowCursorRequest->OutputHandle, + &Buff, + GENERIC_WRITE, + TRUE); + if (!NT_SUCCESS(Status)) return Status; + + Console = Buff->Header.Console; + + ShowCursorRequest->RefCount = ConioShowMouseCursor(Console, ShowCursorRequest->Show); + + ConSrvReleaseScreenBuffer(Buff, TRUE); + return STATUS_SUCCESS; +} + +CSR_API(SrvSetConsoleCursor) +{ + NTSTATUS Status; + BOOL Success; + PCONSOLE_SETCURSOR SetCursorRequest = &((PCONSOLE_API_MESSAGE)ApiMessage)->Data.SetCursorRequest; + PCONSOLE Console; + PCONSOLE_SCREEN_BUFFER Buff; + + // FIXME: Tests show that this function is used only for graphics screen buffers + // and otherwise it returns false + set last error to invalid handle. + // NOTE: I find that behaviour is ridiculous but ok, let's accept that at the moment... + Status = ConSrvGetGraphicsBuffer(ConsoleGetPerProcessData(CsrGetClientThread()->Process), + SetCursorRequest->OutputHandle, + &Buff, + GENERIC_WRITE, + TRUE); + if (!NT_SUCCESS(Status)) return Status; + + Console = Buff->Header.Console; + + Success = ConioSetMouseCursor(Console, SetCursorRequest->hCursor); + + ConSrvReleaseScreenBuffer(Buff, TRUE); + return (Success ? STATUS_SUCCESS : STATUS_UNSUCCESSFUL); +} + +CSR_API(SrvConsoleMenuControl) +{ + NTSTATUS Status; + PCONSOLE_MENUCONTROL MenuControlRequest = &((PCONSOLE_API_MESSAGE)ApiMessage)->Data.MenuControlRequest; + PCONSOLE Console; + PCONSOLE_SCREEN_BUFFER Buff; + + Status = ConSrvGetScreenBuffer(ConsoleGetPerProcessData(CsrGetClientThread()->Process), + MenuControlRequest->OutputHandle, + &Buff, + GENERIC_WRITE, + TRUE); + if (!NT_SUCCESS(Status)) return Status; + + Console = Buff->Header.Console; + + MenuControlRequest->hMenu = ConioMenuControl(Console, + MenuControlRequest->dwCmdIdLow, + MenuControlRequest->dwCmdIdHigh); + + ConSrvReleaseScreenBuffer(Buff, TRUE); + return STATUS_SUCCESS; +} + +CSR_API(SrvSetConsoleMenuClose) +{ + NTSTATUS Status; + BOOL Success; + PCONSOLE_SETMENUCLOSE SetMenuCloseRequest = &((PCONSOLE_API_MESSAGE)ApiMessage)->Data.SetMenuCloseRequest; + PCONSOLE Console; + + Status = ConSrvGetConsole(ConsoleGetPerProcessData(CsrGetClientThread()->Process), + &Console, TRUE); + if (!NT_SUCCESS(Status)) return Status; + + Success = ConioSetMenuClose(Console, SetMenuCloseRequest->Enable); + + ConSrvReleaseConsole(Console, TRUE); + return (Success ? STATUS_SUCCESS : STATUS_UNSUCCESSFUL); +} + +CSR_API(SrvGetConsoleWindow) +{ + NTSTATUS Status; + PCONSOLE_GETWINDOW GetWindowRequest = &((PCONSOLE_API_MESSAGE)ApiMessage)->Data.GetWindowRequest; + PCONSOLE Console; + + Status = ConSrvGetConsole(ConsoleGetPerProcessData(CsrGetClientThread()->Process), &Console, TRUE); + if (!NT_SUCCESS(Status)) return Status; + + GetWindowRequest->WindowHandle = ConioGetConsoleWindowHandle(Console); + + ConSrvReleaseConsole(Console, TRUE); + return STATUS_SUCCESS; +} + +CSR_API(SrvSetConsoleIcon) +{ + NTSTATUS Status; + PCONSOLE_SETICON SetIconRequest = &((PCONSOLE_API_MESSAGE)ApiMessage)->Data.SetIconRequest; + PCONSOLE Console; + + Status = ConSrvGetConsole(ConsoleGetPerProcessData(CsrGetClientThread()->Process), &Console, TRUE); + if (!NT_SUCCESS(Status)) return Status; + + Status = (ConioChangeIcon(Console, SetIconRequest->WindowIcon) + ? STATUS_SUCCESS + : STATUS_UNSUCCESSFUL); + + ConSrvReleaseConsole(Console, TRUE); + return Status; +} + +CSR_API(SrvGetConsoleSelectionInfo) +{ + NTSTATUS Status; + PCONSOLE_GETSELECTIONINFO GetSelectionInfoRequest = &((PCONSOLE_API_MESSAGE)ApiMessage)->Data.GetSelectionInfoRequest; + PCONSOLE Console; + + Status = ConSrvGetConsole(ConsoleGetPerProcessData(CsrGetClientThread()->Process), &Console, TRUE); + if (NT_SUCCESS(Status)) + { + memset(&GetSelectionInfoRequest->Info, 0, sizeof(CONSOLE_SELECTION_INFO)); + if (Console->Selection.dwFlags != 0) + GetSelectionInfoRequest->Info = Console->Selection; + ConSrvReleaseConsole(Console, TRUE); + } + + return Status; +} + +/* EOF */ diff --git a/reactos/win32ss/user/winsrv/consrv_new/frontends/frontends.rc b/reactos/win32ss/user/winsrv/consrv_new/frontends/frontends.rc new file mode 100644 index 00000000000..b6f87126d9c --- /dev/null +++ b/reactos/win32ss/user/winsrv/consrv_new/frontends/frontends.rc @@ -0,0 +1,2 @@ + +#include "gui/guiterm.rc" diff --git a/reactos/win32ss/user/winsrv/consrv_new/frontends/gui/consolecpl.h b/reactos/win32ss/user/winsrv/consrv_new/frontends/gui/consolecpl.h new file mode 100644 index 00000000000..b33b7174217 --- /dev/null +++ b/reactos/win32ss/user/winsrv/consrv_new/frontends/gui/consolecpl.h @@ -0,0 +1,14 @@ +/* + * COPYRIGHT: See COPYING in the top level directory + * PROJECT: ReactOS Console Server DLL + * FILE: win32ss/user/winsrv/consrv/consolecpl.h + * PURPOSE: GUI front-end settings management - Header for console.dll + * PROGRAMMERS: Hermes Belusca-Maito (hermes.belusca@sfr.fr) + */ + +#pragma once + +#include "include/settings.h" +#include "guisettings.h" + +/* EOF */ diff --git a/reactos/win32ss/user/winsrv/consrv_new/frontends/gui/graphics.c b/reactos/win32ss/user/winsrv/consrv_new/frontends/gui/graphics.c new file mode 100644 index 00000000000..58267a7c15a --- /dev/null +++ b/reactos/win32ss/user/winsrv/consrv_new/frontends/gui/graphics.c @@ -0,0 +1,81 @@ +/* + * COPYRIGHT: See COPYING in the top level directory + * PROJECT: ReactOS Console Server DLL + * FILE: win32ss/user/winsrv/consrv/frontends/gui/graphics.c + * PURPOSE: GUI Terminal Front-End - Support for graphics-mode screen-buffers + * PROGRAMMERS: Hermes Belusca-Maito (hermes.belusca@sfr.fr) + */ + +/* INCLUDES *******************************************************************/ + +#include "consrv.h" +#include "include/conio.h" +#include "include/settings.h" +#include "guisettings.h" + +#define NDEBUG +#include + + +/* FUNCTIONS ******************************************************************/ + +VOID +GuiCopyFromGraphicsBuffer(PGRAPHICS_SCREEN_BUFFER Buffer) +{ + /* + * This function supposes that the system clipboard was opened. + */ + + // PCONSOLE Console = Buffer->Header.Console; + + UNIMPLEMENTED; +} + +VOID +GuiPasteToGraphicsBuffer(PGRAPHICS_SCREEN_BUFFER Buffer) +{ + /* + * This function supposes that the system clipboard was opened. + */ + + // PCONSOLE Console = Buffer->Header.Console; + + UNIMPLEMENTED; +} + +VOID +GuiPaintGraphicsBuffer(PGRAPHICS_SCREEN_BUFFER Buffer, + PGUI_CONSOLE_DATA GuiData, + HDC hDC, + PRECT rc) +{ + if (Buffer->BitMap == NULL) return; + + /* Grab the mutex */ + NtWaitForSingleObject(Buffer->Mutex, FALSE, NULL); + + /* + * The seventh parameter (YSrc) of SetDIBitsToDevice always designates + * the Y-coordinate of the "lower-left corner" of the image, be the DIB + * in bottom-up or top-down mode. + */ + SetDIBitsToDevice(hDC, + /* Coordinates / size of the repainted rectangle, in the view's frame */ + rc->left, + rc->top, + rc->right - rc->left, + rc->bottom - rc->top, + /* Coordinates / size of the corresponding image portion, in the graphics screen-buffer's frame */ + Buffer->ViewOrigin.X + rc->left, + Buffer->ViewOrigin.Y + rc->top, + 0, + Buffer->ScreenBufferSize.Y, // == Buffer->BitMapInfo->bmiHeader.biHeight + Buffer->BitMap, + Buffer->BitMapInfo, + Buffer->BitMapUsage); + + /* Release the mutex */ + NtReleaseMutant(Buffer->Mutex, NULL); +} + +/* EOF */ diff --git a/reactos/win32ss/user/winsrv/consrv_new/frontends/gui/guisettings.c b/reactos/win32ss/user/winsrv/consrv_new/frontends/gui/guisettings.c new file mode 100644 index 00000000000..dc22dfc9cd5 --- /dev/null +++ b/reactos/win32ss/user/winsrv/consrv_new/frontends/gui/guisettings.c @@ -0,0 +1,541 @@ +/* + * COPYRIGHT: See COPYING in the top level directory + * PROJECT: ReactOS Console Server DLL + * FILE: win32ss/user/winsrv/consrv/frontends/gui/guisettings.c + * PURPOSE: GUI Terminal Front-End Settings Management + * PROGRAMMERS: Johannes Anderwald + * Hermes Belusca-Maito (hermes.belusca@sfr.fr) + */ + +/* INCLUDES *******************************************************************/ + +#include "consrv.h" +#include "include/conio.h" +#include "include/settings.h" +#include "guisettings.h" + +#define NDEBUG +#include + + +VOID GuiConsoleMoveWindow(PGUI_CONSOLE_DATA GuiData); + +/* FUNCTIONS ******************************************************************/ + +BOOL +GuiConsoleReadUserSettings(IN OUT PGUI_CONSOLE_INFO TermInfo, + IN LPCWSTR ConsoleTitle, + IN DWORD ProcessId) +{ + /***************************************************** + * Adapted from ConSrvReadUserSettings in settings.c * + *****************************************************/ + + BOOL RetVal = FALSE; + HKEY hKey; + DWORD dwNumSubKeys = 0; + DWORD dwIndex; + DWORD dwType; + WCHAR szValueName[MAX_PATH]; + DWORD dwValueName; + WCHAR szValue[LF_FACESIZE] = L"\0"; + DWORD Value; + DWORD dwValue; + + if (!ConSrvOpenUserSettings(ProcessId, + ConsoleTitle, + &hKey, KEY_READ, + FALSE)) + { + DPRINT("ConSrvOpenUserSettings failed\n"); + return FALSE; + } + + if (RegQueryInfoKey(hKey, NULL, NULL, NULL, NULL, NULL, NULL, + &dwNumSubKeys, NULL, NULL, NULL, NULL) != ERROR_SUCCESS) + { + DPRINT("GuiConsoleReadUserSettings: RegQueryInfoKey failed\n"); + RegCloseKey(hKey); + return FALSE; + } + + DPRINT("GuiConsoleReadUserSettings entered dwNumSubKeys %d\n", dwNumSubKeys); + + for (dwIndex = 0; dwIndex < dwNumSubKeys; dwIndex++) + { + dwValue = sizeof(Value); + dwValueName = MAX_PATH; // sizeof(szValueName)/sizeof(szValueName[0]) + + if (RegEnumValueW(hKey, dwIndex, szValueName, &dwValueName, NULL, &dwType, (BYTE*)&Value, &dwValue) != ERROR_SUCCESS) + { + if (dwType == REG_SZ) + { + /* + * Retry in case of string value + */ + dwValue = sizeof(szValue); + dwValueName = MAX_PATH; // sizeof(szValueName)/sizeof(szValueName[0]) + if (RegEnumValueW(hKey, dwIndex, szValueName, &dwValueName, NULL, NULL, (BYTE*)szValue, &dwValue) != ERROR_SUCCESS) + break; + } + else + { + break; + } + } + + if (!wcscmp(szValueName, L"FaceName")) + { + SIZE_T Length = min(wcslen(szValue) + 1, LF_FACESIZE); // wcsnlen + wcsncpy(TermInfo->FaceName, szValue, LF_FACESIZE); + TermInfo->FaceName[Length] = L'\0'; + RetVal = TRUE; + } + else if (!wcscmp(szValueName, L"FontFamily")) + { + TermInfo->FontFamily = Value; + RetVal = TRUE; + } + else if (!wcscmp(szValueName, L"FontSize")) + { + TermInfo->FontSize = Value; + RetVal = TRUE; + } + else if (!wcscmp(szValueName, L"FontWeight")) + { + TermInfo->FontWeight = Value; + RetVal = TRUE; + } + else if (!wcscmp(szValueName, L"FullScreen")) + { + TermInfo->FullScreen = Value; + RetVal = TRUE; + } + else if (!wcscmp(szValueName, L"WindowPosition")) + { + TermInfo->AutoPosition = FALSE; + TermInfo->WindowOrigin.x = LOWORD(Value); + TermInfo->WindowOrigin.y = HIWORD(Value); + RetVal = TRUE; + } + } + + RegCloseKey(hKey); + return RetVal; +} + +BOOL +GuiConsoleWriteUserSettings(IN OUT PGUI_CONSOLE_INFO TermInfo, + IN LPCWSTR ConsoleTitle, + IN DWORD ProcessId) +{ + /****************************************************** + * Adapted from ConSrvWriteUserSettings in settings.c * + ******************************************************/ + + BOOL GlobalSettings = (ConsoleTitle[0] == L'\0'); + HKEY hKey; + DWORD Storage = 0; + +#define SetConsoleSetting(SettingName, SettingType, SettingSize, Setting, DefaultValue) \ +do { \ + if (GlobalSettings || (!GlobalSettings && (*(Setting) != (DefaultValue)))) \ + { \ + RegSetValueExW(hKey, (SettingName), 0, (SettingType), (PBYTE)(Setting), (SettingSize)); \ + } \ + else \ + { \ + RegDeleteValue(hKey, (SettingName)); \ + } \ +} while (0) + + if (!ConSrvOpenUserSettings(ProcessId, + ConsoleTitle, + &hKey, KEY_WRITE, + TRUE)) + { + return FALSE; + } + + SetConsoleSetting(L"FaceName", REG_SZ, (wcslen(TermInfo->FaceName) + 1) * sizeof(WCHAR), TermInfo->FaceName, L'\0'); // wcsnlen + SetConsoleSetting(L"FontFamily", REG_DWORD, sizeof(DWORD), &TermInfo->FontFamily, FF_DONTCARE); + SetConsoleSetting(L"FontSize", REG_DWORD, sizeof(DWORD), &TermInfo->FontSize, 0); + SetConsoleSetting(L"FontWeight", REG_DWORD, sizeof(DWORD), &TermInfo->FontWeight, FW_DONTCARE); + + Storage = TermInfo->FullScreen; + SetConsoleSetting(L"FullScreen", REG_DWORD, sizeof(DWORD), &Storage, FALSE); + + if (TermInfo->AutoPosition == FALSE) + { + Storage = MAKELONG(TermInfo->WindowOrigin.x, TermInfo->WindowOrigin.y); + RegSetValueExW(hKey, L"WindowPosition", 0, REG_DWORD, (PBYTE)&Storage, sizeof(DWORD)); + } + else + { + RegDeleteValue(hKey, L"WindowPosition"); + } + + RegCloseKey(hKey); + return TRUE; +} + +VOID +GuiConsoleGetDefaultSettings(IN OUT PGUI_CONSOLE_INFO TermInfo, + IN DWORD ProcessId) +{ + /******************************************************* + * Adapted from ConSrvGetDefaultSettings in settings.c * + *******************************************************/ + + if (TermInfo == NULL) return; + + /* + * 1. Load the default values + */ + // wcsncpy(TermInfo->FaceName, L"DejaVu Sans Mono", LF_FACESIZE); + // TermInfo->FontSize = MAKELONG(12, 8); // 0x0008000C; // font is 8x12 + // TermInfo->FontSize = MAKELONG(16, 16); // font is 16x16 + // TermInfo->FontWeight = FW_NORMAL; + + wcsncpy(TermInfo->FaceName, L"Fixedsys", LF_FACESIZE); // HACK: !! + // TermInfo->FaceName[0] = L'\0'; + TermInfo->FontFamily = FF_DONTCARE; + TermInfo->FontSize = 0; + TermInfo->FontWeight = FW_DONTCARE; + TermInfo->UseRasterFonts = TRUE; + + TermInfo->FullScreen = FALSE; + TermInfo->ShowWindow = SW_SHOWNORMAL; + TermInfo->AutoPosition = TRUE; + TermInfo->WindowOrigin.x = 0; + TermInfo->WindowOrigin.y = 0; + + /* + * 2. Overwrite them with the ones stored in HKCU\Console. + * If the HKCU\Console key doesn't exist, create it + * and store the default values inside. + */ + if (!GuiConsoleReadUserSettings(TermInfo, L"", ProcessId)) + { + GuiConsoleWriteUserSettings(TermInfo, L"", ProcessId); + } +} + +VOID +GuiConsoleShowConsoleProperties(PGUI_CONSOLE_DATA GuiData, + BOOL Defaults) +{ + NTSTATUS Status; + PCONSOLE Console = GuiData->Console; + PCONSOLE_SCREEN_BUFFER ActiveBuffer = Console->ActiveBuffer; + PCONSOLE_PROCESS_DATA ProcessData; + HANDLE hSection = NULL, hClientSection = NULL; + LARGE_INTEGER SectionSize; + ULONG ViewSize = 0; + SIZE_T Length = 0; + PCONSOLE_PROPS pSharedInfo = NULL; + PGUI_CONSOLE_INFO GuiInfo = NULL; + + DPRINT("GuiConsoleShowConsoleProperties entered\n"); + + /* + * Create a memory section to share with the applet, and map it. + */ + /* Holds data for console.dll + console info + terminal-specific info */ + SectionSize.QuadPart = sizeof(CONSOLE_PROPS) + sizeof(GUI_CONSOLE_INFO); + Status = NtCreateSection(&hSection, + SECTION_ALL_ACCESS, + NULL, + &SectionSize, + PAGE_READWRITE, + SEC_COMMIT, + NULL); + if (!NT_SUCCESS(Status)) + { + DPRINT1("Error: Impossible to create a shared section ; Status = %lu\n", Status); + return; + } + + Status = NtMapViewOfSection(hSection, + NtCurrentProcess(), + (PVOID*)&pSharedInfo, + 0, + 0, + NULL, + &ViewSize, + ViewUnmap, + 0, + PAGE_READWRITE); + if (!NT_SUCCESS(Status)) + { + DPRINT1("Error: Impossible to map the shared section ; Status = %lu\n", Status); + NtClose(hSection); + return; + } + + + /* + * Setup the shared console properties structure. + */ + + /* Header */ + pSharedInfo->hConsoleWindow = GuiData->hWindow; + pSharedInfo->ShowDefaultParams = Defaults; + + /* + * We fill-in the fields only if we display + * our properties, not the default ones. + */ + if (!Defaults) + { + /* Console information */ + pSharedInfo->ci.HistoryBufferSize = Console->HistoryBufferSize; + pSharedInfo->ci.NumberOfHistoryBuffers = Console->NumberOfHistoryBuffers; + pSharedInfo->ci.HistoryNoDup = Console->HistoryNoDup; + pSharedInfo->ci.QuickEdit = Console->QuickEdit; + pSharedInfo->ci.InsertMode = Console->InsertMode; + pSharedInfo->ci.InputBufferSize = 0; + pSharedInfo->ci.ScreenBufferSize = ActiveBuffer->ScreenBufferSize; + pSharedInfo->ci.ConsoleSize = ActiveBuffer->ViewSize; + pSharedInfo->ci.CursorBlinkOn; + pSharedInfo->ci.ForceCursorOff; + pSharedInfo->ci.CursorSize = ActiveBuffer->CursorInfo.dwSize; + if (GetType(ActiveBuffer) == TEXTMODE_BUFFER) + { + PTEXTMODE_SCREEN_BUFFER Buffer = (PTEXTMODE_SCREEN_BUFFER)ActiveBuffer; + + pSharedInfo->ci.ScreenAttrib = Buffer->ScreenDefaultAttrib; + pSharedInfo->ci.PopupAttrib = Buffer->PopupDefaultAttrib; + } + else // if (GetType(ActiveBuffer) == GRAPHICS_BUFFER) + { + // PGRAPHICS_SCREEN_BUFFER Buffer = (PGRAPHICS_SCREEN_BUFFER)ActiveBuffer; + DPRINT1("GuiConsoleShowConsoleProperties - Graphics buffer\n"); + + // FIXME: Gather defaults from the registry ? + pSharedInfo->ci.ScreenAttrib = DEFAULT_SCREEN_ATTRIB; + pSharedInfo->ci.PopupAttrib = DEFAULT_POPUP_ATTRIB ; + } + pSharedInfo->ci.CodePage; + + /* GUI Information */ + pSharedInfo->TerminalInfo.Size = sizeof(GUI_CONSOLE_INFO); + GuiInfo = pSharedInfo->TerminalInfo.TermInfo = (PGUI_CONSOLE_INFO)(pSharedInfo + 1); + Length = min(wcslen(GuiData->GuiInfo.FaceName) + 1, LF_FACESIZE); // wcsnlen + wcsncpy(GuiInfo->FaceName, GuiData->GuiInfo.FaceName, LF_FACESIZE); + GuiInfo->FaceName[Length] = L'\0'; + GuiInfo->FontFamily = GuiData->GuiInfo.FontFamily; + GuiInfo->FontSize = GuiData->GuiInfo.FontSize; + GuiInfo->FontWeight = GuiData->GuiInfo.FontWeight; + GuiInfo->UseRasterFonts = GuiData->GuiInfo.UseRasterFonts; + GuiInfo->FullScreen = GuiData->GuiInfo.FullScreen; + /// GuiInfo->WindowPosition = GuiData->GuiInfo.WindowPosition; + GuiInfo->AutoPosition = GuiData->GuiInfo.AutoPosition; + GuiInfo->WindowOrigin = GuiData->GuiInfo.WindowOrigin; + /* Offsetize */ + pSharedInfo->TerminalInfo.TermInfo = (PVOID)((ULONG_PTR)GuiInfo - (ULONG_PTR)pSharedInfo); + + /* Palette */ + memcpy(pSharedInfo->ci.Colors, Console->Colors, sizeof(Console->Colors)); + + /* Title of the console, original one corresponding to the one set by the console leader */ + Length = min(sizeof(pSharedInfo->ci.ConsoleTitle) / sizeof(pSharedInfo->ci.ConsoleTitle[0]) - 1, + Console->OriginalTitle.Length / sizeof(WCHAR)); + wcsncpy(pSharedInfo->ci.ConsoleTitle, Console->OriginalTitle.Buffer, Length); + } + else + { + Length = 0; + // FIXME: Load the default parameters from the registry. + } + + /* Null-terminate the title */ + pSharedInfo->ci.ConsoleTitle[Length] = L'\0'; + + + /* Unmap the view */ + NtUnmapViewOfSection(NtCurrentProcess(), pSharedInfo); + + /* Get the console leader process, our client */ + ProcessData = CONTAINING_RECORD(Console->ProcessList.Blink, + CONSOLE_PROCESS_DATA, + ConsoleLink); + + /* Duplicate the section handle for the client */ + Status = NtDuplicateObject(NtCurrentProcess(), + hSection, + ProcessData->Process->ProcessHandle, + &hClientSection, + 0, 0, DUPLICATE_SAME_ACCESS); + if (!NT_SUCCESS(Status)) + { + DPRINT1("Error: Impossible to duplicate section handle for client ; Status = %lu\n", Status); + goto Quit; + } + + /* Start the properties dialog */ + if (ProcessData->PropDispatcher) + { + _SEH2_TRY + { + HANDLE Thread = NULL; + + _SEH2_TRY + { + Thread = CreateRemoteThread(ProcessData->Process->ProcessHandle, NULL, 0, + ProcessData->PropDispatcher, + (PVOID)hClientSection, 0, NULL); + if (NULL == Thread) + { + DPRINT1("Failed thread creation (Error: 0x%x)\n", GetLastError()); + } + else + { + DPRINT("ProcessData->PropDispatcher remote thread creation succeeded, ProcessId = %x, Process = 0x%p\n", ProcessData->Process->ClientId.UniqueProcess, ProcessData->Process); + /// WaitForSingleObject(Thread, INFINITE); + } + } + _SEH2_FINALLY + { + CloseHandle(Thread); + } + _SEH2_END; + } + _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER) + { + Status = _SEH2_GetExceptionCode(); + DPRINT1("GuiConsoleShowConsoleProperties - Caught an exception, Status = %08X\n", Status); + } + _SEH2_END; + } + +Quit: + /* We have finished, close the section handle */ + NtClose(hSection); + return; +} + +NTSTATUS +GuiApplyUserSettings(PGUI_CONSOLE_DATA GuiData, + HANDLE hClientSection, + BOOL SaveSettings) +{ + NTSTATUS Status = STATUS_SUCCESS; + PCONSOLE Console = GuiData->Console; + PCONSOLE_PROCESS_DATA ProcessData; + HANDLE hSection = NULL; + ULONG ViewSize = 0; + PCONSOLE_PROPS pConInfo = NULL; + PCONSOLE_INFO ConInfo = NULL; + PTERMINAL_INFO TermInfo = NULL; + PGUI_CONSOLE_INFO GuiInfo = NULL; + + /* Get the console leader process, our client */ + ProcessData = CONTAINING_RECORD(Console->ProcessList.Blink, + CONSOLE_PROCESS_DATA, + ConsoleLink); + + /* Duplicate the section handle for ourselves */ + Status = NtDuplicateObject(ProcessData->Process->ProcessHandle, + hClientSection, + NtCurrentProcess(), + &hSection, + 0, 0, DUPLICATE_SAME_ACCESS); + if (!NT_SUCCESS(Status)) + { + DPRINT1("Error when mapping client handle, Status = %lu\n", Status); + return Status; + } + + /* Get a view of the shared section */ + Status = NtMapViewOfSection(hSection, + NtCurrentProcess(), + (PVOID*)&pConInfo, + 0, + 0, + NULL, + &ViewSize, + ViewUnmap, + 0, + PAGE_READONLY); + if (!NT_SUCCESS(Status)) + { + DPRINT1("Error when mapping view of file, Status = %lu\n", Status); + NtClose(hSection); + return Status; + } + + _SEH2_TRY + { + /* Check that the section is well-sized */ + if ( (ViewSize < sizeof(CONSOLE_PROPS)) || + (pConInfo->TerminalInfo.Size != sizeof(GUI_CONSOLE_INFO)) || + (ViewSize < sizeof(CONSOLE_PROPS) + pConInfo->TerminalInfo.Size) ) + { + DPRINT1("Error: section bad-sized: sizeof(Section) < sizeof(CONSOLE_PROPS) + sizeof(Terminal_specific_info)\n"); + Status = STATUS_INVALID_VIEW_SIZE; + _SEH2_YIELD(goto Quit); + } + + // TODO: Check that GuiData->hWindow == pConInfo->hConsoleWindow + + /* Retrieve terminal informations */ + ConInfo = &pConInfo->ci; + TermInfo = &pConInfo->TerminalInfo; + GuiInfo = TermInfo->TermInfo = (PVOID)((ULONG_PTR)pConInfo + (ULONG_PTR)TermInfo->TermInfo); + + /* + * If we don't set the default parameters, + * apply them, otherwise just save them. + */ + if (pConInfo->ShowDefaultParams == FALSE) + { + /* Set the console informations */ + ConSrvApplyUserSettings(Console, ConInfo); + + /* Set the terminal informations */ + + // memcpy(&GuiData->GuiInfo, GuiInfo, sizeof(GUI_CONSOLE_INFO)); + + /* Move the window to the user's values */ + GuiData->GuiInfo.AutoPosition = GuiInfo->AutoPosition; + GuiData->GuiInfo.WindowOrigin = GuiInfo->WindowOrigin; + GuiConsoleMoveWindow(GuiData); + + InvalidateRect(GuiData->hWindow, NULL, TRUE); + + /* + * Apply full-screen mode. + */ + GuiData->GuiInfo.FullScreen = GuiInfo->FullScreen; + // TODO: Apply it really + } + + /* + * Save settings if needed + */ + // FIXME: Do it in the console properties applet ?? + if (SaveSettings) + { + DWORD ProcessId = HandleToUlong(ProcessData->Process->ClientId.UniqueProcess); + ConSrvWriteUserSettings(ConInfo, ProcessId); + GuiConsoleWriteUserSettings(GuiInfo, ConInfo->ConsoleTitle, ProcessId); + } + + Status = STATUS_SUCCESS; + } + _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER) + { + Status = _SEH2_GetExceptionCode(); + DPRINT1("GuiApplyUserSettings - Caught an exception, Status = %08X\n", Status); + } + _SEH2_END; + +Quit: + /* Finally, close the section and return */ + NtUnmapViewOfSection(NtCurrentProcess(), pConInfo); + NtClose(hSection); + return Status; +} + +/* EOF */ diff --git a/reactos/win32ss/user/winsrv/consrv_new/frontends/gui/guisettings.h b/reactos/win32ss/user/winsrv/consrv_new/frontends/gui/guisettings.h new file mode 100644 index 00000000000..0b0201178be --- /dev/null +++ b/reactos/win32ss/user/winsrv/consrv_new/frontends/gui/guisettings.h @@ -0,0 +1,94 @@ +/* + * COPYRIGHT: See COPYING in the top level directory + * PROJECT: ReactOS Console Server DLL + * FILE: win32ss/user/winsrv/consrv/guisettings.h + * PURPOSE: GUI front-end settings management + * PROGRAMMERS: Johannes Anderwald + * Hermes Belusca-Maito (hermes.belusca@sfr.fr) + * + * NOTE: Also used by console.dll + */ + +#pragma once + +#ifndef WM_APP + #define WM_APP 0x8000 +#endif +#define PM_APPLY_CONSOLE_INFO (WM_APP + 100) + +/* STRUCTURES *****************************************************************/ + +typedef struct _GUI_CONSOLE_INFO +{ + // FONTSIGNATURE FontSignature; + WCHAR FaceName[LF_FACESIZE]; + UINT FontFamily; + DWORD FontSize; + DWORD FontWeight; + BOOL UseRasterFonts; + + BOOL FullScreen; /* Whether the console is displayed in full-screen or windowed mode */ +// ULONG HardwareState; /* _GDI_MANAGED, _DIRECT */ + + WORD ShowWindow; + BOOL AutoPosition; + POINT WindowOrigin; +} GUI_CONSOLE_INFO, *PGUI_CONSOLE_INFO; + +#ifndef CONSOLE_H__ // If we aren't included by console.dll + +typedef struct _GUI_CONSOLE_DATA +{ + CRITICAL_SECTION Lock; + HANDLE hGuiInitEvent; + BOOL WindowSizeLock; + POINT OldCursor; + + HWND hWindow; /* Handle to the console's window */ + HICON hIcon; /* Handle to the console's icon (big) */ + HICON hIconSm; /* Handle to the console's icon (small) */ + + HCURSOR hCursor; /* Handle to the mouse cursor */ + INT MouseCursorRefCount; /* The reference counter associated with the mouse cursor. >= 0 and the cursor is shown; < 0 and the cursor is hidden. */ + BOOL IgnoreNextMouseSignal; /* Used in cases where we don't want to treat a mouse signal */ + + BOOL IsCloseButtonEnabled; /* TRUE if the Close button and the corresponding system menu item are enabled (default), FALSE otherwise */ + UINT cmdIdLow ; /* Lowest menu id of the user-reserved menu id range */ + UINT cmdIdHigh; /* Highest menu id of the user-reserved menu id range */ + +// COLORREF Colors[16]; + +// PVOID ScreenBuffer; /* Hardware screen buffer */ + + HFONT Font; + UINT CharWidth; + UINT CharHeight; + + PCONSOLE Console; /* Pointer to the owned console */ + GUI_CONSOLE_INFO GuiInfo; /* GUI terminal settings */ +} GUI_CONSOLE_DATA, *PGUI_CONSOLE_DATA; + +/* FUNCTIONS ******************************************************************/ + +BOOL +GuiConsoleReadUserSettings(IN OUT PGUI_CONSOLE_INFO TermInfo, + IN LPCWSTR ConsoleTitle, + IN DWORD ProcessId); +BOOL +GuiConsoleWriteUserSettings(IN OUT PGUI_CONSOLE_INFO TermInfo, + IN LPCWSTR ConsoleTitle, + IN DWORD ProcessId); +VOID +GuiConsoleGetDefaultSettings(IN OUT PGUI_CONSOLE_INFO TermInfo, + IN DWORD ProcessId); +VOID +GuiConsoleShowConsoleProperties(PGUI_CONSOLE_DATA GuiData, + BOOL Defaults); +NTSTATUS +GuiApplyUserSettings(PGUI_CONSOLE_DATA GuiData, + HANDLE hClientSection, + BOOL SaveSettings); + +#endif + +/* EOF */ diff --git a/reactos/win32ss/user/winsrv/consrv_new/frontends/gui/guiterm.c b/reactos/win32ss/user/winsrv/consrv_new/frontends/gui/guiterm.c new file mode 100644 index 00000000000..a974e9b47b0 --- /dev/null +++ b/reactos/win32ss/user/winsrv/consrv_new/frontends/gui/guiterm.c @@ -0,0 +1,2960 @@ +/* + * COPYRIGHT: See COPYING in the top level directory + * PROJECT: ReactOS Console Server DLL + * FILE: win32ss/user/winsrv/consrv/frontends/gui/guiterm.c + * PURPOSE: GUI Terminal Front-End + * PROGRAMMERS: Gé van Geldorp + * Johannes Anderwald + * Jeffrey Morlan + * Hermes Belusca-Maito (hermes.belusca@sfr.fr) + */ + +/* INCLUDES *******************************************************************/ + +#define COBJMACROS +#define NONAMELESSUNION + +#include "consrv.h" +#include "include/conio.h" +#include "include/console.h" +#include "include/settings.h" +#include "conoutput.h" +#include "guiterm.h" +#include "guisettings.h" +#include "resource.h" + +#include + +#include +#include + +#define NDEBUG +#include + +/* GUI Console Window Class name */ +#define GUI_CONSOLE_WINDOW_CLASS L"ConsoleWindowClass" + +#ifndef WM_APP + #define WM_APP 0x8000 +#endif +#define PM_CREATE_CONSOLE (WM_APP + 1) +#define PM_DESTROY_CONSOLE (WM_APP + 2) +#define PM_RESIZE_TERMINAL (WM_APP + 3) +#define PM_CONSOLE_BEEP (WM_APP + 4) +#define PM_CONSOLE_SET_TITLE (WM_APP + 5) + + +/* Not defined in any header file */ +extern VOID WINAPI PrivateCsrssManualGuiCheck(LONG Check); +// See winsrv/usersrv/init.c line 234 + + +/* GLOBALS ********************************************************************/ + +typedef struct _GUI_INIT_INFO +{ + PCONSOLE_INFO ConsoleInfo; + PCONSOLE_START_INFO ConsoleStartInfo; + ULONG ProcessId; +} GUI_INIT_INFO, *PGUI_INIT_INFO; + +/**************************************************************\ +\** Define the Console Leader Process for the console window **/ +#define GWLP_CONSOLEWND_ALLOC (2 * sizeof(LONG_PTR)) +#define GWLP_CONSOLE_LEADER_PID 0 +#define GWLP_CONSOLE_LEADER_TID 4 + +#define SetConsoleWndConsoleLeaderCID(GuiData) \ +do { \ + PCONSOLE_PROCESS_DATA ProcessData; \ + CLIENT_ID ConsoleLeaderCID; \ + ProcessData = CONTAINING_RECORD((GuiData)->Console->ProcessList.Blink, \ + CONSOLE_PROCESS_DATA, \ + ConsoleLink); \ + ConsoleLeaderCID = ProcessData->Process->ClientId; \ + SetWindowLongPtrW((GuiData)->hWindow, GWLP_CONSOLE_LEADER_PID, (LONG_PTR)(ConsoleLeaderCID.UniqueProcess)); \ + SetWindowLongPtrW((GuiData)->hWindow, GWLP_CONSOLE_LEADER_TID, (LONG_PTR)(ConsoleLeaderCID.UniqueThread )); \ +} while (0) +/**************************************************************/ + +static BOOL ConsInitialized = FALSE; +static HICON ghDefaultIcon = NULL; +static HICON ghDefaultIconSm = NULL; +static HCURSOR ghDefaultCursor = NULL; +static HWND NotifyWnd = NULL; + +typedef struct _GUICONSOLE_MENUITEM +{ + UINT uID; + const struct _GUICONSOLE_MENUITEM *SubMenu; + WORD wCmdID; +} GUICONSOLE_MENUITEM, *PGUICONSOLE_MENUITEM; + +static const GUICONSOLE_MENUITEM GuiConsoleEditMenuItems[] = +{ + { IDS_MARK, NULL, ID_SYSTEM_EDIT_MARK }, + { IDS_COPY, NULL, ID_SYSTEM_EDIT_COPY }, + { IDS_PASTE, NULL, ID_SYSTEM_EDIT_PASTE }, + { IDS_SELECTALL, NULL, ID_SYSTEM_EDIT_SELECTALL }, + { IDS_SCROLL, NULL, ID_SYSTEM_EDIT_SCROLL }, + { IDS_FIND, NULL, ID_SYSTEM_EDIT_FIND }, + + { 0, NULL, 0 } /* End of list */ +}; + +static const GUICONSOLE_MENUITEM GuiConsoleMainMenuItems[] = +{ + { IDS_EDIT, GuiConsoleEditMenuItems, 0 }, + { IDS_DEFAULTS, NULL, ID_SYSTEM_DEFAULTS }, + { IDS_PROPERTIES, NULL, ID_SYSTEM_PROPERTIES }, + + { 0, NULL, 0 } /* End of list */ +}; + +/* + * Default 16-color palette for foreground and background + * (corresponding flags in comments). + */ +const COLORREF s_Colors[16] = +{ + RGB(0, 0, 0), // (Black) + RGB(0, 0, 128), // BLUE + RGB(0, 128, 0), // GREEN + RGB(0, 128, 128), // BLUE | GREEN + RGB(128, 0, 0), // RED + RGB(128, 0, 128), // BLUE | RED + RGB(128, 128, 0), // GREEN | RED + RGB(192, 192, 192), // BLUE | GREEN | RED + + RGB(128, 128, 128), // (Grey) INTENSITY + RGB(0, 0, 255), // BLUE | INTENSITY + RGB(0, 255, 0), // GREEN | INTENSITY + RGB(0, 255, 255), // BLUE | GREEN | INTENSITY + RGB(255, 0, 0), // RED | INTENSITY + RGB(255, 0, 255), // BLUE | RED | INTENSITY + RGB(255, 255, 0), // GREEN | RED | INTENSITY + RGB(255, 255, 255) // BLUE | GREEN | RED | INTENSITY +}; + +/* FUNCTIONS ******************************************************************/ + +static VOID +GetScreenBufferSizeUnits(IN PCONSOLE_SCREEN_BUFFER Buffer, + IN PGUI_CONSOLE_DATA GuiData, + OUT PUINT WidthUnit, + OUT PUINT HeightUnit) +{ + if (Buffer == NULL || GuiData == NULL || + WidthUnit == NULL || HeightUnit == NULL) + { + return; + } + + if (GetType(Buffer) == TEXTMODE_BUFFER) + { + *WidthUnit = GuiData->CharWidth ; + *HeightUnit = GuiData->CharHeight; + } + else /* if (GetType(Buffer) == GRAPHICS_BUFFER) */ + { + *WidthUnit = 1; + *HeightUnit = 1; + } +} + + + +static VOID +GuiConsoleAppendMenuItems(HMENU hMenu, + const GUICONSOLE_MENUITEM *Items) +{ + UINT i = 0; + WCHAR szMenuString[255]; + HMENU hSubMenu; + + do + { + if (Items[i].uID != (UINT)-1) + { + if (LoadStringW(ConSrvDllInstance, + Items[i].uID, + szMenuString, + sizeof(szMenuString) / sizeof(szMenuString[0])) > 0) + { + if (Items[i].SubMenu != NULL) + { + hSubMenu = CreatePopupMenu(); + if (hSubMenu != NULL) + { + GuiConsoleAppendMenuItems(hSubMenu, + Items[i].SubMenu); + + if (!AppendMenuW(hMenu, + MF_STRING | MF_POPUP, + (UINT_PTR)hSubMenu, + szMenuString)) + { + DestroyMenu(hSubMenu); + } + } + } + else + { + AppendMenuW(hMenu, + MF_STRING, + Items[i].wCmdID, + szMenuString); + } + } + } + else + { + AppendMenuW(hMenu, + MF_SEPARATOR, + 0, + NULL); + } + i++; + } while (!(Items[i].uID == 0 && Items[i].SubMenu == NULL && Items[i].wCmdID == 0)); +} + +static VOID +GuiConsoleCreateSysMenu(HWND hWnd) +{ + HMENU hMenu = GetSystemMenu(hWnd, FALSE); + if (hMenu != NULL) + { + GuiConsoleAppendMenuItems(hMenu, GuiConsoleMainMenuItems); + DrawMenuBar(hWnd); + } +} + +static VOID +GuiSendMenuEvent(PCONSOLE Console, UINT CmdId) +{ + INPUT_RECORD er; + + er.EventType = MENU_EVENT; + er.Event.MenuEvent.dwCommandId = CmdId; + + ConioProcessInputEvent(Console, &er); +} + +static VOID +GuiConsoleCopy(PGUI_CONSOLE_DATA GuiData); +static VOID +GuiConsolePaste(PGUI_CONSOLE_DATA GuiData); +static VOID +GuiConsoleUpdateSelection(PCONSOLE Console, PCOORD coord); +static VOID WINAPI +GuiDrawRegion(IN OUT PFRONTEND This, SMALL_RECT* Region); +static VOID +GuiConsoleResizeWindow(PGUI_CONSOLE_DATA GuiData); + + +static LRESULT +GuiConsoleHandleSysMenuCommand(PGUI_CONSOLE_DATA GuiData, WPARAM wParam, LPARAM lParam) +{ + LRESULT Ret = TRUE; + PCONSOLE Console = GuiData->Console; + PCONSOLE_SCREEN_BUFFER ActiveBuffer; + + if (!ConDrvValidateConsoleUnsafe(Console, CONSOLE_RUNNING, TRUE)) + { + Ret = FALSE; + goto Quit; + } + ActiveBuffer = ConDrvGetActiveScreenBuffer(Console); + + /* + * In case the selected menu item belongs to the user-reserved menu id range, + * send to him a menu event and return directly. The user must handle those + * reserved menu commands... + */ + if (GuiData->cmdIdLow <= (UINT)wParam && (UINT)wParam <= GuiData->cmdIdHigh) + { + GuiSendMenuEvent(Console, (UINT)wParam); + goto Unlock_Quit; + } + + /* ... otherwise, perform actions. */ + switch (wParam) + { + case ID_SYSTEM_EDIT_MARK: + { + LPWSTR WindowTitle = NULL; + SIZE_T Length = 0; + + Console->dwSelectionCursor.X = ActiveBuffer->ViewOrigin.X; + Console->dwSelectionCursor.Y = ActiveBuffer->ViewOrigin.Y; + Console->Selection.dwSelectionAnchor = Console->dwSelectionCursor; + Console->Selection.dwFlags |= CONSOLE_SELECTION_IN_PROGRESS; + GuiConsoleUpdateSelection(Console, &Console->Selection.dwSelectionAnchor); + + Length = Console->Title.Length + sizeof(L"Mark - ")/sizeof(WCHAR) + 1; + WindowTitle = ConsoleAllocHeap(0, Length * sizeof(WCHAR)); + wcscpy(WindowTitle, L"Mark - "); + wcscat(WindowTitle, Console->Title.Buffer); + SetWindowText(GuiData->hWindow, WindowTitle); + ConsoleFreeHeap(WindowTitle); + + break; + } + + case ID_SYSTEM_EDIT_COPY: + GuiConsoleCopy(GuiData); + break; + + case ID_SYSTEM_EDIT_PASTE: + GuiConsolePaste(GuiData); + break; + + case ID_SYSTEM_EDIT_SELECTALL: + { + LPWSTR WindowTitle = NULL; + SIZE_T Length = 0; + + Console->Selection.dwSelectionAnchor.X = 0; + Console->Selection.dwSelectionAnchor.Y = 0; + Console->dwSelectionCursor.X = ActiveBuffer->ViewSize.X - 1; + Console->dwSelectionCursor.Y = ActiveBuffer->ViewSize.Y - 1; + Console->Selection.dwFlags |= CONSOLE_SELECTION_IN_PROGRESS | CONSOLE_MOUSE_SELECTION; + GuiConsoleUpdateSelection(Console, &Console->dwSelectionCursor); + + Length = Console->Title.Length + sizeof(L"Selection - ")/sizeof(WCHAR) + 1; + WindowTitle = ConsoleAllocHeap(0, Length * sizeof(WCHAR)); + wcscpy(WindowTitle, L"Selection - "); + wcscat(WindowTitle, Console->Title.Buffer); + SetWindowText(GuiData->hWindow, WindowTitle); + ConsoleFreeHeap(WindowTitle); + + break; + } + + case ID_SYSTEM_EDIT_SCROLL: + DPRINT1("Scrolling is not handled yet\n"); + break; + + case ID_SYSTEM_EDIT_FIND: + DPRINT1("Finding is not handled yet\n"); + break; + + case ID_SYSTEM_DEFAULTS: + GuiConsoleShowConsoleProperties(GuiData, TRUE); + break; + + case ID_SYSTEM_PROPERTIES: + GuiConsoleShowConsoleProperties(GuiData, FALSE); + break; + + default: + Ret = FALSE; + break; + } + +Unlock_Quit: + LeaveCriticalSection(&Console->Lock); +Quit: + if (!Ret) + Ret = DefWindowProcW(GuiData->hWindow, WM_SYSCOMMAND, wParam, lParam); + + return Ret; +} + +static PGUI_CONSOLE_DATA +GuiGetGuiData(HWND hWnd) +{ + /* This function ensures that the console pointer is not NULL */ + PGUI_CONSOLE_DATA GuiData = (PGUI_CONSOLE_DATA)GetWindowLongPtrW(hWnd, GWLP_USERDATA); + return ( ((GuiData == NULL) || (GuiData->hWindow == hWnd && GuiData->Console != NULL)) ? GuiData : NULL ); +} + +VOID +GuiConsoleMoveWindow(PGUI_CONSOLE_DATA GuiData) +{ + /* Move the window if needed (not positioned by the system) */ + if (!GuiData->GuiInfo.AutoPosition) + { + SetWindowPos(GuiData->hWindow, + NULL, + GuiData->GuiInfo.WindowOrigin.x, + GuiData->GuiInfo.WindowOrigin.y, + 0, + 0, + SWP_NOZORDER | SWP_NOSIZE | SWP_NOACTIVATE); + } +} + +static VOID +GuiConsoleResizeWindow(PGUI_CONSOLE_DATA GuiData) +{ + PCONSOLE Console = GuiData->Console; + PCONSOLE_SCREEN_BUFFER Buff = ConDrvGetActiveScreenBuffer(Console); + SCROLLINFO sInfo; + + DWORD Width, Height; + UINT WidthUnit, HeightUnit; + + GetScreenBufferSizeUnits(Buff, GuiData, &WidthUnit, &HeightUnit); + + Width = Buff->ViewSize.X * WidthUnit + + 2 * (GetSystemMetrics(SM_CXFRAME) + GetSystemMetrics(SM_CXEDGE)); + Height = Buff->ViewSize.Y * HeightUnit + + 2 * (GetSystemMetrics(SM_CYFRAME) + GetSystemMetrics(SM_CYEDGE)) + GetSystemMetrics(SM_CYCAPTION); + + /* Set scrollbar sizes */ + sInfo.cbSize = sizeof(SCROLLINFO); + sInfo.fMask = SIF_RANGE | SIF_PAGE | SIF_POS; + sInfo.nMin = 0; + if (Buff->ScreenBufferSize.Y > Buff->ViewSize.Y) + { + sInfo.nMax = Buff->ScreenBufferSize.Y - 1; + sInfo.nPage = Buff->ViewSize.Y; + sInfo.nPos = Buff->ViewOrigin.Y; + SetScrollInfo(GuiData->hWindow, SB_VERT, &sInfo, TRUE); + Width += GetSystemMetrics(SM_CXVSCROLL); + ShowScrollBar(GuiData->hWindow, SB_VERT, TRUE); + } + else + { + ShowScrollBar(GuiData->hWindow, SB_VERT, FALSE); + } + + if (Buff->ScreenBufferSize.X > Buff->ViewSize.X) + { + sInfo.nMax = Buff->ScreenBufferSize.X - 1; + sInfo.nPage = Buff->ViewSize.X; + sInfo.nPos = Buff->ViewOrigin.X; + SetScrollInfo(GuiData->hWindow, SB_HORZ, &sInfo, TRUE); + Height += GetSystemMetrics(SM_CYHSCROLL); + ShowScrollBar(GuiData->hWindow, SB_HORZ, TRUE); + } + else + { + ShowScrollBar(GuiData->hWindow, SB_HORZ, FALSE); + } + + /* Resize the window */ + SetWindowPos(GuiData->hWindow, NULL, 0, 0, Width, Height, + SWP_NOZORDER | SWP_NOMOVE | SWP_NOACTIVATE | SWP_NOCOPYBITS); + // NOTE: The SWP_NOCOPYBITS flag can be replaced by a subsequent call + // to: InvalidateRect(GuiData->hWindow, NULL, TRUE); +} + +static VOID +GuiConsoleSwitchFullScreen(PGUI_CONSOLE_DATA GuiData) +{ + PCONSOLE Console = GuiData->Console; + // DEVMODE dmScreenSettings; + + if (!ConDrvValidateConsoleUnsafe(Console, CONSOLE_RUNNING, TRUE)) return; + + /* Switch to full-screen or to windowed mode */ + GuiData->GuiInfo.FullScreen = !GuiData->GuiInfo.FullScreen; + DPRINT1("GuiConsoleSwitchFullScreen - Switch to %s ...\n", + (GuiData->GuiInfo.FullScreen ? "full-screen" : "windowed mode")); + + // TODO: Change window appearance. + // See: + // http://stackoverflow.com/questions/2382464/win32-full-screen-and-hiding-taskbar + // http://stackoverflow.com/questions/3549148/fullscreen-management-with-winapi + // http://blogs.msdn.com/b/oldnewthing/archive/2010/04/12/9994016.aspx + // http://stackoverflow.com/questions/1400654/how-do-i-put-my-opengl-app-into-fullscreen-mode + // http://nehe.gamedev.net/tutorial/creating_an_opengl_window_win32/13001/ +#if 0 + if (GuiData->GuiInfo.FullScreen) + { + memset(&dmScreenSettings, 0, sizeof(dmScreenSettings)); + dmScreenSettings.dmSize = sizeof(dmScreenSettings); + dmScreenSettings.dmDisplayFixedOutput = DMDFO_CENTER; // DMDFO_STRETCH // DMDFO_DEFAULT + dmScreenSettings.dmPelsWidth = 640; // Console->ActiveBuffer->ViewSize.X * GuiData->CharWidth; + dmScreenSettings.dmPelsHeight = 480; // Console->ActiveBuffer->ViewSize.Y * GuiData->CharHeight; + dmScreenSettings.dmBitsPerPel = 32; + dmScreenSettings.dmFields = DM_BITSPERPEL | DM_PELSWIDTH | DM_PELSHEIGHT; + ChangeDisplaySettings(&dmScreenSettings, CDS_FULLSCREEN); + } + else + { + } +#endif + + LeaveCriticalSection(&Console->Lock); +} + +static BOOL +GuiConsoleHandleNcCreate(HWND hWnd, LPCREATESTRUCTW Create) +{ + PGUI_CONSOLE_DATA GuiData = (PGUI_CONSOLE_DATA)Create->lpCreateParams; + PCONSOLE Console; + HDC Dc; + HFONT OldFont; + TEXTMETRICW Metrics; + SIZE CharSize; + + DPRINT("GuiConsoleHandleNcCreate\n"); + + if (NULL == GuiData) + { + DPRINT1("GuiConsoleNcCreate: No GUI data\n"); + return FALSE; + } + + Console = GuiData->Console; + + GuiData->hWindow = hWnd; + + GuiData->Font = CreateFontW(LOWORD(GuiData->GuiInfo.FontSize), + 0, // HIWORD(GuiData->GuiInfo.FontSize), + 0, + TA_BASELINE, + GuiData->GuiInfo.FontWeight, + FALSE, + FALSE, + FALSE, + OEM_CHARSET, + OUT_DEFAULT_PRECIS, + CLIP_DEFAULT_PRECIS, + NONANTIALIASED_QUALITY, + FIXED_PITCH | GuiData->GuiInfo.FontFamily /* FF_DONTCARE */, + GuiData->GuiInfo.FaceName); + + if (NULL == GuiData->Font) + { + DPRINT1("GuiConsoleNcCreate: CreateFont failed\n"); + GuiData->hWindow = NULL; + SetEvent(GuiData->hGuiInitEvent); + return FALSE; + } + Dc = GetDC(GuiData->hWindow); + if (NULL == Dc) + { + DPRINT1("GuiConsoleNcCreate: GetDC failed\n"); + DeleteObject(GuiData->Font); + GuiData->hWindow = NULL; + SetEvent(GuiData->hGuiInitEvent); + return FALSE; + } + OldFont = SelectObject(Dc, GuiData->Font); + if (NULL == OldFont) + { + DPRINT1("GuiConsoleNcCreate: SelectObject failed\n"); + ReleaseDC(GuiData->hWindow, Dc); + DeleteObject(GuiData->Font); + GuiData->hWindow = NULL; + SetEvent(GuiData->hGuiInitEvent); + return FALSE; + } + if (!GetTextMetricsW(Dc, &Metrics)) + { + DPRINT1("GuiConsoleNcCreate: GetTextMetrics failed\n"); + SelectObject(Dc, OldFont); + ReleaseDC(GuiData->hWindow, Dc); + DeleteObject(GuiData->Font); + GuiData->hWindow = NULL; + SetEvent(GuiData->hGuiInitEvent); + return FALSE; + } + GuiData->CharWidth = Metrics.tmMaxCharWidth; + GuiData->CharHeight = Metrics.tmHeight + Metrics.tmExternalLeading; + + /* Measure real char width more precisely if possible. */ + if (GetTextExtentPoint32W(Dc, L"R", 1, &CharSize)) + GuiData->CharWidth = CharSize.cx; + + SelectObject(Dc, OldFont); + + ReleaseDC(GuiData->hWindow, Dc); + + // FIXME: Keep these instructions here ? /////////////////////////////////// + Console->ActiveBuffer->CursorBlinkOn = TRUE; + Console->ActiveBuffer->ForceCursorOff = FALSE; + //////////////////////////////////////////////////////////////////////////// + + SetWindowLongPtrW(GuiData->hWindow, GWLP_USERDATA, (DWORD_PTR)GuiData); + + SetTimer(GuiData->hWindow, CONGUI_UPDATE_TIMER, CONGUI_UPDATE_TIME, NULL); + GuiConsoleCreateSysMenu(GuiData->hWindow); + + DPRINT("GuiConsoleHandleNcCreate - setting start event\n"); + SetEvent(GuiData->hGuiInitEvent); + + return (BOOL)DefWindowProcW(GuiData->hWindow, WM_NCCREATE, 0, (LPARAM)Create); +} + +static VOID +SmallRectToRect(PGUI_CONSOLE_DATA GuiData, PRECT Rect, PSMALL_RECT SmallRect) +{ + PCONSOLE Console = GuiData->Console; + PCONSOLE_SCREEN_BUFFER Buffer = ConDrvGetActiveScreenBuffer(Console); + UINT WidthUnit, HeightUnit; + + GetScreenBufferSizeUnits(Buffer, GuiData, &WidthUnit, &HeightUnit); + + Rect->left = (SmallRect->Left - Buffer->ViewOrigin.X) * WidthUnit ; + Rect->top = (SmallRect->Top - Buffer->ViewOrigin.Y) * HeightUnit; + Rect->right = (SmallRect->Right + 1 - Buffer->ViewOrigin.X) * WidthUnit ; + Rect->bottom = (SmallRect->Bottom + 1 - Buffer->ViewOrigin.Y) * HeightUnit; +} + +static VOID +GuiConsoleUpdateSelection(PCONSOLE Console, PCOORD coord) +{ + PGUI_CONSOLE_DATA GuiData = Console->TermIFace.Data; + RECT oldRect, newRect; + + SmallRectToRect(GuiData, &oldRect, &Console->Selection.srSelection); + + if (coord != NULL) + { + SMALL_RECT rc; + /* exchange left/top with right/bottom if required */ + rc.Left = min(Console->Selection.dwSelectionAnchor.X, coord->X); + rc.Top = min(Console->Selection.dwSelectionAnchor.Y, coord->Y); + rc.Right = max(Console->Selection.dwSelectionAnchor.X, coord->X); + rc.Bottom = max(Console->Selection.dwSelectionAnchor.Y, coord->Y); + + SmallRectToRect(GuiData, &newRect, &rc); + + if (Console->Selection.dwFlags & CONSOLE_SELECTION_NOT_EMPTY) + { + if (memcmp(&rc, &Console->Selection.srSelection, sizeof(SMALL_RECT)) != 0) + { + HRGN rgn1, rgn2; + + /* calculate the region that needs to be updated */ + if ((rgn1 = CreateRectRgnIndirect(&oldRect))) + { + if ((rgn2 = CreateRectRgnIndirect(&newRect))) + { + if (CombineRgn(rgn1, rgn2, rgn1, RGN_XOR) != ERROR) + { + InvalidateRgn(GuiData->hWindow, rgn1, FALSE); + } + DeleteObject(rgn2); + } + DeleteObject(rgn1); + } + } + } + else + { + InvalidateRect(GuiData->hWindow, &newRect, FALSE); + } + Console->Selection.dwFlags |= CONSOLE_SELECTION_NOT_EMPTY; + Console->Selection.srSelection = rc; + Console->dwSelectionCursor = *coord; + ConioPause(Console, PAUSED_FROM_SELECTION); + } + else + { + /* clear the selection */ + if (Console->Selection.dwFlags & CONSOLE_SELECTION_NOT_EMPTY) + { + InvalidateRect(GuiData->hWindow, &oldRect, FALSE); + } + Console->Selection.dwFlags = CONSOLE_NO_SELECTION; + ConioUnpause(Console, PAUSED_FROM_SELECTION); + } +} + + +VOID +GuiPaintTextModeBuffer(PTEXTMODE_SCREEN_BUFFER Buffer, + PGUI_CONSOLE_DATA GuiData, + HDC hDC, + PRECT rc); +VOID +GuiPaintGraphicsBuffer(PGRAPHICS_SCREEN_BUFFER Buffer, + PGUI_CONSOLE_DATA GuiData, + HDC hDC, + PRECT rc); + +static VOID +GuiConsoleHandlePaint(PGUI_CONSOLE_DATA GuiData) +{ + BOOL Success = TRUE; + PCONSOLE Console = GuiData->Console; + PCONSOLE_SCREEN_BUFFER ActiveBuffer; + HDC hDC; + PAINTSTRUCT ps; + + if (!ConDrvValidateConsoleUnsafe(Console, CONSOLE_RUNNING, TRUE)) + { + Success = FALSE; + goto Quit; + } + ActiveBuffer = ConDrvGetActiveScreenBuffer(Console); + + hDC = BeginPaint(GuiData->hWindow, &ps); + if (hDC != NULL && + ps.rcPaint.left < ps.rcPaint.right && + ps.rcPaint.top < ps.rcPaint.bottom) + { + EnterCriticalSection(&GuiData->Lock); + + if (GetType(ActiveBuffer) == TEXTMODE_BUFFER) + { + GuiPaintTextModeBuffer((PTEXTMODE_SCREEN_BUFFER)ActiveBuffer, + GuiData, hDC, &ps.rcPaint); + } + else /* if (GetType(ActiveBuffer) == GRAPHICS_BUFFER) */ + { + GuiPaintGraphicsBuffer((PGRAPHICS_SCREEN_BUFFER)ActiveBuffer, + GuiData, hDC, &ps.rcPaint); + } + + if (Console->Selection.dwFlags & CONSOLE_SELECTION_NOT_EMPTY) + { + RECT rc; + SmallRectToRect(GuiData, &rc, &Console->Selection.srSelection); + + /* invert the selection */ + if (IntersectRect(&rc, &ps.rcPaint, &rc)) + { + PatBlt(hDC, + rc.left, + rc.top, + rc.right - rc.left, + rc.bottom - rc.top, + DSTINVERT); + } + } + + LeaveCriticalSection(&GuiData->Lock); + } + EndPaint(GuiData->hWindow, &ps); + +Quit: + if (Success) + LeaveCriticalSection(&Console->Lock); + else + DefWindowProcW(GuiData->hWindow, WM_PAINT, 0, 0); + + return; +} + +static BOOL +IsSystemKey(WORD VirtualKeyCode) +{ + switch (VirtualKeyCode) + { + /* From MSDN, "Virtual-Key Codes" */ + case VK_RETURN: + case VK_SHIFT: + case VK_CONTROL: + case VK_MENU: + case VK_PAUSE: + case VK_CAPITAL: + case VK_ESCAPE: + case VK_LWIN: + case VK_RWIN: + case VK_NUMLOCK: + case VK_SCROLL: + return TRUE; + default: + return FALSE; + } +} + +static VOID +GuiConsoleHandleKey(PGUI_CONSOLE_DATA GuiData, UINT msg, WPARAM wParam, LPARAM lParam) +{ + PCONSOLE Console = GuiData->Console; + PCONSOLE_SCREEN_BUFFER ActiveBuffer; + + if (!ConDrvValidateConsoleUnsafe(Console, CONSOLE_RUNNING, TRUE)) return; + + ActiveBuffer = ConDrvGetActiveScreenBuffer(Console); + + if (Console->Selection.dwFlags & CONSOLE_SELECTION_IN_PROGRESS) + { + WORD VirtualKeyCode = LOWORD(wParam); + + if (msg != WM_KEYDOWN) goto Quit; + + if (VirtualKeyCode == VK_RETURN) + { + /* Copy (and clear) selection if ENTER is pressed */ + GuiConsoleCopy(GuiData); + goto Quit; + } + else if ( VirtualKeyCode == VK_ESCAPE || + (VirtualKeyCode == 'C' && GetKeyState(VK_CONTROL) & 0x8000) ) + { + /* Cancel selection if ESC or Ctrl-C are pressed */ + GuiConsoleUpdateSelection(Console, NULL); + SetWindowText(GuiData->hWindow, Console->Title.Buffer); + + goto Quit; + } + + if ((Console->Selection.dwFlags & CONSOLE_MOUSE_SELECTION) == 0) + { + /* Keyboard selection mode */ + BOOL Interpreted = FALSE; + BOOL MajPressed = (GetKeyState(VK_SHIFT) & 0x8000); + + switch (VirtualKeyCode) + { + case VK_LEFT: + { + Interpreted = TRUE; + if (Console->dwSelectionCursor.X > 0) + Console->dwSelectionCursor.X--; + + break; + } + + case VK_RIGHT: + { + Interpreted = TRUE; + if (Console->dwSelectionCursor.X < ActiveBuffer->ScreenBufferSize.X - 1) + Console->dwSelectionCursor.X++; + + break; + } + + case VK_UP: + { + Interpreted = TRUE; + if (Console->dwSelectionCursor.Y > 0) + Console->dwSelectionCursor.Y--; + + break; + } + + case VK_DOWN: + { + Interpreted = TRUE; + if (Console->dwSelectionCursor.Y < ActiveBuffer->ScreenBufferSize.Y - 1) + Console->dwSelectionCursor.Y++; + + break; + } + + case VK_HOME: + { + Interpreted = TRUE; + Console->dwSelectionCursor.X = 0; + Console->dwSelectionCursor.Y = 0; + break; + } + + case VK_END: + { + Interpreted = TRUE; + Console->dwSelectionCursor.Y = ActiveBuffer->ScreenBufferSize.Y - 1; + break; + } + + case VK_PRIOR: + { + Interpreted = TRUE; + Console->dwSelectionCursor.Y -= ActiveBuffer->ViewSize.Y; + if (Console->dwSelectionCursor.Y < 0) + Console->dwSelectionCursor.Y = 0; + + break; + } + + case VK_NEXT: + { + Interpreted = TRUE; + Console->dwSelectionCursor.Y += ActiveBuffer->ViewSize.Y; + if (Console->dwSelectionCursor.Y >= ActiveBuffer->ScreenBufferSize.Y) + Console->dwSelectionCursor.Y = ActiveBuffer->ScreenBufferSize.Y - 1; + + break; + } + + default: + break; + } + + if (Interpreted) + { + if (!MajPressed) + Console->Selection.dwSelectionAnchor = Console->dwSelectionCursor; + + GuiConsoleUpdateSelection(Console, &Console->dwSelectionCursor); + } + else if (!IsSystemKey(VirtualKeyCode)) + { + /* Emit an error beep sound */ + SendNotifyMessage(GuiData->hWindow, PM_CONSOLE_BEEP, 0, 0); + } + + goto Quit; + } + else + { + /* Mouse selection mode */ + + if (!IsSystemKey(VirtualKeyCode)) + { + /* Clear the selection and send the key into the input buffer */ + GuiConsoleUpdateSelection(Console, NULL); + SetWindowText(GuiData->hWindow, Console->Title.Buffer); + } + else + { + goto Quit; + } + } + } + + if ((Console->Selection.dwFlags & CONSOLE_SELECTION_IN_PROGRESS) == 0) + { + MSG Message; + + Message.hwnd = GuiData->hWindow; + Message.message = msg; + Message.wParam = wParam; + Message.lParam = lParam; + + ConioProcessKey(Console, &Message); + } + +Quit: + LeaveCriticalSection(&Console->Lock); +} + +static VOID +GuiInvalidateCell(IN OUT PFRONTEND This, SHORT x, SHORT y) +{ + SMALL_RECT CellRect = { x, y, x, y }; + GuiDrawRegion(This, &CellRect); +} + +static VOID +GuiConsoleHandleTimer(PGUI_CONSOLE_DATA GuiData) +{ + PCONSOLE Console = GuiData->Console; + PCONSOLE_SCREEN_BUFFER Buff; + + SetTimer(GuiData->hWindow, CONGUI_UPDATE_TIMER, CURSOR_BLINK_TIME, NULL); + + if (!ConDrvValidateConsoleUnsafe(Console, CONSOLE_RUNNING, TRUE)) return; + + Buff = ConDrvGetActiveScreenBuffer(Console); + + if (GetType(Buff) == TEXTMODE_BUFFER) + { + GuiInvalidateCell(&Console->TermIFace, Buff->CursorPosition.X, Buff->CursorPosition.Y); + Buff->CursorBlinkOn = !Buff->CursorBlinkOn; + + if ((GuiData->OldCursor.x != Buff->CursorPosition.X) || (GuiData->OldCursor.y != Buff->CursorPosition.Y)) + { + SCROLLINFO xScroll; + int OldScrollX = -1, OldScrollY = -1; + int NewScrollX = -1, NewScrollY = -1; + + xScroll.cbSize = sizeof(SCROLLINFO); + xScroll.fMask = SIF_POS; + // Capture the original position of the scroll bars and save them. + if (GetScrollInfo(GuiData->hWindow, SB_HORZ, &xScroll)) OldScrollX = xScroll.nPos; + if (GetScrollInfo(GuiData->hWindow, SB_VERT, &xScroll)) OldScrollY = xScroll.nPos; + + // If we successfully got the info for the horizontal scrollbar + if (OldScrollX >= 0) + { + if ((Buff->CursorPosition.X < Buff->ViewOrigin.X) || (Buff->CursorPosition.X >= (Buff->ViewOrigin.X + Buff->ViewSize.X))) + { + // Handle the horizontal scroll bar + if (Buff->CursorPosition.X >= Buff->ViewSize.X) NewScrollX = Buff->CursorPosition.X - Buff->ViewSize.X + 1; + else NewScrollX = 0; + } + else + { + NewScrollX = OldScrollX; + } + } + // If we successfully got the info for the vertical scrollbar + if (OldScrollY >= 0) + { + if ((Buff->CursorPosition.Y < Buff->ViewOrigin.Y) || (Buff->CursorPosition.Y >= (Buff->ViewOrigin.Y + Buff->ViewSize.Y))) + { + // Handle the vertical scroll bar + if (Buff->CursorPosition.Y >= Buff->ViewSize.Y) NewScrollY = Buff->CursorPosition.Y - Buff->ViewSize.Y + 1; + else NewScrollY = 0; + } + else + { + NewScrollY = OldScrollY; + } + } + + // Adjust scroll bars and refresh the window if the cursor has moved outside the visible area + // NOTE: OldScroll# and NewScroll# will both be -1 (initial value) if the info for the respective scrollbar + // was not obtained successfully in the previous steps. This means their difference is 0 (no scrolling) + // and their associated scrollbar is left alone. + if ((OldScrollX != NewScrollX) || (OldScrollY != NewScrollY)) + { + Buff->ViewOrigin.X = NewScrollX; + Buff->ViewOrigin.Y = NewScrollY; + ScrollWindowEx(GuiData->hWindow, + (OldScrollX - NewScrollX) * GuiData->CharWidth, + (OldScrollY - NewScrollY) * GuiData->CharHeight, + NULL, + NULL, + NULL, + NULL, + SW_INVALIDATE); + if (NewScrollX >= 0) + { + xScroll.nPos = NewScrollX; + SetScrollInfo(GuiData->hWindow, SB_HORZ, &xScroll, TRUE); + } + if (NewScrollY >= 0) + { + xScroll.nPos = NewScrollY; + SetScrollInfo(GuiData->hWindow, SB_VERT, &xScroll, TRUE); + } + UpdateWindow(GuiData->hWindow); + GuiData->OldCursor.x = Buff->CursorPosition.X; + GuiData->OldCursor.y = Buff->CursorPosition.Y; + } + } + } + else /* if (GetType(Buff) == GRAPHICS_BUFFER) */ + { + } + + LeaveCriticalSection(&Console->Lock); +} + +static BOOL +GuiConsoleHandleClose(PGUI_CONSOLE_DATA GuiData) +{ + PCONSOLE Console = GuiData->Console; + + if (!ConDrvValidateConsoleUnsafe(Console, CONSOLE_RUNNING, TRUE)) + return TRUE; + + // TODO: Prompt for termination ? (Warn the user about possible apps running in this console) + + /* + * FIXME: Windows will wait up to 5 seconds for the thread to exit. + * We shouldn't wait here, though, since the console lock is entered. + * A copy of the thread list probably needs to be made. + */ + ConDrvConsoleProcessCtrlEvent(Console, 0, CTRL_CLOSE_EVENT); + + LeaveCriticalSection(&Console->Lock); + return FALSE; +} + +static LRESULT +GuiConsoleHandleNcDestroy(HWND hWnd) +{ + KillTimer(hWnd, CONGUI_UPDATE_TIMER); + GetSystemMenu(hWnd, TRUE); + + /* Free the GuiData registration */ + SetWindowLongPtrW(hWnd, GWLP_USERDATA, (DWORD_PTR)NULL); + + return DefWindowProcW(hWnd, WM_NCDESTROY, 0, 0); +} + +static COORD +PointToCoord(PGUI_CONSOLE_DATA GuiData, LPARAM lParam) +{ + PCONSOLE Console = GuiData->Console; + PCONSOLE_SCREEN_BUFFER Buffer = ConDrvGetActiveScreenBuffer(Console); + COORD Coord; + UINT WidthUnit, HeightUnit; + + GetScreenBufferSizeUnits(Buffer, GuiData, &WidthUnit, &HeightUnit); + + Coord.X = Buffer->ViewOrigin.X + ((SHORT)LOWORD(lParam) / (int)WidthUnit ); + Coord.Y = Buffer->ViewOrigin.Y + ((SHORT)HIWORD(lParam) / (int)HeightUnit); + + /* Clip coordinate to ensure it's inside buffer */ + if (Coord.X < 0) + Coord.X = 0; + else if (Coord.X >= Buffer->ScreenBufferSize.X) + Coord.X = Buffer->ScreenBufferSize.X - 1; + + if (Coord.Y < 0) + Coord.Y = 0; + else if (Coord.Y >= Buffer->ScreenBufferSize.Y) + Coord.Y = Buffer->ScreenBufferSize.Y - 1; + + return Coord; +} + +static LRESULT +GuiConsoleHandleMouse(PGUI_CONSOLE_DATA GuiData, UINT msg, WPARAM wParam, LPARAM lParam) +{ + BOOL Err = FALSE; + PCONSOLE Console = GuiData->Console; + + if (GuiData->IgnoreNextMouseSignal) + { + if (msg != WM_LBUTTONDOWN && + msg != WM_MBUTTONDOWN && + msg != WM_RBUTTONDOWN) + { + /* + * If this mouse signal is not a button-down action, + * then it is the last signal being ignored. + */ + GuiData->IgnoreNextMouseSignal = FALSE; + } + else + { + /* + * This mouse signal is a button-down action. + * Ignore it and perform default action. + */ + Err = TRUE; + } + goto Quit; + } + + if (!ConDrvValidateConsoleUnsafe(Console, CONSOLE_RUNNING, TRUE)) + { + Err = TRUE; + goto Quit; + } + + if ( (Console->Selection.dwFlags & CONSOLE_SELECTION_IN_PROGRESS) || + (Console->QuickEdit) ) + { + switch (msg) + { + case WM_LBUTTONDOWN: + { + LPWSTR WindowTitle = NULL; + SIZE_T Length = 0; + + Console->Selection.dwSelectionAnchor = PointToCoord(GuiData, lParam); + SetCapture(GuiData->hWindow); + Console->Selection.dwFlags |= CONSOLE_SELECTION_IN_PROGRESS | CONSOLE_MOUSE_SELECTION | CONSOLE_MOUSE_DOWN; + GuiConsoleUpdateSelection(Console, &Console->Selection.dwSelectionAnchor); + + Length = Console->Title.Length + sizeof(L"Selection - ")/sizeof(WCHAR) + 1; + WindowTitle = ConsoleAllocHeap(0, Length * sizeof(WCHAR)); + wcscpy(WindowTitle, L"Selection - "); + wcscat(WindowTitle, Console->Title.Buffer); + SetWindowText(GuiData->hWindow, WindowTitle); + ConsoleFreeHeap(WindowTitle); + + break; + } + + case WM_LBUTTONUP: + { + COORD c; + + if (!(Console->Selection.dwFlags & CONSOLE_MOUSE_DOWN)) break; + + c = PointToCoord(GuiData, lParam); + Console->Selection.dwFlags &= ~CONSOLE_MOUSE_DOWN; + GuiConsoleUpdateSelection(Console, &c); + ReleaseCapture(); + + break; + } + + case WM_LBUTTONDBLCLK: + { + DPRINT1("Handle left-double-click for selecting a word\n"); + break; + } + + case WM_RBUTTONDOWN: + case WM_RBUTTONDBLCLK: + { + if (!(Console->Selection.dwFlags & CONSOLE_SELECTION_NOT_EMPTY)) + { + GuiConsolePaste(GuiData); + } + else + { + GuiConsoleCopy(GuiData); + } + + GuiData->IgnoreNextMouseSignal = TRUE; + break; + } + + case WM_MOUSEMOVE: + { + COORD c; + + if (!(wParam & MK_LBUTTON)) break; + if (!(Console->Selection.dwFlags & CONSOLE_MOUSE_DOWN)) break; + + c = PointToCoord(GuiData, lParam); /* TODO: Scroll buffer to bring c into view */ + GuiConsoleUpdateSelection(Console, &c); + + break; + } + + default: + Err = FALSE; // TRUE; + break; + } + } + else if (Console->InputBuffer.Mode & ENABLE_MOUSE_INPUT) + { + INPUT_RECORD er; + WORD wKeyState = GET_KEYSTATE_WPARAM(wParam); + DWORD dwButtonState = 0; + DWORD dwControlKeyState = 0; + DWORD dwEventFlags = 0; + + switch (msg) + { + case WM_LBUTTONDOWN: + SetCapture(GuiData->hWindow); + dwButtonState = FROM_LEFT_1ST_BUTTON_PRESSED; + dwEventFlags = 0; + break; + + case WM_MBUTTONDOWN: + SetCapture(GuiData->hWindow); + dwButtonState = FROM_LEFT_2ND_BUTTON_PRESSED; + dwEventFlags = 0; + break; + + case WM_RBUTTONDOWN: + SetCapture(GuiData->hWindow); + dwButtonState = RIGHTMOST_BUTTON_PRESSED; + dwEventFlags = 0; + break; + + case WM_LBUTTONUP: + ReleaseCapture(); + dwButtonState = 0; + dwEventFlags = 0; + break; + + case WM_MBUTTONUP: + ReleaseCapture(); + dwButtonState = 0; + dwEventFlags = 0; + break; + + case WM_RBUTTONUP: + ReleaseCapture(); + dwButtonState = 0; + dwEventFlags = 0; + break; + + case WM_LBUTTONDBLCLK: + dwButtonState = FROM_LEFT_1ST_BUTTON_PRESSED; + dwEventFlags = DOUBLE_CLICK; + break; + + case WM_MBUTTONDBLCLK: + dwButtonState = FROM_LEFT_2ND_BUTTON_PRESSED; + dwEventFlags = DOUBLE_CLICK; + break; + + case WM_RBUTTONDBLCLK: + dwButtonState = RIGHTMOST_BUTTON_PRESSED; + dwEventFlags = DOUBLE_CLICK; + break; + + case WM_MOUSEMOVE: + dwButtonState = 0; + dwEventFlags = MOUSE_MOVED; + break; + + case WM_MOUSEWHEEL: + dwButtonState = GET_WHEEL_DELTA_WPARAM(wParam) << 16; + dwEventFlags = MOUSE_WHEELED; + break; + + case WM_MOUSEHWHEEL: + dwButtonState = GET_WHEEL_DELTA_WPARAM(wParam) << 16; + dwEventFlags = MOUSE_HWHEELED; + break; + + default: + Err = TRUE; + break; + } + + if (!Err) + { + if (wKeyState & MK_LBUTTON) + dwButtonState |= FROM_LEFT_1ST_BUTTON_PRESSED; + if (wKeyState & MK_MBUTTON) + dwButtonState |= FROM_LEFT_2ND_BUTTON_PRESSED; + if (wKeyState & MK_RBUTTON) + dwButtonState |= RIGHTMOST_BUTTON_PRESSED; + + if (GetKeyState(VK_RMENU) & 0x8000) + dwControlKeyState |= RIGHT_ALT_PRESSED; + if (GetKeyState(VK_LMENU) & 0x8000) + dwControlKeyState |= LEFT_ALT_PRESSED; + if (GetKeyState(VK_RCONTROL) & 0x8000) + dwControlKeyState |= RIGHT_CTRL_PRESSED; + if (GetKeyState(VK_LCONTROL) & 0x8000) + dwControlKeyState |= LEFT_CTRL_PRESSED; + if (GetKeyState(VK_SHIFT) & 0x8000) + dwControlKeyState |= SHIFT_PRESSED; + if (GetKeyState(VK_NUMLOCK) & 0x0001) + dwControlKeyState |= NUMLOCK_ON; + if (GetKeyState(VK_SCROLL) & 0x0001) + dwControlKeyState |= SCROLLLOCK_ON; + if (GetKeyState(VK_CAPITAL) & 0x0001) + dwControlKeyState |= CAPSLOCK_ON; + /* See WM_CHAR MSDN documentation for instance */ + if (lParam & 0x01000000) + dwControlKeyState |= ENHANCED_KEY; + + er.EventType = MOUSE_EVENT; + er.Event.MouseEvent.dwMousePosition = PointToCoord(GuiData, lParam); + er.Event.MouseEvent.dwButtonState = dwButtonState; + er.Event.MouseEvent.dwControlKeyState = dwControlKeyState; + er.Event.MouseEvent.dwEventFlags = dwEventFlags; + + ConioProcessInputEvent(Console, &er); + } + } + else + { + Err = TRUE; + } + + LeaveCriticalSection(&Console->Lock); + +Quit: + if (Err) + return DefWindowProcW(GuiData->hWindow, msg, wParam, lParam); + else + return 0; +} + +VOID GuiCopyFromTextModeBuffer(PTEXTMODE_SCREEN_BUFFER Buffer); +VOID GuiCopyFromGraphicsBuffer(PGRAPHICS_SCREEN_BUFFER Buffer); + +static VOID +GuiConsoleCopy(PGUI_CONSOLE_DATA GuiData) +{ + if (OpenClipboard(GuiData->hWindow) == TRUE) + { + PCONSOLE Console = GuiData->Console; + PCONSOLE_SCREEN_BUFFER Buffer = ConDrvGetActiveScreenBuffer(Console); + + if (GetType(Buffer) == TEXTMODE_BUFFER) + { + GuiCopyFromTextModeBuffer((PTEXTMODE_SCREEN_BUFFER)Buffer); + } + else /* if (GetType(Buffer) == GRAPHICS_BUFFER) */ + { + GuiCopyFromGraphicsBuffer((PGRAPHICS_SCREEN_BUFFER)Buffer); + } + + CloseClipboard(); + + /* Clear the selection */ + GuiConsoleUpdateSelection(Console, NULL); + SetWindowText(GuiData->hWindow, Console->Title.Buffer); + } +} + +VOID GuiPasteToTextModeBuffer(PTEXTMODE_SCREEN_BUFFER Buffer); +VOID GuiPasteToGraphicsBuffer(PGRAPHICS_SCREEN_BUFFER Buffer); + +static VOID +GuiConsolePaste(PGUI_CONSOLE_DATA GuiData) +{ + if (OpenClipboard(GuiData->hWindow) == TRUE) + { + PCONSOLE Console = GuiData->Console; + PCONSOLE_SCREEN_BUFFER Buffer = ConDrvGetActiveScreenBuffer(Console); + + if (GetType(Buffer) == TEXTMODE_BUFFER) + { + GuiPasteToTextModeBuffer((PTEXTMODE_SCREEN_BUFFER)Buffer); + } + else /* if (GetType(Buffer) == GRAPHICS_BUFFER) */ + { + GuiPasteToGraphicsBuffer((PGRAPHICS_SCREEN_BUFFER)Buffer); + } + + CloseClipboard(); + } +} + +static VOID +GuiConsoleGetMinMaxInfo(PGUI_CONSOLE_DATA GuiData, PMINMAXINFO minMaxInfo) +{ + PCONSOLE Console = GuiData->Console; + PCONSOLE_SCREEN_BUFFER ActiveBuffer; + DWORD windx, windy; + UINT WidthUnit, HeightUnit; + + if (!ConDrvValidateConsoleUnsafe(Console, CONSOLE_RUNNING, TRUE)) return; + + ActiveBuffer = ConDrvGetActiveScreenBuffer(Console); + + GetScreenBufferSizeUnits(ActiveBuffer, GuiData, &WidthUnit, &HeightUnit); + + windx = CONGUI_MIN_WIDTH * WidthUnit + 2 * (GetSystemMetrics(SM_CXFRAME) + GetSystemMetrics(SM_CXEDGE)); + windy = CONGUI_MIN_HEIGHT * HeightUnit + 2 * (GetSystemMetrics(SM_CYFRAME) + GetSystemMetrics(SM_CYEDGE)) + GetSystemMetrics(SM_CYCAPTION); + + minMaxInfo->ptMinTrackSize.x = windx; + minMaxInfo->ptMinTrackSize.y = windy; + + windx = (ActiveBuffer->ScreenBufferSize.X) * WidthUnit + 2 * (GetSystemMetrics(SM_CXFRAME) + GetSystemMetrics(SM_CXEDGE)); + windy = (ActiveBuffer->ScreenBufferSize.Y) * HeightUnit + 2 * (GetSystemMetrics(SM_CYFRAME) + GetSystemMetrics(SM_CYEDGE)) + GetSystemMetrics(SM_CYCAPTION); + + if (ActiveBuffer->ViewSize.X < ActiveBuffer->ScreenBufferSize.X) windy += GetSystemMetrics(SM_CYHSCROLL); // window currently has a horizontal scrollbar + if (ActiveBuffer->ViewSize.Y < ActiveBuffer->ScreenBufferSize.Y) windx += GetSystemMetrics(SM_CXVSCROLL); // window currently has a vertical scrollbar + + minMaxInfo->ptMaxTrackSize.x = windx; + minMaxInfo->ptMaxTrackSize.y = windy; + + LeaveCriticalSection(&Console->Lock); +} + +static VOID +GuiConsoleResize(PGUI_CONSOLE_DATA GuiData, WPARAM wParam, LPARAM lParam) +{ + PCONSOLE Console = GuiData->Console; + + if (!ConDrvValidateConsoleUnsafe(Console, CONSOLE_RUNNING, TRUE)) return; + + if ((GuiData->WindowSizeLock == FALSE) && + (wParam == SIZE_RESTORED || wParam == SIZE_MAXIMIZED || wParam == SIZE_MINIMIZED)) + { + PCONSOLE_SCREEN_BUFFER Buff = ConDrvGetActiveScreenBuffer(Console); + DWORD windx, windy, charx, chary; + UINT WidthUnit, HeightUnit; + + GetScreenBufferSizeUnits(Buff, GuiData, &WidthUnit, &HeightUnit); + + GuiData->WindowSizeLock = TRUE; + + windx = LOWORD(lParam); + windy = HIWORD(lParam); + + // Compensate for existing scroll bars (because lParam values do not accommodate scroll bar) + if (Buff->ViewSize.X < Buff->ScreenBufferSize.X) windy += GetSystemMetrics(SM_CYHSCROLL); // window currently has a horizontal scrollbar + if (Buff->ViewSize.Y < Buff->ScreenBufferSize.Y) windx += GetSystemMetrics(SM_CXVSCROLL); // window currently has a vertical scrollbar + + charx = windx / (int)WidthUnit ; + chary = windy / (int)HeightUnit; + + // Character alignment (round size up or down) + if ((windx % WidthUnit ) >= (WidthUnit / 2)) ++charx; + if ((windy % HeightUnit) >= (HeightUnit / 2)) ++chary; + + // Compensate for added scroll bars in new window + if (charx < Buff->ScreenBufferSize.X) windy -= GetSystemMetrics(SM_CYHSCROLL); // new window will have a horizontal scroll bar + if (chary < Buff->ScreenBufferSize.Y) windx -= GetSystemMetrics(SM_CXVSCROLL); // new window will have a vertical scroll bar + + charx = windx / (int)WidthUnit ; + chary = windy / (int)HeightUnit; + + // Character alignment (round size up or down) + if ((windx % WidthUnit ) >= (WidthUnit / 2)) ++charx; + if ((windy % HeightUnit) >= (HeightUnit / 2)) ++chary; + + // Resize window + if ((charx != Buff->ViewSize.X) || (chary != Buff->ViewSize.Y)) + { + Buff->ViewSize.X = (charx <= Buff->ScreenBufferSize.X) ? charx : Buff->ScreenBufferSize.X; + Buff->ViewSize.Y = (chary <= Buff->ScreenBufferSize.Y) ? chary : Buff->ScreenBufferSize.Y; + } + + GuiConsoleResizeWindow(GuiData); + + // Adjust the start of the visible area if we are attempting to show nonexistent areas + if ((Buff->ScreenBufferSize.X - Buff->ViewOrigin.X) < Buff->ViewSize.X) Buff->ViewOrigin.X = Buff->ScreenBufferSize.X - Buff->ViewSize.X; + if ((Buff->ScreenBufferSize.Y - Buff->ViewOrigin.Y) < Buff->ViewSize.Y) Buff->ViewOrigin.Y = Buff->ScreenBufferSize.Y - Buff->ViewSize.Y; + InvalidateRect(GuiData->hWindow, NULL, TRUE); + + GuiData->WindowSizeLock = FALSE; + } + + LeaveCriticalSection(&Console->Lock); +} + +/* +// HACK: This functionality is standard for general scrollbars. Don't add it by hand. + +VOID +FASTCALL +GuiConsoleHandleScrollbarMenu(VOID) +{ + HMENU hMenu; + + hMenu = CreatePopupMenu(); + if (hMenu == NULL) + { + DPRINT("CreatePopupMenu failed\n"); + return; + } + + //InsertItem(hMenu, MIIM_STRING, MIIM_ID | MIIM_FTYPE | MIIM_STRING, 0, NULL, IDS_SCROLLHERE); + //InsertItem(hMenu, MFT_SEPARATOR, MIIM_FTYPE, 0, NULL, -1); + //InsertItem(hMenu, MIIM_STRING, MIIM_ID | MIIM_FTYPE | MIIM_STRING, 0, NULL, IDS_SCROLLTOP); + //InsertItem(hMenu, MIIM_STRING, MIIM_ID | MIIM_FTYPE | MIIM_STRING, 0, NULL, IDS_SCROLLBOTTOM); + //InsertItem(hMenu, MFT_SEPARATOR, MIIM_FTYPE, 0, NULL, -1); + //InsertItem(hMenu, MIIM_STRING, MIIM_ID | MIIM_FTYPE | MIIM_STRING, 0, NULL, IDS_SCROLLPAGE_UP); + //InsertItem(hMenu, MIIM_STRING, MIIM_ID | MIIM_FTYPE | MIIM_STRING, 0, NULL, IDS_SCROLLPAGE_DOWN); + //InsertItem(hMenu, MFT_SEPARATOR, MIIM_FTYPE, 0, NULL, -1); + //InsertItem(hMenu, MIIM_STRING, MIIM_ID | MIIM_FTYPE | MIIM_STRING, 0, NULL, IDS_SCROLLUP); + //InsertItem(hMenu, MIIM_STRING, MIIM_ID | MIIM_FTYPE | MIIM_STRING, 0, NULL, IDS_SCROLLDOWN); +} +*/ + +static LRESULT +GuiConsoleHandleScroll(PGUI_CONSOLE_DATA GuiData, UINT uMsg, WPARAM wParam) +{ + PCONSOLE Console = GuiData->Console; + PCONSOLE_SCREEN_BUFFER Buff; + SCROLLINFO sInfo; + int fnBar; + int old_pos, Maximum; + PSHORT pShowXY; + + if (!ConDrvValidateConsoleUnsafe(Console, CONSOLE_RUNNING, TRUE)) return 0; + + Buff = ConDrvGetActiveScreenBuffer(Console); + + if (uMsg == WM_HSCROLL) + { + fnBar = SB_HORZ; + Maximum = Buff->ScreenBufferSize.X - Buff->ViewSize.X; + pShowXY = &Buff->ViewOrigin.X; + } + else + { + fnBar = SB_VERT; + Maximum = Buff->ScreenBufferSize.Y - Buff->ViewSize.Y; + pShowXY = &Buff->ViewOrigin.Y; + } + + /* set scrollbar sizes */ + sInfo.cbSize = sizeof(SCROLLINFO); + sInfo.fMask = SIF_RANGE | SIF_POS | SIF_PAGE | SIF_TRACKPOS; + + if (!GetScrollInfo(GuiData->hWindow, fnBar, &sInfo)) goto Quit; + + old_pos = sInfo.nPos; + + switch (LOWORD(wParam)) + { + case SB_LINELEFT: + sInfo.nPos -= 1; + break; + + case SB_LINERIGHT: + sInfo.nPos += 1; + break; + + case SB_PAGELEFT: + sInfo.nPos -= sInfo.nPage; + break; + + case SB_PAGERIGHT: + sInfo.nPos += sInfo.nPage; + break; + + case SB_THUMBTRACK: + sInfo.nPos = sInfo.nTrackPos; + ConioPause(Console, PAUSED_FROM_SCROLLBAR); + break; + + case SB_THUMBPOSITION: + ConioUnpause(Console, PAUSED_FROM_SCROLLBAR); + break; + + case SB_TOP: + sInfo.nPos = sInfo.nMin; + break; + + case SB_BOTTOM: + sInfo.nPos = sInfo.nMax; + break; + + default: + break; + } + + sInfo.nPos = max(sInfo.nPos, 0); + sInfo.nPos = min(sInfo.nPos, Maximum); + + if (old_pos != sInfo.nPos) + { + USHORT OldX = Buff->ViewOrigin.X; + USHORT OldY = Buff->ViewOrigin.Y; + UINT WidthUnit, HeightUnit; + + *pShowXY = sInfo.nPos; + + GetScreenBufferSizeUnits(Buff, GuiData, &WidthUnit, &HeightUnit); + + ScrollWindowEx(GuiData->hWindow, + (OldX - Buff->ViewOrigin.X) * WidthUnit , + (OldY - Buff->ViewOrigin.Y) * HeightUnit, + NULL, + NULL, + NULL, + NULL, + SW_INVALIDATE); + + sInfo.fMask = SIF_POS; + SetScrollInfo(GuiData->hWindow, fnBar, &sInfo, TRUE); + + UpdateWindow(GuiData->hWindow); + } + +Quit: + LeaveCriticalSection(&Console->Lock); + return 0; +} + +static LRESULT CALLBACK +GuiConsoleWndProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam) +{ + LRESULT Result = 0; + PGUI_CONSOLE_DATA GuiData = NULL; + PCONSOLE Console = NULL; + + /* + * - If it's the first time we create a window for the terminal, + * just initialize it and return. + * + * - If we are destroying the window, just do it and return. + */ + if (msg == WM_NCCREATE) + { + return (LRESULT)GuiConsoleHandleNcCreate(hWnd, (LPCREATESTRUCTW)lParam); + } + else if (msg == WM_NCDESTROY) + { + return GuiConsoleHandleNcDestroy(hWnd); + } + + /* + * Now the terminal window is initialized. + * Get the terminal data via the window's data. + * If there is no data, just go away. + */ + GuiData = GuiGetGuiData(hWnd); + if (GuiData == NULL) return DefWindowProcW(hWnd, msg, wParam, lParam); + + /* + * Just retrieve a pointer to the console in case somebody needs it. + * It is not NULL because it was checked in GuiGetGuiData. + * Each helper function which needs the console has to validate and lock it. + */ + Console = GuiData->Console; + + /* We have a console, start message dispatching */ + switch (msg) + { + case WM_ACTIVATE: + { + if (LOWORD(wParam) == WA_CLICKACTIVE) GuiData->IgnoreNextMouseSignal = TRUE; + break; + } + + case WM_CLOSE: + if (GuiConsoleHandleClose(GuiData)) goto Default; + break; + + case WM_PAINT: + GuiConsoleHandlePaint(GuiData); + break; + + case WM_KEYDOWN: + case WM_KEYUP: + case WM_CHAR: + case WM_DEADCHAR: + case WM_SYSKEYDOWN: + case WM_SYSKEYUP: + case WM_SYSCHAR: + case WM_SYSDEADCHAR: + { + /* Detect Alt-Enter presses and switch back and forth to fullscreen mode */ + if (msg == WM_SYSKEYDOWN && (HIWORD(lParam) & KF_ALTDOWN) && wParam == VK_RETURN) + { + /* Switch only at first Alt-Enter press, and ignore subsequent key repetitions */ + if ((HIWORD(lParam) & (KF_UP | KF_REPEAT)) != KF_REPEAT) GuiConsoleSwitchFullScreen(GuiData); + break; + } + + GuiConsoleHandleKey(GuiData, msg, wParam, lParam); + break; + } + + case WM_TIMER: + GuiConsoleHandleTimer(GuiData); + break; + + case WM_SETCURSOR: + { + /* + * The message was sent because we are manually triggering a change. + * Check whether the mouse is indeed present on this console window + * and take appropriate decisions. + */ + if (wParam == -1 && lParam == -1) + { + POINT mouseCoords; + HWND hWndHit; + + /* Get the placement of the mouse */ + GetCursorPos(&mouseCoords); + + /* On which window is placed the mouse ? */ + hWndHit = WindowFromPoint(mouseCoords); + + /* It's our window. Perform the hit-test to be used later on. */ + if (hWndHit == hWnd) + { + wParam = (WPARAM)hWnd; + lParam = DefWindowProcW(hWndHit, WM_NCHITTEST, 0, + MAKELPARAM(mouseCoords.x, mouseCoords.y)); + } + } + + /* Set the mouse cursor only when we are in the client area */ + if ((HWND)wParam == hWnd && LOWORD(lParam) == HTCLIENT) + { + if (GuiData->MouseCursorRefCount >= 0) + { + /* Show the cursor */ + SetCursor(GuiData->hCursor); + } + else + { + /* Hide the cursor if the reference count is negative */ + SetCursor(NULL); + } + return TRUE; + } + else + { + goto Default; + } + } + + case WM_LBUTTONDOWN: + case WM_MBUTTONDOWN: + case WM_RBUTTONDOWN: + case WM_LBUTTONUP: + case WM_MBUTTONUP: + case WM_RBUTTONUP: + case WM_LBUTTONDBLCLK: + case WM_MBUTTONDBLCLK: + case WM_RBUTTONDBLCLK: + case WM_MOUSEMOVE: + case WM_MOUSEWHEEL: + case WM_MOUSEHWHEEL: + { + Result = GuiConsoleHandleMouse(GuiData, msg, wParam, lParam); + break; + } + + case WM_HSCROLL: + case WM_VSCROLL: + { + Result = GuiConsoleHandleScroll(GuiData, msg, wParam); + break; + } + + case WM_NCRBUTTONDOWN: + { + DPRINT1("WM_NCRBUTTONDOWN\n"); + /* + * HACK: !! Because, when we deal with WM_RBUTTON* and we do not + * call after that DefWindowProc, on ReactOS, right-clicks on the + * (non-client) application title-bar does not display the system + * menu and does not trigger a WM_NCRBUTTONUP message too. + * See: http://git.reactos.org/?p=reactos.git;a=blob;f=reactos/win32ss/user/user32/windows/defwnd.c;hb=HEAD#l1103 + * and line 1135 too. + */ + if (DefWindowProcW(hWnd, WM_NCHITTEST, 0, lParam) == HTCAPTION) + { + /* Call DefWindowProcW with the WM_CONTEXTMENU message */ + msg = WM_CONTEXTMENU; + } + goto Default; + } + // case WM_NCRBUTTONUP: + // DPRINT1("WM_NCRBUTTONUP\n"); + // goto Default; + + case WM_CONTEXTMENU: + { + if (DefWindowProcW(hWnd /*GuiData->hWindow*/, WM_NCHITTEST, 0, lParam) == HTCLIENT) + { + HMENU hMenu = CreatePopupMenu(); + if (hMenu != NULL) + { + GuiConsoleAppendMenuItems(hMenu, GuiConsoleEditMenuItems); + TrackPopupMenuEx(hMenu, + TPM_RIGHTBUTTON, + GET_X_LPARAM(lParam), + GET_Y_LPARAM(lParam), + hWnd, + NULL); + DestroyMenu(hMenu); + } + break; + } + else + { + goto Default; + } + } + + case WM_INITMENU: + { + HMENU hMenu = (HMENU)wParam; + if (hMenu != NULL) + { + /* Enable or disable the Close menu item */ + EnableMenuItem(hMenu, SC_CLOSE, MF_BYCOMMAND | + (GuiData->IsCloseButtonEnabled ? MF_ENABLED : MF_GRAYED)); + + /* Enable or disable the Copy and Paste items */ + EnableMenuItem(hMenu, ID_SYSTEM_EDIT_COPY , MF_BYCOMMAND | + ((Console->Selection.dwFlags & CONSOLE_SELECTION_IN_PROGRESS) && + (Console->Selection.dwFlags & CONSOLE_SELECTION_NOT_EMPTY) ? MF_ENABLED : MF_GRAYED)); + EnableMenuItem(hMenu, ID_SYSTEM_EDIT_PASTE, MF_BYCOMMAND | + (!(Console->Selection.dwFlags & CONSOLE_SELECTION_IN_PROGRESS) && + IsClipboardFormatAvailable(CF_UNICODETEXT) ? MF_ENABLED : MF_GRAYED)); + } + + if (ConDrvValidateConsoleUnsafe(Console, CONSOLE_RUNNING, TRUE)) + { + GuiSendMenuEvent(Console, WM_INITMENU); + LeaveCriticalSection(&Console->Lock); + } + break; + } + + case WM_MENUSELECT: + { + if (HIWORD(wParam) == 0xFFFF) // Allow all the menu flags + { + if (ConDrvValidateConsoleUnsafe(Console, CONSOLE_RUNNING, TRUE)) + { + GuiSendMenuEvent(Console, WM_MENUSELECT); + LeaveCriticalSection(&Console->Lock); + } + } + break; + } + + case WM_COMMAND: + case WM_SYSCOMMAND: + { + Result = GuiConsoleHandleSysMenuCommand(GuiData, wParam, lParam); + break; + } + + case WM_SETFOCUS: + case WM_KILLFOCUS: + { + if (ConDrvValidateConsoleUnsafe(Console, CONSOLE_RUNNING, TRUE)) + { + INPUT_RECORD er; + er.EventType = FOCUS_EVENT; + er.Event.FocusEvent.bSetFocus = (msg == WM_SETFOCUS); + ConioProcessInputEvent(Console, &er); + + if (msg == WM_SETFOCUS) + DPRINT1("TODO: Create console caret\n"); + else // if (msg == WM_KILLFOCUS) + DPRINT1("TODO: Destroy console caret\n"); + + LeaveCriticalSection(&Console->Lock); + } + break; + } + + case WM_GETMINMAXINFO: + GuiConsoleGetMinMaxInfo(GuiData, (PMINMAXINFO)lParam); + break; + + case WM_SIZE: + GuiConsoleResize(GuiData, wParam, lParam); + break; + + case PM_RESIZE_TERMINAL: + { + /* Resize the window to the user's values */ + GuiData->WindowSizeLock = TRUE; + GuiConsoleResizeWindow(GuiData); + GuiData->WindowSizeLock = FALSE; + break; + } + + case PM_APPLY_CONSOLE_INFO: + { + if (ConDrvValidateConsoleUnsafe(Console, CONSOLE_RUNNING, TRUE)) + { + GuiApplyUserSettings(GuiData, (HANDLE)wParam, (BOOL)lParam); + LeaveCriticalSection(&Console->Lock); + } + break; + } + + case PM_CONSOLE_BEEP: + DPRINT1("Beep !!\n"); + Beep(800, 200); + break; + + // case PM_CONSOLE_SET_TITLE: + // SetWindowText(GuiData->hWindow, GuiData->Console->Title.Buffer); + // break; + + default: Default: + Result = DefWindowProcW(hWnd, msg, wParam, lParam); + break; + } + + return Result; +} + + + +/****************************************************************************** + * GUI Terminal Initialization * + ******************************************************************************/ + +static LRESULT CALLBACK +GuiConsoleNotifyWndProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam) +{ + HWND NewWindow; + LONG WindowCount; + MSG Msg; + + switch (msg) + { + case WM_CREATE: + { + SetWindowLongW(hWnd, GWL_USERDATA, 0); + return 0; + } + + case PM_CREATE_CONSOLE: + { + PGUI_CONSOLE_DATA GuiData = (PGUI_CONSOLE_DATA)lParam; + PCONSOLE Console = GuiData->Console; + + NewWindow = CreateWindowExW(WS_EX_CLIENTEDGE, + GUI_CONSOLE_WINDOW_CLASS, + Console->Title.Buffer, + WS_OVERLAPPEDWINDOW | WS_HSCROLL | WS_VSCROLL, + CW_USEDEFAULT, + CW_USEDEFAULT, + CW_USEDEFAULT, + CW_USEDEFAULT, + NULL, + NULL, + ConSrvDllInstance, + (PVOID)GuiData); + if (NULL != NewWindow) + { + WindowCount = GetWindowLongW(hWnd, GWL_USERDATA); + WindowCount++; + SetWindowLongW(hWnd, GWL_USERDATA, WindowCount); + + DPRINT("Set icons via PM_CREATE_CONSOLE\n"); + if (GuiData->hIcon == NULL) + { + DPRINT("Not really /o\\...\n"); + GuiData->hIcon = ghDefaultIcon; + GuiData->hIconSm = ghDefaultIconSm; + } + else if (GuiData->hIcon != ghDefaultIcon) + { + DPRINT("Yes \\o/\n"); + SendMessageW(GuiData->hWindow, WM_SETICON, ICON_BIG, (LPARAM)GuiData->hIcon); + SendMessageW(GuiData->hWindow, WM_SETICON, ICON_SMALL, (LPARAM)GuiData->hIconSm); + } + + /* Move and resize the window to the user's values */ + /* CAN WE DEADLOCK ?? */ + GuiConsoleMoveWindow(GuiData); + GuiData->WindowSizeLock = TRUE; + GuiConsoleResizeWindow(GuiData); + GuiData->WindowSizeLock = FALSE; + + // ShowWindow(NewWindow, (int)wParam); + ShowWindowAsync(NewWindow, (int)wParam); + DPRINT("Window showed\n"); + } + + return (LRESULT)NewWindow; + } + + case PM_DESTROY_CONSOLE: + { + PGUI_CONSOLE_DATA GuiData = (PGUI_CONSOLE_DATA)lParam; + + /* + * Window creation is done using a PostMessage(), so it's possible + * that the window that we want to destroy doesn't exist yet. + * So first empty the message queue. + */ + /* + while (PeekMessageW(&Msg, NULL, 0, 0, PM_REMOVE)) + { + TranslateMessage(&Msg); + DispatchMessageW(&Msg); + }*/ + while (PeekMessageW(&Msg, NULL, 0, 0, PM_REMOVE)) ; + + if (GuiData->hWindow != NULL) /* && DestroyWindow(GuiData->hWindow) */ + { + DestroyWindow(GuiData->hWindow); + + WindowCount = GetWindowLongW(hWnd, GWL_USERDATA); + WindowCount--; + SetWindowLongW(hWnd, GWL_USERDATA, WindowCount); + if (0 == WindowCount) + { + NotifyWnd = NULL; + DestroyWindow(hWnd); + DPRINT("CONSRV: Going to quit the Gui Thread!!\n"); + PostQuitMessage(0); + } + } + + return 0; + } + + default: + return DefWindowProcW(hWnd, msg, wParam, lParam); + } +} + +static DWORD WINAPI +GuiConsoleGuiThread(PVOID Data) +{ + MSG msg; + PHANDLE GraphicsStartupEvent = (PHANDLE)Data; + + /* + * This thread dispatches all the console notifications to the notify window. + * It is common for all the console windows. + */ + + PrivateCsrssManualGuiCheck(+1); + + NotifyWnd = CreateWindowW(L"ConSrvCreateNotify", + L"", + WS_OVERLAPPEDWINDOW, + CW_USEDEFAULT, + CW_USEDEFAULT, + CW_USEDEFAULT, + CW_USEDEFAULT, + NULL, + NULL, + ConSrvDllInstance, + NULL); + if (NULL == NotifyWnd) + { + PrivateCsrssManualGuiCheck(-1); + SetEvent(*GraphicsStartupEvent); + return 1; + } + + SetEvent(*GraphicsStartupEvent); + + while (GetMessageW(&msg, NULL, 0, 0)) + { + TranslateMessage(&msg); + DispatchMessageW(&msg); + } + + DPRINT("CONSRV: Quit the Gui Thread!!\n"); + PrivateCsrssManualGuiCheck(-1); + + return 1; +} + +static BOOL +GuiInit(VOID) +{ + WNDCLASSEXW wc; + ATOM ConsoleClassAtom; + + /* Exit if we were already initialized */ + // if (ConsInitialized) return TRUE; + + /* + * Initialize and register the different window classes, if needed. + */ + if (!ConsInitialized) + { + /* Initialize the notification window class */ + wc.cbSize = sizeof(WNDCLASSEXW); + wc.lpszClassName = L"ConSrvCreateNotify"; + wc.lpfnWndProc = GuiConsoleNotifyWndProc; + wc.style = 0; + wc.hInstance = ConSrvDllInstance; + wc.hIcon = NULL; + wc.hIconSm = NULL; + wc.hCursor = NULL; + wc.hbrBackground = NULL; + wc.lpszMenuName = NULL; + wc.cbClsExtra = 0; + wc.cbWndExtra = 0; + if (RegisterClassExW(&wc) == 0) + { + DPRINT1("Failed to register GUI notify wndproc\n"); + return FALSE; + } + + /* Initialize the console window class */ + ghDefaultIcon = LoadImageW(ConSrvDllInstance, + MAKEINTRESOURCEW(IDI_TERMINAL), + IMAGE_ICON, + GetSystemMetrics(SM_CXICON), + GetSystemMetrics(SM_CYICON), + LR_SHARED); + ghDefaultIconSm = LoadImageW(ConSrvDllInstance, + MAKEINTRESOURCEW(IDI_TERMINAL), + IMAGE_ICON, + GetSystemMetrics(SM_CXSMICON), + GetSystemMetrics(SM_CYSMICON), + LR_SHARED); + ghDefaultCursor = LoadCursorW(NULL, IDC_ARROW); + wc.cbSize = sizeof(WNDCLASSEXW); + wc.lpszClassName = GUI_CONSOLE_WINDOW_CLASS; + wc.lpfnWndProc = GuiConsoleWndProc; + wc.style = CS_DBLCLKS /* | CS_HREDRAW | CS_VREDRAW */; + wc.hInstance = ConSrvDllInstance; + wc.hIcon = ghDefaultIcon; + wc.hIconSm = ghDefaultIconSm; + wc.hCursor = ghDefaultCursor; + wc.hbrBackground = (HBRUSH)GetStockObject(BLACK_BRUSH); // The color of a terminal when it is switch off. + wc.lpszMenuName = NULL; + wc.cbClsExtra = 0; + wc.cbWndExtra = GWLP_CONSOLEWND_ALLOC; + + ConsoleClassAtom = RegisterClassExW(&wc); + if (ConsoleClassAtom == 0) + { + DPRINT1("Failed to register GUI console wndproc\n"); + return FALSE; + } + else + { + NtUserConsoleControl(GuiConsoleWndClassAtom, &ConsoleClassAtom, sizeof(ATOM)); + } + + ConsInitialized = TRUE; + } + + /* + * Set-up the notification window + */ + if (NULL == NotifyWnd) + { + HANDLE ThreadHandle; + HANDLE GraphicsStartupEvent; + + GraphicsStartupEvent = CreateEventW(NULL, FALSE, FALSE, NULL); + if (NULL == GraphicsStartupEvent) return FALSE; + + ThreadHandle = CreateThread(NULL, + 0, + GuiConsoleGuiThread, + (PVOID)&GraphicsStartupEvent, + 0, + NULL); + if (NULL == ThreadHandle) + { + CloseHandle(GraphicsStartupEvent); + DPRINT1("CONSRV: Failed to create graphics console thread. Expect problems\n"); + return FALSE; + } + SetThreadPriority(ThreadHandle, THREAD_PRIORITY_HIGHEST); + CloseHandle(ThreadHandle); + + WaitForSingleObject(GraphicsStartupEvent, INFINITE); + CloseHandle(GraphicsStartupEvent); + + if (NULL == NotifyWnd) + { + DPRINT1("CONSRV: Failed to create notification window.\n"); + return FALSE; + } + } + + // ConsInitialized = TRUE; + + return TRUE; +} + + + +/****************************************************************************** + * GUI Console Driver * + ******************************************************************************/ + +static VOID WINAPI +GuiDeinitFrontEnd(IN OUT PFRONTEND This); + +NTSTATUS NTAPI +GuiInitFrontEnd(IN OUT PFRONTEND This, + IN PCONSOLE Console) +{ + PGUI_INIT_INFO GuiInitInfo; + PCONSOLE_INFO ConsoleInfo; + PCONSOLE_START_INFO ConsoleStartInfo; + + PGUI_CONSOLE_DATA GuiData; + GUI_CONSOLE_INFO TermInfo; + + SIZE_T Length = 0; + LPWSTR IconPath = NULL; + INT IconIndex = 0; + + if (This == NULL || Console == NULL || This->OldData == NULL) + return STATUS_INVALID_PARAMETER; + + ASSERT(This->Console == Console); + + GuiInitInfo = This->OldData; + + if (GuiInitInfo->ConsoleInfo == NULL || GuiInitInfo->ConsoleStartInfo == NULL) + return STATUS_INVALID_PARAMETER; + + ConsoleInfo = GuiInitInfo->ConsoleInfo; + ConsoleStartInfo = GuiInitInfo->ConsoleStartInfo; + + IconPath = ConsoleStartInfo->IconPath; + IconIndex = ConsoleStartInfo->IconIndex; + + + /* Terminal data allocation */ + GuiData = ConsoleAllocHeap(HEAP_ZERO_MEMORY, sizeof(GUI_CONSOLE_DATA)); + if (!GuiData) + { + DPRINT1("CONSRV: Failed to create GUI_CONSOLE_DATA\n"); + return STATUS_UNSUCCESSFUL; + } + /* HACK */ Console->TermIFace.Data = (PVOID)GuiData; /* HACK */ + GuiData->Console = Console; + GuiData->hWindow = NULL; + + /* The console can be resized */ + Console->FixedSize = FALSE; + + InitializeCriticalSection(&GuiData->Lock); + + + /* + * Load terminal settings + */ + + /* 1. Load the default settings */ + GuiConsoleGetDefaultSettings(&TermInfo, GuiInitInfo->ProcessId); + + /* 3. Load the remaining console settings via the registry. */ + if ((ConsoleStartInfo->dwStartupFlags & STARTF_TITLEISLINKNAME) == 0) + { + /* Load the terminal infos from the registry. */ + GuiConsoleReadUserSettings(&TermInfo, + ConsoleInfo->ConsoleTitle, + GuiInitInfo->ProcessId); + + /* + * Now, update them with the properties the user might gave to us + * via the STARTUPINFO structure before calling CreateProcess + * (and which was transmitted via the ConsoleStartInfo structure). + * We therefore overwrite the values read in the registry. + */ + if (ConsoleStartInfo->dwStartupFlags & STARTF_USESHOWWINDOW) + { + TermInfo.ShowWindow = ConsoleStartInfo->ShowWindow; + } + if (ConsoleStartInfo->dwStartupFlags & STARTF_USEPOSITION) + { + TermInfo.AutoPosition = FALSE; + TermInfo.WindowOrigin = ConsoleStartInfo->ConsoleWindowOrigin; + } + if (ConsoleStartInfo->dwStartupFlags & STARTF_RUNFULLSCREEN) + { + TermInfo.FullScreen = TRUE; + } + } + + + /* + * Set up GUI data + */ + + Length = min(wcslen(TermInfo.FaceName) + 1, LF_FACESIZE); // wcsnlen + wcsncpy(GuiData->GuiInfo.FaceName, TermInfo.FaceName, LF_FACESIZE); + GuiData->GuiInfo.FaceName[Length] = L'\0'; + GuiData->GuiInfo.FontFamily = TermInfo.FontFamily; + GuiData->GuiInfo.FontSize = TermInfo.FontSize; + GuiData->GuiInfo.FontWeight = TermInfo.FontWeight; + GuiData->GuiInfo.UseRasterFonts = TermInfo.UseRasterFonts; + GuiData->GuiInfo.FullScreen = TermInfo.FullScreen; + GuiData->GuiInfo.ShowWindow = TermInfo.ShowWindow; + GuiData->GuiInfo.AutoPosition = TermInfo.AutoPosition; + GuiData->GuiInfo.WindowOrigin = TermInfo.WindowOrigin; + + /* Initialize the icon handles to their default values */ + GuiData->hIcon = ghDefaultIcon; + GuiData->hIconSm = ghDefaultIconSm; + + /* Get the associated icon, if any */ + if (IconPath == NULL || IconPath[0] == L'\0') + { + IconPath = ConsoleStartInfo->AppPath; + IconIndex = 0; + } + DPRINT("IconPath = %S ; IconIndex = %lu\n", (IconPath ? IconPath : L"n/a"), IconIndex); + if (IconPath && IconPath[0] != L'\0') + { + HICON hIcon = NULL, hIconSm = NULL; + PrivateExtractIconExW(IconPath, + IconIndex, + &hIcon, + &hIconSm, + 1); + DPRINT("hIcon = 0x%p ; hIconSm = 0x%p\n", hIcon, hIconSm); + if (hIcon != NULL) + { + DPRINT("Effectively set the icons\n"); + GuiData->hIcon = hIcon; + GuiData->hIconSm = hIconSm; + } + } + + /* Mouse is shown by default with its default cursor shape */ + GuiData->hCursor = ghDefaultCursor; + GuiData->MouseCursorRefCount = 0; + + /* A priori don't ignore mouse signals */ + GuiData->IgnoreNextMouseSignal = FALSE; + + /* Close button and the corresponding system menu item are enabled by default */ + GuiData->IsCloseButtonEnabled = TRUE; + + /* There is no user-reserved menu id range by default */ + GuiData->cmdIdLow = GuiData->cmdIdHigh = 0; + + /* + * We need to wait until the GUI has been fully initialized + * to retrieve custom settings i.e. WindowSize etc... + * Ideally we could use SendNotifyMessage for this but its not + * yet implemented. + */ + GuiData->hGuiInitEvent = CreateEventW(NULL, FALSE, FALSE, NULL); + + DPRINT("GUI - Checkpoint\n"); + + /* Create the terminal window */ + PostMessageW(NotifyWnd, PM_CREATE_CONSOLE, GuiData->GuiInfo.ShowWindow, (LPARAM)GuiData); + + /* Wait until initialization has finished */ + WaitForSingleObject(GuiData->hGuiInitEvent, INFINITE); + DPRINT("OK we created the console window\n"); + CloseHandle(GuiData->hGuiInitEvent); + GuiData->hGuiInitEvent = NULL; + + /* Check whether we really succeeded in initializing the terminal window */ + if (GuiData->hWindow == NULL) + { + DPRINT("GuiInitConsole - We failed at creating a new terminal window\n"); + GuiDeinitFrontEnd(This); + return STATUS_UNSUCCESSFUL; + } + + /* Finally, finish to initialize the frontend structure */ + This->Data = GuiData; + if (This->OldData) ConsoleFreeHeap(This->OldData); + This->OldData = NULL; + + return STATUS_SUCCESS; +} + +static VOID WINAPI +GuiDeinitFrontEnd(IN OUT PFRONTEND This) +{ + PGUI_CONSOLE_DATA GuiData = This->Data; + + SendMessageW(NotifyWnd, PM_DESTROY_CONSOLE, 0, (LPARAM)GuiData); + + DPRINT("Destroying icons !! - GuiData->hIcon = 0x%p ; ghDefaultIcon = 0x%p ; GuiData->hIconSm = 0x%p ; ghDefaultIconSm = 0x%p\n", + GuiData->hIcon, ghDefaultIcon, GuiData->hIconSm, ghDefaultIconSm); + if (GuiData->hIcon != NULL && GuiData->hIcon != ghDefaultIcon) + { + DPRINT("Destroy hIcon\n"); + DestroyIcon(GuiData->hIcon); + } + if (GuiData->hIconSm != NULL && GuiData->hIconSm != ghDefaultIconSm) + { + DPRINT("Destroy hIconSm\n"); + DestroyIcon(GuiData->hIconSm); + } + + This->Data = NULL; + DeleteCriticalSection(&GuiData->Lock); + ConsoleFreeHeap(GuiData); + + DPRINT("Quit GuiDeinitFrontEnd\n"); +} + +static VOID WINAPI +GuiDrawRegion(IN OUT PFRONTEND This, + SMALL_RECT* Region) +{ + PGUI_CONSOLE_DATA GuiData = This->Data; + RECT RegionRect; + + SmallRectToRect(GuiData, &RegionRect, Region); + /* Do not erase the background: it speeds up redrawing and reduce flickering */ + InvalidateRect(GuiData->hWindow, &RegionRect, FALSE); +} + +static VOID WINAPI +GuiWriteStream(IN OUT PFRONTEND This, + SMALL_RECT* Region, + SHORT CursorStartX, + SHORT CursorStartY, + UINT ScrolledLines, + PWCHAR Buffer, + UINT Length) +{ + PGUI_CONSOLE_DATA GuiData = This->Data; + PCONSOLE_SCREEN_BUFFER Buff; + SHORT CursorEndX, CursorEndY; + RECT ScrollRect; + + if (NULL == GuiData || NULL == GuiData->hWindow) return; + + Buff = ConDrvGetActiveScreenBuffer(GuiData->Console); + if (GetType(Buff) != TEXTMODE_BUFFER) return; + + if (0 != ScrolledLines) + { + ScrollRect.left = 0; + ScrollRect.top = 0; + ScrollRect.right = Buff->ViewSize.X * GuiData->CharWidth; + ScrollRect.bottom = Region->Top * GuiData->CharHeight; + + ScrollWindowEx(GuiData->hWindow, + 0, + -(int)(ScrolledLines * GuiData->CharHeight), + &ScrollRect, + NULL, + NULL, + NULL, + SW_INVALIDATE); + } + + GuiDrawRegion(This, Region); + + if (CursorStartX < Region->Left || Region->Right < CursorStartX + || CursorStartY < Region->Top || Region->Bottom < CursorStartY) + { + GuiInvalidateCell(This, CursorStartX, CursorStartY); + } + + CursorEndX = Buff->CursorPosition.X; + CursorEndY = Buff->CursorPosition.Y; + if ((CursorEndX < Region->Left || Region->Right < CursorEndX + || CursorEndY < Region->Top || Region->Bottom < CursorEndY) + && (CursorEndX != CursorStartX || CursorEndY != CursorStartY)) + { + GuiInvalidateCell(This, CursorEndX, CursorEndY); + } + + // Set up the update timer (very short interval) - this is a "hack" for getting the OS to + // repaint the window without having it just freeze up and stay on the screen permanently. + Buff->CursorBlinkOn = TRUE; + SetTimer(GuiData->hWindow, CONGUI_UPDATE_TIMER, CONGUI_UPDATE_TIME, NULL); +} + +static BOOL WINAPI +GuiSetCursorInfo(IN OUT PFRONTEND This, + PCONSOLE_SCREEN_BUFFER Buff) +{ + PGUI_CONSOLE_DATA GuiData = This->Data; + + if (ConDrvGetActiveScreenBuffer(GuiData->Console) == Buff) + { + GuiInvalidateCell(This, Buff->CursorPosition.X, Buff->CursorPosition.Y); + } + + return TRUE; +} + +static BOOL WINAPI +GuiSetScreenInfo(IN OUT PFRONTEND This, + PCONSOLE_SCREEN_BUFFER Buff, + SHORT OldCursorX, + SHORT OldCursorY) +{ + PGUI_CONSOLE_DATA GuiData = This->Data; + + if (ConDrvGetActiveScreenBuffer(GuiData->Console) == Buff) + { + /* Redraw char at old position (remove cursor) */ + GuiInvalidateCell(This, OldCursorX, OldCursorY); + /* Redraw char at new position (show cursor) */ + GuiInvalidateCell(This, Buff->CursorPosition.X, Buff->CursorPosition.Y); + } + + return TRUE; +} + +static VOID WINAPI +GuiResizeTerminal(IN OUT PFRONTEND This) +{ + PGUI_CONSOLE_DATA GuiData = This->Data; + + /* Resize the window to the user's values */ + // GuiData->WindowSizeLock = TRUE; + // GuiConsoleResizeWindow(GuiData); + // GuiData->WindowSizeLock = FALSE; + // NOTE: This code ^^ causes deadlocks... + + PostMessageW(GuiData->hWindow, PM_RESIZE_TERMINAL, 0, 0); +} + +static BOOL WINAPI +GuiProcessKeyCallback(IN OUT PFRONTEND This, + MSG* msg, + BYTE KeyStateMenu, + DWORD ShiftState, + UINT VirtualKeyCode, + BOOL Down) +{ + if ((ShiftState & (RIGHT_ALT_PRESSED | LEFT_ALT_PRESSED) || KeyStateMenu & 0x80) && + (VirtualKeyCode == VK_ESCAPE || VirtualKeyCode == VK_TAB || VirtualKeyCode == VK_SPACE)) + { + DefWindowProcW(msg->hwnd, msg->message, msg->wParam, msg->lParam); + return TRUE; + } + + return FALSE; +} + +static BOOL WINAPI +GuiSetMouseCursor(IN OUT PFRONTEND This, + HCURSOR hCursor); + +static VOID WINAPI +GuiRefreshInternalInfo(IN OUT PFRONTEND This) +{ + PGUI_CONSOLE_DATA GuiData = This->Data; + + /* Update the console leader information held by the window */ + SetConsoleWndConsoleLeaderCID(GuiData); + + /* + * HACK: + * We reset the cursor here so that, when a console app quits, we reset + * the cursor to the default one. It's quite a hack since it doesn't proceed + * per - console process... This must be fixed. + * + * See GuiInitConsole(...) for more information. + */ + + /* Mouse is shown by default with its default cursor shape */ + GuiData->MouseCursorRefCount = 0; // Reinitialize the reference counter + GuiSetMouseCursor(This, NULL); +} + +static VOID WINAPI +GuiChangeTitle(IN OUT PFRONTEND This) +{ + PGUI_CONSOLE_DATA GuiData = This->Data; + // PostMessageW(GuiData->hWindow, PM_CONSOLE_SET_TITLE, 0, 0); + SetWindowText(GuiData->hWindow, GuiData->Console->Title.Buffer); +} + +static BOOL WINAPI +GuiChangeIcon(IN OUT PFRONTEND This, + HICON hWindowIcon) +{ + PGUI_CONSOLE_DATA GuiData = This->Data; + HICON hIcon, hIconSm; + + if (hWindowIcon == NULL) + { + hIcon = ghDefaultIcon; + hIconSm = ghDefaultIconSm; + } + else + { + hIcon = CopyIcon(hWindowIcon); + hIconSm = CopyIcon(hWindowIcon); + } + + if (hIcon == NULL) + { + return FALSE; + } + + if (hIcon != GuiData->hIcon) + { + if (GuiData->hIcon != NULL && GuiData->hIcon != ghDefaultIcon) + { + DestroyIcon(GuiData->hIcon); + } + if (GuiData->hIconSm != NULL && GuiData->hIconSm != ghDefaultIconSm) + { + DestroyIcon(GuiData->hIconSm); + } + + GuiData->hIcon = hIcon; + GuiData->hIconSm = hIconSm; + + DPRINT("Set icons in GuiChangeIcon\n"); + PostMessageW(GuiData->hWindow, WM_SETICON, ICON_BIG, (LPARAM)GuiData->hIcon); + PostMessageW(GuiData->hWindow, WM_SETICON, ICON_SMALL, (LPARAM)GuiData->hIconSm); + } + + return TRUE; +} + +static HWND WINAPI +GuiGetConsoleWindowHandle(IN OUT PFRONTEND This) +{ + PGUI_CONSOLE_DATA GuiData = This->Data; + return GuiData->hWindow; +} + +static VOID WINAPI +GuiGetLargestConsoleWindowSize(IN OUT PFRONTEND This, + PCOORD pSize) +{ + PGUI_CONSOLE_DATA GuiData = This->Data; + PCONSOLE_SCREEN_BUFFER ActiveBuffer; + RECT WorkArea; + LONG width, height; + UINT WidthUnit, HeightUnit; + + if (!pSize) return; + + if (!SystemParametersInfoW(SPI_GETWORKAREA, 0, &WorkArea, 0)) + { + DPRINT1("SystemParametersInfoW failed - What to do ??\n"); + return; + } + + ActiveBuffer = ConDrvGetActiveScreenBuffer(GuiData->Console); + if (ActiveBuffer) + { + GetScreenBufferSizeUnits(ActiveBuffer, GuiData, &WidthUnit, &HeightUnit); + } + else + { + /* Default: text mode */ + WidthUnit = GuiData->CharWidth ; + HeightUnit = GuiData->CharHeight; + } + + width = WorkArea.right; + height = WorkArea.bottom; + + width -= (2 * (GetSystemMetrics(SM_CXFRAME) + GetSystemMetrics(SM_CXEDGE))); + height -= (2 * (GetSystemMetrics(SM_CYFRAME) + GetSystemMetrics(SM_CYEDGE)) + GetSystemMetrics(SM_CYCAPTION)); + + if (width < 0) width = 0; + if (height < 0) height = 0; + + pSize->X = (SHORT)(width / (int)WidthUnit ) /* HACK */ + 2; + pSize->Y = (SHORT)(height / (int)HeightUnit) /* HACK */ + 1; +} + +static ULONG WINAPI +GuiGetDisplayMode(IN OUT PFRONTEND This) +{ + PGUI_CONSOLE_DATA GuiData = This->Data; + ULONG DisplayMode = 0; + + if (GuiData->GuiInfo.FullScreen) + DisplayMode |= CONSOLE_FULLSCREEN_HARDWARE; // CONSOLE_FULLSCREEN + else + DisplayMode |= CONSOLE_WINDOWED; + + return DisplayMode; +} + +static BOOL WINAPI +GuiSetDisplayMode(IN OUT PFRONTEND This, + ULONG NewMode) +{ + PGUI_CONSOLE_DATA GuiData = This->Data; + + if (NewMode & ~(CONSOLE_FULLSCREEN_MODE | CONSOLE_WINDOWED_MODE)) + return FALSE; + + GuiData->GuiInfo.FullScreen = (NewMode & CONSOLE_FULLSCREEN_MODE); + // TODO: Change the display mode + return TRUE; +} + +static INT WINAPI +GuiShowMouseCursor(IN OUT PFRONTEND This, + BOOL Show) +{ + PGUI_CONSOLE_DATA GuiData = This->Data; + + /* Set the reference count */ + if (Show) ++GuiData->MouseCursorRefCount; + else --GuiData->MouseCursorRefCount; + + /* Effectively show (or hide) the cursor (use special values for (w|l)Param) */ + PostMessageW(GuiData->hWindow, WM_SETCURSOR, -1, -1); + + return GuiData->MouseCursorRefCount; +} + +static BOOL WINAPI +GuiSetMouseCursor(IN OUT PFRONTEND This, + HCURSOR hCursor) +{ + PGUI_CONSOLE_DATA GuiData = This->Data; + + /* + * Set the cursor's handle. If the given handle is NULL, + * then restore the default cursor. + */ + GuiData->hCursor = (hCursor ? hCursor : ghDefaultCursor); + + /* Effectively modify the shape of the cursor (use special values for (w|l)Param) */ + PostMessageW(GuiData->hWindow, WM_SETCURSOR, -1, -1); + + return TRUE; +} + +static HMENU WINAPI +GuiMenuControl(IN OUT PFRONTEND This, + UINT cmdIdLow, + UINT cmdIdHigh) +{ + PGUI_CONSOLE_DATA GuiData = This->Data; + + GuiData->cmdIdLow = cmdIdLow ; + GuiData->cmdIdHigh = cmdIdHigh; + + return GetSystemMenu(GuiData->hWindow, FALSE); +} + +static BOOL WINAPI +GuiSetMenuClose(IN OUT PFRONTEND This, + BOOL Enable) +{ + /* + * NOTE: See http://www.mail-archive.com/harbour@harbour-project.org/msg27509.html + * or http://harbour-devel.1590103.n2.nabble.com/Question-about-hb-gt-win-CtrlHandler-usage-td4670862i20.html + * for more information. + */ + + PGUI_CONSOLE_DATA GuiData = This->Data; + HMENU hSysMenu = GetSystemMenu(GuiData->hWindow, FALSE); + + if (hSysMenu == NULL) return FALSE; + + GuiData->IsCloseButtonEnabled = Enable; + EnableMenuItem(hSysMenu, SC_CLOSE, MF_BYCOMMAND | (Enable ? MF_ENABLED : MF_GRAYED)); + + return TRUE; +} + +static FRONTEND_VTBL GuiVtbl = +{ + GuiInitFrontEnd, + GuiDeinitFrontEnd, + GuiDrawRegion, + GuiWriteStream, + GuiSetCursorInfo, + GuiSetScreenInfo, + GuiResizeTerminal, + GuiProcessKeyCallback, + GuiRefreshInternalInfo, + GuiChangeTitle, + GuiChangeIcon, + GuiGetConsoleWindowHandle, + GuiGetLargestConsoleWindowSize, + GuiGetDisplayMode, + GuiSetDisplayMode, + GuiShowMouseCursor, + GuiSetMouseCursor, + GuiMenuControl, + GuiSetMenuClose, +}; + + +static BOOL +LoadShellLinkConsoleInfo(IN OUT PCONSOLE_START_INFO ConsoleStartInfo, + IN OUT PCONSOLE_INFO ConsoleInfo) +{ +#define PATH_SEPARATOR L'\\' + + BOOL RetVal = FALSE; + HRESULT hRes = S_OK; + LPWSTR LinkName = NULL; + SIZE_T Length = 0; + + if ((ConsoleStartInfo->dwStartupFlags & STARTF_TITLEISLINKNAME) == 0) + return FALSE; + + ConsoleStartInfo->IconPath[0] = L'\0'; + ConsoleStartInfo->IconIndex = 0; + + /* 1- Find the last path separator if any */ + LinkName = wcsrchr(ConsoleStartInfo->ConsoleTitle, PATH_SEPARATOR); + if (LinkName == NULL) + { + LinkName = ConsoleStartInfo->ConsoleTitle; + } + else + { + /* Skip the path separator */ + ++LinkName; + } + + /* 2- Check for the link extension. The name ".lnk" is considered invalid. */ + Length = wcslen(LinkName); + if ( (Length <= 4) || (wcsicmp(LinkName + (Length - 4), L".lnk") != 0) ) + return FALSE; + + /* 3- It may be a link. Try to retrieve some properties */ + hRes = CoInitialize(NULL); + if (SUCCEEDED(hRes)) + { + /* Get a pointer to the IShellLink interface */ + IShellLinkW* pshl = NULL; + hRes = CoCreateInstance(&CLSID_ShellLink, + NULL, + CLSCTX_INPROC_SERVER, + &IID_IShellLinkW, + (LPVOID*)&pshl); + if (SUCCEEDED(hRes)) + { + /* Get a pointer to the IPersistFile interface */ + IPersistFile* ppf = NULL; + hRes = IPersistFile_QueryInterface(pshl, &IID_IPersistFile, (LPVOID*)&ppf); + if (SUCCEEDED(hRes)) + { + /* Load the shortcut */ + hRes = IPersistFile_Load(ppf, ConsoleStartInfo->ConsoleTitle, STGM_READ); + if (SUCCEEDED(hRes)) + { + /* + * Finally we can get the properties ! + * Update the old ones if needed. + */ + INT ShowCmd = 0; + // WORD HotKey = 0; + + /* Reset the name of the console with the name of the shortcut */ + Length = min(/*Length*/ Length - 4, // 4 == len(".lnk") + sizeof(ConsoleInfo->ConsoleTitle) / sizeof(ConsoleInfo->ConsoleTitle[0]) - 1); + wcsncpy(ConsoleInfo->ConsoleTitle, LinkName, Length); + ConsoleInfo->ConsoleTitle[Length] = L'\0'; + + /* Get the window showing command */ + hRes = IShellLinkW_GetShowCmd(pshl, &ShowCmd); + if (SUCCEEDED(hRes)) ConsoleStartInfo->ShowWindow = (WORD)ShowCmd; + + /* Get the hotkey */ + // hRes = pshl->GetHotkey(&ShowCmd); + // if (SUCCEEDED(hRes)) ConsoleStartInfo->HotKey = HotKey; + + /* Get the icon location, if any */ + + hRes = IShellLinkW_GetIconLocation(pshl, + ConsoleStartInfo->IconPath, + sizeof(ConsoleStartInfo->IconPath)/sizeof(ConsoleStartInfo->IconPath[0]) - 1, // == MAX_PATH + &ConsoleStartInfo->IconIndex); + if (!SUCCEEDED(hRes)) + { + ConsoleStartInfo->IconPath[0] = L'\0'; + ConsoleStartInfo->IconIndex = 0; + } + + // FIXME: Since we still don't load console properties from the shortcut, + // return false. When this will be done, we will return true instead. + RetVal = FALSE; + } + IPersistFile_Release(ppf); + } + IShellLinkW_Release(pshl); + } + } + CoUninitialize(); + + return RetVal; +} + +NTSTATUS NTAPI +GuiLoadFrontEnd(IN OUT PFRONTEND FrontEnd, + IN OUT PCONSOLE_INFO ConsoleInfo, + IN OUT PVOID ExtraConsoleInfo, + IN ULONG ProcessId) +{ + PCONSOLE_START_INFO ConsoleStartInfo = ExtraConsoleInfo; + PGUI_INIT_INFO GuiInitInfo; + + if (FrontEnd == NULL || ConsoleInfo == NULL || ConsoleStartInfo == NULL) + return STATUS_INVALID_PARAMETER; + + /* Initialize GUI terminal emulator common functionalities */ + if (!GuiInit()) return STATUS_UNSUCCESSFUL; + + /* + * Load per-application terminal settings. + * + * Check whether the process creating the console was launched via + * a shell-link. ConsoleInfo->ConsoleTitle may be updated with the + * name of the shortcut, and ConsoleStartInfo->Icon[Path|Index] too. + */ + if (ConsoleStartInfo->dwStartupFlags & STARTF_TITLEISLINKNAME) + { + if (!LoadShellLinkConsoleInfo(ConsoleStartInfo, ConsoleInfo)) + { + ConsoleStartInfo->dwStartupFlags &= ~STARTF_TITLEISLINKNAME; + } + } + + /* + * Initialize a private initialization info structure for later use. + * It must be freed by a call to GuiUnloadFrontEnd or GuiInitFrontEnd. + */ + GuiInitInfo = ConsoleAllocHeap(HEAP_ZERO_MEMORY, sizeof(GUI_INIT_INFO)); + if (GuiInitInfo == NULL) return STATUS_NO_MEMORY; + + // HACK: We suppose that the pointers will be valid in GuiInitFrontEnd... + GuiInitInfo->ConsoleInfo = ConsoleInfo; + GuiInitInfo->ConsoleStartInfo = ConsoleStartInfo; + GuiInitInfo->ProcessId = ProcessId; + + /* Finally, initialize the frontend structure */ + FrontEnd->Vtbl = &GuiVtbl; + FrontEnd->Data = NULL; + FrontEnd->OldData = GuiInitInfo; + + return STATUS_SUCCESS; +} + +NTSTATUS NTAPI +GuiUnloadFrontEnd(IN OUT PFRONTEND FrontEnd) +{ + if (FrontEnd == NULL) return STATUS_INVALID_PARAMETER; + + if (FrontEnd->Data) GuiDeinitFrontEnd(FrontEnd); + if (FrontEnd->OldData) ConsoleFreeHeap(FrontEnd->OldData); + + return STATUS_SUCCESS; +} + +/* EOF */ diff --git a/reactos/win32ss/user/winsrv/consrv_new/frontends/gui/guiterm.h b/reactos/win32ss/user/winsrv/consrv_new/frontends/gui/guiterm.h new file mode 100644 index 00000000000..c91915b033e --- /dev/null +++ b/reactos/win32ss/user/winsrv/consrv_new/frontends/gui/guiterm.h @@ -0,0 +1,28 @@ +/* + * COPYRIGHT: See COPYING in the top level directory + * PROJECT: ReactOS Console Server DLL + * FILE: win32ss/user/winsrv/consrv/frontends/gui/guiterm.h + * PURPOSE: GUI Terminal Front-End + * PROGRAMMERS: Gé van Geldorp + * Johannes Anderwald + * Jeffrey Morlan + * Hermes Belusca-Maito (hermes.belusca@sfr.fr) + */ + +#pragma once + +#define CONGUI_MIN_WIDTH 10 +#define CONGUI_MIN_HEIGHT 10 +#define CONGUI_UPDATE_TIME 0 +#define CONGUI_UPDATE_TIMER 1 + +#define CURSOR_BLINK_TIME 500 + +NTSTATUS FASTCALL GuiInitConsole(PCONSOLE Console, + /*IN*/ PCONSOLE_START_INFO ConsoleStartInfo, + PCONSOLE_INFO ConsoleInfo, + DWORD ProcessId, + LPCWSTR IconPath, + INT IconIndex); + +/* EOF */ diff --git a/reactos/win32ss/user/winsrv/consrv_new/frontends/gui/guiterm.rc b/reactos/win32ss/user/winsrv/consrv_new/frontends/gui/guiterm.rc new file mode 100644 index 00000000000..7a88fce0e39 --- /dev/null +++ b/reactos/win32ss/user/winsrv/consrv_new/frontends/gui/guiterm.rc @@ -0,0 +1,16 @@ +#include +#include +#include "resource.h" + +LANGUAGE LANG_NEUTRAL, SUBLANG_NEUTRAL + +// #define REACTOS_VERSION_DLL +// #define REACTOS_STR_FILE_DESCRIPTION "ReactOS Console Server & Terminal Emulator DLL" +// #define REACTOS_STR_INTERNAL_NAME "consrv" +// #define REACTOS_STR_ORIGINAL_FILENAME "consrv.dll" +// #include + +// IDI_TERMINAL ICON DISCARDABLE "res/terminal.ico" +IDI_TERMINAL ICON DISCARDABLE "frontends/gui/res/terminal.ico" + +#include "rsrc.rc" diff --git a/reactos/win32ss/user/winsrv/consrv_new/frontends/gui/lang/bg-BG.rc b/reactos/win32ss/user/winsrv/consrv_new/frontends/gui/lang/bg-BG.rc new file mode 100644 index 00000000000..cb6a87b6f5e --- /dev/null +++ b/reactos/win32ss/user/winsrv/consrv_new/frontends/gui/lang/bg-BG.rc @@ -0,0 +1,32 @@ +/* + * PROJECT: ReactOS CSRSS subsystem + * FILE: win32ss/user/winsrv/consrv/lang/bg-BG.rc + * PURPOSE: Bulgarian resource file + */ + +LANGUAGE LANG_BULGARIAN, SUBLANG_DEFAULT + +STRINGTABLE +BEGIN + IDS_EDIT "Обработка" + IDS_MARK "Отбелязване" + IDS_COPY "Запомняне\tEnter" + IDS_PASTE "Поставяне" + IDS_SELECTALL "Избор на всичко" + IDS_SCROLL "Прелистване" + IDS_FIND "Търсене..." + IDS_DEFAULTS "Подразбирани" + IDS_PROPERTIES "Свойства" + +/* + IDS_SCROLLHERE "Прелистване тук" + IDS_SCROLLTOP "Прелистване Đ´Đž горе" + IDS_SCROLLBOTTOM "Прелистване Đ´Đž дОНу" + IDS_SCROLLPAGE_UP "Горна страница" + IDS_SCROLLPAGE_DOWN "Долна страница" + IDS_SCROLLUP "Прелистване нагоре" + IDS_SCROLLDOWN "Прелистване надОНу" +*/ + + IDS_TERMINAL_TITLE "ReactOS Console" +END diff --git a/reactos/win32ss/user/winsrv/consrv_new/frontends/gui/lang/cs-CZ.rc b/reactos/win32ss/user/winsrv/consrv_new/frontends/gui/lang/cs-CZ.rc new file mode 100644 index 00000000000..a558b892aaa --- /dev/null +++ b/reactos/win32ss/user/winsrv/consrv_new/frontends/gui/lang/cs-CZ.rc @@ -0,0 +1,32 @@ +/* + * FILE: win32ss/user/winsrv/consrv/lang/cs-CZ.rc + * TRANSLATOR: Radek Liska aka Black_Fox (radekliska at gmail dot com) + * UPDATED: 2011-04-09 + */ + +LANGUAGE LANG_CZECH, SUBLANG_DEFAULT + +STRINGTABLE +BEGIN + IDS_EDIT "Upravit" + IDS_MARK "Označit" + IDS_COPY "KopĂ­rovat\tEnter" + IDS_PASTE "VloĹžit" + IDS_SELECTALL "Označit vĹĄe" + IDS_SCROLL "Posunout" + IDS_FIND "NajĂ­t..." + IDS_DEFAULTS "VĂ˝chozĂ­" + IDS_PROPERTIES "Vlastnosti" + +/* + IDS_SCROLLHERE "Posunout sem" + IDS_SCROLLTOP "Posunout na začátek" + IDS_SCROLLBOTTOM "Posunout na konec" + IDS_SCROLLPAGE_UP "O strĂĄnku výťe" + IDS_SCROLLPAGE_DOWN "O strĂĄnku nĂ­Ĺže" + IDS_SCROLLUP "Posunout nahoru" + IDS_SCROLLDOWN "Posunout dolĹŻ" +*/ + + IDS_TERMINAL_TITLE "ReactOS Console" +END diff --git a/reactos/win32ss/user/winsrv/consrv_new/frontends/gui/lang/de-DE.rc b/reactos/win32ss/user/winsrv/consrv_new/frontends/gui/lang/de-DE.rc new file mode 100644 index 00000000000..3d0e60efe3c --- /dev/null +++ b/reactos/win32ss/user/winsrv/consrv_new/frontends/gui/lang/de-DE.rc @@ -0,0 +1,26 @@ +LANGUAGE LANG_GERMAN, SUBLANG_NEUTRAL + +STRINGTABLE +BEGIN + IDS_EDIT "Bearbeiten" + IDS_MARK "Markieren" + IDS_COPY "Kopieren\tEnter" + IDS_PASTE "EinfĂźgen" + IDS_SELECTALL "Alles auswählen" + IDS_SCROLL "Scrollen" + IDS_FIND "Suchen..." + IDS_DEFAULTS "Standardwerte" + IDS_PROPERTIES "Eigenschaften" + +/* + IDS_SCROLLHERE "Hier scrollen" + IDS_SCROLLTOP "Ganz nach oben scrollen" + IDS_SCROLLBOTTOM "Ganz nach unten scrollen" + IDS_SCROLLPAGE_UP "Seite nach oben" + IDS_SCROLLPAGE_DOWN "Seite nach unten" + IDS_SCROLLUP "Nach oben scrollen" + IDS_SCROLLDOWN "Nach unten scrollen" +*/ + + IDS_TERMINAL_TITLE "ReactOS Eingabeaufforderung" +END diff --git a/reactos/win32ss/user/winsrv/consrv_new/frontends/gui/lang/el-GR.rc b/reactos/win32ss/user/winsrv/consrv_new/frontends/gui/lang/el-GR.rc new file mode 100644 index 00000000000..6041800756c --- /dev/null +++ b/reactos/win32ss/user/winsrv/consrv_new/frontends/gui/lang/el-GR.rc @@ -0,0 +1,26 @@ +LANGUAGE LANG_GREEK, SUBLANG_DEFAULT + +STRINGTABLE +BEGIN + IDS_EDIT "Επεξεργασία" + IDS_MARK "Μαρκάρισμα" + IDS_COPY "Αντιγραφή\tEnter" + IDS_PASTE "Επικόλληση" + IDS_SELECTALL "Επιλογή όλων" + IDS_SCROLL "Κύλιση" + IDS_FIND "Εύρεση..." + IDS_DEFAULTS "Προεπιλογή" + IDS_PROPERTIES "Ιδιότητες" + +/* + IDS_SCROLLHERE "Κύλιση εδώ" + IDS_SCROLLTOP "Αρχή" + IDS_SCROLLBOTTOM "Τέλος" + IDS_SCROLLPAGE_UP "Προηγούμενη σξΝίδι" + IDS_SCROLLPAGE_DOWN "Επόμενη σξΝίδι" + IDS_SCROLLUP "Κύλιση πάνω" + IDS_SCROLLDOWN "Κύλιση κάτω" +*/ + + IDS_TERMINAL_TITLE "ReactOS Console" +END diff --git a/reactos/win32ss/user/winsrv/consrv_new/frontends/gui/lang/en-US.rc b/reactos/win32ss/user/winsrv/consrv_new/frontends/gui/lang/en-US.rc new file mode 100644 index 00000000000..04de038bffa --- /dev/null +++ b/reactos/win32ss/user/winsrv/consrv_new/frontends/gui/lang/en-US.rc @@ -0,0 +1,26 @@ +LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_US + +STRINGTABLE +BEGIN + IDS_EDIT "Edit" + IDS_MARK "Mark" + IDS_COPY "Copy\tEnter" + IDS_PASTE "Paste" + IDS_SELECTALL "Select All" + IDS_SCROLL "Scroll" + IDS_FIND "Find..." + IDS_DEFAULTS "Defaults" + IDS_PROPERTIES "Properties" + +/* + IDS_SCROLLHERE "Scroll here" + IDS_SCROLLTOP "Scroll top" + IDS_SCROLLBOTTOM "Scroll bottom" + IDS_SCROLLPAGE_UP "Page up" + IDS_SCROLLPAGE_DOWN "Page down" + IDS_SCROLLUP "Scroll up" + IDS_SCROLLDOWN "Scroll down" +*/ + + IDS_TERMINAL_TITLE "ReactOS Console" +END diff --git a/reactos/win32ss/user/winsrv/consrv_new/frontends/gui/lang/es-ES.rc b/reactos/win32ss/user/winsrv/consrv_new/frontends/gui/lang/es-ES.rc new file mode 100644 index 00000000000..78ffc2221e1 --- /dev/null +++ b/reactos/win32ss/user/winsrv/consrv_new/frontends/gui/lang/es-ES.rc @@ -0,0 +1,31 @@ +/* + * Spanish Language resource file + * Traducido por: Javier Remacha 2008-26-01 + */ + +LANGUAGE LANG_SPANISH, SUBLANG_NEUTRAL + +STRINGTABLE +BEGIN + IDS_EDIT "Editar" + IDS_MARK "Marcar" + IDS_COPY "Copiar\tIntroducir" + IDS_PASTE "Pegar" + IDS_SELECTALL "Seleccionar Todo" + IDS_SCROLL "Desplazar" + IDS_FIND "Buscar..." + IDS_DEFAULTS "Por defecto" + IDS_PROPERTIES "Propiedades" + +/* + IDS_SCROLLHERE "Desplazar aquĂ­" + IDS_SCROLLTOP "Desplazar hasta arriba" + IDS_SCROLLBOTTOM "Desplazar hasta abajo" + IDS_SCROLLPAGE_UP "Subir pĂĄgina" + IDS_SCROLLPAGE_DOWN "Bajar pĂĄgina" + IDS_SCROLLUP "Desplazar arriba" + IDS_SCROLLDOWN "Desplazar abajo" +*/ + + IDS_TERMINAL_TITLE "ReactOS Console" +END diff --git a/reactos/win32ss/user/winsrv/consrv_new/frontends/gui/lang/fr-FR.rc b/reactos/win32ss/user/winsrv/consrv_new/frontends/gui/lang/fr-FR.rc new file mode 100644 index 00000000000..f1834c18545 --- /dev/null +++ b/reactos/win32ss/user/winsrv/consrv_new/frontends/gui/lang/fr-FR.rc @@ -0,0 +1,28 @@ +LANGUAGE LANG_FRENCH, SUBLANG_NEUTRAL + +/* Fixme : Translation could be really improved, with context + La traduction pourrait rĂŠellement ĂŞtre amĂŠliorĂŠe grâce au contexte */ +STRINGTABLE +BEGIN + IDS_EDIT "Éditer" + IDS_MARK "Marquer" + IDS_COPY "Copier\tEntrĂŠe" + IDS_PASTE "Coller" + IDS_SELECTALL "Tout sĂŠlectionner" + IDS_SCROLL "DĂŠfiler" + IDS_FIND "Trouver..." + IDS_DEFAULTS "DĂŠfauts" + IDS_PROPERTIES "PropriĂŠtĂŠs" + +/* + IDS_SCROLLHERE "DĂŠfiler ici" + IDS_SCROLLTOP "DĂŠfiler tout en haut" + IDS_SCROLLBOTTOM "DĂŠfiler tout en bas" + IDS_SCROLLPAGE_UP "Page prĂŠcĂŠdente" + IDS_SCROLLPAGE_DOWN "Page suivante" + IDS_SCROLLUP "DĂŠfiler en haut" + IDS_SCROLLDOWN "DĂŠfiler en bas" +*/ + + IDS_TERMINAL_TITLE "ReactOS Console" +END diff --git a/reactos/win32ss/user/winsrv/consrv_new/frontends/gui/lang/he-IL.rc b/reactos/win32ss/user/winsrv/consrv_new/frontends/gui/lang/he-IL.rc new file mode 100644 index 00000000000..de4f25870bb --- /dev/null +++ b/reactos/win32ss/user/winsrv/consrv_new/frontends/gui/lang/he-IL.rc @@ -0,0 +1,26 @@ +LANGUAGE LANG_HEBREW, SUBLANG_DEFAULT + +STRINGTABLE +BEGIN + IDS_EDIT "ערוך" + IDS_MARK "סמן" + IDS_COPY "העתק\tEnter" + IDS_PASTE "הדבק" + IDS_SELECTALL "בחר הכל" + IDS_SCROLL "גלול" + IDS_FIND "מצא..." + IDS_DEFAULTS "ברירת מחדל" + IDS_PROPERTIES "מאפיינים" + +/* + IDS_SCROLLHERE "גלול לכאן" + IDS_SCROLLTOP "גלול למעלה" + IDS_SCROLLBOTTOM "גלול למטה" + IDS_SCROLLPAGE_UP "עמוד מעלה" + IDS_SCROLLPAGE_DOWN "עמוד מטה" + IDS_SCROLLUP "גלול מעלה" + IDS_SCROLLDOWN "גלול מטה" +*/ + + IDS_TERMINAL_TITLE "ReactOS Console" +END diff --git a/reactos/win32ss/user/winsrv/consrv_new/frontends/gui/lang/id-ID.rc b/reactos/win32ss/user/winsrv/consrv_new/frontends/gui/lang/id-ID.rc new file mode 100644 index 00000000000..6e889163363 --- /dev/null +++ b/reactos/win32ss/user/winsrv/consrv_new/frontends/gui/lang/id-ID.rc @@ -0,0 +1,26 @@ +LANGUAGE LANG_INDONESIAN, SUBLANG_DEFAULT + +STRINGTABLE +BEGIN + IDS_EDIT "Edit" + IDS_MARK "Tandai" + IDS_COPY "Copy\tEnter" + IDS_PASTE "Paste" + IDS_SELECTALL "Pilih Semua" + IDS_SCROLL "Gulung" + IDS_FIND "Cari..." + IDS_DEFAULTS "Standar" + IDS_PROPERTIES "Properti" + +/* + IDS_SCROLLHERE "Gulung ke Sini" + IDS_SCROLLTOP "Gulung ke Atas" + IDS_SCROLLBOTTOM "Gulung ke Bawah" + IDS_SCROLLPAGE_UP "Halaman Naik" + IDS_SCROLLPAGE_DOWN "Halaman Turun" + IDS_SCROLLUP "Gulung Naik" + IDS_SCROLLDOWN "Gulung Turun" +*/ + + IDS_TERMINAL_TITLE "ReactOS Console" +END diff --git a/reactos/win32ss/user/winsrv/consrv_new/frontends/gui/lang/it-IT.rc b/reactos/win32ss/user/winsrv/consrv_new/frontends/gui/lang/it-IT.rc new file mode 100644 index 00000000000..4d0bd524cc7 --- /dev/null +++ b/reactos/win32ss/user/winsrv/consrv_new/frontends/gui/lang/it-IT.rc @@ -0,0 +1,35 @@ +/* +* PROJECT: ReactOS Client/Server Runtime subsystem +* LICENSE: GPL - See COPYING in the top level directory +* FILE: win32ss/user/winsrv/consrv/lang/it-IT.rc +* PURPOSE: Italian Translation +* PROGRAMMERS: +* Copyright (C) 2007 Daniele Forsi (dforsi at gmail.com) Italian Translation +*/ + +LANGUAGE LANG_ITALIAN, SUBLANG_NEUTRAL + +STRINGTABLE +BEGIN + IDS_EDIT "Modifica" + IDS_MARK "Seleziona" + IDS_COPY "Copia\tInvio" + IDS_PASTE "Incolla" + IDS_SELECTALL "Seleziona tutto" + IDS_SCROLL "Scorri" + IDS_FIND "Trova..." + IDS_DEFAULTS "Impostazioni predefinite" + IDS_PROPERTIES "ProprietĂ " + +/* + IDS_SCROLLHERE "Scorri qui" + IDS_SCROLLTOP "Scorri in cima" + IDS_SCROLLBOTTOM "Scorri in fondo" + IDS_SCROLLPAGE_UP "Pagina sĂš" + IDS_SCROLLPAGE_DOWN "Pagina giĂš" + IDS_SCROLLUP "Scorri sĂš" + IDS_SCROLLDOWN "Scorri giĂš" +*/ + + IDS_TERMINAL_TITLE "ReactOS Console" +END diff --git a/reactos/win32ss/user/winsrv/consrv_new/frontends/gui/lang/ja-JP.rc b/reactos/win32ss/user/winsrv/consrv_new/frontends/gui/lang/ja-JP.rc new file mode 100644 index 00000000000..8c4f66dcb48 --- /dev/null +++ b/reactos/win32ss/user/winsrv/consrv_new/frontends/gui/lang/ja-JP.rc @@ -0,0 +1,26 @@ +LANGUAGE LANG_JAPANESE, SUBLANG_DEFAULT + +STRINGTABLE +BEGIN + IDS_EDIT "編集" + IDS_MARK "範囲指定" + IDS_COPY "コピー\tEnter" + IDS_PASTE "貼り付け" + IDS_SELECTALL "すべて選択" + IDS_SCROLL "スクロール" + IDS_FIND "検索..." + IDS_DEFAULTS "規定値" + IDS_PROPERTIES "プロパティ" + +/* + IDS_SCROLLHERE "ここにスクロール" + IDS_SCROLLTOP "一番上にスクロール" + IDS_SCROLLBOTTOM "一番下にスクロール" + IDS_SCROLLPAGE_UP "Page up" + IDS_SCROLLPAGE_DOWN "Page down" + IDS_SCROLLUP "上にスクロール" + IDS_SCROLLDOWN "下にスクロール" +*/ + + IDS_TERMINAL_TITLE "ReactOS Console" +END diff --git a/reactos/win32ss/user/winsrv/consrv_new/frontends/gui/lang/no-NO.rc b/reactos/win32ss/user/winsrv/consrv_new/frontends/gui/lang/no-NO.rc new file mode 100644 index 00000000000..9ffd8d70518 --- /dev/null +++ b/reactos/win32ss/user/winsrv/consrv_new/frontends/gui/lang/no-NO.rc @@ -0,0 +1,26 @@ +LANGUAGE LANG_NORWEGIAN, SUBLANG_NEUTRAL + +STRINGTABLE +BEGIN + IDS_EDIT "Rediger" + IDS_MARK "Merk" + IDS_COPY "Kopier\tEnter" + IDS_PASTE "Lim inn" + IDS_SELECTALL "Velg alt" + IDS_SCROLL "Rull" + IDS_FIND "Finn..." + IDS_DEFAULTS "Standard" + IDS_PROPERTIES "Egenskaper" + +/* + IDS_SCROLLHERE "Rull her" + IDS_SCROLLTOP "Rull til toppen" + IDS_SCROLLBOTTOM "Rull knapp" + IDS_SCROLLPAGE_UP "Side opp" + IDS_SCROLLPAGE_DOWN "Side ned" + IDS_SCROLLUP "Rull opp" + IDS_SCROLLDOWN "Rull ned" +*/ + + IDS_TERMINAL_TITLE "ReactOS Console" +END diff --git a/reactos/win32ss/user/winsrv/consrv_new/frontends/gui/lang/pl-PL.rc b/reactos/win32ss/user/winsrv/consrv_new/frontends/gui/lang/pl-PL.rc new file mode 100644 index 00000000000..f6c4835bc8a --- /dev/null +++ b/reactos/win32ss/user/winsrv/consrv_new/frontends/gui/lang/pl-PL.rc @@ -0,0 +1,34 @@ +/* + * translated by xrogers + * xxrogers@users.sourceforge.net + * https://sourceforge.net/projects/reactospl + * translation update by Olaf Siejka (Caemyr), Apr 2011 + * UTF-8 conversion by Caemyr (May, 2011) + */ + +LANGUAGE LANG_POLISH, SUBLANG_DEFAULT + +STRINGTABLE +BEGIN + IDS_EDIT "Edytuj" + IDS_MARK "Zaznacz" + IDS_COPY "Kopiuj\tWejdĹş" + IDS_PASTE "Wklej" + IDS_SELECTALL "Zaznacz wszystko" + IDS_SCROLL "Przewiń" + IDS_FIND "ZnajdĹş..." + IDS_DEFAULTS "Ustawienia domyślne" + IDS_PROPERTIES "Właściwości" + +/* + IDS_SCROLLHERE "Przewiń tutaj" + IDS_SCROLLTOP "Przewiń na początek" + IDS_SCROLLBOTTOM "Przewiń na koniec" + IDS_SCROLLPAGE_UP "Poprzednia strona" + IDS_SCROLLPAGE_DOWN "Następna strona" + IDS_SCROLLUP "Przewiń do gĂłry" + IDS_SCROLLDOWN "Przewiń na dół" +*/ + + IDS_TERMINAL_TITLE "Konsola ReactOS" +END diff --git a/reactos/win32ss/user/winsrv/consrv_new/frontends/gui/lang/pt-BR.rc b/reactos/win32ss/user/winsrv/consrv_new/frontends/gui/lang/pt-BR.rc new file mode 100644 index 00000000000..37e66f8afb4 --- /dev/null +++ b/reactos/win32ss/user/winsrv/consrv_new/frontends/gui/lang/pt-BR.rc @@ -0,0 +1,28 @@ +/* Translation and UTF-8 Conversion by mkbu95 (May, 2012) */ + +LANGUAGE LANG_PORTUGUESE, SUBLANG_NEUTRAL + +STRINGTABLE +BEGIN + IDS_EDIT "Editar" + IDS_MARK "Marcar" + IDS_COPY "Copiar\tEnter" + IDS_PASTE "Colar" + IDS_SELECTALL "Selecionar Tudo" + IDS_SCROLL "Rolar" + IDS_FIND "Procurar..." + IDS_DEFAULTS "PadrĂľes" + IDS_PROPERTIES "Propriedades" + +/* + IDS_SCROLLHERE "Rolar aqui" + IDS_SCROLLTOP "Rolar atĂŠ o topo" + IDS_SCROLLBOTTOM "Rolar atĂŠ o fim" + IDS_SCROLLPAGE_UP "Page up" + IDS_SCROLLPAGE_DOWN "Page down" + IDS_SCROLLUP "Scroll up" + IDS_SCROLLDOWN "Scroll down" +*/ + + IDS_TERMINAL_TITLE "ReactOS Console" +END diff --git a/reactos/win32ss/user/winsrv/consrv_new/frontends/gui/lang/ro-RO.rc b/reactos/win32ss/user/winsrv/consrv_new/frontends/gui/lang/ro-RO.rc new file mode 100644 index 00000000000..1796eb85bbb --- /dev/null +++ b/reactos/win32ss/user/winsrv/consrv_new/frontends/gui/lang/ro-RO.rc @@ -0,0 +1,33 @@ +/* + * FILE: win32ss/user/winsrv/consrv/lang/ro-RO.rc + * ReactOS Project (http://www.reactos.org) + * TRANSLATOR: Fulea Ștefan (PM on ReactOS Forum at fulea.stefan) + * CHANGE LOG: 2011-10-16 initial translation + */ + +LANGUAGE LANG_ROMANIAN, SUBLANG_NEUTRAL + +STRINGTABLE +BEGIN + IDS_EDIT "Editare" + IDS_MARK "Marchează" + IDS_COPY "Copiază\tEnter" + IDS_PASTE "Lipește" + IDS_SELECTALL "Selectează tot" + IDS_SCROLL "Derulează" + IDS_FIND "Găsire…" + IDS_DEFAULTS "Implicite" + IDS_PROPERTIES "Proprietăți" + +/* + IDS_SCROLLHERE "Derulează aici" + IDS_SCROLLTOP "Derulează la ĂŽnceput" + IDS_SCROLLBOTTOM "Derulează la sfârșit" + IDS_SCROLLPAGE_UP "Pagina anterioară" + IDS_SCROLLPAGE_DOWN "Pagina următoare" + IDS_SCROLLUP "Derulează ĂŽn sus" + IDS_SCROLLDOWN "Derulează ĂŽn jos" +*/ + + IDS_TERMINAL_TITLE "ReactOS Console" +END diff --git a/reactos/win32ss/user/winsrv/consrv_new/frontends/gui/lang/ru-RU.rc b/reactos/win32ss/user/winsrv/consrv_new/frontends/gui/lang/ru-RU.rc new file mode 100644 index 00000000000..b1587a2baf7 --- /dev/null +++ b/reactos/win32ss/user/winsrv/consrv_new/frontends/gui/lang/ru-RU.rc @@ -0,0 +1,26 @@ +LANGUAGE LANG_RUSSIAN, SUBLANG_DEFAULT + +STRINGTABLE +BEGIN + IDS_EDIT "Изменить" + IDS_MARK "Пометить" + IDS_COPY "Копировать\tEnter" + IDS_PASTE "Вставить" + IDS_SELECTALL "Выделить всо" + IDS_SCROLL "Прокрутить" + IDS_FIND "Искать..." + IDS_DEFAULTS "Умолчания" + IDS_PROPERTIES "Свойства" + +/* + IDS_SCROLLHERE "Scroll Here" + IDS_SCROLLTOP "Scroll Top" + IDS_SCROLLBOTTOM "Прокрутить вниС" + IDS_SCROLLPAGE_UP "Вверх страницы" + IDS_SCROLLPAGE_DOWN "Вниз страницы" + IDS_SCROLLUP "Прокрутить вверх" + IDS_SCROLLDOWN "Scroll Down" +*/ + + IDS_TERMINAL_TITLE "ReactOS Console" +END diff --git a/reactos/win32ss/user/winsrv/consrv_new/frontends/gui/lang/sk-SK.rc b/reactos/win32ss/user/winsrv/consrv_new/frontends/gui/lang/sk-SK.rc new file mode 100644 index 00000000000..f518891626f --- /dev/null +++ b/reactos/win32ss/user/winsrv/consrv_new/frontends/gui/lang/sk-SK.rc @@ -0,0 +1,31 @@ +/* TRANSLATOR: MĂĄrio KačmĂĄr /Mario Kacmar/ aka Kario (kario@szm.sk) + * DATE OF TR: 29-05-2008 + * LastChange: 12-04-2011 + */ + +LANGUAGE LANG_SLOVAK, SUBLANG_DEFAULT + +STRINGTABLE +BEGIN + IDS_EDIT "UpraviĹĽ" + IDS_MARK "OznačiĹĽ" + IDS_COPY "KopĂ­rovaĹĽ\tEnter" + IDS_PASTE "VloĹžiĹĽ" + IDS_SELECTALL "VybraĹĽ vĹĄetko" + IDS_SCROLL "RolovaĹĽ" + IDS_FIND "NĂĄjsĹĽ..." // Find + IDS_DEFAULTS "PredvolenĂŠ" // Defaults + IDS_PROPERTIES "Vlastnosti" + +/* + IDS_SCROLLHERE "RolovaĹĽ sem" + IDS_SCROLLTOP "K hornĂŠmu okraju" + IDS_SCROLLBOTTOM "K dolnĂŠmu okraju" + IDS_SCROLLPAGE_UP "O strĂĄnku vyĹĄĹĄie" + IDS_SCROLLPAGE_DOWN "O strĂĄnku niŞťie" + IDS_SCROLLUP "RolovaĹĽ nahor" + IDS_SCROLLDOWN "RolovaĹĽ nadol" +*/ + + IDS_TERMINAL_TITLE "ReactOS Console" +END diff --git a/reactos/win32ss/user/winsrv/consrv_new/frontends/gui/lang/sv-SE.rc b/reactos/win32ss/user/winsrv/consrv_new/frontends/gui/lang/sv-SE.rc new file mode 100644 index 00000000000..0a478b8b5b5 --- /dev/null +++ b/reactos/win32ss/user/winsrv/consrv_new/frontends/gui/lang/sv-SE.rc @@ -0,0 +1,33 @@ +/* + * PROJECT: ReactOS CSRSS subsystem + * FILE: win32ss/user/winsrv/consrv/lang/sv-SE.rc + * PURPOSE: Swedish resource file + * Translation: Jaix Bly + */ + +LANGUAGE LANG_SWEDISH, SUBLANG_NEUTRAL + +STRINGTABLE +BEGIN + IDS_EDIT "Redigera" + IDS_MARK "Markera" + IDS_COPY "Kopiera\tEnter" + IDS_PASTE "Klistra in" + IDS_SELECTALL "Markera Allt" + IDS_SCROLL "Skrolla" + IDS_FIND "SĂśk..." + IDS_DEFAULTS "Ursprunglig" + IDS_PROPERTIES "Egenskaper" + +/* + IDS_SCROLLHERE "Skrolla hit" + IDS_SCROLLTOP "Skrolla till topp" + IDS_SCROLLBOTTOM "Skrolla till botten" + IDS_SCROLLPAGE_UP "Sida upp" + IDS_SCROLLPAGE_DOWN "Sida ner" + IDS_SCROLLUP "Skrolla upp" + IDS_SCROLLDOWN "Skrolla ner" +*/ + + IDS_TERMINAL_TITLE "ReactOS Console" +END diff --git a/reactos/win32ss/user/winsrv/consrv_new/frontends/gui/lang/tr-TR.rc b/reactos/win32ss/user/winsrv/consrv_new/frontends/gui/lang/tr-TR.rc new file mode 100644 index 00000000000..1b2a9b6034f --- /dev/null +++ b/reactos/win32ss/user/winsrv/consrv_new/frontends/gui/lang/tr-TR.rc @@ -0,0 +1,46 @@ +/* + * Turkish resources + * + * Copyright 2012 Arda Tanrikulu + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +LANGUAGE LANG_TURKISH, SUBLANG_DEFAULT + +STRINGTABLE +BEGIN + IDS_EDIT "DĂźzenle" + IDS_MARK "İşaretle" + IDS_COPY "Kopyala\tEnter" + IDS_PASTE "YapıştÄąr" + IDS_SELECTALL "TĂźmĂźnĂź Seç" + IDS_SCROLL "Yuvarla" + IDS_FIND "Bul..." + IDS_DEFAULTS "VarsayÄąlanlar" + IDS_PROPERTIES "Özellikler" + +/* + IDS_SCROLLHERE "Buraya yuvarla" + IDS_SCROLLTOP "Üste yuvarla" + IDS_SCROLLBOTTOM "Alta yuvarla" + IDS_SCROLLPAGE_UP "Sayfa yukarÄą" + IDS_SCROLLPAGE_DOWN "Sayfa aşağı" + IDS_SCROLLUP "YukarÄą yuvarla" + IDS_SCROLLDOWN "Aşağı yuvarla" +*/ + + IDS_TERMINAL_TITLE "ReactOS Console" +END diff --git a/reactos/win32ss/user/winsrv/consrv_new/frontends/gui/lang/uk-UA.rc b/reactos/win32ss/user/winsrv/consrv_new/frontends/gui/lang/uk-UA.rc new file mode 100644 index 00000000000..de4715037d7 --- /dev/null +++ b/reactos/win32ss/user/winsrv/consrv_new/frontends/gui/lang/uk-UA.rc @@ -0,0 +1,34 @@ +/* + * PROJECT: ReactOS CSRSS subsystem + * LICENSE: GPL - See COPYING in the top level directory + * FILE: win32ss/user/winsrv/consrv/lang/uk-UA.rc + * PURPOSE: Ukraianian resource file + * TRANSLATOR: Artem Reznikov + */ + +LANGUAGE LANG_UKRAINIAN, SUBLANG_DEFAULT + +STRINGTABLE +BEGIN + IDS_EDIT "Редагувати" + IDS_MARK "Виділити" + IDS_COPY "Копіювати\tEnter" + IDS_PASTE "Вставити" + IDS_SELECTALL "Виділити всо" + IDS_SCROLL "Прокрутити" + IDS_FIND "Знайти..." + IDS_DEFAULTS "Замовчування" + IDS_PROPERTIES "Властивості" + +/* + IDS_SCROLLHERE "Прокрутити сюди" + IDS_SCROLLTOP "Прокрутити на початок" + IDS_SCROLLBOTTOM "Прокрутити на кінець" + IDS_SCROLLPAGE_UP "Попередня сотрінка" + IDS_SCROLLPAGE_DOWN "Наступна сторінка" + IDS_SCROLLUP "Прокрутити догори" + IDS_SCROLLDOWN "Прокрутити дОниСу" +*/ + + IDS_TERMINAL_TITLE "ReactOS Console" +END diff --git a/reactos/win32ss/user/winsrv/consrv_new/frontends/gui/lang/zh-CN.rc b/reactos/win32ss/user/winsrv/consrv_new/frontends/gui/lang/zh-CN.rc new file mode 100644 index 00000000000..3d7c645f66a --- /dev/null +++ b/reactos/win32ss/user/winsrv/consrv_new/frontends/gui/lang/zh-CN.rc @@ -0,0 +1,26 @@ +LANGUAGE LANG_CHINESE, SUBLANG_CHINESE_SIMPLIFIED + +STRINGTABLE +BEGIN + IDS_EDIT "编辑" + IDS_MARK "标记" + IDS_COPY "复制\tEnter" + IDS_PASTE "黏贴" + IDS_SELECTALL "全部选择" + IDS_SCROLL "滚动" + IDS_FIND "查找..." + IDS_DEFAULTS "靘莤" + IDS_PROPERTIES "属性" + +/* + IDS_SCROLLHERE "滚动到此" + IDS_SCROLLTOP "滚动至顶端" + IDS_SCROLLBOTTOM "滚动至末端" + IDS_SCROLLPAGE_UP "上一页" + IDS_SCROLLPAGE_DOWN "下一页" + IDS_SCROLLUP "向上滚动" + IDS_SCROLLDOWN "向下滚动" +*/ + + IDS_TERMINAL_TITLE "ReactOS Console" +END diff --git a/reactos/win32ss/user/winsrv/consrv_new/frontends/gui/lang/zh-TW.rc b/reactos/win32ss/user/winsrv/consrv_new/frontends/gui/lang/zh-TW.rc new file mode 100644 index 00000000000..6ff4e5865ed --- /dev/null +++ b/reactos/win32ss/user/winsrv/consrv_new/frontends/gui/lang/zh-TW.rc @@ -0,0 +1,26 @@ +LANGUAGE LANG_CHINESE, SUBLANG_CHINESE_TRADITIONAL + +STRINGTABLE +BEGIN + IDS_EDIT "硨蟯" + IDS_MARK "標記" + IDS_COPY "複製\tEnter" + IDS_PASTE "黏貼" + IDS_SELECTALL "全部選擇" + IDS_SCROLL "滾動" + IDS_FIND "尋找..." + IDS_DEFAULTS "預設" + IDS_PROPERTIES "屬性" + +/* + IDS_SCROLLHERE "滾動到此 " + IDS_SCROLLTOP "滾動到頂置" + IDS_SCROLLBOTTOM "滾動到末端" + IDS_SCROLLPAGE_UP "上一頁" + IDS_SCROLLPAGE_DOWN "下一頁" + IDS_SCROLLUP "向上滾動" + IDS_SCROLLDOWN "向下滾動" +*/ + + IDS_TERMINAL_TITLE "ReactOS Console" +END diff --git a/reactos/win32ss/user/winsrv/consrv_new/frontends/gui/res/terminal.ico b/reactos/win32ss/user/winsrv/consrv_new/frontends/gui/res/terminal.ico new file mode 100644 index 00000000000..7e3e77d2927 Binary files /dev/null and b/reactos/win32ss/user/winsrv/consrv_new/frontends/gui/res/terminal.ico differ diff --git a/reactos/win32ss/user/winsrv/consrv_new/frontends/gui/resource.h b/reactos/win32ss/user/winsrv/consrv_new/frontends/gui/resource.h new file mode 100644 index 00000000000..dbc9e1f9a30 --- /dev/null +++ b/reactos/win32ss/user/winsrv/consrv_new/frontends/gui/resource.h @@ -0,0 +1,45 @@ +/* + * COPYRIGHT: See COPYING in the top level directory + * PROJECT: ReactOS Console Server DLL + * FILE: win32ss/user/winsrv/consrv/resource.h + * PURPOSE: Resource #defines + */ + +#pragma once + +#define ID_SYSTEM_EDIT_MARK 1001 +#define ID_SYSTEM_EDIT_COPY 1002 +#define ID_SYSTEM_EDIT_PASTE 1003 +#define ID_SYSTEM_EDIT_SELECTALL 1004 +#define ID_SYSTEM_EDIT_SCROLL 1005 +#define ID_SYSTEM_EDIT_FIND 1006 +#define ID_SYSTEM_DEFAULTS 1007 +#define ID_SYSTEM_PROPERTIES 1008 + +#define NCPOPUP_MENU 103 + +#define IDS_EDIT 204 +#define IDS_MARK 205 +#define IDS_COPY 206 +#define IDS_PASTE 207 +#define IDS_SELECTALL 208 +#define IDS_SCROLL 209 +#define IDS_FIND 210 +#define IDS_DEFAULTS 211 +#define IDS_PROPERTIES 212 + +// Scrollbar resource ids. Unused. +/* +#define IDS_SCROLLHERE 304 +#define IDS_SCROLLTOP 305 +#define IDS_SCROLLBOTTOM 306 +#define IDS_SCROLLPAGE_UP 307 +#define IDS_SCROLLPAGE_DOWN 308 +#define IDS_SCROLLUP 309 +#define IDS_SCROLLDOWN 310 +*/ + +#define IDI_TERMINAL 1 +#define IDS_TERMINAL_TITLE 400 + +/* EOF */ diff --git a/reactos/win32ss/user/winsrv/consrv_new/frontends/gui/rsrc.rc b/reactos/win32ss/user/winsrv/consrv_new/frontends/gui/rsrc.rc new file mode 100644 index 00000000000..1839748b631 --- /dev/null +++ b/reactos/win32ss/user/winsrv/consrv_new/frontends/gui/rsrc.rc @@ -0,0 +1,70 @@ +LANGUAGE LANG_NEUTRAL, SUBLANG_NEUTRAL + +// UTF-8 +#pragma code_page(65001) +#ifdef LANGUAGE_BG_BG + #include "lang/bg-BG.rc" +#endif +#ifdef LANGUAGE_CS_CZ + #include "lang/cs-CZ.rc" +#endif +#ifdef LANGUAGE_DE_DE + #include "lang/de-DE.rc" +#endif +#ifdef LANGUAGE_EL_GR + #include "lang/el-GR.rc" +#endif +#ifdef LANGUAGE_EN_US + #include "lang/en-US.rc" +#endif +#ifdef LANGUAGE_ES_ES + #include "lang/es-ES.rc" +#endif +#ifdef LANGUAGE_FR_FR + #include "lang/fr-FR.rc" +#endif +#ifdef LANGUAGE_HE_IL + #include "lang/he-IL.rc" +#endif +#ifdef LANGUAGE_ID_ID + #include "lang/id-ID.rc" +#endif +#ifdef LANGUAGE_IT_IT + #include "lang/it-IT.rc" +#endif +#ifdef LANGUAGE_JA_JP + #include "lang/ja-JP.rc" +#endif +#ifdef LANGUAGE_NB_NO + #include "lang/no-NO.rc" +#endif +#ifdef LANGUAGE_PL_PL + #include "lang/pl-PL.rc" +#endif +#ifdef LANGUAGE_PT_BR + #include "lang/pt-BR.rc" +#endif +#ifdef LANGUAGE_RO_RO + #include "lang/ro-RO.rc" +#endif +#ifdef LANGUAGE_RU_RU + #include "lang/ru-RU.rc" +#endif +#ifdef LANGUAGE_SK_SK + #include "lang/sk-SK.rc" +#endif +#ifdef LANGUAGE_SV_SE + #include "lang/sv-SE.rc" +#endif +#ifdef LANGUAGE_TR_TR + #include "lang/tr-TR.rc" +#endif +#ifdef LANGUAGE_UK_UA + #include "lang/uk-UA.rc" +#endif +#ifdef LANGUAGE_ZH_CN + #include "lang/zh-CN.rc" +#endif +#ifdef LANGUAGE_ZH_TW + #include "lang/zh-TW.rc" +#endif diff --git a/reactos/win32ss/user/winsrv/consrv_new/frontends/gui/text.c b/reactos/win32ss/user/winsrv/consrv_new/frontends/gui/text.c new file mode 100644 index 00000000000..38bc4413063 --- /dev/null +++ b/reactos/win32ss/user/winsrv/consrv_new/frontends/gui/text.c @@ -0,0 +1,300 @@ +/* + * COPYRIGHT: See COPYING in the top level directory + * PROJECT: ReactOS Console Server DLL + * FILE: win32ss/user/winsrv/consrv/frontends/gui/text.c + * PURPOSE: GUI Terminal Front-End - Support for text-mode screen-buffers + * PROGRAMMERS: Gé van Geldorp + * Johannes Anderwald + * Jeffrey Morlan + * Hermes Belusca-Maito (hermes.belusca@sfr.fr) + */ + +/* INCLUDES *******************************************************************/ + +#include "consrv.h" +#include "include/conio.h" +#include "include/settings.h" +#include "guisettings.h" + +#define NDEBUG +#include + + +/* FUNCTIONS ******************************************************************/ + +VOID +GuiCopyFromTextModeBuffer(PTEXTMODE_SCREEN_BUFFER Buffer) +{ + /* + * This function supposes that the system clipboard was opened. + */ + + PCONSOLE Console = Buffer->Header.Console; + + /* + * Pressing the Shift key while copying text, allows us to copy + * text without newline characters (inline-text copy mode). + */ + BOOL InlineCopyMode = (GetKeyState(VK_SHIFT) & 0x8000); + + HANDLE hData; + PCHAR_INFO ptr; + LPWSTR data, dstPos; + ULONG selWidth, selHeight; + ULONG xPos, yPos, size; + + selWidth = Console->Selection.srSelection.Right - Console->Selection.srSelection.Left + 1; + selHeight = Console->Selection.srSelection.Bottom - Console->Selection.srSelection.Top + 1; + DPRINT("Selection is (%d|%d) to (%d|%d)\n", + Console->Selection.srSelection.Left, + Console->Selection.srSelection.Top, + Console->Selection.srSelection.Right, + Console->Selection.srSelection.Bottom); + + /* Basic size for one line... */ + size = selWidth; + /* ... and for the other lines, add newline characters if needed. */ + if (selHeight > 0) + { + /* + * If we are not in inline-text copy mode, each selected line must + * finish with \r\n . Otherwise, the lines will be just concatenated. + */ + size += (selWidth + (!InlineCopyMode ? 2 : 0)) * (selHeight - 1); + } + size += 1; /* Null-termination */ + size *= sizeof(WCHAR); + + /* Allocate memory, it will be passed to the system and may not be freed here */ + hData = GlobalAlloc(GMEM_MOVEABLE | GMEM_ZEROINIT, size); + if (hData == NULL) return; + + data = GlobalLock(hData); + if (data == NULL) return; + + DPRINT("Copying %dx%d selection\n", selWidth, selHeight); + dstPos = data; + + for (yPos = 0; yPos < selHeight; yPos++) + { + ptr = ConioCoordToPointer(Buffer, + Console->Selection.srSelection.Left, + yPos + Console->Selection.srSelection.Top); + /* Copy only the characters, leave attributes alone */ + for (xPos = 0; xPos < selWidth; xPos++) + { + /* + * Sometimes, applications can put NULL chars into the screen-buffer + * (this behaviour is allowed). Detect this and replace by a space. + * FIXME - HACK: Improve the way we're doing that (i.e., put spaces + * instead of NULLs (or even, nothing) only if it exists a non-null + * char *after* those NULLs, before the end-of-line of the selection. + * Do the same concerning spaces -- i.e. trailing spaces --). + */ + dstPos[xPos] = (ptr[xPos].Char.UnicodeChar ? ptr[xPos].Char.UnicodeChar : L' '); + } + dstPos += selWidth; + + /* Add newline characters if we are not in inline-text copy mode */ + if (!InlineCopyMode) + { + if (yPos != (selHeight - 1)) + { + wcscat(data, L"\r\n"); + dstPos += 2; + } + } + } + + DPRINT("Setting data <%S> to clipboard\n", data); + GlobalUnlock(hData); + + EmptyClipboard(); + SetClipboardData(CF_UNICODETEXT, hData); +} + +VOID +GuiPasteToTextModeBuffer(PTEXTMODE_SCREEN_BUFFER Buffer) +{ + /* + * This function supposes that the system clipboard was opened. + */ + + PCONSOLE Console = Buffer->Header.Console; + + HANDLE hData; + LPWSTR str; + WCHAR CurChar = 0; + + SHORT VkKey; // MAKEWORD(low = vkey_code, high = shift_state); + INPUT_RECORD er; + + hData = GetClipboardData(CF_UNICODETEXT); + if (hData == NULL) return; + + str = GlobalLock(hData); + if (str == NULL) return; + + DPRINT("Got data <%S> from clipboard\n", str); + + er.EventType = KEY_EVENT; + er.Event.KeyEvent.wRepeatCount = 1; + while (*str) + { + /* \r or \n characters. Go to the line only if we get "\r\n" sequence. */ + if (CurChar == L'\r' && *str == L'\n') + { + str++; + continue; + } + CurChar = *str++; + + /* Get the key code (+ shift state) corresponding to the character */ + VkKey = VkKeyScanW(CurChar); + if (VkKey == 0xFFFF) + { + DPRINT1("VkKeyScanW failed - Should simulate the key...\n"); + continue; + } + + /* Pressing some control keys */ + + /* Pressing the character key, with the control keys maintained pressed */ + er.Event.KeyEvent.bKeyDown = TRUE; + er.Event.KeyEvent.wVirtualKeyCode = LOBYTE(VkKey); + er.Event.KeyEvent.wVirtualScanCode = MapVirtualKeyW(LOBYTE(VkKey), MAPVK_VK_TO_CHAR); + er.Event.KeyEvent.uChar.UnicodeChar = CurChar; + er.Event.KeyEvent.dwControlKeyState = 0; + if (HIBYTE(VkKey) & 1) + er.Event.KeyEvent.dwControlKeyState |= SHIFT_PRESSED; + if (HIBYTE(VkKey) & 2) + er.Event.KeyEvent.dwControlKeyState |= LEFT_CTRL_PRESSED; // RIGHT_CTRL_PRESSED; + if (HIBYTE(VkKey) & 4) + er.Event.KeyEvent.dwControlKeyState |= LEFT_ALT_PRESSED; // RIGHT_ALT_PRESSED; + + ConioProcessInputEvent(Console, &er); + + /* Up all the character and control keys */ + er.Event.KeyEvent.bKeyDown = FALSE; + ConioProcessInputEvent(Console, &er); + } + + GlobalUnlock(hData); +} + +VOID +GuiPaintTextModeBuffer(PTEXTMODE_SCREEN_BUFFER Buffer, + PGUI_CONSOLE_DATA GuiData, + HDC hDC, + PRECT rc) +{ + PCONSOLE Console = Buffer->Header.Console; + // ASSERT(Console == GuiData->Console); + + ULONG TopLine, BottomLine, LeftChar, RightChar; + ULONG Line, Char, Start; + PCHAR_INFO From; + PWCHAR To; + WORD LastAttribute, Attribute; + ULONG CursorX, CursorY, CursorHeight; + HBRUSH CursorBrush, OldBrush; + HFONT OldFont; + + if (Buffer->Buffer == NULL) return; + + TopLine = rc->top / GuiData->CharHeight + Buffer->ViewOrigin.Y; + BottomLine = (rc->bottom + (GuiData->CharHeight - 1)) / GuiData->CharHeight - 1 + Buffer->ViewOrigin.Y; + LeftChar = rc->left / GuiData->CharWidth + Buffer->ViewOrigin.X; + RightChar = (rc->right + (GuiData->CharWidth - 1)) / GuiData->CharWidth - 1 + Buffer->ViewOrigin.X; + + LastAttribute = ConioCoordToPointer(Buffer, LeftChar, TopLine)->Attributes; + + SetTextColor(hDC, RGBFromAttrib(Console, TextAttribFromAttrib(LastAttribute))); + SetBkColor(hDC, RGBFromAttrib(Console, BkgdAttribFromAttrib(LastAttribute))); + + if (BottomLine >= Buffer->ScreenBufferSize.Y) BottomLine = Buffer->ScreenBufferSize.Y - 1; + if (RightChar >= Buffer->ScreenBufferSize.X) RightChar = Buffer->ScreenBufferSize.X - 1; + + OldFont = SelectObject(hDC, GuiData->Font); + + for (Line = TopLine; Line <= BottomLine; Line++) + { + WCHAR LineBuffer[80]; // Buffer containing a part or all the line to be displayed + From = ConioCoordToPointer(Buffer, LeftChar, Line); // Get the first code of the line + Start = LeftChar; + To = LineBuffer; + + for (Char = LeftChar; Char <= RightChar; Char++) + { + /* + * We flush the buffer if the new attribute is different + * from the current one, or if the buffer is full. + */ + if (From->Attributes != LastAttribute || (Char - Start == sizeof(LineBuffer) / sizeof(WCHAR))) + { + TextOutW(hDC, + (Start - Buffer->ViewOrigin.X) * GuiData->CharWidth , + (Line - Buffer->ViewOrigin.Y) * GuiData->CharHeight, + LineBuffer, + Char - Start); + Start = Char; + To = LineBuffer; + Attribute = From->Attributes; + if (Attribute != LastAttribute) + { + SetTextColor(hDC, RGBFromAttrib(Console, TextAttribFromAttrib(Attribute))); + SetBkColor(hDC, RGBFromAttrib(Console, BkgdAttribFromAttrib(Attribute))); + LastAttribute = Attribute; + } + } + + *(To++) = (From++)->Char.UnicodeChar; + } + + TextOutW(hDC, + (Start - Buffer->ViewOrigin.X) * GuiData->CharWidth , + (Line - Buffer->ViewOrigin.Y) * GuiData->CharHeight, + LineBuffer, + RightChar - Start + 1); + } + + /* + * Draw the caret + */ + if (Buffer->CursorInfo.bVisible && + Buffer->CursorBlinkOn && + !Buffer->ForceCursorOff) + { + CursorX = Buffer->CursorPosition.X; + CursorY = Buffer->CursorPosition.Y; + if (LeftChar <= CursorX && CursorX <= RightChar && + TopLine <= CursorY && CursorY <= BottomLine) + { + CursorHeight = ConioEffectiveCursorSize(Console, GuiData->CharHeight); + Attribute = ConioCoordToPointer(Buffer, Buffer->CursorPosition.X, Buffer->CursorPosition.Y)->Attributes; + + if (Attribute != DEFAULT_SCREEN_ATTRIB) + { + CursorBrush = CreateSolidBrush(RGBFromAttrib(Console, Attribute)); + } + else + { + CursorBrush = CreateSolidBrush(RGBFromAttrib(Console, Buffer->ScreenDefaultAttrib)); + } + + OldBrush = SelectObject(hDC, CursorBrush); + PatBlt(hDC, + (CursorX - Buffer->ViewOrigin.X) * GuiData->CharWidth, + (CursorY - Buffer->ViewOrigin.Y) * GuiData->CharHeight + (GuiData->CharHeight - CursorHeight), + GuiData->CharWidth, + CursorHeight, + PATCOPY); + SelectObject(hDC, OldBrush); + DeleteObject(CursorBrush); + } + } + + SelectObject(hDC, OldFont); +} + +/* EOF */ diff --git a/reactos/win32ss/user/winsrv/consrv_new/frontends/input.c b/reactos/win32ss/user/winsrv/consrv_new/frontends/input.c new file mode 100644 index 00000000000..b0779463cb6 --- /dev/null +++ b/reactos/win32ss/user/winsrv/consrv_new/frontends/input.c @@ -0,0 +1,146 @@ +/* + * COPYRIGHT: See COPYING in the top level directory + * PROJECT: ReactOS Console Server DLL + * FILE: win32ss/user/winsrv/consrv/frontends/input.c + * PURPOSE: Common Front-Ends Input functions + * PROGRAMMERS: Jeffrey Morlan + * Hermes Belusca-Maito (hermes.belusca@sfr.fr) + */ + +/* INCLUDES *******************************************************************/ + +#include "consrv.h" +#include "include/conio.h" +#include "include/conio2.h" +#include "coninput.h" + +#define NDEBUG +#include + + +/* PRIVATE FUNCTIONS **********************************************************/ + +static DWORD FASTCALL +ConioGetShiftState(PBYTE KeyState, LPARAM lParam) +{ + DWORD ssOut = 0; + + if (KeyState[VK_CAPITAL] & 0x01) + ssOut |= CAPSLOCK_ON; + + if (KeyState[VK_NUMLOCK] & 0x01) + ssOut |= NUMLOCK_ON; + + if (KeyState[VK_SCROLL] & 0x01) + 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; + + /* See WM_CHAR MSDN documentation for instance */ + if (lParam & 0x01000000) + ssOut |= ENHANCED_KEY; + + return ssOut; +} + +VOID WINAPI +ConioProcessKey(PCONSOLE Console, MSG* msg) +{ + 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; + WCHAR UnicodeChar; + UINT VirtualKeyCode; + UINT VirtualScanCode; + BOOL Down = FALSE; + BOOLEAN Fake; // synthesized, not a real event + BOOLEAN NotChar; // message should not be used to return a character + + if (NULL == Console) + { + DPRINT1("No Active Console!\n"); + return; + } + + VirtualScanCode = HIWORD(msg->lParam) & 0xFF; + Down = msg->message == WM_KEYDOWN || msg->message == WM_CHAR || + msg->message == WM_SYSKEYDOWN || msg->message == WM_SYSCHAR; + + GetKeyboardState(KeyState); + ShiftState = ConioGetShiftState(KeyState, msg->lParam); + + 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, + NULL); + UnicodeChar = (1 == RetChars ? Chars[0] : 0); + } + + if (ConioProcessKeyCallback(Console, + msg, + KeyState[VK_MENU], + ShiftState, + VirtualKeyCode, + Down)) + { + 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("CONSRV: %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; + + /* Send the key press to the console driver */ + ConDrvProcessKey(Console, + Down, + VirtualKeyCode, + VirtualScanCode, + UnicodeChar, + ShiftState, + KeyState[VK_CONTROL]); +} + +/* EOF */ diff --git a/reactos/win32ss/user/winsrv/consrv_new/frontends/tui/tuiterm.c b/reactos/win32ss/user/winsrv/consrv_new/frontends/tui/tuiterm.c new file mode 100644 index 00000000000..84ae5c42358 --- /dev/null +++ b/reactos/win32ss/user/winsrv/consrv_new/frontends/tui/tuiterm.c @@ -0,0 +1,861 @@ +/* + * COPYRIGHT: See COPYING in the top level directory + * PROJECT: ReactOS Console Server DLL + * FILE: win32ss/user/winsrv/consrv/frontends/tui/tuiterm.c + * PURPOSE: TUI Terminal Front-End - Virtual Consoles... + * PROGRAMMERS: David Welch + * Gé van Geldorp + * Jeffrey Morlan + * Hermes Belusca-Maito (hermes.belusca@sfr.fr) + */ + +#ifdef TUITERM_COMPILE + +#include "consrv.h" +#include "include/conio.h" +#include "include/console.h" +#include "include/settings.h" +#include "tuiterm.h" +#include + +#define NDEBUG +#include + + +/* GLOBALS ********************************************************************/ + +#define GetNextConsole(Console) \ + CONTAINING_RECORD(Console->Entry.Flink, TUI_CONSOLE_DATA, Entry) + +#define GetPrevConsole(Console) \ + CONTAINING_RECORD(Console->Entry.Blink, TUI_CONSOLE_DATA, Entry) + + +/* TUI Console Window Class name */ +#define TUI_CONSOLE_WINDOW_CLASS L"TuiConsoleWindowClass" + +typedef struct _TUI_CONSOLE_DATA +{ + CRITICAL_SECTION Lock; + LIST_ENTRY Entry; /* Entry in the list of virtual consoles */ + // HANDLE hTuiInitEvent; + + HWND hWindow; /* Handle to the console's window (used for the window's procedure */ + + PCONSOLE Console; /* Pointer to the owned console */ + // TUI_CONSOLE_INFO TuiInfo; /* TUI terminal settings */ +} TUI_CONSOLE_DATA, *PTUI_CONSOLE_DATA; + +/* List of the maintained virtual consoles and its lock */ +static LIST_ENTRY VirtConsList; +static PTUI_CONSOLE_DATA ActiveConsole; /* The active console on screen */ +static CRITICAL_SECTION ActiveVirtConsLock; + +static COORD PhysicalConsoleSize; +static HANDLE ConsoleDeviceHandle; + +static BOOL ConsInitialized = FALSE; + +/******************************************************************************\ +|** BlueScreen Driver management **| +\**/ +/* Code taken and adapted from base/system/services/driver.c */ +static DWORD +ScmLoadDriver(LPCWSTR lpServiceName) +{ + NTSTATUS Status = STATUS_SUCCESS; + BOOLEAN WasPrivilegeEnabled = FALSE; + PWSTR pszDriverPath; + UNICODE_STRING DriverPath; + + /* Build the driver path */ + /* 52 = wcslen(L"\\Registry\\Machine\\System\\CurrentControlSet\\Services\\") */ + pszDriverPath = ConsoleAllocHeap(HEAP_ZERO_MEMORY, + (52 + wcslen(lpServiceName) + 1) * sizeof(WCHAR)); + if (pszDriverPath == NULL) + return ERROR_NOT_ENOUGH_MEMORY; + + wcscpy(pszDriverPath, + L"\\Registry\\Machine\\System\\CurrentControlSet\\Services\\"); + wcscat(pszDriverPath, + lpServiceName); + + RtlInitUnicodeString(&DriverPath, + pszDriverPath); + + DPRINT(" Path: %wZ\n", &DriverPath); + + /* Acquire driver-loading privilege */ + Status = RtlAdjustPrivilege(SE_LOAD_DRIVER_PRIVILEGE, + TRUE, + FALSE, + &WasPrivilegeEnabled); + if (!NT_SUCCESS(Status)) + { + /* We encountered a failure, exit properly */ + DPRINT1("CONSRV: Cannot acquire driver-loading privilege, Status = 0x%08lx\n", Status); + goto done; + } + + Status = NtLoadDriver(&DriverPath); + + /* Release driver-loading privilege */ + RtlAdjustPrivilege(SE_LOAD_DRIVER_PRIVILEGE, + WasPrivilegeEnabled, + FALSE, + &WasPrivilegeEnabled); + +done: + ConsoleFreeHeap(pszDriverPath); + return RtlNtStatusToDosError(Status); +} + +#ifdef BLUESCREEN_DRIVER_UNLOADING +static DWORD +ScmUnloadDriver(LPCWSTR lpServiceName) +{ + NTSTATUS Status = STATUS_SUCCESS; + BOOLEAN WasPrivilegeEnabled = FALSE; + PWSTR pszDriverPath; + UNICODE_STRING DriverPath; + + /* Build the driver path */ + /* 52 = wcslen(L"\\Registry\\Machine\\System\\CurrentControlSet\\Services\\") */ + pszDriverPath = ConsoleAllocHeap(HEAP_ZERO_MEMORY, + (52 + wcslen(lpServiceName) + 1) * sizeof(WCHAR)); + if (pszDriverPath == NULL) + return ERROR_NOT_ENOUGH_MEMORY; + + wcscpy(pszDriverPath, + L"\\Registry\\Machine\\System\\CurrentControlSet\\Services\\"); + wcscat(pszDriverPath, + lpServiceName); + + RtlInitUnicodeString(&DriverPath, + pszDriverPath); + + DPRINT(" Path: %wZ\n", &DriverPath); + + /* Acquire driver-unloading privilege */ + Status = RtlAdjustPrivilege(SE_LOAD_DRIVER_PRIVILEGE, + TRUE, + FALSE, + &WasPrivilegeEnabled); + if (!NT_SUCCESS(Status)) + { + /* We encountered a failure, exit properly */ + DPRINT1("CONSRV: Cannot acquire driver-unloading privilege, Status = 0x%08lx\n", Status); + goto done; + } + + Status = NtUnloadDriver(&DriverPath); + + /* Release driver-unloading privilege */ + RtlAdjustPrivilege(SE_LOAD_DRIVER_PRIVILEGE, + WasPrivilegeEnabled, + FALSE, + &WasPrivilegeEnabled); + +done: + ConsoleFreeHeap(pszDriverPath); + return RtlNtStatusToDosError(Status); +} +#endif +/**\ +\******************************************************************************/ + +static BOOL FASTCALL +TuiSwapConsole(INT Next) +{ + static PTUI_CONSOLE_DATA SwapConsole = NULL; /* Console we are thinking about swapping with */ + DWORD BytesReturned; + ANSI_STRING Title; + PVOID Buffer; + PCOORD pos; + + if (0 != Next) + { + /* + * Alt-Tab, swap consoles. + * move SwapConsole to next console, and print its title. + */ + EnterCriticalSection(&ActiveVirtConsLock); + if (!SwapConsole) SwapConsole = ActiveConsole; + + SwapConsole = (0 < Next ? GetNextConsole(SwapConsole) : GetPrevConsole(SwapConsole)); + Title.MaximumLength = RtlUnicodeStringToAnsiSize(&SwapConsole->Console->Title); + Title.Length = 0; + Buffer = ConsoleAllocHeap(0, sizeof(COORD) + Title.MaximumLength); + pos = (PCOORD)Buffer; + Title.Buffer = (PVOID)((ULONG_PTR)Buffer + sizeof(COORD)); + + RtlUnicodeStringToAnsiString(&Title, &SwapConsole->Console->Title, FALSE); + pos->X = (PhysicalConsoleSize.X - Title.Length) / 2; + pos->Y = PhysicalConsoleSize.Y / 2; + /* Redraw the console to clear off old title */ + ConioDrawConsole(ActiveConsole->Console); + if (!DeviceIoControl(ConsoleDeviceHandle, IOCTL_CONSOLE_WRITE_OUTPUT_CHARACTER, + NULL, 0, Buffer, sizeof(COORD) + Title.Length, + &BytesReturned, NULL)) + { + DPRINT1( "Error writing to console\n" ); + } + ConsoleFreeHeap(Buffer); + LeaveCriticalSection(&ActiveVirtConsLock); + + return TRUE; + } + else if (NULL != SwapConsole) + { + EnterCriticalSection(&ActiveVirtConsLock); + if (SwapConsole != ActiveConsole) + { + /* First remove swapconsole from the list */ + SwapConsole->Entry.Blink->Flink = SwapConsole->Entry.Flink; + SwapConsole->Entry.Flink->Blink = SwapConsole->Entry.Blink; + /* Now insert before activeconsole */ + SwapConsole->Entry.Flink = &ActiveConsole->Entry; + SwapConsole->Entry.Blink = ActiveConsole->Entry.Blink; + ActiveConsole->Entry.Blink->Flink = &SwapConsole->Entry; + ActiveConsole->Entry.Blink = &SwapConsole->Entry; + } + ActiveConsole = SwapConsole; + SwapConsole = NULL; + ConioDrawConsole(ActiveConsole->Console); + LeaveCriticalSection(&ActiveVirtConsLock); + return TRUE; + } + else + { + return FALSE; + } +} + +static VOID FASTCALL +TuiCopyRect(PCHAR Dest, PTEXTMODE_SCREEN_BUFFER Buff, SMALL_RECT* Region) +{ + UINT SrcDelta, DestDelta; + LONG i; + PCHAR_INFO Src, SrcEnd; + + Src = ConioCoordToPointer(Buff, Region->Left, Region->Top); + SrcDelta = Buff->ScreenBufferSize.X * sizeof(CHAR_INFO); + SrcEnd = Buff->Buffer + Buff->ScreenBufferSize.Y * Buff->ScreenBufferSize.X * sizeof(CHAR_INFO); + DestDelta = ConioRectWidth(Region) * 2 /* 2 == sizeof(CHAR) + sizeof(BYTE) */; + for (i = Region->Top; i <= Region->Bottom; i++) + { + ConsoleUnicodeCharToAnsiChar(Buff->Header.Console, (PCHAR)Dest, &Src->Char.UnicodeChar); + *(PBYTE)(Dest + 1) = (BYTE)Src->Attributes; + + Src += SrcDelta; + if (SrcEnd <= Src) + { + Src -= Buff->ScreenBufferSize.Y * Buff->ScreenBufferSize.X * sizeof(CHAR_INFO); + } + Dest += DestDelta; + } +} + +static LRESULT CALLBACK +TuiConsoleWndProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam) +{ +/* + PTUI_CONSOLE_DATA TuiData = NULL; + PCONSOLE Console = NULL; + + TuiData = TuiGetGuiData(hWnd); + if (TuiData == NULL) return 0; +*/ + + switch (msg) + { + case WM_CHAR: + case WM_SYSCHAR: + case WM_KEYDOWN: + case WM_SYSKEYDOWN: + case WM_KEYUP: + case WM_SYSKEYUP: + { + if (ConDrvValidateConsoleUnsafe(ActiveConsole->Console, CONSOLE_RUNNING, TRUE)) + { + MSG Message; + Message.hwnd = hWnd; + Message.message = msg; + Message.wParam = wParam; + Message.lParam = lParam; + + ConioProcessKey(ActiveConsole->Console, &Message); + LeaveCriticalSection(&ActiveConsole->Console->Lock); + } + break; + } + + case WM_ACTIVATE: + { + if (ConDrvValidateConsoleUnsafe(ActiveConsole->Console, CONSOLE_RUNNING, TRUE)) + { + if (LOWORD(wParam) != WA_INACTIVE) + { + SetFocus(hWnd); + ConioDrawConsole(ActiveConsole->Console); + } + LeaveCriticalSection(&ActiveConsole->Console->Lock); + } + break; + } + + default: + break; + } + + return DefWindowProcW(hWnd, msg, wParam, lParam); +} + +static DWORD WINAPI +TuiConsoleThread(PVOID Data) +{ + PTUI_CONSOLE_DATA TuiData = (PTUI_CONSOLE_DATA)Data; + PCONSOLE Console = TuiData->Console; + HWND NewWindow; + MSG msg; + + NewWindow = CreateWindowW(TUI_CONSOLE_WINDOW_CLASS, + Console->Title.Buffer, + 0, + -32000, -32000, 0, 0, + NULL, NULL, + ConSrvDllInstance, + (PVOID)Console); + if (NULL == NewWindow) + { + DPRINT1("CONSRV: Unable to create console window\n"); + return 1; + } + TuiData->hWindow = NewWindow; + + SetForegroundWindow(TuiData->hWindow); + NtUserConsoleControl(ConsoleAcquireDisplayOwnership, NULL, 0); + + while (GetMessageW(&msg, NULL, 0, 0)) + { + TranslateMessage(&msg); + DispatchMessageW(&msg); + } + + return 0; +} + +static BOOL +TuiInit(DWORD OemCP) +{ + BOOL Ret = FALSE; + CONSOLE_SCREEN_BUFFER_INFO ScrInfo; + DWORD BytesReturned; + WNDCLASSEXW wc; + ATOM ConsoleClassAtom; + USHORT TextAttribute = FOREGROUND_BLUE | FOREGROUND_GREEN | FOREGROUND_RED; + + /* Exit if we were already initialized */ + if (ConsInitialized) return TRUE; + + /* + * Initialize the TUI front-end: + * - load the console driver, + * - set default screen attributes, + * - grab the console size. + */ + ScmLoadDriver(L"Blue"); + + ConsoleDeviceHandle = CreateFileW(L"\\\\.\\BlueScreen", + FILE_ALL_ACCESS, + 0, NULL, + OPEN_EXISTING, + 0, NULL); + if (INVALID_HANDLE_VALUE == ConsoleDeviceHandle) + { + DPRINT1("Failed to open BlueScreen.\n"); + return FALSE; + } + + if (!DeviceIoControl(ConsoleDeviceHandle, IOCTL_CONSOLE_LOADFONT, + &OemCP, sizeof(OemCP), NULL, 0, + &BytesReturned, NULL)) + { + DPRINT1("Failed to load the font for codepage %d\n", OemCP); + /* Let's suppose the font is good enough to continue */ + } + + if (!DeviceIoControl(ConsoleDeviceHandle, IOCTL_CONSOLE_SET_TEXT_ATTRIBUTE, + &TextAttribute, sizeof(TextAttribute), NULL, 0, + &BytesReturned, NULL)) + { + DPRINT1("Failed to set text attribute\n"); + } + + ActiveConsole = NULL; + InitializeListHead(&VirtConsList); + InitializeCriticalSection(&ActiveVirtConsLock); + + if (!DeviceIoControl(ConsoleDeviceHandle, IOCTL_CONSOLE_GET_SCREEN_BUFFER_INFO, + NULL, 0, &ScrInfo, sizeof(ScrInfo), &BytesReturned, NULL)) + { + DPRINT1("Failed to get console info\n"); + Ret = FALSE; + goto Quit; + } + PhysicalConsoleSize = ScrInfo.dwSize; + + /* Register the TUI notification window class */ + RtlZeroMemory(&wc, sizeof(WNDCLASSEXW)); + wc.cbSize = sizeof(WNDCLASSEXW); + wc.lpszClassName = TUI_CONSOLE_WINDOW_CLASS; + wc.lpfnWndProc = TuiConsoleWndProc; + wc.cbWndExtra = 0; + wc.hInstance = ConSrvDllInstance; + + ConsoleClassAtom = RegisterClassExW(&wc); + if (ConsoleClassAtom == 0) + { + DPRINT1("Failed to register TUI console wndproc\n"); + Ret = FALSE; + } + else + { + Ret = TRUE; + } + +Quit: + if (Ret == FALSE) + { + DeleteCriticalSection(&ActiveVirtConsLock); + CloseHandle(ConsoleDeviceHandle); + } + + ConsInitialized = Ret; + return Ret; +} + + + +/****************************************************************************** + * TUI Console Driver * + ******************************************************************************/ + +static VOID WINAPI +TuiDeinitFrontEnd(IN OUT PFRONTEND This /*, + IN PCONSOLE Console */); + +NTSTATUS NTAPI +TuiInitFrontEnd(IN OUT PFRONTEND This, + IN PCONSOLE Console) +{ + PTUI_CONSOLE_DATA TuiData; + HANDLE ThreadHandle; + + if (This == NULL || Console == NULL) + return STATUS_INVALID_PARAMETER; + + // if (GetType(Console->ActiveBuffer) != TEXTMODE_BUFFER) + // return STATUS_INVALID_PARAMETER; + + // /* Initialize the console */ + // Console->TermIFace.Vtbl = &TuiVtbl; + + TuiData = ConsoleAllocHeap(HEAP_ZERO_MEMORY, sizeof(TUI_CONSOLE_DATA)); + if (!TuiData) + { + DPRINT1("CONSRV: Failed to create TUI_CONSOLE_DATA\n"); + return STATUS_UNSUCCESSFUL; + } + // Console->TermIFace.Data = (PVOID)TuiData; + TuiData->Console = Console; + TuiData->hWindow = NULL; + + InitializeCriticalSection(&TuiData->Lock); + + /* + * HACK: Resize the console since we don't support for now changing + * the console size when we display it with the hardware. + */ + // Console->ConsoleSize = PhysicalConsoleSize; + // ConioResizeBuffer(Console, (PTEXTMODE_SCREEN_BUFFER)(Console->ActiveBuffer), PhysicalConsoleSize); + + // /* The console cannot be resized anymore */ + // Console->FixedSize = TRUE; // MUST be placed AFTER the call to ConioResizeBuffer !! + // // ConioResizeTerminal(Console); + + /* + * Contrary to what we do in the GUI front-end, here we create + * an input thread for each console. It will dispatch all the + * input messages to the proper console (on the GUI it is done + * via the default GUI dispatch thread). + */ + ThreadHandle = CreateThread(NULL, + 0, + TuiConsoleThread, + (PVOID)TuiData, + 0, + NULL); + if (NULL == ThreadHandle) + { + DPRINT1("CONSRV: Unable to create console thread\n"); + // TuiDeinitFrontEnd(Console); + TuiDeinitFrontEnd(This); + return STATUS_UNSUCCESSFUL; + } + CloseHandle(ThreadHandle); + + /* + * Insert the newly created console in the list of virtual consoles + * and activate it (give it the focus). + */ + EnterCriticalSection(&ActiveVirtConsLock); + InsertTailList(&VirtConsList, &TuiData->Entry); + ActiveConsole = TuiData; + LeaveCriticalSection(&ActiveVirtConsLock); + + /* Finally, initialize the frontend structure */ + This->Data = TuiData; + This->OldData = NULL; + + return STATUS_SUCCESS; +} + +static VOID WINAPI +TuiDeinitFrontEnd(IN OUT PFRONTEND This) +{ + // PCONSOLE Console = This->Console; + PTUI_CONSOLE_DATA TuiData = This->Data; // Console->TermIFace.Data; + + /* Close the notification window */ + DestroyWindow(TuiData->hWindow); + + /* + * Set the active console to the next one + * and remove the console from the list. + */ + EnterCriticalSection(&ActiveVirtConsLock); + ActiveConsole = GetNextConsole(TuiData); + RemoveEntryList(&TuiData->Entry); + + // /* Switch to next console */ + // if (ActiveConsole == TuiData) + // if (ActiveConsole->Console == Console) + // { + // ActiveConsole = (TuiData->Entry.Flink != TuiData->Entry ? GetNextConsole(TuiData) : NULL); + // } + + // if (GetNextConsole(TuiData) != TuiData) + // { + // TuiData->Entry.Blink->Flink = TuiData->Entry.Flink; + // TuiData->Entry.Flink->Blink = TuiData->Entry.Blink; + // } + + LeaveCriticalSection(&ActiveVirtConsLock); + + /* Switch to the next console */ + if (NULL != ActiveConsole) ConioDrawConsole(ActiveConsole->Console); + + // Console->TermIFace.Data = NULL; + This->Data = NULL; + DeleteCriticalSection(&TuiData->Lock); + ConsoleFreeHeap(TuiData); +} + +static VOID WINAPI +TuiDrawRegion(IN OUT PFRONTEND This, + SMALL_RECT* Region) +{ + DWORD BytesReturned; + PCONSOLE_SCREEN_BUFFER Buff = Console->ActiveBuffer; + PCONSOLE_DRAW ConsoleDraw; + UINT ConsoleDrawSize; + + if (ActiveConsole->Console != Console || GetType(Buff) != TEXTMODE_BUFFER) return; + + ConsoleDrawSize = sizeof(CONSOLE_DRAW) + + (ConioRectWidth(Region) * ConioRectHeight(Region)) * 2; + ConsoleDraw = ConsoleAllocHeap(0, ConsoleDrawSize); + if (NULL == ConsoleDraw) + { + DPRINT1("ConsoleAllocHeap failed\n"); + return; + } + ConsoleDraw->X = Region->Left; + ConsoleDraw->Y = Region->Top; + ConsoleDraw->SizeX = ConioRectWidth(Region); + ConsoleDraw->SizeY = ConioRectHeight(Region); + ConsoleDraw->CursorX = Buff->CursorPosition.X; + ConsoleDraw->CursorY = Buff->CursorPosition.Y; + + TuiCopyRect((PCHAR)(ConsoleDraw + 1), (PTEXTMODE_SCREEN_BUFFER)Buff, Region); + + if (!DeviceIoControl(ConsoleDeviceHandle, IOCTL_CONSOLE_DRAW, + NULL, 0, ConsoleDraw, ConsoleDrawSize, &BytesReturned, NULL)) + { + DPRINT1("Failed to draw console\n"); + ConsoleFreeHeap(ConsoleDraw); + return; + } + + ConsoleFreeHeap(ConsoleDraw); +} + +static VOID WINAPI +TuiWriteStream(IN OUT PFRONTEND This, + SMALL_RECT* Region, + SHORT CursorStartX, + SHORT CursorStartY, + UINT ScrolledLines, + PWCHAR Buffer, + UINT Length) +{ + PCONSOLE_SCREEN_BUFFER Buff = Console->ActiveBuffer; + PCHAR NewBuffer; + ULONG NewLength; + DWORD BytesWritten; + + if (ActiveConsole->Console->ActiveBuffer != Buff) return; + + NewLength = WideCharToMultiByte(Console->OutputCodePage, 0, + Buffer, Length, + NULL, 0, NULL, NULL); + NewBuffer = RtlAllocateHeap(RtlGetProcessHeap(), 0, NewLength * sizeof(CHAR)); + if (!NewBuffer) return; + + WideCharToMultiByte(Console->OutputCodePage, 0, + Buffer, Length, + NewBuffer, NewLength, NULL, NULL); + + if (!WriteFile(ConsoleDeviceHandle, NewBuffer, NewLength * sizeof(CHAR), &BytesWritten, NULL)) + { + DPRINT1("Error writing to BlueScreen\n"); + } + + RtlFreeHeap(RtlGetProcessHeap(), 0, NewBuffer); +} + +static BOOL WINAPI +TuiSetCursorInfo(IN OUT PFRONTEND This, + PCONSOLE_SCREEN_BUFFER Buff) +{ + CONSOLE_CURSOR_INFO Info; + DWORD BytesReturned; + + if (ActiveConsole->Console->ActiveBuffer != Buff) return TRUE; + + Info.dwSize = ConioEffectiveCursorSize(Console, 100); + Info.bVisible = Buff->CursorInfo.bVisible; + + if (!DeviceIoControl(ConsoleDeviceHandle, IOCTL_CONSOLE_SET_CURSOR_INFO, + &Info, sizeof(Info), NULL, 0, &BytesReturned, NULL)) + { + DPRINT1( "Failed to set cursor info\n" ); + return FALSE; + } + + return TRUE; +} + +static BOOL WINAPI +TuiSetScreenInfo(IN OUT PFRONTEND This, + PCONSOLE_SCREEN_BUFFER Buff, + SHORT OldCursorX, + SHORT OldCursorY) +{ + CONSOLE_SCREEN_BUFFER_INFO Info; + DWORD BytesReturned; + + if (ActiveConsole->Console->ActiveBuffer != Buff) return TRUE; + if (GetType(Buff) != TEXTMODE_BUFFER) return FALSE; + + Info.dwCursorPosition = Buff->CursorPosition; + Info.wAttributes = ((PTEXTMODE_SCREEN_BUFFER)Buff)->ScreenDefaultAttrib; + + if (!DeviceIoControl(ConsoleDeviceHandle, IOCTL_CONSOLE_SET_SCREEN_BUFFER_INFO, + &Info, sizeof(CONSOLE_SCREEN_BUFFER_INFO), NULL, 0, + &BytesReturned, NULL)) + { + DPRINT1( "Failed to set cursor position\n" ); + return FALSE; + } + + return TRUE; +} + +static VOID WINAPI +TuiResizeTerminal(IN OUT PFRONTEND This) +{ +} + +static BOOL WINAPI +TuiProcessKeyCallback(IN OUT PFRONTEND This, + MSG* msg, + BYTE KeyStateMenu, + DWORD ShiftState, + UINT VirtualKeyCode, + BOOL Down) +{ + if (0 != (ShiftState & (RIGHT_ALT_PRESSED | LEFT_ALT_PRESSED)) && + VK_TAB == VirtualKeyCode) + { + if (Down) + { + TuiSwapConsole(ShiftState & SHIFT_PRESSED ? -1 : 1); + } + + return TRUE; + } + else if (VK_MENU == VirtualKeyCode && !Down) + { + return TuiSwapConsole(0); + } + + return FALSE; +} + +static VOID WINAPI +TuiRefreshInternalInfo(IN OUT PFRONTEND This) +{ +} + +static VOID WINAPI +TuiChangeTitle(IN OUT PFRONTEND This) +{ +} + +static BOOL WINAPI +TuiChangeIcon(IN OUT PFRONTEND This, + HICON hWindowIcon) +{ + return TRUE; +} + +static HWND WINAPI +TuiGetConsoleWindowHandle(IN OUT PFRONTEND This) +{ + PTUI_CONSOLE_DATA TuiData = This->Data; + return TuiData->hWindow; +} + +static VOID WINAPI +TuiGetLargestConsoleWindowSize(IN OUT PFRONTEND This, + PCOORD pSize) +{ + if (!pSize) return; + *pSize = PhysicalConsoleSize; +} + +static ULONG WINAPI +TuiGetDisplayMode(IN OUT PFRONTEND This) +{ + return CONSOLE_FULLSCREEN_HARDWARE; // CONSOLE_FULLSCREEN; +} + +static BOOL WINAPI +TuiSetDisplayMode(IN OUT PFRONTEND This, + ULONG NewMode) +{ + // if (NewMode & ~(CONSOLE_FULLSCREEN_MODE | CONSOLE_WINDOWED_MODE)) + // return FALSE; + return TRUE; +} + +static INT WINAPI +TuiShowMouseCursor(IN OUT PFRONTEND This, + BOOL Show) +{ + return 0; +} + +static BOOL WINAPI +TuiSetMouseCursor(IN OUT PFRONTEND This, + HCURSOR hCursor) +{ + return TRUE; +} + +static HMENU WINAPI +TuiMenuControl(IN OUT PFRONTEND This, + UINT cmdIdLow, + UINT cmdIdHigh) +{ + return NULL; +} + +static BOOL WINAPI +TuiSetMenuClose(IN OUT PFRONTEND This, + BOOL Enable) +{ + return TRUE; +} + +static FRONTEND_VTBL TuiVtbl = +{ + TuiInitFrontEnd, + TuiDeinitFrontEnd, + TuiDrawRegion, + TuiWriteStream, + TuiSetCursorInfo, + TuiSetScreenInfo, + TuiResizeTerminal, + TuiProcessKeyCallback, + TuiRefreshInternalInfo, + TuiChangeTitle, + TuiChangeIcon, + TuiGetConsoleWindowHandle, + TuiGetLargestConsoleWindowSize, + TuiGetDisplayMode, + TuiSetDisplayMode, + TuiShowMouseCursor, + TuiSetMouseCursor, + TuiMenuControl, + TuiSetMenuClose, +}; + +// static BOOL +// DtbgIsDesktopVisible(VOID) +// { + // return !((BOOL)NtUserCallNoParam(NOPARAM_ROUTINE_ISCONSOLEMODE)); +// } +static BOOLEAN +IsConsoleMode(VOID) +{ + return (BOOLEAN)NtUserCallNoParam(NOPARAM_ROUTINE_ISCONSOLEMODE); +} + +NTSTATUS NTAPI +TuiLoadFrontEnd(IN OUT PFRONTEND FrontEnd, + IN OUT PCONSOLE_INFO ConsoleInfo, + IN OUT PVOID ExtraConsoleInfo, + IN ULONG ProcessId) +{ + if (FrontEnd == NULL || ConsoleInfo == NULL) + return STATUS_INVALID_PARAMETER; + + /* We must be in console mode already */ + if (!IsConsoleMode()) return STATUS_UNSUCCESSFUL; + + /* Initialize the TUI terminal emulator */ + if (!TuiInit(ConsoleInfo->CodePage)) return STATUS_UNSUCCESSFUL; + + /* Finally, initialize the frontend structure */ + FrontEnd->Vtbl = &TuiVtbl; + FrontEnd->Data = NULL; + FrontEnd->OldData = NULL; + + return STATUS_SUCCESS; +} + +NTSTATUS NTAPI +TuiUnloadFrontEnd(IN OUT PFRONTEND FrontEnd) +{ + if (FrontEnd == NULL) return STATUS_INVALID_PARAMETER; + if (FrontEnd->Data) TuiDeinitFrontEnd(FrontEnd); + + return STATUS_SUCCESS; +} + +#endif + +/* EOF */ diff --git a/reactos/win32ss/user/winsrv/consrv_new/frontends/tui/tuiterm.h b/reactos/win32ss/user/winsrv/consrv_new/frontends/tui/tuiterm.h new file mode 100644 index 00000000000..8abbcb9810b --- /dev/null +++ b/reactos/win32ss/user/winsrv/consrv_new/frontends/tui/tuiterm.h @@ -0,0 +1,19 @@ +/* + * COPYRIGHT: See COPYING in the top level directory + * PROJECT: ReactOS Console Server DLL + * FILE: win32ss/user/winsrv/consrv/frontends/tui/tuiterm.h + * PURPOSE: TUI Terminal Front-End + * PROGRAMMERS: David Welch + * Gé van Geldorp + * Jeffrey Morlan + * Hermes Belusca-Maito (hermes.belusca@sfr.fr) + */ + +#pragma once + +NTSTATUS FASTCALL TuiInitConsole(PCONSOLE Console, + /*IN*/ PCONSOLE_START_INFO ConsoleStartInfo, + PCONSOLE_INFO ConsoleInfo, + DWORD ProcessId); + +/* EOF */ diff --git a/reactos/win32ss/user/winsrv/consrv_new/handle.c b/reactos/win32ss/user/winsrv/consrv_new/handle.c new file mode 100644 index 00000000000..f5ef3e755f3 --- /dev/null +++ b/reactos/win32ss/user/winsrv/consrv_new/handle.c @@ -0,0 +1,866 @@ +/* + * LICENSE: GPL - See COPYING in the top level directory + * PROJECT: ReactOS Console Server DLL + * FILE: win32ss/user/winsrv/consrv/handle.c + * PURPOSE: Console I/O Handles functions + * PROGRAMMERS: David Welch + * Jeffrey Morlan + * Hermes Belusca-Maito (hermes.belusca@sfr.fr) + */ + +/* INCLUDES *******************************************************************/ + +#include "consrv.h" +#include "include/conio.h" +#include "include/conio2.h" +#include "handle.h" +#include "include/console.h" +#include "console.h" +#include "conoutput.h" + +#define NDEBUG +#include + + +/* GLOBALS ********************************************************************/ + +typedef struct _CONSOLE_IO_HANDLE +{ + PCONSOLE_IO_OBJECT Object; /* The object on which the handle points to */ + DWORD Access; + BOOL Inheritable; + DWORD ShareMode; +} CONSOLE_IO_HANDLE, *PCONSOLE_IO_HANDLE; + + +/* PRIVATE FUNCTIONS **********************************************************/ + +static INT +AdjustHandleCounts(PCONSOLE_IO_HANDLE Entry, INT Change) +{ + PCONSOLE_IO_OBJECT Object = Entry->Object; + + DPRINT("AdjustHandleCounts(0x%p, %d), Object = 0x%p\n", Entry, Change, Object); + DPRINT("\tAdjustHandleCounts(0x%p, %d), Object = 0x%p, Object->HandleCount = %d, Object->Type = %lu\n", Entry, Change, Object, Object->HandleCount, Object->Type); + + if (Entry->Access & GENERIC_READ) Object->AccessRead += Change; + if (Entry->Access & GENERIC_WRITE) Object->AccessWrite += Change; + if (!(Entry->ShareMode & FILE_SHARE_READ)) Object->ExclusiveRead += Change; + if (!(Entry->ShareMode & FILE_SHARE_WRITE)) Object->ExclusiveWrite += Change; + + Object->HandleCount += Change; + + return Object->HandleCount; +} + +static VOID +ConSrvCreateHandleEntry(PCONSOLE_IO_HANDLE Entry) +{ + /// LOCK /// PCONSOLE_IO_OBJECT Object = Entry->Object; + /// LOCK /// EnterCriticalSection(&Object->Console->Lock); + AdjustHandleCounts(Entry, +1); + /// LOCK /// LeaveCriticalSection(&Object->Console->Lock); +} + +static VOID +ConSrvCloseHandleEntry(PCONSOLE_IO_HANDLE Entry) +{ + PCONSOLE_IO_OBJECT Object = Entry->Object; + if (Object != NULL) + { + /// LOCK /// PCONSOLE Console = Object->Console; + /// LOCK /// EnterCriticalSection(&Console->Lock); + + /* + * If this is a input handle, notify and dereference + * all the waits related to this handle. + */ + if (Object->Type == INPUT_BUFFER) + { + PCONSOLE_INPUT_BUFFER InputBuffer = (PCONSOLE_INPUT_BUFFER)Object; + + /* + * Wake up all the writing waiters related to this handle for this + * input buffer, if any, then dereference them and purge them all + * from the list. + * To select them amongst all the waiters for this input buffer, + * pass the handle pointer to the waiters, then they will check + * whether or not they are related to this handle and if so, they + * return. + */ + CsrNotifyWait(&InputBuffer->ReadWaitQueue, + WaitAll, + NULL, + (PVOID)Entry); + if (!IsListEmpty(&InputBuffer->ReadWaitQueue)) + { + CsrDereferenceWait(&InputBuffer->ReadWaitQueue); + } + } + + /* If the last handle to a screen buffer is closed, delete it... */ + if (AdjustHandleCounts(Entry, -1) == 0) + { + if (Object->Type == TEXTMODE_BUFFER || Object->Type == GRAPHICS_BUFFER) + { + PCONSOLE_SCREEN_BUFFER Buffer = (PCONSOLE_SCREEN_BUFFER)Object; + /* ...unless it's the only buffer left. Windows allows deletion + * even of the last buffer, but having to deal with a lack of + * any active buffer might be error-prone. */ + if (Buffer->ListEntry.Flink != Buffer->ListEntry.Blink) + ConioDeleteScreenBuffer(Buffer); + } + else if (Object->Type == INPUT_BUFFER) + { + DPRINT("Closing the input buffer\n"); + } + else + { + DPRINT1("Invalid object type %d\n", Object->Type); + } + } + + /// LOCK /// LeaveCriticalSection(&Console->Lock); + + /* Invalidate (zero-out) this handle entry */ + // Entry->Object = NULL; + // RtlZeroMemory(Entry, sizeof(*Entry)); + } + RtlZeroMemory(Entry, sizeof(*Entry)); // Be sure the whole entry is invalidated. +} + + +/* Forward declaration, used in ConSrvInitHandlesTable */ +static VOID ConSrvFreeHandlesTable(PCONSOLE_PROCESS_DATA ProcessData); + +static NTSTATUS +ConSrvInitHandlesTable(IN OUT PCONSOLE_PROCESS_DATA ProcessData, + IN PCONSOLE Console, + OUT PHANDLE pInputHandle, + OUT PHANDLE pOutputHandle, + OUT PHANDLE pErrorHandle) +{ + NTSTATUS Status; + HANDLE InputHandle = INVALID_HANDLE_VALUE, + OutputHandle = INVALID_HANDLE_VALUE, + ErrorHandle = INVALID_HANDLE_VALUE; + + /* + * Initialize the handles table. Use temporary variables to store + * the handles values in such a way that, if we fail, we don't + * return to the caller invalid handle values. + * + * Insert the IO handles. + */ + + RtlEnterCriticalSection(&ProcessData->HandleTableLock); + + /* Insert the Input handle */ + Status = ConSrvInsertObject(ProcessData, + &InputHandle, + &Console->InputBuffer.Header, + GENERIC_READ | GENERIC_WRITE, + TRUE, + FILE_SHARE_READ | FILE_SHARE_WRITE); + if (!NT_SUCCESS(Status)) + { + DPRINT1("Failed to insert the input handle\n"); + RtlLeaveCriticalSection(&ProcessData->HandleTableLock); + ConSrvFreeHandlesTable(ProcessData); + return Status; + } + + /* Insert the Output handle */ + Status = ConSrvInsertObject(ProcessData, + &OutputHandle, + &Console->ActiveBuffer->Header, + GENERIC_READ | GENERIC_WRITE, + TRUE, + FILE_SHARE_READ | FILE_SHARE_WRITE); + if (!NT_SUCCESS(Status)) + { + DPRINT1("Failed to insert the output handle\n"); + RtlLeaveCriticalSection(&ProcessData->HandleTableLock); + ConSrvFreeHandlesTable(ProcessData); + return Status; + } + + /* Insert the Error handle */ + Status = ConSrvInsertObject(ProcessData, + &ErrorHandle, + &Console->ActiveBuffer->Header, + GENERIC_READ | GENERIC_WRITE, + TRUE, + FILE_SHARE_READ | FILE_SHARE_WRITE); + if (!NT_SUCCESS(Status)) + { + DPRINT1("Failed to insert the error handle\n"); + RtlLeaveCriticalSection(&ProcessData->HandleTableLock); + ConSrvFreeHandlesTable(ProcessData); + return Status; + } + + /* Return the newly created handles */ + *pInputHandle = InputHandle; + *pOutputHandle = OutputHandle; + *pErrorHandle = ErrorHandle; + + RtlLeaveCriticalSection(&ProcessData->HandleTableLock); + return STATUS_SUCCESS; +} + +NTSTATUS +ConSrvInheritHandlesTable(IN PCONSOLE_PROCESS_DATA SourceProcessData, + IN PCONSOLE_PROCESS_DATA TargetProcessData) +{ + NTSTATUS Status = STATUS_SUCCESS; + ULONG i, j; + + RtlEnterCriticalSection(&SourceProcessData->HandleTableLock); + + /* Inherit a handles table only if there is no already */ + if (TargetProcessData->HandleTable != NULL /* || TargetProcessData->HandleTableSize != 0 */) + { + Status = STATUS_UNSUCCESSFUL; + goto Quit; + } + + /* Allocate a new handle table for the child process */ + TargetProcessData->HandleTable = ConsoleAllocHeap(HEAP_ZERO_MEMORY, + SourceProcessData->HandleTableSize + * sizeof(CONSOLE_IO_HANDLE)); + if (TargetProcessData->HandleTable == NULL) + { + Status = STATUS_NO_MEMORY; + goto Quit; + } + + TargetProcessData->HandleTableSize = SourceProcessData->HandleTableSize; + + /* + * Parse the parent process' handles table and, for each handle, + * do a copy of it and reference it, if the handle is inheritable. + */ + for (i = 0, j = 0; i < SourceProcessData->HandleTableSize; i++) + { + if (SourceProcessData->HandleTable[i].Object != NULL && + SourceProcessData->HandleTable[i].Inheritable) + { + /* + * Copy the handle data and increment the reference count of the + * pointed object (via the call to ConSrvCreateHandleEntry). + */ + TargetProcessData->HandleTable[j] = SourceProcessData->HandleTable[i]; + ConSrvCreateHandleEntry(&TargetProcessData->HandleTable[j]); + ++j; + } + } + +Quit: + RtlLeaveCriticalSection(&SourceProcessData->HandleTableLock); + return Status; +} + +static VOID +ConSrvFreeHandlesTable(PCONSOLE_PROCESS_DATA ProcessData) +{ + RtlEnterCriticalSection(&ProcessData->HandleTableLock); + + if (ProcessData->HandleTable != NULL) + { + ULONG i; + + /* + * ProcessData->ConsoleHandle is NULL (and the assertion fails) when + * ConSrvFreeHandlesTable is called in ConSrvConnect during the + * allocation of a new console. + */ + // ASSERT(ProcessData->ConsoleHandle); + if (ProcessData->ConsoleHandle != NULL) + { + /* Close all the console handles */ + for (i = 0; i < ProcessData->HandleTableSize; i++) + { + ConSrvCloseHandleEntry(&ProcessData->HandleTable[i]); + } + } + /* Free the handles table memory */ + ConsoleFreeHeap(ProcessData->HandleTable); + ProcessData->HandleTable = NULL; + } + + ProcessData->HandleTableSize = 0; + + RtlLeaveCriticalSection(&ProcessData->HandleTableLock); +} + +VOID +FASTCALL +ConSrvInitObject(IN OUT PCONSOLE_IO_OBJECT Object, + IN CONSOLE_IO_OBJECT_TYPE Type, + IN PCONSOLE Console) +{ + ASSERT(Object); + // if (!Object) return; + + Object->Type = Type; + Object->Console = Console; + Object->AccessRead = Object->AccessWrite = 0; + Object->ExclusiveRead = Object->ExclusiveWrite = 0; + Object->HandleCount = 0; +} + +NTSTATUS +FASTCALL +ConSrvInsertObject(PCONSOLE_PROCESS_DATA ProcessData, + PHANDLE Handle, + PCONSOLE_IO_OBJECT Object, + DWORD Access, + BOOL Inheritable, + DWORD ShareMode) +{ +#define IO_HANDLES_INCREMENT 2 * 3 + + ULONG i = 0; + PCONSOLE_IO_HANDLE Block; + + // NOTE: Commented out because calling code always lock HandleTableLock before. + // RtlEnterCriticalSection(&ProcessData->HandleTableLock); + + ASSERT( (ProcessData->HandleTable == NULL && ProcessData->HandleTableSize == 0) || + (ProcessData->HandleTable != NULL && ProcessData->HandleTableSize != 0) ); + + if (ProcessData->HandleTable) + { + for (i = 0; i < ProcessData->HandleTableSize; i++) + { + if (ProcessData->HandleTable[i].Object == NULL) + break; + } + } + + if (i >= ProcessData->HandleTableSize) + { + /* Allocate a new handles table */ + Block = ConsoleAllocHeap(HEAP_ZERO_MEMORY, + (ProcessData->HandleTableSize + + IO_HANDLES_INCREMENT) * sizeof(CONSOLE_IO_HANDLE)); + if (Block == NULL) + { + // RtlLeaveCriticalSection(&ProcessData->HandleTableLock); + return STATUS_UNSUCCESSFUL; + } + + /* If we previously had a handles table, free it and use the new one */ + if (ProcessData->HandleTable) + { + /* Copy the handles from the old table to the new one */ + RtlCopyMemory(Block, + ProcessData->HandleTable, + ProcessData->HandleTableSize * sizeof(CONSOLE_IO_HANDLE)); + ConsoleFreeHeap(ProcessData->HandleTable); + } + ProcessData->HandleTable = Block; + ProcessData->HandleTableSize += IO_HANDLES_INCREMENT; + } + + ProcessData->HandleTable[i].Object = Object; + ProcessData->HandleTable[i].Access = Access; + ProcessData->HandleTable[i].Inheritable = Inheritable; + ProcessData->HandleTable[i].ShareMode = ShareMode; + ConSrvCreateHandleEntry(&ProcessData->HandleTable[i]); + *Handle = ULongToHandle((i << 2) | 0x3); + + // RtlLeaveCriticalSection(&ProcessData->HandleTableLock); + + return STATUS_SUCCESS; +} + +NTSTATUS +FASTCALL +ConSrvRemoveObject(PCONSOLE_PROCESS_DATA ProcessData, + HANDLE Handle) +{ + ULONG Index = HandleToULong(Handle) >> 2; + PCONSOLE_IO_OBJECT Object; + + RtlEnterCriticalSection(&ProcessData->HandleTableLock); + + ASSERT(ProcessData->HandleTable); + + if (Index >= ProcessData->HandleTableSize || + (Object = ProcessData->HandleTable[Index].Object) == NULL) + { + RtlLeaveCriticalSection(&ProcessData->HandleTableLock); + return STATUS_INVALID_HANDLE; + } + + ASSERT(ProcessData->ConsoleHandle); + ConSrvCloseHandleEntry(&ProcessData->HandleTable[Index]); + + RtlLeaveCriticalSection(&ProcessData->HandleTableLock); + return STATUS_SUCCESS; +} + +NTSTATUS +FASTCALL +ConSrvGetObject(PCONSOLE_PROCESS_DATA ProcessData, + HANDLE Handle, + PCONSOLE_IO_OBJECT* Object, + PVOID* Entry OPTIONAL, + DWORD Access, + BOOL LockConsole, + CONSOLE_IO_OBJECT_TYPE Type) +{ + // NTSTATUS Status; + ULONG Index = HandleToULong(Handle) >> 2; + PCONSOLE_IO_HANDLE HandleEntry = NULL; + PCONSOLE_IO_OBJECT ObjectEntry = NULL; + // PCONSOLE ObjectConsole; + + ASSERT(Object); + if (Entry) *Entry = NULL; + + DPRINT("ConSrvGetObject -- Object: 0x%x, Handle: 0x%x\n", Object, Handle); + + RtlEnterCriticalSection(&ProcessData->HandleTableLock); + + if ( IsConsoleHandle(Handle) && + Index < ProcessData->HandleTableSize ) + { + HandleEntry = &ProcessData->HandleTable[Index]; + ObjectEntry = HandleEntry->Object; + } + + if ( HandleEntry == NULL || + ObjectEntry == NULL || + (HandleEntry->Access & Access) == 0 || + /*(Type != 0 && ObjectEntry->Type != Type)*/ + (Type != 0 && (ObjectEntry->Type & Type) == 0) ) + { + DPRINT1("ConSrvGetObject -- Invalid handle 0x%x of type %lu with access %lu ; retrieved object 0x%x (handle 0x%x) of type %lu with access %lu\n", + Handle, Type, Access, ObjectEntry, HandleEntry, (ObjectEntry ? ObjectEntry->Type : 0), (HandleEntry ? HandleEntry->Access : 0)); + + RtlLeaveCriticalSection(&ProcessData->HandleTableLock); + return STATUS_INVALID_HANDLE; + } + + RtlLeaveCriticalSection(&ProcessData->HandleTableLock); + + // Status = ConDrvGetConsole(&ObjectConsole, ProcessData->ConsoleHandle, LockConsole); + // if (NT_SUCCESS(Status)) + if (ConDrvValidateConsoleUnsafe(ObjectEntry->Console, CONSOLE_RUNNING, LockConsole)) + { + _InterlockedIncrement(&ObjectEntry->Console->ReferenceCount); + + /* Return the objects to the caller */ + *Object = ObjectEntry; + if (Entry) *Entry = HandleEntry; + + // RtlLeaveCriticalSection(&ProcessData->HandleTableLock); + return STATUS_SUCCESS; + } + else + { + // RtlLeaveCriticalSection(&ProcessData->HandleTableLock); + return STATUS_INVALID_HANDLE; + } +} + +VOID +FASTCALL +ConSrvReleaseObject(PCONSOLE_IO_OBJECT Object, + BOOL IsConsoleLocked) +{ + ConSrvReleaseConsole(Object->Console, IsConsoleLocked); +} + +NTSTATUS +FASTCALL +ConSrvAllocateConsole(PCONSOLE_PROCESS_DATA ProcessData, + PHANDLE pInputHandle, + PHANDLE pOutputHandle, + PHANDLE pErrorHandle, + PCONSOLE_START_INFO ConsoleStartInfo) +{ + NTSTATUS Status = STATUS_SUCCESS; + HANDLE ConsoleHandle; + PCONSOLE Console; + + /* + * We are about to create a new console. However when ConSrvNewProcess + * was called, we didn't know that we wanted to create a new console and + * therefore, we by default inherited the handles table from our parent + * process. It's only now that we notice that in fact we do not need + * them, because we've created a new console and thus we must use it. + * + * Therefore, free the handles table so that we can recreate + * a new one later on. + */ + ConSrvFreeHandlesTable(ProcessData); + + /* Initialize a new Console owned by this process */ + Status = ConSrvInitConsole(&ConsoleHandle, + &Console, + ConsoleStartInfo, + HandleToUlong(ProcessData->Process->ClientId.UniqueProcess)); + if (!NT_SUCCESS(Status)) + { + DPRINT1("Console initialization failed\n"); + return Status; + } + + /* Assign the new console handle */ + ProcessData->ConsoleHandle = ConsoleHandle; + + /* Initialize the handles table */ + Status = ConSrvInitHandlesTable(ProcessData, + Console, + pInputHandle, + pOutputHandle, + pErrorHandle); + if (!NT_SUCCESS(Status)) + { + DPRINT1("Failed to initialize the handles table\n"); + ConSrvDeleteConsole(Console); + ProcessData->ConsoleHandle = NULL; + return Status; + } + + /* Duplicate the Input Event */ + Status = NtDuplicateObject(NtCurrentProcess(), + Console->InputBuffer.ActiveEvent, + ProcessData->Process->ProcessHandle, + &ProcessData->ConsoleEvent, + EVENT_ALL_ACCESS, 0, 0); + if (!NT_SUCCESS(Status)) + { + DPRINT1("NtDuplicateObject() failed: %lu\n", Status); + ConSrvFreeHandlesTable(ProcessData); + ConSrvDeleteConsole(Console); + ProcessData->ConsoleHandle = NULL; + return Status; + } + + /* Insert the process into the processes list of the console */ + InsertHeadList(&Console->ProcessList, &ProcessData->ConsoleLink); + + /* Add a reference count because the process is tied to the console */ + _InterlockedIncrement(&Console->ReferenceCount); + + /* Update the internal info of the terminal */ + ConioRefreshInternalInfo(Console); + + return STATUS_SUCCESS; +} + +NTSTATUS +FASTCALL +ConSrvInheritConsole(PCONSOLE_PROCESS_DATA ProcessData, + HANDLE ConsoleHandle, + BOOL CreateNewHandlesTable, + PHANDLE pInputHandle, + PHANDLE pOutputHandle, + PHANDLE pErrorHandle) +{ + NTSTATUS Status = STATUS_SUCCESS; + PCONSOLE Console; + + /* Validate and lock the console */ + if (!ConDrvValidateConsole(&Console, + ConsoleHandle, + CONSOLE_RUNNING, TRUE)) + { + // FIXME: Find another status code + return STATUS_UNSUCCESSFUL; + } + + /* Inherit the console */ + ProcessData->ConsoleHandle = ConsoleHandle; + + if (CreateNewHandlesTable) + { + /* + * We are about to create a new console. However when ConSrvNewProcess + * was called, we didn't know that we wanted to create a new console and + * therefore, we by default inherited the handles table from our parent + * process. It's only now that we notice that in fact we do not need + * them, because we've created a new console and thus we must use it. + * + * Therefore, free the handles table so that we can recreate + * a new one later on. + */ + ConSrvFreeHandlesTable(ProcessData); + + /* Initialize the handles table */ + Status = ConSrvInitHandlesTable(ProcessData, + Console, + pInputHandle, + pOutputHandle, + pErrorHandle); + if (!NT_SUCCESS(Status)) + { + DPRINT1("Failed to initialize the handles table\n"); + ProcessData->ConsoleHandle = NULL; + goto Quit; + } + } + + /* Duplicate the Input Event */ + Status = NtDuplicateObject(NtCurrentProcess(), + Console->InputBuffer.ActiveEvent, + ProcessData->Process->ProcessHandle, + &ProcessData->ConsoleEvent, + EVENT_ALL_ACCESS, 0, 0); + if (!NT_SUCCESS(Status)) + { + DPRINT1("NtDuplicateObject() failed: %lu\n", Status); + ConSrvFreeHandlesTable(ProcessData); // NOTE: Always free the handles table. + ProcessData->ConsoleHandle = NULL; + goto Quit; + } + + /* Insert the process into the processes list of the console */ + InsertHeadList(&Console->ProcessList, &ProcessData->ConsoleLink); + + /* Add a reference count because the process is tied to the console */ + _InterlockedIncrement(&Console->ReferenceCount); + + /* Update the internal info of the terminal */ + ConioRefreshInternalInfo(Console); + + Status = STATUS_SUCCESS; + +Quit: + /* Unlock the console and return */ + LeaveCriticalSection(&Console->Lock); + return Status; +} + +VOID +FASTCALL +ConSrvRemoveConsole(PCONSOLE_PROCESS_DATA ProcessData) +{ + PCONSOLE Console; + + DPRINT("ConSrvRemoveConsole\n"); + + // RtlEnterCriticalSection(&ProcessData->HandleTableLock); + + /* Validate and lock the console */ + if (ConDrvValidateConsole(&Console, + ProcessData->ConsoleHandle, + CONSOLE_RUNNING, TRUE)) + { + DPRINT("ConSrvRemoveConsole - Locking OK\n"); + + /* Close all console handles and free the handles table */ + ConSrvFreeHandlesTable(ProcessData); + + /* Detach the process from the console */ + ProcessData->ConsoleHandle = NULL; + + /* Remove ourselves from the console's list of processes */ + RemoveEntryList(&ProcessData->ConsoleLink); + + /* Update the internal info of the terminal */ + ConioRefreshInternalInfo(Console); + + /* Release the console */ + DPRINT("ConSrvRemoveConsole - Decrement Console->ReferenceCount = %lu\n", Console->ReferenceCount); + ConDrvReleaseConsole(Console, TRUE); + //CloseHandle(ProcessData->ConsoleEvent); + //ProcessData->ConsoleEvent = NULL; + } + + // RtlLeaveCriticalSection(&ProcessData->HandleTableLock); +} + + +/* PUBLIC SERVER APIS *********************************************************/ + +CSR_API(SrvOpenConsole) +{ + /* + * This API opens a handle to either the input buffer or to + * a screen-buffer of the console of the current process. + */ + + NTSTATUS Status; + PCONSOLE_OPENCONSOLE OpenConsoleRequest = &((PCONSOLE_API_MESSAGE)ApiMessage)->Data.OpenConsoleRequest; + PCONSOLE_PROCESS_DATA ProcessData = ConsoleGetPerProcessData(CsrGetClientThread()->Process); + PCONSOLE Console; + + DWORD DesiredAccess = OpenConsoleRequest->Access; + DWORD ShareMode = OpenConsoleRequest->ShareMode; + PCONSOLE_IO_OBJECT Object; + + OpenConsoleRequest->ConsoleHandle = INVALID_HANDLE_VALUE; + + Status = ConSrvGetConsole(ProcessData, &Console, TRUE); + if (!NT_SUCCESS(Status)) + { + DPRINT1("Can't get console\n"); + return Status; + } + + RtlEnterCriticalSection(&ProcessData->HandleTableLock); + + /* + * Open a handle to either the active screen buffer or the input buffer. + */ + if (OpenConsoleRequest->HandleType == HANDLE_OUTPUT) + { + Object = &Console->ActiveBuffer->Header; + } + else // HANDLE_INPUT + { + Object = &Console->InputBuffer.Header; + } + + if (((DesiredAccess & GENERIC_READ) && Object->ExclusiveRead != 0) || + ((DesiredAccess & GENERIC_WRITE) && Object->ExclusiveWrite != 0) || + (!(ShareMode & FILE_SHARE_READ) && Object->AccessRead != 0) || + (!(ShareMode & FILE_SHARE_WRITE) && Object->AccessWrite != 0)) + { + DPRINT1("Sharing violation\n"); + Status = STATUS_SHARING_VIOLATION; + } + else + { + Status = ConSrvInsertObject(ProcessData, + &OpenConsoleRequest->ConsoleHandle, + Object, + DesiredAccess, + OpenConsoleRequest->Inheritable, + ShareMode); + } + + RtlLeaveCriticalSection(&ProcessData->HandleTableLock); + + ConSrvReleaseConsole(Console, TRUE); + return Status; +} + +CSR_API(SrvCloseHandle) +{ + NTSTATUS Status; + PCONSOLE_CLOSEHANDLE CloseHandleRequest = &((PCONSOLE_API_MESSAGE)ApiMessage)->Data.CloseHandleRequest; + PCONSOLE_PROCESS_DATA ProcessData = ConsoleGetPerProcessData(CsrGetClientThread()->Process); + PCONSOLE Console; + + Status = ConSrvGetConsole(ProcessData, &Console, TRUE); + if (!NT_SUCCESS(Status)) + { + DPRINT1("Can't get console\n"); + return Status; + } + + Status = ConSrvRemoveObject(ProcessData, CloseHandleRequest->ConsoleHandle); + + ConSrvReleaseConsole(Console, TRUE); + return Status; +} + +CSR_API(SrvVerifyConsoleIoHandle) +{ + NTSTATUS Status; + PCONSOLE_VERIFYHANDLE VerifyHandleRequest = &((PCONSOLE_API_MESSAGE)ApiMessage)->Data.VerifyHandleRequest; + PCONSOLE_PROCESS_DATA ProcessData = ConsoleGetPerProcessData(CsrGetClientThread()->Process); + PCONSOLE Console; + + HANDLE ConsoleHandle = VerifyHandleRequest->ConsoleHandle; + ULONG Index = HandleToULong(ConsoleHandle) >> 2; + + Status = ConSrvGetConsole(ProcessData, &Console, TRUE); + if (!NT_SUCCESS(Status)) + { + DPRINT1("Can't get console\n"); + return Status; + } + + RtlEnterCriticalSection(&ProcessData->HandleTableLock); + + if (!IsConsoleHandle(ConsoleHandle) || + Index >= ProcessData->HandleTableSize || + ProcessData->HandleTable[Index].Object == NULL) + { + DPRINT("SrvVerifyConsoleIoHandle failed\n"); + Status = STATUS_INVALID_HANDLE; + } + + RtlLeaveCriticalSection(&ProcessData->HandleTableLock); + + ConSrvReleaseConsole(Console, TRUE); + return Status; +} + +CSR_API(SrvDuplicateHandle) +{ + NTSTATUS Status; + PCONSOLE_DUPLICATEHANDLE DuplicateHandleRequest = &((PCONSOLE_API_MESSAGE)ApiMessage)->Data.DuplicateHandleRequest; + PCONSOLE_PROCESS_DATA ProcessData = ConsoleGetPerProcessData(CsrGetClientThread()->Process); + PCONSOLE Console; + + HANDLE ConsoleHandle = DuplicateHandleRequest->ConsoleHandle; + ULONG Index = HandleToULong(ConsoleHandle) >> 2; + PCONSOLE_IO_HANDLE Entry; + DWORD DesiredAccess; + + Status = ConSrvGetConsole(ProcessData, &Console, TRUE); + if (!NT_SUCCESS(Status)) + { + DPRINT1("Can't get console\n"); + return Status; + } + + RtlEnterCriticalSection(&ProcessData->HandleTableLock); + + if ( /** !IsConsoleHandle(ConsoleHandle) || **/ + Index >= ProcessData->HandleTableSize || + (Entry = &ProcessData->HandleTable[Index])->Object == NULL) + { + DPRINT1("Couldn't duplicate invalid handle %p\n", ConsoleHandle); + Status = STATUS_INVALID_HANDLE; + goto Quit; + } + + if (DuplicateHandleRequest->Options & DUPLICATE_SAME_ACCESS) + { + DesiredAccess = Entry->Access; + } + else + { + DesiredAccess = DuplicateHandleRequest->Access; + /* Make sure the source handle has all the desired flags */ + if ((Entry->Access & DesiredAccess) == 0) + { + DPRINT1("Handle %p only has access %X; requested %X\n", + ConsoleHandle, Entry->Access, DesiredAccess); + Status = STATUS_INVALID_PARAMETER; + goto Quit; + } + } + + /* Insert the new handle inside the process handles table */ + Status = ConSrvInsertObject(ProcessData, + &DuplicateHandleRequest->ConsoleHandle, // Use the new handle value! + Entry->Object, + DesiredAccess, + DuplicateHandleRequest->Inheritable, + Entry->ShareMode); + if (NT_SUCCESS(Status) && + (DuplicateHandleRequest->Options & DUPLICATE_CLOSE_SOURCE)) + { + /* Close the original handle if needed */ + ConSrvCloseHandleEntry(Entry); + } + +Quit: + RtlLeaveCriticalSection(&ProcessData->HandleTableLock); + + ConSrvReleaseConsole(Console, TRUE); + return Status; +} + +/* EOF */ diff --git a/reactos/win32ss/user/winsrv/consrv_new/handle.h b/reactos/win32ss/user/winsrv/consrv_new/handle.h new file mode 100644 index 00000000000..dcd7b590e1c --- /dev/null +++ b/reactos/win32ss/user/winsrv/consrv_new/handle.h @@ -0,0 +1,34 @@ +/* + * LICENSE: GPL - See COPYING in the top level directory + * PROJECT: ReactOS Console Server DLL + * FILE: win32ss/user/winsrv/consrv/handle.h + * PURPOSE: Console I/O Handles functions + * PROGRAMMERS: David Welch + * Jeffrey Morlan + * Hermes Belusca-Maito (hermes.belusca@sfr.fr) + */ + +#pragma once + +VOID FASTCALL ConSrvInitObject(IN OUT PCONSOLE_IO_OBJECT Object, + IN CONSOLE_IO_OBJECT_TYPE Type, + IN PCONSOLE Console); +NTSTATUS FASTCALL ConSrvInsertObject(PCONSOLE_PROCESS_DATA ProcessData, + PHANDLE Handle, + PCONSOLE_IO_OBJECT Object, + DWORD Access, + BOOL Inheritable, + DWORD ShareMode); +NTSTATUS FASTCALL ConSrvRemoveObject(PCONSOLE_PROCESS_DATA ProcessData, + HANDLE Handle); +NTSTATUS FASTCALL ConSrvGetObject(PCONSOLE_PROCESS_DATA ProcessData, + HANDLE Handle, + PCONSOLE_IO_OBJECT* Object, + PVOID* Entry OPTIONAL, + DWORD Access, + BOOL LockConsole, + CONSOLE_IO_OBJECT_TYPE Type); +VOID FASTCALL ConSrvReleaseObject(PCONSOLE_IO_OBJECT Object, + BOOL IsConsoleLocked); + +/* EOF */ diff --git a/reactos/win32ss/user/winsrv/consrv_new/heap.h b/reactos/win32ss/user/winsrv/consrv_new/heap.h new file mode 100644 index 00000000000..4d21c657f76 --- /dev/null +++ b/reactos/win32ss/user/winsrv/consrv_new/heap.h @@ -0,0 +1,17 @@ +/* + * COPYRIGHT: See COPYING in the top level directory + * PROJECT: ReactOS Console Server DLL + * FILE: win32ss/user/winsrv/consrv/heap.h + * PURPOSE: Heap Helpers + * PROGRAMMERS: Hermes Belusca-Maito (hermes.belusca@sfr.fr) + */ + +#pragma once + +/* See init.c */ +extern HANDLE ConSrvHeap; + +#define ConsoleAllocHeap(Flags, Size) RtlAllocateHeap(ConSrvHeap, Flags, Size) +#define ConsoleFreeHeap(HeapBase) RtlFreeHeap(ConSrvHeap, 0, HeapBase) + +/* EOF */ diff --git a/reactos/win32ss/user/winsrv/consrv_new/include/conio.h b/reactos/win32ss/user/winsrv/consrv_new/include/conio.h new file mode 100644 index 00000000000..3ef623fb592 --- /dev/null +++ b/reactos/win32ss/user/winsrv/consrv_new/include/conio.h @@ -0,0 +1,377 @@ +/* + * COPYRIGHT: See COPYING in the top level directory + * PROJECT: ReactOS Console Server DLL + * FILE: win32ss/user/winsrv/consrv/include/conio.h + * PURPOSE: Public Console I/O Interface + * PROGRAMMERS: Gé van Geldorp + * Jeffrey Morlan + * Hermes Belusca-Maito (hermes.belusca@sfr.fr) + */ + +#pragma once + +#define CSR_DEFAULT_CURSOR_SIZE 25 + +/* Default attributes */ +#define DEFAULT_SCREEN_ATTRIB (FOREGROUND_BLUE | FOREGROUND_GREEN | FOREGROUND_RED) +#define DEFAULT_POPUP_ATTRIB (FOREGROUND_BLUE | FOREGROUND_RED | \ + BACKGROUND_BLUE | BACKGROUND_GREEN | BACKGROUND_RED | BACKGROUND_INTENSITY) + +/* Object type magic numbers */ +typedef enum _CONSOLE_IO_OBJECT_TYPE +{ +// ANY_TYPE_BUFFER = 0x00, // --> Match any types of IO handles + TEXTMODE_BUFFER = 0x01, // --> Output-type handles for text SBs + GRAPHICS_BUFFER = 0x02, // --> Output-type handles for graphics SBs + SCREEN_BUFFER = 0x03, // --> Any SB type + INPUT_BUFFER = 0x04 // --> Input-type handles +} CONSOLE_IO_OBJECT_TYPE; + +typedef struct _CONSOLE_IO_OBJECT +{ + CONSOLE_IO_OBJECT_TYPE Type; + struct _CONSOLE* /* PCONSOLE */ Console; + LONG AccessRead, AccessWrite; + LONG ExclusiveRead, ExclusiveWrite; + LONG HandleCount; +} CONSOLE_IO_OBJECT, *PCONSOLE_IO_OBJECT; + + +/******************************************************************************\ +|* *| +|* Abstract "class" for screen-buffers, be they text-mode or graphics *| +|* *| +\******************************************************************************/ + +/* + * See conoutput.c for the implementation + */ + +typedef struct _CONSOLE_SCREEN_BUFFER CONSOLE_SCREEN_BUFFER, + *PCONSOLE_SCREEN_BUFFER; + +typedef struct _CONSOLE_SCREEN_BUFFER_VTBL +{ + CONSOLE_IO_OBJECT_TYPE (*GetType)(PCONSOLE_SCREEN_BUFFER This); +} CONSOLE_SCREEN_BUFFER_VTBL, *PCONSOLE_SCREEN_BUFFER_VTBL; + +#define GetType(This) (This)->Vtbl->GetType(This) + +struct _CONSOLE_SCREEN_BUFFER +{ + CONSOLE_IO_OBJECT Header; /* Object header - MUST BE IN FIRST PLACE */ + PCONSOLE_SCREEN_BUFFER_VTBL Vtbl; /* Virtual table */ + + LIST_ENTRY ListEntry; /* Entry in console's list of buffers */ + + COORD ScreenBufferSize; /* Size of this screen buffer. (Rows, Columns) for text-mode and (Width, Height) for graphics */ + COORD ViewSize; /* Associated "view" (i.e. console) size */ + + COORD OldScreenBufferSize; /* Old size of this screen buffer */ + COORD OldViewSize; /* Old associated view size */ + + COORD ViewOrigin; /* Beginning offset for the actual display area */ + +/***** Put that VV in TEXTMODE_SCREEN_BUFFER ?? *****/ + USHORT VirtualY; /* Top row of buffer being displayed, reported to callers */ + + COORD CursorPosition; /* Current cursor position */ + BOOLEAN CursorBlinkOn; + BOOLEAN ForceCursorOff; +// ULONG CursorSize; + CONSOLE_CURSOR_INFO CursorInfo; // FIXME: Keep this member or not ?? +/*********************************************/ + +// WORD ScreenDefaultAttrib; /* Default screen char attribute */ +// WORD PopupDefaultAttrib; /* Default popup char attribute */ + USHORT Mode; /* Output buffer modes */ +}; + + + +/******************************************************************************\ +|* *| +|* Text-mode and graphics-mode screen-buffer "classes" *| +|* *| +\******************************************************************************/ + +/* + * See text.c for the implementation + */ + +/************************************************************************ + * Screen buffer structure represents the win32 screen buffer object. * + * Internally, the portion of the buffer being shown CAN loop past the * + * bottom of the virtual buffer and wrap around to the top. Win32 does * + * not do this. I decided to do this because it eliminates the need to * + * do a massive memcpy() to scroll the contents of the buffer up to * + * scroll the screen on output, instead I just shift down the position * + * to be displayed, and let it wrap around to the top again. * + * The VirtualY member keeps track of the top Y coord that win32 * + * clients THINK is currently being displayed, because they think that * + * when the display reaches the bottom of the buffer and another line * + * being printed causes another line to scroll down, that the buffer IS * + * memcpy()'s up, and the bottom of the buffer is still displayed, but * + * internally, I just wrap back to the top of the buffer. * + ************************************************************************/ + +typedef struct _TEXTMODE_BUFFER_INFO +{ + COORD ScreenBufferSize; + USHORT ScreenAttrib; + USHORT PopupAttrib; + BOOLEAN IsCursorVisible; + ULONG CursorSize; +} TEXTMODE_BUFFER_INFO, *PTEXTMODE_BUFFER_INFO; + +typedef struct _TEXTMODE_SCREEN_BUFFER +{ + CONSOLE_SCREEN_BUFFER; /* Screen buffer base class - MUST BE IN FIRST PLACE */ + + PCHAR_INFO Buffer; /* Pointer to UNICODE screen buffer (Buffer->Char.UnicodeChar only is valid, not Char.AsciiChar) */ + + WORD ScreenDefaultAttrib; /* Default screen char attribute */ + WORD PopupDefaultAttrib; /* Default popup char attribute */ +} TEXTMODE_SCREEN_BUFFER, *PTEXTMODE_SCREEN_BUFFER; + + +/* + * See graphics.c for the implementation + */ + +typedef struct _GRAPHICS_BUFFER_INFO +{ + CONSOLE_GRAPHICS_BUFFER_INFO Info; +} GRAPHICS_BUFFER_INFO, *PGRAPHICS_BUFFER_INFO; + +typedef struct _GRAPHICS_SCREEN_BUFFER +{ + CONSOLE_SCREEN_BUFFER; /* Screen buffer base class - MUST BE IN FIRST PLACE */ + + ULONG BitMapInfoLength; /* Real size of the structure pointed by BitMapInfo */ + LPBITMAPINFO BitMapInfo; /* Information on the bitmap buffer */ + ULONG BitMapUsage; /* See the uUsage parameter of GetDIBits */ + HANDLE hSection; /* Handle to the memory shared section for the bitmap buffer */ + PVOID BitMap; /* Our bitmap buffer */ + PVOID ClientBitMap; /* A copy of the client view of our bitmap buffer */ + HANDLE Mutex; /* Our mutex, used to synchronize read / writes to the bitmap buffer */ + HANDLE ClientMutex; /* A copy of the client handle to our mutex */ + HANDLE ClientProcess; /* Handle to the client process who opened the buffer, to unmap the view */ +} GRAPHICS_SCREEN_BUFFER, *PGRAPHICS_SCREEN_BUFFER; + + + +typedef struct _CONSOLE_INPUT_BUFFER +{ + CONSOLE_IO_OBJECT Header; /* Object header - MUST BE IN FIRST PLACE */ + + ULONG InputBufferSize; /* Size of this input buffer */ + LIST_ENTRY InputEvents; /* List head for input event queue */ + HANDLE ActiveEvent; /* Event set when an input event is added in its queue */ + LIST_ENTRY ReadWaitQueue; /* List head for the queue of read wait blocks */ + + USHORT Mode; /* Input buffer modes */ +} CONSOLE_INPUT_BUFFER, *PCONSOLE_INPUT_BUFFER; + + +typedef struct _FRONTEND FRONTEND, *PFRONTEND; +/* HACK: */ typedef struct _CONSOLE_INFO *PCONSOLE_INFO; +typedef struct _FRONTEND_VTBL +{ + /* + * Internal interface (functions called by the console server only) + */ + NTSTATUS (WINAPI *InitFrontEnd)(IN OUT PFRONTEND This, + IN struct _CONSOLE* Console); + VOID (WINAPI *DeinitFrontEnd)(IN OUT PFRONTEND This); + + /* Interface used for both text-mode and graphics screen buffers */ + VOID (WINAPI *DrawRegion)(IN OUT PFRONTEND This, + SMALL_RECT* Region); + /* Interface used only for text-mode screen buffers */ + VOID (WINAPI *WriteStream)(IN OUT PFRONTEND This, + SMALL_RECT* Block, + SHORT CursorStartX, + SHORT CursorStartY, + UINT ScrolledLines, + PWCHAR Buffer, + UINT Length); + BOOL (WINAPI *SetCursorInfo)(IN OUT PFRONTEND This, + PCONSOLE_SCREEN_BUFFER ScreenBuffer); + BOOL (WINAPI *SetScreenInfo)(IN OUT PFRONTEND This, + PCONSOLE_SCREEN_BUFFER ScreenBuffer, + SHORT OldCursorX, + SHORT OldCursorY); + VOID (WINAPI *ResizeTerminal)(IN OUT PFRONTEND This); + BOOL (WINAPI *ProcessKeyCallback)(IN OUT PFRONTEND This, + MSG* msg, + BYTE KeyStateMenu, + DWORD ShiftState, + UINT VirtualKeyCode, + BOOL Down); + VOID (WINAPI *RefreshInternalInfo)(IN OUT PFRONTEND This); + + /* + * External interface (functions corresponding to the Console API) + */ + VOID (WINAPI *ChangeTitle)(IN OUT PFRONTEND This); + BOOL (WINAPI *ChangeIcon)(IN OUT PFRONTEND This, + HICON hWindowIcon); + HWND (WINAPI *GetConsoleWindowHandle)(IN OUT PFRONTEND This); + VOID (WINAPI *GetLargestConsoleWindowSize)(IN OUT PFRONTEND This, + PCOORD pSize); + ULONG (WINAPI *GetDisplayMode)(IN OUT PFRONTEND This); + BOOL (WINAPI *SetDisplayMode)(IN OUT PFRONTEND This, + ULONG NewMode); + INT (WINAPI *ShowMouseCursor)(IN OUT PFRONTEND This, + BOOL Show); + BOOL (WINAPI *SetMouseCursor)(IN OUT PFRONTEND This, + HCURSOR hCursor); + HMENU (WINAPI *MenuControl)(IN OUT PFRONTEND This, + UINT cmdIdLow, + UINT cmdIdHigh); + BOOL (WINAPI *SetMenuClose)(IN OUT PFRONTEND This, + BOOL Enable); + +#if 0 // Possible future front-end interface + BOOL (WINAPI *GetFrontEndProperty)(IN OUT PFRONTEND This, + ULONG Flag, + PVOID Info, + ULONG Size); + BOOL (WINAPI *SetFrontEndProperty)(IN OUT PFRONTEND This, + ULONG Flag, + PVOID Info /*, + ULONG Size */); +#endif +} FRONTEND_VTBL, *PFRONTEND_VTBL; + +struct _FRONTEND +{ + PFRONTEND_VTBL Vtbl; /* Virtual table */ + struct _CONSOLE* Console; /* Console to which the frontend is attached to */ + PVOID Data; /* Private data */ + PVOID OldData; /* Reserved */ +}; + +/* + * WARNING: Change the state of the console ONLY when the console is locked ! + */ +typedef enum _CONSOLE_STATE +{ + CONSOLE_INITIALIZING, /* Console is initializing */ + CONSOLE_RUNNING , /* Console running */ + CONSOLE_TERMINATING , /* Console about to be destroyed (but still not) */ + CONSOLE_IN_DESTRUCTION /* Console in destruction */ +} CONSOLE_STATE, *PCONSOLE_STATE; + +typedef struct _CONSOLE +{ + LONG ReferenceCount; /* Is incremented each time a handle to something in the console (a screen-buffer or the input buffer of this console) gets referenced */ + CRITICAL_SECTION Lock; + CONSOLE_STATE State; /* State of the console */ + + LIST_ENTRY ProcessList; /* List of processes owning the console. The first one is the so-called "Console Leader Process" */ + + FRONTEND TermIFace; /* Frontend-specific interface */ + +/**************************** Input buffer and data ***************************/ + CONSOLE_INPUT_BUFFER InputBuffer; /* Input buffer of the console */ + + /** Put those things in TEXTMODE_SCREEN_BUFFER ?? **/ + PWCHAR LineBuffer; /* Current line being input, in line buffered mode */ + WORD LineMaxSize; /* Maximum size of line in characters (including CR+LF) */ + WORD LineSize; /* Current size of line */ + 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 */ + /***************************************************/ + + BOOLEAN QuickEdit; + BOOLEAN InsertMode; + UINT CodePage; + + CONSOLE_SELECTION_INFO Selection; /* Contains information about the selection */ + COORD dwSelectionCursor; /* Selection cursor position, most of the time different from Selection.dwSelectionAnchor */ + +/******************************* Screen buffers *******************************/ + LIST_ENTRY BufferList; /* List of all screen buffers for this console */ + PCONSOLE_SCREEN_BUFFER ActiveBuffer; /* Pointer to currently active screen buffer */ + BYTE PauseFlags; + HANDLE UnpauseEvent; + LIST_ENTRY WriteWaitQueue; /* List head for the queue of write wait blocks */ + UINT OutputCodePage; + +/**************************** Aliases and Histories ***************************/ + struct _ALIAS_HEADER *Aliases; + LIST_ENTRY HistoryBuffers; + ULONG HistoryBufferSize; /* Size for newly created history buffers */ + ULONG NumberOfHistoryBuffers; /* Maximum number of history buffers allowed */ + BOOLEAN HistoryNoDup; /* Remove old duplicate history entries */ + +/****************************** Other properties ******************************/ + UNICODE_STRING OriginalTitle; /* Original title of console, the one defined when the console leader is launched; it never changes. Always NULL-terminated */ + UNICODE_STRING Title; /* Title of console. Always NULL-terminated */ + + COORD ConsoleSize; /* The current size of the console, for text-mode only */ + BOOLEAN FixedSize; /* TRUE if the console is of fixed size */ + + COLORREF Colors[16]; /* Colour palette */ + +} CONSOLE, *PCONSOLE; + +/* PauseFlags values (internal only) */ +#define PAUSED_FROM_KEYBOARD 0x1 +#define PAUSED_FROM_SCROLLBAR 0x2 +#define PAUSED_FROM_SELECTION 0x4 + +/* console.c */ +VOID FASTCALL ConioPause(PCONSOLE Console, UINT Flags); +VOID FASTCALL ConioUnpause(PCONSOLE Console, UINT Flags); + +NTSTATUS NTAPI +ConDrvConsoleProcessCtrlEvent(IN PCONSOLE Console, + IN ULONG ProcessGroupId, + IN ULONG Event); + +/* coninput.c */ +VOID WINAPI ConioProcessKey(PCONSOLE Console, MSG* msg); +NTSTATUS FASTCALL ConioProcessInputEvent(PCONSOLE Console, + PINPUT_RECORD InputEvent); + +/* conoutput.c */ +#define ConioInitRect(Rect, top, left, bottom, right) \ +do { \ + ((Rect)->Top) = top; \ + ((Rect)->Left) = left; \ + ((Rect)->Bottom) = bottom; \ + ((Rect)->Right) = right; \ +} while (0) +#define ConioIsRectEmpty(Rect) \ + (((Rect)->Left > (Rect)->Right) || ((Rect)->Top > (Rect)->Bottom)) +#define ConioRectHeight(Rect) \ + (((Rect)->Top) > ((Rect)->Bottom) ? 0 : ((Rect)->Bottom) - ((Rect)->Top) + 1) +#define ConioRectWidth(Rect) \ + (((Rect)->Left) > ((Rect)->Right) ? 0 : ((Rect)->Right) - ((Rect)->Left) + 1) + +#define ConsoleUnicodeCharToAnsiChar(Console, dChar, sWChar) \ + WideCharToMultiByte((Console)->OutputCodePage, 0, (sWChar), 1, (dChar), 1, NULL, NULL) + +#define ConsoleAnsiCharToUnicodeChar(Console, dWChar, sChar) \ + MultiByteToWideChar((Console)->OutputCodePage, 0, (sChar), 1, (dWChar), 1) + +PCHAR_INFO ConioCoordToPointer(PTEXTMODE_SCREEN_BUFFER Buff, ULONG X, ULONG Y); +VOID FASTCALL ConioDrawConsole(PCONSOLE Console); +NTSTATUS ConioResizeBuffer(PCONSOLE Console, + PTEXTMODE_SCREEN_BUFFER ScreenBuffer, + COORD Size); +NTSTATUS ConioWriteConsole(PCONSOLE Console, + PTEXTMODE_SCREEN_BUFFER Buff, + PWCHAR Buffer, + DWORD Length, + BOOL Attrib); +DWORD FASTCALL ConioEffectiveCursorSize(PCONSOLE Console, + DWORD Scale); + +/* EOF */ diff --git a/reactos/win32ss/user/winsrv/consrv_new/include/conio2.h b/reactos/win32ss/user/winsrv/consrv_new/include/conio2.h new file mode 100644 index 00000000000..ce8efd006f9 --- /dev/null +++ b/reactos/win32ss/user/winsrv/consrv_new/include/conio2.h @@ -0,0 +1,50 @@ +/* + * COPYRIGHT: See COPYING in the top level directory + * PROJECT: ReactOS Console Server DLL + * FILE: win32ss/user/winsrv/consrv/conio.h + * PURPOSE: Internal Console I/O Interface + * PROGRAMMERS: Hermes Belusca-Maito (hermes.belusca@sfr.fr) + */ + +#pragma once + +/* Macros used to call functions in the FRONTEND_VTBL virtual table */ + +#define ConioDrawRegion(Console, Region) \ + (Console)->TermIFace.Vtbl->DrawRegion(&(Console)->TermIFace, (Region)) +#define ConioWriteStream(Console, Block, CurStartX, CurStartY, ScrolledLines, Buffer, Length) \ + (Console)->TermIFace.Vtbl->WriteStream(&(Console)->TermIFace, (Block), (CurStartX), (CurStartY), \ + (ScrolledLines), (Buffer), (Length)) +#define ConioSetCursorInfo(Console, Buff) \ + (Console)->TermIFace.Vtbl->SetCursorInfo(&(Console)->TermIFace, (Buff)) +#define ConioSetScreenInfo(Console, Buff, OldCursorX, OldCursorY) \ + (Console)->TermIFace.Vtbl->SetScreenInfo(&(Console)->TermIFace, (Buff), (OldCursorX), (OldCursorY)) +#define ConioResizeTerminal(Console) \ + (Console)->TermIFace.Vtbl->ResizeTerminal(&(Console)->TermIFace) +#define ConioProcessKeyCallback(Console, Msg, KeyStateMenu, ShiftState, VirtualKeyCode, Down) \ + (Console)->TermIFace.Vtbl->ProcessKeyCallback(&(Console)->TermIFace, (Msg), (KeyStateMenu), (ShiftState), (VirtualKeyCode), (Down)) +#define ConioRefreshInternalInfo(Console) \ + (Console)->TermIFace.Vtbl->RefreshInternalInfo(&(Console)->TermIFace) + +#define ConioChangeTitle(Console) \ + (Console)->TermIFace.Vtbl->ChangeTitle(&(Console)->TermIFace) +#define ConioChangeIcon(Console, hWindowIcon) \ + (Console)->TermIFace.Vtbl->ChangeIcon(&(Console)->TermIFace, (hWindowIcon)) +#define ConioGetConsoleWindowHandle(Console) \ + (Console)->TermIFace.Vtbl->GetConsoleWindowHandle(&(Console)->TermIFace) +#define ConioGetLargestConsoleWindowSize(Console, pSize) \ + (Console)->TermIFace.Vtbl->GetLargestConsoleWindowSize(&(Console)->TermIFace, (pSize)) +#define ConioGetDisplayMode(Console) \ + (Console)->TermIFace.Vtbl->GetDisplayMode(&(Console)->TermIFace) +#define ConioSetDisplayMode(Console, NewMode) \ + (Console)->TermIFace.Vtbl->SetDisplayMode(&(Console)->TermIFace, (NewMode)) +#define ConioShowMouseCursor(Console, Show) \ + (Console)->TermIFace.Vtbl->ShowMouseCursor(&(Console)->TermIFace, (Show)) +#define ConioSetMouseCursor(Console, hCursor) \ + (Console)->TermIFace.Vtbl->SetMouseCursor(&(Console)->TermIFace, (hCursor)) +#define ConioMenuControl(Console, CmdIdLow, CmdIdHigh) \ + (Console)->TermIFace.Vtbl->MenuControl(&(Console)->TermIFace, (CmdIdLow), (CmdIdHigh)) +#define ConioSetMenuClose(Console, Enable) \ + (Console)->TermIFace.Vtbl->SetMenuClose(&(Console)->TermIFace, (Enable)) + +/* EOF */ diff --git a/reactos/win32ss/user/winsrv/consrv_new/include/console.h b/reactos/win32ss/user/winsrv/consrv_new/include/console.h new file mode 100644 index 00000000000..4214e071e18 --- /dev/null +++ b/reactos/win32ss/user/winsrv/consrv_new/include/console.h @@ -0,0 +1,54 @@ +/* + * COPYRIGHT: See COPYING in the top level directory + * PROJECT: ReactOS Console Server DLL + * FILE: win32ss/user/winsrv/consrv/include/console.h + * PURPOSE: Public Console Management Interface + * PROGRAMMERS: Hermes Belusca-Maito (hermes.belusca@sfr.fr) + */ + +#pragma once + +VOID NTAPI +ConDrvInitConsoleSupport(VOID); + +NTSTATUS NTAPI +ConDrvInitConsole(OUT PHANDLE NewConsoleHandle, + OUT PCONSOLE* NewConsole, + IN PCONSOLE_INFO ConsoleInfo, + IN ULONG ConsoleLeaderProcessId); +NTSTATUS NTAPI +ConDrvRegisterFrontEnd(IN PCONSOLE Console, + IN PFRONTEND FrontEnd); +NTSTATUS NTAPI +ConDrvDeregisterFrontEnd(IN PCONSOLE Console); +VOID NTAPI +ConDrvDeleteConsole(IN PCONSOLE Console); + + + +BOOLEAN NTAPI +ConDrvValidateConsoleState(IN PCONSOLE Console, + IN CONSOLE_STATE ExpectedState); + +BOOLEAN NTAPI +ConDrvValidateConsoleUnsafe(IN PCONSOLE Console, + IN CONSOLE_STATE ExpectedState, + IN BOOLEAN LockConsole); + +BOOLEAN NTAPI +ConDrvValidateConsole(OUT PCONSOLE* Console, + IN HANDLE ConsoleHandle, + IN CONSOLE_STATE ExpectedState, + IN BOOLEAN LockConsole); + + + +NTSTATUS NTAPI +ConDrvGetConsole(OUT PCONSOLE* Console, + IN HANDLE ConsoleHandle, + IN BOOLEAN LockConsole); +VOID NTAPI +ConDrvReleaseConsole(IN PCONSOLE Console, + IN BOOLEAN WasConsoleLocked); + +/* EOF */ diff --git a/reactos/win32ss/user/winsrv/consrv_new/include/settings.h b/reactos/win32ss/user/winsrv/consrv_new/include/settings.h new file mode 100644 index 00000000000..f538460685b --- /dev/null +++ b/reactos/win32ss/user/winsrv/consrv_new/include/settings.h @@ -0,0 +1,93 @@ +/* + * COPYRIGHT: See COPYING in the top level directory + * PROJECT: ReactOS Console Server DLL + * FILE: win32ss/user/winsrv/consrv/include/settings.h + * PURPOSE: Public Console Settings Management Interface + * PROGRAMMERS: Johannes Anderwald + * Hermes Belusca-Maito (hermes.belusca@sfr.fr) + */ + +#pragma once + +/* STRUCTURES *****************************************************************/ + +/* + * Structure used to hold terminal-specific information + */ +typedef struct _TERMINAL_INFO +{ + ULONG Size; /* Size of the memory buffer pointed by TermInfo */ + PVOID TermInfo; /* Address (or offset when talking to console.dll) of the memory buffer holding terminal information */ +} TERMINAL_INFO, *PTERMINAL_INFO; + +/* + * Structure used to hold console information + */ +typedef struct _CONSOLE_INFO +{ + ULONG HistoryBufferSize; + ULONG NumberOfHistoryBuffers; + BOOLEAN HistoryNoDup; + + BOOLEAN QuickEdit; + BOOLEAN InsertMode; + ULONG InputBufferSize; + COORD ScreenBufferSize; + COORD ConsoleSize; /* The size of the console */ + + BOOLEAN CursorBlinkOn; + BOOLEAN ForceCursorOff; + ULONG CursorSize; + + USHORT ScreenAttrib; // CHAR_INFO ScreenFillAttrib + USHORT PopupAttrib; + + COLORREF Colors[16]; /* Color palette */ + + ULONG CodePage; + + WCHAR ConsoleTitle[MAX_PATH + 1]; +} CONSOLE_INFO, *PCONSOLE_INFO; + +#define RGBFromAttrib(Console, Attribute) ((Console)->Colors[(Attribute) & 0xF]) +#define TextAttribFromAttrib(Attribute) ((Attribute) & 0xF) +#define BkgdAttribFromAttrib(Attribute) (((Attribute) >> 4) & 0xF) +#define MakeAttrib(TextAttrib, BkgdAttrib) (DWORD)((((BkgdAttrib) & 0xF) << 4) | ((TextAttrib) & 0xF)) + +/* + * Structure used to communicate with console.dll + */ +typedef struct _CONSOLE_PROPS +{ + HWND hConsoleWindow; + BOOL ShowDefaultParams; + + BOOLEAN AppliedConfig; + DWORD ActiveStaticControl; + + CONSOLE_INFO ci; /* Console-specific informations */ + TERMINAL_INFO TerminalInfo; /* Frontend-specific parameters */ +} CONSOLE_PROPS, *PCONSOLE_PROPS; + +/* FUNCTIONS ******************************************************************/ + +#ifndef CONSOLE_H__ // If we aren't included by console.dll + +BOOL ConSrvOpenUserSettings(DWORD ProcessId, + LPCWSTR ConsoleTitle, + PHKEY hSubKey, + REGSAM samDesired, + BOOL bCreate); + +BOOL ConSrvReadUserSettings(IN OUT PCONSOLE_INFO ConsoleInfo, + IN DWORD ProcessId); +BOOL ConSrvWriteUserSettings(IN PCONSOLE_INFO ConsoleInfo, + IN DWORD ProcessId); +VOID ConSrvGetDefaultSettings(IN OUT PCONSOLE_INFO ConsoleInfo, + IN DWORD ProcessId); +VOID ConSrvApplyUserSettings(IN PCONSOLE Console, + IN PCONSOLE_INFO ConsoleInfo); + +#endif + +/* EOF */ diff --git a/reactos/win32ss/user/winsrv/consrv_new/init.c b/reactos/win32ss/user/winsrv/consrv_new/init.c new file mode 100644 index 00000000000..1cc32b6acd9 --- /dev/null +++ b/reactos/win32ss/user/winsrv/consrv_new/init.c @@ -0,0 +1,531 @@ +/* + * COPYRIGHT: See COPYING in the top level directory + * PROJECT: ReactOS Console Server DLL + * FILE: win32ss/user/winsrv/consrv/init.c + * PURPOSE: Initialization + * PROGRAMMERS: Hermes Belusca-Maito (hermes.belusca@sfr.fr) + */ + +/* INCLUDES *******************************************************************/ + +#include "consrv.h" +#include "api.h" +#include "procinit.h" +#include "include/conio.h" +#include "include/console.h" +#include "console.h" + +#define NDEBUG +#include + +/* GLOBALS ********************************************************************/ + +HINSTANCE ConSrvDllInstance = NULL; + +/* Memory */ +HANDLE ConSrvHeap = NULL; // Our own heap. + +// Windows Server 2003 table from http://j00ru.vexillium.org/csrss_list/api_list.html#Windows_2k3 +// plus a little bit of Windows 7. +PCSR_API_ROUTINE ConsoleServerApiDispatchTable[ConsolepMaxApiNumber - CONSRV_FIRST_API_NUMBER] = +{ + SrvOpenConsole, + SrvGetConsoleInput, + SrvWriteConsoleInput, + SrvReadConsoleOutput, + SrvWriteConsoleOutput, + SrvReadConsoleOutputString, + SrvWriteConsoleOutputString, + SrvFillConsoleOutput, + SrvGetConsoleMode, + // SrvGetConsoleNumberOfFonts, + SrvGetConsoleNumberOfInputEvents, + SrvGetConsoleScreenBufferInfo, + SrvGetConsoleCursorInfo, + // SrvGetConsoleMouseInfo, + // SrvGetConsoleFontInfo, + // SrvGetConsoleFontSize, + // SrvGetConsoleCurrentFont, + SrvSetConsoleMode, + SrvSetConsoleActiveScreenBuffer, + SrvFlushConsoleInputBuffer, + SrvGetLargestConsoleWindowSize, + SrvSetConsoleScreenBufferSize, + SrvSetConsoleCursorPosition, + SrvSetConsoleCursorInfo, + SrvSetConsoleWindowInfo, + SrvScrollConsoleScreenBuffer, + SrvSetConsoleTextAttribute, + // SrvSetConsoleFont, + SrvSetConsoleIcon, + SrvReadConsole, + SrvWriteConsole, + SrvDuplicateHandle, + // SrvGetHandleInformation, + // SrvSetHandleInformation, + SrvCloseHandle, + SrvVerifyConsoleIoHandle, + SrvAllocConsole, + SrvFreeConsole, + SrvGetConsoleTitle, + SrvSetConsoleTitle, + SrvCreateConsoleScreenBuffer, + SrvInvalidateBitMapRect, + // SrvVDMConsoleOperation, + SrvSetConsoleCursor, + SrvShowConsoleCursor, + SrvConsoleMenuControl, + // SrvSetConsolePalette, + SrvSetConsoleDisplayMode, + // SrvRegisterConsoleVDM, + SrvGetConsoleHardwareState, + SrvSetConsoleHardwareState, + SrvGetConsoleDisplayMode, + SrvAddConsoleAlias, + SrvGetConsoleAlias, + SrvGetConsoleAliasesLength, + SrvGetConsoleAliasExesLength, + SrvGetConsoleAliases, + SrvGetConsoleAliasExes, + SrvExpungeConsoleCommandHistory, + SrvSetConsoleNumberOfCommands, + SrvGetConsoleCommandHistoryLength, + SrvGetConsoleCommandHistory, + // SrvSetConsoleCommandHistoryMode, + SrvGetConsoleCP, + SrvSetConsoleCP, + // SrvSetConsoleKeyShortcuts, + SrvSetConsoleMenuClose, + // SrvConsoleNotifyLastClose, + SrvGenerateConsoleCtrlEvent, + // SrvGetConsoleKeyboardLayoutName, + SrvGetConsoleWindow, + // SrvGetConsoleCharType, + // SrvSetConsoleLocalEUDC, + // SrvSetConsoleCursorMode, + // SrvGetConsoleCursorMode, + // SrvRegisterConsoleOS2, + // SrvSetConsoleOS2OemFormat, + // SrvGetConsoleNlsMode, + // SrvSetConsoleNlsMode, + // SrvRegisterConsoleIME, + // SrvUnregisterConsoleIME, + // SrvGetConsoleLangId, + SrvAttachConsole, + SrvGetConsoleSelectionInfo, + SrvGetConsoleProcessList, + SrvGetConsoleHistory, + SrvSetConsoleHistory, +}; + +BOOLEAN ConsoleServerApiServerValidTable[ConsolepMaxApiNumber - CONSRV_FIRST_API_NUMBER] = +{ + FALSE, // SrvOpenConsole, + FALSE, // SrvGetConsoleInput, + FALSE, // SrvWriteConsoleInput, + FALSE, // SrvReadConsoleOutput, + FALSE, // SrvWriteConsoleOutput, + FALSE, // SrvReadConsoleOutputString, + FALSE, // SrvWriteConsoleOutputString, + FALSE, // SrvFillConsoleOutput, + FALSE, // SrvGetConsoleMode, + // FALSE, // SrvGetConsoleNumberOfFonts, + FALSE, // SrvGetConsoleNumberOfInputEvents, + FALSE, // SrvGetConsoleScreenBufferInfo, + FALSE, // SrvGetConsoleCursorInfo, + // FALSE, // SrvGetConsoleMouseInfo, + // FALSE, // SrvGetConsoleFontInfo, + // FALSE, // SrvGetConsoleFontSize, + // FALSE, // SrvGetConsoleCurrentFont, + FALSE, // SrvSetConsoleMode, + FALSE, // SrvSetConsoleActiveScreenBuffer, + FALSE, // SrvFlushConsoleInputBuffer, + FALSE, // SrvGetLargestConsoleWindowSize, + FALSE, // SrvSetConsoleScreenBufferSize, + FALSE, // SrvSetConsoleCursorPosition, + FALSE, // SrvSetConsoleCursorInfo, + FALSE, // SrvSetConsoleWindowInfo, + FALSE, // SrvScrollConsoleScreenBuffer, + FALSE, // SrvSetConsoleTextAttribute, + // FALSE, // SrvSetConsoleFont, + FALSE, // SrvSetConsoleIcon, + FALSE, // SrvReadConsole, + FALSE, // SrvWriteConsole, + FALSE, // SrvDuplicateHandle, + // FALSE, // SrvGetHandleInformation, + // FALSE, // SrvSetHandleInformation, + FALSE, // SrvCloseHandle, + FALSE, // SrvVerifyConsoleIoHandle, + FALSE, // SrvAllocConsole, + FALSE, // SrvFreeConsole, + FALSE, // SrvGetConsoleTitle, + FALSE, // SrvSetConsoleTitle, + FALSE, // SrvCreateConsoleScreenBuffer, + FALSE, // SrvInvalidateBitMapRect, + // FALSE, // SrvVDMConsoleOperation, + FALSE, // SrvSetConsoleCursor, + FALSE, // SrvShowConsoleCursor, + FALSE, // SrvConsoleMenuControl, + // FALSE, // SrvSetConsolePalette, + FALSE, // SrvSetConsoleDisplayMode, + // FALSE, // SrvRegisterConsoleVDM, + FALSE, // SrvGetConsoleHardwareState, + FALSE, // SrvSetConsoleHardwareState, + TRUE, // SrvGetConsoleDisplayMode, + FALSE, // SrvAddConsoleAlias, + FALSE, // SrvGetConsoleAlias, + FALSE, // SrvGetConsoleAliasesLength, + FALSE, // SrvGetConsoleAliasExesLength, + FALSE, // SrvGetConsoleAliases, + FALSE, // SrvGetConsoleAliasExes, + FALSE, // SrvExpungeConsoleCommandHistory, + FALSE, // SrvSetConsoleNumberOfCommands, + FALSE, // SrvGetConsoleCommandHistoryLength, + FALSE, // SrvGetConsoleCommandHistory, + // FALSE, // SrvSetConsoleCommandHistoryMode, + FALSE, // SrvGetConsoleCP, + FALSE, // SrvSetConsoleCP, + // FALSE, // SrvSetConsoleKeyShortcuts, + FALSE, // SrvSetConsoleMenuClose, + // FALSE, // SrvConsoleNotifyLastClose, + FALSE, // SrvGenerateConsoleCtrlEvent, + // FALSE, // SrvGetConsoleKeyboardLayoutName, + FALSE, // SrvGetConsoleWindow, + // FALSE, // SrvGetConsoleCharType, + // FALSE, // SrvSetConsoleLocalEUDC, + // FALSE, // SrvSetConsoleCursorMode, + // FALSE, // SrvGetConsoleCursorMode, + // FALSE, // SrvRegisterConsoleOS2, + // FALSE, // SrvSetConsoleOS2OemFormat, + // FALSE, // SrvGetConsoleNlsMode, + // FALSE, // SrvSetConsoleNlsMode, + // FALSE, // SrvRegisterConsoleIME, + // FALSE, // SrvUnregisterConsoleIME, + // FALSE, // SrvGetConsoleLangId, + FALSE, // SrvAttachConsole, + FALSE, // SrvGetConsoleSelectionInfo, + FALSE, // SrvGetConsoleProcessList, + FALSE, // SrvGetConsoleHistory, + FALSE, // SrvSetConsoleHistory +}; + +PCHAR ConsoleServerApiNameTable[ConsolepMaxApiNumber - CONSRV_FIRST_API_NUMBER] = +{ + "OpenConsole", + "GetConsoleInput", + "WriteConsoleInput", + "ReadConsoleOutput", + "WriteConsoleOutput", + "ReadConsoleOutputString", + "WriteConsoleOutputString", + "FillConsoleOutput", + "GetConsoleMode", + // "GetConsoleNumberOfFonts", + "GetConsoleNumberOfInputEvents", + "GetConsoleScreenBufferInfo", + "GetConsoleCursorInfo", + // "GetConsoleMouseInfo", + // "GetConsoleFontInfo", + // "GetConsoleFontSize", + // "GetConsoleCurrentFont", + "SetConsoleMode", + "SetConsoleActiveScreenBuffer", + "FlushConsoleInputBuffer", + "GetLargestConsoleWindowSize", + "SetConsoleScreenBufferSize", + "SetConsoleCursorPosition", + "SetConsoleCursorInfo", + "SetConsoleWindowInfo", + "ScrollConsoleScreenBuffer", + "SetConsoleTextAttribute", + // "SetConsoleFont", + "SetConsoleIcon", + "ReadConsole", + "WriteConsole", + "DuplicateHandle", + // "GetHandleInformation", + // "SetHandleInformation", + "CloseHandle", + "VerifyConsoleIoHandle", + "AllocConsole", + "FreeConsole", + "GetConsoleTitle", + "SetConsoleTitle", + "CreateConsoleScreenBuffer", + "InvalidateBitMapRect", + // "VDMConsoleOperation", + "SetConsoleCursor", + "ShowConsoleCursor", + "ConsoleMenuControl", + // "SetConsolePalette", + "SetConsoleDisplayMode", + // "RegisterConsoleVDM", + "GetConsoleHardwareState", + "SetConsoleHardwareState", + "GetConsoleDisplayMode", + "AddConsoleAlias", + "GetConsoleAlias", + "GetConsoleAliasesLength", + "GetConsoleAliasExesLength", + "GetConsoleAliases", + "GetConsoleAliasExes", + "ExpungeConsoleCommandHistory", + "SetConsoleNumberOfCommands", + "GetConsoleCommandHistoryLength", + "GetConsoleCommandHistory", + // "SetConsoleCommandHistoryMode", + "GetConsoleCP", + "SetConsoleCP", + // "SetConsoleKeyShortcuts", + "SetConsoleMenuClose", + // "ConsoleNotifyLastClose", + "GenerateConsoleCtrlEvent", + // "GetConsoleKeyboardLayoutName", + "GetConsoleWindow", + // "GetConsoleCharType", + // "SetConsoleLocalEUDC", + // "SetConsoleCursorMode", + // "GetConsoleCursorMode", + // "RegisterConsoleOS2", + // "SetConsoleOS2OemFormat", + // "GetConsoleNlsMode", + // "SetConsoleNlsMode", + // "RegisterConsoleIME", + // "UnregisterConsoleIME", + // "GetConsoleLangId", + "AttachConsole", + "GetConsoleSelectionInfo", + "GetConsoleProcessList", + "GetConsoleHistory", + "SetConsoleHistory", +}; + + +/* FUNCTIONS ******************************************************************/ + +/* See handle.c */ +NTSTATUS +ConSrvInheritHandlesTable(IN PCONSOLE_PROCESS_DATA SourceProcessData, + IN PCONSOLE_PROCESS_DATA TargetProcessData); + +NTSTATUS +NTAPI +ConSrvNewProcess(PCSR_PROCESS SourceProcess, + PCSR_PROCESS TargetProcess) +{ + /************************************************************************** + * This function is called whenever a new process (GUI or CUI) is created. + * + * Copy the parent's handles table here if both the parent and the child + * processes are CUI. If we must actually create our proper console (and + * thus do not inherit from the console handles of the parent's), then we + * will clean this table in the next ConSrvConnect call. Why we are doing + * this? It's because here, we still don't know whether or not we must create + * a new console instead of inherit it from the parent, and, because in + * ConSrvConnect we don't have any reference to the parent process anymore. + **************************************************************************/ + + NTSTATUS Status = STATUS_SUCCESS; + PCONSOLE_PROCESS_DATA TargetProcessData; + + /* An empty target process is invalid */ + if (!TargetProcess) return STATUS_INVALID_PARAMETER; + + TargetProcessData = ConsoleGetPerProcessData(TargetProcess); + + /* Initialize the new (target) process */ + RtlZeroMemory(TargetProcessData, sizeof(*TargetProcessData)); + TargetProcessData->Process = TargetProcess; + TargetProcessData->ConsoleEvent = NULL; + TargetProcessData->ConsoleHandle = TargetProcessData->ParentConsoleHandle = NULL; + TargetProcessData->ConsoleApp = ((TargetProcess->Flags & CsrProcessIsConsoleApp) ? TRUE : FALSE); + + /* + * The handles table gets initialized either when inheriting from + * another console process, or when creating a new console. + */ + TargetProcessData->HandleTableSize = 0; + TargetProcessData->HandleTable = NULL; + + RtlInitializeCriticalSection(&TargetProcessData->HandleTableLock); + + /* Do nothing if the source process is NULL */ + if (!SourceProcess) return STATUS_SUCCESS; + + // SourceProcessData = ConsoleGetPerProcessData(SourceProcess); + + /* + * If the child process is a console application and the parent process is + * either a console application or just has a valid console (with a valid + * handles table: this can happen if it is a GUI application having called + * AllocConsole), then try to inherit handles from the parent process. + */ + if (TargetProcessData->ConsoleApp /* && SourceProcessData->ConsoleApp */) + { + PCONSOLE_PROCESS_DATA SourceProcessData = ConsoleGetPerProcessData(SourceProcess); + PCONSOLE SourceConsole; + + /* Validate and lock the parent's console */ + if (ConDrvValidateConsole(&SourceConsole, + SourceProcessData->ConsoleHandle, + CONSOLE_RUNNING, TRUE)) + { + /* Inherit the parent's handles table */ + Status = ConSrvInheritHandlesTable(SourceProcessData, TargetProcessData); + if (NT_SUCCESS(Status)) + { + /* Temporary save the parent's console too */ + TargetProcessData->ParentConsoleHandle = SourceProcessData->ConsoleHandle; + } + + /* Unlock the parent's console */ + LeaveCriticalSection(&SourceConsole->Lock); + } + } + + return Status; +} + +NTSTATUS +NTAPI +ConSrvConnect(IN PCSR_PROCESS CsrProcess, + IN OUT PVOID ConnectionInfo, + IN OUT PULONG ConnectionInfoLength) +{ + /************************************************************************** + * This function is called whenever a CUI new process is created. + **************************************************************************/ + + NTSTATUS Status = STATUS_SUCCESS; + PCONSOLE_CONNECTION_INFO ConnectInfo = (PCONSOLE_CONNECTION_INFO)ConnectionInfo; + PCONSOLE_PROCESS_DATA ProcessData = ConsoleGetPerProcessData(CsrProcess); + + if ( ConnectionInfo == NULL || + ConnectionInfoLength == NULL || + *ConnectionInfoLength != sizeof(CONSOLE_CONNECTION_INFO) ) + { + DPRINT1("CONSRV: Connection failed\n"); + return STATUS_UNSUCCESSFUL; + } + + /* If we don't need a console, then get out of here */ + if (!ConnectInfo->ConsoleNeeded || !ProcessData->ConsoleApp) // In fact, it is for GUI apps. + { + return STATUS_SUCCESS; + } + + /* If we don't have a console, then create a new one... */ + if (!ConnectInfo->ConsoleHandle || + ConnectInfo->ConsoleHandle != ProcessData->ParentConsoleHandle) + { + DPRINT("ConSrvConnect - Allocate a new console\n"); + + /* + * We are about to create a new console. However when ConSrvNewProcess + * was called, we didn't know that we wanted to create a new console and + * therefore, we by default inherited the handles table from our parent + * process. It's only now that we notice that in fact we do not need + * them, because we've created a new console and thus we must use it. + * + * ConSrvAllocateConsole will free our old handles table + * and recreate a new valid one. + */ + + /* Initialize a new Console owned by the Console Leader Process */ + Status = ConSrvAllocateConsole(ProcessData, + &ConnectInfo->InputHandle, + &ConnectInfo->OutputHandle, + &ConnectInfo->ErrorHandle, + &ConnectInfo->ConsoleStartInfo); + if (!NT_SUCCESS(Status)) + { + DPRINT1("Console allocation failed\n"); + return Status; + } + } + else /* We inherit it from the parent */ + { + DPRINT("ConSrvConnect - Reuse current (parent's) console\n"); + + /* Reuse our current console */ + Status = ConSrvInheritConsole(ProcessData, + ConnectInfo->ConsoleHandle, + FALSE, + NULL, // &ConnectInfo->InputHandle, + NULL, // &ConnectInfo->OutputHandle, + NULL); // &ConnectInfo->ErrorHandle); + if (!NT_SUCCESS(Status)) + { + DPRINT1("Console inheritance failed\n"); + return Status; + } + } + + /* Return the console handle and the input wait handle to the caller */ + ConnectInfo->ConsoleHandle = ProcessData->ConsoleHandle; + ConnectInfo->InputWaitHandle = ProcessData->ConsoleEvent; + + /* Set the Property-Dialog and Control-Dispatcher handlers */ + ProcessData->PropDispatcher = ConnectInfo->PropDispatcher; + ProcessData->CtrlDispatcher = ConnectInfo->CtrlDispatcher; + + return STATUS_SUCCESS; +} + +VOID +NTAPI +ConSrvDisconnect(PCSR_PROCESS Process) +{ + PCONSOLE_PROCESS_DATA ProcessData = ConsoleGetPerProcessData(Process); + + /************************************************************************** + * This function is called whenever a new process (GUI or CUI) is destroyed. + **************************************************************************/ + + if ( ProcessData->ConsoleHandle != NULL || + ProcessData->HandleTable != NULL ) + { + DPRINT("ConSrvDisconnect - calling ConSrvRemoveConsole\n"); + ConSrvRemoveConsole(ProcessData); + } + + RtlDeleteCriticalSection(&ProcessData->HandleTableLock); +} + +CSR_SERVER_DLL_INIT(ConServerDllInitialization) +{ + /* Initialize the memory */ + ConSrvHeap = RtlGetProcessHeap(); +/* + // We can use our own heap instead of the CSR heap to investigate heap corruptions :) + ConSrvHeap = RtlCreateHeap(HEAP_GROWABLE | + HEAP_PROTECTION_ENABLED | + HEAP_FREE_CHECKING_ENABLED | + HEAP_TAIL_CHECKING_ENABLED | + HEAP_VALIDATE_ALL_ENABLED, + NULL, 0, 0, NULL, NULL); + if (!ConSrvHeap) return STATUS_NO_MEMORY; +*/ + + ConDrvInitConsoleSupport(); + + /* Setup the DLL Object */ + LoadedServerDll->ApiBase = CONSRV_FIRST_API_NUMBER; + LoadedServerDll->HighestApiSupported = ConsolepMaxApiNumber; + LoadedServerDll->DispatchTable = ConsoleServerApiDispatchTable; + LoadedServerDll->ValidTable = ConsoleServerApiServerValidTable; + LoadedServerDll->NameTable = ConsoleServerApiNameTable; + LoadedServerDll->SizeOfProcessData = sizeof(CONSOLE_PROCESS_DATA); + LoadedServerDll->ConnectCallback = ConSrvConnect; + LoadedServerDll->DisconnectCallback = ConSrvDisconnect; + LoadedServerDll->NewProcessCallback = ConSrvNewProcess; + // LoadedServerDll->HardErrorCallback = ConSrvHardError; + LoadedServerDll->ShutdownProcessCallback = NULL; + + ConSrvDllInstance = LoadedServerDll->ServerHandle; + + /* All done */ + return STATUS_SUCCESS; +} + +/* EOF */ diff --git a/reactos/win32ss/user/winsrv/consrv_new/lang/bg-BG.rc b/reactos/win32ss/user/winsrv/consrv_new/lang/bg-BG.rc new file mode 100644 index 00000000000..368c4d6b61f --- /dev/null +++ b/reactos/win32ss/user/winsrv/consrv_new/lang/bg-BG.rc @@ -0,0 +1,6 @@ +LANGUAGE LANG_BULGARIAN, SUBLANG_DEFAULT + +STRINGTABLE +BEGIN + IDS_CONSOLE_TITLE "ReactOS Console" +END diff --git a/reactos/win32ss/user/winsrv/consrv_new/lang/cs-CZ.rc b/reactos/win32ss/user/winsrv/consrv_new/lang/cs-CZ.rc new file mode 100644 index 00000000000..fe5150c885b --- /dev/null +++ b/reactos/win32ss/user/winsrv/consrv_new/lang/cs-CZ.rc @@ -0,0 +1,6 @@ +LANGUAGE LANG_CZECH, SUBLANG_DEFAULT + +STRINGTABLE +BEGIN + IDS_CONSOLE_TITLE "ReactOS Console" +END diff --git a/reactos/win32ss/user/winsrv/consrv_new/lang/de-DE.rc b/reactos/win32ss/user/winsrv/consrv_new/lang/de-DE.rc new file mode 100644 index 00000000000..bec1289417b --- /dev/null +++ b/reactos/win32ss/user/winsrv/consrv_new/lang/de-DE.rc @@ -0,0 +1,6 @@ +LANGUAGE LANG_GERMAN, SUBLANG_NEUTRAL + +STRINGTABLE +BEGIN + IDS_CONSOLE_TITLE "ReactOS Eingabeaufforderung" +END diff --git a/reactos/win32ss/user/winsrv/consrv_new/lang/el-GR.rc b/reactos/win32ss/user/winsrv/consrv_new/lang/el-GR.rc new file mode 100644 index 00000000000..18cb347092b --- /dev/null +++ b/reactos/win32ss/user/winsrv/consrv_new/lang/el-GR.rc @@ -0,0 +1,6 @@ +LANGUAGE LANG_GREEK, SUBLANG_DEFAULT + +STRINGTABLE +BEGIN + IDS_CONSOLE_TITLE "ReactOS Console" +END diff --git a/reactos/win32ss/user/winsrv/consrv_new/lang/en-US.rc b/reactos/win32ss/user/winsrv/consrv_new/lang/en-US.rc new file mode 100644 index 00000000000..5ade0134158 --- /dev/null +++ b/reactos/win32ss/user/winsrv/consrv_new/lang/en-US.rc @@ -0,0 +1,6 @@ +LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_US + +STRINGTABLE +BEGIN + IDS_CONSOLE_TITLE "ReactOS Console" +END diff --git a/reactos/win32ss/user/winsrv/consrv_new/lang/es-ES.rc b/reactos/win32ss/user/winsrv/consrv_new/lang/es-ES.rc new file mode 100644 index 00000000000..d963c96989c --- /dev/null +++ b/reactos/win32ss/user/winsrv/consrv_new/lang/es-ES.rc @@ -0,0 +1,6 @@ +LANGUAGE LANG_SPANISH, SUBLANG_NEUTRAL + +STRINGTABLE +BEGIN + IDS_CONSOLE_TITLE "ReactOS Console" +END diff --git a/reactos/win32ss/user/winsrv/consrv_new/lang/fr-FR.rc b/reactos/win32ss/user/winsrv/consrv_new/lang/fr-FR.rc new file mode 100644 index 00000000000..02f9934dd88 --- /dev/null +++ b/reactos/win32ss/user/winsrv/consrv_new/lang/fr-FR.rc @@ -0,0 +1,6 @@ +LANGUAGE LANG_FRENCH, SUBLANG_NEUTRAL + +STRINGTABLE +BEGIN + IDS_CONSOLE_TITLE "ReactOS Console" +END diff --git a/reactos/win32ss/user/winsrv/consrv_new/lang/he-IL.rc b/reactos/win32ss/user/winsrv/consrv_new/lang/he-IL.rc new file mode 100644 index 00000000000..309fab76f58 --- /dev/null +++ b/reactos/win32ss/user/winsrv/consrv_new/lang/he-IL.rc @@ -0,0 +1,6 @@ +LANGUAGE LANG_HEBREW, SUBLANG_DEFAULT + +STRINGTABLE +BEGIN + IDS_CONSOLE_TITLE "ReactOS Console" +END diff --git a/reactos/win32ss/user/winsrv/consrv_new/lang/id-ID.rc b/reactos/win32ss/user/winsrv/consrv_new/lang/id-ID.rc new file mode 100644 index 00000000000..dea778c7e72 --- /dev/null +++ b/reactos/win32ss/user/winsrv/consrv_new/lang/id-ID.rc @@ -0,0 +1,6 @@ +LANGUAGE LANG_INDONESIAN, SUBLANG_DEFAULT + +STRINGTABLE +BEGIN + IDS_CONSOLE_TITLE "ReactOS Console" +END diff --git a/reactos/win32ss/user/winsrv/consrv_new/lang/it-IT.rc b/reactos/win32ss/user/winsrv/consrv_new/lang/it-IT.rc new file mode 100644 index 00000000000..8474619dde4 --- /dev/null +++ b/reactos/win32ss/user/winsrv/consrv_new/lang/it-IT.rc @@ -0,0 +1,6 @@ +LANGUAGE LANG_ITALIAN, SUBLANG_NEUTRAL + +STRINGTABLE +BEGIN + IDS_CONSOLE_TITLE "ReactOS Console" +END diff --git a/reactos/win32ss/user/winsrv/consrv_new/lang/ja-JP.rc b/reactos/win32ss/user/winsrv/consrv_new/lang/ja-JP.rc new file mode 100644 index 00000000000..64d3bc3acdf --- /dev/null +++ b/reactos/win32ss/user/winsrv/consrv_new/lang/ja-JP.rc @@ -0,0 +1,6 @@ +LANGUAGE LANG_JAPANESE, SUBLANG_DEFAULT + +STRINGTABLE +BEGIN + IDS_CONSOLE_TITLE "ReactOS Console" +END diff --git a/reactos/win32ss/user/winsrv/consrv_new/lang/no-NO.rc b/reactos/win32ss/user/winsrv/consrv_new/lang/no-NO.rc new file mode 100644 index 00000000000..35056bb495a --- /dev/null +++ b/reactos/win32ss/user/winsrv/consrv_new/lang/no-NO.rc @@ -0,0 +1,6 @@ +LANGUAGE LANG_NORWEGIAN, SUBLANG_NEUTRAL + +STRINGTABLE +BEGIN + IDS_CONSOLE_TITLE "ReactOS Console" +END diff --git a/reactos/win32ss/user/winsrv/consrv_new/lang/pl-PL.rc b/reactos/win32ss/user/winsrv/consrv_new/lang/pl-PL.rc new file mode 100644 index 00000000000..c0b25b738bc --- /dev/null +++ b/reactos/win32ss/user/winsrv/consrv_new/lang/pl-PL.rc @@ -0,0 +1,6 @@ +LANGUAGE LANG_POLISH, SUBLANG_DEFAULT + +STRINGTABLE +BEGIN + IDS_CONSOLE_TITLE "Konsola ReactOS" +END diff --git a/reactos/win32ss/user/winsrv/consrv_new/lang/pt-BR.rc b/reactos/win32ss/user/winsrv/consrv_new/lang/pt-BR.rc new file mode 100644 index 00000000000..e5711d131ab --- /dev/null +++ b/reactos/win32ss/user/winsrv/consrv_new/lang/pt-BR.rc @@ -0,0 +1,6 @@ +LANGUAGE LANG_PORTUGUESE, SUBLANG_NEUTRAL + +STRINGTABLE +BEGIN + IDS_CONSOLE_TITLE "ReactOS Console" +END diff --git a/reactos/win32ss/user/winsrv/consrv_new/lang/ro-RO.rc b/reactos/win32ss/user/winsrv/consrv_new/lang/ro-RO.rc new file mode 100644 index 00000000000..f3b5ff08dca --- /dev/null +++ b/reactos/win32ss/user/winsrv/consrv_new/lang/ro-RO.rc @@ -0,0 +1,6 @@ +LANGUAGE LANG_ROMANIAN, SUBLANG_NEUTRAL + +STRINGTABLE +BEGIN + IDS_CONSOLE_TITLE "ReactOS Console" +END diff --git a/reactos/win32ss/user/winsrv/consrv_new/lang/ru-RU.rc b/reactos/win32ss/user/winsrv/consrv_new/lang/ru-RU.rc new file mode 100644 index 00000000000..b104f3abab5 --- /dev/null +++ b/reactos/win32ss/user/winsrv/consrv_new/lang/ru-RU.rc @@ -0,0 +1,6 @@ +LANGUAGE LANG_RUSSIAN, SUBLANG_DEFAULT + +STRINGTABLE +BEGIN + IDS_CONSOLE_TITLE "ReactOS Console" +END diff --git a/reactos/win32ss/user/winsrv/consrv_new/lang/sk-SK.rc b/reactos/win32ss/user/winsrv/consrv_new/lang/sk-SK.rc new file mode 100644 index 00000000000..69aa5f754ff --- /dev/null +++ b/reactos/win32ss/user/winsrv/consrv_new/lang/sk-SK.rc @@ -0,0 +1,6 @@ +LANGUAGE LANG_SLOVAK, SUBLANG_DEFAULT + +STRINGTABLE +BEGIN + IDS_CONSOLE_TITLE "ReactOS Console" +END diff --git a/reactos/win32ss/user/winsrv/consrv_new/lang/sv-SE.rc b/reactos/win32ss/user/winsrv/consrv_new/lang/sv-SE.rc new file mode 100644 index 00000000000..16a37e6be75 --- /dev/null +++ b/reactos/win32ss/user/winsrv/consrv_new/lang/sv-SE.rc @@ -0,0 +1,6 @@ +LANGUAGE LANG_SWEDISH, SUBLANG_NEUTRAL + +STRINGTABLE +BEGIN + IDS_CONSOLE_TITLE "ReactOS Console" +END diff --git a/reactos/win32ss/user/winsrv/consrv_new/lang/tr-TR.rc b/reactos/win32ss/user/winsrv/consrv_new/lang/tr-TR.rc new file mode 100644 index 00000000000..6b409b7a1da --- /dev/null +++ b/reactos/win32ss/user/winsrv/consrv_new/lang/tr-TR.rc @@ -0,0 +1,6 @@ +LANGUAGE LANG_TURKISH, SUBLANG_DEFAULT + +STRINGTABLE +BEGIN + IDS_CONSOLE_TITLE "ReactOS Console" +END diff --git a/reactos/win32ss/user/winsrv/consrv_new/lang/uk-UA.rc b/reactos/win32ss/user/winsrv/consrv_new/lang/uk-UA.rc new file mode 100644 index 00000000000..5b6441c78af --- /dev/null +++ b/reactos/win32ss/user/winsrv/consrv_new/lang/uk-UA.rc @@ -0,0 +1,6 @@ +LANGUAGE LANG_UKRAINIAN, SUBLANG_DEFAULT + +STRINGTABLE +BEGIN + IDS_CONSOLE_TITLE "ReactOS Console" +END diff --git a/reactos/win32ss/user/winsrv/consrv_new/lang/zh-CN.rc b/reactos/win32ss/user/winsrv/consrv_new/lang/zh-CN.rc new file mode 100644 index 00000000000..62b312b436e --- /dev/null +++ b/reactos/win32ss/user/winsrv/consrv_new/lang/zh-CN.rc @@ -0,0 +1,6 @@ +LANGUAGE LANG_CHINESE, SUBLANG_CHINESE_SIMPLIFIED + +STRINGTABLE +BEGIN + IDS_CONSOLE_TITLE "ReactOS Console" +END diff --git a/reactos/win32ss/user/winsrv/consrv_new/lang/zh-TW.rc b/reactos/win32ss/user/winsrv/consrv_new/lang/zh-TW.rc new file mode 100644 index 00000000000..1356e0194f9 --- /dev/null +++ b/reactos/win32ss/user/winsrv/consrv_new/lang/zh-TW.rc @@ -0,0 +1,6 @@ +LANGUAGE LANG_CHINESE, SUBLANG_CHINESE_TRADITIONAL + +STRINGTABLE +BEGIN + IDS_CONSOLE_TITLE "ReactOS Console" +END diff --git a/reactos/win32ss/user/winsrv/consrv_new/lineinput.c b/reactos/win32ss/user/winsrv/consrv_new/lineinput.c new file mode 100644 index 00000000000..2f54f0e64be --- /dev/null +++ b/reactos/win32ss/user/winsrv/consrv_new/lineinput.c @@ -0,0 +1,644 @@ +/* + * LICENSE: GPL - See COPYING in the top level directory + * PROJECT: ReactOS Console Server DLL + * FILE: win32ss/user/winsrv/consrv/lineinput.c + * PURPOSE: Console line input functions + * PROGRAMMERS: Jeffrey Morlan + */ + +/* INCLUDES *******************************************************************/ + +#include "consrv.h" +#include "console.h" +#include "include/conio.h" +#include "include/conio2.h" + +#define NDEBUG +#include + +typedef struct _HISTORY_BUFFER +{ + LIST_ENTRY ListEntry; + UINT Position; + UINT MaxEntries; + UINT NumEntries; + PUNICODE_STRING Entries; + UNICODE_STRING ExeName; +} HISTORY_BUFFER, *PHISTORY_BUFFER; + + +/* PRIVATE FUNCTIONS **********************************************************/ + +static PHISTORY_BUFFER +HistoryCurrentBuffer(PCONSOLE Console) +{ + /* TODO: use actual EXE name sent from process that called ReadConsole */ + UNICODE_STRING ExeName = { 14, 14, L"cmd.exe" }; + PLIST_ENTRY Entry = Console->HistoryBuffers.Flink; + PHISTORY_BUFFER Hist; + + for (; Entry != &Console->HistoryBuffers; Entry = Entry->Flink) + { + Hist = CONTAINING_RECORD(Entry, HISTORY_BUFFER, ListEntry); + if (RtlEqualUnicodeString(&ExeName, &Hist->ExeName, FALSE)) + return Hist; + } + + /* Couldn't find the buffer, create a new one */ + Hist = ConsoleAllocHeap(0, sizeof(HISTORY_BUFFER) + ExeName.Length); + if (!Hist) return NULL; + Hist->MaxEntries = Console->HistoryBufferSize; + Hist->NumEntries = 0; + Hist->Entries = ConsoleAllocHeap(0, Hist->MaxEntries * sizeof(UNICODE_STRING)); + if (!Hist->Entries) + { + ConsoleFreeHeap(Hist); + return NULL; + } + Hist->ExeName.Length = Hist->ExeName.MaximumLength = ExeName.Length; + Hist->ExeName.Buffer = (PWCHAR)(Hist + 1); + memcpy(Hist->ExeName.Buffer, ExeName.Buffer, ExeName.Length); + InsertHeadList(&Console->HistoryBuffers, &Hist->ListEntry); + return Hist; +} + +static VOID +HistoryAddEntry(PCONSOLE Console) +{ + UNICODE_STRING NewEntry; + PHISTORY_BUFFER Hist = HistoryCurrentBuffer(Console); + INT i; + + if (!Hist) return; + + NewEntry.Length = NewEntry.MaximumLength = Console->LineSize * sizeof(WCHAR); + NewEntry.Buffer = Console->LineBuffer; + + /* Don't add blank or duplicate entries */ + if (NewEntry.Length == 0 || Hist->MaxEntries == 0 || + (Hist->NumEntries > 0 && + RtlEqualUnicodeString(&Hist->Entries[Hist->NumEntries - 1], &NewEntry, FALSE))) + { + return; + } + + if (Console->HistoryNoDup) + { + /* Check if this line has been entered before */ + for (i = Hist->NumEntries - 1; i >= 0; i--) + { + if (RtlEqualUnicodeString(&Hist->Entries[i], &NewEntry, FALSE)) + { + /* Just rotate the list to bring this entry to the end */ + NewEntry = Hist->Entries[i]; + memmove(&Hist->Entries[i], &Hist->Entries[i + 1], + (Hist->NumEntries - (i + 1)) * sizeof(UNICODE_STRING)); + Hist->Entries[Hist->NumEntries - 1] = NewEntry; + Hist->Position = Hist->NumEntries - 1; + return; + } + } + } + + if (Hist->NumEntries == Hist->MaxEntries) + { + /* List is full, remove oldest entry */ + RtlFreeUnicodeString(&Hist->Entries[0]); + memmove(&Hist->Entries[0], &Hist->Entries[1], + --Hist->NumEntries * sizeof(UNICODE_STRING)); + } + + if (NT_SUCCESS(RtlDuplicateUnicodeString(0, &NewEntry, &Hist->Entries[Hist->NumEntries]))) + Hist->NumEntries++; + Hist->Position = Hist->NumEntries - 1; +} + +static VOID +HistoryGetCurrentEntry(PCONSOLE Console, PUNICODE_STRING Entry) +{ + PHISTORY_BUFFER Hist = HistoryCurrentBuffer(Console); + + if (!Hist || Hist->NumEntries == 0) + Entry->Length = 0; + else + *Entry = Hist->Entries[Hist->Position]; +} + +static PHISTORY_BUFFER +HistoryFindBuffer(PCONSOLE Console, PUNICODE_STRING ExeName) +{ + PLIST_ENTRY Entry = Console->HistoryBuffers.Flink; + while (Entry != &Console->HistoryBuffers) + { + /* For the history APIs, the caller is allowed to give only part of the name */ + PHISTORY_BUFFER Hist = CONTAINING_RECORD(Entry, HISTORY_BUFFER, ListEntry); + if (RtlPrefixUnicodeString(ExeName, &Hist->ExeName, TRUE)) + return Hist; + Entry = Entry->Flink; + } + return NULL; +} + +static VOID +HistoryDeleteBuffer(PHISTORY_BUFFER Hist) +{ + if (!Hist) return; + + while (Hist->NumEntries != 0) + RtlFreeUnicodeString(&Hist->Entries[--Hist->NumEntries]); + + ConsoleFreeHeap(Hist->Entries); + RemoveEntryList(&Hist->ListEntry); + ConsoleFreeHeap(Hist); +} + +VOID FASTCALL +HistoryDeleteBuffers(PCONSOLE Console) +{ + PLIST_ENTRY CurrentEntry; + PHISTORY_BUFFER HistoryBuffer; + + while (!IsListEmpty(&Console->HistoryBuffers)) + { + CurrentEntry = RemoveHeadList(&Console->HistoryBuffers); + HistoryBuffer = CONTAINING_RECORD(CurrentEntry, HISTORY_BUFFER, ListEntry); + HistoryDeleteBuffer(HistoryBuffer); + } +} + +static VOID +LineInputSetPos(PCONSOLE Console, UINT Pos) +{ + if (Pos != Console->LinePos && Console->InputBuffer.Mode & ENABLE_ECHO_INPUT) + { + PCONSOLE_SCREEN_BUFFER Buffer = Console->ActiveBuffer; + SHORT OldCursorX = Buffer->CursorPosition.X; + SHORT OldCursorY = Buffer->CursorPosition.Y; + INT XY = OldCursorY * Buffer->ScreenBufferSize.X + OldCursorX; + + XY += (Pos - Console->LinePos); + if (XY < 0) + XY = 0; + else if (XY >= Buffer->ScreenBufferSize.Y * Buffer->ScreenBufferSize.X) + XY = Buffer->ScreenBufferSize.Y * Buffer->ScreenBufferSize.X - 1; + + Buffer->CursorPosition.X = XY % Buffer->ScreenBufferSize.X; + Buffer->CursorPosition.Y = XY / Buffer->ScreenBufferSize.X; + ConioSetScreenInfo(Console, Buffer, OldCursorX, OldCursorY); + } + + Console->LinePos = Pos; +} + +static VOID +LineInputEdit(PCONSOLE Console, UINT NumToDelete, UINT NumToInsert, WCHAR *Insertion) +{ + PTEXTMODE_SCREEN_BUFFER ActiveBuffer; + UINT Pos = Console->LinePos; + UINT NewSize = Console->LineSize - NumToDelete + NumToInsert; + UINT i; + + if (GetType(Console->ActiveBuffer) != TEXTMODE_BUFFER) return; + ActiveBuffer = (PTEXTMODE_SCREEN_BUFFER)Console->ActiveBuffer; + + /* Make sure there's always enough room for ending \r\n */ + if (NewSize + 2 > Console->LineMaxSize) + return; + + memmove(&Console->LineBuffer[Pos + NumToInsert], + &Console->LineBuffer[Pos + NumToDelete], + (Console->LineSize - (Pos + NumToDelete)) * sizeof(WCHAR)); + memcpy(&Console->LineBuffer[Pos], Insertion, NumToInsert * sizeof(WCHAR)); + + if (Console->InputBuffer.Mode & ENABLE_ECHO_INPUT) + { + for (i = Pos; i < NewSize; i++) + { + ConioWriteConsole(Console, ActiveBuffer, &Console->LineBuffer[i], 1, TRUE); + } + for (; i < Console->LineSize; i++) + { + ConioWriteConsole(Console, ActiveBuffer, L" ", 1, TRUE); + } + Console->LinePos = i; + } + + Console->LineSize = NewSize; + LineInputSetPos(Console, Pos + NumToInsert); +} + +static VOID +LineInputRecallHistory(PCONSOLE Console, INT Offset) +{ + PHISTORY_BUFFER Hist = HistoryCurrentBuffer(Console); + UINT Position = 0; + + if (!Hist || Hist->NumEntries == 0) return; + + Position = Hist->Position + Offset; + Position = min(max(Position, 0), Hist->NumEntries - 1); + Hist->Position = Position; + + LineInputSetPos(Console, 0); + LineInputEdit(Console, Console->LineSize, + Hist->Entries[Hist->Position].Length / sizeof(WCHAR), + Hist->Entries[Hist->Position].Buffer); +} + +VOID FASTCALL +LineInputKeyDown(PCONSOLE Console, KEY_EVENT_RECORD *KeyEvent) +{ + UINT Pos = Console->LinePos; + PHISTORY_BUFFER Hist; + UNICODE_STRING Entry; + INT HistPos; + + switch (KeyEvent->wVirtualKeyCode) + { + case VK_ESCAPE: + /* Clear entire line */ + LineInputSetPos(Console, 0); + LineInputEdit(Console, Console->LineSize, 0, NULL); + return; + case VK_HOME: + /* Move to start of line. With ctrl, erase everything left of cursor */ + LineInputSetPos(Console, 0); + if (KeyEvent->dwControlKeyState & (LEFT_CTRL_PRESSED | RIGHT_CTRL_PRESSED)) + LineInputEdit(Console, Pos, 0, NULL); + return; + case VK_END: + /* Move to end of line. With ctrl, erase everything right of cursor */ + if (KeyEvent->dwControlKeyState & (LEFT_CTRL_PRESSED | RIGHT_CTRL_PRESSED)) + LineInputEdit(Console, Console->LineSize - Pos, 0, NULL); + else + LineInputSetPos(Console, Console->LineSize); + return; + case VK_LEFT: + /* Move left. With ctrl, move to beginning of previous word */ + if (KeyEvent->dwControlKeyState & (LEFT_CTRL_PRESSED | RIGHT_CTRL_PRESSED)) + { + while (Pos > 0 && Console->LineBuffer[Pos - 1] == L' ') Pos--; + while (Pos > 0 && Console->LineBuffer[Pos - 1] != L' ') Pos--; + } + else + { + Pos -= (Pos > 0); + } + LineInputSetPos(Console, Pos); + return; + case VK_RIGHT: + case VK_F1: + /* Move right. With ctrl, move to beginning of next word */ + if (KeyEvent->dwControlKeyState & (LEFT_CTRL_PRESSED | RIGHT_CTRL_PRESSED)) + { + while (Pos < Console->LineSize && Console->LineBuffer[Pos] != L' ') Pos++; + while (Pos < Console->LineSize && Console->LineBuffer[Pos] == L' ') Pos++; + LineInputSetPos(Console, Pos); + return; + } + else + { + /* Recall one character (but don't overwrite current line) */ + HistoryGetCurrentEntry(Console, &Entry); + if (Pos < Console->LineSize) + LineInputSetPos(Console, Pos + 1); + else if (Pos * sizeof(WCHAR) < Entry.Length) + 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) + LineInputEdit(Console, 1, 0, NULL); + return; + case VK_PRIOR: + /* Recall first history entry */ + LineInputRecallHistory(Console, -((WORD)-1)); + return; + case VK_NEXT: + /* Recall last history entry */ + LineInputRecallHistory(Console, +((WORD)-1)); + return; + case VK_UP: + case VK_F5: + /* Recall previous history entry. On first time, actually recall the + * current (usually last) entry; on subsequent times go back. */ + LineInputRecallHistory(Console, Console->LineUpPressed ? -1 : 0); + Console->LineUpPressed = TRUE; + return; + case VK_DOWN: + /* Recall next history entry */ + LineInputRecallHistory(Console, +1); + return; + case VK_F3: + /* Recall remainder of current history entry */ + HistoryGetCurrentEntry(Console, &Entry); + if (Pos * sizeof(WCHAR) < Entry.Length) + { + UINT InsertSize = (Entry.Length / sizeof(WCHAR) - Pos); + UINT DeleteSize = min(Console->LineSize - Pos, InsertSize); + LineInputEdit(Console, DeleteSize, InsertSize, &Entry.Buffer[Pos]); + } + return; + case VK_F6: + /* Insert a ^Z character */ + KeyEvent->uChar.UnicodeChar = 26; + break; + case VK_F7: + if (KeyEvent->dwControlKeyState & (LEFT_ALT_PRESSED | RIGHT_ALT_PRESSED)) + HistoryDeleteBuffer(HistoryCurrentBuffer(Console)); + return; + case VK_F8: + /* Search for history entries starting with input. */ + Hist = HistoryCurrentBuffer(Console); + if (!Hist || Hist->NumEntries == 0) return; + + /* Like Up/F5, on first time start from current (usually last) entry, + * but on subsequent times start at previous entry. */ + if (Console->LineUpPressed) + Hist->Position = (Hist->Position ? Hist->Position : Hist->NumEntries) - 1; + Console->LineUpPressed = TRUE; + + Entry.Length = Console->LinePos * sizeof(WCHAR); + Entry.Buffer = Console->LineBuffer; + + /* Keep going backwards, even wrapping around to the end, + * until we get back to starting point */ + HistPos = Hist->Position; + do + { + if (RtlPrefixUnicodeString(&Entry, &Hist->Entries[HistPos], FALSE)) + { + Hist->Position = HistPos; + LineInputEdit(Console, Console->LineSize - Pos, + Hist->Entries[HistPos].Length / sizeof(WCHAR) - Pos, + &Hist->Entries[HistPos].Buffer[Pos]); + /* Cursor stays where it was */ + LineInputSetPos(Console, Pos); + return; + } + if (--HistPos < 0) HistPos += Hist->NumEntries; + } while (HistPos != Hist->Position); + return; + } + + if (KeyEvent->uChar.UnicodeChar == L'\b' && Console->InputBuffer.Mode & ENABLE_PROCESSED_INPUT) + { + /* backspace handling - if processed input enabled then we handle it here + * otherwise we treat it like a normal char. */ + if (Pos > 0) + { + LineInputSetPos(Console, Pos - 1); + LineInputEdit(Console, 1, 0, NULL); + } + } + else if (KeyEvent->uChar.UnicodeChar == L'\r') + { + HistoryAddEntry(Console); + + /* TODO: Expand aliases */ + + LineInputSetPos(Console, Console->LineSize); + Console->LineBuffer[Console->LineSize++] = L'\r'; + if (Console->InputBuffer.Mode & ENABLE_ECHO_INPUT) + { + if (GetType(Console->ActiveBuffer) == TEXTMODE_BUFFER) + { + ConioWriteConsole(Console, (PTEXTMODE_SCREEN_BUFFER)(Console->ActiveBuffer), L"\r", 1, TRUE); + } + } + + /* 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->InputBuffer.Mode & ENABLE_PROCESSED_INPUT && + Console->LineSize < Console->LineMaxSize) + { + Console->LineBuffer[Console->LineSize++] = L'\n'; + if (Console->InputBuffer.Mode & ENABLE_ECHO_INPUT) + { + if (GetType(Console->ActiveBuffer) == TEXTMODE_BUFFER) + { + ConioWriteConsole(Console, (PTEXTMODE_SCREEN_BUFFER)(Console->ActiveBuffer), L"\n", 1, TRUE); + } + } + } + Console->LineComplete = TRUE; + Console->LinePos = 0; + } + else if (KeyEvent->uChar.UnicodeChar != L'\0') + { + 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); + } + } +} + + +/* PUBLIC SERVER APIS *********************************************************/ + +CSR_API(SrvGetConsoleCommandHistory) +{ + PCONSOLE_GETCOMMANDHISTORY GetCommandHistoryRequest = &((PCONSOLE_API_MESSAGE)ApiMessage)->Data.GetCommandHistoryRequest; + PCONSOLE_PROCESS_DATA ProcessData = ConsoleGetPerProcessData(CsrGetClientThread()->Process); + PCONSOLE Console; + NTSTATUS Status; + PHISTORY_BUFFER Hist; + PBYTE Buffer = (PBYTE)GetCommandHistoryRequest->History; + ULONG BufferSize = GetCommandHistoryRequest->Length; + UINT i; + + if ( !CsrValidateMessageBuffer(ApiMessage, + (PVOID*)&GetCommandHistoryRequest->History, + GetCommandHistoryRequest->Length, + sizeof(BYTE)) || + !CsrValidateMessageBuffer(ApiMessage, + (PVOID*)&GetCommandHistoryRequest->ExeName.Buffer, + GetCommandHistoryRequest->ExeName.Length, + sizeof(BYTE)) ) + { + return STATUS_INVALID_PARAMETER; + } + + Status = ConSrvGetConsole(ProcessData, &Console, TRUE); + if (NT_SUCCESS(Status)) + { + Hist = HistoryFindBuffer(Console, &GetCommandHistoryRequest->ExeName); + if (Hist) + { + for (i = 0; i < Hist->NumEntries; i++) + { + if (BufferSize < (Hist->Entries[i].Length + sizeof(WCHAR))) + { + Status = STATUS_BUFFER_OVERFLOW; + break; + } + memcpy(Buffer, Hist->Entries[i].Buffer, Hist->Entries[i].Length); + Buffer += Hist->Entries[i].Length; + *(PWCHAR)Buffer = L'\0'; + Buffer += sizeof(WCHAR); + } + } + GetCommandHistoryRequest->Length = Buffer - (PBYTE)GetCommandHistoryRequest->History; + ConSrvReleaseConsole(Console, TRUE); + } + return Status; +} + +CSR_API(SrvGetConsoleCommandHistoryLength) +{ + PCONSOLE_GETCOMMANDHISTORYLENGTH GetCommandHistoryLengthRequest = &((PCONSOLE_API_MESSAGE)ApiMessage)->Data.GetCommandHistoryLengthRequest; + PCONSOLE_PROCESS_DATA ProcessData = ConsoleGetPerProcessData(CsrGetClientThread()->Process); + PCONSOLE Console; + NTSTATUS Status; + PHISTORY_BUFFER Hist; + ULONG Length = 0; + UINT i; + + if (!CsrValidateMessageBuffer(ApiMessage, + (PVOID*)&GetCommandHistoryLengthRequest->ExeName.Buffer, + GetCommandHistoryLengthRequest->ExeName.Length, + sizeof(BYTE))) + { + return STATUS_INVALID_PARAMETER; + } + + Status = ConSrvGetConsole(ProcessData, &Console, TRUE); + if (NT_SUCCESS(Status)) + { + Hist = HistoryFindBuffer(Console, &GetCommandHistoryLengthRequest->ExeName); + if (Hist) + { + for (i = 0; i < Hist->NumEntries; i++) + Length += Hist->Entries[i].Length + sizeof(WCHAR); + } + GetCommandHistoryLengthRequest->Length = Length; + ConSrvReleaseConsole(Console, TRUE); + } + return Status; +} + +CSR_API(SrvExpungeConsoleCommandHistory) +{ + PCONSOLE_EXPUNGECOMMANDHISTORY ExpungeCommandHistoryRequest = &((PCONSOLE_API_MESSAGE)ApiMessage)->Data.ExpungeCommandHistoryRequest; + PCONSOLE_PROCESS_DATA ProcessData = ConsoleGetPerProcessData(CsrGetClientThread()->Process); + PCONSOLE Console; + PHISTORY_BUFFER Hist; + NTSTATUS Status; + + if (!CsrValidateMessageBuffer(ApiMessage, + (PVOID*)&ExpungeCommandHistoryRequest->ExeName.Buffer, + ExpungeCommandHistoryRequest->ExeName.Length, + sizeof(BYTE))) + { + return STATUS_INVALID_PARAMETER; + } + + Status = ConSrvGetConsole(ProcessData, &Console, TRUE); + if (NT_SUCCESS(Status)) + { + Hist = HistoryFindBuffer(Console, &ExpungeCommandHistoryRequest->ExeName); + HistoryDeleteBuffer(Hist); + ConSrvReleaseConsole(Console, TRUE); + } + return Status; +} + +CSR_API(SrvSetConsoleNumberOfCommands) +{ + PCONSOLE_SETHISTORYNUMBERCOMMANDS SetHistoryNumberCommandsRequest = &((PCONSOLE_API_MESSAGE)ApiMessage)->Data.SetHistoryNumberCommandsRequest; + PCONSOLE_PROCESS_DATA ProcessData = ConsoleGetPerProcessData(CsrGetClientThread()->Process); + PCONSOLE Console; + PHISTORY_BUFFER Hist; + NTSTATUS Status; + UINT MaxEntries = SetHistoryNumberCommandsRequest->NumCommands; + PUNICODE_STRING OldEntryList, NewEntryList; + + if (!CsrValidateMessageBuffer(ApiMessage, + (PVOID*)&SetHistoryNumberCommandsRequest->ExeName.Buffer, + SetHistoryNumberCommandsRequest->ExeName.Length, + sizeof(BYTE))) + { + return STATUS_INVALID_PARAMETER; + } + + Status = ConSrvGetConsole(ProcessData, &Console, TRUE); + if (NT_SUCCESS(Status)) + { + Hist = HistoryFindBuffer(Console, &SetHistoryNumberCommandsRequest->ExeName); + if (Hist) + { + OldEntryList = Hist->Entries; + NewEntryList = ConsoleAllocHeap(0, MaxEntries * sizeof(UNICODE_STRING)); + if (!NewEntryList) + { + Status = STATUS_NO_MEMORY; + } + else + { + /* If necessary, shrink by removing oldest entries */ + for (; Hist->NumEntries > MaxEntries; Hist->NumEntries--) + { + RtlFreeUnicodeString(Hist->Entries++); + Hist->Position += (Hist->Position == 0); + } + + Hist->MaxEntries = MaxEntries; + Hist->Entries = memcpy(NewEntryList, Hist->Entries, + Hist->NumEntries * sizeof(UNICODE_STRING)); + ConsoleFreeHeap(OldEntryList); + } + } + ConSrvReleaseConsole(Console, TRUE); + } + return Status; +} + +CSR_API(SrvGetConsoleHistory) +{ + PCONSOLE_GETSETHISTORYINFO HistoryInfoRequest = &((PCONSOLE_API_MESSAGE)ApiMessage)->Data.HistoryInfoRequest; + PCONSOLE Console; + NTSTATUS Status = ConSrvGetConsole(ConsoleGetPerProcessData(CsrGetClientThread()->Process), &Console, TRUE); + if (NT_SUCCESS(Status)) + { + HistoryInfoRequest->HistoryBufferSize = Console->HistoryBufferSize; + HistoryInfoRequest->NumberOfHistoryBuffers = Console->NumberOfHistoryBuffers; + HistoryInfoRequest->dwFlags = Console->HistoryNoDup; + ConSrvReleaseConsole(Console, TRUE); + } + return Status; +} + +CSR_API(SrvSetConsoleHistory) +{ + PCONSOLE_GETSETHISTORYINFO HistoryInfoRequest = &((PCONSOLE_API_MESSAGE)ApiMessage)->Data.HistoryInfoRequest; + PCONSOLE Console; + NTSTATUS Status = ConSrvGetConsole(ConsoleGetPerProcessData(CsrGetClientThread()->Process), &Console, TRUE); + if (NT_SUCCESS(Status)) + { + Console->HistoryBufferSize = HistoryInfoRequest->HistoryBufferSize; + Console->NumberOfHistoryBuffers = HistoryInfoRequest->NumberOfHistoryBuffers; + Console->HistoryNoDup = HistoryInfoRequest->dwFlags & HISTORY_NO_DUP_FLAG; + ConSrvReleaseConsole(Console, TRUE); + } + return Status; +} + +/* EOF */ diff --git a/reactos/win32ss/user/winsrv/consrv_new/lineinput.h b/reactos/win32ss/user/winsrv/consrv_new/lineinput.h new file mode 100644 index 00000000000..77dae2edbb0 --- /dev/null +++ b/reactos/win32ss/user/winsrv/consrv_new/lineinput.h @@ -0,0 +1,14 @@ +/* + * LICENSE: GPL - See COPYING in the top level directory + * PROJECT: ReactOS Console Server DLL + * FILE: win32ss/user/winsrv/consrv/lineinput.c + * PURPOSE: Console line input functions + * PROGRAMMERS: Jeffrey Morlan + */ + +#pragma once + +VOID FASTCALL HistoryDeleteBuffers(PCONSOLE Console); +VOID FASTCALL LineInputKeyDown(PCONSOLE Console, KEY_EVENT_RECORD *KeyEvent); + +/* EOF */ diff --git a/reactos/win32ss/user/winsrv/consrv_new/procinit.h b/reactos/win32ss/user/winsrv/consrv_new/procinit.h new file mode 100644 index 00000000000..93f046d99a5 --- /dev/null +++ b/reactos/win32ss/user/winsrv/consrv_new/procinit.h @@ -0,0 +1,24 @@ +/* + * LICENSE: GPL - See COPYING in the top level directory + * PROJECT: ReactOS Console Server DLL + * FILE: win32ss/user/winsrv/consrv/procinit.h + * PURPOSE: Functions for console processes initialization + * PROGRAMMERS: Hermes Belusca-Maito (hermes.belusca@sfr.fr) + */ + +#pragma once + +NTSTATUS FASTCALL ConSrvAllocateConsole(PCONSOLE_PROCESS_DATA ProcessData, + PHANDLE pInputHandle, + PHANDLE pOutputHandle, + PHANDLE pErrorHandle, + PCONSOLE_START_INFO ConsoleStartInfo); +NTSTATUS FASTCALL ConSrvInheritConsole(PCONSOLE_PROCESS_DATA ProcessData, + HANDLE ConsoleHandle, + BOOL CreateNewHandlesTable, + PHANDLE pInputHandle, + PHANDLE pOutputHandle, + PHANDLE pErrorHandle); +VOID FASTCALL ConSrvRemoveConsole(PCONSOLE_PROCESS_DATA ProcessData); + +/* EOF */ diff --git a/reactos/win32ss/user/winsrv/consrv_new/resource.h b/reactos/win32ss/user/winsrv/consrv_new/resource.h new file mode 100644 index 00000000000..dc6f4a9898f --- /dev/null +++ b/reactos/win32ss/user/winsrv/consrv_new/resource.h @@ -0,0 +1,12 @@ +/* + * COPYRIGHT: See COPYING in the top level directory + * PROJECT: ReactOS Console Server DLL + * FILE: win32ss/user/winsrv/consrv/resource.h + * PURPOSE: Resource #defines + */ + +#pragma once + +#define IDS_CONSOLE_TITLE 500 + +/* EOF */ diff --git a/reactos/win32ss/user/winsrv/consrv_new/rsrc.rc b/reactos/win32ss/user/winsrv/consrv_new/rsrc.rc new file mode 100644 index 00000000000..1839748b631 --- /dev/null +++ b/reactos/win32ss/user/winsrv/consrv_new/rsrc.rc @@ -0,0 +1,70 @@ +LANGUAGE LANG_NEUTRAL, SUBLANG_NEUTRAL + +// UTF-8 +#pragma code_page(65001) +#ifdef LANGUAGE_BG_BG + #include "lang/bg-BG.rc" +#endif +#ifdef LANGUAGE_CS_CZ + #include "lang/cs-CZ.rc" +#endif +#ifdef LANGUAGE_DE_DE + #include "lang/de-DE.rc" +#endif +#ifdef LANGUAGE_EL_GR + #include "lang/el-GR.rc" +#endif +#ifdef LANGUAGE_EN_US + #include "lang/en-US.rc" +#endif +#ifdef LANGUAGE_ES_ES + #include "lang/es-ES.rc" +#endif +#ifdef LANGUAGE_FR_FR + #include "lang/fr-FR.rc" +#endif +#ifdef LANGUAGE_HE_IL + #include "lang/he-IL.rc" +#endif +#ifdef LANGUAGE_ID_ID + #include "lang/id-ID.rc" +#endif +#ifdef LANGUAGE_IT_IT + #include "lang/it-IT.rc" +#endif +#ifdef LANGUAGE_JA_JP + #include "lang/ja-JP.rc" +#endif +#ifdef LANGUAGE_NB_NO + #include "lang/no-NO.rc" +#endif +#ifdef LANGUAGE_PL_PL + #include "lang/pl-PL.rc" +#endif +#ifdef LANGUAGE_PT_BR + #include "lang/pt-BR.rc" +#endif +#ifdef LANGUAGE_RO_RO + #include "lang/ro-RO.rc" +#endif +#ifdef LANGUAGE_RU_RU + #include "lang/ru-RU.rc" +#endif +#ifdef LANGUAGE_SK_SK + #include "lang/sk-SK.rc" +#endif +#ifdef LANGUAGE_SV_SE + #include "lang/sv-SE.rc" +#endif +#ifdef LANGUAGE_TR_TR + #include "lang/tr-TR.rc" +#endif +#ifdef LANGUAGE_UK_UA + #include "lang/uk-UA.rc" +#endif +#ifdef LANGUAGE_ZH_CN + #include "lang/zh-CN.rc" +#endif +#ifdef LANGUAGE_ZH_TW + #include "lang/zh-TW.rc" +#endif diff --git a/reactos/win32ss/user/winsrv/consrv_new/settings.c b/reactos/win32ss/user/winsrv/consrv_new/settings.c new file mode 100644 index 00000000000..738d5bfce3c --- /dev/null +++ b/reactos/win32ss/user/winsrv/consrv_new/settings.c @@ -0,0 +1,576 @@ +/* + * COPYRIGHT: See COPYING in the top level directory + * PROJECT: ReactOS Console Server DLL + * FILE: win32ss/user/winsrv/consrv/settings.c + * PURPOSE: Console settings management + * PROGRAMMERS: Johannes Anderwald + * Hermes Belusca-Maito (hermes.belusca@sfr.fr) + */ + +/* INCLUDES *******************************************************************/ + +#include "consrv.h" +#include "include/conio.h" +#include "include/conio2.h" +#include "include/settings.h" + +#include // for swprintf + +#define NDEBUG +#include + + +/* GLOBALS ********************************************************************/ + +extern const COLORREF s_Colors[16]; + + +/* FUNCTIONS ******************************************************************/ + +static VOID +TranslateConsoleName(OUT LPWSTR DestString, + IN LPCWSTR ConsoleName, + IN UINT MaxStrLen) +{ +#define PATH_SEPARATOR L'\\' + + UINT wLength; + + if ( DestString == NULL || ConsoleName == NULL || + *ConsoleName == L'\0' || MaxStrLen == 0 ) + { + return; + } + + wLength = GetWindowsDirectoryW(DestString, MaxStrLen); + if ((wLength > 0) && (_wcsnicmp(ConsoleName, DestString, wLength) == 0)) + { + wcsncpy(DestString, L"%SystemRoot%", MaxStrLen); + // FIXME: Fix possible buffer overflows there !!!!! + wcsncat(DestString, ConsoleName + wLength, MaxStrLen); + } + else + { + wcsncpy(DestString, ConsoleName, MaxStrLen); + } + + /* Replace path separators (backslashes) by underscores */ + while ((DestString = wcschr(DestString, PATH_SEPARATOR))) *DestString = L'_'; +} + +static BOOL +OpenUserRegistryPathPerProcessId(DWORD ProcessId, + PHKEY hResult, + REGSAM samDesired) +{ + BOOL bRet = TRUE; + HANDLE hProcessToken = NULL; + HANDLE hProcess; + BYTE Buffer[256]; + DWORD Length = 0; + UNICODE_STRING SidName; + PTOKEN_USER TokUser; + + hProcess = OpenProcess(PROCESS_QUERY_INFORMATION | PROCESS_VM_READ | READ_CONTROL, FALSE, ProcessId); + if (!hProcess) + { + DPRINT1("Error: OpenProcess failed(0x%x)\n", GetLastError()); + return FALSE; + } + + if (!OpenProcessToken(hProcess, TOKEN_QUERY, &hProcessToken)) + { + DPRINT1("Error: OpenProcessToken failed(0x%x)\n", GetLastError()); + CloseHandle(hProcess); + return FALSE; + } + + if (!GetTokenInformation(hProcessToken, TokenUser, (PVOID)Buffer, sizeof(Buffer), &Length)) + { + DPRINT1("Error: GetTokenInformation failed(0x%x)\n",GetLastError()); + CloseHandle(hProcessToken); + CloseHandle(hProcess); + return FALSE; + } + + TokUser = ((PTOKEN_USER)Buffer)->User.Sid; + if (!NT_SUCCESS(RtlConvertSidToUnicodeString(&SidName, TokUser, TRUE))) + { + DPRINT1("Error: RtlConvertSidToUnicodeString failed(0x%x)\n", GetLastError()); + CloseHandle(hProcessToken); + CloseHandle(hProcess); + return FALSE; + } + + /* + * Might fail for LiveCD... Why ? Because only HKU\.DEFAULT exists. + */ + bRet = (RegOpenKeyExW(HKEY_USERS, + SidName.Buffer, + 0, + samDesired, + hResult) == ERROR_SUCCESS); + + RtlFreeUnicodeString(&SidName); + + CloseHandle(hProcessToken); + CloseHandle(hProcess); + + return bRet; +} + +/*static*/ BOOL +ConSrvOpenUserSettings(DWORD ProcessId, + LPCWSTR ConsoleTitle, + PHKEY hSubKey, + REGSAM samDesired, + BOOL bCreate) +{ + BOOL bRet = TRUE; + WCHAR szBuffer[MAX_PATH] = L"Console\\"; + WCHAR szBuffer2[MAX_PATH] = L""; + HKEY hKey; + + /* + * Console properties are stored under the HKCU\Console\* key. + * + * We use the original console title as the subkey name for storing + * console properties. We need to distinguish whether we were launched + * via the console application directly or via a shortcut. + * + * If the title of the console corresponds to a path (more precisely, + * if the title is of the form: C:\ReactOS\\), + * then use the corresponding unexpanded path and with the backslashes + * replaced by underscores, to make the registry happy, + * i.e. %SystemRoot%__ + */ + + /* Open the registry key where we saved the console properties */ + if (!OpenUserRegistryPathPerProcessId(ProcessId, &hKey, samDesired)) + { + DPRINT1("OpenUserRegistryPathPerProcessId failed\n"); + return FALSE; + } + + /* + * Try to open properties via the console title: + * to make the registry happy, replace all the + * backslashes by underscores. + */ + TranslateConsoleName(szBuffer2, ConsoleTitle, MAX_PATH); + + /* Create the registry path */ + wcsncat(szBuffer, szBuffer2, MAX_PATH); + + /* Create or open the registry key */ + if (bCreate) + { + /* Create the key */ + bRet = (RegCreateKeyExW(hKey, + szBuffer, + 0, NULL, + REG_OPTION_NON_VOLATILE, + samDesired, + NULL, + hSubKey, + NULL) == ERROR_SUCCESS); + } + else + { + /* Open the key */ + bRet = (RegOpenKeyExW(hKey, + szBuffer, + 0, + samDesired, + hSubKey) == ERROR_SUCCESS); + } + + /* Close the parent key and return success or not */ + RegCloseKey(hKey); + return bRet; +} + +BOOL +ConSrvReadUserSettings(IN OUT PCONSOLE_INFO ConsoleInfo, + IN DWORD ProcessId) +{ + BOOL RetVal = FALSE; + HKEY hKey; + DWORD dwNumSubKeys = 0; + DWORD dwIndex; + DWORD dwColorIndex = 0; + DWORD dwType; + WCHAR szValueName[MAX_PATH]; + DWORD dwValueName; + WCHAR szValue[LF_FACESIZE] = L"\0"; + DWORD Value; + DWORD dwValue; + + if (!ConSrvOpenUserSettings(ProcessId, + ConsoleInfo->ConsoleTitle, + &hKey, KEY_READ, + FALSE)) + { + DPRINT("ConSrvOpenUserSettings failed\n"); + return FALSE; + } + + if (RegQueryInfoKey(hKey, NULL, NULL, NULL, NULL, NULL, NULL, + &dwNumSubKeys, NULL, NULL, NULL, NULL) != ERROR_SUCCESS) + { + DPRINT("ConSrvReadUserSettings: RegQueryInfoKey failed\n"); + RegCloseKey(hKey); + return FALSE; + } + + DPRINT("ConSrvReadUserSettings entered dwNumSubKeys %d\n", dwNumSubKeys); + + for (dwIndex = 0; dwIndex < dwNumSubKeys; dwIndex++) + { + dwValue = sizeof(Value); + dwValueName = MAX_PATH; // sizeof(szValueName)/sizeof(szValueName[0]) + + if (RegEnumValueW(hKey, dwIndex, szValueName, &dwValueName, NULL, &dwType, (BYTE*)&Value, &dwValue) != ERROR_SUCCESS) + { + if (dwType == REG_SZ) + { + /* + * Retry in case of string value + */ + dwValue = sizeof(szValue); + dwValueName = MAX_PATH; // sizeof(szValueName)/sizeof(szValueName[0]) + if (RegEnumValueW(hKey, dwIndex, szValueName, &dwValueName, NULL, NULL, (BYTE*)szValue, &dwValue) != ERROR_SUCCESS) + break; + } + else + { + break; + } + } + + /* Maybe it is UI-specific ?? */ + if (!wcsncmp(szValueName, L"ColorTable", wcslen(L"ColorTable"))) + { + dwColorIndex = 0; + swscanf(szValueName, L"ColorTable%2d", &dwColorIndex); + if (dwColorIndex < sizeof(ConsoleInfo->Colors)/sizeof(ConsoleInfo->Colors[0])) + { + ConsoleInfo->Colors[dwColorIndex] = Value; + RetVal = TRUE; + } + } + else if (!wcscmp(szValueName, L"HistoryBufferSize")) + { + ConsoleInfo->HistoryBufferSize = Value; + RetVal = TRUE; + } + else if (!wcscmp(szValueName, L"NumberOfHistoryBuffers")) + { + ConsoleInfo->NumberOfHistoryBuffers = Value; + RetVal = TRUE; + } + else if (!wcscmp(szValueName, L"HistoryNoDup")) + { + ConsoleInfo->HistoryNoDup = (BOOLEAN)Value; + RetVal = TRUE; + } + else if (!wcscmp(szValueName, L"QuickEdit")) + { + ConsoleInfo->QuickEdit = (BOOLEAN)Value; + RetVal = TRUE; + } + else if (!wcscmp(szValueName, L"InsertMode")) + { + ConsoleInfo->InsertMode = (BOOLEAN)Value; + RetVal = TRUE; + } + else if (!wcscmp(szValueName, L"ScreenBufferSize")) + { + ConsoleInfo->ScreenBufferSize.X = LOWORD(Value); + ConsoleInfo->ScreenBufferSize.Y = HIWORD(Value); + RetVal = TRUE; + } + else if (!wcscmp(szValueName, L"WindowSize")) + { + ConsoleInfo->ConsoleSize.X = LOWORD(Value); + ConsoleInfo->ConsoleSize.Y = HIWORD(Value); + RetVal = TRUE; + } + else if (!wcscmp(szValueName, L"CursorSize")) + { + ConsoleInfo->CursorSize = min(max(Value, 0), 100); + RetVal = TRUE; + } + else if (!wcscmp(szValueName, L"ScreenColors")) + { + ConsoleInfo->ScreenAttrib = (USHORT)Value; + RetVal = TRUE; + } + else if (!wcscmp(szValueName, L"PopupColors")) + { + ConsoleInfo->PopupAttrib = (USHORT)Value; + RetVal = TRUE; + } + } + + RegCloseKey(hKey); + return RetVal; +} + +BOOL +ConSrvWriteUserSettings(IN PCONSOLE_INFO ConsoleInfo, + IN DWORD ProcessId) +{ + BOOL GlobalSettings = (ConsoleInfo->ConsoleTitle[0] == L'\0'); + HKEY hKey; + DWORD Storage = 0; + +#define SetConsoleSetting(SettingName, SettingType, SettingSize, Setting, DefaultValue) \ +do { \ + if (GlobalSettings || (!GlobalSettings && (*(Setting) != (DefaultValue)))) \ + { \ + RegSetValueExW(hKey, (SettingName), 0, (SettingType), (PBYTE)(Setting), (SettingSize)); \ + } \ + else \ + { \ + RegDeleteValue(hKey, (SettingName)); \ + } \ +} while (0) + + WCHAR szValueName[15]; + UINT i; + + if (!ConSrvOpenUserSettings(ProcessId, + ConsoleInfo->ConsoleTitle, + &hKey, KEY_WRITE, + TRUE)) + { + return FALSE; + } + + for (i = 0 ; i < sizeof(ConsoleInfo->Colors)/sizeof(ConsoleInfo->Colors[0]) ; ++i) + { + /* + * Write only the new value if we are saving the global settings + * or we are saving settings for a particular console, which differs + * from the default ones. + */ + swprintf(szValueName, L"ColorTable%02d", i); + SetConsoleSetting(szValueName, REG_DWORD, sizeof(DWORD), &ConsoleInfo->Colors[i], s_Colors[i]); + } + + SetConsoleSetting(L"HistoryBufferSize", REG_DWORD, sizeof(DWORD), &ConsoleInfo->HistoryBufferSize, 50); + SetConsoleSetting(L"NumberOfHistoryBuffers", REG_DWORD, sizeof(DWORD), &ConsoleInfo->NumberOfHistoryBuffers, 4); + + Storage = ConsoleInfo->HistoryNoDup; + SetConsoleSetting(L"HistoryNoDup", REG_DWORD, sizeof(DWORD), &Storage, FALSE); + + Storage = ConsoleInfo->QuickEdit; + SetConsoleSetting(L"QuickEdit", REG_DWORD, sizeof(DWORD), &Storage, FALSE); + + Storage = ConsoleInfo->InsertMode; + SetConsoleSetting(L"InsertMode", REG_DWORD, sizeof(DWORD), &Storage, TRUE); + + Storage = MAKELONG(ConsoleInfo->ScreenBufferSize.X, ConsoleInfo->ScreenBufferSize.Y); + SetConsoleSetting(L"ScreenBufferSize", REG_DWORD, sizeof(DWORD), &Storage, MAKELONG(80, 300)); + + Storage = MAKELONG(ConsoleInfo->ConsoleSize.X, ConsoleInfo->ConsoleSize.Y); + SetConsoleSetting(L"WindowSize", REG_DWORD, sizeof(DWORD), &Storage, MAKELONG(80, 25)); + + SetConsoleSetting(L"CursorSize", REG_DWORD, sizeof(DWORD), &ConsoleInfo->CursorSize, CSR_DEFAULT_CURSOR_SIZE); + + Storage = ConsoleInfo->ScreenAttrib; + SetConsoleSetting(L"ScreenColors", REG_DWORD, sizeof(DWORD), &Storage, DEFAULT_SCREEN_ATTRIB); + + Storage = ConsoleInfo->PopupAttrib; + SetConsoleSetting(L"PopupColors", REG_DWORD, sizeof(DWORD), &Storage, DEFAULT_POPUP_ATTRIB); + + RegCloseKey(hKey); + return TRUE; +} + +VOID +ConSrvGetDefaultSettings(IN OUT PCONSOLE_INFO ConsoleInfo, + IN DWORD ProcessId) +{ + if (ConsoleInfo == NULL) return; + +/// HKCU,"Console","LoadConIme",0x00010003,1 + + /* + * 1. Load the default values + */ +// #define DEFAULT_HISTORY_COMMANDS_NUMBER 50 +// #define DEFAULT_HISTORY_BUFFERS_NUMBER 4 + ConsoleInfo->HistoryBufferSize = 50; + ConsoleInfo->NumberOfHistoryBuffers = 4; + ConsoleInfo->HistoryNoDup = FALSE; + + ConsoleInfo->QuickEdit = FALSE; + ConsoleInfo->InsertMode = TRUE; + // ConsoleInfo->InputBufferSize; + + // Rule: ScreenBufferSize >= ConsoleSize + ConsoleInfo->ScreenBufferSize.X = 80; + ConsoleInfo->ScreenBufferSize.Y = 300; + ConsoleInfo->ConsoleSize.X = 80; + ConsoleInfo->ConsoleSize.Y = 25; + + ConsoleInfo->CursorBlinkOn; + ConsoleInfo->ForceCursorOff; + ConsoleInfo->CursorSize = CSR_DEFAULT_CURSOR_SIZE; // #define SMALL_SIZE 25 + + ConsoleInfo->ScreenAttrib = DEFAULT_SCREEN_ATTRIB; + ConsoleInfo->PopupAttrib = DEFAULT_POPUP_ATTRIB; + + memcpy(ConsoleInfo->Colors, s_Colors, sizeof(s_Colors)); + + // ConsoleInfo->CodePage; + + ConsoleInfo->ConsoleTitle[0] = L'\0'; + + /* + * 2. Overwrite them with the ones stored in HKCU\Console. + * If the HKCU\Console key doesn't exist, create it + * and store the default values inside. + */ + if (!ConSrvReadUserSettings(ConsoleInfo, ProcessId)) + { + ConSrvWriteUserSettings(ConsoleInfo, ProcessId); + } +} + +VOID +ConSrvApplyUserSettings(IN PCONSOLE Console, + IN PCONSOLE_INFO ConsoleInfo) +{ + PCONSOLE_SCREEN_BUFFER ActiveBuffer = Console->ActiveBuffer; + + /* + * Apply terminal-edition settings: + * - QuickEdit and Insert modes, + * - history settings. + */ + Console->QuickEdit = ConsoleInfo->QuickEdit; + Console->InsertMode = ConsoleInfo->InsertMode; + + /* + * Apply foreground and background colors for both screen and popup + * and copy the new palette. + */ + if (GetType(ActiveBuffer) == TEXTMODE_BUFFER) + { + PTEXTMODE_SCREEN_BUFFER Buffer = (PTEXTMODE_SCREEN_BUFFER)ActiveBuffer; + + Buffer->ScreenDefaultAttrib = ConsoleInfo->ScreenAttrib; + Buffer->PopupDefaultAttrib = ConsoleInfo->PopupAttrib; + } + else // if (Console->ActiveBuffer->Header.Type == GRAPHICS_BUFFER) + { + } + + // FIXME: Possible buffer overflow if s_colors is bigger than pConInfo->Colors. + memcpy(Console->Colors, ConsoleInfo->Colors, sizeof(s_Colors)); + + // TODO: Really update the screen attributes as FillConsoleOutputAttribute does. + + /* Apply cursor size */ + ActiveBuffer->CursorInfo.bVisible = (ConsoleInfo->CursorSize != 0); + ActiveBuffer->CursorInfo.dwSize = min(max(ConsoleInfo->CursorSize, 0), 100); + + if (GetType(ActiveBuffer) == TEXTMODE_BUFFER) + { + PTEXTMODE_SCREEN_BUFFER Buffer = (PTEXTMODE_SCREEN_BUFFER)ActiveBuffer; + COORD BufSize; + + /* Resize its active screen-buffer */ + BufSize = ConsoleInfo->ScreenBufferSize; + + if (Console->FixedSize) + { + /* + * The console is in fixed-size mode, so we cannot resize anything + * at the moment. However, keep those settings somewhere so that + * we can try to set them up when we will be allowed to do so. + */ + if (ConsoleInfo->ConsoleSize.X != Buffer->OldViewSize.X || + ConsoleInfo->ConsoleSize.Y != Buffer->OldViewSize.Y) + { + Buffer->OldViewSize = ConsoleInfo->ConsoleSize; + } + + /* Buffer size is not allowed to be smaller than the view size */ + if (BufSize.X >= Buffer->OldViewSize.X && BufSize.Y >= Buffer->OldViewSize.Y) + { + if (BufSize.X != Buffer->OldScreenBufferSize.X || + BufSize.Y != Buffer->OldScreenBufferSize.Y) + { + /* + * The console is in fixed-size mode, so we cannot resize anything + * at the moment. However, keep those settings somewhere so that + * we can try to set them up when we will be allowed to do so. + */ + Buffer->OldScreenBufferSize = BufSize; + } + } + } + else + { + BOOL SizeChanged = FALSE; + + /* Resize the console */ + if (ConsoleInfo->ConsoleSize.X != Buffer->ViewSize.X || + ConsoleInfo->ConsoleSize.Y != Buffer->ViewSize.Y) + { + Buffer->ViewSize = ConsoleInfo->ConsoleSize; + SizeChanged = TRUE; + } + + /* Resize the screen-buffer */ + if (BufSize.X != Buffer->ScreenBufferSize.X || + BufSize.Y != Buffer->ScreenBufferSize.Y) + { + if (NT_SUCCESS(ConioResizeBuffer(Console, Buffer, BufSize))) + SizeChanged = TRUE; + } + + if (SizeChanged) ConioResizeTerminal(Console); + } + } + else // if (GetType(ActiveBuffer) == GRAPHICS_BUFFER) + { + PGRAPHICS_SCREEN_BUFFER Buffer = (PGRAPHICS_SCREEN_BUFFER)ActiveBuffer; + + /* + * In any case we do NOT modify the size of the graphics screen-buffer. + * We just allow resizing the view only if the new size is smaller + * than the older one. + */ + + if (Console->FixedSize) + { + /* + * The console is in fixed-size mode, so we cannot resize anything + * at the moment. However, keep those settings somewhere so that + * we can try to set them up when we will be allowed to do so. + */ + if (ConsoleInfo->ConsoleSize.X <= Buffer->ViewSize.X || + ConsoleInfo->ConsoleSize.Y <= Buffer->ViewSize.Y) + { + Buffer->OldViewSize = ConsoleInfo->ConsoleSize; + } + } + else + { + /* Resize the view if its size is bigger than the specified size */ + if (ConsoleInfo->ConsoleSize.X <= Buffer->ViewSize.X || + ConsoleInfo->ConsoleSize.Y <= Buffer->ViewSize.Y) + { + Buffer->ViewSize = ConsoleInfo->ConsoleSize; + // SizeChanged = TRUE; + } + } + } +} + +/* EOF */