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)
9 /* INCLUDES *******************************************************************/
12 #include "concfg/font.h"
14 // #include "frontends/gui/guiterm.h"
15 #ifdef TUITERM_COMPILE
16 #include "frontends/tui/tuiterm.h"
26 /********** HACK HACK HACK HACK HACK HACK HACK HACK HACK HACK HACK ************/
28 /* GLOBALS ********************************************************************/
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."
36 #define ConsoleInputUnicodeCharToAnsiChar(Console, dChar, sWChar) \
38 ASSERT((ULONG_PTR)(dChar) != (ULONG_PTR)(sWChar)); \
39 WideCharToMultiByte((Console)->InputCodePage, 0, (sWChar), 1, (dChar), 1, NULL, NULL); \
42 #define ConsoleInputAnsiCharToUnicodeChar(Console, dWChar, sChar) \
44 ASSERT((ULONG_PTR)(dWChar) != (ULONG_PTR)(sChar)); \
45 MultiByteToWideChar((Console)->InputCodePage, 0, (sChar), 1, (dWChar), 1); \
48 /* PRIVATE FUNCTIONS **********************************************************/
53 ConioInputEventToAnsi(PCONSOLE Console
, PINPUT_RECORD InputEvent
)
55 if (InputEvent
->EventType
== KEY_EVENT
)
57 WCHAR UnicodeChar
= InputEvent
->Event
.KeyEvent
.uChar
.UnicodeChar
;
58 InputEvent
->Event
.KeyEvent
.uChar
.UnicodeChar
= 0;
59 ConsoleInputUnicodeCharToAnsiChar(Console
,
60 &InputEvent
->Event
.KeyEvent
.uChar
.AsciiChar
,
66 ConioInputEventToUnicode(PCONSOLE Console
, PINPUT_RECORD InputEvent
)
68 if (InputEvent
->EventType
== KEY_EVENT
)
70 CHAR AsciiChar
= InputEvent
->Event
.KeyEvent
.uChar
.AsciiChar
;
71 InputEvent
->Event
.KeyEvent
.uChar
.AsciiChar
= 0;
72 ConsoleInputAnsiCharToUnicodeChar(Console
,
73 &InputEvent
->Event
.KeyEvent
.uChar
.UnicodeChar
,
80 /********** HACK HACK HACK HACK HACK HACK HACK HACK HACK HACK HACK ************/
89 /* CONSRV TERMINAL FRONTENDS INTERFACE ****************************************/
92 #ifdef TUITERM_COMPILE
94 TuiLoadFrontEnd(IN OUT PFRONTEND FrontEnd
,
95 IN OUT PCONSOLE_STATE_INFO ConsoleInfo
,
96 IN OUT PCONSOLE_INIT_INFO ConsoleInitInfo
,
97 IN HANDLE ConsoleLeaderProcessHandle
);
99 TuiUnloadFrontEnd(IN OUT PFRONTEND FrontEnd
);
103 GuiLoadFrontEnd(IN OUT PFRONTEND FrontEnd
,
104 IN OUT PCONSOLE_STATE_INFO ConsoleInfo
,
105 IN OUT PCONSOLE_INIT_INFO ConsoleInitInfo
,
106 IN HANDLE ConsoleLeaderProcessHandle
);
108 GuiUnloadFrontEnd(IN OUT PFRONTEND FrontEnd
);
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
);
118 NTSTATUS (NTAPI
*FRONTEND_UNLOAD
)(IN OUT PFRONTEND FrontEnd
);
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.
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).
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).
140 CHAR FrontEndName
[80];
141 FRONTEND_LOAD FrontEndLoad
;
142 FRONTEND_UNLOAD FrontEndUnload
;
143 } FrontEndLoadingMethods
[] =
145 #ifdef TUITERM_COMPILE
146 {"TUI", TuiLoadFrontEnd
, TuiUnloadFrontEnd
},
148 {"GUI", GuiLoadFrontEnd
, GuiUnloadFrontEnd
},
150 // {"Not found", 0, NULL}
154 ConSrvLoadFrontEnd(IN OUT PFRONTEND FrontEnd
,
155 IN OUT PCONSOLE_STATE_INFO ConsoleInfo
,
156 IN OUT PCONSOLE_INIT_INFO ConsoleInitInfo
,
157 IN HANDLE ConsoleLeaderProcessHandle
)
159 NTSTATUS Status
= STATUS_SUCCESS
;
163 * Choose an adequate terminal front-end to load, and load it
165 for (i
= 0; i
< ARRAYSIZE(FrontEndLoadingMethods
); ++i
)
167 DPRINT("CONSRV: Trying to load %s frontend...\n",
168 FrontEndLoadingMethods
[i
].FrontEndName
);
169 Status
= FrontEndLoadingMethods
[i
].FrontEndLoad(FrontEnd
,
172 ConsoleLeaderProcessHandle
);
173 if (NT_SUCCESS(Status
))
175 /* Save the unload callback */
176 FrontEnd
->UnloadFrontEnd
= FrontEndLoadingMethods
[i
].FrontEndUnload
;
178 DPRINT("CONSRV: %s frontend loaded successfully\n",
179 FrontEndLoadingMethods
[i
].FrontEndName
);
184 DPRINT1("CONSRV: Loading %s frontend failed, Status = 0x%08lx , continuing...\n",
185 FrontEndLoadingMethods
[i
].FrontEndName
, Status
);
193 ConSrvUnloadFrontEnd(IN PFRONTEND FrontEnd
)
195 if (FrontEnd
== NULL
) return STATUS_INVALID_PARAMETER
;
196 // return FrontEnd->Vtbl->UnloadFrontEnd(FrontEnd);
197 return FrontEnd
->UnloadFrontEnd(FrontEnd
);
201 static TERMINAL_VTBL ConSrvTermVtbl
;
204 ConSrvInitTerminal(IN OUT PTERMINAL Terminal
,
205 IN OUT PCONSOLE_STATE_INFO ConsoleInfo
,
206 IN OUT PCONSOLE_INIT_INFO ConsoleInitInfo
,
207 IN HANDLE ConsoleLeaderProcessHandle
)
212 /* Load a suitable frontend for the ConSrv terminal */
213 FrontEnd
= ConsoleAllocHeap(HEAP_ZERO_MEMORY
, sizeof(*FrontEnd
));
214 if (!FrontEnd
) return STATUS_NO_MEMORY
;
216 Status
= ConSrvLoadFrontEnd(FrontEnd
,
219 ConsoleLeaderProcessHandle
);
220 if (!NT_SUCCESS(Status
))
222 DPRINT1("CONSRV: Failed to initialize a frontend, Status = 0x%08lx\n", Status
);
223 ConsoleFreeHeap(FrontEnd
);
226 DPRINT("CONSRV: Frontend initialized\n");
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 */
233 return STATUS_SUCCESS
;
237 ConSrvDeinitTerminal(IN OUT PTERMINAL Terminal
)
239 NTSTATUS Status
= STATUS_SUCCESS
;
240 PFRONTEND FrontEnd
= Terminal
->Context
;
242 /* Reset the ConSrv terminal */
243 Terminal
->Context
= NULL
;
244 Terminal
->Vtbl
= NULL
;
246 /* Unload the frontend */
247 if (FrontEnd
!= NULL
)
249 Status
= ConSrvUnloadFrontEnd(FrontEnd
);
250 ConsoleFreeHeap(FrontEnd
);
257 /* CONSRV TERMINAL INTERFACE **************************************************/
259 static NTSTATUS NTAPI
260 ConSrvTermInitTerminal(IN OUT PTERMINAL This
,
264 PFRONTEND FrontEnd
= This
->Context
;
265 PCONSRV_CONSOLE ConSrvConsole
= (PCONSRV_CONSOLE
)Console
;
267 /* Initialize the console pointer for our frontend */
268 FrontEnd
->Console
= ConSrvConsole
;
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
;
274 Status
= FrontEnd
->Vtbl
->InitFrontEnd(FrontEnd
, ConSrvConsole
);
275 if (!NT_SUCCESS(Status
))
276 DPRINT1("InitFrontEnd failed, Status = 0x%08lx\n", Status
);
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
;
286 ConSrvTermDeinitTerminal(IN OUT PTERMINAL This
)
288 PFRONTEND FrontEnd
= This
->Context
;
289 FrontEnd
->Vtbl
->DeinitFrontEnd(FrontEnd
);
294 /************ Line discipline ***************/
296 static NTSTATUS NTAPI
297 ConSrvTermReadStream(IN OUT PTERMINAL This
,
301 IN OUT PCONSOLE_READCONSOLE_CONTROL ReadControl
,
302 IN PVOID Parameter OPTIONAL
,
303 IN ULONG NumCharsToRead
,
304 OUT PULONG NumCharsRead OPTIONAL
)
306 PFRONTEND FrontEnd
= This
->Context
;
307 PCONSRV_CONSOLE Console
= FrontEnd
->Console
;
308 PCONSOLE_INPUT_BUFFER InputBuffer
= &Console
->InputBuffer
;
309 PUNICODE_STRING ExeName
= Parameter
;
311 // STATUS_PENDING : Wait if more to read ; STATUS_SUCCESS : Don't wait.
312 NTSTATUS Status
= STATUS_PENDING
;
314 PLIST_ENTRY CurrentEntry
;
318 /* Validity checks */
319 // ASSERT(Console == InputBuffer->Header.Console);
320 ASSERT((Buffer
!= NULL
) || (Buffer
== NULL
&& NumCharsToRead
== 0));
322 /* We haven't read anything (yet) */
324 if (InputBuffer
->Mode
& ENABLE_LINE_INPUT
)
326 /* COOKED mode, call the line discipline */
328 if (Console
->LineBuffer
== NULL
)
330 /* Start a new line */
331 Console
->LineMaxSize
= max(256, NumCharsToRead
);
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.
338 ReadControl
->nInitialChars
= min(ReadControl
->nInitialChars
, NumCharsToRead
);
340 Console
->LineBuffer
= ConsoleAllocHeap(0, Console
->LineMaxSize
* sizeof(WCHAR
));
341 if (Console
->LineBuffer
== NULL
) return STATUS_NO_MEMORY
;
343 Console
->LinePos
= Console
->LineSize
= ReadControl
->nInitialChars
;
344 Console
->LineComplete
= Console
->LineUpPressed
= FALSE
;
345 Console
->LineInsertToggle
= Console
->InsertMode
;
346 Console
->LineWakeupMask
= ReadControl
->dwCtrlWakeupMask
;
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.
353 memcpy(Console
->LineBuffer
, Buffer
, Console
->LineSize
* sizeof(WCHAR
));
354 if (Console
->LineSize
>= Console
->LineMaxSize
)
356 Console
->LineComplete
= TRUE
;
357 Console
->LinePos
= 0;
361 /* If we don't have a complete line yet, process the pending input */
362 while (!Console
->LineComplete
&& !IsListEmpty(&InputBuffer
->InputEvents
))
364 /* Remove an input event from the queue */
365 _InterlockedDecrement((PLONG
)&InputBuffer
->NumberOfEvents
);
366 CurrentEntry
= RemoveHeadList(&InputBuffer
->InputEvents
);
367 if (IsListEmpty(&InputBuffer
->InputEvents
))
369 NtClearEvent(InputBuffer
->ActiveEvent
);
371 Input
= CONTAINING_RECORD(CurrentEntry
, ConsoleInput
, ListEntry
);
373 /* Only pay attention to key down */
374 if (Input
->InputEvent
.EventType
== KEY_EVENT
&&
375 Input
->InputEvent
.Event
.KeyEvent
.bKeyDown
)
377 LineInputKeyDown(Console
, ExeName
,
378 &Input
->InputEvent
.Event
.KeyEvent
);
379 ReadControl
->dwControlKeyState
= Input
->InputEvent
.Event
.KeyEvent
.dwControlKeyState
;
381 ConsoleFreeHeap(Input
);
384 /* Check if we have a complete line to read from */
385 if (Console
->LineComplete
)
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
394 while (i
< NumCharsToRead
&& Console
->LinePos
< Console
->LineSize
)
396 WCHAR Char
= Console
->LineBuffer
[Console
->LinePos
++];
400 ((PWCHAR
)Buffer
)[i
] = Char
;
404 ConsoleInputUnicodeCharToAnsiChar(Console
, &((PCHAR
)Buffer
)[i
], &Char
);
409 if (Console
->LinePos
>= Console
->LineSize
)
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
;
419 Status
= STATUS_SUCCESS
;
426 /* Character input */
427 while (i
< NumCharsToRead
&& !IsListEmpty(&InputBuffer
->InputEvents
))
429 /* Remove an input event from the queue */
430 _InterlockedDecrement((PLONG
)&InputBuffer
->NumberOfEvents
);
431 CurrentEntry
= RemoveHeadList(&InputBuffer
->InputEvents
);
432 if (IsListEmpty(&InputBuffer
->InputEvents
))
434 NtClearEvent(InputBuffer
->ActiveEvent
);
436 Input
= CONTAINING_RECORD(CurrentEntry
, ConsoleInput
, ListEntry
);
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')
443 WCHAR Char
= Input
->InputEvent
.Event
.KeyEvent
.uChar
.UnicodeChar
;
447 ((PWCHAR
)Buffer
)[i
] = Char
;
451 ConsoleInputUnicodeCharToAnsiChar(Console
, &((PCHAR
)Buffer
)[i
], &Char
);
455 /* Did read something */
456 Status
= STATUS_SUCCESS
;
458 ConsoleFreeHeap(Input
);
462 // FIXME: Only set if Status == STATUS_SUCCESS ???
463 if (NumCharsRead
) *NumCharsRead
= i
;
471 /* GLOBALS ********************************************************************/
477 ClearLineBuffer(PTEXTMODE_SCREEN_BUFFER Buff
);
480 ConioNextLine(PTEXTMODE_SCREEN_BUFFER Buff
, PSMALL_RECT UpdateRect
, PUINT ScrolledLines
)
482 /* If we hit bottom, slide the viewable screen */
483 if (++Buff
->CursorPosition
.Y
== Buff
->ScreenBufferSize
.Y
)
485 Buff
->CursorPosition
.Y
--;
486 if (++Buff
->VirtualY
== Buff
->ScreenBufferSize
.Y
)
491 ClearLineBuffer(Buff
);
492 if (UpdateRect
->Top
!= 0)
497 UpdateRect
->Left
= 0;
498 UpdateRect
->Right
= Buff
->ScreenBufferSize
.X
- 1;
499 UpdateRect
->Bottom
= Buff
->CursorPosition
.Y
;
503 ConioWriteConsole(PFRONTEND FrontEnd
,
504 PTEXTMODE_SCREEN_BUFFER Buff
,
509 PCONSRV_CONSOLE Console
= FrontEnd
->Console
;
513 SMALL_RECT UpdateRect
;
514 SHORT CursorStartX
, CursorStartY
;
517 BOOLEAN bCJK
= Console
->IsCJK
;
519 /* If nothing to write, bail out now */
521 return STATUS_SUCCESS
;
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
;
531 for (i
= 0; i
< Length
; i
++)
534 * If we are in processed mode, interpret special characters and
535 * display them correctly. Otherwise, just put them into the buffer.
537 if (Buff
->Mode
& ENABLE_PROCESSED_OUTPUT
)
540 if (Buffer
[i
] == L
'\r')
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
);
549 else if (Buffer
[i
] == L
'\n')
551 Buff
->CursorPosition
.X
= 0; // TODO: Make this behaviour optional!
552 CursorStartX
= Buff
->CursorPosition
.X
;
553 ConioNextLine(Buff
, &UpdateRect
, &ScrolledLines
);
557 else if (Buffer
[i
] == L
'\b')
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)
563 if (Buff
->CursorPosition
.X
== 0)
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
);
573 Buff
->CursorPosition
.X
--;
575 Ptr
= ConioCoordToPointer(Buff
, Buff
->CursorPosition
.X
, Buff
->CursorPosition
.Y
);
577 if (Ptr
->Attributes
& COMMON_LVB_LEADING_BYTE
)
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.
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)
589 if (Buff
->CursorPosition
.X
== 0)
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
);
599 Buff
->CursorPosition
.X
--;
601 Ptr
= ConioCoordToPointer(Buff
, Buff
->CursorPosition
.X
, Buff
->CursorPosition
.Y
);
604 if (Ptr
->Attributes
& COMMON_LVB_TRAILING_BYTE
)
606 /* The cursor is on the trailing byte of a full-width character */
608 /* Delete its trailing byte... */
609 Ptr
->Char
.UnicodeChar
= L
' ';
611 Ptr
->Attributes
= Buff
->ScreenDefaultAttrib
;
612 Ptr
->Attributes
&= ~COMMON_LVB_SBCSDBCS
;
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
);
620 Ptr
->Char
.UnicodeChar
= L
' ';
622 Ptr
->Attributes
= Buff
->ScreenDefaultAttrib
;
623 Ptr
->Attributes
&= ~COMMON_LVB_SBCSDBCS
;
625 UpdateRect
.Left
= min(UpdateRect
.Left
, Buff
->CursorPosition
.X
);
626 UpdateRect
.Right
= max(UpdateRect
.Right
, Buff
->CursorPosition
.X
);
630 else if (Buffer
[i
] == L
'\t')
634 Ptr
= ConioCoordToPointer(Buff
, Buff
->CursorPosition
.X
, Buff
->CursorPosition
.Y
);
636 if (Ptr
->Attributes
& COMMON_LVB_TRAILING_BYTE
)
639 * The cursor is on the trailing byte of a full-width character.
640 * Go back one position to be on its leading byte.
642 if (Buff
->CursorPosition
.X
> 0)
643 Buff
->CursorPosition
.X
--;
644 Ptr
= ConioCoordToPointer(Buff
, Buff
->CursorPosition
.X
, Buff
->CursorPosition
.Y
);
647 UpdateRect
.Left
= min(UpdateRect
.Left
, Buff
->CursorPosition
.X
);
649 EndX
= (Buff
->CursorPosition
.X
+ TAB_WIDTH
) & ~(TAB_WIDTH
- 1);
650 EndX
= min(EndX
, (UINT
)Buff
->ScreenBufferSize
.X
);
652 while ((UINT
)Buff
->CursorPosition
.X
< EndX
)
654 Ptr
->Char
.UnicodeChar
= L
' ';
656 Ptr
->Attributes
= Buff
->ScreenDefaultAttrib
;
657 Ptr
->Attributes
&= ~COMMON_LVB_SBCSDBCS
;
660 Buff
->CursorPosition
.X
++;
662 if (Buff
->CursorPosition
.X
< Buff
->ScreenBufferSize
.X
)
664 /* If the following cell is the trailing byte of a full-width character, reset it */
665 if (Ptr
->Attributes
& COMMON_LVB_TRAILING_BYTE
)
667 Ptr
->Char
.UnicodeChar
= L
' ';
669 Ptr
->Attributes
= Buff
->ScreenDefaultAttrib
;
670 Ptr
->Attributes
&= ~COMMON_LVB_SBCSDBCS
;
673 UpdateRect
.Right
= max(UpdateRect
.Right
, Buff
->CursorPosition
.X
);
675 if (Buff
->CursorPosition
.X
>= Buff
->ScreenBufferSize
.X
)
677 if (Buff
->Mode
& ENABLE_WRAP_AT_EOL_OUTPUT
)
679 /* Wrapping mode: Go to next line */
680 Buff
->CursorPosition
.X
= 0;
681 CursorStartX
= Buff
->CursorPosition
.X
;
682 ConioNextLine(Buff
, &UpdateRect
, &ScrolledLines
);
686 /* The cursor wraps back to its starting position on the same line */
687 Buff
->CursorPosition
.X
= CursorStartX
;
693 else if (Buffer
[i
] == L
'\a')
695 FrontEnd
->Vtbl
->RingBell(FrontEnd
);
699 UpdateRect
.Left
= min(UpdateRect
.Left
, Buff
->CursorPosition
.X
);
700 UpdateRect
.Right
= max(UpdateRect
.Right
, Buff
->CursorPosition
.X
);
702 /* For Chinese, Japanese and Korean */
703 bFullwidth
= (bCJK
&& IS_FULL_WIDTH(Buffer
[i
]));
705 /* Check whether we can insert the full-width character */
708 /* It spans two cells and should all fit on the current line */
709 if (Buff
->CursorPosition
.X
>= Buff
->ScreenBufferSize
.X
- 1)
711 if (Buff
->Mode
& ENABLE_WRAP_AT_EOL_OUTPUT
)
713 /* Wrapping mode: Go to next line */
714 Buff
->CursorPosition
.X
= 0;
715 CursorStartX
= Buff
->CursorPosition
.X
;
716 ConioNextLine(Buff
, &UpdateRect
, &ScrolledLines
);
720 /* The cursor wraps back to its starting position on the same line */
721 Buff
->CursorPosition
.X
= CursorStartX
;
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.
730 if (Buff
->CursorPosition
.X
>= Buff
->ScreenBufferSize
.X
- 1)
732 DPRINT1("Cannot display full-width character! CursorPosition.X = %d, ScreenBufferSize.X = %d\n",
733 Buff
->CursorPosition
.X
, Buff
->ScreenBufferSize
.X
);
738 Ptr
= ConioCoordToPointer(Buff
, Buff
->CursorPosition
.X
, Buff
->CursorPosition
.Y
);
741 * Check whether we are overwriting part of a full-width character,
742 * in which case we need to invalidate it.
744 if (Ptr
->Attributes
& COMMON_LVB_TRAILING_BYTE
)
747 * The cursor is on the trailing byte of a full-width character.
748 * Go back one position to kill the previous leading byte.
750 if (Buff
->CursorPosition
.X
> 0)
752 Ptr
= ConioCoordToPointer(Buff
, Buff
->CursorPosition
.X
- 1, Buff
->CursorPosition
.Y
);
753 Ptr
->Char
.UnicodeChar
= L
' ';
755 Ptr
->Attributes
= Buff
->ScreenDefaultAttrib
;
756 Ptr
->Attributes
&= ~COMMON_LVB_SBCSDBCS
;
758 Ptr
= ConioCoordToPointer(Buff
, Buff
->CursorPosition
.X
, Buff
->CursorPosition
.Y
);
761 /* Insert the character */
764 ASSERT(Buff
->CursorPosition
.X
< Buff
->ScreenBufferSize
.X
- 1);
766 /* Set the leading byte */
767 Ptr
->Char
.UnicodeChar
= Buffer
[i
];
769 Ptr
->Attributes
= Buff
->ScreenDefaultAttrib
;
770 Ptr
->Attributes
&= ~COMMON_LVB_SBCSDBCS
;
771 Ptr
->Attributes
|= COMMON_LVB_LEADING_BYTE
;
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' ';
778 Ptr
->Attributes
= Buff
->ScreenDefaultAttrib
;
779 Ptr
->Attributes
&= ~COMMON_LVB_SBCSDBCS
;
780 Ptr
->Attributes
|= COMMON_LVB_TRAILING_BYTE
;
784 Ptr
->Char
.UnicodeChar
= Buffer
[i
];
786 Ptr
->Attributes
= Buff
->ScreenDefaultAttrib
;
787 Ptr
->Attributes
&= ~COMMON_LVB_SBCSDBCS
;
791 Buff
->CursorPosition
.X
++;
793 if (Buff
->CursorPosition
.X
< Buff
->ScreenBufferSize
.X
)
795 /* If the following cell is the trailing byte of a full-width character, reset it */
796 if (Ptr
->Attributes
& COMMON_LVB_TRAILING_BYTE
)
798 Ptr
->Char
.UnicodeChar
= L
' ';
800 Ptr
->Attributes
= Buff
->ScreenDefaultAttrib
;
801 Ptr
->Attributes
&= ~COMMON_LVB_SBCSDBCS
;
805 if (Buff
->CursorPosition
.X
>= Buff
->ScreenBufferSize
.X
)
807 if (Buff
->Mode
& ENABLE_WRAP_AT_EOL_OUTPUT
)
809 /* Wrapping mode: Go to next line */
810 Buff
->CursorPosition
.X
= 0;
811 CursorStartX
= Buff
->CursorPosition
.X
;
812 ConioNextLine(Buff
, &UpdateRect
, &ScrolledLines
);
816 /* The cursor wraps back to its starting position on the same line */
817 Buff
->CursorPosition
.X
= CursorStartX
;
822 if (!ConioIsRectEmpty(&UpdateRect
) && (PCONSOLE_SCREEN_BUFFER
)Buff
== Console
->ActiveBuffer
)
824 // TermWriteStream(Console, &UpdateRect, CursorStartX, CursorStartY,
825 // ScrolledLines, Buffer, Length);
826 FrontEnd
->Vtbl
->WriteStream(FrontEnd
,
835 return STATUS_SUCCESS
;
840 static NTSTATUS NTAPI
841 ConSrvTermWriteStream(IN OUT PTERMINAL This
,
842 PTEXTMODE_SCREEN_BUFFER Buff
,
847 PFRONTEND FrontEnd
= This
->Context
;
848 return ConioWriteConsole(FrontEnd
,
855 /************ Line discipline ***************/
860 ConioDrawConsole(PCONSRV_CONSOLE Console
)
863 PCONSOLE_SCREEN_BUFFER ActiveBuffer
= Console
->ActiveBuffer
;
865 if (!ActiveBuffer
) return;
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);
875 ConSrvTermDrawRegion(IN OUT PTERMINAL This
,
878 PFRONTEND FrontEnd
= This
->Context
;
879 FrontEnd
->Vtbl
->DrawRegion(FrontEnd
, Region
);
883 ConSrvTermSetCursorInfo(IN OUT PTERMINAL This
,
884 PCONSOLE_SCREEN_BUFFER ScreenBuffer
)
886 PFRONTEND FrontEnd
= This
->Context
;
887 return FrontEnd
->Vtbl
->SetCursorInfo(FrontEnd
, ScreenBuffer
);
891 ConSrvTermSetScreenInfo(IN OUT PTERMINAL This
,
892 PCONSOLE_SCREEN_BUFFER ScreenBuffer
,
896 PFRONTEND FrontEnd
= This
->Context
;
897 return FrontEnd
->Vtbl
->SetScreenInfo(FrontEnd
,
904 ConSrvTermResizeTerminal(IN OUT PTERMINAL This
)
906 PFRONTEND FrontEnd
= This
->Context
;
907 FrontEnd
->Vtbl
->ResizeTerminal(FrontEnd
);
911 ConSrvTermSetActiveScreenBuffer(IN OUT PTERMINAL This
)
913 PFRONTEND FrontEnd
= This
->Context
;
914 FrontEnd
->Vtbl
->SetActiveScreenBuffer(FrontEnd
);
918 ConSrvTermReleaseScreenBuffer(IN OUT PTERMINAL This
,
919 IN PCONSOLE_SCREEN_BUFFER ScreenBuffer
)
921 PFRONTEND FrontEnd
= This
->Context
;
922 FrontEnd
->Vtbl
->ReleaseScreenBuffer(FrontEnd
, ScreenBuffer
);
926 ConSrvTermGetLargestConsoleWindowSize(IN OUT PTERMINAL This
,
929 PFRONTEND FrontEnd
= This
->Context
;
930 FrontEnd
->Vtbl
->GetLargestConsoleWindowSize(FrontEnd
, pSize
);
934 ConSrvTermSetPalette(IN OUT PTERMINAL This
,
935 HPALETTE PaletteHandle
,
938 PFRONTEND FrontEnd
= This
->Context
;
939 return FrontEnd
->Vtbl
->SetPalette(FrontEnd
, PaletteHandle
, PaletteUsage
);
943 ConSrvTermSetCodePage(IN OUT PTERMINAL This
,
946 PFRONTEND FrontEnd
= This
->Context
;
947 return FrontEnd
->Vtbl
->SetCodePage(FrontEnd
, CodePage
);
951 ConSrvTermShowMouseCursor(IN OUT PTERMINAL This
,
954 PFRONTEND FrontEnd
= This
->Context
;
955 return FrontEnd
->Vtbl
->ShowMouseCursor(FrontEnd
, Show
);
958 static TERMINAL_VTBL ConSrvTermVtbl
=
960 ConSrvTermInitTerminal
,
961 ConSrvTermDeinitTerminal
,
963 ConSrvTermReadStream
,
964 ConSrvTermWriteStream
,
966 ConSrvTermDrawRegion
,
967 ConSrvTermSetCursorInfo
,
968 ConSrvTermSetScreenInfo
,
969 ConSrvTermResizeTerminal
,
970 ConSrvTermSetActiveScreenBuffer
,
971 ConSrvTermReleaseScreenBuffer
,
972 ConSrvTermGetLargestConsoleWindowSize
,
973 ConSrvTermSetPalette
,
974 ConSrvTermSetCodePage
,
975 ConSrvTermShowMouseCursor
,
980 ResetFrontEnd(IN PCONSOLE Console
)
982 PCONSRV_CONSOLE ConSrvConsole
= (PCONSRV_CONSOLE
)Console
;
983 if (!Console
) return;
985 /* Reinitialize the frontend interface */
986 RtlZeroMemory(&ConSrvConsole
->FrontEndIFace
, sizeof(ConSrvConsole
->FrontEndIFace
));
987 ConSrvConsole
->FrontEndIFace
.Vtbl
= &ConSrvTermVtbl
;