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