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 Ret = FALSE;
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      * - set default screen attributes,
392      * - grab the console size.
393      */
394     ScmLoadDriver(L"Blue");
395 
396     ConsoleDeviceHandle = CreateFileW(L"\\\\.\\BlueScreen",
397                                       FILE_ALL_ACCESS,
398                                       0, NULL,
399                                       OPEN_EXISTING,
400                                       0, NULL);
401     if (ConsoleDeviceHandle == INVALID_HANDLE_VALUE)
402     {
403         DPRINT1("Failed to open BlueScreen.\n");
404         return FALSE;
405     }
406 
407     if (!DeviceIoControl(ConsoleDeviceHandle, IOCTL_CONSOLE_LOADFONT,
408                          &OemCP, sizeof(OemCP), NULL, 0,
409                          &BytesReturned, NULL))
410     {
411         DPRINT1("Failed to load the font for codepage %d\n", OemCP);
412         /* Let's suppose the font is good enough to continue */
413     }
414 
415     if (!DeviceIoControl(ConsoleDeviceHandle, IOCTL_CONSOLE_SET_TEXT_ATTRIBUTE,
416                          &TextAttribute, sizeof(TextAttribute), NULL, 0,
417                          &BytesReturned, NULL))
418     {
419         DPRINT1("Failed to set text attribute\n");
420     }
421 
422     ActiveConsole = NULL;
423     InitializeListHead(&VirtConsList);
424     InitializeCriticalSection(&ActiveVirtConsLock);
425 
426     if (!DeviceIoControl(ConsoleDeviceHandle, IOCTL_CONSOLE_GET_SCREEN_BUFFER_INFO,
427                          NULL, 0, &ScrInfo, sizeof(ScrInfo), &BytesReturned, NULL))
428     {
429         DPRINT1("Failed to get console info\n");
430         Ret = FALSE;
431         goto Quit;
432     }
433     PhysicalConsoleSize = ScrInfo.dwSize;
434 
435     /* Register the TUI notification window class */
436     RtlZeroMemory(&wc, sizeof(WNDCLASSEXW));
437     wc.cbSize = sizeof(WNDCLASSEXW);
438     wc.lpszClassName = TUI_CONSOLE_WINDOW_CLASS;
439     wc.lpfnWndProc = TuiConsoleWndProc;
440     wc.cbWndExtra = 0;
441     wc.hInstance = ConSrvDllInstance;
442 
443     ConsoleClassAtom = RegisterClassExW(&wc);
444     if (ConsoleClassAtom == 0)
445     {
446         DPRINT1("Failed to register TUI console wndproc\n");
447         Ret = FALSE;
448     }
449     else
450     {
451         Ret = TRUE;
452     }
453 
454 Quit:
455     if (!Ret)
456     {
457         DeleteCriticalSection(&ActiveVirtConsLock);
458         CloseHandle(ConsoleDeviceHandle);
459     }
460 
461     ConsInitialized = Ret;
462     return Ret;
463 }
464 
465 
466 
467 /******************************************************************************
468  *                             TUI Console Driver                             *
469  ******************************************************************************/
470 
471 static VOID NTAPI
472 TuiDeinitFrontEnd(IN OUT PFRONTEND This /*,
473                   IN PCONSRV_CONSOLE Console */);
474 
475 static NTSTATUS NTAPI
476 TuiInitFrontEnd(IN OUT PFRONTEND This,
477                 IN PCONSRV_CONSOLE Console)
478 {
479     PTUI_CONSOLE_DATA TuiData;
480     HANDLE ThreadHandle;
481 
482     if (This == NULL || Console == NULL)
483         return STATUS_INVALID_PARAMETER;
484 
485     if (GetType(Console->ActiveBuffer) != TEXTMODE_BUFFER)
486         return STATUS_INVALID_PARAMETER;
487 
488     TuiData = ConsoleAllocHeap(HEAP_ZERO_MEMORY, sizeof(TUI_CONSOLE_DATA));
489     if (!TuiData)
490     {
491         DPRINT1("CONSRV: Failed to create TUI_CONSOLE_DATA\n");
492         return STATUS_UNSUCCESSFUL;
493     }
494     // Console->FrontEndIFace.Context = (PVOID)TuiData;
495     TuiData->Console      = Console;
496     TuiData->ActiveBuffer = Console->ActiveBuffer;
497     TuiData->hWindow = NULL;
498 
499     InitializeCriticalSection(&TuiData->Lock);
500 
501     /*
502      * HACK: Resize the console since we don't support for now changing
503      * the console size when we display it with the hardware.
504      */
505     // Console->ConsoleSize = PhysicalConsoleSize;
506     // ConioResizeBuffer(Console, (PTEXTMODE_SCREEN_BUFFER)(Console->ActiveBuffer), PhysicalConsoleSize);
507 
508     // /* The console cannot be resized anymore */
509     // Console->FixedSize = TRUE; // MUST be placed AFTER the call to ConioResizeBuffer !!
510     // // TermResizeTerminal(Console);
511 
512     /*
513      * Contrary to what we do in the GUI front-end, here we create
514      * an input thread for each console. It will dispatch all the
515      * input messages to the proper console (on the GUI it is done
516      * via the default GUI dispatch thread).
517      */
518     ThreadHandle = CreateThread(NULL,
519                                 0,
520                                 TuiConsoleThread,
521                                 (PVOID)TuiData,
522                                 0,
523                                 NULL);
524     if (NULL == ThreadHandle)
525     {
526         DPRINT1("CONSRV: Unable to create console thread\n");
527         // TuiDeinitFrontEnd(Console);
528         TuiDeinitFrontEnd(This);
529         return STATUS_UNSUCCESSFUL;
530     }
531     CloseHandle(ThreadHandle);
532 
533     /*
534      * Insert the newly created console in the list of virtual consoles
535      * and activate it (give it the focus).
536      */
537     EnterCriticalSection(&ActiveVirtConsLock);
538     InsertTailList(&VirtConsList, &TuiData->Entry);
539     ActiveConsole = TuiData;
540     LeaveCriticalSection(&ActiveVirtConsLock);
541 
542     /* Finally, initialize the frontend structure */
543     This->Context  = TuiData;
544     This->Context2 = NULL;
545 
546     return STATUS_SUCCESS;
547 }
548 
549 static VOID NTAPI
550 TuiDeinitFrontEnd(IN OUT PFRONTEND This)
551 {
552     // PCONSRV_CONSOLE Console = This->Console;
553     PTUI_CONSOLE_DATA TuiData = This->Context;
554 
555     /* Close the notification window */
556     DestroyWindow(TuiData->hWindow);
557 
558     /*
559      * Set the active console to the next one
560      * and remove the console from the list.
561      */
562     EnterCriticalSection(&ActiveVirtConsLock);
563     ActiveConsole = GetNextConsole(TuiData);
564     RemoveEntryList(&TuiData->Entry);
565 
566     // /* Switch to next console */
567     // if (ActiveConsole == TuiData)
568     // if (ActiveConsole->Console == Console)
569     // {
570         // ActiveConsole = (TuiData->Entry.Flink != TuiData->Entry ? GetNextConsole(TuiData) : NULL);
571     // }
572 
573     // if (GetNextConsole(TuiData) != TuiData)
574     // {
575         // TuiData->Entry.Blink->Flink = TuiData->Entry.Flink;
576         // TuiData->Entry.Flink->Blink = TuiData->Entry.Blink;
577     // }
578 
579     LeaveCriticalSection(&ActiveVirtConsLock);
580 
581     /* Switch to the next console */
582     if (NULL != ActiveConsole) ConioDrawConsole(ActiveConsole->Console);
583 
584     This->Context = NULL;
585     DeleteCriticalSection(&TuiData->Lock);
586     ConsoleFreeHeap(TuiData);
587 }
588 
589 static VOID NTAPI
590 TuiDrawRegion(IN OUT PFRONTEND This,
591               SMALL_RECT* Region)
592 {
593     PTUI_CONSOLE_DATA TuiData = This->Context;
594     PCONSOLE_SCREEN_BUFFER Buff = TuiData->Console->ActiveBuffer;
595     PCONSOLE_DRAW ConsoleDraw;
596     DWORD BytesReturned;
597     UINT ConsoleDrawSize;
598 
599     if (TuiData != ActiveConsole) return;
600     if (GetType(Buff) != TEXTMODE_BUFFER) return;
601 
602     ConsoleDrawSize = sizeof(CONSOLE_DRAW) +
603                       (ConioRectWidth(Region) * ConioRectHeight(Region)) * 2;
604     ConsoleDraw = ConsoleAllocHeap(0, ConsoleDrawSize);
605     if (NULL == ConsoleDraw)
606     {
607         DPRINT1("ConsoleAllocHeap failed\n");
608         return;
609     }
610     ConsoleDraw->X = Region->Left;
611     ConsoleDraw->Y = Region->Top;
612     ConsoleDraw->SizeX = ConioRectWidth(Region);
613     ConsoleDraw->SizeY = ConioRectHeight(Region);
614     ConsoleDraw->CursorX = Buff->CursorPosition.X;
615     ConsoleDraw->CursorY = Buff->CursorPosition.Y;
616 
617     TuiCopyRect((PCHAR)(ConsoleDraw + 1), (PTEXTMODE_SCREEN_BUFFER)Buff, Region);
618 
619     if (!DeviceIoControl(ConsoleDeviceHandle, IOCTL_CONSOLE_DRAW,
620                          NULL, 0, ConsoleDraw, ConsoleDrawSize, &BytesReturned, NULL))
621     {
622         DPRINT1("Failed to draw console\n");
623         ConsoleFreeHeap(ConsoleDraw);
624         return;
625     }
626 
627     ConsoleFreeHeap(ConsoleDraw);
628 }
629 
630 static VOID NTAPI
631 TuiWriteStream(IN OUT PFRONTEND This,
632                SMALL_RECT* Region,
633                SHORT CursorStartX,
634                SHORT CursorStartY,
635                UINT ScrolledLines,
636                PWCHAR Buffer,
637                UINT Length)
638 {
639     PTUI_CONSOLE_DATA TuiData = This->Context;
640     PCONSOLE_SCREEN_BUFFER Buff = TuiData->Console->ActiveBuffer;
641     PCHAR NewBuffer;
642     ULONG NewLength;
643     DWORD BytesWritten;
644 
645     if (TuiData != ActiveConsole) return;
646     if (GetType(Buff) != TEXTMODE_BUFFER) return;
647 
648     NewLength = WideCharToMultiByte(TuiData->Console->OutputCodePage, 0,
649                                     Buffer, Length,
650                                     NULL, 0, NULL, NULL);
651     NewBuffer = RtlAllocateHeap(RtlGetProcessHeap(), 0, NewLength * sizeof(CHAR));
652     if (!NewBuffer) return;
653 
654     WideCharToMultiByte(TuiData->Console->OutputCodePage, 0,
655                         Buffer, Length,
656                         NewBuffer, NewLength, NULL, NULL);
657 
658     if (!WriteFile(ConsoleDeviceHandle, NewBuffer, NewLength * sizeof(CHAR), &BytesWritten, NULL))
659     {
660         DPRINT1("Error writing to BlueScreen\n");
661     }
662 
663     RtlFreeHeap(RtlGetProcessHeap(), 0, NewBuffer);
664 }
665 
666 static VOID NTAPI
667 TuiRingBell(IN OUT PFRONTEND This)
668 {
669     Beep(800, 200);
670 }
671 
672 static BOOL NTAPI
673 TuiSetCursorInfo(IN OUT PFRONTEND This,
674                  PCONSOLE_SCREEN_BUFFER Buff)
675 {
676     PTUI_CONSOLE_DATA TuiData = This->Context;
677     CONSOLE_CURSOR_INFO Info;
678     DWORD BytesReturned;
679 
680     if (TuiData != ActiveConsole) return TRUE;
681     if (TuiData->Console->ActiveBuffer != Buff) return TRUE;
682     if (GetType(Buff) != TEXTMODE_BUFFER) return FALSE;
683 
684     Info.dwSize = ConioEffectiveCursorSize(TuiData->Console, 100);
685     Info.bVisible = Buff->CursorInfo.bVisible;
686 
687     if (!DeviceIoControl(ConsoleDeviceHandle, IOCTL_CONSOLE_SET_CURSOR_INFO,
688                          &Info, sizeof(Info), NULL, 0, &BytesReturned, NULL))
689     {
690         DPRINT1( "Failed to set cursor info\n" );
691         return FALSE;
692     }
693 
694     return TRUE;
695 }
696 
697 static BOOL NTAPI
698 TuiSetScreenInfo(IN OUT PFRONTEND This,
699                  PCONSOLE_SCREEN_BUFFER Buff,
700                  SHORT OldCursorX,
701                  SHORT OldCursorY)
702 {
703     PTUI_CONSOLE_DATA TuiData = This->Context;
704     CONSOLE_SCREEN_BUFFER_INFO Info;
705     DWORD BytesReturned;
706 
707     if (TuiData != ActiveConsole) return TRUE;
708     if (TuiData->Console->ActiveBuffer != Buff) return TRUE;
709     if (GetType(Buff) != TEXTMODE_BUFFER) return FALSE;
710 
711     Info.dwCursorPosition = Buff->CursorPosition;
712     Info.wAttributes = ((PTEXTMODE_SCREEN_BUFFER)Buff)->ScreenDefaultAttrib;
713 
714     if (!DeviceIoControl(ConsoleDeviceHandle, IOCTL_CONSOLE_SET_SCREEN_BUFFER_INFO,
715                          &Info, sizeof(CONSOLE_SCREEN_BUFFER_INFO), NULL, 0,
716                          &BytesReturned, NULL))
717     {
718         DPRINT1( "Failed to set cursor position\n" );
719         return FALSE;
720     }
721 
722     return TRUE;
723 }
724 
725 static VOID NTAPI
726 TuiResizeTerminal(IN OUT PFRONTEND This)
727 {
728 }
729 
730 static VOID NTAPI
731 TuiSetActiveScreenBuffer(IN OUT PFRONTEND This)
732 {
733     // PGUI_CONSOLE_DATA GuiData = This->Context;
734     // PCONSOLE_SCREEN_BUFFER ActiveBuffer;
735     // HPALETTE hPalette;
736 
737     // EnterCriticalSection(&GuiData->Lock);
738     // GuiData->WindowSizeLock = TRUE;
739 
740     // InterlockedExchangePointer(&GuiData->ActiveBuffer,
741                                // ConDrvGetActiveScreenBuffer(GuiData->Console));
742 
743     // GuiData->WindowSizeLock = FALSE;
744     // LeaveCriticalSection(&GuiData->Lock);
745 
746     // ActiveBuffer = GuiData->ActiveBuffer;
747 
748     // /* Change the current palette */
749     // if (ActiveBuffer->PaletteHandle == NULL)
750     // {
751         // hPalette = GuiData->hSysPalette;
752     // }
753     // else
754     // {
755         // hPalette = ActiveBuffer->PaletteHandle;
756     // }
757 
758     // DPRINT("GuiSetActiveScreenBuffer using palette 0x%p\n", hPalette);
759 
760     // /* Set the new palette for the framebuffer */
761     // SelectPalette(GuiData->hMemDC, hPalette, FALSE);
762 
763     // /* Specify the use of the system palette for the framebuffer */
764     // SetSystemPaletteUse(GuiData->hMemDC, ActiveBuffer->PaletteUsage);
765 
766     // /* Realize the (logical) palette */
767     // RealizePalette(GuiData->hMemDC);
768 
769     // GuiResizeTerminal(This);
770     // // ConioDrawConsole(Console);
771 }
772 
773 static VOID NTAPI
774 TuiReleaseScreenBuffer(IN OUT PFRONTEND This,
775                        IN PCONSOLE_SCREEN_BUFFER ScreenBuffer)
776 {
777     // PGUI_CONSOLE_DATA GuiData = This->Context;
778 
779     // /*
780      // * If we were notified to release a screen buffer that is not actually
781      // * ours, then just ignore the notification...
782      // */
783     // if (ScreenBuffer != GuiData->ActiveBuffer) return;
784 
785     // /*
786      // * ... else, we must release our active buffer. Two cases are present:
787      // * - If ScreenBuffer (== GuiData->ActiveBuffer) IS NOT the console
788      // *   active screen buffer, then we can safely switch to it.
789      // * - If ScreenBuffer IS the console active screen buffer, we must release
790      // *   it ONLY.
791      // */
792 
793     // /* Release the old active palette and set the default one */
794     // if (GetCurrentObject(GuiData->hMemDC, OBJ_PAL) == ScreenBuffer->PaletteHandle)
795     // {
796         // /* Set the new palette */
797         // SelectPalette(GuiData->hMemDC, GuiData->hSysPalette, FALSE);
798     // }
799 
800     // /* Set the adequate active screen buffer */
801     // if (ScreenBuffer != GuiData->Console->ActiveBuffer)
802     // {
803         // GuiSetActiveScreenBuffer(This);
804     // }
805     // else
806     // {
807         // EnterCriticalSection(&GuiData->Lock);
808         // GuiData->WindowSizeLock = TRUE;
809 
810         // InterlockedExchangePointer(&GuiData->ActiveBuffer, NULL);
811 
812         // GuiData->WindowSizeLock = FALSE;
813         // LeaveCriticalSection(&GuiData->Lock);
814     // }
815 }
816 
817 static VOID NTAPI
818 TuiRefreshInternalInfo(IN OUT PFRONTEND This)
819 {
820 }
821 
822 static VOID NTAPI
823 TuiChangeTitle(IN OUT PFRONTEND This)
824 {
825 }
826 
827 static BOOL NTAPI
828 TuiChangeIcon(IN OUT PFRONTEND This,
829               HICON IconHandle)
830 {
831     return TRUE;
832 }
833 
834 static HDESK NTAPI
835 TuiGetThreadConsoleDesktop(IN OUT PFRONTEND This)
836 {
837     // PTUI_CONSOLE_DATA TuiData = This->Context;
838     return NULL;
839 }
840 
841 static HWND NTAPI
842 TuiGetConsoleWindowHandle(IN OUT PFRONTEND This)
843 {
844     PTUI_CONSOLE_DATA TuiData = This->Context;
845     return TuiData->hWindow;
846 }
847 
848 static VOID NTAPI
849 TuiGetLargestConsoleWindowSize(IN OUT PFRONTEND This,
850                                PCOORD pSize)
851 {
852     if (!pSize) return;
853     *pSize = PhysicalConsoleSize;
854 }
855 
856 static BOOL NTAPI
857 TuiGetSelectionInfo(IN OUT PFRONTEND This,
858                     PCONSOLE_SELECTION_INFO pSelectionInfo)
859 {
860     return TRUE;
861 }
862 
863 static BOOL NTAPI
864 TuiSetPalette(IN OUT PFRONTEND This,
865               HPALETTE PaletteHandle,
866               UINT PaletteUsage)
867 {
868     return TRUE;
869 }
870 
871 static ULONG NTAPI
872 TuiGetDisplayMode(IN OUT PFRONTEND This)
873 {
874     return CONSOLE_FULLSCREEN_HARDWARE; // CONSOLE_FULLSCREEN;
875 }
876 
877 static BOOL NTAPI
878 TuiSetDisplayMode(IN OUT PFRONTEND This,
879                   ULONG NewMode)
880 {
881     // if (NewMode & ~(CONSOLE_FULLSCREEN_MODE | CONSOLE_WINDOWED_MODE))
882     //     return FALSE;
883     return TRUE;
884 }
885 
886 static INT NTAPI
887 TuiShowMouseCursor(IN OUT PFRONTEND This,
888                    BOOL Show)
889 {
890     return 0;
891 }
892 
893 static BOOL NTAPI
894 TuiSetMouseCursor(IN OUT PFRONTEND This,
895                   HCURSOR CursorHandle)
896 {
897     return TRUE;
898 }
899 
900 static HMENU NTAPI
901 TuiMenuControl(IN OUT PFRONTEND This,
902                UINT CmdIdLow,
903                UINT CmdIdHigh)
904 {
905     return NULL;
906 }
907 
908 static BOOL NTAPI
909 TuiSetMenuClose(IN OUT PFRONTEND This,
910                 BOOL Enable)
911 {
912     return TRUE;
913 }
914 
915 static FRONTEND_VTBL TuiVtbl =
916 {
917     TuiInitFrontEnd,
918     TuiDeinitFrontEnd,
919     TuiDrawRegion,
920     TuiWriteStream,
921     TuiRingBell,
922     TuiSetCursorInfo,
923     TuiSetScreenInfo,
924     TuiResizeTerminal,
925     TuiSetActiveScreenBuffer,
926     TuiReleaseScreenBuffer,
927     TuiRefreshInternalInfo,
928     TuiChangeTitle,
929     TuiChangeIcon,
930     TuiGetThreadConsoleDesktop,
931     TuiGetConsoleWindowHandle,
932     TuiGetLargestConsoleWindowSize,
933     TuiGetSelectionInfo,
934     TuiSetPalette,
935     TuiGetDisplayMode,
936     TuiSetDisplayMode,
937     TuiShowMouseCursor,
938     TuiSetMouseCursor,
939     TuiMenuControl,
940     TuiSetMenuClose,
941 };
942 
943 static BOOLEAN
944 IsConsoleMode(VOID)
945 {
946     return (BOOLEAN)NtUserCallNoParam(NOPARAM_ROUTINE_ISCONSOLEMODE);
947 }
948 
949 NTSTATUS NTAPI
950 TuiLoadFrontEnd(IN OUT PFRONTEND FrontEnd,
951                 IN OUT PCONSOLE_STATE_INFO ConsoleInfo,
952                 IN OUT PCONSOLE_INIT_INFO ConsoleInitInfo,
953                 IN HANDLE ConsoleLeaderProcessHandle)
954 {
955     if (FrontEnd == NULL || ConsoleInfo == NULL)
956         return STATUS_INVALID_PARAMETER;
957 
958     /* We must be in console mode already */
959     if (!IsConsoleMode()) return STATUS_UNSUCCESSFUL;
960 
961     /* Initialize the TUI terminal emulator */
962     if (!TuiInit(ConsoleInfo->CodePage)) return STATUS_UNSUCCESSFUL;
963 
964     /* Finally, initialize the frontend structure */
965     FrontEnd->Vtbl     = &TuiVtbl;
966     FrontEnd->Context  = NULL;
967     FrontEnd->Context2 = NULL;
968 
969     return STATUS_SUCCESS;
970 }
971 
972 NTSTATUS NTAPI
973 TuiUnloadFrontEnd(IN OUT PFRONTEND FrontEnd)
974 {
975     if (FrontEnd == NULL) return STATUS_INVALID_PARAMETER;
976     if (FrontEnd->Context) TuiDeinitFrontEnd(FrontEnd);
977 
978     return STATUS_SUCCESS;
979 }
980 
981 #endif
982 
983 /* EOF */
984