[TASKMGR] Process page: Allow using "Open File Location" functionality without runnin...
[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 BOOL NTAPI
943 ConSrvTermSetCodePage(IN OUT PTERMINAL This,
944 UINT CodePage)
945 {
946 PFRONTEND FrontEnd = This->Context;
947 return FrontEnd->Vtbl->SetCodePage(FrontEnd, CodePage);
948 }
949
950 static INT NTAPI
951 ConSrvTermShowMouseCursor(IN OUT PTERMINAL This,
952 BOOL Show)
953 {
954 PFRONTEND FrontEnd = This->Context;
955 return FrontEnd->Vtbl->ShowMouseCursor(FrontEnd, Show);
956 }
957
958 static TERMINAL_VTBL ConSrvTermVtbl =
959 {
960 ConSrvTermInitTerminal,
961 ConSrvTermDeinitTerminal,
962
963 ConSrvTermReadStream,
964 ConSrvTermWriteStream,
965
966 ConSrvTermDrawRegion,
967 ConSrvTermSetCursorInfo,
968 ConSrvTermSetScreenInfo,
969 ConSrvTermResizeTerminal,
970 ConSrvTermSetActiveScreenBuffer,
971 ConSrvTermReleaseScreenBuffer,
972 ConSrvTermGetLargestConsoleWindowSize,
973 ConSrvTermSetPalette,
974 ConSrvTermSetCodePage,
975 ConSrvTermShowMouseCursor,
976 };
977
978 #if 0
979 VOID
980 ResetFrontEnd(IN PCONSOLE Console)
981 {
982 PCONSRV_CONSOLE ConSrvConsole = (PCONSRV_CONSOLE)Console;
983 if (!Console) return;
984
985 /* Reinitialize the frontend interface */
986 RtlZeroMemory(&ConSrvConsole->FrontEndIFace, sizeof(ConSrvConsole->FrontEndIFace));
987 ConSrvConsole->FrontEndIFace.Vtbl = &ConSrvTermVtbl;
988 }
989 #endif
990
991 /* EOF */