xref: /reactos/win32ss/user/winsrv/consrv/condrv/text.c (revision 83c238e4)
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