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