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