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