e0ecef01d385dbfe3cb153d53512f0d49e50fd58
[reactos.git] / win32ss / user / winsrv / consrv / frontends / tui / tuiterm.c
1 /*
2 * COPYRIGHT: See COPYING in the top level directory
3 * PROJECT: ReactOS Console Server DLL
4 * FILE: win32ss/user/winsrv/consrv/frontends/tui/tuiterm.c
5 * PURPOSE: TUI Terminal Front-End - Virtual Consoles...
6 * PROGRAMMERS: David Welch
7 * Gé van Geldorp
8 * Jeffrey Morlan
9 * Hermes Belusca-Maito (hermes.belusca@sfr.fr)
10 */
11
12 #ifdef TUITERM_COMPILE
13
14 #include <consrv.h>
15
16 // #include "include/conio.h"
17 #include "include/console.h"
18 #include "include/settings.h"
19 #include "tuiterm.h"
20
21 #include <ndk/iofuncs.h>
22 #include <ndk/setypes.h>
23 #include <drivers/blue/ntddblue.h>
24
25 #define NDEBUG
26 #include <debug.h>
27
28
29 /* CAB FILE STRUCTURES ******************************************************/
30
31 typedef struct _CFHEADER
32 {
33 ULONG Signature; // File signature 'MSCF' (CAB_SIGNATURE)
34 ULONG Reserved1; // Reserved field
35 ULONG CabinetSize; // Cabinet file size
36 ULONG Reserved2; // Reserved field
37 ULONG FileTableOffset; // Offset of first CFFILE
38 ULONG Reserved3; // Reserved field
39 USHORT Version; // Cabinet version (CAB_VERSION)
40 USHORT FolderCount; // Number of folders
41 USHORT FileCount; // Number of files
42 USHORT Flags; // Cabinet flags (CAB_FLAG_*)
43 USHORT SetID; // Cabinet set id
44 USHORT CabinetNumber; // Zero-based cabinet number
45 } CFHEADER, *PCFHEADER;
46
47 typedef struct _CFFILE
48 {
49 ULONG FileSize; // Uncompressed file size in bytes
50 ULONG FileOffset; // Uncompressed offset of file in the folder
51 USHORT FileControlID; // File control ID (CAB_FILE_*)
52 USHORT FileDate; // File date stamp, as used by DOS
53 USHORT FileTime; // File time stamp, as used by DOS
54 USHORT Attributes; // File attributes (CAB_ATTRIB_*)
55 /* After this is the NULL terminated filename */
56 // CHAR FileName[ANYSIZE_ARRAY];
57 } CFFILE, *PCFFILE;
58
59 #define CAB_SIGNATURE 0x4643534D // "MSCF"
60 #define CAB_VERSION 0x0103
61
62
63 /* GLOBALS ******************************************************************/
64
65 #define ConsoleOutputUnicodeToAnsiChar(Console, dChar, sWChar) \
66 do { \
67 ASSERT((ULONG_PTR)(dChar) != (ULONG_PTR)(sWChar)); \
68 WideCharToMultiByte((Console)->OutputCodePage, 0, (sWChar), 1, (dChar), 1, NULL, NULL); \
69 } while (0)
70
71 /* TUI Console Window Class name */
72 #define TUI_CONSOLE_WINDOW_CLASS L"TuiConsoleWindowClass"
73
74 typedef struct _TUI_CONSOLE_DATA
75 {
76 CRITICAL_SECTION Lock;
77 LIST_ENTRY Entry; /* Entry in the list of virtual consoles */
78 // HANDLE hTuiInitEvent;
79 // HANDLE hTuiTermEvent;
80
81 HWND hWindow; /* Handle to the console's window (used for the window's procedure) */
82
83 PCONSRV_CONSOLE Console; /* Pointer to the owned console */
84 PCONSOLE_SCREEN_BUFFER ActiveBuffer; /* Pointer to the active screen buffer (then maybe the previous Console member is redundant?? Or not...) */
85 // TUI_CONSOLE_INFO TuiInfo; /* TUI terminal settings */
86 } TUI_CONSOLE_DATA, *PTUI_CONSOLE_DATA;
87
88 #define GetNextConsole(Console) \
89 CONTAINING_RECORD(Console->Entry.Flink, TUI_CONSOLE_DATA, Entry)
90
91 #define GetPrevConsole(Console) \
92 CONTAINING_RECORD(Console->Entry.Blink, TUI_CONSOLE_DATA, Entry)
93
94
95 /* List of the maintained virtual consoles and its lock */
96 static LIST_ENTRY VirtConsList;
97 static PTUI_CONSOLE_DATA ActiveConsole; /* The active console on screen */
98 static CRITICAL_SECTION ActiveVirtConsLock;
99
100 static COORD PhysicalConsoleSize;
101 static HANDLE ConsoleDeviceHandle;
102
103 static BOOL ConsInitialized = FALSE;
104
105 /******************************************************************************\
106 |** BlueScreen Driver management **|
107 \**/
108 /* Code taken and adapted from base/system/services/driver.c */
109 static DWORD
110 ScmLoadDriver(LPCWSTR lpServiceName)
111 {
112 NTSTATUS Status = STATUS_SUCCESS;
113 BOOLEAN WasPrivilegeEnabled = FALSE;
114 PWSTR pszDriverPath;
115 UNICODE_STRING DriverPath;
116
117 /* Build the driver path */
118 /* 52 = wcslen(L"\\Registry\\Machine\\System\\CurrentControlSet\\Services\\") */
119 pszDriverPath = ConsoleAllocHeap(HEAP_ZERO_MEMORY,
120 (52 + wcslen(lpServiceName) + 1) * sizeof(WCHAR));
121 if (pszDriverPath == NULL)
122 return ERROR_NOT_ENOUGH_MEMORY;
123
124 wcscpy(pszDriverPath,
125 L"\\Registry\\Machine\\System\\CurrentControlSet\\Services\\");
126 wcscat(pszDriverPath,
127 lpServiceName);
128
129 RtlInitUnicodeString(&DriverPath,
130 pszDriverPath);
131
132 DPRINT(" Path: %wZ\n", &DriverPath);
133
134 /* Acquire driver-loading privilege */
135 Status = RtlAdjustPrivilege(SE_LOAD_DRIVER_PRIVILEGE,
136 TRUE,
137 FALSE,
138 &WasPrivilegeEnabled);
139 if (!NT_SUCCESS(Status))
140 {
141 /* We encountered a failure, exit properly */
142 DPRINT1("CONSRV: Cannot acquire driver-loading privilege, Status = 0x%08lx\n", Status);
143 goto done;
144 }
145
146 Status = NtLoadDriver(&DriverPath);
147
148 /* Release driver-loading privilege */
149 RtlAdjustPrivilege(SE_LOAD_DRIVER_PRIVILEGE,
150 WasPrivilegeEnabled,
151 FALSE,
152 &WasPrivilegeEnabled);
153
154 done:
155 ConsoleFreeHeap(pszDriverPath);
156 return RtlNtStatusToDosError(Status);
157 }
158
159 #ifdef BLUESCREEN_DRIVER_UNLOADING
160 static DWORD
161 ScmUnloadDriver(LPCWSTR lpServiceName)
162 {
163 NTSTATUS Status = STATUS_SUCCESS;
164 BOOLEAN WasPrivilegeEnabled = FALSE;
165 PWSTR pszDriverPath;
166 UNICODE_STRING DriverPath;
167
168 /* Build the driver path */
169 /* 52 = wcslen(L"\\Registry\\Machine\\System\\CurrentControlSet\\Services\\") */
170 pszDriverPath = ConsoleAllocHeap(HEAP_ZERO_MEMORY,
171 (52 + wcslen(lpServiceName) + 1) * sizeof(WCHAR));
172 if (pszDriverPath == NULL)
173 return ERROR_NOT_ENOUGH_MEMORY;
174
175 wcscpy(pszDriverPath,
176 L"\\Registry\\Machine\\System\\CurrentControlSet\\Services\\");
177 wcscat(pszDriverPath,
178 lpServiceName);
179
180 RtlInitUnicodeString(&DriverPath,
181 pszDriverPath);
182
183 DPRINT(" Path: %wZ\n", &DriverPath);
184
185 /* Acquire driver-unloading privilege */
186 Status = RtlAdjustPrivilege(SE_LOAD_DRIVER_PRIVILEGE,
187 TRUE,
188 FALSE,
189 &WasPrivilegeEnabled);
190 if (!NT_SUCCESS(Status))
191 {
192 /* We encountered a failure, exit properly */
193 DPRINT1("CONSRV: Cannot acquire driver-unloading privilege, Status = 0x%08lx\n", Status);
194 goto done;
195 }
196
197 Status = NtUnloadDriver(&DriverPath);
198
199 /* Release driver-unloading privilege */
200 RtlAdjustPrivilege(SE_LOAD_DRIVER_PRIVILEGE,
201 WasPrivilegeEnabled,
202 FALSE,
203 &WasPrivilegeEnabled);
204
205 done:
206 ConsoleFreeHeap(pszDriverPath);
207 return RtlNtStatusToDosError(Status);
208 }
209 #endif
210 /**\
211 \******************************************************************************/
212
213 #if 0
214 static BOOL
215 TuiSwapConsole(INT Next)
216 {
217 static PTUI_CONSOLE_DATA SwapConsole = NULL; /* Console we are thinking about swapping with */
218 DWORD BytesReturned;
219 ANSI_STRING Title;
220 PVOID Buffer;
221 PCOORD pos;
222
223 if (0 != Next)
224 {
225 /*
226 * Alt-Tab, swap consoles.
227 * move SwapConsole to next console, and print its title.
228 */
229 EnterCriticalSection(&ActiveVirtConsLock);
230 if (!SwapConsole) SwapConsole = ActiveConsole;
231
232 SwapConsole = (0 < Next ? GetNextConsole(SwapConsole) : GetPrevConsole(SwapConsole));
233 Title.MaximumLength = RtlUnicodeStringToAnsiSize(&SwapConsole->Console->Title);
234 Title.Length = 0;
235 Buffer = ConsoleAllocHeap(0, sizeof(COORD) + Title.MaximumLength);
236 pos = (PCOORD)Buffer;
237 Title.Buffer = (PVOID)((ULONG_PTR)Buffer + sizeof(COORD));
238
239 RtlUnicodeStringToAnsiString(&Title, &SwapConsole->Console->Title, FALSE);
240 pos->X = (PhysicalConsoleSize.X - Title.Length) / 2;
241 pos->Y = PhysicalConsoleSize.Y / 2;
242 /* Redraw the console to clear off old title */
243 ConioDrawConsole(ActiveConsole->Console);
244 if (!DeviceIoControl(ConsoleDeviceHandle, IOCTL_CONSOLE_WRITE_OUTPUT_CHARACTER,
245 NULL, 0, Buffer, sizeof(COORD) + Title.Length,
246 &BytesReturned, NULL))
247 {
248 DPRINT1( "Error writing to console\n" );
249 }
250 ConsoleFreeHeap(Buffer);
251 LeaveCriticalSection(&ActiveVirtConsLock);
252
253 return TRUE;
254 }
255 else if (NULL != SwapConsole)
256 {
257 EnterCriticalSection(&ActiveVirtConsLock);
258 if (SwapConsole != ActiveConsole)
259 {
260 /* First remove swapconsole from the list */
261 SwapConsole->Entry.Blink->Flink = SwapConsole->Entry.Flink;
262 SwapConsole->Entry.Flink->Blink = SwapConsole->Entry.Blink;
263 /* Now insert before activeconsole */
264 SwapConsole->Entry.Flink = &ActiveConsole->Entry;
265 SwapConsole->Entry.Blink = ActiveConsole->Entry.Blink;
266 ActiveConsole->Entry.Blink->Flink = &SwapConsole->Entry;
267 ActiveConsole->Entry.Blink = &SwapConsole->Entry;
268 }
269 ActiveConsole = SwapConsole;
270 SwapConsole = NULL;
271 ConioDrawConsole(ActiveConsole->Console);
272 LeaveCriticalSection(&ActiveVirtConsLock);
273 return TRUE;
274 }
275 else
276 {
277 return FALSE;
278 }
279 }
280 #endif
281
282 static VOID
283 TuiCopyRect(PCHAR Dest, PTEXTMODE_SCREEN_BUFFER Buff, SMALL_RECT* Region)
284 {
285 UINT SrcDelta, DestDelta;
286 LONG i;
287 PCHAR_INFO Src, SrcEnd;
288
289 Src = ConioCoordToPointer(Buff, Region->Left, Region->Top);
290 SrcDelta = Buff->ScreenBufferSize.X * sizeof(CHAR_INFO);
291 SrcEnd = Buff->Buffer + Buff->ScreenBufferSize.Y * Buff->ScreenBufferSize.X * sizeof(CHAR_INFO);
292 DestDelta = ConioRectWidth(Region) * 2 /* 2 == sizeof(CHAR) + sizeof(BYTE) */;
293 for (i = Region->Top; i <= Region->Bottom; i++)
294 {
295 ConsoleOutputUnicodeToAnsiChar(Buff->Header.Console, (PCHAR)Dest, &Src->Char.UnicodeChar);
296 *(PBYTE)(Dest + 1) = (BYTE)Src->Attributes;
297
298 Src += SrcDelta;
299 if (SrcEnd <= Src)
300 {
301 Src -= Buff->ScreenBufferSize.Y * Buff->ScreenBufferSize.X * sizeof(CHAR_INFO);
302 }
303 Dest += DestDelta;
304 }
305 }
306
307 static LRESULT CALLBACK
308 TuiConsoleWndProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam)
309 {
310 /*
311 PTUI_CONSOLE_DATA TuiData = NULL;
312 PCONSRV_CONSOLE Console = NULL;
313
314 TuiData = TuiGetGuiData(hWnd);
315 if (TuiData == NULL) return 0;
316 */
317
318 switch (msg)
319 {
320 case WM_CHAR:
321 case WM_SYSCHAR:
322 case WM_KEYDOWN:
323 case WM_SYSKEYDOWN:
324 case WM_KEYUP:
325 case WM_SYSKEYUP:
326 {
327 #if 0
328 if ((HIWORD(lParam) & KF_ALTDOWN) && wParam == VK_TAB)
329 {
330 // if ((HIWORD(lParam) & (KF_UP | KF_REPEAT)) != KF_REPEAT)
331 TuiSwapConsole(ShiftState & SHIFT_PRESSED ? -1 : 1);
332
333 break;
334 }
335 else if (wParam == VK_MENU /* && !Down */)
336 {
337 TuiSwapConsole(0);
338 break;
339 }
340 #endif
341
342 if (ConDrvValidateConsoleUnsafe((PCONSOLE)ActiveConsole->Console, CONSOLE_RUNNING, TRUE))
343 {
344 MSG Message;
345 Message.hwnd = hWnd;
346 Message.message = msg;
347 Message.wParam = wParam;
348 Message.lParam = lParam;
349
350 ConioProcessKey(ActiveConsole->Console, &Message);
351 LeaveCriticalSection(&ActiveConsole->Console->Lock);
352 }
353 break;
354 }
355
356 case WM_ACTIVATE:
357 {
358 if (ConDrvValidateConsoleUnsafe((PCONSOLE)ActiveConsole->Console, CONSOLE_RUNNING, TRUE))
359 {
360 if (LOWORD(wParam) != WA_INACTIVE)
361 {
362 SetFocus(hWnd);
363 ConioDrawConsole(ActiveConsole->Console);
364 }
365 LeaveCriticalSection(&ActiveConsole->Console->Lock);
366 }
367 break;
368 }
369
370 default:
371 break;
372 }
373
374 return DefWindowProcW(hWnd, msg, wParam, lParam);
375 }
376
377 static DWORD NTAPI
378 TuiConsoleThread(PVOID Param)
379 {
380 PTUI_CONSOLE_DATA TuiData = (PTUI_CONSOLE_DATA)Param;
381 PCONSRV_CONSOLE Console = TuiData->Console;
382 HWND NewWindow;
383 MSG msg;
384
385 NewWindow = CreateWindowW(TUI_CONSOLE_WINDOW_CLASS,
386 Console->Title.Buffer,
387 0,
388 -32000, -32000, 0, 0,
389 NULL, NULL,
390 ConSrvDllInstance,
391 (PVOID)Console);
392 if (NULL == NewWindow)
393 {
394 DPRINT1("CONSRV: Unable to create console window\n");
395 return 1;
396 }
397 TuiData->hWindow = NewWindow;
398
399 SetForegroundWindow(TuiData->hWindow);
400 NtUserConsoleControl(ConsoleAcquireDisplayOwnership, NULL, 0);
401
402 while (GetMessageW(&msg, NULL, 0, 0))
403 {
404 TranslateMessage(&msg);
405 DispatchMessageW(&msg);
406 }
407
408 return 0;
409 }
410
411 static BOOLEAN
412 TuiSetConsoleOutputCP(
413 IN HANDLE hNtConddHandle,
414 IN UINT CodePage)
415 {
416 static UINT LastLoadedCodepage = 0;
417 UNICODE_STRING FontFile = RTL_CONSTANT_STRING(L"\\SystemRoot\\vgafonts.cab");
418 CHAR FontName[20];
419
420 NTSTATUS Status;
421 HANDLE FileHandle;
422 OBJECT_ATTRIBUTES ObjectAttributes;
423 IO_STATUS_BLOCK IoStatusBlock;
424 // ULONG ReadCP;
425 PUCHAR FontBitField = NULL;
426
427 /* CAB-specific data */
428 HANDLE FileSectionHandle;
429 PUCHAR FileBuffer = NULL;
430 SIZE_T FileSize = 0;
431 PCFHEADER CabFileHeader;
432 union
433 {
434 PCFFILE CabFile;
435 PVOID Buffer;
436 } Data;
437 PCFFILE FoundFile = NULL;
438 PSTR FileName;
439 USHORT Index;
440
441 if (CodePage == LastLoadedCodepage)
442 return TRUE;
443
444 /*
445 * Open the *uncompressed* fonts archive file.
446 */
447 InitializeObjectAttributes(&ObjectAttributes,
448 &FontFile,
449 OBJ_CASE_INSENSITIVE,
450 NULL,
451 NULL);
452
453 Status = NtOpenFile(&FileHandle,
454 GENERIC_READ | SYNCHRONIZE,
455 &ObjectAttributes,
456 &IoStatusBlock,
457 FILE_SHARE_READ,
458 FILE_SYNCHRONOUS_IO_NONALERT);
459 if (!NT_SUCCESS(Status))
460 {
461 DPRINT1("Error: Cannot open '%wZ' (0x%lx)\n", &FontFile, Status);
462 return FALSE;
463 }
464
465 /*
466 * Load it.
467 */
468 Status = NtCreateSection(&FileSectionHandle,
469 SECTION_ALL_ACCESS,
470 0, 0,
471 PAGE_READONLY,
472 SEC_COMMIT,
473 FileHandle);
474 if (!NT_SUCCESS(Status))
475 {
476 DPRINT1("NtCreateSection failed (0x%lx)\n", Status);
477 goto Exit;
478 }
479
480 Status = NtMapViewOfSection(FileSectionHandle,
481 NtCurrentProcess(),
482 (PVOID*)&FileBuffer,
483 0, 0, NULL,
484 &FileSize,
485 ViewUnmap,
486 0,
487 PAGE_READONLY);
488 if (!NT_SUCCESS(Status))
489 {
490 DPRINT1("NtMapViewOfSection failed (0x%lx)\n", Status);
491 goto Exit;
492 }
493
494 /* Wrap in SEH to protect against ill-formed file */
495 _SEH2_TRY
496 {
497 DPRINT("Cabinet file '%wZ' opened and mapped to 0x%p\n",
498 &FontFile, FileBuffer);
499
500 CabFileHeader = (PCFHEADER)FileBuffer;
501
502 /* Validate the CAB file */
503 if (FileSize <= sizeof(CFHEADER) ||
504 CabFileHeader->Signature != CAB_SIGNATURE ||
505 CabFileHeader->Version != CAB_VERSION ||
506 CabFileHeader->FolderCount == 0 ||
507 CabFileHeader->FileCount == 0 ||
508 CabFileHeader->FileTableOffset < sizeof(CFHEADER))
509 {
510 DPRINT1("Cabinet file '%wZ' has an invalid header\n", &FontFile);
511 Status = STATUS_UNSUCCESSFUL;
512 _SEH2_YIELD(goto Exit);
513 }
514
515 /*
516 * Find the font file within the archive.
517 */
518 RtlStringCbPrintfA(FontName, sizeof(FontName),
519 "%u-8x8.bin", CodePage);
520
521 /* Read the file table, find the file of interest and the end of the table */
522 Data.CabFile = (PCFFILE)(FileBuffer + CabFileHeader->FileTableOffset);
523 for (Index = 0; Index < CabFileHeader->FileCount; ++Index)
524 {
525 FileName = (PSTR)(Data.CabFile + 1);
526
527 if (!FoundFile)
528 {
529 // Status = RtlCharToInteger(FileName, 0, &ReadCP);
530 // if (NT_SUCCESS(Status) && (ReadCP == CodePage))
531 if (_stricmp(FontName, FileName) == 0)
532 {
533 /* We've got the correct file. Save the offset and
534 * loop through the rest of the file table to find
535 * the position, where the actual data starts. */
536 FoundFile = Data.CabFile;
537 }
538 }
539
540 /* Move to the next file (go past the filename NULL terminator) */
541 Data.CabFile = (PCFFILE)(strchr(FileName, 0) + 1);
542 }
543
544 if (!FoundFile)
545 {
546 DPRINT("File '%S' not found in cabinet '%wZ'\n",
547 FontName, &FontFile);
548 Status = STATUS_OBJECT_NAME_NOT_FOUND;
549 _SEH2_YIELD(goto Exit);
550 }
551
552 /*
553 * Extract the font file.
554 */
555 /* Verify the font file size; we only support a fixed 256-char 8-bit font */
556 if (FoundFile->FileSize != 256 * 8)
557 {
558 DPRINT1("File of size %lu is not of the expected size %lu\n",
559 FoundFile->FileSize, 256 * 8);
560 Status = STATUS_INVALID_BUFFER_SIZE;
561 _SEH2_YIELD(goto Exit);
562 }
563
564 FontBitField = RtlAllocateHeap(RtlGetProcessHeap(), 0, FoundFile->FileSize);
565 if (!FontBitField)
566 {
567 DPRINT1("ExAllocatePoolWithTag(%lu) failed\n", FoundFile->FileSize);
568 Status = STATUS_NO_MEMORY;
569 _SEH2_YIELD(goto Exit);
570 }
571
572 /* 8 = Size of a CFFOLDER structure (see cabman). As we don't need
573 * the values of that structure, just increase the offset here. */
574 Data.Buffer = (PVOID)((ULONG_PTR)Data.Buffer + 8); // sizeof(CFFOLDER);
575 Data.Buffer = (PVOID)((ULONG_PTR)Data.Buffer + FoundFile->FileOffset);
576
577 /* Data.Buffer now points to the actual data of the RAW font */
578 RtlCopyMemory(FontBitField, Data.Buffer, FoundFile->FileSize);
579 Status = STATUS_SUCCESS;
580 }
581 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
582 {
583 Status = _SEH2_GetExceptionCode();
584 DPRINT1("TuiSetConsoleOutputCP - Caught an exception, Status = 0x%08lx\n", Status);
585 }
586 _SEH2_END;
587
588 /*
589 * Load the font.
590 */
591 if (NT_SUCCESS(Status))
592 {
593 ASSERT(FoundFile);
594 ASSERT(FontBitField);
595 Status = NtDeviceIoControlFile(hNtConddHandle,
596 NULL,
597 NULL,
598 NULL,
599 &IoStatusBlock,
600 IOCTL_CONSOLE_LOADFONT,
601 FontBitField,
602 FoundFile->FileSize,
603 NULL,
604 0);
605 }
606
607 if (FontBitField)
608 RtlFreeHeap(RtlGetProcessHeap(), 0, FontBitField);
609
610 Exit:
611 if (FileBuffer)
612 NtUnmapViewOfSection(NtCurrentProcess(), FileBuffer);
613
614 if (FileSectionHandle)
615 NtClose(FileSectionHandle);
616
617 NtClose(FileHandle);
618
619 if (NT_SUCCESS(Status))
620 LastLoadedCodepage = CodePage;
621
622 return NT_SUCCESS(Status);
623 }
624
625 static BOOL
626 TuiInit(IN UINT OemCP)
627 {
628 BOOL Success;
629 CONSOLE_SCREEN_BUFFER_INFO ScrInfo;
630 DWORD BytesReturned;
631 WNDCLASSEXW wc;
632 ATOM ConsoleClassAtom;
633 USHORT TextAttribute = FOREGROUND_BLUE | FOREGROUND_GREEN | FOREGROUND_RED;
634
635 /* Exit if we were already initialized */
636 if (ConsInitialized) return TRUE;
637
638 /*
639 * Initialize the TUI front-end:
640 * - load the console driver,
641 * - open BlueScreen device and enable it,
642 * - set default screen attributes,
643 * - grab the console size.
644 */
645 ScmLoadDriver(L"Blue");
646
647 ConsoleDeviceHandle = CreateFileW(L"\\\\.\\BlueScreen",
648 FILE_ALL_ACCESS,
649 0, NULL,
650 OPEN_EXISTING,
651 0, NULL);
652 if (ConsoleDeviceHandle == INVALID_HANDLE_VALUE)
653 {
654 DPRINT1("Failed to open BlueScreen.\n");
655 return FALSE;
656 }
657
658 Success = TRUE;
659 if (!DeviceIoControl(ConsoleDeviceHandle, IOCTL_CONSOLE_RESET_SCREEN,
660 &Success, sizeof(Success), NULL, 0,
661 &BytesReturned, NULL))
662 {
663 DPRINT1("Failed to enable the screen.\n");
664 CloseHandle(ConsoleDeviceHandle);
665 return FALSE;
666 }
667
668 if (!TuiSetConsoleOutputCP(ConsoleDeviceHandle, OemCP))
669 {
670 DPRINT1("Failed to load the font for codepage %d\n", OemCP);
671 /* Let's suppose the font is good enough to continue */
672 }
673
674 if (!DeviceIoControl(ConsoleDeviceHandle, IOCTL_CONSOLE_SET_TEXT_ATTRIBUTE,
675 &TextAttribute, sizeof(TextAttribute), NULL, 0,
676 &BytesReturned, NULL))
677 {
678 DPRINT1("Failed to set text attribute.\n");
679 }
680
681 ActiveConsole = NULL;
682 InitializeListHead(&VirtConsList);
683 InitializeCriticalSection(&ActiveVirtConsLock);
684
685 if (!DeviceIoControl(ConsoleDeviceHandle, IOCTL_CONSOLE_GET_SCREEN_BUFFER_INFO,
686 NULL, 0, &ScrInfo, sizeof(ScrInfo), &BytesReturned, NULL))
687 {
688 DPRINT1("Failed to get console info.\n");
689 Success = FALSE;
690 goto Quit;
691 }
692 PhysicalConsoleSize = ScrInfo.dwSize;
693
694 /* Register the TUI notification window class */
695 RtlZeroMemory(&wc, sizeof(WNDCLASSEXW));
696 wc.cbSize = sizeof(WNDCLASSEXW);
697 wc.lpszClassName = TUI_CONSOLE_WINDOW_CLASS;
698 wc.lpfnWndProc = TuiConsoleWndProc;
699 wc.cbWndExtra = 0;
700 wc.hInstance = ConSrvDllInstance;
701
702 ConsoleClassAtom = RegisterClassExW(&wc);
703 if (ConsoleClassAtom == 0)
704 {
705 DPRINT1("Failed to register TUI console wndproc.\n");
706 Success = FALSE;
707 }
708 else
709 {
710 Success = TRUE;
711 }
712
713 Quit:
714 if (!Success)
715 {
716 DeleteCriticalSection(&ActiveVirtConsLock);
717 CloseHandle(ConsoleDeviceHandle);
718 }
719
720 ConsInitialized = Success;
721 return Success;
722 }
723
724
725
726 /******************************************************************************
727 * TUI Console Driver *
728 ******************************************************************************/
729
730 static VOID NTAPI
731 TuiDeinitFrontEnd(IN OUT PFRONTEND This /*,
732 IN PCONSRV_CONSOLE Console */);
733
734 static NTSTATUS NTAPI
735 TuiInitFrontEnd(IN OUT PFRONTEND This,
736 IN PCONSRV_CONSOLE Console)
737 {
738 PTUI_CONSOLE_DATA TuiData;
739 HANDLE ThreadHandle;
740
741 if (This == NULL || Console == NULL)
742 return STATUS_INVALID_PARAMETER;
743
744 if (GetType(Console->ActiveBuffer) != TEXTMODE_BUFFER)
745 return STATUS_INVALID_PARAMETER;
746
747 TuiData = ConsoleAllocHeap(HEAP_ZERO_MEMORY, sizeof(TUI_CONSOLE_DATA));
748 if (!TuiData)
749 {
750 DPRINT1("CONSRV: Failed to create TUI_CONSOLE_DATA\n");
751 return STATUS_UNSUCCESSFUL;
752 }
753 // Console->FrontEndIFace.Context = (PVOID)TuiData;
754 TuiData->Console = Console;
755 TuiData->ActiveBuffer = Console->ActiveBuffer;
756 TuiData->hWindow = NULL;
757
758 InitializeCriticalSection(&TuiData->Lock);
759
760 /*
761 * HACK: Resize the console since we don't support for now changing
762 * the console size when we display it with the hardware.
763 */
764 // Console->ConsoleSize = PhysicalConsoleSize;
765 // ConioResizeBuffer(Console, (PTEXTMODE_SCREEN_BUFFER)(Console->ActiveBuffer), PhysicalConsoleSize);
766
767 // /* The console cannot be resized anymore */
768 // Console->FixedSize = TRUE; // MUST be placed AFTER the call to ConioResizeBuffer !!
769 // // TermResizeTerminal(Console);
770
771 /*
772 * Contrary to what we do in the GUI front-end, here we create
773 * an input thread for each console. It will dispatch all the
774 * input messages to the proper console (on the GUI it is done
775 * via the default GUI dispatch thread).
776 */
777 ThreadHandle = CreateThread(NULL,
778 0,
779 TuiConsoleThread,
780 (PVOID)TuiData,
781 0,
782 NULL);
783 if (NULL == ThreadHandle)
784 {
785 DPRINT1("CONSRV: Unable to create console thread\n");
786 // TuiDeinitFrontEnd(Console);
787 TuiDeinitFrontEnd(This);
788 return STATUS_UNSUCCESSFUL;
789 }
790 CloseHandle(ThreadHandle);
791
792 /*
793 * Insert the newly created console in the list of virtual consoles
794 * and activate it (give it the focus).
795 */
796 EnterCriticalSection(&ActiveVirtConsLock);
797 InsertTailList(&VirtConsList, &TuiData->Entry);
798 ActiveConsole = TuiData;
799 LeaveCriticalSection(&ActiveVirtConsLock);
800
801 /* Finally, initialize the frontend structure */
802 This->Context = TuiData;
803 This->Context2 = NULL;
804
805 return STATUS_SUCCESS;
806 }
807
808 static VOID NTAPI
809 TuiDeinitFrontEnd(IN OUT PFRONTEND This)
810 {
811 // PCONSRV_CONSOLE Console = This->Console;
812 PTUI_CONSOLE_DATA TuiData = This->Context;
813
814 /* Close the notification window */
815 DestroyWindow(TuiData->hWindow);
816
817 /*
818 * Set the active console to the next one
819 * and remove the console from the list.
820 */
821 EnterCriticalSection(&ActiveVirtConsLock);
822 ActiveConsole = GetNextConsole(TuiData);
823 RemoveEntryList(&TuiData->Entry);
824
825 // /* Switch to next console */
826 // if (ActiveConsole == TuiData)
827 // if (ActiveConsole->Console == Console)
828 // {
829 // ActiveConsole = (TuiData->Entry.Flink != TuiData->Entry ? GetNextConsole(TuiData) : NULL);
830 // }
831
832 // if (GetNextConsole(TuiData) != TuiData)
833 // {
834 // TuiData->Entry.Blink->Flink = TuiData->Entry.Flink;
835 // TuiData->Entry.Flink->Blink = TuiData->Entry.Blink;
836 // }
837
838 LeaveCriticalSection(&ActiveVirtConsLock);
839
840 /* Switch to the next console */
841 if (NULL != ActiveConsole) ConioDrawConsole(ActiveConsole->Console);
842
843 This->Context = NULL;
844 DeleteCriticalSection(&TuiData->Lock);
845 ConsoleFreeHeap(TuiData);
846 }
847
848 static VOID NTAPI
849 TuiDrawRegion(IN OUT PFRONTEND This,
850 SMALL_RECT* Region)
851 {
852 PTUI_CONSOLE_DATA TuiData = This->Context;
853 PCONSOLE_SCREEN_BUFFER Buff = TuiData->Console->ActiveBuffer;
854 PCONSOLE_DRAW ConsoleDraw;
855 DWORD BytesReturned;
856 UINT ConsoleDrawSize;
857
858 if (TuiData != ActiveConsole) return;
859 if (GetType(Buff) != TEXTMODE_BUFFER) return;
860
861 ConsoleDrawSize = sizeof(CONSOLE_DRAW) +
862 (ConioRectWidth(Region) * ConioRectHeight(Region)) * 2;
863 ConsoleDraw = ConsoleAllocHeap(0, ConsoleDrawSize);
864 if (NULL == ConsoleDraw)
865 {
866 DPRINT1("ConsoleAllocHeap failed\n");
867 return;
868 }
869 ConsoleDraw->X = Region->Left;
870 ConsoleDraw->Y = Region->Top;
871 ConsoleDraw->SizeX = ConioRectWidth(Region);
872 ConsoleDraw->SizeY = ConioRectHeight(Region);
873 ConsoleDraw->CursorX = Buff->CursorPosition.X;
874 ConsoleDraw->CursorY = Buff->CursorPosition.Y;
875
876 TuiCopyRect((PCHAR)(ConsoleDraw + 1), (PTEXTMODE_SCREEN_BUFFER)Buff, Region);
877
878 if (!DeviceIoControl(ConsoleDeviceHandle, IOCTL_CONSOLE_DRAW,
879 NULL, 0, ConsoleDraw, ConsoleDrawSize, &BytesReturned, NULL))
880 {
881 DPRINT1("Failed to draw console\n");
882 ConsoleFreeHeap(ConsoleDraw);
883 return;
884 }
885
886 ConsoleFreeHeap(ConsoleDraw);
887 }
888
889 static VOID NTAPI
890 TuiWriteStream(IN OUT PFRONTEND This,
891 SMALL_RECT* Region,
892 SHORT CursorStartX,
893 SHORT CursorStartY,
894 UINT ScrolledLines,
895 PWCHAR Buffer,
896 UINT Length)
897 {
898 PTUI_CONSOLE_DATA TuiData = This->Context;
899 PCONSOLE_SCREEN_BUFFER Buff = TuiData->Console->ActiveBuffer;
900 PCHAR NewBuffer;
901 ULONG NewLength;
902 DWORD BytesWritten;
903
904 if (TuiData != ActiveConsole) return;
905 if (GetType(Buff) != TEXTMODE_BUFFER) return;
906
907 NewLength = WideCharToMultiByte(TuiData->Console->OutputCodePage, 0,
908 Buffer, Length,
909 NULL, 0, NULL, NULL);
910 NewBuffer = RtlAllocateHeap(RtlGetProcessHeap(), 0, NewLength * sizeof(CHAR));
911 if (!NewBuffer) return;
912
913 WideCharToMultiByte(TuiData->Console->OutputCodePage, 0,
914 Buffer, Length,
915 NewBuffer, NewLength, NULL, NULL);
916
917 if (!WriteFile(ConsoleDeviceHandle, NewBuffer, NewLength * sizeof(CHAR), &BytesWritten, NULL))
918 {
919 DPRINT1("Error writing to BlueScreen\n");
920 }
921
922 RtlFreeHeap(RtlGetProcessHeap(), 0, NewBuffer);
923 }
924
925 static VOID NTAPI
926 TuiRingBell(IN OUT PFRONTEND This)
927 {
928 Beep(800, 200);
929 }
930
931 static BOOL NTAPI
932 TuiSetCursorInfo(IN OUT PFRONTEND This,
933 PCONSOLE_SCREEN_BUFFER Buff)
934 {
935 PTUI_CONSOLE_DATA TuiData = This->Context;
936 CONSOLE_CURSOR_INFO Info;
937 DWORD BytesReturned;
938
939 if (TuiData != ActiveConsole) return TRUE;
940 if (TuiData->Console->ActiveBuffer != Buff) return TRUE;
941 if (GetType(Buff) != TEXTMODE_BUFFER) return FALSE;
942
943 Info.dwSize = ConioEffectiveCursorSize(TuiData->Console, 100);
944 Info.bVisible = Buff->CursorInfo.bVisible;
945
946 if (!DeviceIoControl(ConsoleDeviceHandle, IOCTL_CONSOLE_SET_CURSOR_INFO,
947 &Info, sizeof(Info), NULL, 0, &BytesReturned, NULL))
948 {
949 DPRINT1( "Failed to set cursor info\n" );
950 return FALSE;
951 }
952
953 return TRUE;
954 }
955
956 static BOOL NTAPI
957 TuiSetScreenInfo(IN OUT PFRONTEND This,
958 PCONSOLE_SCREEN_BUFFER Buff,
959 SHORT OldCursorX,
960 SHORT OldCursorY)
961 {
962 PTUI_CONSOLE_DATA TuiData = This->Context;
963 CONSOLE_SCREEN_BUFFER_INFO Info;
964 DWORD BytesReturned;
965
966 if (TuiData != ActiveConsole) return TRUE;
967 if (TuiData->Console->ActiveBuffer != Buff) return TRUE;
968 if (GetType(Buff) != TEXTMODE_BUFFER) return FALSE;
969
970 Info.dwCursorPosition = Buff->CursorPosition;
971 Info.wAttributes = ((PTEXTMODE_SCREEN_BUFFER)Buff)->ScreenDefaultAttrib;
972
973 if (!DeviceIoControl(ConsoleDeviceHandle, IOCTL_CONSOLE_SET_SCREEN_BUFFER_INFO,
974 &Info, sizeof(CONSOLE_SCREEN_BUFFER_INFO), NULL, 0,
975 &BytesReturned, NULL))
976 {
977 DPRINT1( "Failed to set cursor position\n" );
978 return FALSE;
979 }
980
981 return TRUE;
982 }
983
984 static VOID NTAPI
985 TuiResizeTerminal(IN OUT PFRONTEND This)
986 {
987 }
988
989 static VOID NTAPI
990 TuiSetActiveScreenBuffer(IN OUT PFRONTEND This)
991 {
992 // PGUI_CONSOLE_DATA GuiData = This->Context;
993 // PCONSOLE_SCREEN_BUFFER ActiveBuffer;
994 // HPALETTE hPalette;
995
996 // EnterCriticalSection(&GuiData->Lock);
997 // GuiData->WindowSizeLock = TRUE;
998
999 // InterlockedExchangePointer(&GuiData->ActiveBuffer,
1000 // ConDrvGetActiveScreenBuffer(GuiData->Console));
1001
1002 // GuiData->WindowSizeLock = FALSE;
1003 // LeaveCriticalSection(&GuiData->Lock);
1004
1005 // ActiveBuffer = GuiData->ActiveBuffer;
1006
1007 // /* Change the current palette */
1008 // if (ActiveBuffer->PaletteHandle == NULL)
1009 // {
1010 // hPalette = GuiData->hSysPalette;
1011 // }
1012 // else
1013 // {
1014 // hPalette = ActiveBuffer->PaletteHandle;
1015 // }
1016
1017 // DPRINT("GuiSetActiveScreenBuffer using palette 0x%p\n", hPalette);
1018
1019 // /* Set the new palette for the framebuffer */
1020 // SelectPalette(GuiData->hMemDC, hPalette, FALSE);
1021
1022 // /* Specify the use of the system palette for the framebuffer */
1023 // SetSystemPaletteUse(GuiData->hMemDC, ActiveBuffer->PaletteUsage);
1024
1025 // /* Realize the (logical) palette */
1026 // RealizePalette(GuiData->hMemDC);
1027
1028 // GuiResizeTerminal(This);
1029 // // ConioDrawConsole(Console);
1030 }
1031
1032 static VOID NTAPI
1033 TuiReleaseScreenBuffer(IN OUT PFRONTEND This,
1034 IN PCONSOLE_SCREEN_BUFFER ScreenBuffer)
1035 {
1036 // PGUI_CONSOLE_DATA GuiData = This->Context;
1037
1038 // /*
1039 // * If we were notified to release a screen buffer that is not actually
1040 // * ours, then just ignore the notification...
1041 // */
1042 // if (ScreenBuffer != GuiData->ActiveBuffer) return;
1043
1044 // /*
1045 // * ... else, we must release our active buffer. Two cases are present:
1046 // * - If ScreenBuffer (== GuiData->ActiveBuffer) IS NOT the console
1047 // * active screen buffer, then we can safely switch to it.
1048 // * - If ScreenBuffer IS the console active screen buffer, we must release
1049 // * it ONLY.
1050 // */
1051
1052 // /* Release the old active palette and set the default one */
1053 // if (GetCurrentObject(GuiData->hMemDC, OBJ_PAL) == ScreenBuffer->PaletteHandle)
1054 // {
1055 // /* Set the new palette */
1056 // SelectPalette(GuiData->hMemDC, GuiData->hSysPalette, FALSE);
1057 // }
1058
1059 // /* Set the adequate active screen buffer */
1060 // if (ScreenBuffer != GuiData->Console->ActiveBuffer)
1061 // {
1062 // GuiSetActiveScreenBuffer(This);
1063 // }
1064 // else
1065 // {
1066 // EnterCriticalSection(&GuiData->Lock);
1067 // GuiData->WindowSizeLock = TRUE;
1068
1069 // InterlockedExchangePointer(&GuiData->ActiveBuffer, NULL);
1070
1071 // GuiData->WindowSizeLock = FALSE;
1072 // LeaveCriticalSection(&GuiData->Lock);
1073 // }
1074 }
1075
1076 static VOID NTAPI
1077 TuiRefreshInternalInfo(IN OUT PFRONTEND This)
1078 {
1079 }
1080
1081 static VOID NTAPI
1082 TuiChangeTitle(IN OUT PFRONTEND This)
1083 {
1084 }
1085
1086 static BOOL NTAPI
1087 TuiChangeIcon(IN OUT PFRONTEND This,
1088 HICON IconHandle)
1089 {
1090 return TRUE;
1091 }
1092
1093 static HDESK NTAPI
1094 TuiGetThreadConsoleDesktop(IN OUT PFRONTEND This)
1095 {
1096 // PTUI_CONSOLE_DATA TuiData = This->Context;
1097 return NULL;
1098 }
1099
1100 static HWND NTAPI
1101 TuiGetConsoleWindowHandle(IN OUT PFRONTEND This)
1102 {
1103 PTUI_CONSOLE_DATA TuiData = This->Context;
1104 return TuiData->hWindow;
1105 }
1106
1107 static VOID NTAPI
1108 TuiGetLargestConsoleWindowSize(IN OUT PFRONTEND This,
1109 PCOORD pSize)
1110 {
1111 if (!pSize) return;
1112 *pSize = PhysicalConsoleSize;
1113 }
1114
1115 static BOOL NTAPI
1116 TuiGetSelectionInfo(IN OUT PFRONTEND This,
1117 PCONSOLE_SELECTION_INFO pSelectionInfo)
1118 {
1119 return TRUE;
1120 }
1121
1122 static BOOL NTAPI
1123 TuiSetPalette(IN OUT PFRONTEND This,
1124 HPALETTE PaletteHandle,
1125 UINT PaletteUsage)
1126 {
1127 return TRUE;
1128 }
1129
1130 static BOOL NTAPI
1131 TuiSetCodePage(IN OUT PFRONTEND This,
1132 UINT CodePage)
1133 {
1134 // PTUI_CONSOLE_DATA TuiData = This->Context;
1135
1136 // TODO: Verify that the console is the visible one.
1137 // Only then can we change the output code page font.
1138
1139 if (!TuiSetConsoleOutputCP(ConsoleDeviceHandle, CodePage))
1140 {
1141 DPRINT1("Failed to load the font for codepage %d\n", CodePage);
1142 /* Let's suppose the font is good enough to continue */
1143 return FALSE;
1144 }
1145
1146 return TRUE;
1147 }
1148
1149 static ULONG NTAPI
1150 TuiGetDisplayMode(IN OUT PFRONTEND This)
1151 {
1152 return CONSOLE_FULLSCREEN_HARDWARE; // CONSOLE_FULLSCREEN;
1153 }
1154
1155 static BOOL NTAPI
1156 TuiSetDisplayMode(IN OUT PFRONTEND This,
1157 ULONG NewMode)
1158 {
1159 // if (NewMode & ~(CONSOLE_FULLSCREEN_MODE | CONSOLE_WINDOWED_MODE))
1160 // return FALSE;
1161 return TRUE;
1162 }
1163
1164 static INT NTAPI
1165 TuiShowMouseCursor(IN OUT PFRONTEND This,
1166 BOOL Show)
1167 {
1168 return 0;
1169 }
1170
1171 static BOOL NTAPI
1172 TuiSetMouseCursor(IN OUT PFRONTEND This,
1173 HCURSOR CursorHandle)
1174 {
1175 return TRUE;
1176 }
1177
1178 static HMENU NTAPI
1179 TuiMenuControl(IN OUT PFRONTEND This,
1180 UINT CmdIdLow,
1181 UINT CmdIdHigh)
1182 {
1183 return NULL;
1184 }
1185
1186 static BOOL NTAPI
1187 TuiSetMenuClose(IN OUT PFRONTEND This,
1188 BOOL Enable)
1189 {
1190 return TRUE;
1191 }
1192
1193 static FRONTEND_VTBL TuiVtbl =
1194 {
1195 TuiInitFrontEnd,
1196 TuiDeinitFrontEnd,
1197 TuiDrawRegion,
1198 TuiWriteStream,
1199 TuiRingBell,
1200 TuiSetCursorInfo,
1201 TuiSetScreenInfo,
1202 TuiResizeTerminal,
1203 TuiSetActiveScreenBuffer,
1204 TuiReleaseScreenBuffer,
1205 TuiRefreshInternalInfo,
1206 TuiChangeTitle,
1207 TuiChangeIcon,
1208 TuiGetThreadConsoleDesktop,
1209 TuiGetConsoleWindowHandle,
1210 TuiGetLargestConsoleWindowSize,
1211 TuiGetSelectionInfo,
1212 TuiSetPalette,
1213 TuiSetCodePage,
1214 TuiGetDisplayMode,
1215 TuiSetDisplayMode,
1216 TuiShowMouseCursor,
1217 TuiSetMouseCursor,
1218 TuiMenuControl,
1219 TuiSetMenuClose,
1220 };
1221
1222 static BOOLEAN
1223 IsConsoleMode(VOID)
1224 {
1225 return (BOOLEAN)NtUserCallNoParam(NOPARAM_ROUTINE_ISCONSOLEMODE);
1226 }
1227
1228 NTSTATUS NTAPI
1229 TuiLoadFrontEnd(IN OUT PFRONTEND FrontEnd,
1230 IN OUT PCONSOLE_STATE_INFO ConsoleInfo,
1231 IN OUT PCONSOLE_INIT_INFO ConsoleInitInfo,
1232 IN HANDLE ConsoleLeaderProcessHandle)
1233 {
1234 if (FrontEnd == NULL || ConsoleInfo == NULL)
1235 return STATUS_INVALID_PARAMETER;
1236
1237 /* We must be in console mode already */
1238 if (!IsConsoleMode()) return STATUS_UNSUCCESSFUL;
1239
1240 /* Initialize the TUI terminal emulator */
1241 if (!TuiInit(ConsoleInfo->CodePage)) return STATUS_UNSUCCESSFUL;
1242
1243 /* Finally, initialize the frontend structure */
1244 FrontEnd->Vtbl = &TuiVtbl;
1245 FrontEnd->Context = NULL;
1246 FrontEnd->Context2 = NULL;
1247
1248 return STATUS_SUCCESS;
1249 }
1250
1251 NTSTATUS NTAPI
1252 TuiUnloadFrontEnd(IN OUT PFRONTEND FrontEnd)
1253 {
1254 if (FrontEnd == NULL) return STATUS_INVALID_PARAMETER;
1255 if (FrontEnd->Context) TuiDeinitFrontEnd(FrontEnd);
1256
1257 return STATUS_SUCCESS;
1258 }
1259
1260 #endif
1261
1262 /* EOF */