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