1 /* 2 * COPYRIGHT: See COPYING in the top level directory 3 * PROJECT: ReactOS Console Driver DLL 4 * FILE: win32ss/user/winsrv/consrv/condrv/text.c 5 * PURPOSE: Console Output Functions for text-mode screen-buffers 6 * PROGRAMMERS: Jeffrey Morlan 7 * Hermes Belusca-Maito (hermes.belusca@sfr.fr) 8 */ 9 10 /* INCLUDES *******************************************************************/ 11 12 #include <consrv.h> 13 14 #define NDEBUG 15 #include <debug.h> 16 17 /* GLOBALS ********************************************************************/ 18 19 /* 20 * From MSDN: 21 * "The lpMultiByteStr and lpWideCharStr pointers must not be the same. 22 * If they are the same, the function fails, and GetLastError returns 23 * ERROR_INVALID_PARAMETER." 24 */ 25 #define ConsoleOutputUnicodeToAnsiChar(Console, dChar, sWChar) \ 26 do { \ 27 ASSERT((ULONG_PTR)(dChar) != (ULONG_PTR)(sWChar)); \ 28 WideCharToMultiByte((Console)->OutputCodePage, 0, (sWChar), 1, (dChar), 1, NULL, NULL); \ 29 } while (0) 30 31 #define ConsoleOutputAnsiToUnicodeChar(Console, dWChar, sChar) \ 32 do { \ 33 ASSERT((ULONG_PTR)(dWChar) != (ULONG_PTR)(sChar)); \ 34 MultiByteToWideChar((Console)->OutputCodePage, 0, (sChar), 1, (dWChar), 1); \ 35 } while (0) 36 37 /* PRIVATE FUNCTIONS **********************************************************/ 38 39 /*static*/ VOID 40 ClearLineBuffer(PTEXTMODE_SCREEN_BUFFER Buff); 41 42 NTSTATUS 43 CONSOLE_SCREEN_BUFFER_Initialize( 44 OUT PCONSOLE_SCREEN_BUFFER* Buffer, 45 IN PCONSOLE Console, 46 IN CONSOLE_IO_OBJECT_TYPE Type, 47 IN SIZE_T Size); 48 49 VOID 50 CONSOLE_SCREEN_BUFFER_Destroy(IN OUT PCONSOLE_SCREEN_BUFFER Buffer); 51 52 53 NTSTATUS 54 TEXTMODE_BUFFER_Initialize(OUT PCONSOLE_SCREEN_BUFFER* Buffer, 55 IN PCONSOLE Console, 56 IN HANDLE ProcessHandle, 57 IN PTEXTMODE_BUFFER_INFO TextModeInfo) 58 { 59 NTSTATUS Status = STATUS_SUCCESS; 60 PTEXTMODE_SCREEN_BUFFER NewBuffer = NULL; 61 62 UNREFERENCED_PARAMETER(ProcessHandle); 63 64 if (Console == NULL || Buffer == NULL || TextModeInfo == NULL) 65 return STATUS_INVALID_PARAMETER; 66 67 if ((TextModeInfo->ScreenBufferSize.X == 0) || 68 (TextModeInfo->ScreenBufferSize.Y == 0)) 69 { 70 return STATUS_INVALID_PARAMETER; 71 } 72 73 *Buffer = NULL; 74 75 Status = CONSOLE_SCREEN_BUFFER_Initialize((PCONSOLE_SCREEN_BUFFER*)&NewBuffer, 76 Console, 77 TEXTMODE_BUFFER, 78 sizeof(TEXTMODE_SCREEN_BUFFER)); 79 if (!NT_SUCCESS(Status)) return Status; 80 81 NewBuffer->Buffer = ConsoleAllocHeap(HEAP_ZERO_MEMORY, 82 TextModeInfo->ScreenBufferSize.X * 83 TextModeInfo->ScreenBufferSize.Y * 84 sizeof(CHAR_INFO)); 85 if (NewBuffer->Buffer == NULL) 86 { 87 CONSOLE_SCREEN_BUFFER_Destroy((PCONSOLE_SCREEN_BUFFER)NewBuffer); 88 return STATUS_INSUFFICIENT_RESOURCES; 89 } 90 91 NewBuffer->ScreenBufferSize = TextModeInfo->ScreenBufferSize; 92 NewBuffer->OldScreenBufferSize = NewBuffer->ScreenBufferSize; 93 94 /* 95 * Set and fix the view size if needed. 96 * The rule is: ScreenBufferSize >= ViewSize (== ConsoleSize) 97 */ 98 NewBuffer->ViewSize.X = min(max(TextModeInfo->ViewSize.X, 1), NewBuffer->ScreenBufferSize.X); 99 NewBuffer->ViewSize.Y = min(max(TextModeInfo->ViewSize.Y, 1), NewBuffer->ScreenBufferSize.Y); 100 NewBuffer->OldViewSize = NewBuffer->ViewSize; 101 102 NewBuffer->ViewOrigin.X = NewBuffer->ViewOrigin.Y = 0; 103 NewBuffer->VirtualY = 0; 104 105 NewBuffer->CursorBlinkOn = NewBuffer->ForceCursorOff = FALSE; 106 NewBuffer->CursorInfo.bVisible = (TextModeInfo->IsCursorVisible && (TextModeInfo->CursorSize != 0)); 107 NewBuffer->CursorInfo.dwSize = min(max(TextModeInfo->CursorSize, 0), 100); 108 109 NewBuffer->ScreenDefaultAttrib = (TextModeInfo->ScreenAttrib & ~COMMON_LVB_SBCSDBCS); 110 NewBuffer->PopupDefaultAttrib = (TextModeInfo->PopupAttrib & ~COMMON_LVB_SBCSDBCS); 111 112 /* Initialize buffer to be empty with default attributes */ 113 for (NewBuffer->CursorPosition.Y = 0 ; NewBuffer->CursorPosition.Y < NewBuffer->ScreenBufferSize.Y; NewBuffer->CursorPosition.Y++) 114 { 115 ClearLineBuffer(NewBuffer); 116 } 117 NewBuffer->CursorPosition.X = NewBuffer->CursorPosition.Y = 0; 118 119 NewBuffer->Mode = ENABLE_PROCESSED_OUTPUT | ENABLE_WRAP_AT_EOL_OUTPUT; 120 121 *Buffer = (PCONSOLE_SCREEN_BUFFER)NewBuffer; 122 return STATUS_SUCCESS; 123 } 124 125 VOID 126 TEXTMODE_BUFFER_Destroy(IN OUT PCONSOLE_SCREEN_BUFFER Buffer) 127 { 128 PTEXTMODE_SCREEN_BUFFER Buff = (PTEXTMODE_SCREEN_BUFFER)Buffer; 129 130 /* 131 * IMPORTANT !! Reinitialize the type so that we don't enter a recursive 132 * infinite loop when calling CONSOLE_SCREEN_BUFFER_Destroy. 133 */ 134 Buffer->Header.Type = SCREEN_BUFFER; 135 136 ConsoleFreeHeap(Buff->Buffer); 137 138 CONSOLE_SCREEN_BUFFER_Destroy(Buffer); 139 } 140 141 142 PCHAR_INFO 143 ConioCoordToPointer(PTEXTMODE_SCREEN_BUFFER Buff, ULONG X, ULONG Y) 144 { 145 ASSERT(X < Buff->ScreenBufferSize.X); 146 ASSERT(Y < Buff->ScreenBufferSize.Y); 147 return &Buff->Buffer[((Y + Buff->VirtualY) % Buff->ScreenBufferSize.Y) * Buff->ScreenBufferSize.X + X]; 148 } 149 150 /*static*/ VOID 151 ClearLineBuffer(PTEXTMODE_SCREEN_BUFFER Buff) 152 { 153 PCHAR_INFO Ptr = ConioCoordToPointer(Buff, 0, Buff->CursorPosition.Y); 154 SHORT Pos; 155 156 for (Pos = 0; Pos < Buff->ScreenBufferSize.X; Pos++, Ptr++) 157 { 158 /* Fill the cell */ 159 Ptr->Char.UnicodeChar = L' '; 160 Ptr->Attributes = Buff->ScreenDefaultAttrib; 161 } 162 } 163 164 static VOID 165 ConioComputeUpdateRect(IN PTEXTMODE_SCREEN_BUFFER Buff, 166 IN OUT PSMALL_RECT UpdateRect, 167 IN PCOORD Start, 168 IN UINT Length) 169 { 170 if ((UINT)Buff->ScreenBufferSize.X <= Start->X + Length) 171 { 172 UpdateRect->Left = 0; 173 UpdateRect->Right = Buff->ScreenBufferSize.X - 1; 174 } 175 else 176 { 177 UpdateRect->Left = Start->X; 178 UpdateRect->Right = Start->X + Length - 1; 179 } 180 UpdateRect->Top = Start->Y; 181 UpdateRect->Bottom = Start->Y + (Start->X + Length - 1) / Buff->ScreenBufferSize.X; 182 if (Buff->ScreenBufferSize.Y <= UpdateRect->Bottom) 183 { 184 UpdateRect->Bottom = Buff->ScreenBufferSize.Y - 1; 185 } 186 } 187 188 /* 189 * Copy from one rectangle to another. We must be careful about the order of 190 * operations, to avoid overwriting parts of the source before they are copied. 191 */ 192 static VOID 193 ConioCopyRegion( 194 IN PTEXTMODE_SCREEN_BUFFER ScreenBuffer, 195 IN PSMALL_RECT SrcRegion, 196 IN PCOORD DstOrigin) 197 { 198 UINT Width, Height; 199 SHORT SY, DY; 200 SHORT YDelta; 201 PCHAR_INFO PtrSrc, PtrDst; 202 #if 0 203 SHORT SXOrg, DXOrg; 204 SHORT SX, DX; 205 SHORT XDelta; 206 UINT i, j; 207 #endif 208 209 if (ConioIsRectEmpty(SrcRegion)) 210 return; 211 212 #if 0 213 ASSERT(SrcRegion->Left >= 0 && SrcRegion->Left < ScreenBuffer->ScreenBufferSize.X); 214 ASSERT(SrcRegion->Right >= 0 && SrcRegion->Right < ScreenBuffer->ScreenBufferSize.X); 215 ASSERT(SrcRegion->Top >= 0 && SrcRegion->Top < ScreenBuffer->ScreenBufferSize.Y); 216 ASSERT(SrcRegion->Bottom >= 0 && SrcRegion->Bottom < ScreenBuffer->ScreenBufferSize.Y); 217 // ASSERT(DstOrigin->X >= 0 && DstOrigin->X < ScreenBuffer->ScreenBufferSize.X); 218 // ASSERT(DstOrigin->Y >= 0 && DstOrigin->Y < ScreenBuffer->ScreenBufferSize.Y); 219 #endif 220 221 /* If the source and destination regions are the same, just bail out */ 222 if ((SrcRegion->Left == DstOrigin->X) && (SrcRegion->Top == DstOrigin->Y)) 223 return; 224 225 SY = SrcRegion->Top; 226 DY = DstOrigin->Y; 227 YDelta = 1; 228 if (SY < DY) 229 { 230 /* Moving down: work from bottom up */ 231 SY = SrcRegion->Bottom; 232 DY = DstOrigin->Y + (SrcRegion->Bottom - SrcRegion->Top); 233 YDelta = -1; 234 } 235 236 #if 0 237 SXOrg = SrcRegion->Left; 238 DXOrg = DstOrigin->X; 239 XDelta = 1; 240 if (SXOrg < DXOrg) 241 { 242 /* Moving right: work from right to left */ 243 SXOrg = SrcRegion->Right; 244 DXOrg = DstOrigin->X + (SrcRegion->Right - SrcRegion->Left); 245 XDelta = -1; 246 } 247 #endif 248 249 /* Loop through the source region */ 250 Width = ConioRectWidth(SrcRegion); 251 Height = ConioRectHeight(SrcRegion); 252 #if 0 253 for (i = 0; i < Height; ++i, SY += YDelta, DY += YDelta) 254 #else 255 for (; Height-- > 0; SY += YDelta, DY += YDelta) 256 #endif 257 { 258 #if 0 259 SX = SXOrg; 260 DX = DXOrg; 261 262 PtrSrc = ConioCoordToPointer(ScreenBuffer, SX, SY); 263 PtrDst = ConioCoordToPointer(ScreenBuffer, DX, DY); 264 #else 265 PtrSrc = ConioCoordToPointer(ScreenBuffer, SrcRegion->Left, SY); 266 PtrDst = ConioCoordToPointer(ScreenBuffer, DstOrigin->X, DY); 267 #endif 268 269 // TODO: Correctly support copying full-width characters. 270 // By construction the source region is supposed to contain valid 271 // (possibly fullwidth) characters, so for these after the copy 272 // we need to check the characters at the borders and adjust the 273 // attributes accordingly. 274 275 #if 0 276 for (j = 0; j < Width; ++j, SX += XDelta, DX += XDelta) 277 { 278 *PtrDst = *PtrSrc; 279 PtrSrc += XDelta; 280 PtrDst += XDelta; 281 } 282 #else 283 /* RtlMoveMemory() takes into account for the direction of the copy */ 284 RtlMoveMemory(PtrDst, PtrSrc, Width * sizeof(CHAR_INFO)); 285 #endif 286 } 287 } 288 289 static VOID 290 ConioFillRegion( 291 IN PTEXTMODE_SCREEN_BUFFER ScreenBuffer, 292 IN PSMALL_RECT Region, 293 IN PSMALL_RECT ExcludeRegion OPTIONAL, 294 IN CHAR_INFO FillChar) 295 { 296 SHORT X, Y; 297 PCHAR_INFO Ptr; 298 // BOOLEAN bFullwidth; 299 300 /* Bail out if the region to fill is empty */ 301 if (ConioIsRectEmpty(Region)) 302 return; 303 304 /* Sanitize the exclusion region: if it's empty, ignore the region */ 305 if (ExcludeRegion && ConioIsRectEmpty(ExcludeRegion)) 306 ExcludeRegion = NULL; 307 308 #if 0 309 ASSERT(Region->Left >= 0 && Region->Left < ScreenBuffer->ScreenBufferSize.X); 310 ASSERT(Region->Right >= 0 && Region->Right < ScreenBuffer->ScreenBufferSize.X); 311 ASSERT(Region->Top >= 0 && Region->Top < ScreenBuffer->ScreenBufferSize.Y); 312 ASSERT(Region->Bottom >= 0 && Region->Bottom < ScreenBuffer->ScreenBufferSize.Y); 313 314 if (ExcludeRegion) 315 { 316 ASSERT(ExcludeRegion->Left >= 0 && ExcludeRegion->Left < ScreenBuffer->ScreenBufferSize.X); 317 ASSERT(ExcludeRegion->Right >= 0 && ExcludeRegion->Right < ScreenBuffer->ScreenBufferSize.X); 318 ASSERT(ExcludeRegion->Top >= 0 && ExcludeRegion->Top < ScreenBuffer->ScreenBufferSize.Y); 319 ASSERT(ExcludeRegion->Bottom >= 0 && ExcludeRegion->Bottom < ScreenBuffer->ScreenBufferSize.Y); 320 } 321 #endif 322 323 // bFullwidth = (ScreenBuffer->Header.Console->IsCJK && IS_FULL_WIDTH(FillChar.Char.UnicodeChar)); 324 325 /* Loop through the destination region */ 326 for (Y = Region->Top; Y <= Region->Bottom; ++Y) 327 { 328 Ptr = ConioCoordToPointer(ScreenBuffer, Region->Left, Y); 329 for (X = Region->Left; X <= Region->Right; ++X) 330 { 331 // TODO: Correctly support filling with full-width characters. 332 333 if (!ExcludeRegion || 334 !(X >= ExcludeRegion->Left && X <= ExcludeRegion->Right && 335 Y >= ExcludeRegion->Top && Y <= ExcludeRegion->Bottom)) 336 { 337 /* We are outside the excluded region, fill the destination */ 338 *Ptr = FillChar; 339 // Ptr->Attributes &= ~COMMON_LVB_SBCSDBCS; 340 } 341 342 ++Ptr; 343 } 344 } 345 } 346 347 348 349 // FIXME! 350 NTSTATUS NTAPI 351 ConDrvWriteConsoleInput(IN PCONSOLE Console, 352 IN PCONSOLE_INPUT_BUFFER InputBuffer, 353 IN BOOLEAN AppendToEnd, 354 IN PINPUT_RECORD InputRecord, 355 IN ULONG NumEventsToWrite, 356 OUT PULONG NumEventsWritten OPTIONAL); 357 358 NTSTATUS 359 ConioResizeBuffer(PCONSOLE Console, 360 PTEXTMODE_SCREEN_BUFFER ScreenBuffer, 361 COORD Size) 362 { 363 PCHAR_INFO Buffer; 364 PCHAR_INFO Ptr; 365 ULONG_PTR Offset = 0; 366 WORD CurrentAttribute; 367 USHORT CurrentY; 368 PCHAR_INFO OldBuffer; 369 DWORD i; 370 DWORD diff; 371 372 /* Zero size is invalid */ 373 if (Size.X == 0 || Size.Y == 0) 374 return STATUS_INVALID_PARAMETER; 375 376 /* Buffer size is not allowed to be smaller than the view size */ 377 if (Size.X < ScreenBuffer->ViewSize.X || Size.Y < ScreenBuffer->ViewSize.Y) 378 return STATUS_INVALID_PARAMETER; 379 380 if (Size.X == ScreenBuffer->ScreenBufferSize.X && Size.Y == ScreenBuffer->ScreenBufferSize.Y) 381 { 382 // FIXME: Trigger a buffer resize event ?? 383 return STATUS_SUCCESS; 384 } 385 386 if (Console->FixedSize) 387 { 388 /* 389 * The console is in fixed-size mode, so we cannot resize anything 390 * at the moment. However, keep those settings somewhere so that 391 * we can try to set them up when we will be allowed to do so. 392 */ 393 ScreenBuffer->OldScreenBufferSize = Size; 394 return STATUS_NOT_SUPPORTED; // STATUS_SUCCESS 395 } 396 397 Buffer = ConsoleAllocHeap(HEAP_ZERO_MEMORY, Size.X * Size.Y * sizeof(CHAR_INFO)); 398 if (!Buffer) return STATUS_NO_MEMORY; 399 400 DPRINT("Resizing (%d,%d) to (%d,%d)\n", ScreenBuffer->ScreenBufferSize.X, ScreenBuffer->ScreenBufferSize.Y, Size.X, Size.Y); 401 402 OldBuffer = ScreenBuffer->Buffer; 403 404 for (CurrentY = 0; CurrentY < ScreenBuffer->ScreenBufferSize.Y && CurrentY < Size.Y; CurrentY++) 405 { 406 Ptr = ConioCoordToPointer(ScreenBuffer, 0, CurrentY); 407 408 if (Size.X <= ScreenBuffer->ScreenBufferSize.X) 409 { 410 /* Reduce size */ 411 RtlCopyMemory(Buffer + Offset, Ptr, Size.X * sizeof(CHAR_INFO)); 412 Offset += Size.X; 413 414 /* If we have cut a trailing full-width character in half, remove it completely */ 415 Ptr = Buffer + Offset - 1; 416 if (Ptr->Attributes & COMMON_LVB_LEADING_BYTE) 417 { 418 Ptr->Char.UnicodeChar = L' '; 419 /* Keep all the other original attributes intact */ 420 Ptr->Attributes &= ~COMMON_LVB_SBCSDBCS; 421 } 422 } 423 else 424 { 425 /* Enlarge size */ 426 RtlCopyMemory(Buffer + Offset, Ptr, ScreenBuffer->ScreenBufferSize.X * sizeof(CHAR_INFO)); 427 Offset += ScreenBuffer->ScreenBufferSize.X; 428 429 /* The attribute to be used is the one of the last cell of the current line */ 430 CurrentAttribute = ConioCoordToPointer(ScreenBuffer, 431 ScreenBuffer->ScreenBufferSize.X - 1, 432 CurrentY)->Attributes; 433 CurrentAttribute &= ~COMMON_LVB_SBCSDBCS; 434 435 diff = Size.X - ScreenBuffer->ScreenBufferSize.X; 436 437 /* Zero-out the new part of the buffer */ 438 for (i = 0; i < diff; i++) 439 { 440 Ptr = Buffer + Offset; 441 Ptr->Char.UnicodeChar = L' '; 442 Ptr->Attributes = CurrentAttribute; 443 ++Offset; 444 } 445 } 446 } 447 448 if (Size.Y > ScreenBuffer->ScreenBufferSize.Y) 449 { 450 diff = Size.X * (Size.Y - ScreenBuffer->ScreenBufferSize.Y); 451 452 /* Zero-out the new part of the buffer */ 453 for (i = 0; i < diff; i++) 454 { 455 Ptr = Buffer + Offset; 456 Ptr->Char.UnicodeChar = L' '; 457 Ptr->Attributes = ScreenBuffer->ScreenDefaultAttrib; 458 ++Offset; 459 } 460 } 461 462 (void)InterlockedExchangePointer((PVOID volatile*)&ScreenBuffer->Buffer, Buffer); 463 ConsoleFreeHeap(OldBuffer); 464 ScreenBuffer->ScreenBufferSize = ScreenBuffer->OldScreenBufferSize = Size; 465 ScreenBuffer->VirtualY = 0; 466 467 /* Ensure the cursor and the view are within the buffer */ 468 ScreenBuffer->CursorPosition.X = min(ScreenBuffer->CursorPosition.X, Size.X - 1); 469 ScreenBuffer->CursorPosition.Y = min(ScreenBuffer->CursorPosition.Y, Size.Y - 1); 470 ScreenBuffer->ViewOrigin.X = min(ScreenBuffer->ViewOrigin.X, Size.X - ScreenBuffer->ViewSize.X); 471 ScreenBuffer->ViewOrigin.Y = min(ScreenBuffer->ViewOrigin.Y, Size.Y - ScreenBuffer->ViewSize.Y); 472 473 /* 474 * Trigger a buffer resize event 475 */ 476 if (Console->InputBuffer.Mode & ENABLE_WINDOW_INPUT) 477 { 478 ULONG NumEventsWritten; 479 INPUT_RECORD er; 480 481 er.EventType = WINDOW_BUFFER_SIZE_EVENT; 482 er.Event.WindowBufferSizeEvent.dwSize = ScreenBuffer->ScreenBufferSize; 483 484 // ConioProcessInputEvent(Console, &er); 485 ConDrvWriteConsoleInput(Console, 486 &Console->InputBuffer, 487 TRUE, 488 &er, 489 1, 490 &NumEventsWritten); 491 } 492 493 return STATUS_SUCCESS; 494 } 495 496 NTSTATUS NTAPI 497 ConDrvChangeScreenBufferAttributes(IN PCONSOLE Console, 498 IN PTEXTMODE_SCREEN_BUFFER Buffer, 499 IN USHORT NewScreenAttrib, 500 IN USHORT NewPopupAttrib) 501 { 502 USHORT X, Y; 503 PCHAR_INFO Ptr; 504 505 COORD TopLeft = {0}; 506 ULONG NumCodesToWrite; 507 USHORT OldScreenAttrib, OldPopupAttrib; 508 509 if (Console == NULL || Buffer == NULL) 510 { 511 return STATUS_INVALID_PARAMETER; 512 } 513 514 /* Validity check */ 515 ASSERT(Console == Buffer->Header.Console); 516 517 /* Sanitize the new attributes */ 518 NewScreenAttrib &= ~COMMON_LVB_SBCSDBCS; 519 NewPopupAttrib &= ~COMMON_LVB_SBCSDBCS; 520 521 NumCodesToWrite = Buffer->ScreenBufferSize.X * Buffer->ScreenBufferSize.Y; 522 OldScreenAttrib = Buffer->ScreenDefaultAttrib; 523 OldPopupAttrib = Buffer->PopupDefaultAttrib; 524 525 for (Y = 0; Y < Buffer->ScreenBufferSize.Y; ++Y) 526 { 527 Ptr = ConioCoordToPointer(Buffer, 0, Y); 528 for (X = 0; X < Buffer->ScreenBufferSize.X; ++X) 529 { 530 /* 531 * Change the current colors only if they are the old ones. 532 */ 533 534 /* Foreground color */ 535 if ((Ptr->Attributes & 0x0F) == (OldScreenAttrib & 0x0F)) 536 Ptr->Attributes = (Ptr->Attributes & 0xFFF0) | (NewScreenAttrib & 0x0F); 537 if ((Ptr->Attributes & 0x0F) == (OldPopupAttrib & 0x0F)) 538 Ptr->Attributes = (Ptr->Attributes & 0xFFF0) | (NewPopupAttrib & 0x0F); 539 540 /* Background color */ 541 if ((Ptr->Attributes & 0xF0) == (OldScreenAttrib & 0xF0)) 542 Ptr->Attributes = (Ptr->Attributes & 0xFF0F) | (NewScreenAttrib & 0xF0); 543 if ((Ptr->Attributes & 0xF0) == (OldPopupAttrib & 0xF0)) 544 Ptr->Attributes = (Ptr->Attributes & 0xFF0F) | (NewPopupAttrib & 0xF0); 545 546 ++Ptr; 547 } 548 } 549 550 /* Save foreground and background attributes for both screen and popup */ 551 Buffer->ScreenDefaultAttrib = NewScreenAttrib; 552 Buffer->PopupDefaultAttrib = NewPopupAttrib; 553 554 /* Refresh the display if needed */ 555 if ((PCONSOLE_SCREEN_BUFFER)Buffer == Console->ActiveBuffer) 556 { 557 SMALL_RECT UpdateRect; 558 ConioComputeUpdateRect(Buffer, &UpdateRect, &TopLeft, NumCodesToWrite); 559 TermDrawRegion(Console, &UpdateRect); 560 } 561 562 return STATUS_SUCCESS; 563 } 564 565 566 /* PUBLIC DRIVER APIS *********************************************************/ 567 568 NTSTATUS NTAPI 569 ConDrvReadConsoleOutput(IN PCONSOLE Console, 570 IN PTEXTMODE_SCREEN_BUFFER Buffer, 571 IN BOOLEAN Unicode, 572 OUT PCHAR_INFO CharInfo/*Buffer*/, 573 IN OUT PSMALL_RECT ReadRegion) 574 { 575 SHORT X, Y; 576 SMALL_RECT ScreenBuffer; 577 PCHAR_INFO CurCharInfo; 578 SMALL_RECT CapturedReadRegion; 579 PCHAR_INFO Ptr; 580 581 if (Console == NULL || Buffer == NULL || CharInfo == NULL || ReadRegion == NULL) 582 { 583 return STATUS_INVALID_PARAMETER; 584 } 585 586 /* Validity check */ 587 ASSERT(Console == Buffer->Header.Console); 588 589 CapturedReadRegion = *ReadRegion; 590 591 /* Make sure ReadRegion is inside the screen buffer */ 592 ConioInitRect(&ScreenBuffer, 0, 0, 593 Buffer->ScreenBufferSize.Y - 1, 594 Buffer->ScreenBufferSize.X - 1); 595 if (!ConioGetIntersection(&CapturedReadRegion, &CapturedReadRegion, &ScreenBuffer)) 596 { 597 /* 598 * It is okay to have a ReadRegion completely outside 599 * the screen buffer. No data is read then. 600 */ 601 return STATUS_SUCCESS; 602 } 603 604 CurCharInfo = CharInfo; 605 606 for (Y = CapturedReadRegion.Top; Y <= CapturedReadRegion.Bottom; ++Y) 607 { 608 Ptr = ConioCoordToPointer(Buffer, CapturedReadRegion.Left, Y); 609 for (X = CapturedReadRegion.Left; X <= CapturedReadRegion.Right; ++X) 610 { 611 if (Unicode) 612 { 613 CurCharInfo->Char.UnicodeChar = Ptr->Char.UnicodeChar; 614 } 615 else 616 { 617 // ConsoleOutputUnicodeToAnsiChar(Console, &CurCharInfo->Char.AsciiChar, &Ptr->Char.UnicodeChar); 618 WideCharToMultiByte(Console->OutputCodePage, 0, &Ptr->Char.UnicodeChar, 1, 619 &CurCharInfo->Char.AsciiChar, 1, NULL, NULL); 620 } 621 #if (_WIN32_WINNT < _WIN32_WINNT_WIN8) 622 /* NOTE: Windows < 8 compatibility: DBCS flags are filtered out */ 623 CurCharInfo->Attributes = (Ptr->Attributes & ~COMMON_LVB_SBCSDBCS); 624 #else 625 CurCharInfo->Attributes = Ptr->Attributes; 626 #endif 627 ++Ptr; 628 ++CurCharInfo; 629 } 630 } 631 632 *ReadRegion = CapturedReadRegion; 633 634 return STATUS_SUCCESS; 635 } 636 637 NTSTATUS NTAPI 638 ConDrvWriteConsoleOutput(IN PCONSOLE Console, 639 IN PTEXTMODE_SCREEN_BUFFER Buffer, 640 IN BOOLEAN Unicode, 641 IN PCHAR_INFO CharInfo/*Buffer*/, 642 IN OUT PSMALL_RECT WriteRegion) 643 { 644 SHORT X, Y; 645 SMALL_RECT ScreenBuffer; 646 PCHAR_INFO CurCharInfo; 647 SMALL_RECT CapturedWriteRegion; 648 PCHAR_INFO Ptr; 649 650 if (Console == NULL || Buffer == NULL || CharInfo == NULL || WriteRegion == NULL) 651 { 652 return STATUS_INVALID_PARAMETER; 653 } 654 655 /* Validity check */ 656 ASSERT(Console == Buffer->Header.Console); 657 658 CapturedWriteRegion = *WriteRegion; 659 660 /* Make sure WriteRegion is inside the screen buffer */ 661 ConioInitRect(&ScreenBuffer, 0, 0, 662 Buffer->ScreenBufferSize.Y - 1, 663 Buffer->ScreenBufferSize.X - 1); 664 if (!ConioGetIntersection(&CapturedWriteRegion, &CapturedWriteRegion, &ScreenBuffer)) 665 { 666 /* 667 * It is okay to have a WriteRegion completely outside 668 * the screen buffer. No data is written then. 669 */ 670 return STATUS_SUCCESS; 671 } 672 673 CurCharInfo = CharInfo; 674 675 for (Y = CapturedWriteRegion.Top; Y <= CapturedWriteRegion.Bottom; ++Y) 676 { 677 Ptr = ConioCoordToPointer(Buffer, CapturedWriteRegion.Left, Y); 678 for (X = CapturedWriteRegion.Left; X <= CapturedWriteRegion.Right; ++X) 679 { 680 if (Unicode) 681 { 682 Ptr->Char.UnicodeChar = CurCharInfo->Char.UnicodeChar; 683 } 684 else 685 { 686 ConsoleOutputAnsiToUnicodeChar(Console, &Ptr->Char.UnicodeChar, &CurCharInfo->Char.AsciiChar); 687 } 688 // TODO: Sanitize DBCS attributes? 689 Ptr->Attributes = CurCharInfo->Attributes; 690 ++Ptr; 691 ++CurCharInfo; 692 } 693 } 694 695 TermDrawRegion(Console, &CapturedWriteRegion); 696 697 *WriteRegion = CapturedWriteRegion; 698 699 return STATUS_SUCCESS; 700 } 701 702 /* 703 * NOTE: This function is strongly inspired by ConDrvWriteConsoleOutput... 704 * FIXME: This function MUST be moved into consrv/conoutput.c because only 705 * consrv knows how to manipulate VDM screenbuffers. 706 */ 707 NTSTATUS NTAPI 708 ConDrvWriteConsoleOutputVDM(IN PCONSOLE Console, 709 IN PTEXTMODE_SCREEN_BUFFER Buffer, 710 IN PCHAR_CELL CharInfo/*Buffer*/, 711 IN COORD CharInfoSize, 712 IN PSMALL_RECT WriteRegion) 713 { 714 SHORT X, Y; 715 SMALL_RECT ScreenBuffer; 716 PCHAR_CELL CurCharInfo; 717 SMALL_RECT CapturedWriteRegion; 718 PCHAR_INFO Ptr; 719 720 if (Console == NULL || Buffer == NULL || CharInfo == NULL || WriteRegion == NULL) 721 { 722 return STATUS_INVALID_PARAMETER; 723 } 724 725 /* Validity check */ 726 ASSERT(Console == Buffer->Header.Console); 727 728 CapturedWriteRegion = *WriteRegion; 729 730 /* Make sure WriteRegion is inside the screen buffer */ 731 ConioInitRect(&ScreenBuffer, 0, 0, 732 Buffer->ScreenBufferSize.Y - 1, 733 Buffer->ScreenBufferSize.X - 1); 734 if (!ConioGetIntersection(&CapturedWriteRegion, &CapturedWriteRegion, &ScreenBuffer)) 735 { 736 /* 737 * It is okay to have a WriteRegion completely outside 738 * the screen buffer. No data is written then. 739 */ 740 return STATUS_SUCCESS; 741 } 742 743 // CurCharInfo = CharInfo; 744 745 for (Y = CapturedWriteRegion.Top; Y <= CapturedWriteRegion.Bottom; ++Y) 746 { 747 /**/CurCharInfo = CharInfo + Y * CharInfoSize.X + CapturedWriteRegion.Left;/**/ 748 749 Ptr = ConioCoordToPointer(Buffer, CapturedWriteRegion.Left, Y); 750 for (X = CapturedWriteRegion.Left; X <= CapturedWriteRegion.Right; ++X) 751 { 752 ConsoleOutputAnsiToUnicodeChar(Console, &Ptr->Char.UnicodeChar, &CurCharInfo->Char); 753 Ptr->Attributes = CurCharInfo->Attributes; 754 ++Ptr; 755 ++CurCharInfo; 756 } 757 } 758 759 return STATUS_SUCCESS; 760 } 761 762 NTSTATUS NTAPI 763 ConDrvWriteConsole(IN PCONSOLE Console, 764 IN PTEXTMODE_SCREEN_BUFFER ScreenBuffer, 765 IN BOOLEAN Unicode, 766 IN PVOID StringBuffer, 767 IN ULONG NumCharsToWrite, 768 OUT PULONG NumCharsWritten OPTIONAL) 769 { 770 NTSTATUS Status = STATUS_SUCCESS; 771 PWCHAR Buffer = NULL; 772 ULONG Written = 0; 773 ULONG Length; 774 775 if (Console == NULL || ScreenBuffer == NULL /* || StringBuffer == NULL */) 776 return STATUS_INVALID_PARAMETER; 777 778 /* Validity checks */ 779 ASSERT(Console == ScreenBuffer->Header.Console); 780 ASSERT((StringBuffer != NULL) || (StringBuffer == NULL && NumCharsToWrite == 0)); 781 782 /* Stop here if the console is paused */ 783 if (Console->ConsolePaused) return STATUS_PENDING; 784 785 /* Convert the string to UNICODE */ 786 if (Unicode) 787 { 788 Buffer = StringBuffer; 789 } 790 else 791 { 792 Length = MultiByteToWideChar(Console->OutputCodePage, 0, 793 (PCHAR)StringBuffer, 794 NumCharsToWrite, 795 NULL, 0); 796 Buffer = ConsoleAllocHeap(0, Length * sizeof(WCHAR)); 797 if (Buffer) 798 { 799 MultiByteToWideChar(Console->OutputCodePage, 0, 800 (PCHAR)StringBuffer, 801 NumCharsToWrite, 802 (PWCHAR)Buffer, Length); 803 } 804 else 805 { 806 Status = STATUS_NO_MEMORY; 807 } 808 } 809 810 /* Send it */ 811 if (Buffer) 812 { 813 if (NT_SUCCESS(Status)) 814 { 815 Status = TermWriteStream(Console, 816 ScreenBuffer, 817 Buffer, 818 NumCharsToWrite, 819 TRUE); 820 if (NT_SUCCESS(Status)) 821 { 822 Written = NumCharsToWrite; 823 } 824 } 825 826 if (!Unicode) ConsoleFreeHeap(Buffer); 827 } 828 829 if (NumCharsWritten) *NumCharsWritten = Written; 830 831 return Status; 832 } 833 834 static NTSTATUS 835 IntReadConsoleOutputStringChars( 836 IN PCONSOLE Console, 837 IN PTEXTMODE_SCREEN_BUFFER Buffer, 838 OUT PVOID StringBuffer, 839 IN BOOLEAN Unicode, 840 IN ULONG NumCodesToRead, 841 IN PCOORD ReadCoord, 842 OUT PULONG NumCodesRead OPTIONAL) 843 { 844 ULONG CodeSize; 845 LPBYTE ReadBuffer = StringBuffer; 846 SHORT X, Y; 847 SHORT XStart = ReadCoord->X; 848 ULONG nNumChars = 0; 849 PCHAR_INFO Ptr; 850 BOOLEAN bCJK = Console->IsCJK; 851 852 CodeSize = (Unicode ? RTL_FIELD_SIZE(CODE_ELEMENT, UnicodeChar) 853 : RTL_FIELD_SIZE(CODE_ELEMENT, AsciiChar)); 854 855 for (Y = ReadCoord->Y; Y < Buffer->ScreenBufferSize.Y; ++Y) 856 { 857 Ptr = ConioCoordToPointer(Buffer, XStart, Y); 858 for (X = XStart; X < Buffer->ScreenBufferSize.X; ++X) 859 { 860 if (nNumChars >= NumCodesToRead) 861 goto Quit; 862 863 /* 864 * For Chinese, Japanese and Korean. 865 * For full-width characters: copy only the character specified 866 * in the leading-byte cell, skipping the trailing-byte cell. 867 */ 868 if (bCJK && (Ptr->Attributes & COMMON_LVB_TRAILING_BYTE)) 869 { 870 /* 871 * Windows "compensates" for the fact this is a full-width 872 * character by reducing the amount of characters to be read. 873 * The understanding being that the specified amount of 874 * characters is also in "units" of (half-width) cells. 875 */ 876 if (NumCodesToRead > 0) --NumCodesToRead; 877 ++Ptr; 878 continue; 879 } 880 881 if (Unicode) 882 *(PWCHAR)ReadBuffer = Ptr->Char.UnicodeChar; 883 else 884 ConsoleOutputUnicodeToAnsiChar(Console, (PCHAR)ReadBuffer, &Ptr->Char.UnicodeChar); 885 886 ++Ptr; 887 888 ReadBuffer += CodeSize; 889 ++nNumChars; 890 } 891 /* Restart at the beginning of the next line */ 892 XStart = 0; 893 } 894 Quit: 895 896 if (NumCodesRead) 897 *NumCodesRead = nNumChars; 898 899 return STATUS_SUCCESS; 900 } 901 902 static NTSTATUS 903 IntReadConsoleOutputStringAttributes( 904 IN PCONSOLE Console, 905 IN PTEXTMODE_SCREEN_BUFFER Buffer, 906 OUT PWORD StringBuffer, 907 IN ULONG NumCodesToRead, 908 IN PCOORD ReadCoord, 909 OUT PULONG NumCodesRead OPTIONAL) 910 { 911 SHORT X, Y; 912 SHORT XStart = ReadCoord->X; 913 ULONG nNumChars = 0; 914 PCHAR_INFO Ptr; 915 916 for (Y = ReadCoord->Y; Y < Buffer->ScreenBufferSize.Y; ++Y) 917 { 918 Ptr = ConioCoordToPointer(Buffer, XStart, Y); 919 for (X = XStart; X < Buffer->ScreenBufferSize.X; ++X) 920 { 921 if (nNumChars >= NumCodesToRead) 922 goto Quit; 923 924 *StringBuffer = Ptr->Attributes; 925 ++Ptr; 926 927 ++StringBuffer; 928 ++nNumChars; 929 } 930 /* Restart at the beginning of the next line */ 931 XStart = 0; 932 } 933 Quit: 934 935 if (NumCodesRead) 936 *NumCodesRead = nNumChars; 937 938 return STATUS_SUCCESS; 939 } 940 941 NTSTATUS NTAPI 942 ConDrvReadConsoleOutputString( 943 IN PCONSOLE Console, 944 IN PTEXTMODE_SCREEN_BUFFER Buffer, 945 IN CODE_TYPE CodeType, 946 OUT PVOID StringBuffer, 947 IN ULONG NumCodesToRead, 948 IN PCOORD ReadCoord, 949 OUT PULONG NumCodesRead OPTIONAL) 950 { 951 if (Console == NULL || Buffer == NULL || ReadCoord == NULL /* || EndCoord == NULL */) 952 { 953 return STATUS_INVALID_PARAMETER; 954 } 955 956 /* Validity checks */ 957 ASSERT(Console == Buffer->Header.Console); 958 ASSERT((StringBuffer != NULL) || (StringBuffer == NULL && NumCodesToRead == 0)); 959 960 if (NumCodesRead) 961 *NumCodesRead = 0; 962 963 if (!StringBuffer || (NumCodesToRead == 0)) 964 return STATUS_SUCCESS; // Nothing to do! 965 966 /* Do nothing if the reading starting point is outside of the screen buffer */ 967 if ( ReadCoord->X < 0 || ReadCoord->X >= Buffer->ScreenBufferSize.X || 968 ReadCoord->Y < 0 || ReadCoord->Y >= Buffer->ScreenBufferSize.Y ) 969 { 970 return STATUS_SUCCESS; 971 } 972 973 NumCodesToRead = min(NumCodesToRead, (ULONG)Buffer->ScreenBufferSize.X * Buffer->ScreenBufferSize.Y); 974 975 switch (CodeType) 976 { 977 case CODE_ASCII: 978 { 979 return IntReadConsoleOutputStringChars(Console, 980 Buffer, 981 StringBuffer, 982 FALSE, 983 NumCodesToRead, 984 ReadCoord, 985 NumCodesRead); 986 } 987 988 case CODE_UNICODE: 989 { 990 return IntReadConsoleOutputStringChars(Console, 991 Buffer, 992 StringBuffer, 993 TRUE, 994 NumCodesToRead, 995 ReadCoord, 996 NumCodesRead); 997 } 998 999 case CODE_ATTRIBUTE: 1000 { 1001 C_ASSERT(RTL_FIELD_SIZE(CODE_ELEMENT, Attribute) == sizeof(WORD)); 1002 return IntReadConsoleOutputStringAttributes(Console, 1003 Buffer, 1004 (PWORD)StringBuffer, 1005 NumCodesToRead, 1006 ReadCoord, 1007 NumCodesRead); 1008 } 1009 1010 default: 1011 return STATUS_INVALID_PARAMETER; 1012 } 1013 } 1014 1015 static NTSTATUS 1016 IntWriteConsoleOutputStringChars( 1017 IN PCONSOLE Console, 1018 IN PTEXTMODE_SCREEN_BUFFER Buffer, 1019 IN PVOID StringBuffer, 1020 IN BOOLEAN Unicode, 1021 IN ULONG NumCodesToWrite, 1022 IN PCOORD WriteCoord, 1023 OUT PULONG NumCodesWritten OPTIONAL) 1024 { 1025 NTSTATUS Status = STATUS_SUCCESS; 1026 PWCHAR WriteBuffer = NULL; 1027 PWCHAR tmpString = NULL; 1028 ULONG Length; 1029 SHORT X, Y; 1030 SHORT XStart = WriteCoord->X; 1031 ULONG nNumChars = 0; 1032 PCHAR_INFO Ptr; 1033 BOOLEAN bCJK = Console->IsCJK; 1034 1035 /* Convert the string to UNICODE */ 1036 if (Unicode) 1037 { 1038 WriteBuffer = StringBuffer; 1039 } 1040 else 1041 { 1042 /* Convert the ASCII string into Unicode before writing it to the console */ 1043 Length = MultiByteToWideChar(Console->OutputCodePage, 0, 1044 (PCHAR)StringBuffer, 1045 NumCodesToWrite, 1046 NULL, 0); 1047 tmpString = ConsoleAllocHeap(0, Length * sizeof(WCHAR)); 1048 if (!tmpString) 1049 { 1050 Status = STATUS_NO_MEMORY; 1051 goto Quit; 1052 } 1053 1054 MultiByteToWideChar(Console->OutputCodePage, 0, 1055 (PCHAR)StringBuffer, 1056 NumCodesToWrite, 1057 tmpString, Length); 1058 1059 NumCodesToWrite = Length; 1060 WriteBuffer = tmpString; 1061 } 1062 1063 for (Y = WriteCoord->Y; Y < Buffer->ScreenBufferSize.Y; ++Y) 1064 { 1065 Ptr = ConioCoordToPointer(Buffer, XStart, Y); 1066 for (X = XStart; X < Buffer->ScreenBufferSize.X; ++X) 1067 { 1068 if (nNumChars >= NumCodesToWrite) 1069 goto Quit; 1070 1071 /* For Chinese, Japanese and Korean */ 1072 if (bCJK && IS_FULL_WIDTH(*WriteBuffer)) 1073 { 1074 /* A full-width character cannot cross a line boundary */ 1075 if (X >= Buffer->ScreenBufferSize.X - 1) 1076 { 1077 /* Go to next line */ 1078 break; // Break the X-loop only. 1079 } 1080 1081 /* Set the leading byte */ 1082 Ptr->Char.UnicodeChar = *WriteBuffer; 1083 Ptr->Attributes &= ~COMMON_LVB_SBCSDBCS; 1084 Ptr->Attributes |= COMMON_LVB_LEADING_BYTE; 1085 ++Ptr; 1086 1087 /* Set the trailing byte */ 1088 Ptr->Char.UnicodeChar = L' '; 1089 Ptr->Attributes &= ~COMMON_LVB_SBCSDBCS; 1090 Ptr->Attributes |= COMMON_LVB_TRAILING_BYTE; 1091 } 1092 else 1093 { 1094 Ptr->Char.UnicodeChar = *WriteBuffer; 1095 } 1096 1097 ++Ptr; 1098 1099 ++WriteBuffer; 1100 ++nNumChars; 1101 } 1102 /* Restart at the beginning of the next line */ 1103 XStart = 0; 1104 } 1105 Quit: 1106 1107 if (tmpString) 1108 { 1109 ASSERT(!Unicode); 1110 ConsoleFreeHeap(tmpString); 1111 } 1112 1113 if (NumCodesWritten) 1114 *NumCodesWritten = nNumChars; 1115 1116 return Status; 1117 } 1118 1119 static NTSTATUS 1120 IntWriteConsoleOutputStringAttribute( 1121 IN PCONSOLE Console, 1122 IN PTEXTMODE_SCREEN_BUFFER Buffer, 1123 IN PWORD StringBuffer, 1124 IN ULONG NumCodesToWrite, 1125 IN PCOORD WriteCoord, 1126 OUT PULONG NumCodesWritten OPTIONAL) 1127 { 1128 SHORT X, Y; 1129 SHORT XStart = WriteCoord->X; 1130 ULONG nNumChars = 0; 1131 PCHAR_INFO Ptr; 1132 1133 for (Y = WriteCoord->Y; Y < Buffer->ScreenBufferSize.Y; ++Y) 1134 { 1135 Ptr = ConioCoordToPointer(Buffer, XStart, Y); 1136 for (X = XStart; X < Buffer->ScreenBufferSize.X; ++X) 1137 { 1138 if (nNumChars >= NumCodesToWrite) 1139 goto Quit; 1140 1141 Ptr->Attributes &= COMMON_LVB_SBCSDBCS; 1142 Ptr->Attributes |= (*StringBuffer & ~COMMON_LVB_SBCSDBCS); 1143 1144 ++Ptr; 1145 1146 ++StringBuffer; 1147 ++nNumChars; 1148 } 1149 /* Restart at the beginning of the next line */ 1150 XStart = 0; 1151 } 1152 Quit: 1153 1154 if (NumCodesWritten) 1155 *NumCodesWritten = nNumChars; 1156 1157 return STATUS_SUCCESS; 1158 } 1159 1160 NTSTATUS NTAPI 1161 ConDrvWriteConsoleOutputString( 1162 IN PCONSOLE Console, 1163 IN PTEXTMODE_SCREEN_BUFFER Buffer, 1164 IN CODE_TYPE CodeType, 1165 IN PVOID StringBuffer, 1166 IN ULONG NumCodesToWrite, 1167 IN PCOORD WriteCoord, 1168 OUT PULONG NumCodesWritten OPTIONAL) 1169 { 1170 NTSTATUS Status; 1171 1172 if (Console == NULL || Buffer == NULL || WriteCoord == NULL /* || EndCoord == NULL */) 1173 { 1174 return STATUS_INVALID_PARAMETER; 1175 } 1176 1177 /* Validity checks */ 1178 ASSERT(Console == Buffer->Header.Console); 1179 ASSERT((StringBuffer != NULL) || (StringBuffer == NULL && NumCodesToWrite == 0)); 1180 1181 if (NumCodesWritten) 1182 *NumCodesWritten = 0; 1183 1184 if (!StringBuffer || (NumCodesToWrite == 0)) 1185 return STATUS_SUCCESS; // Nothing to do! 1186 1187 /* Do nothing if the writing starting point is outside of the screen buffer */ 1188 if ( WriteCoord->X < 0 || WriteCoord->X >= Buffer->ScreenBufferSize.X || 1189 WriteCoord->Y < 0 || WriteCoord->Y >= Buffer->ScreenBufferSize.Y ) 1190 { 1191 return STATUS_SUCCESS; 1192 } 1193 1194 NumCodesToWrite = min(NumCodesToWrite, (ULONG)Buffer->ScreenBufferSize.X * Buffer->ScreenBufferSize.Y); 1195 1196 switch (CodeType) 1197 { 1198 case CODE_ASCII: 1199 { 1200 C_ASSERT(RTL_FIELD_SIZE(CODE_ELEMENT, AsciiChar) == sizeof(CHAR)); 1201 Status = IntWriteConsoleOutputStringChars(Console, 1202 Buffer, 1203 StringBuffer, 1204 FALSE, 1205 NumCodesToWrite, 1206 WriteCoord, 1207 NumCodesWritten); 1208 break; 1209 } 1210 1211 case CODE_UNICODE: 1212 { 1213 C_ASSERT(RTL_FIELD_SIZE(CODE_ELEMENT, UnicodeChar) == sizeof(WCHAR)); 1214 Status = IntWriteConsoleOutputStringChars(Console, 1215 Buffer, 1216 StringBuffer, 1217 TRUE, 1218 NumCodesToWrite, 1219 WriteCoord, 1220 NumCodesWritten); 1221 break; 1222 } 1223 1224 case CODE_ATTRIBUTE: 1225 { 1226 C_ASSERT(RTL_FIELD_SIZE(CODE_ELEMENT, Attribute) == sizeof(WORD)); 1227 Status = IntWriteConsoleOutputStringAttribute(Console, 1228 Buffer, 1229 (PWORD)StringBuffer, 1230 NumCodesToWrite, 1231 WriteCoord, 1232 NumCodesWritten); 1233 break; 1234 } 1235 1236 default: 1237 return STATUS_INVALID_PARAMETER; 1238 } 1239 1240 if ((PCONSOLE_SCREEN_BUFFER)Buffer == Console->ActiveBuffer) 1241 { 1242 SMALL_RECT UpdateRect; 1243 ConioComputeUpdateRect(Buffer, &UpdateRect, WriteCoord, NumCodesToWrite); 1244 TermDrawRegion(Console, &UpdateRect); 1245 } 1246 1247 return Status; 1248 } 1249 1250 NTSTATUS NTAPI 1251 ConDrvFillConsoleOutput(IN PCONSOLE Console, 1252 IN PTEXTMODE_SCREEN_BUFFER Buffer, 1253 IN CODE_TYPE CodeType, 1254 IN CODE_ELEMENT Code, 1255 IN ULONG NumCodesToWrite, 1256 IN PCOORD WriteCoord, 1257 OUT PULONG NumCodesWritten OPTIONAL) 1258 { 1259 SHORT X, Y; 1260 SHORT XStart; 1261 ULONG nNumChars = 0; 1262 PCHAR_INFO Ptr; 1263 BOOLEAN bLead, bFullwidth; 1264 1265 if (Console == NULL || Buffer == NULL || WriteCoord == NULL) 1266 { 1267 return STATUS_INVALID_PARAMETER; 1268 } 1269 1270 /* Validity check */ 1271 ASSERT(Console == Buffer->Header.Console); 1272 1273 if (NumCodesWritten) 1274 *NumCodesWritten = 0; 1275 1276 if (NumCodesToWrite == 0) 1277 return STATUS_SUCCESS; // Nothing to do! 1278 1279 /* Do nothing if the writing starting point is outside of the screen buffer */ 1280 if ( WriteCoord->X < 0 || WriteCoord->X >= Buffer->ScreenBufferSize.X || 1281 WriteCoord->Y < 0 || WriteCoord->Y >= Buffer->ScreenBufferSize.Y ) 1282 { 1283 return STATUS_SUCCESS; 1284 } 1285 1286 NumCodesToWrite = min(NumCodesToWrite, (ULONG)Buffer->ScreenBufferSize.X * Buffer->ScreenBufferSize.Y); 1287 1288 if (CodeType == CODE_ASCII) 1289 { 1290 /* Conversion from the ASCII char to the UNICODE char */ 1291 CODE_ELEMENT tmp; 1292 ConsoleOutputAnsiToUnicodeChar(Console, &tmp.UnicodeChar, &Code.AsciiChar); 1293 Code = tmp; 1294 } 1295 1296 XStart = WriteCoord->X; 1297 1298 /* For Chinese, Japanese and Korean */ 1299 X = XStart; 1300 Y = WriteCoord->Y; 1301 bLead = TRUE; 1302 bFullwidth = FALSE; 1303 if (Console->IsCJK) 1304 { 1305 bFullwidth = IS_FULL_WIDTH(Code.UnicodeChar); 1306 if (X > 0) 1307 { 1308 Ptr = ConioCoordToPointer(Buffer, X - 1, Y); 1309 if (Ptr->Attributes & COMMON_LVB_LEADING_BYTE) 1310 { 1311 Ptr->Char.UnicodeChar = L' '; 1312 Ptr->Attributes &= ~COMMON_LVB_SBCSDBCS; 1313 } 1314 } 1315 } 1316 1317 for (Y = WriteCoord->Y; Y < Buffer->ScreenBufferSize.Y; ++Y) 1318 { 1319 Ptr = ConioCoordToPointer(Buffer, XStart, Y); 1320 for (X = XStart; X < Buffer->ScreenBufferSize.X; ++X) 1321 { 1322 if (nNumChars >= NumCodesToWrite) 1323 goto Quit; 1324 1325 switch (CodeType) 1326 { 1327 case CODE_ASCII: 1328 case CODE_UNICODE: 1329 Ptr->Char.UnicodeChar = Code.UnicodeChar; 1330 Ptr->Attributes &= ~COMMON_LVB_SBCSDBCS; 1331 if (bFullwidth) 1332 { 1333 if (bLead) 1334 Ptr->Attributes |= COMMON_LVB_LEADING_BYTE; 1335 else 1336 Ptr->Attributes |= COMMON_LVB_TRAILING_BYTE; 1337 } 1338 bLead = !bLead; 1339 break; 1340 1341 case CODE_ATTRIBUTE: 1342 Ptr->Attributes &= COMMON_LVB_SBCSDBCS; 1343 Ptr->Attributes |= (Code.Attribute & ~COMMON_LVB_SBCSDBCS); 1344 break; 1345 } 1346 1347 ++Ptr; 1348 1349 ++nNumChars; 1350 } 1351 /* Restart at the beginning of the next line */ 1352 XStart = 0; 1353 } 1354 Quit: 1355 1356 if ((nNumChars & 1) & bFullwidth) 1357 { 1358 if (X + Y * Buffer->ScreenBufferSize.X > 0) 1359 { 1360 Ptr = ConioCoordToPointer(Buffer, X - 1, Y); 1361 Ptr->Char.UnicodeChar = L' '; 1362 Ptr->Attributes &= ~COMMON_LVB_SBCSDBCS; 1363 } 1364 } 1365 1366 if (NumCodesWritten) 1367 *NumCodesWritten = nNumChars; 1368 1369 if ((PCONSOLE_SCREEN_BUFFER)Buffer == Console->ActiveBuffer) 1370 { 1371 SMALL_RECT UpdateRect; 1372 ConioComputeUpdateRect(Buffer, &UpdateRect, WriteCoord, nNumChars); 1373 TermDrawRegion(Console, &UpdateRect); 1374 } 1375 1376 return STATUS_SUCCESS; 1377 } 1378 1379 NTSTATUS NTAPI 1380 ConDrvGetConsoleScreenBufferInfo(IN PCONSOLE Console, 1381 IN PTEXTMODE_SCREEN_BUFFER Buffer, 1382 OUT PCOORD ScreenBufferSize, 1383 OUT PCOORD CursorPosition, 1384 OUT PCOORD ViewOrigin, 1385 OUT PCOORD ViewSize, 1386 OUT PCOORD MaximumViewSize, 1387 OUT PWORD Attributes) 1388 { 1389 COORD LargestWindowSize; 1390 1391 if (Console == NULL || Buffer == NULL || ScreenBufferSize == NULL || 1392 CursorPosition == NULL || ViewOrigin == NULL || ViewSize == NULL || 1393 MaximumViewSize == NULL || Attributes == NULL) 1394 { 1395 return STATUS_INVALID_PARAMETER; 1396 } 1397 1398 /* Validity check */ 1399 ASSERT(Console == Buffer->Header.Console); 1400 1401 *ScreenBufferSize = Buffer->ScreenBufferSize; 1402 *CursorPosition = Buffer->CursorPosition; 1403 *ViewOrigin = Buffer->ViewOrigin; 1404 *ViewSize = Buffer->ViewSize; 1405 *Attributes = Buffer->ScreenDefaultAttrib; 1406 1407 /* 1408 * Retrieve the largest possible console window size, taking 1409 * into account the size of the console screen buffer. 1410 */ 1411 TermGetLargestConsoleWindowSize(Console, &LargestWindowSize); 1412 LargestWindowSize.X = min(LargestWindowSize.X, Buffer->ScreenBufferSize.X); 1413 LargestWindowSize.Y = min(LargestWindowSize.Y, Buffer->ScreenBufferSize.Y); 1414 *MaximumViewSize = LargestWindowSize; 1415 1416 return STATUS_SUCCESS; 1417 } 1418 1419 NTSTATUS NTAPI 1420 ConDrvSetConsoleTextAttribute(IN PCONSOLE Console, 1421 IN PTEXTMODE_SCREEN_BUFFER Buffer, 1422 IN WORD Attributes) 1423 { 1424 if (Console == NULL || Buffer == NULL) 1425 return STATUS_INVALID_PARAMETER; 1426 1427 /* Validity check */ 1428 ASSERT(Console == Buffer->Header.Console); 1429 1430 Buffer->ScreenDefaultAttrib = (Attributes & ~COMMON_LVB_SBCSDBCS); 1431 return STATUS_SUCCESS; 1432 } 1433 1434 NTSTATUS NTAPI 1435 ConDrvSetConsoleScreenBufferSize(IN PCONSOLE Console, 1436 IN PTEXTMODE_SCREEN_BUFFER Buffer, 1437 IN PCOORD Size) 1438 { 1439 NTSTATUS Status; 1440 1441 if (Console == NULL || Buffer == NULL || Size == NULL) 1442 return STATUS_INVALID_PARAMETER; 1443 1444 /* Validity check */ 1445 ASSERT(Console == Buffer->Header.Console); 1446 1447 Status = ConioResizeBuffer(Console, Buffer, *Size); 1448 if (NT_SUCCESS(Status)) TermResizeTerminal(Console); 1449 1450 return Status; 1451 } 1452 1453 NTSTATUS NTAPI 1454 ConDrvScrollConsoleScreenBuffer( 1455 IN PCONSOLE Console, 1456 IN PTEXTMODE_SCREEN_BUFFER Buffer, 1457 IN BOOLEAN Unicode, 1458 IN PSMALL_RECT ScrollRectangle, 1459 IN BOOLEAN UseClipRectangle, 1460 IN PSMALL_RECT ClipRectangle OPTIONAL, 1461 IN PCOORD DestinationOrigin, 1462 IN CHAR_INFO FillChar) 1463 { 1464 COORD CapturedDestinationOrigin; 1465 SMALL_RECT ScreenBuffer; 1466 SMALL_RECT CapturedClipRectangle; 1467 SMALL_RECT SrcRegion; 1468 SMALL_RECT DstRegion; 1469 SMALL_RECT UpdateRegion; 1470 1471 if (Console == NULL || Buffer == NULL || ScrollRectangle == NULL || 1472 (UseClipRectangle && (ClipRectangle == NULL)) || DestinationOrigin == NULL) 1473 { 1474 return STATUS_INVALID_PARAMETER; 1475 } 1476 1477 /* Validity check */ 1478 ASSERT(Console == Buffer->Header.Console); 1479 1480 CapturedDestinationOrigin = *DestinationOrigin; 1481 1482 /* Make sure the source rectangle is inside the screen buffer */ 1483 ConioInitRect(&ScreenBuffer, 0, 0, 1484 Buffer->ScreenBufferSize.Y - 1, 1485 Buffer->ScreenBufferSize.X - 1); 1486 if (!ConioGetIntersection(&SrcRegion, ScrollRectangle, &ScreenBuffer)) 1487 { 1488 return STATUS_SUCCESS; 1489 } 1490 1491 /* If the source was clipped on the left or top, adjust the destination accordingly */ 1492 if (ScrollRectangle->Left < 0) 1493 CapturedDestinationOrigin.X -= ScrollRectangle->Left; 1494 if (ScrollRectangle->Top < 0) 1495 CapturedDestinationOrigin.Y -= ScrollRectangle->Top; 1496 1497 /* 1498 * If a clip rectangle is provided, clip it to the screen buffer, 1499 * otherwise use the latter one as the clip rectangle. 1500 */ 1501 if (UseClipRectangle) 1502 { 1503 CapturedClipRectangle = *ClipRectangle; 1504 if (!ConioGetIntersection(&CapturedClipRectangle, &CapturedClipRectangle, &ScreenBuffer)) 1505 { 1506 return STATUS_SUCCESS; 1507 } 1508 } 1509 else 1510 { 1511 CapturedClipRectangle = ScreenBuffer; 1512 } 1513 1514 /* 1515 * Windows compatibility: Do nothing if the intersection of the source region 1516 * with the clip rectangle is empty, even if the intersection of destination 1517 * region with the clip rectangle is NOT empty and therefore it would have 1518 * been possible to copy contents to it... 1519 */ 1520 if (!ConioGetIntersection(&UpdateRegion, &SrcRegion, &CapturedClipRectangle)) 1521 { 1522 return STATUS_SUCCESS; 1523 } 1524 1525 /* Initialize the destination rectangle, of same size as the source rectangle */ 1526 ConioInitRect(&DstRegion, 1527 CapturedDestinationOrigin.Y, 1528 CapturedDestinationOrigin.X, 1529 CapturedDestinationOrigin.Y + ConioRectHeight(&SrcRegion) - 1, 1530 CapturedDestinationOrigin.X + ConioRectWidth(&SrcRegion ) - 1); 1531 1532 if (ConioGetIntersection(&DstRegion, &DstRegion, &CapturedClipRectangle)) 1533 { 1534 /* 1535 * Build the region image, within the source region, 1536 * of the destination region we should copy into. 1537 */ 1538 SrcRegion.Left += DstRegion.Left - CapturedDestinationOrigin.X; 1539 SrcRegion.Top += DstRegion.Top - CapturedDestinationOrigin.Y; 1540 SrcRegion.Right = SrcRegion.Left + (DstRegion.Right - DstRegion.Left); 1541 SrcRegion.Bottom = SrcRegion.Top + (DstRegion.Bottom - DstRegion.Top); 1542 1543 /* Do the copy */ 1544 CapturedDestinationOrigin.X = DstRegion.Left; 1545 CapturedDestinationOrigin.Y = DstRegion.Top; 1546 ConioCopyRegion(Buffer, &SrcRegion, &CapturedDestinationOrigin); 1547 } 1548 1549 if (!Unicode) 1550 { 1551 /* Conversion from the ASCII char to the UNICODE char */ 1552 WCHAR tmp; 1553 ConsoleOutputAnsiToUnicodeChar(Console, &tmp, &FillChar.Char.AsciiChar); 1554 FillChar.Char.UnicodeChar = tmp; 1555 } 1556 /* Sanitize the attribute */ 1557 FillChar.Attributes &= ~COMMON_LVB_SBCSDBCS; 1558 1559 /* 1560 * Fill the intersection (== UpdateRegion) of the source region with the 1561 * clip rectangle, excluding the destination region. 1562 */ 1563 ConioFillRegion(Buffer, &UpdateRegion, &DstRegion, FillChar); 1564 1565 if ((PCONSOLE_SCREEN_BUFFER)Buffer == Console->ActiveBuffer) 1566 { 1567 ConioGetUnion(&UpdateRegion, &UpdateRegion, &DstRegion); 1568 if (ConioGetIntersection(&UpdateRegion, &UpdateRegion, &CapturedClipRectangle)) 1569 { 1570 /* Draw update region */ 1571 TermDrawRegion(Console, &UpdateRegion); 1572 } 1573 } 1574 1575 return STATUS_SUCCESS; 1576 } 1577 1578 NTSTATUS NTAPI 1579 ConDrvSetConsoleWindowInfo(IN PCONSOLE Console, 1580 IN PTEXTMODE_SCREEN_BUFFER Buffer, 1581 IN BOOLEAN Absolute, 1582 IN PSMALL_RECT WindowRect) 1583 { 1584 SMALL_RECT CapturedWindowRect; 1585 COORD LargestWindowSize; 1586 1587 if (Console == NULL || Buffer == NULL || WindowRect == NULL) 1588 return STATUS_INVALID_PARAMETER; 1589 1590 /* Validity check */ 1591 ASSERT(Console == Buffer->Header.Console); 1592 1593 CapturedWindowRect = *WindowRect; 1594 1595 if (!Absolute) 1596 { 1597 /* Relative positions are given, transform them to absolute ones */ 1598 CapturedWindowRect.Left += Buffer->ViewOrigin.X; 1599 CapturedWindowRect.Top += Buffer->ViewOrigin.Y; 1600 CapturedWindowRect.Right += Buffer->ViewOrigin.X + Buffer->ViewSize.X - 1; 1601 CapturedWindowRect.Bottom += Buffer->ViewOrigin.Y + Buffer->ViewSize.Y - 1; 1602 } 1603 1604 /* 1605 * The MSDN documentation on SetConsoleWindowInfo() is partially wrong about 1606 * the performed checks this API performs. While it is correct that the 1607 * 'Right'/'Bottom' members cannot be strictly smaller than the 'Left'/'Top' 1608 * members (the rectangle cannot be empty), they can be equal (describe one cell). 1609 * Also, if the 'Left' or 'Top' members are negative, this is automatically 1610 * corrected for, and the window rectangle coordinates are shifted accordingly. 1611 */ 1612 if (ConioIsRectEmpty(&CapturedWindowRect)) 1613 { 1614 return STATUS_INVALID_PARAMETER; 1615 } 1616 1617 /* 1618 * Forbid window sizes larger than the largest allowed console window size, 1619 * taking into account the size of the console screen buffer. 1620 */ 1621 TermGetLargestConsoleWindowSize(Console, &LargestWindowSize); 1622 LargestWindowSize.X = min(LargestWindowSize.X, Buffer->ScreenBufferSize.X); 1623 LargestWindowSize.Y = min(LargestWindowSize.Y, Buffer->ScreenBufferSize.Y); 1624 if ((ConioRectWidth(&CapturedWindowRect) > LargestWindowSize.X) || 1625 (ConioRectHeight(&CapturedWindowRect) > LargestWindowSize.Y)) 1626 { 1627 return STATUS_INVALID_PARAMETER; 1628 } 1629 1630 /* Shift the window rectangle coordinates if 'Left' or 'Top' are negative */ 1631 if (CapturedWindowRect.Left < 0) 1632 { 1633 CapturedWindowRect.Right -= CapturedWindowRect.Left; 1634 CapturedWindowRect.Left = 0; 1635 } 1636 if (CapturedWindowRect.Top < 0) 1637 { 1638 CapturedWindowRect.Bottom -= CapturedWindowRect.Top; 1639 CapturedWindowRect.Top = 0; 1640 } 1641 1642 /* Clip the window rectangle to the screen buffer */ 1643 CapturedWindowRect.Right = min(CapturedWindowRect.Right , Buffer->ScreenBufferSize.X); 1644 CapturedWindowRect.Bottom = min(CapturedWindowRect.Bottom, Buffer->ScreenBufferSize.Y); 1645 1646 Buffer->ViewOrigin.X = CapturedWindowRect.Left; 1647 Buffer->ViewOrigin.Y = CapturedWindowRect.Top; 1648 1649 Buffer->ViewSize.X = ConioRectWidth(&CapturedWindowRect); 1650 Buffer->ViewSize.Y = ConioRectHeight(&CapturedWindowRect); 1651 1652 TermResizeTerminal(Console); 1653 1654 return STATUS_SUCCESS; 1655 } 1656 1657 /* EOF */ 1658