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