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