ece6bb29933d20c5ea054899bd4772a021299660
[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
266 /* Initialize the console pointer for our frontend */
267 FrontEnd->Console = Console;
268
269 /** HACK HACK!! Copy FrontEnd into the console!! **/
270 DPRINT("Using FrontEndIFace HACK(1), should be removed after proper implementation!\n");
271 Console->FrontEndIFace = *FrontEnd;
272
273 Status = FrontEnd->Vtbl->InitFrontEnd(FrontEnd, FrontEnd->Console);
274 if (!NT_SUCCESS(Status))
275 DPRINT1("InitFrontEnd failed, Status = 0x%08lx\n", Status);
276
277 /** HACK HACK!! Be sure FrontEndIFace is correctly updated in the console!! **/
278 DPRINT("Using FrontEndIFace HACK(2), should be removed after proper implementation!\n");
279 Console->FrontEndIFace = *FrontEnd;
280
281 return Status;
282 }
283
284 static VOID NTAPI
285 ConSrvTermDeinitTerminal(IN OUT PTERMINAL This)
286 {
287 PFRONTEND FrontEnd = This->Context;
288 FrontEnd->Vtbl->DeinitFrontEnd(FrontEnd);
289 }
290
291
292
293 /************ Line discipline ***************/
294
295 static NTSTATUS NTAPI
296 ConSrvTermReadStream(IN OUT PTERMINAL This,
297 IN BOOLEAN Unicode,
298 /**PWCHAR Buffer,**/
299 OUT PVOID Buffer,
300 IN OUT PCONSOLE_READCONSOLE_CONTROL ReadControl,
301 IN PVOID Parameter OPTIONAL,
302 IN ULONG NumCharsToRead,
303 OUT PULONG NumCharsRead OPTIONAL)
304 {
305 PFRONTEND FrontEnd = This->Context;
306 PCONSRV_CONSOLE Console = FrontEnd->Console;
307 PCONSOLE_INPUT_BUFFER InputBuffer = &Console->InputBuffer;
308 PUNICODE_STRING ExeName = Parameter;
309
310 // STATUS_PENDING : Wait if more to read ; STATUS_SUCCESS : Don't wait.
311 NTSTATUS Status = STATUS_PENDING;
312
313 PLIST_ENTRY CurrentEntry;
314 ConsoleInput *Input;
315 ULONG i = 0;
316
317 /* Validity checks */
318 // ASSERT(Console == InputBuffer->Header.Console);
319 ASSERT((Buffer != NULL) || (Buffer == NULL && NumCharsToRead == 0));
320
321 /* We haven't read anything (yet) */
322
323 if (InputBuffer->Mode & ENABLE_LINE_INPUT)
324 {
325 /* COOKED mode, call the line discipline */
326
327 if (Console->LineBuffer == NULL)
328 {
329 /* Start a new line */
330 Console->LineMaxSize = max(256, NumCharsToRead);
331
332 /*
333 * Fixup ReadControl->nInitialChars in case the number of initial
334 * characters is bigger than the number of characters to be read.
335 * It will always be, lesser than or equal to Console->LineMaxSize.
336 */
337 ReadControl->nInitialChars = min(ReadControl->nInitialChars, NumCharsToRead);
338
339 Console->LineBuffer = ConsoleAllocHeap(0, Console->LineMaxSize * sizeof(WCHAR));
340 if (Console->LineBuffer == NULL) return STATUS_NO_MEMORY;
341
342 Console->LinePos = Console->LineSize = ReadControl->nInitialChars;
343 Console->LineComplete = Console->LineUpPressed = FALSE;
344 Console->LineInsertToggle = Console->InsertMode;
345 Console->LineWakeupMask = ReadControl->dwCtrlWakeupMask;
346
347 /*
348 * Pre-fill the buffer with the nInitialChars from the user buffer.
349 * Since pre-filling is only allowed in Unicode, we don't need to
350 * worry about ANSI <-> Unicode conversion.
351 */
352 memcpy(Console->LineBuffer, Buffer, Console->LineSize * sizeof(WCHAR));
353 if (Console->LineSize >= Console->LineMaxSize)
354 {
355 Console->LineComplete = TRUE;
356 Console->LinePos = 0;
357 }
358 }
359
360 /* If we don't have a complete line yet, process the pending input */
361 while (!Console->LineComplete && !IsListEmpty(&InputBuffer->InputEvents))
362 {
363 /* Remove an input event from the queue */
364 _InterlockedDecrement((PLONG)&InputBuffer->NumberOfEvents);
365 CurrentEntry = RemoveHeadList(&InputBuffer->InputEvents);
366 if (IsListEmpty(&InputBuffer->InputEvents))
367 {
368 ResetEvent(InputBuffer->ActiveEvent);
369 }
370 Input = CONTAINING_RECORD(CurrentEntry, ConsoleInput, ListEntry);
371
372 /* Only pay attention to key down */
373 if (Input->InputEvent.EventType == KEY_EVENT &&
374 Input->InputEvent.Event.KeyEvent.bKeyDown)
375 {
376 LineInputKeyDown(Console, ExeName,
377 &Input->InputEvent.Event.KeyEvent);
378 ReadControl->dwControlKeyState = Input->InputEvent.Event.KeyEvent.dwControlKeyState;
379 }
380 ConsoleFreeHeap(Input);
381 }
382
383 /* Check if we have a complete line to read from */
384 if (Console->LineComplete)
385 {
386 /*
387 * Console->LinePos keeps the next position of the character to read
388 * in the line buffer across the different calls of the function,
389 * so that the line buffer can be read by chunks after all the input
390 * has been buffered.
391 */
392
393 while (i < NumCharsToRead && Console->LinePos < Console->LineSize)
394 {
395 WCHAR Char = Console->LineBuffer[Console->LinePos++];
396
397 if (Unicode)
398 {
399 ((PWCHAR)Buffer)[i] = Char;
400 }
401 else
402 {
403 ConsoleInputUnicodeCharToAnsiChar(Console, &((PCHAR)Buffer)[i], &Char);
404 }
405 ++i;
406 }
407
408 if (Console->LinePos >= Console->LineSize)
409 {
410 /* The entire line has been read */
411 ConsoleFreeHeap(Console->LineBuffer);
412 Console->LineBuffer = NULL;
413 Console->LinePos = Console->LineMaxSize = Console->LineSize = 0;
414 // Console->LineComplete = Console->LineUpPressed = FALSE;
415 Console->LineComplete = FALSE;
416 }
417
418 Status = STATUS_SUCCESS;
419 }
420 }
421 else
422 {
423 /* RAW mode */
424
425 /* Character input */
426 while (i < NumCharsToRead && !IsListEmpty(&InputBuffer->InputEvents))
427 {
428 /* Remove an input event from the queue */
429 _InterlockedDecrement((PLONG)&InputBuffer->NumberOfEvents);
430 CurrentEntry = RemoveHeadList(&InputBuffer->InputEvents);
431 if (IsListEmpty(&InputBuffer->InputEvents))
432 {
433 ResetEvent(InputBuffer->ActiveEvent);
434 }
435 Input = CONTAINING_RECORD(CurrentEntry, ConsoleInput, ListEntry);
436
437 /* Only pay attention to valid characters, on key down */
438 if (Input->InputEvent.EventType == KEY_EVENT &&
439 Input->InputEvent.Event.KeyEvent.bKeyDown &&
440 Input->InputEvent.Event.KeyEvent.uChar.UnicodeChar != L'\0')
441 {
442 WCHAR Char = Input->InputEvent.Event.KeyEvent.uChar.UnicodeChar;
443
444 if (Unicode)
445 {
446 ((PWCHAR)Buffer)[i] = Char;
447 }
448 else
449 {
450 ConsoleInputUnicodeCharToAnsiChar(Console, &((PCHAR)Buffer)[i], &Char);
451 }
452 ++i;
453
454 /* Did read something */
455 Status = STATUS_SUCCESS;
456 }
457 ConsoleFreeHeap(Input);
458 }
459 }
460
461 // FIXME: Only set if Status == STATUS_SUCCESS ???
462 if (NumCharsRead) *NumCharsRead = i;
463
464 return Status;
465 }
466
467
468
469
470 /* GLOBALS ********************************************************************/
471
472 #define TAB_WIDTH 8
473
474 // See condrv/text.c
475 /*static*/ VOID
476 ClearLineBuffer(PTEXTMODE_SCREEN_BUFFER Buff);
477
478 static VOID
479 ConioNextLine(PTEXTMODE_SCREEN_BUFFER Buff, PSMALL_RECT UpdateRect, PUINT ScrolledLines)
480 {
481 /* If we hit bottom, slide the viewable screen */
482 if (++Buff->CursorPosition.Y == Buff->ScreenBufferSize.Y)
483 {
484 Buff->CursorPosition.Y--;
485 if (++Buff->VirtualY == Buff->ScreenBufferSize.Y)
486 {
487 Buff->VirtualY = 0;
488 }
489 (*ScrolledLines)++;
490 ClearLineBuffer(Buff);
491 if (UpdateRect->Top != 0)
492 {
493 UpdateRect->Top--;
494 }
495 }
496 UpdateRect->Left = 0;
497 UpdateRect->Right = Buff->ScreenBufferSize.X - 1;
498 UpdateRect->Bottom = Buff->CursorPosition.Y;
499 }
500
501 static NTSTATUS
502 ConioWriteConsole(PFRONTEND FrontEnd,
503 PTEXTMODE_SCREEN_BUFFER Buff,
504 PWCHAR Buffer,
505 DWORD Length,
506 BOOL Attrib)
507 {
508 PCONSRV_CONSOLE Console = FrontEnd->Console;
509
510 UINT i;
511 PCHAR_INFO Ptr;
512 SMALL_RECT UpdateRect;
513 SHORT CursorStartX, CursorStartY;
514 UINT ScrolledLines;
515 BOOLEAN bFullwidth;
516 BOOLEAN bCJK = Console->IsCJK;
517
518 /* If nothing to write, bail out now */
519 if (Length == 0)
520 return STATUS_SUCCESS;
521
522 CursorStartX = Buff->CursorPosition.X;
523 CursorStartY = Buff->CursorPosition.Y;
524 UpdateRect.Left = Buff->ScreenBufferSize.X;
525 UpdateRect.Top = Buff->CursorPosition.Y;
526 UpdateRect.Right = -1;
527 UpdateRect.Bottom = Buff->CursorPosition.Y;
528 ScrolledLines = 0;
529
530 for (i = 0; i < Length; i++)
531 {
532 /*
533 * If we are in processed mode, interpret special characters and
534 * display them correctly. Otherwise, just put them into the buffer.
535 */
536 if (Buff->Mode & ENABLE_PROCESSED_OUTPUT)
537 {
538 /* --- CR --- */
539 if (Buffer[i] == L'\r')
540 {
541 Buff->CursorPosition.X = 0;
542 CursorStartX = Buff->CursorPosition.X;
543 UpdateRect.Left = min(UpdateRect.Left , Buff->CursorPosition.X);
544 UpdateRect.Right = max(UpdateRect.Right, Buff->CursorPosition.X);
545 continue;
546 }
547 /* --- LF --- */
548 else if (Buffer[i] == L'\n')
549 {
550 Buff->CursorPosition.X = 0; // TODO: Make this behaviour optional!
551 CursorStartX = Buff->CursorPosition.X;
552 ConioNextLine(Buff, &UpdateRect, &ScrolledLines);
553 continue;
554 }
555 /* --- BS --- */
556 else if (Buffer[i] == L'\b')
557 {
558 /* Only handle BS if we are not on the first position of the first line */
559 if (Buff->CursorPosition.X == 0 && Buff->CursorPosition.Y == 0)
560 continue;
561
562 if (Buff->CursorPosition.X == 0)
563 {
564 /* Slide virtual position up */
565 Buff->CursorPosition.X = Buff->ScreenBufferSize.X - 1;
566 Buff->CursorPosition.Y--;
567 // TODO? : Update CursorStartY = Buff->CursorPosition.Y;
568 UpdateRect.Top = min(UpdateRect.Top, Buff->CursorPosition.Y);
569 }
570 else
571 {
572 Buff->CursorPosition.X--;
573 }
574 Ptr = ConioCoordToPointer(Buff, Buff->CursorPosition.X, Buff->CursorPosition.Y);
575
576 if (Ptr->Attributes & COMMON_LVB_LEADING_BYTE)
577 {
578 /*
579 * The cursor just moved on the leading byte of the same
580 * current character. We should go one position before to
581 * go to the actual previous character to erase.
582 */
583
584 /* Only handle BS if we are not on the first position of the first line */
585 if (Buff->CursorPosition.X == 0 && Buff->CursorPosition.Y == 0)
586 continue;
587
588 if (Buff->CursorPosition.X == 0)
589 {
590 /* Slide virtual position up */
591 Buff->CursorPosition.X = Buff->ScreenBufferSize.X - 1;
592 Buff->CursorPosition.Y--;
593 // TODO? : Update CursorStartY = Buff->CursorPosition.Y;
594 UpdateRect.Top = min(UpdateRect.Top, Buff->CursorPosition.Y);
595 }
596 else
597 {
598 Buff->CursorPosition.X--;
599 }
600 Ptr = ConioCoordToPointer(Buff, Buff->CursorPosition.X, Buff->CursorPosition.Y);
601 }
602
603 if (Ptr->Attributes & COMMON_LVB_TRAILING_BYTE)
604 {
605 /* The cursor is on the trailing byte of a full-width character */
606
607 /* Delete its trailing byte... */
608 Ptr->Char.UnicodeChar = L' ';
609 if (Attrib)
610 Ptr->Attributes = Buff->ScreenDefaultAttrib;
611 Ptr->Attributes &= ~COMMON_LVB_SBCSDBCS;
612
613 if (Buff->CursorPosition.X > 0)
614 Buff->CursorPosition.X--;
615 /* ... and now its leading byte */
616 Ptr = ConioCoordToPointer(Buff, Buff->CursorPosition.X, Buff->CursorPosition.Y);
617 }
618
619 Ptr->Char.UnicodeChar = L' ';
620 if (Attrib)
621 Ptr->Attributes = Buff->ScreenDefaultAttrib;
622 Ptr->Attributes &= ~COMMON_LVB_SBCSDBCS;
623
624 UpdateRect.Left = min(UpdateRect.Left , Buff->CursorPosition.X);
625 UpdateRect.Right = max(UpdateRect.Right, Buff->CursorPosition.X);
626 continue;
627 }
628 /* --- TAB --- */
629 else if (Buffer[i] == L'\t')
630 {
631 UINT EndX;
632
633 Ptr = ConioCoordToPointer(Buff, Buff->CursorPosition.X, Buff->CursorPosition.Y);
634
635 if (Ptr->Attributes & COMMON_LVB_TRAILING_BYTE)
636 {
637 /*
638 * The cursor is on the trailing byte of a full-width character.
639 * Go back one position to be on its leading byte.
640 */
641 if (Buff->CursorPosition.X > 0)
642 Buff->CursorPosition.X--;
643 Ptr = ConioCoordToPointer(Buff, Buff->CursorPosition.X, Buff->CursorPosition.Y);
644 }
645
646 UpdateRect.Left = min(UpdateRect.Left, Buff->CursorPosition.X);
647
648 EndX = (Buff->CursorPosition.X + TAB_WIDTH) & ~(TAB_WIDTH - 1);
649 EndX = min(EndX, (UINT)Buff->ScreenBufferSize.X);
650
651 while ((UINT)Buff->CursorPosition.X < EndX)
652 {
653 Ptr->Char.UnicodeChar = L' ';
654 if (Attrib)
655 Ptr->Attributes = Buff->ScreenDefaultAttrib;
656 Ptr->Attributes &= ~COMMON_LVB_SBCSDBCS;
657
658 ++Ptr;
659 Buff->CursorPosition.X++;
660 }
661 if (Buff->CursorPosition.X < Buff->ScreenBufferSize.X)
662 {
663 /* If the following cell is the trailing byte of a full-width character, reset it */
664 if (Ptr->Attributes & COMMON_LVB_TRAILING_BYTE)
665 {
666 Ptr->Char.UnicodeChar = L' ';
667 if (Attrib)
668 Ptr->Attributes = Buff->ScreenDefaultAttrib;
669 Ptr->Attributes &= ~COMMON_LVB_SBCSDBCS;
670 }
671 }
672 UpdateRect.Right = max(UpdateRect.Right, Buff->CursorPosition.X);
673
674 if (Buff->CursorPosition.X >= Buff->ScreenBufferSize.X)
675 {
676 if (Buff->Mode & ENABLE_WRAP_AT_EOL_OUTPUT)
677 {
678 /* Wrapping mode: Go to next line */
679 Buff->CursorPosition.X = 0;
680 CursorStartX = Buff->CursorPosition.X;
681 ConioNextLine(Buff, &UpdateRect, &ScrolledLines);
682 }
683 else
684 {
685 /* The cursor wraps back to its starting position on the same line */
686 Buff->CursorPosition.X = CursorStartX;
687 }
688 }
689 continue;
690 }
691 /* --- BEL ---*/
692 else if (Buffer[i] == L'\a')
693 {
694 FrontEnd->Vtbl->RingBell(FrontEnd);
695 continue;
696 }
697 }
698 UpdateRect.Left = min(UpdateRect.Left , Buff->CursorPosition.X);
699 UpdateRect.Right = max(UpdateRect.Right, Buff->CursorPosition.X);
700
701 /* For Chinese, Japanese and Korean */
702 bFullwidth = (bCJK && IS_FULL_WIDTH(Buffer[i]));
703
704 /* Check whether we can insert the full-width character */
705 if (bFullwidth)
706 {
707 /* It spans two cells and should all fit on the current line */
708 if (Buff->CursorPosition.X >= Buff->ScreenBufferSize.X - 1)
709 {
710 if (Buff->Mode & ENABLE_WRAP_AT_EOL_OUTPUT)
711 {
712 /* Wrapping mode: Go to next line */
713 Buff->CursorPosition.X = 0;
714 CursorStartX = Buff->CursorPosition.X;
715 ConioNextLine(Buff, &UpdateRect, &ScrolledLines);
716 }
717 else
718 {
719 /* The cursor wraps back to its starting position on the same line */
720 Buff->CursorPosition.X = CursorStartX;
721 }
722 }
723
724 /*
725 * Now be sure we can fit the full-width character.
726 * If the screenbuffer is one cell wide we cannot display
727 * the full-width character, so just skip it.
728 */
729 if (Buff->CursorPosition.X >= Buff->ScreenBufferSize.X - 1)
730 {
731 DPRINT1("Cannot display full-width character! CursorPosition.X = %d, ScreenBufferSize.X = %d\n",
732 Buff->CursorPosition.X, Buff->ScreenBufferSize.X);
733 continue;
734 }
735 }
736
737 Ptr = ConioCoordToPointer(Buff, Buff->CursorPosition.X, Buff->CursorPosition.Y);
738
739 /*
740 * Check whether we are overwriting part of a full-width character,
741 * in which case we need to invalidate it.
742 */
743 if (Ptr->Attributes & COMMON_LVB_TRAILING_BYTE)
744 {
745 /*
746 * The cursor is on the trailing byte of a full-width character.
747 * Go back one position to kill the previous leading byte.
748 */
749 if (Buff->CursorPosition.X > 0)
750 {
751 Ptr = ConioCoordToPointer(Buff, Buff->CursorPosition.X - 1, Buff->CursorPosition.Y);
752 Ptr->Char.UnicodeChar = L' ';
753 if (Attrib)
754 Ptr->Attributes = Buff->ScreenDefaultAttrib;
755 Ptr->Attributes &= ~COMMON_LVB_SBCSDBCS;
756 }
757 Ptr = ConioCoordToPointer(Buff, Buff->CursorPosition.X, Buff->CursorPosition.Y);
758 }
759
760 /* Insert the character */
761 if (bFullwidth)
762 {
763 ASSERT(Buff->CursorPosition.X < Buff->ScreenBufferSize.X - 1);
764
765 /* Set the leading byte */
766 Ptr->Char.UnicodeChar = Buffer[i];
767 if (Attrib)
768 Ptr->Attributes = Buff->ScreenDefaultAttrib;
769 Ptr->Attributes &= ~COMMON_LVB_SBCSDBCS;
770 Ptr->Attributes |= COMMON_LVB_LEADING_BYTE;
771
772 /* Set the trailing byte */
773 Buff->CursorPosition.X++;
774 Ptr = ConioCoordToPointer(Buff, Buff->CursorPosition.X, Buff->CursorPosition.Y);
775 // Ptr->Char.UnicodeChar = Buffer[i]; // L' ';
776 if (Attrib)
777 Ptr->Attributes = Buff->ScreenDefaultAttrib;
778 Ptr->Attributes &= ~COMMON_LVB_SBCSDBCS;
779 Ptr->Attributes |= COMMON_LVB_TRAILING_BYTE;
780 }
781 else
782 {
783 Ptr->Char.UnicodeChar = Buffer[i];
784 if (Attrib)
785 Ptr->Attributes = Buff->ScreenDefaultAttrib;
786 Ptr->Attributes &= ~COMMON_LVB_SBCSDBCS;
787 }
788
789 ++Ptr;
790 Buff->CursorPosition.X++;
791
792 if (Buff->CursorPosition.X < Buff->ScreenBufferSize.X)
793 {
794 /* If the following cell is the trailing byte of a full-width character, reset it */
795 if (Ptr->Attributes & COMMON_LVB_TRAILING_BYTE)
796 {
797 Ptr->Char.UnicodeChar = L' ';
798 if (Attrib)
799 Ptr->Attributes = Buff->ScreenDefaultAttrib;
800 Ptr->Attributes &= ~COMMON_LVB_SBCSDBCS;
801 }
802 }
803
804 if (Buff->CursorPosition.X >= Buff->ScreenBufferSize.X)
805 {
806 if (Buff->Mode & ENABLE_WRAP_AT_EOL_OUTPUT)
807 {
808 /* Wrapping mode: Go to next line */
809 Buff->CursorPosition.X = 0;
810 CursorStartX = Buff->CursorPosition.X;
811 ConioNextLine(Buff, &UpdateRect, &ScrolledLines);
812 }
813 else
814 {
815 /* The cursor wraps back to its starting position on the same line */
816 Buff->CursorPosition.X = CursorStartX;
817 }
818 }
819 }
820
821 if (!ConioIsRectEmpty(&UpdateRect) && (PCONSOLE_SCREEN_BUFFER)Buff == Console->ActiveBuffer)
822 {
823 // TermWriteStream(Console, &UpdateRect, CursorStartX, CursorStartY,
824 // ScrolledLines, Buffer, Length);
825 FrontEnd->Vtbl->WriteStream(FrontEnd,
826 &UpdateRect,
827 CursorStartX,
828 CursorStartY,
829 ScrolledLines,
830 Buffer,
831 Length);
832 }
833
834 return STATUS_SUCCESS;
835 }
836
837
838
839 static NTSTATUS NTAPI
840 ConSrvTermWriteStream(IN OUT PTERMINAL This,
841 PTEXTMODE_SCREEN_BUFFER Buff,
842 PWCHAR Buffer,
843 DWORD Length,
844 BOOL Attrib)
845 {
846 PFRONTEND FrontEnd = This->Context;
847 return ConioWriteConsole(FrontEnd,
848 Buff,
849 Buffer,
850 Length,
851 Attrib);
852 }
853
854 /************ Line discipline ***************/
855
856
857
858 VOID
859 ConioDrawConsole(PCONSRV_CONSOLE Console)
860 {
861 SMALL_RECT Region;
862 PCONSOLE_SCREEN_BUFFER ActiveBuffer = Console->ActiveBuffer;
863
864 if (!ActiveBuffer) return;
865
866 ConioInitRect(&Region, 0, 0,
867 ActiveBuffer->ViewSize.Y - 1,
868 ActiveBuffer->ViewSize.X - 1);
869 TermDrawRegion(Console, &Region);
870 // Console->FrontEndIFace.Vtbl->DrawRegion(&Console->FrontEndIFace, &Region);
871 }
872
873 static VOID NTAPI
874 ConSrvTermDrawRegion(IN OUT PTERMINAL This,
875 SMALL_RECT* Region)
876 {
877 PFRONTEND FrontEnd = This->Context;
878 FrontEnd->Vtbl->DrawRegion(FrontEnd, Region);
879 }
880
881 static BOOL NTAPI
882 ConSrvTermSetCursorInfo(IN OUT PTERMINAL This,
883 PCONSOLE_SCREEN_BUFFER ScreenBuffer)
884 {
885 PFRONTEND FrontEnd = This->Context;
886 return FrontEnd->Vtbl->SetCursorInfo(FrontEnd, ScreenBuffer);
887 }
888
889 static BOOL NTAPI
890 ConSrvTermSetScreenInfo(IN OUT PTERMINAL This,
891 PCONSOLE_SCREEN_BUFFER ScreenBuffer,
892 SHORT OldCursorX,
893 SHORT OldCursorY)
894 {
895 PFRONTEND FrontEnd = This->Context;
896 return FrontEnd->Vtbl->SetScreenInfo(FrontEnd,
897 ScreenBuffer,
898 OldCursorX,
899 OldCursorY);
900 }
901
902 static VOID NTAPI
903 ConSrvTermResizeTerminal(IN OUT PTERMINAL This)
904 {
905 PFRONTEND FrontEnd = This->Context;
906 FrontEnd->Vtbl->ResizeTerminal(FrontEnd);
907 }
908
909 static VOID NTAPI
910 ConSrvTermSetActiveScreenBuffer(IN OUT PTERMINAL This)
911 {
912 PFRONTEND FrontEnd = This->Context;
913 FrontEnd->Vtbl->SetActiveScreenBuffer(FrontEnd);
914 }
915
916 static VOID NTAPI
917 ConSrvTermReleaseScreenBuffer(IN OUT PTERMINAL This,
918 IN PCONSOLE_SCREEN_BUFFER ScreenBuffer)
919 {
920 PFRONTEND FrontEnd = This->Context;
921 FrontEnd->Vtbl->ReleaseScreenBuffer(FrontEnd, ScreenBuffer);
922 }
923
924 static VOID NTAPI
925 ConSrvTermGetLargestConsoleWindowSize(IN OUT PTERMINAL This,
926 PCOORD pSize)
927 {
928 PFRONTEND FrontEnd = This->Context;
929 FrontEnd->Vtbl->GetLargestConsoleWindowSize(FrontEnd, pSize);
930 }
931
932 static BOOL NTAPI
933 ConSrvTermSetPalette(IN OUT PTERMINAL This,
934 HPALETTE PaletteHandle,
935 UINT PaletteUsage)
936 {
937 PFRONTEND FrontEnd = This->Context;
938 return FrontEnd->Vtbl->SetPalette(FrontEnd, PaletteHandle, PaletteUsage);
939 }
940
941 static INT NTAPI
942 ConSrvTermShowMouseCursor(IN OUT PTERMINAL This,
943 BOOL Show)
944 {
945 PFRONTEND FrontEnd = This->Context;
946 return FrontEnd->Vtbl->ShowMouseCursor(FrontEnd, Show);
947 }
948
949 static TERMINAL_VTBL ConSrvTermVtbl =
950 {
951 ConSrvTermInitTerminal,
952 ConSrvTermDeinitTerminal,
953
954 ConSrvTermReadStream,
955 ConSrvTermWriteStream,
956
957 ConSrvTermDrawRegion,
958 ConSrvTermSetCursorInfo,
959 ConSrvTermSetScreenInfo,
960 ConSrvTermResizeTerminal,
961 ConSrvTermSetActiveScreenBuffer,
962 ConSrvTermReleaseScreenBuffer,
963 ConSrvTermGetLargestConsoleWindowSize,
964 ConSrvTermSetPalette,
965 ConSrvTermShowMouseCursor,
966 };
967
968 #if 0
969 VOID
970 ResetFrontEnd(IN PCONSOLE Console)
971 {
972 if (!Console) return;
973
974 /* Reinitialize the frontend interface */
975 RtlZeroMemory(&Console->FrontEndIFace, sizeof(Console->FrontEndIFace));
976 Console->FrontEndIFace.Vtbl = &ConSrvTermVtbl;
977 }
978 #endif
979
980 /* EOF */