[CMAKE] winetests/: Move '/wd4334' to 3 sub-modules only
[reactos.git] / win32ss / user / winsrv / consrv / frontends / terminal.c
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
904 ConSrvTermResizeTerminal(IN OUT PTERMINAL This)
905 {
906 PFRONTEND FrontEnd = This->Context;
907 FrontEnd->Vtbl->ResizeTerminal(FrontEnd);
908 }
909
910 static VOID NTAPI
911 ConSrvTermSetActiveScreenBuffer(IN OUT PTERMINAL This)
912 {
913 PFRONTEND FrontEnd = This->Context;
914 FrontEnd->Vtbl->SetActiveScreenBuffer(FrontEnd);
915 }
916
917 static VOID NTAPI
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
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
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 INT NTAPI
943 ConSrvTermShowMouseCursor(IN OUT PTERMINAL This,
944 BOOL Show)
945 {
946 PFRONTEND FrontEnd = This->Context;
947 return FrontEnd->Vtbl->ShowMouseCursor(FrontEnd, Show);
948 }
949
950 static TERMINAL_VTBL ConSrvTermVtbl =
951 {
952 ConSrvTermInitTerminal,
953 ConSrvTermDeinitTerminal,
954
955 ConSrvTermReadStream,
956 ConSrvTermWriteStream,
957
958 ConSrvTermDrawRegion,
959 ConSrvTermSetCursorInfo,
960 ConSrvTermSetScreenInfo,
961 ConSrvTermResizeTerminal,
962 ConSrvTermSetActiveScreenBuffer,
963 ConSrvTermReleaseScreenBuffer,
964 ConSrvTermGetLargestConsoleWindowSize,
965 ConSrvTermSetPalette,
966 ConSrvTermShowMouseCursor,
967 };
968
969 #if 0
970 VOID
971 ResetFrontEnd(IN PCONSOLE Console)
972 {
973 PCONSRV_CONSOLE ConSrvConsole = (PCONSRV_CONSOLE)Console;
974 if (!Console) return;
975
976 /* Reinitialize the frontend interface */
977 RtlZeroMemory(&ConSrvConsole->FrontEndIFace, sizeof(ConSrvConsole->FrontEndIFace));
978 ConSrvConsole->FrontEndIFace.Vtbl = &ConSrvTermVtbl;
979 }
980 #endif
981
982 /* EOF */