[WIN32SS][WINSRV] Fullwidth character handling Part 2 (#2240)
[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
562 if (Ptr->Attributes & COMMON_LVB_TRAILING_BYTE)
563 {
564 /* Delete a full-width character */
565 Ptr->Attributes = Buff->ScreenDefaultAttrib;
566 if (Buff->CursorPosition.X > 0)
567 Buff->CursorPosition.X--;
568 Ptr = ConioCoordToPointer(Buff, Buff->CursorPosition.X, Buff->CursorPosition.Y);
569 Ptr->Char.UnicodeChar = L' ';
570 }
571
572 Ptr->Attributes = Buff->ScreenDefaultAttrib;
573 UpdateRect.Left = min(UpdateRect.Left, Buff->CursorPosition.X);
574 UpdateRect.Right = max(UpdateRect.Right, Buff->CursorPosition.X);
575 }
576 continue;
577 }
578 /* --- TAB --- */
579 else if (Buffer[i] == L'\t')
580 {
581 UINT EndX;
582
583 UpdateRect.Left = min(UpdateRect.Left, Buff->CursorPosition.X);
584 EndX = (Buff->CursorPosition.X + TAB_WIDTH) & ~(TAB_WIDTH - 1);
585 EndX = min(EndX, (UINT)Buff->ScreenBufferSize.X);
586 Ptr = ConioCoordToPointer(Buff, Buff->CursorPosition.X, Buff->CursorPosition.Y);
587 while ((UINT)Buff->CursorPosition.X < EndX)
588 {
589 Ptr->Char.UnicodeChar = L' ';
590 Ptr->Attributes = Buff->ScreenDefaultAttrib;
591 ++Ptr;
592 Buff->CursorPosition.X++;
593 }
594 UpdateRect.Right = max(UpdateRect.Right, Buff->CursorPosition.X - 1);
595 if (Buff->CursorPosition.X == Buff->ScreenBufferSize.X)
596 {
597 if (Buff->Mode & ENABLE_WRAP_AT_EOL_OUTPUT)
598 {
599 Buff->CursorPosition.X = 0;
600 ConioNextLine(Buff, &UpdateRect, &ScrolledLines);
601 }
602 else
603 {
604 Buff->CursorPosition.X--;
605 }
606 }
607 continue;
608 }
609 /* --- BEL ---*/
610 else if (Buffer[i] == L'\a')
611 {
612 FrontEnd->Vtbl->RingBell(FrontEnd);
613 continue;
614 }
615 }
616 UpdateRect.Left = min(UpdateRect.Left, Buff->CursorPosition.X);
617 UpdateRect.Right = max(UpdateRect.Right, Buff->CursorPosition.X);
618
619 /* For Chinese, Japanese and Korean */
620 if (bCJK && Buffer[i] >= 0x80 && mk_wcwidth_cjk(Buffer[i]) == 2)
621 {
622 /* Buffer[i] is a fullwidth character */
623
624 if (Buff->CursorPosition.X > 0)
625 {
626 /* Kill the previous leading byte */
627 Ptr = ConioCoordToPointer(Buff, Buff->CursorPosition.X - 1, Buff->CursorPosition.Y);
628 if (Ptr->Attributes & COMMON_LVB_LEADING_BYTE)
629 {
630 Ptr->Char.UnicodeChar = L' ';
631 if (Attrib)
632 Ptr->Attributes &= ~COMMON_LVB_LEADING_BYTE;
633 }
634 }
635
636 if (Buff->CursorPosition.X == Buff->ScreenBufferSize.X - 1)
637 {
638 /* New line */
639 if (Buff->Mode & ENABLE_WRAP_AT_EOL_OUTPUT)
640 {
641 Buff->CursorPosition.X = 0;
642 ConioNextLine(Buff, &UpdateRect, &ScrolledLines);
643 }
644 else
645 {
646 Buff->CursorPosition.X = CursorStartX;
647 }
648 }
649
650 /* Set leading */
651 Ptr = ConioCoordToPointer(Buff, Buff->CursorPosition.X, Buff->CursorPosition.Y);
652 Ptr->Char.UnicodeChar = Buffer[i];
653 if (Attrib)
654 Ptr->Attributes = Buff->ScreenDefaultAttrib | COMMON_LVB_LEADING_BYTE;
655
656 /* Set trailing */
657 Buff->CursorPosition.X++;
658 Ptr = ConioCoordToPointer(Buff, Buff->CursorPosition.X, Buff->CursorPosition.Y);
659 if (Attrib)
660 Ptr->Attributes = Buff->ScreenDefaultAttrib | COMMON_LVB_TRAILING_BYTE;
661 }
662 else
663 {
664 Ptr = ConioCoordToPointer(Buff, Buff->CursorPosition.X, Buff->CursorPosition.Y);
665 Ptr->Char.UnicodeChar = Buffer[i];
666 if (Attrib)
667 Ptr->Attributes = Buff->ScreenDefaultAttrib;
668 }
669
670 Buff->CursorPosition.X++;
671 if (Buff->CursorPosition.X == Buff->ScreenBufferSize.X)
672 {
673 if (Buff->Mode & ENABLE_WRAP_AT_EOL_OUTPUT)
674 {
675 Buff->CursorPosition.X = 0;
676 ConioNextLine(Buff, &UpdateRect, &ScrolledLines);
677 }
678 else
679 {
680 Buff->CursorPosition.X = CursorStartX;
681 }
682 }
683 }
684
685 if (bCJK && Buff->CursorPosition.X > 0)
686 {
687 /* Delete trailing */
688 Ptr = ConioCoordToPointer(Buff, Buff->CursorPosition.X, Buff->CursorPosition.Y);
689 if (Ptr->Attributes & COMMON_LVB_TRAILING_BYTE)
690 {
691 Ptr->Char.UnicodeChar = L' ';
692 if (Attrib)
693 Ptr->Attributes = Buff->ScreenDefaultAttrib;
694 }
695 }
696
697 if (!ConioIsRectEmpty(&UpdateRect) && (PCONSOLE_SCREEN_BUFFER)Buff == Console->ActiveBuffer)
698 {
699 // TermWriteStream(Console, &UpdateRect, CursorStartX, CursorStartY,
700 // ScrolledLines, Buffer, Length);
701 FrontEnd->Vtbl->WriteStream(FrontEnd,
702 &UpdateRect,
703 CursorStartX,
704 CursorStartY,
705 ScrolledLines,
706 Buffer,
707 Length);
708 }
709
710 return STATUS_SUCCESS;
711 }
712
713
714
715 static NTSTATUS NTAPI
716 ConSrvTermWriteStream(IN OUT PTERMINAL This,
717 PTEXTMODE_SCREEN_BUFFER Buff,
718 PWCHAR Buffer,
719 DWORD Length,
720 BOOL Attrib)
721 {
722 PFRONTEND FrontEnd = This->Context;
723 return ConioWriteConsole(FrontEnd,
724 Buff,
725 Buffer,
726 Length,
727 Attrib);
728 }
729
730 /************ Line discipline ***************/
731
732
733
734 VOID
735 ConioDrawConsole(PCONSRV_CONSOLE Console)
736 {
737 SMALL_RECT Region;
738 PCONSOLE_SCREEN_BUFFER ActiveBuffer = Console->ActiveBuffer;
739
740 if (!ActiveBuffer) return;
741
742 ConioInitRect(&Region, 0, 0,
743 ActiveBuffer->ViewSize.Y - 1,
744 ActiveBuffer->ViewSize.X - 1);
745 TermDrawRegion(Console, &Region);
746 // Console->FrontEndIFace.Vtbl->DrawRegion(&Console->FrontEndIFace, &Region);
747 }
748
749 static VOID NTAPI
750 ConSrvTermDrawRegion(IN OUT PTERMINAL This,
751 SMALL_RECT* Region)
752 {
753 PFRONTEND FrontEnd = This->Context;
754 FrontEnd->Vtbl->DrawRegion(FrontEnd, Region);
755 }
756
757 static BOOL NTAPI
758 ConSrvTermSetCursorInfo(IN OUT PTERMINAL This,
759 PCONSOLE_SCREEN_BUFFER ScreenBuffer)
760 {
761 PFRONTEND FrontEnd = This->Context;
762 return FrontEnd->Vtbl->SetCursorInfo(FrontEnd, ScreenBuffer);
763 }
764
765 static BOOL NTAPI
766 ConSrvTermSetScreenInfo(IN OUT PTERMINAL This,
767 PCONSOLE_SCREEN_BUFFER ScreenBuffer,
768 SHORT OldCursorX,
769 SHORT OldCursorY)
770 {
771 PFRONTEND FrontEnd = This->Context;
772 return FrontEnd->Vtbl->SetScreenInfo(FrontEnd,
773 ScreenBuffer,
774 OldCursorX,
775 OldCursorY);
776 }
777
778 static VOID NTAPI
779 ConSrvTermResizeTerminal(IN OUT PTERMINAL This)
780 {
781 PFRONTEND FrontEnd = This->Context;
782 FrontEnd->Vtbl->ResizeTerminal(FrontEnd);
783 }
784
785 static VOID NTAPI
786 ConSrvTermSetActiveScreenBuffer(IN OUT PTERMINAL This)
787 {
788 PFRONTEND FrontEnd = This->Context;
789 FrontEnd->Vtbl->SetActiveScreenBuffer(FrontEnd);
790 }
791
792 static VOID NTAPI
793 ConSrvTermReleaseScreenBuffer(IN OUT PTERMINAL This,
794 IN PCONSOLE_SCREEN_BUFFER ScreenBuffer)
795 {
796 PFRONTEND FrontEnd = This->Context;
797 FrontEnd->Vtbl->ReleaseScreenBuffer(FrontEnd, ScreenBuffer);
798 }
799
800 static VOID NTAPI
801 ConSrvTermGetLargestConsoleWindowSize(IN OUT PTERMINAL This,
802 PCOORD pSize)
803 {
804 PFRONTEND FrontEnd = This->Context;
805 FrontEnd->Vtbl->GetLargestConsoleWindowSize(FrontEnd, pSize);
806 }
807
808 static BOOL NTAPI
809 ConSrvTermSetPalette(IN OUT PTERMINAL This,
810 HPALETTE PaletteHandle,
811 UINT PaletteUsage)
812 {
813 PFRONTEND FrontEnd = This->Context;
814 return FrontEnd->Vtbl->SetPalette(FrontEnd, PaletteHandle, PaletteUsage);
815 }
816
817 static INT NTAPI
818 ConSrvTermShowMouseCursor(IN OUT PTERMINAL This,
819 BOOL Show)
820 {
821 PFRONTEND FrontEnd = This->Context;
822 return FrontEnd->Vtbl->ShowMouseCursor(FrontEnd, Show);
823 }
824
825 static TERMINAL_VTBL ConSrvTermVtbl =
826 {
827 ConSrvTermInitTerminal,
828 ConSrvTermDeinitTerminal,
829
830 ConSrvTermReadStream,
831 ConSrvTermWriteStream,
832
833 ConSrvTermDrawRegion,
834 ConSrvTermSetCursorInfo,
835 ConSrvTermSetScreenInfo,
836 ConSrvTermResizeTerminal,
837 ConSrvTermSetActiveScreenBuffer,
838 ConSrvTermReleaseScreenBuffer,
839 ConSrvTermGetLargestConsoleWindowSize,
840 ConSrvTermSetPalette,
841 ConSrvTermShowMouseCursor,
842 };
843
844 #if 0
845 VOID
846 ResetFrontEnd(IN PCONSOLE Console)
847 {
848 if (!Console) return;
849
850 /* Reinitialize the frontend interface */
851 RtlZeroMemory(&Console->FrontEndIFace, sizeof(Console->FrontEndIFace));
852 Console->FrontEndIFace.Vtbl = &ConSrvTermVtbl;
853 }
854 #endif
855
856 /* EOF */