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 /* GLOBALS ********************************************************************/
30 
31 #define ConsoleOutputUnicodeToAnsiChar(Console, dChar, sWChar) \
32 do { \
33     ASSERT((ULONG_PTR)(dChar) != (ULONG_PTR)(sWChar)); \
34     WideCharToMultiByte((Console)->OutputCodePage, 0, (sWChar), 1, (dChar), 1, NULL, NULL); \
35 } while (0)
36 
37 /* TUI Console Window Class name */
38 #define TUI_CONSOLE_WINDOW_CLASS L"TuiConsoleWindowClass"
39 
40 typedef struct _TUI_CONSOLE_DATA
41 {
42     CRITICAL_SECTION Lock;
43     LIST_ENTRY Entry;           /* Entry in the list of virtual consoles */
44     // HANDLE hTuiInitEvent;
45     // HANDLE hTuiTermEvent;
46 
47     HWND hWindow;               /* Handle to the console's window (used for the window's procedure) */
48 
49     PCONSRV_CONSOLE Console;           /* Pointer to the owned console */
50     PCONSOLE_SCREEN_BUFFER ActiveBuffer;    /* Pointer to the active screen buffer (then maybe the previous Console member is redundant?? Or not...) */
51     // TUI_CONSOLE_INFO TuiInfo;   /* TUI terminal settings */
52 } TUI_CONSOLE_DATA, *PTUI_CONSOLE_DATA;
53 
54 #define GetNextConsole(Console) \
55     CONTAINING_RECORD(Console->Entry.Flink, TUI_CONSOLE_DATA, Entry)
56 
57 #define GetPrevConsole(Console) \
58     CONTAINING_RECORD(Console->Entry.Blink, TUI_CONSOLE_DATA, Entry)
59 
60 
61 /* List of the maintained virtual consoles and its lock */
62 static LIST_ENTRY VirtConsList;
63 static PTUI_CONSOLE_DATA ActiveConsole; /* The active console on screen */
64 static CRITICAL_SECTION ActiveVirtConsLock;
65 
66 static COORD PhysicalConsoleSize;
67 static HANDLE ConsoleDeviceHandle;
68 
69 static BOOL ConsInitialized = FALSE;
70 
71 /******************************************************************************\
72 |** BlueScreen Driver management                                             **|
73 \**/
74 /* Code taken and adapted from base/system/services/driver.c */
75 static DWORD
76 ScmLoadDriver(LPCWSTR lpServiceName)
77 {
78     NTSTATUS Status = STATUS_SUCCESS;
79     BOOLEAN WasPrivilegeEnabled = FALSE;
80     PWSTR pszDriverPath;
81     UNICODE_STRING DriverPath;
82 
83     /* Build the driver path */
84     /* 52 = wcslen(L"\\Registry\\Machine\\System\\CurrentControlSet\\Services\\") */
85     pszDriverPath = ConsoleAllocHeap(HEAP_ZERO_MEMORY,
86                                      (52 + wcslen(lpServiceName) + 1) * sizeof(WCHAR));
87     if (pszDriverPath == NULL)
88         return ERROR_NOT_ENOUGH_MEMORY;
89 
90     wcscpy(pszDriverPath,
91            L"\\Registry\\Machine\\System\\CurrentControlSet\\Services\\");
92     wcscat(pszDriverPath,
93            lpServiceName);
94 
95     RtlInitUnicodeString(&DriverPath,
96                          pszDriverPath);
97 
98     DPRINT("  Path: %wZ\n", &DriverPath);
99 
100     /* Acquire driver-loading privilege */
101     Status = RtlAdjustPrivilege(SE_LOAD_DRIVER_PRIVILEGE,
102                                 TRUE,
103                                 FALSE,
104                                 &WasPrivilegeEnabled);
105     if (!NT_SUCCESS(Status))
106     {
107         /* We encountered a failure, exit properly */
108         DPRINT1("CONSRV: Cannot acquire driver-loading privilege, Status = 0x%08lx\n", Status);
109         goto done;
110     }
111 
112     Status = NtLoadDriver(&DriverPath);
113 
114     /* Release driver-loading privilege */
115     RtlAdjustPrivilege(SE_LOAD_DRIVER_PRIVILEGE,
116                        WasPrivilegeEnabled,
117                        FALSE,
118                        &WasPrivilegeEnabled);
119 
120 done:
121     ConsoleFreeHeap(pszDriverPath);
122     return RtlNtStatusToDosError(Status);
123 }
124 
125 #ifdef BLUESCREEN_DRIVER_UNLOADING
126 static DWORD
127 ScmUnloadDriver(LPCWSTR lpServiceName)
128 {
129     NTSTATUS Status = STATUS_SUCCESS;
130     BOOLEAN WasPrivilegeEnabled = FALSE;
131     PWSTR pszDriverPath;
132     UNICODE_STRING DriverPath;
133 
134     /* Build the driver path */
135     /* 52 = wcslen(L"\\Registry\\Machine\\System\\CurrentControlSet\\Services\\") */
136     pszDriverPath = ConsoleAllocHeap(HEAP_ZERO_MEMORY,
137                                      (52 + wcslen(lpServiceName) + 1) * sizeof(WCHAR));
138     if (pszDriverPath == NULL)
139         return ERROR_NOT_ENOUGH_MEMORY;
140 
141     wcscpy(pszDriverPath,
142            L"\\Registry\\Machine\\System\\CurrentControlSet\\Services\\");
143     wcscat(pszDriverPath,
144            lpServiceName);
145 
146     RtlInitUnicodeString(&DriverPath,
147                          pszDriverPath);
148 
149     DPRINT("  Path: %wZ\n", &DriverPath);
150 
151     /* Acquire driver-unloading privilege */
152     Status = RtlAdjustPrivilege(SE_LOAD_DRIVER_PRIVILEGE,
153                                 TRUE,
154                                 FALSE,
155                                 &WasPrivilegeEnabled);
156     if (!NT_SUCCESS(Status))
157     {
158         /* We encountered a failure, exit properly */
159         DPRINT1("CONSRV: Cannot acquire driver-unloading privilege, Status = 0x%08lx\n", Status);
160         goto done;
161     }
162 
163     Status = NtUnloadDriver(&DriverPath);
164 
165     /* Release driver-unloading privilege */
166     RtlAdjustPrivilege(SE_LOAD_DRIVER_PRIVILEGE,
167                        WasPrivilegeEnabled,
168                        FALSE,
169                        &WasPrivilegeEnabled);
170 
171 done:
172     ConsoleFreeHeap(pszDriverPath);
173     return RtlNtStatusToDosError(Status);
174 }
175 #endif
176 /**\
177 \******************************************************************************/
178 
179 #if 0
180 static BOOL
181 TuiSwapConsole(INT Next)
182 {
183     static PTUI_CONSOLE_DATA SwapConsole = NULL; /* Console we are thinking about swapping with */
184     DWORD BytesReturned;
185     ANSI_STRING Title;
186     PVOID Buffer;
187     PCOORD pos;
188 
189     if (0 != Next)
190     {
191         /*
192          * Alt-Tab, swap consoles.
193          * move SwapConsole to next console, and print its title.
194          */
195         EnterCriticalSection(&ActiveVirtConsLock);
196         if (!SwapConsole) SwapConsole = ActiveConsole;
197 
198         SwapConsole = (0 < Next ? GetNextConsole(SwapConsole) : GetPrevConsole(SwapConsole));
199         Title.MaximumLength = RtlUnicodeStringToAnsiSize(&SwapConsole->Console->Title);
200         Title.Length = 0;
201         Buffer = ConsoleAllocHeap(0, sizeof(COORD) + Title.MaximumLength);
202         pos = (PCOORD)Buffer;
203         Title.Buffer = (PVOID)((ULONG_PTR)Buffer + sizeof(COORD));
204 
205         RtlUnicodeStringToAnsiString(&Title, &SwapConsole->Console->Title, FALSE);
206         pos->X = (PhysicalConsoleSize.X - Title.Length) / 2;
207         pos->Y = PhysicalConsoleSize.Y / 2;
208         /* Redraw the console to clear off old title */
209         ConioDrawConsole(ActiveConsole->Console);
210         if (!DeviceIoControl(ConsoleDeviceHandle, IOCTL_CONSOLE_WRITE_OUTPUT_CHARACTER,
211                              NULL, 0, Buffer, sizeof(COORD) + Title.Length,
212                              &BytesReturned, NULL))
213         {
214             DPRINT1( "Error writing to console\n" );
215         }
216         ConsoleFreeHeap(Buffer);
217         LeaveCriticalSection(&ActiveVirtConsLock);
218 
219         return TRUE;
220     }
221     else if (NULL != SwapConsole)
222     {
223         EnterCriticalSection(&ActiveVirtConsLock);
224         if (SwapConsole != ActiveConsole)
225         {
226             /* First remove swapconsole from the list */
227             SwapConsole->Entry.Blink->Flink = SwapConsole->Entry.Flink;
228             SwapConsole->Entry.Flink->Blink = SwapConsole->Entry.Blink;
229             /* Now insert before activeconsole */
230             SwapConsole->Entry.Flink = &ActiveConsole->Entry;
231             SwapConsole->Entry.Blink = ActiveConsole->Entry.Blink;
232             ActiveConsole->Entry.Blink->Flink = &SwapConsole->Entry;
233             ActiveConsole->Entry.Blink = &SwapConsole->Entry;
234         }
235         ActiveConsole = SwapConsole;
236         SwapConsole = NULL;
237         ConioDrawConsole(ActiveConsole->Console);
238         LeaveCriticalSection(&ActiveVirtConsLock);
239         return TRUE;
240     }
241     else
242     {
243         return FALSE;
244     }
245 }
246 #endif
247 
248 static VOID
249 TuiCopyRect(PCHAR Dest, PTEXTMODE_SCREEN_BUFFER Buff, SMALL_RECT* Region)
250 {
251     UINT SrcDelta, DestDelta;
252     LONG i;
253     PCHAR_INFO Src, SrcEnd;
254 
255     Src = ConioCoordToPointer(Buff, Region->Left, Region->Top);
256     SrcDelta = Buff->ScreenBufferSize.X * sizeof(CHAR_INFO);
257     SrcEnd = Buff->Buffer + Buff->ScreenBufferSize.Y * Buff->ScreenBufferSize.X * sizeof(CHAR_INFO);
258     DestDelta = ConioRectWidth(Region) * 2 /* 2 == sizeof(CHAR) + sizeof(BYTE) */;
259     for (i = Region->Top; i <= Region->Bottom; i++)
260     {
261         ConsoleOutputUnicodeToAnsiChar(Buff->Header.Console, (PCHAR)Dest, &Src->Char.UnicodeChar);
262         *(PBYTE)(Dest + 1) = (BYTE)Src->Attributes;
263 
264         Src += SrcDelta;
265         if (SrcEnd <= Src)
266         {
267             Src -= Buff->ScreenBufferSize.Y * Buff->ScreenBufferSize.X * sizeof(CHAR_INFO);
268         }
269         Dest += DestDelta;
270     }
271 }
272 
273 static LRESULT CALLBACK
274 TuiConsoleWndProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam)
275 {
276 /*
277     PTUI_CONSOLE_DATA TuiData = NULL;
278     PCONSRV_CONSOLE Console = NULL;
279 
280     TuiData = TuiGetGuiData(hWnd);
281     if (TuiData == NULL) return 0;
282 */
283 
284     switch (msg)
285     {
286         case WM_CHAR:
287         case WM_SYSCHAR:
288         case WM_KEYDOWN:
289         case WM_SYSKEYDOWN:
290         case WM_KEYUP:
291         case WM_SYSKEYUP:
292         {
293 #if 0
294             if ((HIWORD(lParam) & KF_ALTDOWN) && wParam == VK_TAB)
295             {
296                 // if ((HIWORD(lParam) & (KF_UP | KF_REPEAT)) != KF_REPEAT)
297                     TuiSwapConsole(ShiftState & SHIFT_PRESSED ? -1 : 1);
298 
299                 break;
300             }
301             else if (wParam == VK_MENU /* && !Down */)
302             {
303                 TuiSwapConsole(0);
304                 break;
305             }
306 #endif
307 
308             if (ConDrvValidateConsoleUnsafe((PCONSOLE)ActiveConsole->Console, CONSOLE_RUNNING, TRUE))
309             {
310                 MSG Message;
311                 Message.hwnd = hWnd;
312                 Message.message = msg;
313                 Message.wParam = wParam;
314                 Message.lParam = lParam;
315 
316                 ConioProcessKey(ActiveConsole->Console, &Message);
317                 LeaveCriticalSection(&ActiveConsole->Console->Lock);
318             }
319             break;
320         }
321 
322         case WM_ACTIVATE:
323         {
324             if (ConDrvValidateConsoleUnsafe((PCONSOLE)ActiveConsole->Console, CONSOLE_RUNNING, TRUE))
325             {
326                 if (LOWORD(wParam) != WA_INACTIVE)
327                 {
328                     SetFocus(hWnd);
329                     ConioDrawConsole(ActiveConsole->Console);
330                 }
331                 LeaveCriticalSection(&ActiveConsole->Console->Lock);
332             }
333             break;
334         }
335 
336         default:
337             break;
338     }
339 
340     return DefWindowProcW(hWnd, msg, wParam, lParam);
341 }
342 
343 static DWORD NTAPI
344 TuiConsoleThread(PVOID Param)
345 {
346     PTUI_CONSOLE_DATA TuiData = (PTUI_CONSOLE_DATA)Param;
347     PCONSRV_CONSOLE Console = TuiData->Console;
348     HWND NewWindow;
349     MSG msg;
350 
351     NewWindow = CreateWindowW(TUI_CONSOLE_WINDOW_CLASS,
352                               Console->Title.Buffer,
353                               0,
354                               -32000, -32000, 0, 0,
355                               NULL, NULL,
356                               ConSrvDllInstance,
357                               (PVOID)Console);
358     if (NULL == NewWindow)
359     {
360         DPRINT1("CONSRV: Unable to create console window\n");
361         return 1;
362     }
363     TuiData->hWindow = NewWindow;
364 
365     SetForegroundWindow(TuiData->hWindow);
366     NtUserConsoleControl(ConsoleAcquireDisplayOwnership, NULL, 0);
367 
368     while (GetMessageW(&msg, NULL, 0, 0))
369     {
370         TranslateMessage(&msg);
371         DispatchMessageW(&msg);
372     }
373 
374     return 0;
375 }
376 
377 static BOOL
378 TuiInit(DWORD OemCP)
379 {
380     BOOL Success;
381     CONSOLE_SCREEN_BUFFER_INFO ScrInfo;
382     DWORD BytesReturned;
383     WNDCLASSEXW wc;
384     ATOM ConsoleClassAtom;
385     USHORT TextAttribute = FOREGROUND_BLUE | FOREGROUND_GREEN | FOREGROUND_RED;
386 
387     /* Exit if we were already initialized */
388     if (ConsInitialized) return TRUE;
389 
390     /*
391      * Initialize the TUI front-end:
392      * - load the console driver,
393      * - open BlueScreen device and enable it,
394      * - set default screen attributes,
395      * - grab the console size.
396      */
397     ScmLoadDriver(L"Blue");
398 
399     ConsoleDeviceHandle = CreateFileW(L"\\\\.\\BlueScreen",
400                                       FILE_ALL_ACCESS,
401                                       0, NULL,
402                                       OPEN_EXISTING,
403                                       0, NULL);
404     if (ConsoleDeviceHandle == INVALID_HANDLE_VALUE)
405     {
406         DPRINT1("Failed to open BlueScreen.\n");
407         return FALSE;
408     }
409 
410     Success = TRUE;
411     if (!DeviceIoControl(ConsoleDeviceHandle, IOCTL_CONSOLE_RESET_SCREEN,
412                          &Success, sizeof(Success), NULL, 0,
413                          &BytesReturned, NULL))
414     {
415         DPRINT1("Failed to enable the screen.\n");
416         CloseHandle(ConsoleDeviceHandle);
417         return FALSE;
418     }
419 
420     if (!DeviceIoControl(ConsoleDeviceHandle, IOCTL_CONSOLE_LOADFONT,
421                          &OemCP, sizeof(OemCP), NULL, 0,
422                          &BytesReturned, NULL))
423     {
424         DPRINT1("Failed to load the font for codepage %d\n", OemCP);
425         /* Let's suppose the font is good enough to continue */
426     }
427 
428     if (!DeviceIoControl(ConsoleDeviceHandle, IOCTL_CONSOLE_SET_TEXT_ATTRIBUTE,
429                          &TextAttribute, sizeof(TextAttribute), NULL, 0,
430                          &BytesReturned, NULL))
431     {
432         DPRINT1("Failed to set text attribute.\n");
433     }
434 
435     ActiveConsole = NULL;
436     InitializeListHead(&VirtConsList);
437     InitializeCriticalSection(&ActiveVirtConsLock);
438 
439     if (!DeviceIoControl(ConsoleDeviceHandle, IOCTL_CONSOLE_GET_SCREEN_BUFFER_INFO,
440                          NULL, 0, &ScrInfo, sizeof(ScrInfo), &BytesReturned, NULL))
441     {
442         DPRINT1("Failed to get console info.\n");
443         Success = FALSE;
444         goto Quit;
445     }
446     PhysicalConsoleSize = ScrInfo.dwSize;
447 
448     /* Register the TUI notification window class */
449     RtlZeroMemory(&wc, sizeof(WNDCLASSEXW));
450     wc.cbSize = sizeof(WNDCLASSEXW);
451     wc.lpszClassName = TUI_CONSOLE_WINDOW_CLASS;
452     wc.lpfnWndProc = TuiConsoleWndProc;
453     wc.cbWndExtra = 0;
454     wc.hInstance = ConSrvDllInstance;
455 
456     ConsoleClassAtom = RegisterClassExW(&wc);
457     if (ConsoleClassAtom == 0)
458     {
459         DPRINT1("Failed to register TUI console wndproc.\n");
460         Success = FALSE;
461     }
462     else
463     {
464         Success = TRUE;
465     }
466 
467 Quit:
468     if (!Success)
469     {
470         DeleteCriticalSection(&ActiveVirtConsLock);
471         CloseHandle(ConsoleDeviceHandle);
472     }
473 
474     ConsInitialized = Success;
475     return Success;
476 }
477 
478 
479 
480 /******************************************************************************
481  *                             TUI Console Driver                             *
482  ******************************************************************************/
483 
484 static VOID NTAPI
485 TuiDeinitFrontEnd(IN OUT PFRONTEND This /*,
486                   IN PCONSRV_CONSOLE Console */);
487 
488 static NTSTATUS NTAPI
489 TuiInitFrontEnd(IN OUT PFRONTEND This,
490                 IN PCONSRV_CONSOLE Console)
491 {
492     PTUI_CONSOLE_DATA TuiData;
493     HANDLE ThreadHandle;
494 
495     if (This == NULL || Console == NULL)
496         return STATUS_INVALID_PARAMETER;
497 
498     if (GetType(Console->ActiveBuffer) != TEXTMODE_BUFFER)
499         return STATUS_INVALID_PARAMETER;
500 
501     TuiData = ConsoleAllocHeap(HEAP_ZERO_MEMORY, sizeof(TUI_CONSOLE_DATA));
502     if (!TuiData)
503     {
504         DPRINT1("CONSRV: Failed to create TUI_CONSOLE_DATA\n");
505         return STATUS_UNSUCCESSFUL;
506     }
507     // Console->FrontEndIFace.Context = (PVOID)TuiData;
508     TuiData->Console      = Console;
509     TuiData->ActiveBuffer = Console->ActiveBuffer;
510     TuiData->hWindow = NULL;
511 
512     InitializeCriticalSection(&TuiData->Lock);
513 
514     /*
515      * HACK: Resize the console since we don't support for now changing
516      * the console size when we display it with the hardware.
517      */
518     // Console->ConsoleSize = PhysicalConsoleSize;
519     // ConioResizeBuffer(Console, (PTEXTMODE_SCREEN_BUFFER)(Console->ActiveBuffer), PhysicalConsoleSize);
520 
521     // /* The console cannot be resized anymore */
522     // Console->FixedSize = TRUE; // MUST be placed AFTER the call to ConioResizeBuffer !!
523     // // TermResizeTerminal(Console);
524 
525     /*
526      * Contrary to what we do in the GUI front-end, here we create
527      * an input thread for each console. It will dispatch all the
528      * input messages to the proper console (on the GUI it is done
529      * via the default GUI dispatch thread).
530      */
531     ThreadHandle = CreateThread(NULL,
532                                 0,
533                                 TuiConsoleThread,
534                                 (PVOID)TuiData,
535                                 0,
536                                 NULL);
537     if (NULL == ThreadHandle)
538     {
539         DPRINT1("CONSRV: Unable to create console thread\n");
540         // TuiDeinitFrontEnd(Console);
541         TuiDeinitFrontEnd(This);
542         return STATUS_UNSUCCESSFUL;
543     }
544     CloseHandle(ThreadHandle);
545 
546     /*
547      * Insert the newly created console in the list of virtual consoles
548      * and activate it (give it the focus).
549      */
550     EnterCriticalSection(&ActiveVirtConsLock);
551     InsertTailList(&VirtConsList, &TuiData->Entry);
552     ActiveConsole = TuiData;
553     LeaveCriticalSection(&ActiveVirtConsLock);
554 
555     /* Finally, initialize the frontend structure */
556     This->Context  = TuiData;
557     This->Context2 = NULL;
558 
559     return STATUS_SUCCESS;
560 }
561 
562 static VOID NTAPI
563 TuiDeinitFrontEnd(IN OUT PFRONTEND This)
564 {
565     // PCONSRV_CONSOLE Console = This->Console;
566     PTUI_CONSOLE_DATA TuiData = This->Context;
567 
568     /* Close the notification window */
569     DestroyWindow(TuiData->hWindow);
570 
571     /*
572      * Set the active console to the next one
573      * and remove the console from the list.
574      */
575     EnterCriticalSection(&ActiveVirtConsLock);
576     ActiveConsole = GetNextConsole(TuiData);
577     RemoveEntryList(&TuiData->Entry);
578 
579     // /* Switch to next console */
580     // if (ActiveConsole == TuiData)
581     // if (ActiveConsole->Console == Console)
582     // {
583         // ActiveConsole = (TuiData->Entry.Flink != TuiData->Entry ? GetNextConsole(TuiData) : NULL);
584     // }
585 
586     // if (GetNextConsole(TuiData) != TuiData)
587     // {
588         // TuiData->Entry.Blink->Flink = TuiData->Entry.Flink;
589         // TuiData->Entry.Flink->Blink = TuiData->Entry.Blink;
590     // }
591 
592     LeaveCriticalSection(&ActiveVirtConsLock);
593 
594     /* Switch to the next console */
595     if (NULL != ActiveConsole) ConioDrawConsole(ActiveConsole->Console);
596 
597     This->Context = NULL;
598     DeleteCriticalSection(&TuiData->Lock);
599     ConsoleFreeHeap(TuiData);
600 }
601 
602 static VOID NTAPI
603 TuiDrawRegion(IN OUT PFRONTEND This,
604               SMALL_RECT* Region)
605 {
606     PTUI_CONSOLE_DATA TuiData = This->Context;
607     PCONSOLE_SCREEN_BUFFER Buff = TuiData->Console->ActiveBuffer;
608     PCONSOLE_DRAW ConsoleDraw;
609     DWORD BytesReturned;
610     UINT ConsoleDrawSize;
611 
612     if (TuiData != ActiveConsole) return;
613     if (GetType(Buff) != TEXTMODE_BUFFER) return;
614 
615     ConsoleDrawSize = sizeof(CONSOLE_DRAW) +
616                       (ConioRectWidth(Region) * ConioRectHeight(Region)) * 2;
617     ConsoleDraw = ConsoleAllocHeap(0, ConsoleDrawSize);
618     if (NULL == ConsoleDraw)
619     {
620         DPRINT1("ConsoleAllocHeap failed\n");
621         return;
622     }
623     ConsoleDraw->X = Region->Left;
624     ConsoleDraw->Y = Region->Top;
625     ConsoleDraw->SizeX = ConioRectWidth(Region);
626     ConsoleDraw->SizeY = ConioRectHeight(Region);
627     ConsoleDraw->CursorX = Buff->CursorPosition.X;
628     ConsoleDraw->CursorY = Buff->CursorPosition.Y;
629 
630     TuiCopyRect((PCHAR)(ConsoleDraw + 1), (PTEXTMODE_SCREEN_BUFFER)Buff, Region);
631 
632     if (!DeviceIoControl(ConsoleDeviceHandle, IOCTL_CONSOLE_DRAW,
633                          NULL, 0, ConsoleDraw, ConsoleDrawSize, &BytesReturned, NULL))
634     {
635         DPRINT1("Failed to draw console\n");
636         ConsoleFreeHeap(ConsoleDraw);
637         return;
638     }
639 
640     ConsoleFreeHeap(ConsoleDraw);
641 }
642 
643 static VOID NTAPI
644 TuiWriteStream(IN OUT PFRONTEND This,
645                SMALL_RECT* Region,
646                SHORT CursorStartX,
647                SHORT CursorStartY,
648                UINT ScrolledLines,
649                PWCHAR Buffer,
650                UINT Length)
651 {
652     PTUI_CONSOLE_DATA TuiData = This->Context;
653     PCONSOLE_SCREEN_BUFFER Buff = TuiData->Console->ActiveBuffer;
654     PCHAR NewBuffer;
655     ULONG NewLength;
656     DWORD BytesWritten;
657 
658     if (TuiData != ActiveConsole) return;
659     if (GetType(Buff) != TEXTMODE_BUFFER) return;
660 
661     NewLength = WideCharToMultiByte(TuiData->Console->OutputCodePage, 0,
662                                     Buffer, Length,
663                                     NULL, 0, NULL, NULL);
664     NewBuffer = RtlAllocateHeap(RtlGetProcessHeap(), 0, NewLength * sizeof(CHAR));
665     if (!NewBuffer) return;
666 
667     WideCharToMultiByte(TuiData->Console->OutputCodePage, 0,
668                         Buffer, Length,
669                         NewBuffer, NewLength, NULL, NULL);
670 
671     if (!WriteFile(ConsoleDeviceHandle, NewBuffer, NewLength * sizeof(CHAR), &BytesWritten, NULL))
672     {
673         DPRINT1("Error writing to BlueScreen\n");
674     }
675 
676     RtlFreeHeap(RtlGetProcessHeap(), 0, NewBuffer);
677 }
678 
679 static VOID NTAPI
680 TuiRingBell(IN OUT PFRONTEND This)
681 {
682     Beep(800, 200);
683 }
684 
685 static BOOL NTAPI
686 TuiSetCursorInfo(IN OUT PFRONTEND This,
687                  PCONSOLE_SCREEN_BUFFER Buff)
688 {
689     PTUI_CONSOLE_DATA TuiData = This->Context;
690     CONSOLE_CURSOR_INFO Info;
691     DWORD BytesReturned;
692 
693     if (TuiData != ActiveConsole) return TRUE;
694     if (TuiData->Console->ActiveBuffer != Buff) return TRUE;
695     if (GetType(Buff) != TEXTMODE_BUFFER) return FALSE;
696 
697     Info.dwSize = ConioEffectiveCursorSize(TuiData->Console, 100);
698     Info.bVisible = Buff->CursorInfo.bVisible;
699 
700     if (!DeviceIoControl(ConsoleDeviceHandle, IOCTL_CONSOLE_SET_CURSOR_INFO,
701                          &Info, sizeof(Info), NULL, 0, &BytesReturned, NULL))
702     {
703         DPRINT1( "Failed to set cursor info\n" );
704         return FALSE;
705     }
706 
707     return TRUE;
708 }
709 
710 static BOOL NTAPI
711 TuiSetScreenInfo(IN OUT PFRONTEND This,
712                  PCONSOLE_SCREEN_BUFFER Buff,
713                  SHORT OldCursorX,
714                  SHORT OldCursorY)
715 {
716     PTUI_CONSOLE_DATA TuiData = This->Context;
717     CONSOLE_SCREEN_BUFFER_INFO Info;
718     DWORD BytesReturned;
719 
720     if (TuiData != ActiveConsole) return TRUE;
721     if (TuiData->Console->ActiveBuffer != Buff) return TRUE;
722     if (GetType(Buff) != TEXTMODE_BUFFER) return FALSE;
723 
724     Info.dwCursorPosition = Buff->CursorPosition;
725     Info.wAttributes = ((PTEXTMODE_SCREEN_BUFFER)Buff)->ScreenDefaultAttrib;
726 
727     if (!DeviceIoControl(ConsoleDeviceHandle, IOCTL_CONSOLE_SET_SCREEN_BUFFER_INFO,
728                          &Info, sizeof(CONSOLE_SCREEN_BUFFER_INFO), NULL, 0,
729                          &BytesReturned, NULL))
730     {
731         DPRINT1( "Failed to set cursor position\n" );
732         return FALSE;
733     }
734 
735     return TRUE;
736 }
737 
738 static VOID NTAPI
739 TuiResizeTerminal(IN OUT PFRONTEND This)
740 {
741 }
742 
743 static VOID NTAPI
744 TuiSetActiveScreenBuffer(IN OUT PFRONTEND This)
745 {
746     // PGUI_CONSOLE_DATA GuiData = This->Context;
747     // PCONSOLE_SCREEN_BUFFER ActiveBuffer;
748     // HPALETTE hPalette;
749 
750     // EnterCriticalSection(&GuiData->Lock);
751     // GuiData->WindowSizeLock = TRUE;
752 
753     // InterlockedExchangePointer(&GuiData->ActiveBuffer,
754                                // ConDrvGetActiveScreenBuffer(GuiData->Console));
755 
756     // GuiData->WindowSizeLock = FALSE;
757     // LeaveCriticalSection(&GuiData->Lock);
758 
759     // ActiveBuffer = GuiData->ActiveBuffer;
760 
761     // /* Change the current palette */
762     // if (ActiveBuffer->PaletteHandle == NULL)
763     // {
764         // hPalette = GuiData->hSysPalette;
765     // }
766     // else
767     // {
768         // hPalette = ActiveBuffer->PaletteHandle;
769     // }
770 
771     // DPRINT("GuiSetActiveScreenBuffer using palette 0x%p\n", hPalette);
772 
773     // /* Set the new palette for the framebuffer */
774     // SelectPalette(GuiData->hMemDC, hPalette, FALSE);
775 
776     // /* Specify the use of the system palette for the framebuffer */
777     // SetSystemPaletteUse(GuiData->hMemDC, ActiveBuffer->PaletteUsage);
778 
779     // /* Realize the (logical) palette */
780     // RealizePalette(GuiData->hMemDC);
781 
782     // GuiResizeTerminal(This);
783     // // ConioDrawConsole(Console);
784 }
785 
786 static VOID NTAPI
787 TuiReleaseScreenBuffer(IN OUT PFRONTEND This,
788                        IN PCONSOLE_SCREEN_BUFFER ScreenBuffer)
789 {
790     // PGUI_CONSOLE_DATA GuiData = This->Context;
791 
792     // /*
793      // * If we were notified to release a screen buffer that is not actually
794      // * ours, then just ignore the notification...
795      // */
796     // if (ScreenBuffer != GuiData->ActiveBuffer) return;
797 
798     // /*
799      // * ... else, we must release our active buffer. Two cases are present:
800      // * - If ScreenBuffer (== GuiData->ActiveBuffer) IS NOT the console
801      // *   active screen buffer, then we can safely switch to it.
802      // * - If ScreenBuffer IS the console active screen buffer, we must release
803      // *   it ONLY.
804      // */
805 
806     // /* Release the old active palette and set the default one */
807     // if (GetCurrentObject(GuiData->hMemDC, OBJ_PAL) == ScreenBuffer->PaletteHandle)
808     // {
809         // /* Set the new palette */
810         // SelectPalette(GuiData->hMemDC, GuiData->hSysPalette, FALSE);
811     // }
812 
813     // /* Set the adequate active screen buffer */
814     // if (ScreenBuffer != GuiData->Console->ActiveBuffer)
815     // {
816         // GuiSetActiveScreenBuffer(This);
817     // }
818     // else
819     // {
820         // EnterCriticalSection(&GuiData->Lock);
821         // GuiData->WindowSizeLock = TRUE;
822 
823         // InterlockedExchangePointer(&GuiData->ActiveBuffer, NULL);
824 
825         // GuiData->WindowSizeLock = FALSE;
826         // LeaveCriticalSection(&GuiData->Lock);
827     // }
828 }
829 
830 static VOID NTAPI
831 TuiRefreshInternalInfo(IN OUT PFRONTEND This)
832 {
833 }
834 
835 static VOID NTAPI
836 TuiChangeTitle(IN OUT PFRONTEND This)
837 {
838 }
839 
840 static BOOL NTAPI
841 TuiChangeIcon(IN OUT PFRONTEND This,
842               HICON IconHandle)
843 {
844     return TRUE;
845 }
846 
847 static HDESK NTAPI
848 TuiGetThreadConsoleDesktop(IN OUT PFRONTEND This)
849 {
850     // PTUI_CONSOLE_DATA TuiData = This->Context;
851     return NULL;
852 }
853 
854 static HWND NTAPI
855 TuiGetConsoleWindowHandle(IN OUT PFRONTEND This)
856 {
857     PTUI_CONSOLE_DATA TuiData = This->Context;
858     return TuiData->hWindow;
859 }
860 
861 static VOID NTAPI
862 TuiGetLargestConsoleWindowSize(IN OUT PFRONTEND This,
863                                PCOORD pSize)
864 {
865     if (!pSize) return;
866     *pSize = PhysicalConsoleSize;
867 }
868 
869 static BOOL NTAPI
870 TuiGetSelectionInfo(IN OUT PFRONTEND This,
871                     PCONSOLE_SELECTION_INFO pSelectionInfo)
872 {
873     return TRUE;
874 }
875 
876 static BOOL NTAPI
877 TuiSetPalette(IN OUT PFRONTEND This,
878               HPALETTE PaletteHandle,
879               UINT PaletteUsage)
880 {
881     return TRUE;
882 }
883 
884 static ULONG NTAPI
885 TuiGetDisplayMode(IN OUT PFRONTEND This)
886 {
887     return CONSOLE_FULLSCREEN_HARDWARE; // CONSOLE_FULLSCREEN;
888 }
889 
890 static BOOL NTAPI
891 TuiSetDisplayMode(IN OUT PFRONTEND This,
892                   ULONG NewMode)
893 {
894     // if (NewMode & ~(CONSOLE_FULLSCREEN_MODE | CONSOLE_WINDOWED_MODE))
895     //     return FALSE;
896     return TRUE;
897 }
898 
899 static INT NTAPI
900 TuiShowMouseCursor(IN OUT PFRONTEND This,
901                    BOOL Show)
902 {
903     return 0;
904 }
905 
906 static BOOL NTAPI
907 TuiSetMouseCursor(IN OUT PFRONTEND This,
908                   HCURSOR CursorHandle)
909 {
910     return TRUE;
911 }
912 
913 static HMENU NTAPI
914 TuiMenuControl(IN OUT PFRONTEND This,
915                UINT CmdIdLow,
916                UINT CmdIdHigh)
917 {
918     return NULL;
919 }
920 
921 static BOOL NTAPI
922 TuiSetMenuClose(IN OUT PFRONTEND This,
923                 BOOL Enable)
924 {
925     return TRUE;
926 }
927 
928 static FRONTEND_VTBL TuiVtbl =
929 {
930     TuiInitFrontEnd,
931     TuiDeinitFrontEnd,
932     TuiDrawRegion,
933     TuiWriteStream,
934     TuiRingBell,
935     TuiSetCursorInfo,
936     TuiSetScreenInfo,
937     TuiResizeTerminal,
938     TuiSetActiveScreenBuffer,
939     TuiReleaseScreenBuffer,
940     TuiRefreshInternalInfo,
941     TuiChangeTitle,
942     TuiChangeIcon,
943     TuiGetThreadConsoleDesktop,
944     TuiGetConsoleWindowHandle,
945     TuiGetLargestConsoleWindowSize,
946     TuiGetSelectionInfo,
947     TuiSetPalette,
948     TuiGetDisplayMode,
949     TuiSetDisplayMode,
950     TuiShowMouseCursor,
951     TuiSetMouseCursor,
952     TuiMenuControl,
953     TuiSetMenuClose,
954 };
955 
956 static BOOLEAN
957 IsConsoleMode(VOID)
958 {
959     return (BOOLEAN)NtUserCallNoParam(NOPARAM_ROUTINE_ISCONSOLEMODE);
960 }
961 
962 NTSTATUS NTAPI
963 TuiLoadFrontEnd(IN OUT PFRONTEND FrontEnd,
964                 IN OUT PCONSOLE_STATE_INFO ConsoleInfo,
965                 IN OUT PCONSOLE_INIT_INFO ConsoleInitInfo,
966                 IN HANDLE ConsoleLeaderProcessHandle)
967 {
968     if (FrontEnd == NULL || ConsoleInfo == NULL)
969         return STATUS_INVALID_PARAMETER;
970 
971     /* We must be in console mode already */
972     if (!IsConsoleMode()) return STATUS_UNSUCCESSFUL;
973 
974     /* Initialize the TUI terminal emulator */
975     if (!TuiInit(ConsoleInfo->CodePage)) return STATUS_UNSUCCESSFUL;
976 
977     /* Finally, initialize the frontend structure */
978     FrontEnd->Vtbl     = &TuiVtbl;
979     FrontEnd->Context  = NULL;
980     FrontEnd->Context2 = NULL;
981 
982     return STATUS_SUCCESS;
983 }
984 
985 NTSTATUS NTAPI
986 TuiUnloadFrontEnd(IN OUT PFRONTEND FrontEnd)
987 {
988     if (FrontEnd == NULL) return STATUS_INVALID_PARAMETER;
989     if (FrontEnd->Context) TuiDeinitFrontEnd(FrontEnd);
990 
991     return STATUS_SUCCESS;
992 }
993 
994 #endif
995 
996 /* EOF */
997