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 */ 992