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
TEXTMODE_BUFFER_Initialize(OUT PCONSOLE_SCREEN_BUFFER * Buffer,IN PCONSOLE Console,IN HANDLE ProcessHandle,IN PTEXTMODE_BUFFER_INFO TextModeInfo)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
TEXTMODE_BUFFER_Destroy(IN OUT PCONSOLE_SCREEN_BUFFER Buffer)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
ConioCoordToPointer(PTEXTMODE_SCREEN_BUFFER Buff,ULONG X,ULONG Y)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
ClearLineBuffer(PTEXTMODE_SCREEN_BUFFER Buff)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
ConioComputeUpdateRect(IN PTEXTMODE_SCREEN_BUFFER Buff,IN OUT PSMALL_RECT UpdateRect,IN PCOORD Start,IN UINT Length)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
ConioCopyRegion(IN PTEXTMODE_SCREEN_BUFFER ScreenBuffer,IN PSMALL_RECT SrcRegion,IN PCOORD DstOrigin)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
ConioFillRegion(IN PTEXTMODE_SCREEN_BUFFER ScreenBuffer,IN PSMALL_RECT Region,IN PSMALL_RECT ExcludeRegion OPTIONAL,IN CHAR_INFO FillChar)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
ConioResizeBuffer(PCONSOLE Console,PTEXTMODE_SCREEN_BUFFER ScreenBuffer,COORD Size)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
ConDrvChangeScreenBufferAttributes(IN PCONSOLE Console,IN PTEXTMODE_SCREEN_BUFFER Buffer,IN USHORT NewScreenAttrib,IN USHORT NewPopupAttrib)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
ConDrvReadConsoleOutput(IN PCONSOLE Console,IN PTEXTMODE_SCREEN_BUFFER Buffer,IN BOOLEAN Unicode,OUT PCHAR_INFO CharInfo,IN OUT PSMALL_RECT ReadRegion)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
ConDrvWriteConsoleOutput(IN PCONSOLE Console,IN PTEXTMODE_SCREEN_BUFFER Buffer,IN BOOLEAN Unicode,IN PCHAR_INFO CharInfo,IN OUT PSMALL_RECT WriteRegion)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
ConDrvWriteConsoleOutputVDM(IN PCONSOLE Console,IN PTEXTMODE_SCREEN_BUFFER Buffer,IN PCHAR_CELL CharInfo,IN COORD CharInfoSize,IN PSMALL_RECT WriteRegion)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
ConDrvWriteConsole(IN PCONSOLE Console,IN PTEXTMODE_SCREEN_BUFFER ScreenBuffer,IN BOOLEAN Unicode,IN PVOID StringBuffer,IN ULONG NumCharsToWrite,OUT PULONG NumCharsWritten OPTIONAL)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
IntReadConsoleOutputStringChars(IN PCONSOLE Console,IN PTEXTMODE_SCREEN_BUFFER Buffer,OUT PVOID StringBuffer,IN BOOLEAN Unicode,IN ULONG NumCodesToRead,IN PCOORD ReadCoord,OUT PULONG NumCodesRead OPTIONAL)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
IntReadConsoleOutputStringAttributes(IN PCONSOLE Console,IN PTEXTMODE_SCREEN_BUFFER Buffer,OUT PWORD StringBuffer,IN ULONG NumCodesToRead,IN PCOORD ReadCoord,OUT PULONG NumCodesRead OPTIONAL)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
ConDrvReadConsoleOutputString(IN PCONSOLE Console,IN PTEXTMODE_SCREEN_BUFFER Buffer,IN CODE_TYPE CodeType,OUT PVOID StringBuffer,IN ULONG NumCodesToRead,IN PCOORD ReadCoord,OUT PULONG NumCodesRead OPTIONAL)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
IntWriteConsoleOutputStringChars(IN PCONSOLE Console,IN PTEXTMODE_SCREEN_BUFFER Buffer,IN PVOID StringBuffer,IN BOOLEAN Unicode,IN ULONG NumCodesToWrite,IN PCOORD WriteCoord,OUT PULONG NumCodesWritten OPTIONAL)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
IntWriteConsoleOutputStringAttribute(IN PCONSOLE Console,IN PTEXTMODE_SCREEN_BUFFER Buffer,IN PWORD StringBuffer,IN ULONG NumCodesToWrite,IN PCOORD WriteCoord,OUT PULONG NumCodesWritten OPTIONAL)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
ConDrvWriteConsoleOutputString(IN PCONSOLE Console,IN PTEXTMODE_SCREEN_BUFFER Buffer,IN CODE_TYPE CodeType,IN PVOID StringBuffer,IN ULONG NumCodesToWrite,IN PCOORD WriteCoord,OUT PULONG NumCodesWritten OPTIONAL)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
ConDrvFillConsoleOutput(IN PCONSOLE Console,IN PTEXTMODE_SCREEN_BUFFER Buffer,IN CODE_TYPE CodeType,IN CODE_ELEMENT Code,IN ULONG NumCodesToWrite,IN PCOORD WriteCoord,OUT PULONG NumCodesWritten OPTIONAL)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
ConDrvGetConsoleScreenBufferInfo(IN PCONSOLE Console,IN PTEXTMODE_SCREEN_BUFFER Buffer,OUT PCOORD ScreenBufferSize,OUT PCOORD CursorPosition,OUT PCOORD ViewOrigin,OUT PCOORD ViewSize,OUT PCOORD MaximumViewSize,OUT PWORD Attributes)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
ConDrvSetConsoleTextAttribute(IN PCONSOLE Console,IN PTEXTMODE_SCREEN_BUFFER Buffer,IN WORD Attributes)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
ConDrvSetConsoleScreenBufferSize(IN PCONSOLE Console,IN PTEXTMODE_SCREEN_BUFFER Buffer,IN PCOORD Size)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
ConDrvScrollConsoleScreenBuffer(IN PCONSOLE Console,IN PTEXTMODE_SCREEN_BUFFER Buffer,IN BOOLEAN Unicode,IN PSMALL_RECT ScrollRectangle,IN BOOLEAN UseClipRectangle,IN PSMALL_RECT ClipRectangle OPTIONAL,IN PCOORD DestinationOrigin,IN CHAR_INFO FillChar)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
ConDrvSetConsoleWindowInfo(IN PCONSOLE Console,IN PTEXTMODE_SCREEN_BUFFER Buffer,IN BOOLEAN Absolute,IN PSMALL_RECT WindowRect)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