From a60aae50e99862f3178e6768641e5c5afb0c419a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Herm=C3=A8s=20B=C3=A9lusca-Ma=C3=AFto?= Date: Sat, 13 Jul 2013 15:18:34 +0000 Subject: [PATCH] [CONSRV] Create a new consrv_new to study its interfacing with the console driver (condrv). svn path=/trunk/; revision=59470 --- .../win32ss/user/winsrv/consrv_new/alias.c | 587 ++++ .../win32ss/user/winsrv/consrv_new/alias.h | 14 + reactos/win32ss/user/winsrv/consrv_new/api.h | 84 + .../user/winsrv/consrv_new/condrv/coninput.c | 514 +++ .../user/winsrv/consrv_new/condrv/conoutput.c | 287 ++ .../user/winsrv/consrv_new/condrv/console.c | 1106 ++++++ .../winsrv/consrv_new/condrv/dummyfrontend.c | 185 ++ .../user/winsrv/consrv_new/condrv/graphics.c | 274 ++ .../user/winsrv/consrv_new/condrv/text.c | 1346 ++++++++ .../win32ss/user/winsrv/consrv_new/coninput.c | 479 +++ .../win32ss/user/winsrv/consrv_new/coninput.h | 23 + .../user/winsrv/consrv_new/conoutput.c | 793 +++++ .../user/winsrv/consrv_new/conoutput.h | 46 + .../win32ss/user/winsrv/consrv_new/console.c | 652 ++++ .../win32ss/user/winsrv/consrv_new/console.h | 27 + .../win32ss/user/winsrv/consrv_new/consrv.h | 79 + .../win32ss/user/winsrv/consrv_new/consrv.rc | 6 + .../user/winsrv/consrv_new/frontendctl.c | 331 ++ .../winsrv/consrv_new/frontends/frontends.rc | 2 + .../consrv_new/frontends/gui/consolecpl.h | 14 + .../consrv_new/frontends/gui/graphics.c | 81 + .../consrv_new/frontends/gui/guisettings.c | 541 +++ .../consrv_new/frontends/gui/guisettings.h | 94 + .../winsrv/consrv_new/frontends/gui/guiterm.c | 2960 +++++++++++++++++ .../winsrv/consrv_new/frontends/gui/guiterm.h | 28 + .../consrv_new/frontends/gui/guiterm.rc | 16 + .../consrv_new/frontends/gui/lang/bg-BG.rc | 32 + .../consrv_new/frontends/gui/lang/cs-CZ.rc | 32 + .../consrv_new/frontends/gui/lang/de-DE.rc | 26 + .../consrv_new/frontends/gui/lang/el-GR.rc | 26 + .../consrv_new/frontends/gui/lang/en-US.rc | 26 + .../consrv_new/frontends/gui/lang/es-ES.rc | 31 + .../consrv_new/frontends/gui/lang/fr-FR.rc | 28 + .../consrv_new/frontends/gui/lang/he-IL.rc | 26 + .../consrv_new/frontends/gui/lang/id-ID.rc | 26 + .../consrv_new/frontends/gui/lang/it-IT.rc | 35 + .../consrv_new/frontends/gui/lang/ja-JP.rc | 26 + .../consrv_new/frontends/gui/lang/no-NO.rc | 26 + .../consrv_new/frontends/gui/lang/pl-PL.rc | 34 + .../consrv_new/frontends/gui/lang/pt-BR.rc | 28 + .../consrv_new/frontends/gui/lang/ro-RO.rc | 33 + .../consrv_new/frontends/gui/lang/ru-RU.rc | 26 + .../consrv_new/frontends/gui/lang/sk-SK.rc | 31 + .../consrv_new/frontends/gui/lang/sv-SE.rc | 33 + .../consrv_new/frontends/gui/lang/tr-TR.rc | 46 + .../consrv_new/frontends/gui/lang/uk-UA.rc | 34 + .../consrv_new/frontends/gui/lang/zh-CN.rc | 26 + .../consrv_new/frontends/gui/lang/zh-TW.rc | 26 + .../consrv_new/frontends/gui/res/terminal.ico | Bin 0 -> 236566 bytes .../consrv_new/frontends/gui/resource.h | 45 + .../winsrv/consrv_new/frontends/gui/rsrc.rc | 70 + .../winsrv/consrv_new/frontends/gui/text.c | 300 ++ .../user/winsrv/consrv_new/frontends/input.c | 146 + .../winsrv/consrv_new/frontends/tui/tuiterm.c | 861 +++++ .../winsrv/consrv_new/frontends/tui/tuiterm.h | 19 + .../win32ss/user/winsrv/consrv_new/handle.c | 866 +++++ .../win32ss/user/winsrv/consrv_new/handle.h | 34 + reactos/win32ss/user/winsrv/consrv_new/heap.h | 17 + .../user/winsrv/consrv_new/include/conio.h | 377 +++ .../user/winsrv/consrv_new/include/conio2.h | 50 + .../user/winsrv/consrv_new/include/console.h | 54 + .../user/winsrv/consrv_new/include/settings.h | 93 + reactos/win32ss/user/winsrv/consrv_new/init.c | 531 +++ .../user/winsrv/consrv_new/lang/bg-BG.rc | 6 + .../user/winsrv/consrv_new/lang/cs-CZ.rc | 6 + .../user/winsrv/consrv_new/lang/de-DE.rc | 6 + .../user/winsrv/consrv_new/lang/el-GR.rc | 6 + .../user/winsrv/consrv_new/lang/en-US.rc | 6 + .../user/winsrv/consrv_new/lang/es-ES.rc | 6 + .../user/winsrv/consrv_new/lang/fr-FR.rc | 6 + .../user/winsrv/consrv_new/lang/he-IL.rc | 6 + .../user/winsrv/consrv_new/lang/id-ID.rc | 6 + .../user/winsrv/consrv_new/lang/it-IT.rc | 6 + .../user/winsrv/consrv_new/lang/ja-JP.rc | 6 + .../user/winsrv/consrv_new/lang/no-NO.rc | 6 + .../user/winsrv/consrv_new/lang/pl-PL.rc | 6 + .../user/winsrv/consrv_new/lang/pt-BR.rc | 6 + .../user/winsrv/consrv_new/lang/ro-RO.rc | 6 + .../user/winsrv/consrv_new/lang/ru-RU.rc | 6 + .../user/winsrv/consrv_new/lang/sk-SK.rc | 6 + .../user/winsrv/consrv_new/lang/sv-SE.rc | 6 + .../user/winsrv/consrv_new/lang/tr-TR.rc | 6 + .../user/winsrv/consrv_new/lang/uk-UA.rc | 6 + .../user/winsrv/consrv_new/lang/zh-CN.rc | 6 + .../user/winsrv/consrv_new/lang/zh-TW.rc | 6 + .../user/winsrv/consrv_new/lineinput.c | 644 ++++ .../user/winsrv/consrv_new/lineinput.h | 14 + .../win32ss/user/winsrv/consrv_new/procinit.h | 24 + .../win32ss/user/winsrv/consrv_new/resource.h | 12 + .../win32ss/user/winsrv/consrv_new/rsrc.rc | 70 + .../win32ss/user/winsrv/consrv_new/settings.c | 576 ++++ 91 files changed, 16161 insertions(+) create mode 100644 reactos/win32ss/user/winsrv/consrv_new/alias.c create mode 100644 reactos/win32ss/user/winsrv/consrv_new/alias.h create mode 100644 reactos/win32ss/user/winsrv/consrv_new/api.h create mode 100644 reactos/win32ss/user/winsrv/consrv_new/condrv/coninput.c create mode 100644 reactos/win32ss/user/winsrv/consrv_new/condrv/conoutput.c create mode 100644 reactos/win32ss/user/winsrv/consrv_new/condrv/console.c create mode 100644 reactos/win32ss/user/winsrv/consrv_new/condrv/dummyfrontend.c create mode 100644 reactos/win32ss/user/winsrv/consrv_new/condrv/graphics.c create mode 100644 reactos/win32ss/user/winsrv/consrv_new/condrv/text.c create mode 100644 reactos/win32ss/user/winsrv/consrv_new/coninput.c create mode 100644 reactos/win32ss/user/winsrv/consrv_new/coninput.h create mode 100644 reactos/win32ss/user/winsrv/consrv_new/conoutput.c create mode 100644 reactos/win32ss/user/winsrv/consrv_new/conoutput.h create mode 100644 reactos/win32ss/user/winsrv/consrv_new/console.c create mode 100644 reactos/win32ss/user/winsrv/consrv_new/console.h create mode 100644 reactos/win32ss/user/winsrv/consrv_new/consrv.h create mode 100644 reactos/win32ss/user/winsrv/consrv_new/consrv.rc create mode 100644 reactos/win32ss/user/winsrv/consrv_new/frontendctl.c create mode 100644 reactos/win32ss/user/winsrv/consrv_new/frontends/frontends.rc create mode 100644 reactos/win32ss/user/winsrv/consrv_new/frontends/gui/consolecpl.h create mode 100644 reactos/win32ss/user/winsrv/consrv_new/frontends/gui/graphics.c create mode 100644 reactos/win32ss/user/winsrv/consrv_new/frontends/gui/guisettings.c create mode 100644 reactos/win32ss/user/winsrv/consrv_new/frontends/gui/guisettings.h create mode 100644 reactos/win32ss/user/winsrv/consrv_new/frontends/gui/guiterm.c create mode 100644 reactos/win32ss/user/winsrv/consrv_new/frontends/gui/guiterm.h create mode 100644 reactos/win32ss/user/winsrv/consrv_new/frontends/gui/guiterm.rc create mode 100644 reactos/win32ss/user/winsrv/consrv_new/frontends/gui/lang/bg-BG.rc create mode 100644 reactos/win32ss/user/winsrv/consrv_new/frontends/gui/lang/cs-CZ.rc create mode 100644 reactos/win32ss/user/winsrv/consrv_new/frontends/gui/lang/de-DE.rc create mode 100644 reactos/win32ss/user/winsrv/consrv_new/frontends/gui/lang/el-GR.rc create mode 100644 reactos/win32ss/user/winsrv/consrv_new/frontends/gui/lang/en-US.rc create mode 100644 reactos/win32ss/user/winsrv/consrv_new/frontends/gui/lang/es-ES.rc create mode 100644 reactos/win32ss/user/winsrv/consrv_new/frontends/gui/lang/fr-FR.rc create mode 100644 reactos/win32ss/user/winsrv/consrv_new/frontends/gui/lang/he-IL.rc create mode 100644 reactos/win32ss/user/winsrv/consrv_new/frontends/gui/lang/id-ID.rc create mode 100644 reactos/win32ss/user/winsrv/consrv_new/frontends/gui/lang/it-IT.rc create mode 100644 reactos/win32ss/user/winsrv/consrv_new/frontends/gui/lang/ja-JP.rc create mode 100644 reactos/win32ss/user/winsrv/consrv_new/frontends/gui/lang/no-NO.rc create mode 100644 reactos/win32ss/user/winsrv/consrv_new/frontends/gui/lang/pl-PL.rc create mode 100644 reactos/win32ss/user/winsrv/consrv_new/frontends/gui/lang/pt-BR.rc create mode 100644 reactos/win32ss/user/winsrv/consrv_new/frontends/gui/lang/ro-RO.rc create mode 100644 reactos/win32ss/user/winsrv/consrv_new/frontends/gui/lang/ru-RU.rc create mode 100644 reactos/win32ss/user/winsrv/consrv_new/frontends/gui/lang/sk-SK.rc create mode 100644 reactos/win32ss/user/winsrv/consrv_new/frontends/gui/lang/sv-SE.rc create mode 100644 reactos/win32ss/user/winsrv/consrv_new/frontends/gui/lang/tr-TR.rc create mode 100644 reactos/win32ss/user/winsrv/consrv_new/frontends/gui/lang/uk-UA.rc create mode 100644 reactos/win32ss/user/winsrv/consrv_new/frontends/gui/lang/zh-CN.rc create mode 100644 reactos/win32ss/user/winsrv/consrv_new/frontends/gui/lang/zh-TW.rc create mode 100644 reactos/win32ss/user/winsrv/consrv_new/frontends/gui/res/terminal.ico create mode 100644 reactos/win32ss/user/winsrv/consrv_new/frontends/gui/resource.h create mode 100644 reactos/win32ss/user/winsrv/consrv_new/frontends/gui/rsrc.rc create mode 100644 reactos/win32ss/user/winsrv/consrv_new/frontends/gui/text.c create mode 100644 reactos/win32ss/user/winsrv/consrv_new/frontends/input.c create mode 100644 reactos/win32ss/user/winsrv/consrv_new/frontends/tui/tuiterm.c create mode 100644 reactos/win32ss/user/winsrv/consrv_new/frontends/tui/tuiterm.h create mode 100644 reactos/win32ss/user/winsrv/consrv_new/handle.c create mode 100644 reactos/win32ss/user/winsrv/consrv_new/handle.h create mode 100644 reactos/win32ss/user/winsrv/consrv_new/heap.h create mode 100644 reactos/win32ss/user/winsrv/consrv_new/include/conio.h create mode 100644 reactos/win32ss/user/winsrv/consrv_new/include/conio2.h create mode 100644 reactos/win32ss/user/winsrv/consrv_new/include/console.h create mode 100644 reactos/win32ss/user/winsrv/consrv_new/include/settings.h create mode 100644 reactos/win32ss/user/winsrv/consrv_new/init.c create mode 100644 reactos/win32ss/user/winsrv/consrv_new/lang/bg-BG.rc create mode 100644 reactos/win32ss/user/winsrv/consrv_new/lang/cs-CZ.rc create mode 100644 reactos/win32ss/user/winsrv/consrv_new/lang/de-DE.rc create mode 100644 reactos/win32ss/user/winsrv/consrv_new/lang/el-GR.rc create mode 100644 reactos/win32ss/user/winsrv/consrv_new/lang/en-US.rc create mode 100644 reactos/win32ss/user/winsrv/consrv_new/lang/es-ES.rc create mode 100644 reactos/win32ss/user/winsrv/consrv_new/lang/fr-FR.rc create mode 100644 reactos/win32ss/user/winsrv/consrv_new/lang/he-IL.rc create mode 100644 reactos/win32ss/user/winsrv/consrv_new/lang/id-ID.rc create mode 100644 reactos/win32ss/user/winsrv/consrv_new/lang/it-IT.rc create mode 100644 reactos/win32ss/user/winsrv/consrv_new/lang/ja-JP.rc create mode 100644 reactos/win32ss/user/winsrv/consrv_new/lang/no-NO.rc create mode 100644 reactos/win32ss/user/winsrv/consrv_new/lang/pl-PL.rc create mode 100644 reactos/win32ss/user/winsrv/consrv_new/lang/pt-BR.rc create mode 100644 reactos/win32ss/user/winsrv/consrv_new/lang/ro-RO.rc create mode 100644 reactos/win32ss/user/winsrv/consrv_new/lang/ru-RU.rc create mode 100644 reactos/win32ss/user/winsrv/consrv_new/lang/sk-SK.rc create mode 100644 reactos/win32ss/user/winsrv/consrv_new/lang/sv-SE.rc create mode 100644 reactos/win32ss/user/winsrv/consrv_new/lang/tr-TR.rc create mode 100644 reactos/win32ss/user/winsrv/consrv_new/lang/uk-UA.rc create mode 100644 reactos/win32ss/user/winsrv/consrv_new/lang/zh-CN.rc create mode 100644 reactos/win32ss/user/winsrv/consrv_new/lang/zh-TW.rc create mode 100644 reactos/win32ss/user/winsrv/consrv_new/lineinput.c create mode 100644 reactos/win32ss/user/winsrv/consrv_new/lineinput.h create mode 100644 reactos/win32ss/user/winsrv/consrv_new/procinit.h create mode 100644 reactos/win32ss/user/winsrv/consrv_new/resource.h create mode 100644 reactos/win32ss/user/winsrv/consrv_new/rsrc.rc create mode 100644 reactos/win32ss/user/winsrv/consrv_new/settings.c 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 0000000000000000000000000000000000000000..7e3e77d2927643b84087068140f60632667bfa3d GIT binary patch literal 236566 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 VVJwmZi1>F$UX5 + + +/* 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 */