1 /*
2  * COPYRIGHT:       See COPYING in the top level directory
3  * PROJECT:         ReactOS Console Server DLL
4  * FILE:            win32ss/user/winsrv/consrv/frontends/terminal.c
5  * PURPOSE:         ConSrv terminal.
6  * PROGRAMMERS:     Hermes Belusca-Maito (hermes.belusca@sfr.fr)
7  */
8 
9 /* INCLUDES *******************************************************************/
10 
11 #include <consrv.h>
12 
13 // #include "frontends/gui/guiterm.h"
14 #ifdef TUITERM_COMPILE
15 #include "frontends/tui/tuiterm.h"
16 #endif
17 
18 #define NDEBUG
19 #include <debug.h>
20 
21 
22 
23 
24 
25 /********** HACK HACK HACK HACK HACK HACK HACK HACK HACK HACK HACK ************/
26 
27 /* GLOBALS ********************************************************************/
28 
29 /*
30  * From MSDN:
31  * "The lpMultiByteStr and lpWideCharStr pointers must not be the same.
32  *  If they are the same, the function fails, and GetLastError returns
33  *  ERROR_INVALID_PARAMETER."
34  */
35 #define ConsoleInputUnicodeCharToAnsiChar(Console, dChar, sWChar) \
36     ASSERT((ULONG_PTR)dChar != (ULONG_PTR)sWChar); \
37     WideCharToMultiByte((Console)->InputCodePage, 0, (sWChar), 1, (dChar), 1, NULL, NULL)
38 
39 #define ConsoleInputAnsiCharToUnicodeChar(Console, dWChar, sChar) \
40     ASSERT((ULONG_PTR)dWChar != (ULONG_PTR)sChar); \
41     MultiByteToWideChar((Console)->InputCodePage, 0, (sChar), 1, (dWChar), 1)
42 
43 /* PRIVATE FUNCTIONS **********************************************************/
44 
45 #if 0
46 
47 static VOID
48 ConioInputEventToAnsi(PCONSOLE Console, PINPUT_RECORD InputEvent)
49 {
50     if (InputEvent->EventType == KEY_EVENT)
51     {
52         WCHAR UnicodeChar = InputEvent->Event.KeyEvent.uChar.UnicodeChar;
53         InputEvent->Event.KeyEvent.uChar.UnicodeChar = 0;
54         ConsoleInputUnicodeCharToAnsiChar(Console,
55                                           &InputEvent->Event.KeyEvent.uChar.AsciiChar,
56                                           &UnicodeChar);
57     }
58 }
59 
60 static VOID
61 ConioInputEventToUnicode(PCONSOLE Console, PINPUT_RECORD InputEvent)
62 {
63     if (InputEvent->EventType == KEY_EVENT)
64     {
65         CHAR AsciiChar = InputEvent->Event.KeyEvent.uChar.AsciiChar;
66         InputEvent->Event.KeyEvent.uChar.AsciiChar = 0;
67         ConsoleInputAnsiCharToUnicodeChar(Console,
68                                           &InputEvent->Event.KeyEvent.uChar.UnicodeChar,
69                                           &AsciiChar);
70     }
71 }
72 
73 #endif
74 
75 /********** HACK HACK HACK HACK HACK HACK HACK HACK HACK HACK HACK ************/
76 
77 
78 
79 
80 
81 
82 
83 
84 /* CONSRV TERMINAL FRONTENDS INTERFACE ****************************************/
85 
86 /***************/
87 #ifdef TUITERM_COMPILE
88 NTSTATUS NTAPI
89 TuiLoadFrontEnd(IN OUT PFRONTEND FrontEnd,
90                 IN OUT PCONSOLE_STATE_INFO ConsoleInfo,
91                 IN OUT PCONSOLE_INIT_INFO ConsoleInitInfo,
92                 IN HANDLE ConsoleLeaderProcessHandle);
93 NTSTATUS NTAPI
94 TuiUnloadFrontEnd(IN OUT PFRONTEND FrontEnd);
95 #endif
96 
97 NTSTATUS NTAPI
98 GuiLoadFrontEnd(IN OUT PFRONTEND FrontEnd,
99                 IN OUT PCONSOLE_STATE_INFO ConsoleInfo,
100                 IN OUT PCONSOLE_INIT_INFO ConsoleInitInfo,
101                 IN HANDLE ConsoleLeaderProcessHandle);
102 NTSTATUS NTAPI
103 GuiUnloadFrontEnd(IN OUT PFRONTEND FrontEnd);
104 /***************/
105 
106 typedef
107 NTSTATUS (NTAPI *FRONTEND_LOAD)(IN OUT PFRONTEND FrontEnd,
108                                 IN OUT PCONSOLE_STATE_INFO ConsoleInfo,
109                                 IN OUT PCONSOLE_INIT_INFO ConsoleInitInfo,
110                                 IN HANDLE ConsoleLeaderProcessHandle);
111 
112 typedef
113 NTSTATUS (NTAPI *FRONTEND_UNLOAD)(IN OUT PFRONTEND FrontEnd);
114 
115 /*
116  * If we are not in GUI-mode, start the text-mode terminal emulator.
117  * If we fail, try to start the GUI-mode terminal emulator.
118  *
119  * Try to open the GUI-mode terminal emulator. Two cases are possible:
120  * - We are in GUI-mode, therefore GuiMode == TRUE, the previous test-case
121  *   failed and we start GUI-mode terminal emulator.
122  * - We are in text-mode, therefore GuiMode == FALSE, the previous test-case
123  *   succeeded BUT we failed at starting text-mode terminal emulator.
124  *   Then GuiMode was switched to TRUE in order to try to open the GUI-mode
125  *   terminal emulator (Win32k will automatically switch to graphical mode,
126  *   therefore no additional code is needed).
127  */
128 
129 /*
130  * NOTE: Each entry of the table should be retrieved when loading a front-end
131  *       (examples of the CSR servers which register some data for CSRSS).
132  */
133 static struct
134 {
135     CHAR            FrontEndName[80];
136     FRONTEND_LOAD   FrontEndLoad;
137     FRONTEND_UNLOAD FrontEndUnload;
138 } FrontEndLoadingMethods[] =
139 {
140 #ifdef TUITERM_COMPILE
141     {"TUI", TuiLoadFrontEnd,    TuiUnloadFrontEnd},
142 #endif
143     {"GUI", GuiLoadFrontEnd,    GuiUnloadFrontEnd},
144 
145 //  {"Not found", 0, NULL}
146 };
147 
148 static NTSTATUS
149 ConSrvLoadFrontEnd(IN OUT PFRONTEND FrontEnd,
150                    IN OUT PCONSOLE_STATE_INFO ConsoleInfo,
151                    IN OUT PCONSOLE_INIT_INFO ConsoleInitInfo,
152                    IN HANDLE ConsoleLeaderProcessHandle)
153 {
154     NTSTATUS Status = STATUS_SUCCESS;
155     ULONG i;
156 
157     /*
158      * Choose an adequate terminal front-end to load, and load it
159      */
160     for (i = 0; i < ARRAYSIZE(FrontEndLoadingMethods); ++i)
161     {
162         DPRINT("CONSRV: Trying to load %s frontend...\n",
163                FrontEndLoadingMethods[i].FrontEndName);
164         Status = FrontEndLoadingMethods[i].FrontEndLoad(FrontEnd,
165                                                         ConsoleInfo,
166                                                         ConsoleInitInfo,
167                                                         ConsoleLeaderProcessHandle);
168         if (NT_SUCCESS(Status))
169         {
170             /* Save the unload callback */
171             FrontEnd->UnloadFrontEnd = FrontEndLoadingMethods[i].FrontEndUnload;
172 
173             DPRINT("CONSRV: %s frontend loaded successfully\n",
174                    FrontEndLoadingMethods[i].FrontEndName);
175             break;
176         }
177         else
178         {
179             DPRINT1("CONSRV: Loading %s frontend failed, Status = 0x%08lx , continuing...\n",
180                     FrontEndLoadingMethods[i].FrontEndName, Status);
181         }
182     }
183 
184     return Status;
185 }
186 
187 static NTSTATUS
188 ConSrvUnloadFrontEnd(IN PFRONTEND FrontEnd)
189 {
190     if (FrontEnd == NULL) return STATUS_INVALID_PARAMETER;
191     // return FrontEnd->Vtbl->UnloadFrontEnd(FrontEnd);
192     return FrontEnd->UnloadFrontEnd(FrontEnd);
193 }
194 
195 // See after...
196 static TERMINAL_VTBL ConSrvTermVtbl;
197 
198 NTSTATUS NTAPI
199 ConSrvInitTerminal(IN OUT PTERMINAL Terminal,
200                    IN OUT PCONSOLE_STATE_INFO ConsoleInfo,
201                    IN OUT PCONSOLE_INIT_INFO ConsoleInitInfo,
202                    IN HANDLE ConsoleLeaderProcessHandle)
203 {
204     NTSTATUS Status;
205     PFRONTEND FrontEnd;
206 
207     /* Load a suitable frontend for the ConSrv terminal */
208     FrontEnd = ConsoleAllocHeap(HEAP_ZERO_MEMORY, sizeof(*FrontEnd));
209     if (!FrontEnd) return STATUS_NO_MEMORY;
210 
211     Status = ConSrvLoadFrontEnd(FrontEnd,
212                                 ConsoleInfo,
213                                 ConsoleInitInfo,
214                                 ConsoleLeaderProcessHandle);
215     if (!NT_SUCCESS(Status))
216     {
217         DPRINT1("CONSRV: Failed to initialize a frontend, Status = 0x%08lx\n", Status);
218         ConsoleFreeHeap(FrontEnd);
219         return Status;
220     }
221     DPRINT("CONSRV: Frontend initialized\n");
222 
223     /* Initialize the ConSrv terminal */
224     Terminal->Vtbl = &ConSrvTermVtbl;
225     // Terminal->Console will be initialized by ConDrvAttachTerminal
226     Terminal->Context = FrontEnd; /* We store the frontend pointer in the terminal private context */
227 
228     return STATUS_SUCCESS;
229 }
230 
231 NTSTATUS NTAPI
232 ConSrvDeinitTerminal(IN OUT PTERMINAL Terminal)
233 {
234     NTSTATUS Status = STATUS_SUCCESS;
235     PFRONTEND FrontEnd = Terminal->Context;
236 
237     /* Reset the ConSrv terminal */
238     Terminal->Context = NULL;
239     Terminal->Vtbl = NULL;
240 
241     /* Unload the frontend */
242     if (FrontEnd != NULL)
243     {
244         Status = ConSrvUnloadFrontEnd(FrontEnd);
245         ConsoleFreeHeap(FrontEnd);
246     }
247 
248     return Status;
249 }
250 
251 
252 /* CONSRV TERMINAL INTERFACE **************************************************/
253 
254 static NTSTATUS NTAPI
255 ConSrvTermInitTerminal(IN OUT PTERMINAL This,
256                        IN PCONSOLE Console)
257 {
258     NTSTATUS Status;
259     PFRONTEND FrontEnd = This->Context;
260 
261     /* Initialize the console pointer for our frontend */
262     FrontEnd->Console = Console;
263 
264     /** HACK HACK!! Copy FrontEnd into the console!! **/
265     DPRINT("Using FrontEndIFace HACK(1), should be removed after proper implementation!\n");
266     Console->FrontEndIFace = *FrontEnd;
267 
268     Status = FrontEnd->Vtbl->InitFrontEnd(FrontEnd, FrontEnd->Console);
269     if (!NT_SUCCESS(Status))
270         DPRINT1("InitFrontEnd failed, Status = 0x%08lx\n", Status);
271 
272     /** HACK HACK!! Be sure FrontEndIFace is correctly updated in the console!! **/
273     DPRINT("Using FrontEndIFace HACK(2), should be removed after proper implementation!\n");
274     Console->FrontEndIFace = *FrontEnd;
275 
276     return Status;
277 }
278 
279 static VOID NTAPI
280 ConSrvTermDeinitTerminal(IN OUT PTERMINAL This)
281 {
282     PFRONTEND FrontEnd = This->Context;
283     FrontEnd->Vtbl->DeinitFrontEnd(FrontEnd);
284 }
285 
286 
287 
288 /************ Line discipline ***************/
289 
290 static NTSTATUS NTAPI
291 ConSrvTermReadStream(IN OUT PTERMINAL This,
292                      IN BOOLEAN Unicode,
293                      /**PWCHAR Buffer,**/
294                      OUT PVOID Buffer,
295                      IN OUT PCONSOLE_READCONSOLE_CONTROL ReadControl,
296                      IN PVOID Parameter OPTIONAL,
297                      IN ULONG NumCharsToRead,
298                      OUT PULONG NumCharsRead OPTIONAL)
299 {
300     PFRONTEND FrontEnd = This->Context;
301     PCONSRV_CONSOLE Console = FrontEnd->Console;
302     PCONSOLE_INPUT_BUFFER InputBuffer = &Console->InputBuffer;
303     PUNICODE_STRING ExeName = Parameter;
304 
305     // STATUS_PENDING : Wait if more to read ; STATUS_SUCCESS : Don't wait.
306     NTSTATUS Status = STATUS_PENDING;
307 
308     PLIST_ENTRY CurrentEntry;
309     ConsoleInput *Input;
310     ULONG i = 0;
311 
312     /* Validity checks */
313     // ASSERT(Console == InputBuffer->Header.Console);
314     ASSERT((Buffer != NULL) || (Buffer == NULL && NumCharsToRead == 0));
315 
316     /* We haven't read anything (yet) */
317 
318     if (InputBuffer->Mode & ENABLE_LINE_INPUT)
319     {
320         /* COOKED mode, call the line discipline */
321 
322         if (Console->LineBuffer == NULL)
323         {
324             /* Start a new line */
325             Console->LineMaxSize = max(256, NumCharsToRead);
326 
327             /*
328              * Fixup ReadControl->nInitialChars in case the number of initial
329              * characters is bigger than the number of characters to be read.
330              * It will always be, lesser than or equal to Console->LineMaxSize.
331              */
332             ReadControl->nInitialChars = min(ReadControl->nInitialChars, NumCharsToRead);
333 
334             Console->LineBuffer = ConsoleAllocHeap(0, Console->LineMaxSize * sizeof(WCHAR));
335             if (Console->LineBuffer == NULL) return STATUS_NO_MEMORY;
336 
337             Console->LinePos = Console->LineSize = ReadControl->nInitialChars;
338             Console->LineComplete = Console->LineUpPressed = FALSE;
339             Console->LineInsertToggle = Console->InsertMode;
340             Console->LineWakeupMask = ReadControl->dwCtrlWakeupMask;
341 
342             /*
343              * Pre-fill the buffer with the nInitialChars from the user buffer.
344              * Since pre-filling is only allowed in Unicode, we don't need to
345              * worry about ANSI <-> Unicode conversion.
346              */
347             memcpy(Console->LineBuffer, Buffer, Console->LineSize * sizeof(WCHAR));
348             if (Console->LineSize >= Console->LineMaxSize)
349             {
350                 Console->LineComplete = TRUE;
351                 Console->LinePos = 0;
352             }
353         }
354 
355         /* If we don't have a complete line yet, process the pending input */
356         while (!Console->LineComplete && !IsListEmpty(&InputBuffer->InputEvents))
357         {
358             /* Remove an input event from the queue */
359             CurrentEntry = RemoveHeadList(&InputBuffer->InputEvents);
360             if (IsListEmpty(&InputBuffer->InputEvents))
361             {
362                 ResetEvent(InputBuffer->ActiveEvent);
363             }
364             Input = CONTAINING_RECORD(CurrentEntry, ConsoleInput, ListEntry);
365 
366             /* Only pay attention to key down */
367             if (Input->InputEvent.EventType == KEY_EVENT &&
368                 Input->InputEvent.Event.KeyEvent.bKeyDown)
369             {
370                 LineInputKeyDown(Console, ExeName,
371                                  &Input->InputEvent.Event.KeyEvent);
372                 ReadControl->dwControlKeyState = Input->InputEvent.Event.KeyEvent.dwControlKeyState;
373             }
374             ConsoleFreeHeap(Input);
375         }
376 
377         /* Check if we have a complete line to read from */
378         if (Console->LineComplete)
379         {
380             /*
381              * Console->LinePos keeps the next position of the character to read
382              * in the line buffer across the different calls of the function,
383              * so that the line buffer can be read by chunks after all the input
384              * has been buffered.
385              */
386 
387             while (i < NumCharsToRead && Console->LinePos < Console->LineSize)
388             {
389                 WCHAR Char = Console->LineBuffer[Console->LinePos++];
390 
391                 if (Unicode)
392                 {
393                     ((PWCHAR)Buffer)[i] = Char;
394                 }
395                 else
396                 {
397                     ConsoleInputUnicodeCharToAnsiChar(Console, &((PCHAR)Buffer)[i], &Char);
398                 }
399                 ++i;
400             }
401 
402             if (Console->LinePos >= Console->LineSize)
403             {
404                 /* The entire line has been read */
405                 ConsoleFreeHeap(Console->LineBuffer);
406                 Console->LineBuffer = NULL;
407                 Console->LinePos = Console->LineMaxSize = Console->LineSize = 0;
408                 // Console->LineComplete = Console->LineUpPressed = FALSE;
409                 Console->LineComplete = FALSE;
410             }
411 
412             Status = STATUS_SUCCESS;
413         }
414     }
415     else
416     {
417         /* RAW mode */
418 
419         /* Character input */
420         while (i < NumCharsToRead && !IsListEmpty(&InputBuffer->InputEvents))
421         {
422             /* Remove an input event from the queue */
423             CurrentEntry = RemoveHeadList(&InputBuffer->InputEvents);
424             if (IsListEmpty(&InputBuffer->InputEvents))
425             {
426                 ResetEvent(InputBuffer->ActiveEvent);
427             }
428             Input = CONTAINING_RECORD(CurrentEntry, ConsoleInput, ListEntry);
429 
430             /* Only pay attention to valid characters, on key down */
431             if (Input->InputEvent.EventType == KEY_EVENT  &&
432                 Input->InputEvent.Event.KeyEvent.bKeyDown &&
433                 Input->InputEvent.Event.KeyEvent.uChar.UnicodeChar != L'\0')
434             {
435                 WCHAR Char = Input->InputEvent.Event.KeyEvent.uChar.UnicodeChar;
436 
437                 if (Unicode)
438                 {
439                     ((PWCHAR)Buffer)[i] = Char;
440                 }
441                 else
442                 {
443                     ConsoleInputUnicodeCharToAnsiChar(Console, &((PCHAR)Buffer)[i], &Char);
444                 }
445                 ++i;
446 
447                 /* Did read something */
448                 Status = STATUS_SUCCESS;
449             }
450             ConsoleFreeHeap(Input);
451         }
452     }
453 
454     // FIXME: Only set if Status == STATUS_SUCCESS ???
455     if (NumCharsRead) *NumCharsRead = i;
456 
457     return Status;
458 }
459 
460 
461 
462 
463 /* GLOBALS ********************************************************************/
464 
465 #define TAB_WIDTH   8
466 
467 // See condrv/text.c
468 /*static*/ VOID
469 ClearLineBuffer(PTEXTMODE_SCREEN_BUFFER Buff);
470 
471 static VOID
472 ConioNextLine(PTEXTMODE_SCREEN_BUFFER Buff, PSMALL_RECT UpdateRect, PUINT ScrolledLines)
473 {
474     /* If we hit bottom, slide the viewable screen */
475     if (++Buff->CursorPosition.Y == Buff->ScreenBufferSize.Y)
476     {
477         Buff->CursorPosition.Y--;
478         if (++Buff->VirtualY == Buff->ScreenBufferSize.Y)
479         {
480             Buff->VirtualY = 0;
481         }
482         (*ScrolledLines)++;
483         ClearLineBuffer(Buff);
484         if (UpdateRect->Top != 0)
485         {
486             UpdateRect->Top--;
487         }
488     }
489     UpdateRect->Left = 0;
490     UpdateRect->Right = Buff->ScreenBufferSize.X - 1;
491     UpdateRect->Bottom = Buff->CursorPosition.Y;
492 }
493 
494 static NTSTATUS
495 ConioWriteConsole(PFRONTEND FrontEnd,
496                   PTEXTMODE_SCREEN_BUFFER Buff,
497                   PWCHAR Buffer,
498                   DWORD Length,
499                   BOOL Attrib)
500 {
501     PCONSRV_CONSOLE Console = FrontEnd->Console;
502 
503     UINT i;
504     PCHAR_INFO Ptr;
505     SMALL_RECT UpdateRect;
506     SHORT CursorStartX, CursorStartY;
507     UINT ScrolledLines;
508 
509     CursorStartX = Buff->CursorPosition.X;
510     CursorStartY = Buff->CursorPosition.Y;
511     UpdateRect.Left = Buff->ScreenBufferSize.X;
512     UpdateRect.Top = Buff->CursorPosition.Y;
513     UpdateRect.Right = -1;
514     UpdateRect.Bottom = Buff->CursorPosition.Y;
515     ScrolledLines = 0;
516 
517     for (i = 0; i < Length; i++)
518     {
519         /*
520          * If we are in processed mode, interpret special characters and
521          * display them correctly. Otherwise, just put them into the buffer.
522          */
523         if (Buff->Mode & ENABLE_PROCESSED_OUTPUT)
524         {
525             /* --- CR --- */
526             if (Buffer[i] == L'\r')
527             {
528                 Buff->CursorPosition.X = 0;
529                 UpdateRect.Left = min(UpdateRect.Left, Buff->CursorPosition.X);
530                 UpdateRect.Right = max(UpdateRect.Right, Buff->CursorPosition.X);
531                 continue;
532             }
533             /* --- LF --- */
534             else if (Buffer[i] == L'\n')
535             {
536                 Buff->CursorPosition.X = 0;
537                 ConioNextLine(Buff, &UpdateRect, &ScrolledLines);
538                 continue;
539             }
540             /* --- BS --- */
541             else if (Buffer[i] == L'\b')
542             {
543                 /* Only handle BS if we're not on the first pos of the first line */
544                 if (0 != Buff->CursorPosition.X || 0 != Buff->CursorPosition.Y)
545                 {
546                     if (0 == Buff->CursorPosition.X)
547                     {
548                         /* slide virtual position up */
549                         Buff->CursorPosition.X = Buff->ScreenBufferSize.X - 1;
550                         Buff->CursorPosition.Y--;
551                         UpdateRect.Top = min(UpdateRect.Top, Buff->CursorPosition.Y);
552                     }
553                     else
554                     {
555                         Buff->CursorPosition.X--;
556                     }
557                     Ptr = ConioCoordToPointer(Buff, Buff->CursorPosition.X, Buff->CursorPosition.Y);
558                     Ptr->Char.UnicodeChar = L' ';
559                     Ptr->Attributes  = Buff->ScreenDefaultAttrib;
560                     UpdateRect.Left  = min(UpdateRect.Left, Buff->CursorPosition.X);
561                     UpdateRect.Right = max(UpdateRect.Right, Buff->CursorPosition.X);
562                 }
563                 continue;
564             }
565             /* --- TAB --- */
566             else if (Buffer[i] == L'\t')
567             {
568                 UINT EndX;
569 
570                 UpdateRect.Left = min(UpdateRect.Left, Buff->CursorPosition.X);
571                 EndX = (Buff->CursorPosition.X + TAB_WIDTH) & ~(TAB_WIDTH - 1);
572                 EndX = min(EndX, (UINT)Buff->ScreenBufferSize.X);
573                 Ptr = ConioCoordToPointer(Buff, Buff->CursorPosition.X, Buff->CursorPosition.Y);
574                 while ((UINT)Buff->CursorPosition.X < EndX)
575                 {
576                     Ptr->Char.UnicodeChar = L' ';
577                     Ptr->Attributes = Buff->ScreenDefaultAttrib;
578                     ++Ptr;
579                     Buff->CursorPosition.X++;
580                 }
581                 UpdateRect.Right = max(UpdateRect.Right, Buff->CursorPosition.X - 1);
582                 if (Buff->CursorPosition.X == Buff->ScreenBufferSize.X)
583                 {
584                     if (Buff->Mode & ENABLE_WRAP_AT_EOL_OUTPUT)
585                     {
586                         Buff->CursorPosition.X = 0;
587                         ConioNextLine(Buff, &UpdateRect, &ScrolledLines);
588                     }
589                     else
590                     {
591                         Buff->CursorPosition.X--;
592                     }
593                 }
594                 continue;
595             }
596             /* --- BEL ---*/
597             else if (Buffer[i] == L'\a')
598             {
599                 FrontEnd->Vtbl->RingBell(FrontEnd);
600                 continue;
601             }
602         }
603         UpdateRect.Left  = min(UpdateRect.Left, Buff->CursorPosition.X);
604         UpdateRect.Right = max(UpdateRect.Right, Buff->CursorPosition.X);
605 
606         Ptr = ConioCoordToPointer(Buff, Buff->CursorPosition.X, Buff->CursorPosition.Y);
607         Ptr->Char.UnicodeChar = Buffer[i];
608         if (Attrib) Ptr->Attributes = Buff->ScreenDefaultAttrib;
609 
610         Buff->CursorPosition.X++;
611         if (Buff->CursorPosition.X == Buff->ScreenBufferSize.X)
612         {
613             if (Buff->Mode & ENABLE_WRAP_AT_EOL_OUTPUT)
614             {
615                 Buff->CursorPosition.X = 0;
616                 ConioNextLine(Buff, &UpdateRect, &ScrolledLines);
617             }
618             else
619             {
620                 Buff->CursorPosition.X = CursorStartX;
621             }
622         }
623     }
624 
625     if (!ConioIsRectEmpty(&UpdateRect) && (PCONSOLE_SCREEN_BUFFER)Buff == Console->ActiveBuffer)
626     {
627         // TermWriteStream(Console, &UpdateRect, CursorStartX, CursorStartY,
628                         // ScrolledLines, Buffer, Length);
629         FrontEnd->Vtbl->WriteStream(FrontEnd,
630                                     &UpdateRect,
631                                     CursorStartX,
632                                     CursorStartY,
633                                     ScrolledLines,
634                                     Buffer,
635                                     Length);
636     }
637 
638     return STATUS_SUCCESS;
639 }
640 
641 
642 
643 static NTSTATUS NTAPI
644 ConSrvTermWriteStream(IN OUT PTERMINAL This,
645                       PTEXTMODE_SCREEN_BUFFER Buff,
646                       PWCHAR Buffer,
647                       DWORD Length,
648                       BOOL Attrib)
649 {
650     PFRONTEND FrontEnd = This->Context;
651     return ConioWriteConsole(FrontEnd,
652                              Buff,
653                              Buffer,
654                              Length,
655                              Attrib);
656 }
657 
658 /************ Line discipline ***************/
659 
660 
661 
662 VOID
663 ConioDrawConsole(PCONSRV_CONSOLE Console)
664 {
665     SMALL_RECT Region;
666     PCONSOLE_SCREEN_BUFFER ActiveBuffer = Console->ActiveBuffer;
667 
668     if (!ActiveBuffer) return;
669 
670     ConioInitRect(&Region, 0, 0,
671                   ActiveBuffer->ViewSize.Y - 1,
672                   ActiveBuffer->ViewSize.X - 1);
673     TermDrawRegion(Console, &Region);
674     // Console->FrontEndIFace.Vtbl->DrawRegion(&Console->FrontEndIFace, &Region);
675 }
676 
677 static VOID NTAPI
678 ConSrvTermDrawRegion(IN OUT PTERMINAL This,
679                 SMALL_RECT* Region)
680 {
681     PFRONTEND FrontEnd = This->Context;
682     FrontEnd->Vtbl->DrawRegion(FrontEnd, Region);
683 }
684 
685 static BOOL NTAPI
686 ConSrvTermSetCursorInfo(IN OUT PTERMINAL This,
687                    PCONSOLE_SCREEN_BUFFER ScreenBuffer)
688 {
689     PFRONTEND FrontEnd = This->Context;
690     return FrontEnd->Vtbl->SetCursorInfo(FrontEnd, ScreenBuffer);
691 }
692 
693 static BOOL NTAPI
694 ConSrvTermSetScreenInfo(IN OUT PTERMINAL This,
695                    PCONSOLE_SCREEN_BUFFER ScreenBuffer,
696                    SHORT OldCursorX,
697                    SHORT OldCursorY)
698 {
699     PFRONTEND FrontEnd = This->Context;
700     return FrontEnd->Vtbl->SetScreenInfo(FrontEnd,
701                                          ScreenBuffer,
702                                          OldCursorX,
703                                          OldCursorY);
704 }
705 
706 static VOID NTAPI
707 ConSrvTermResizeTerminal(IN OUT PTERMINAL This)
708 {
709     PFRONTEND FrontEnd = This->Context;
710     FrontEnd->Vtbl->ResizeTerminal(FrontEnd);
711 }
712 
713 static VOID NTAPI
714 ConSrvTermSetActiveScreenBuffer(IN OUT PTERMINAL This)
715 {
716     PFRONTEND FrontEnd = This->Context;
717     FrontEnd->Vtbl->SetActiveScreenBuffer(FrontEnd);
718 }
719 
720 static VOID NTAPI
721 ConSrvTermReleaseScreenBuffer(IN OUT PTERMINAL This,
722                          IN PCONSOLE_SCREEN_BUFFER ScreenBuffer)
723 {
724     PFRONTEND FrontEnd = This->Context;
725     FrontEnd->Vtbl->ReleaseScreenBuffer(FrontEnd, ScreenBuffer);
726 }
727 
728 static VOID NTAPI
729 ConSrvTermGetLargestConsoleWindowSize(IN OUT PTERMINAL This,
730                                  PCOORD pSize)
731 {
732     PFRONTEND FrontEnd = This->Context;
733     FrontEnd->Vtbl->GetLargestConsoleWindowSize(FrontEnd, pSize);
734 }
735 
736 static BOOL NTAPI
737 ConSrvTermSetPalette(IN OUT PTERMINAL This,
738                 HPALETTE PaletteHandle,
739                 UINT PaletteUsage)
740 {
741     PFRONTEND FrontEnd = This->Context;
742     return FrontEnd->Vtbl->SetPalette(FrontEnd, PaletteHandle, PaletteUsage);
743 }
744 
745 static INT NTAPI
746 ConSrvTermShowMouseCursor(IN OUT PTERMINAL This,
747                      BOOL Show)
748 {
749     PFRONTEND FrontEnd = This->Context;
750     return FrontEnd->Vtbl->ShowMouseCursor(FrontEnd, Show);
751 }
752 
753 static TERMINAL_VTBL ConSrvTermVtbl =
754 {
755     ConSrvTermInitTerminal,
756     ConSrvTermDeinitTerminal,
757 
758     ConSrvTermReadStream,
759     ConSrvTermWriteStream,
760 
761     ConSrvTermDrawRegion,
762     ConSrvTermSetCursorInfo,
763     ConSrvTermSetScreenInfo,
764     ConSrvTermResizeTerminal,
765     ConSrvTermSetActiveScreenBuffer,
766     ConSrvTermReleaseScreenBuffer,
767     ConSrvTermGetLargestConsoleWindowSize,
768     ConSrvTermSetPalette,
769     ConSrvTermShowMouseCursor,
770 };
771 
772 #if 0
773 VOID
774 ResetFrontEnd(IN PCONSOLE Console)
775 {
776     if (!Console) return;
777 
778     /* Reinitialize the frontend interface */
779     RtlZeroMemory(&Console->FrontEndIFace, sizeof(Console->FrontEndIFace));
780     Console->FrontEndIFace.Vtbl = &ConSrvTermVtbl;
781 }
782 #endif
783 
784 /* EOF */
785