d54d8207508a2684977b5523a5815c9885f89cf0
[reactos.git] / reactos / win32ss / user / winsrv / consrv / condrv / text.c
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 ASSERT((ULONG_PTR)dChar != (ULONG_PTR)sWChar); \
27 WideCharToMultiByte((Console)->OutputCodePage, 0, (sWChar), 1, (dChar), 1, NULL, NULL)
28
29 #define ConsoleOutputAnsiToUnicodeChar(Console, dWChar, sChar) \
30 ASSERT((ULONG_PTR)dWChar != (ULONG_PTR)sChar); \
31 MultiByteToWideChar((Console)->OutputCodePage, 0, (sChar), 1, (dWChar), 1)
32
33 /* PRIVATE FUNCTIONS **********************************************************/
34
35 CONSOLE_IO_OBJECT_TYPE
36 TEXTMODE_BUFFER_GetType(PCONSOLE_SCREEN_BUFFER This)
37 {
38 // return This->Header.Type;
39 return TEXTMODE_BUFFER;
40 }
41
42 static CONSOLE_SCREEN_BUFFER_VTBL TextVtbl =
43 {
44 TEXTMODE_BUFFER_GetType,
45 };
46
47
48 /*static*/ VOID
49 ClearLineBuffer(PTEXTMODE_SCREEN_BUFFER Buff);
50
51
52 NTSTATUS
53 CONSOLE_SCREEN_BUFFER_Initialize(OUT PCONSOLE_SCREEN_BUFFER* Buffer,
54 IN PCONSOLE Console,
55 IN PCONSOLE_SCREEN_BUFFER_VTBL Vtbl,
56 IN SIZE_T Size);
57 VOID
58 CONSOLE_SCREEN_BUFFER_Destroy(IN OUT PCONSOLE_SCREEN_BUFFER Buffer);
59
60
61 NTSTATUS
62 TEXTMODE_BUFFER_Initialize(OUT PCONSOLE_SCREEN_BUFFER* Buffer,
63 IN PCONSOLE Console,
64 IN HANDLE ProcessHandle,
65 IN PTEXTMODE_BUFFER_INFO TextModeInfo)
66 {
67 NTSTATUS Status = STATUS_SUCCESS;
68 PTEXTMODE_SCREEN_BUFFER NewBuffer = NULL;
69
70 UNREFERENCED_PARAMETER(ProcessHandle);
71
72 if (Console == NULL || Buffer == NULL || TextModeInfo == NULL)
73 return STATUS_INVALID_PARAMETER;
74
75 *Buffer = NULL;
76
77 Status = CONSOLE_SCREEN_BUFFER_Initialize((PCONSOLE_SCREEN_BUFFER*)&NewBuffer,
78 Console,
79 &TextVtbl,
80 sizeof(TEXTMODE_SCREEN_BUFFER));
81 if (!NT_SUCCESS(Status)) return Status;
82 NewBuffer->Header.Type = TEXTMODE_BUFFER;
83
84 NewBuffer->Buffer = ConsoleAllocHeap(HEAP_ZERO_MEMORY,
85 TextModeInfo->ScreenBufferSize.X *
86 TextModeInfo->ScreenBufferSize.Y *
87 sizeof(CHAR_INFO));
88 if (NewBuffer->Buffer == NULL)
89 {
90 CONSOLE_SCREEN_BUFFER_Destroy((PCONSOLE_SCREEN_BUFFER)NewBuffer);
91 return STATUS_INSUFFICIENT_RESOURCES;
92 }
93
94 NewBuffer->ScreenBufferSize = NewBuffer->OldScreenBufferSize
95 = TextModeInfo->ScreenBufferSize;
96 NewBuffer->ViewSize = NewBuffer->OldViewSize
97 = Console->ConsoleSize;
98
99 NewBuffer->ViewOrigin.X = NewBuffer->ViewOrigin.Y = 0;
100 NewBuffer->VirtualY = 0;
101
102 NewBuffer->CursorBlinkOn = NewBuffer->ForceCursorOff = FALSE;
103 NewBuffer->CursorInfo.bVisible = (TextModeInfo->IsCursorVisible && (TextModeInfo->CursorSize != 0));
104 NewBuffer->CursorInfo.dwSize = min(max(TextModeInfo->CursorSize, 0), 100);
105
106 NewBuffer->ScreenDefaultAttrib = TextModeInfo->ScreenAttrib;
107 NewBuffer->PopupDefaultAttrib = TextModeInfo->PopupAttrib;
108
109 /* Initialize buffer to be empty with default attributes */
110 for (NewBuffer->CursorPosition.Y = 0 ; NewBuffer->CursorPosition.Y < NewBuffer->ScreenBufferSize.Y; NewBuffer->CursorPosition.Y++)
111 {
112 ClearLineBuffer(NewBuffer);
113 }
114 NewBuffer->CursorPosition.X = NewBuffer->CursorPosition.Y = 0;
115
116 NewBuffer->Mode = ENABLE_PROCESSED_OUTPUT | ENABLE_WRAP_AT_EOL_OUTPUT;
117
118 *Buffer = (PCONSOLE_SCREEN_BUFFER)NewBuffer;
119 return STATUS_SUCCESS;
120 }
121
122 VOID
123 TEXTMODE_BUFFER_Destroy(IN OUT PCONSOLE_SCREEN_BUFFER Buffer)
124 {
125 PTEXTMODE_SCREEN_BUFFER Buff = (PTEXTMODE_SCREEN_BUFFER)Buffer;
126
127 /*
128 * IMPORTANT !! Reinitialize the type so that we don't enter a recursive
129 * infinite loop when calling CONSOLE_SCREEN_BUFFER_Destroy.
130 */
131 Buffer->Header.Type = SCREEN_BUFFER;
132
133 ConsoleFreeHeap(Buff->Buffer);
134
135 CONSOLE_SCREEN_BUFFER_Destroy(Buffer);
136 }
137
138
139 PCHAR_INFO
140 ConioCoordToPointer(PTEXTMODE_SCREEN_BUFFER Buff, ULONG X, ULONG Y)
141 {
142 return &Buff->Buffer[((Y + Buff->VirtualY) % Buff->ScreenBufferSize.Y) * Buff->ScreenBufferSize.X + X];
143 }
144
145 /*static*/ VOID
146 ClearLineBuffer(PTEXTMODE_SCREEN_BUFFER Buff)
147 {
148 PCHAR_INFO Ptr = ConioCoordToPointer(Buff, 0, Buff->CursorPosition.Y);
149 SHORT Pos;
150
151 for (Pos = 0; Pos < Buff->ScreenBufferSize.X; Pos++, Ptr++)
152 {
153 /* Fill the cell */
154 Ptr->Char.UnicodeChar = L' ';
155 Ptr->Attributes = Buff->ScreenDefaultAttrib;
156 }
157 }
158
159 static VOID
160 ConioComputeUpdateRect(IN PTEXTMODE_SCREEN_BUFFER Buff,
161 IN OUT PSMALL_RECT UpdateRect,
162 IN PCOORD Start,
163 IN UINT Length)
164 {
165 if ((UINT)Buff->ScreenBufferSize.X <= Start->X + Length)
166 {
167 UpdateRect->Left = 0;
168 UpdateRect->Right = Buff->ScreenBufferSize.X - 1;
169 }
170 else
171 {
172 UpdateRect->Left = Start->X;
173 UpdateRect->Right = Start->X + Length - 1;
174 }
175 UpdateRect->Top = Start->Y;
176 UpdateRect->Bottom = Start->Y + (Start->X + Length - 1) / Buff->ScreenBufferSize.X;
177 if (Buff->ScreenBufferSize.Y <= UpdateRect->Bottom)
178 {
179 UpdateRect->Bottom = Buff->ScreenBufferSize.Y - 1;
180 }
181 }
182
183 /*
184 * Move from one rectangle to another. We must be careful about the order that
185 * this is done, to avoid overwriting parts of the source before they are moved.
186 */
187 static VOID
188 ConioMoveRegion(PTEXTMODE_SCREEN_BUFFER ScreenBuffer,
189 PSMALL_RECT SrcRegion,
190 PSMALL_RECT DstRegion,
191 PSMALL_RECT ClipRegion,
192 CHAR_INFO FillChar)
193 {
194 int Width = ConioRectWidth(SrcRegion);
195 int Height = ConioRectHeight(SrcRegion);
196 int SX, SY;
197 int DX, DY;
198 int XDelta, YDelta;
199 int i, j;
200
201 SY = SrcRegion->Top;
202 DY = DstRegion->Top;
203 YDelta = 1;
204 if (SY < DY)
205 {
206 /* Moving down: work from bottom up */
207 SY = SrcRegion->Bottom;
208 DY = DstRegion->Bottom;
209 YDelta = -1;
210 }
211 for (i = 0; i < Height; i++)
212 {
213 PCHAR_INFO SRow = ConioCoordToPointer(ScreenBuffer, 0, SY);
214 PCHAR_INFO DRow = ConioCoordToPointer(ScreenBuffer, 0, DY);
215
216 SX = SrcRegion->Left;
217 DX = DstRegion->Left;
218 XDelta = 1;
219 if (SX < DX)
220 {
221 /* Moving right: work from right to left */
222 SX = SrcRegion->Right;
223 DX = DstRegion->Right;
224 XDelta = -1;
225 }
226 for (j = 0; j < Width; j++)
227 {
228 CHAR_INFO Cell = SRow[SX];
229 if (SX >= ClipRegion->Left && SX <= ClipRegion->Right &&
230 SY >= ClipRegion->Top && SY <= ClipRegion->Bottom)
231 {
232 SRow[SX] = FillChar;
233 }
234 if (DX >= ClipRegion->Left && DX <= ClipRegion->Right &&
235 DY >= ClipRegion->Top && DY <= ClipRegion->Bottom)
236 {
237 DRow[DX] = Cell;
238 }
239 SX += XDelta;
240 DX += XDelta;
241 }
242 SY += YDelta;
243 DY += YDelta;
244 }
245 }
246
247 // FIXME!
248 NTSTATUS NTAPI
249 ConDrvWriteConsoleInput(IN PCONSOLE Console,
250 IN PCONSOLE_INPUT_BUFFER InputBuffer,
251 IN BOOLEAN AppendToEnd,
252 IN PINPUT_RECORD InputRecord,
253 IN ULONG NumEventsToWrite,
254 OUT PULONG NumEventsWritten OPTIONAL);
255
256 NTSTATUS
257 ConioResizeBuffer(PCONSOLE Console,
258 PTEXTMODE_SCREEN_BUFFER ScreenBuffer,
259 COORD Size)
260 {
261 PCHAR_INFO Buffer;
262 DWORD Offset = 0;
263 PCHAR_INFO ptr;
264 WORD CurrentAttribute;
265 USHORT CurrentY;
266 PCHAR_INFO OldBuffer;
267 DWORD i;
268 DWORD diff;
269
270 /* Buffer size is not allowed to be smaller than the view size */
271 if (Size.X < ScreenBuffer->ViewSize.X || Size.Y < ScreenBuffer->ViewSize.Y)
272 return STATUS_INVALID_PARAMETER;
273
274 if (Size.X == ScreenBuffer->ScreenBufferSize.X && Size.Y == ScreenBuffer->ScreenBufferSize.Y)
275 {
276 // FIXME: Trigger a buffer resize event ??
277 return STATUS_SUCCESS;
278 }
279
280 if (Console->FixedSize)
281 {
282 /*
283 * The console is in fixed-size mode, so we cannot resize anything
284 * at the moment. However, keep those settings somewhere so that
285 * we can try to set them up when we will be allowed to do so.
286 */
287 ScreenBuffer->OldScreenBufferSize = Size;
288 return STATUS_NOT_SUPPORTED; // STATUS_SUCCESS
289 }
290
291 Buffer = ConsoleAllocHeap(HEAP_ZERO_MEMORY, Size.X * Size.Y * sizeof(CHAR_INFO));
292 if (!Buffer) return STATUS_NO_MEMORY;
293
294 DPRINT("Resizing (%d,%d) to (%d,%d)\n", ScreenBuffer->ScreenBufferSize.X, ScreenBuffer->ScreenBufferSize.Y, Size.X, Size.Y);
295 OldBuffer = ScreenBuffer->Buffer;
296
297 for (CurrentY = 0; CurrentY < ScreenBuffer->ScreenBufferSize.Y && CurrentY < Size.Y; CurrentY++)
298 {
299 ptr = ConioCoordToPointer(ScreenBuffer, 0, CurrentY);
300 if (Size.X <= ScreenBuffer->ScreenBufferSize.X)
301 {
302 /* Reduce size */
303 RtlCopyMemory(Buffer + Offset, ptr, Size.X * sizeof(CHAR_INFO));
304 Offset += Size.X;
305 }
306 else
307 {
308 /* Enlarge size */
309 RtlCopyMemory(Buffer + Offset, ptr, ScreenBuffer->ScreenBufferSize.X * sizeof(CHAR_INFO));
310 Offset += ScreenBuffer->ScreenBufferSize.X;
311
312 /* The attribute to be used is the one of the last cell of the current line */
313 CurrentAttribute = ConioCoordToPointer(ScreenBuffer,
314 ScreenBuffer->ScreenBufferSize.X - 1,
315 CurrentY)->Attributes;
316
317 diff = Size.X - ScreenBuffer->ScreenBufferSize.X;
318
319 /* Zero-out the new part of the buffer */
320 for (i = 0; i < diff; i++)
321 {
322 ptr = Buffer + Offset;
323 ptr->Char.UnicodeChar = L' ';
324 ptr->Attributes = CurrentAttribute;
325 ++Offset;
326 }
327 }
328 }
329
330 if (Size.Y > ScreenBuffer->ScreenBufferSize.Y)
331 {
332 diff = Size.X * (Size.Y - ScreenBuffer->ScreenBufferSize.Y);
333
334 /* Zero-out the new part of the buffer */
335 for (i = 0; i < diff; i++)
336 {
337 ptr = Buffer + Offset;
338 ptr->Char.UnicodeChar = L' ';
339 ptr->Attributes = ScreenBuffer->ScreenDefaultAttrib;
340 ++Offset;
341 }
342 }
343
344 (void)InterlockedExchangePointer((PVOID volatile*)&ScreenBuffer->Buffer, Buffer);
345 ConsoleFreeHeap(OldBuffer);
346 ScreenBuffer->ScreenBufferSize = ScreenBuffer->OldScreenBufferSize = Size;
347 ScreenBuffer->VirtualY = 0;
348
349 /* Ensure cursor and window are within buffer */
350 if (ScreenBuffer->CursorPosition.X >= Size.X)
351 ScreenBuffer->CursorPosition.X = Size.X - 1;
352 if (ScreenBuffer->CursorPosition.Y >= Size.Y)
353 ScreenBuffer->CursorPosition.Y = Size.Y - 1;
354 if (ScreenBuffer->ViewOrigin.X > Size.X - ScreenBuffer->ViewSize.X)
355 ScreenBuffer->ViewOrigin.X = Size.X - ScreenBuffer->ViewSize.X;
356 if (ScreenBuffer->ViewOrigin.Y > Size.Y - ScreenBuffer->ViewSize.Y)
357 ScreenBuffer->ViewOrigin.Y = Size.Y - ScreenBuffer->ViewSize.Y;
358
359 /*
360 * Trigger a buffer resize event
361 */
362 if (Console->InputBuffer.Mode & ENABLE_WINDOW_INPUT)
363 {
364 ULONG NumEventsWritten;
365 INPUT_RECORD er;
366
367 er.EventType = WINDOW_BUFFER_SIZE_EVENT;
368 er.Event.WindowBufferSizeEvent.dwSize = ScreenBuffer->ScreenBufferSize;
369
370 // ConioProcessInputEvent(Console, &er);
371 ConDrvWriteConsoleInput(Console,
372 &Console->InputBuffer,
373 TRUE,
374 &er,
375 1,
376 &NumEventsWritten);
377 }
378
379 return STATUS_SUCCESS;
380 }
381
382 NTSTATUS NTAPI
383 ConDrvChangeScreenBufferAttributes(IN PCONSOLE Console,
384 IN PTEXTMODE_SCREEN_BUFFER Buffer,
385 IN USHORT NewScreenAttrib,
386 IN USHORT NewPopupAttrib)
387 {
388 ULONG X, Y, Length;
389 PCHAR_INFO Ptr;
390
391 COORD TopLeft = {0};
392 ULONG NumCodesToWrite;
393 USHORT OldScreenAttrib, OldPopupAttrib;
394
395 if (Console == NULL || Buffer == NULL)
396 {
397 return STATUS_INVALID_PARAMETER;
398 }
399
400 /* Validity check */
401 ASSERT(Console == Buffer->Header.Console);
402
403 NumCodesToWrite = Buffer->ScreenBufferSize.X * Buffer->ScreenBufferSize.Y;
404 OldScreenAttrib = Buffer->ScreenDefaultAttrib;
405 OldPopupAttrib = Buffer->PopupDefaultAttrib;
406
407 X = TopLeft.X;
408 Y = (TopLeft.Y + Buffer->VirtualY) % Buffer->ScreenBufferSize.Y;
409 Length = NumCodesToWrite;
410
411 // Ptr = ConioCoordToPointer(Buffer, X, Y); // Doesn't work
412 // Ptr = &Buffer->Buffer[X + Y * Buffer->ScreenBufferSize.X]; // May work
413
414 while (Length--)
415 {
416 // Ptr = ConioCoordToPointer(Buffer, X, Y); // Doesn't work either
417 Ptr = &Buffer->Buffer[X + Y * Buffer->ScreenBufferSize.X];
418
419 /*
420 * Change the current colors only if they are the old ones.
421 */
422
423 /* Foreground color */
424 if ((Ptr->Attributes & 0x0F) == (OldScreenAttrib & 0x0F))
425 Ptr->Attributes = (Ptr->Attributes & 0xFFF0) | (NewScreenAttrib & 0x0F);
426 if ((Ptr->Attributes & 0x0F) == (OldPopupAttrib & 0x0F))
427 Ptr->Attributes = (Ptr->Attributes & 0xFFF0) | (NewPopupAttrib & 0x0F);
428
429 /* Background color */
430 if ((Ptr->Attributes & 0xF0) == (OldScreenAttrib & 0xF0))
431 Ptr->Attributes = (Ptr->Attributes & 0xFF0F) | (NewScreenAttrib & 0xF0);
432 if ((Ptr->Attributes & 0xF0) == (OldPopupAttrib & 0xF0))
433 Ptr->Attributes = (Ptr->Attributes & 0xFF0F) | (NewPopupAttrib & 0xF0);
434
435 // ++Ptr;
436
437 if (++X == Buffer->ScreenBufferSize.X)
438 {
439 X = 0;
440
441 if (++Y == Buffer->ScreenBufferSize.Y)
442 {
443 Y = 0;
444 }
445 }
446 }
447
448 /* Save foreground and background colors for both screen and popup */
449 Buffer->ScreenDefaultAttrib = (NewScreenAttrib & 0x00FF);
450 Buffer->PopupDefaultAttrib = (NewPopupAttrib & 0x00FF);
451
452 /* Refresh the display if needed */
453 if ((PCONSOLE_SCREEN_BUFFER)Buffer == Console->ActiveBuffer)
454 {
455 SMALL_RECT UpdateRect;
456 ConioComputeUpdateRect(Buffer, &UpdateRect, &TopLeft, NumCodesToWrite);
457 TermDrawRegion(Console, &UpdateRect);
458 }
459
460 return STATUS_SUCCESS;
461 }
462
463
464 /* PUBLIC DRIVER APIS *********************************************************/
465
466 NTSTATUS NTAPI
467 ConDrvReadConsoleOutput(IN PCONSOLE Console,
468 IN PTEXTMODE_SCREEN_BUFFER Buffer,
469 IN BOOLEAN Unicode,
470 OUT PCHAR_INFO CharInfo/*Buffer*/,
471 IN OUT PSMALL_RECT ReadRegion)
472 {
473 SHORT X, Y;
474 SMALL_RECT ScreenBuffer;
475 PCHAR_INFO CurCharInfo;
476 SMALL_RECT CapturedReadRegion;
477 PCHAR_INFO Ptr;
478
479 if (Console == NULL || Buffer == NULL || CharInfo == NULL || ReadRegion == NULL)
480 {
481 return STATUS_INVALID_PARAMETER;
482 }
483
484 /* Validity check */
485 ASSERT(Console == Buffer->Header.Console);
486
487 CapturedReadRegion = *ReadRegion;
488
489 /* Make sure ReadRegion is inside the screen buffer */
490 ConioInitRect(&ScreenBuffer, 0, 0,
491 Buffer->ScreenBufferSize.Y - 1,
492 Buffer->ScreenBufferSize.X - 1);
493 if (!ConioGetIntersection(&CapturedReadRegion, &ScreenBuffer, &CapturedReadRegion))
494 {
495 /*
496 * It is okay to have a ReadRegion completely outside
497 * the screen buffer. No data is read then.
498 */
499 return STATUS_SUCCESS;
500 }
501
502 CurCharInfo = CharInfo;
503
504 for (Y = CapturedReadRegion.Top; Y <= CapturedReadRegion.Bottom; ++Y)
505 {
506 Ptr = ConioCoordToPointer(Buffer, CapturedReadRegion.Left, Y);
507 for (X = CapturedReadRegion.Left; X <= CapturedReadRegion.Right; ++X)
508 {
509 if (Unicode)
510 {
511 CurCharInfo->Char.UnicodeChar = Ptr->Char.UnicodeChar;
512 }
513 else
514 {
515 // ConsoleOutputUnicodeToAnsiChar(Console, &CurCharInfo->Char.AsciiChar, &Ptr->Char.UnicodeChar);
516 WideCharToMultiByte(Console->OutputCodePage, 0, &Ptr->Char.UnicodeChar, 1,
517 &CurCharInfo->Char.AsciiChar, 1, NULL, NULL);
518 }
519 CurCharInfo->Attributes = Ptr->Attributes;
520 ++Ptr;
521 ++CurCharInfo;
522 }
523 }
524
525 *ReadRegion = CapturedReadRegion;
526
527 return STATUS_SUCCESS;
528 }
529
530 NTSTATUS NTAPI
531 ConDrvWriteConsoleOutput(IN PCONSOLE Console,
532 IN PTEXTMODE_SCREEN_BUFFER Buffer,
533 IN BOOLEAN Unicode,
534 IN PCHAR_INFO CharInfo/*Buffer*/,
535 IN OUT PSMALL_RECT WriteRegion)
536 {
537 SHORT X, Y;
538 SMALL_RECT ScreenBuffer;
539 PCHAR_INFO CurCharInfo;
540 SMALL_RECT CapturedWriteRegion;
541 PCHAR_INFO Ptr;
542
543 if (Console == NULL || Buffer == NULL || CharInfo == NULL || WriteRegion == NULL)
544 {
545 return STATUS_INVALID_PARAMETER;
546 }
547
548 /* Validity check */
549 ASSERT(Console == Buffer->Header.Console);
550
551 CapturedWriteRegion = *WriteRegion;
552
553 /* Make sure WriteRegion is inside the screen buffer */
554 ConioInitRect(&ScreenBuffer, 0, 0,
555 Buffer->ScreenBufferSize.Y - 1,
556 Buffer->ScreenBufferSize.X - 1);
557 if (!ConioGetIntersection(&CapturedWriteRegion, &ScreenBuffer, &CapturedWriteRegion))
558 {
559 /*
560 * It is okay to have a WriteRegion completely outside
561 * the screen buffer. No data is written then.
562 */
563 return STATUS_SUCCESS;
564 }
565
566 CurCharInfo = CharInfo;
567
568 for (Y = CapturedWriteRegion.Top; Y <= CapturedWriteRegion.Bottom; ++Y)
569 {
570 Ptr = ConioCoordToPointer(Buffer, CapturedWriteRegion.Left, Y);
571 for (X = CapturedWriteRegion.Left; X <= CapturedWriteRegion.Right; ++X)
572 {
573 if (Unicode)
574 {
575 Ptr->Char.UnicodeChar = CurCharInfo->Char.UnicodeChar;
576 }
577 else
578 {
579 ConsoleOutputAnsiToUnicodeChar(Console, &Ptr->Char.UnicodeChar, &CurCharInfo->Char.AsciiChar);
580 }
581 Ptr->Attributes = CurCharInfo->Attributes;
582 ++Ptr;
583 ++CurCharInfo;
584 }
585 }
586
587 TermDrawRegion(Console, &CapturedWriteRegion);
588
589 *WriteRegion = CapturedWriteRegion;
590
591 return STATUS_SUCCESS;
592 }
593
594 /*
595 * NOTE: This function is strongly inspired by ConDrvWriteConsoleOutput...
596 * FIXME: This function MUST be moved into consrv/conoutput.c because only
597 * consrv knows how to manipulate VDM screenbuffers.
598 */
599 NTSTATUS NTAPI
600 ConDrvWriteConsoleOutputVDM(IN PCONSOLE Console,
601 IN PTEXTMODE_SCREEN_BUFFER Buffer,
602 IN PCHAR_CELL CharInfo/*Buffer*/,
603 IN COORD CharInfoSize,
604 IN PSMALL_RECT WriteRegion)
605 {
606 SHORT X, Y;
607 SMALL_RECT ScreenBuffer;
608 PCHAR_CELL CurCharInfo;
609 SMALL_RECT CapturedWriteRegion;
610 PCHAR_INFO Ptr;
611
612 if (Console == NULL || Buffer == NULL || CharInfo == NULL || WriteRegion == NULL)
613 {
614 return STATUS_INVALID_PARAMETER;
615 }
616
617 /* Validity check */
618 ASSERT(Console == Buffer->Header.Console);
619
620 CapturedWriteRegion = *WriteRegion;
621
622 /* Make sure WriteRegion is inside the screen buffer */
623 ConioInitRect(&ScreenBuffer, 0, 0,
624 Buffer->ScreenBufferSize.Y - 1,
625 Buffer->ScreenBufferSize.X - 1);
626 if (!ConioGetIntersection(&CapturedWriteRegion, &ScreenBuffer, &CapturedWriteRegion))
627 {
628 /*
629 * It is okay to have a WriteRegion completely outside
630 * the screen buffer. No data is written then.
631 */
632 return STATUS_SUCCESS;
633 }
634
635 // CurCharInfo = CharInfo;
636
637 for (Y = CapturedWriteRegion.Top; Y <= CapturedWriteRegion.Bottom; ++Y)
638 {
639 /**/CurCharInfo = CharInfo + Y * CharInfoSize.X + CapturedWriteRegion.Left;/**/
640
641 Ptr = ConioCoordToPointer(Buffer, CapturedWriteRegion.Left, Y);
642 for (X = CapturedWriteRegion.Left; X <= CapturedWriteRegion.Right; ++X)
643 {
644 ConsoleOutputAnsiToUnicodeChar(Console, &Ptr->Char.UnicodeChar, &CurCharInfo->Char);
645 Ptr->Attributes = CurCharInfo->Attributes;
646 ++Ptr;
647 ++CurCharInfo;
648 }
649 }
650
651 return STATUS_SUCCESS;
652 }
653
654 NTSTATUS NTAPI
655 ConDrvWriteConsole(IN PCONSOLE Console,
656 IN PTEXTMODE_SCREEN_BUFFER ScreenBuffer,
657 IN BOOLEAN Unicode,
658 IN PVOID StringBuffer,
659 IN ULONG NumCharsToWrite,
660 OUT PULONG NumCharsWritten OPTIONAL)
661 {
662 NTSTATUS Status = STATUS_SUCCESS;
663 PWCHAR Buffer = NULL;
664 ULONG Written = 0;
665 ULONG Length;
666
667 if (Console == NULL || ScreenBuffer == NULL /* || StringBuffer == NULL */)
668 return STATUS_INVALID_PARAMETER;
669
670 /* Validity checks */
671 ASSERT(Console == ScreenBuffer->Header.Console);
672 ASSERT((StringBuffer != NULL) || (StringBuffer == NULL && NumCharsToWrite == 0));
673
674 /* Stop here if the console is paused */
675 if (Console->UnpauseEvent != NULL) return STATUS_PENDING;
676
677 /* Convert the string to UNICODE */
678 if (Unicode)
679 {
680 Buffer = StringBuffer;
681 }
682 else
683 {
684 Length = MultiByteToWideChar(Console->OutputCodePage, 0,
685 (PCHAR)StringBuffer,
686 NumCharsToWrite,
687 NULL, 0);
688 Buffer = RtlAllocateHeap(RtlGetProcessHeap(), 0, Length * sizeof(WCHAR));
689 if (Buffer)
690 {
691 MultiByteToWideChar(Console->OutputCodePage, 0,
692 (PCHAR)StringBuffer,
693 NumCharsToWrite,
694 (PWCHAR)Buffer, Length);
695 }
696 else
697 {
698 Status = STATUS_NO_MEMORY;
699 }
700 }
701
702 /* Send it */
703 if (Buffer)
704 {
705 if (NT_SUCCESS(Status))
706 {
707 Status = TermWriteStream(Console,
708 ScreenBuffer,
709 Buffer,
710 NumCharsToWrite,
711 TRUE);
712 if (NT_SUCCESS(Status))
713 {
714 Written = NumCharsToWrite;
715 }
716 }
717
718 if (!Unicode) RtlFreeHeap(RtlGetProcessHeap(), 0, Buffer);
719 }
720
721 if (NumCharsWritten) *NumCharsWritten = Written;
722
723 return Status;
724 }
725
726 NTSTATUS NTAPI
727 ConDrvReadConsoleOutputString(IN PCONSOLE Console,
728 IN PTEXTMODE_SCREEN_BUFFER Buffer,
729 IN CODE_TYPE CodeType,
730 OUT PVOID StringBuffer,
731 IN ULONG NumCodesToRead,
732 IN PCOORD ReadCoord,
733 // OUT PCOORD EndCoord,
734 OUT PULONG NumCodesRead OPTIONAL)
735 {
736 SHORT Xpos, Ypos;
737 PVOID ReadBuffer;
738 ULONG i;
739 ULONG CodeSize;
740 PCHAR_INFO Ptr;
741
742 if (Console == NULL || Buffer == NULL || ReadCoord == NULL /* || EndCoord == NULL */)
743 {
744 return STATUS_INVALID_PARAMETER;
745 }
746
747 /* Validity checks */
748 ASSERT(Console == Buffer->Header.Console);
749 ASSERT((StringBuffer != NULL) || (StringBuffer == NULL && NumCodesToRead == 0));
750
751 //
752 // FIXME: Make overflow checks on ReadCoord !!!!!!
753 //
754
755 if (NumCodesRead) *NumCodesRead = 0;
756
757 switch (CodeType)
758 {
759 case CODE_ASCII:
760 CodeSize = RTL_FIELD_SIZE(CODE_ELEMENT, AsciiChar);
761 break;
762
763 case CODE_UNICODE:
764 CodeSize = RTL_FIELD_SIZE(CODE_ELEMENT, UnicodeChar);
765 break;
766
767 case CODE_ATTRIBUTE:
768 CodeSize = RTL_FIELD_SIZE(CODE_ELEMENT, Attribute);
769 break;
770
771 default:
772 return STATUS_INVALID_PARAMETER;
773 }
774
775 ReadBuffer = StringBuffer;
776 Xpos = ReadCoord->X;
777 Ypos = (ReadCoord->Y + Buffer->VirtualY) % Buffer->ScreenBufferSize.Y;
778
779 /*
780 * MSDN (ReadConsoleOutputAttribute and ReadConsoleOutputCharacter) :
781 *
782 * If the number of attributes (resp. characters) to be read from extends
783 * beyond the end of the specified screen buffer row, attributes (resp.
784 * characters) are read from the next row. If the number of attributes
785 * (resp. characters) to be read from extends beyond the end of the console
786 * screen buffer, attributes (resp. characters) up to the end of the console
787 * screen buffer are read.
788 *
789 * TODO: Do NOT loop up to NumCodesToRead, but stop before
790 * if we are going to overflow...
791 */
792 // Ptr = ConioCoordToPointer(Buffer, Xpos, Ypos); // Doesn't work
793 for (i = 0; i < min(NumCodesToRead, (ULONG)Buffer->ScreenBufferSize.X * Buffer->ScreenBufferSize.Y); ++i)
794 {
795 // Ptr = ConioCoordToPointer(Buffer, Xpos, Ypos); // Doesn't work either
796 Ptr = &Buffer->Buffer[Xpos + Ypos * Buffer->ScreenBufferSize.X];
797
798 switch (CodeType)
799 {
800 case CODE_ASCII:
801 ConsoleOutputUnicodeToAnsiChar(Console, (PCHAR)ReadBuffer, &Ptr->Char.UnicodeChar);
802 break;
803
804 case CODE_UNICODE:
805 *(PWCHAR)ReadBuffer = Ptr->Char.UnicodeChar;
806 break;
807
808 case CODE_ATTRIBUTE:
809 *(PWORD)ReadBuffer = Ptr->Attributes;
810 break;
811 }
812 ReadBuffer = (PVOID)((ULONG_PTR)ReadBuffer + CodeSize);
813 // ++Ptr;
814
815 Xpos++;
816
817 if (Xpos == Buffer->ScreenBufferSize.X)
818 {
819 Xpos = 0;
820 Ypos++;
821
822 if (Ypos == Buffer->ScreenBufferSize.Y)
823 {
824 Ypos = 0;
825 }
826 }
827 }
828
829 // EndCoord->X = Xpos;
830 // EndCoord->Y = (Ypos - Buffer->VirtualY + Buffer->ScreenBufferSize.Y) % Buffer->ScreenBufferSize.Y;
831
832 if (NumCodesRead)
833 *NumCodesRead = (ULONG)((ULONG_PTR)ReadBuffer - (ULONG_PTR)StringBuffer) / CodeSize;
834 // <= NumCodesToRead
835
836 return STATUS_SUCCESS;
837 }
838
839 NTSTATUS NTAPI
840 ConDrvWriteConsoleOutputString(IN PCONSOLE Console,
841 IN PTEXTMODE_SCREEN_BUFFER Buffer,
842 IN CODE_TYPE CodeType,
843 IN PVOID StringBuffer,
844 IN ULONG NumCodesToWrite,
845 IN PCOORD WriteCoord,
846 // OUT PCOORD EndCoord,
847 OUT PULONG NumCodesWritten OPTIONAL)
848 {
849 NTSTATUS Status = STATUS_SUCCESS;
850 PVOID WriteBuffer = NULL;
851 PWCHAR tmpString = NULL;
852 ULONG X, Y, Length; // , Written = 0;
853 ULONG CodeSize;
854 PCHAR_INFO Ptr;
855
856 if (Console == NULL || Buffer == NULL || WriteCoord == NULL /* || EndCoord == NULL */)
857 {
858 return STATUS_INVALID_PARAMETER;
859 }
860
861 /* Validity checks */
862 ASSERT(Console == Buffer->Header.Console);
863 ASSERT((StringBuffer != NULL) || (StringBuffer == NULL && NumCodesToWrite == 0));
864
865 //
866 // FIXME: Make overflow checks on WriteCoord !!!!!!
867 //
868
869 if (NumCodesWritten) *NumCodesWritten = 0;
870
871 switch (CodeType)
872 {
873 case CODE_ASCII:
874 CodeSize = RTL_FIELD_SIZE(CODE_ELEMENT, AsciiChar);
875 break;
876
877 case CODE_UNICODE:
878 CodeSize = RTL_FIELD_SIZE(CODE_ELEMENT, UnicodeChar);
879 break;
880
881 case CODE_ATTRIBUTE:
882 CodeSize = RTL_FIELD_SIZE(CODE_ELEMENT, Attribute);
883 break;
884
885 default:
886 return STATUS_INVALID_PARAMETER;
887 }
888
889 if (CodeType == CODE_ASCII)
890 {
891 /* Convert the ASCII string into Unicode before writing it to the console */
892 Length = MultiByteToWideChar(Console->OutputCodePage, 0,
893 (PCHAR)StringBuffer,
894 NumCodesToWrite,
895 NULL, 0);
896 tmpString = WriteBuffer = RtlAllocateHeap(RtlGetProcessHeap(), 0, Length * sizeof(WCHAR));
897 if (WriteBuffer)
898 {
899 MultiByteToWideChar(Console->OutputCodePage, 0,
900 (PCHAR)StringBuffer,
901 NumCodesToWrite,
902 (PWCHAR)WriteBuffer, Length);
903 }
904 else
905 {
906 Status = STATUS_NO_MEMORY;
907 }
908
909 // FIXME: Quick fix: fix the CodeType and CodeSize since the
910 // ASCII string was converted into UNICODE.
911 // A proper fix needs to be written.
912 CodeType = CODE_UNICODE;
913 CodeSize = RTL_FIELD_SIZE(CODE_ELEMENT, UnicodeChar);
914 }
915 else
916 {
917 /* For CODE_UNICODE or CODE_ATTRIBUTE, we are already OK */
918 WriteBuffer = StringBuffer;
919 }
920
921 if (WriteBuffer == NULL || !NT_SUCCESS(Status)) goto Cleanup;
922
923 X = WriteCoord->X;
924 Y = (WriteCoord->Y + Buffer->VirtualY) % Buffer->ScreenBufferSize.Y;
925 Length = NumCodesToWrite;
926 // Ptr = ConioCoordToPointer(Buffer, X, Y); // Doesn't work
927 // Ptr = &Buffer->Buffer[X + Y * Buffer->ScreenBufferSize.X]; // May work
928
929 while (Length--)
930 {
931 // Ptr = ConioCoordToPointer(Buffer, X, Y); // Doesn't work either
932 Ptr = &Buffer->Buffer[X + Y * Buffer->ScreenBufferSize.X];
933
934 switch (CodeType)
935 {
936 case CODE_ASCII:
937 case CODE_UNICODE:
938 Ptr->Char.UnicodeChar = *(PWCHAR)WriteBuffer;
939 break;
940
941 case CODE_ATTRIBUTE:
942 Ptr->Attributes = *(PWORD)WriteBuffer;
943 break;
944 }
945 WriteBuffer = (PVOID)((ULONG_PTR)WriteBuffer + CodeSize);
946 // ++Ptr;
947
948 // Written++;
949 if (++X == Buffer->ScreenBufferSize.X)
950 {
951 X = 0;
952
953 if (++Y == Buffer->ScreenBufferSize.Y)
954 {
955 Y = 0;
956 }
957 }
958 }
959
960 if ((PCONSOLE_SCREEN_BUFFER)Buffer == Console->ActiveBuffer)
961 {
962 SMALL_RECT UpdateRect;
963 ConioComputeUpdateRect(Buffer, &UpdateRect, WriteCoord, NumCodesToWrite);
964 TermDrawRegion(Console, &UpdateRect);
965 }
966
967 // EndCoord->X = X;
968 // EndCoord->Y = (Y + Buffer->ScreenBufferSize.Y - Buffer->VirtualY) % Buffer->ScreenBufferSize.Y;
969
970 Cleanup:
971 if (tmpString) RtlFreeHeap(RtlGetProcessHeap(), 0, tmpString);
972
973 if (NumCodesWritten) *NumCodesWritten = NumCodesToWrite; // Written;
974 return Status;
975 }
976
977 NTSTATUS NTAPI
978 ConDrvFillConsoleOutput(IN PCONSOLE Console,
979 IN PTEXTMODE_SCREEN_BUFFER Buffer,
980 IN CODE_TYPE CodeType,
981 IN CODE_ELEMENT Code,
982 IN ULONG NumCodesToWrite,
983 IN PCOORD WriteCoord,
984 OUT PULONG NumCodesWritten OPTIONAL)
985 {
986 ULONG X, Y, Length; // , Written = 0;
987 PCHAR_INFO Ptr;
988
989 if (Console == NULL || Buffer == NULL || WriteCoord == NULL)
990 {
991 return STATUS_INVALID_PARAMETER;
992 }
993
994 /* Validity check */
995 ASSERT(Console == Buffer->Header.Console);
996
997 //
998 // FIXME: Make overflow checks on WriteCoord !!!!!!
999 //
1000
1001 if (NumCodesWritten) *NumCodesWritten = 0;
1002
1003 if (CodeType == CODE_ASCII)
1004 {
1005 /* Conversion from the ASCII char to the UNICODE char */
1006 CODE_ELEMENT tmp;
1007 ConsoleOutputAnsiToUnicodeChar(Console, &tmp.UnicodeChar, &Code.AsciiChar);
1008 Code = tmp;
1009 }
1010
1011 X = WriteCoord->X;
1012 Y = (WriteCoord->Y + Buffer->VirtualY) % Buffer->ScreenBufferSize.Y;
1013 Length = NumCodesToWrite;
1014 // Ptr = ConioCoordToPointer(Buffer, X, Y); // Doesn't work
1015 // Ptr = &Buffer->Buffer[X + Y * Buffer->ScreenBufferSize.X]; // May work
1016
1017 while (Length--)
1018 {
1019 // Ptr = ConioCoordToPointer(Buffer, X, Y); // Doesn't work either
1020 Ptr = &Buffer->Buffer[X + Y * Buffer->ScreenBufferSize.X];
1021
1022 switch (CodeType)
1023 {
1024 case CODE_ASCII:
1025 case CODE_UNICODE:
1026 Ptr->Char.UnicodeChar = Code.UnicodeChar;
1027 break;
1028
1029 case CODE_ATTRIBUTE:
1030 Ptr->Attributes = Code.Attribute;
1031 break;
1032 }
1033 // ++Ptr;
1034
1035 // Written++;
1036 if (++X == Buffer->ScreenBufferSize.X)
1037 {
1038 X = 0;
1039
1040 if (++Y == Buffer->ScreenBufferSize.Y)
1041 {
1042 Y = 0;
1043 }
1044 }
1045 }
1046
1047 if ((PCONSOLE_SCREEN_BUFFER)Buffer == Console->ActiveBuffer)
1048 {
1049 SMALL_RECT UpdateRect;
1050 ConioComputeUpdateRect(Buffer, &UpdateRect, WriteCoord, NumCodesToWrite);
1051 TermDrawRegion(Console, &UpdateRect);
1052 }
1053
1054 if (NumCodesWritten) *NumCodesWritten = NumCodesToWrite; // Written;
1055 return STATUS_SUCCESS;
1056 }
1057
1058 NTSTATUS NTAPI
1059 ConDrvGetConsoleScreenBufferInfo(IN PCONSOLE Console,
1060 IN PTEXTMODE_SCREEN_BUFFER Buffer,
1061 OUT PCOORD ScreenBufferSize,
1062 OUT PCOORD CursorPosition,
1063 OUT PCOORD ViewOrigin,
1064 OUT PCOORD ViewSize,
1065 OUT PCOORD MaximumViewSize,
1066 OUT PWORD Attributes)
1067 {
1068 if (Console == NULL || Buffer == NULL || ScreenBufferSize == NULL ||
1069 CursorPosition == NULL || ViewOrigin == NULL || ViewSize == NULL ||
1070 MaximumViewSize == NULL || Attributes == NULL)
1071 {
1072 return STATUS_INVALID_PARAMETER;
1073 }
1074
1075 /* Validity check */
1076 ASSERT(Console == Buffer->Header.Console);
1077
1078 *ScreenBufferSize = Buffer->ScreenBufferSize;
1079 *CursorPosition = Buffer->CursorPosition;
1080 *ViewOrigin = Buffer->ViewOrigin;
1081 *ViewSize = Buffer->ViewSize;
1082 *Attributes = Buffer->ScreenDefaultAttrib;
1083
1084 // FIXME: Refine the computation
1085 *MaximumViewSize = Buffer->ScreenBufferSize;
1086
1087 return STATUS_SUCCESS;
1088 }
1089
1090 NTSTATUS NTAPI
1091 ConDrvSetConsoleTextAttribute(IN PCONSOLE Console,
1092 IN PTEXTMODE_SCREEN_BUFFER Buffer,
1093 IN WORD Attributes)
1094 {
1095 if (Console == NULL || Buffer == NULL)
1096 return STATUS_INVALID_PARAMETER;
1097
1098 /* Validity check */
1099 ASSERT(Console == Buffer->Header.Console);
1100
1101 Buffer->ScreenDefaultAttrib = Attributes;
1102 return STATUS_SUCCESS;
1103 }
1104
1105 NTSTATUS NTAPI
1106 ConDrvSetConsoleScreenBufferSize(IN PCONSOLE Console,
1107 IN PTEXTMODE_SCREEN_BUFFER Buffer,
1108 IN PCOORD Size)
1109 {
1110 NTSTATUS Status;
1111
1112 if (Console == NULL || Buffer == NULL || Size == NULL)
1113 return STATUS_INVALID_PARAMETER;
1114
1115 /* Validity check */
1116 ASSERT(Console == Buffer->Header.Console);
1117
1118 Status = ConioResizeBuffer(Console, Buffer, *Size);
1119 if (NT_SUCCESS(Status)) TermResizeTerminal(Console);
1120
1121 return Status;
1122 }
1123
1124 NTSTATUS NTAPI
1125 ConDrvScrollConsoleScreenBuffer(IN PCONSOLE Console,
1126 IN PTEXTMODE_SCREEN_BUFFER Buffer,
1127 IN BOOLEAN Unicode,
1128 IN PSMALL_RECT ScrollRectangle,
1129 IN BOOLEAN UseClipRectangle,
1130 IN PSMALL_RECT ClipRectangle OPTIONAL,
1131 IN PCOORD DestinationOrigin,
1132 IN CHAR_INFO FillChar)
1133 {
1134 COORD CapturedDestinationOrigin;
1135 SMALL_RECT ScreenBuffer;
1136 SMALL_RECT SrcRegion;
1137 SMALL_RECT DstRegion;
1138 SMALL_RECT UpdateRegion;
1139 SMALL_RECT CapturedClipRectangle;
1140
1141 if (Console == NULL || Buffer == NULL || ScrollRectangle == NULL ||
1142 (UseClipRectangle ? ClipRectangle == NULL : FALSE) || DestinationOrigin == NULL)
1143 {
1144 return STATUS_INVALID_PARAMETER;
1145 }
1146
1147 /* Validity check */
1148 ASSERT(Console == Buffer->Header.Console);
1149
1150 CapturedDestinationOrigin = *DestinationOrigin;
1151
1152 /* Make sure the source rectangle is inside the screen buffer */
1153 ConioInitRect(&ScreenBuffer, 0, 0,
1154 Buffer->ScreenBufferSize.Y - 1,
1155 Buffer->ScreenBufferSize.X - 1);
1156 if (!ConioGetIntersection(&SrcRegion, &ScreenBuffer, ScrollRectangle))
1157 {
1158 return STATUS_SUCCESS;
1159 }
1160
1161 /* If the source was clipped on the left or top, adjust the destination accordingly */
1162 if (ScrollRectangle->Left < 0)
1163 {
1164 CapturedDestinationOrigin.X -= ScrollRectangle->Left;
1165 }
1166 if (ScrollRectangle->Top < 0)
1167 {
1168 CapturedDestinationOrigin.Y -= ScrollRectangle->Top;
1169 }
1170
1171 if (UseClipRectangle)
1172 {
1173 CapturedClipRectangle = *ClipRectangle;
1174 if (!ConioGetIntersection(&CapturedClipRectangle, &CapturedClipRectangle, &ScreenBuffer))
1175 {
1176 return STATUS_SUCCESS;
1177 }
1178 }
1179 else
1180 {
1181 CapturedClipRectangle = ScreenBuffer;
1182 }
1183
1184 ConioInitRect(&DstRegion,
1185 CapturedDestinationOrigin.Y,
1186 CapturedDestinationOrigin.X,
1187 CapturedDestinationOrigin.Y + ConioRectHeight(&SrcRegion) - 1,
1188 CapturedDestinationOrigin.X + ConioRectWidth(&SrcRegion ) - 1);
1189
1190 if (!Unicode)
1191 {
1192 WCHAR tmp;
1193 ConsoleOutputAnsiToUnicodeChar(Console, &tmp, &FillChar.Char.AsciiChar);
1194 FillChar.Char.UnicodeChar = tmp;
1195 }
1196
1197 ConioMoveRegion(Buffer, &SrcRegion, &DstRegion, &CapturedClipRectangle, FillChar);
1198
1199 if ((PCONSOLE_SCREEN_BUFFER)Buffer == Console->ActiveBuffer)
1200 {
1201 ConioGetUnion(&UpdateRegion, &SrcRegion, &DstRegion);
1202 if (ConioGetIntersection(&UpdateRegion, &UpdateRegion, &CapturedClipRectangle))
1203 {
1204 /* Draw update region */
1205 TermDrawRegion(Console, &UpdateRegion);
1206 }
1207 }
1208
1209 return STATUS_SUCCESS;
1210 }
1211
1212 NTSTATUS NTAPI
1213 ConDrvSetConsoleWindowInfo(IN PCONSOLE Console,
1214 IN PTEXTMODE_SCREEN_BUFFER Buffer,
1215 IN BOOLEAN Absolute,
1216 IN PSMALL_RECT WindowRect)
1217 {
1218 SMALL_RECT CapturedWindowRect;
1219
1220 if (Console == NULL || Buffer == NULL || WindowRect == NULL)
1221 return STATUS_INVALID_PARAMETER;
1222
1223 /* Validity check */
1224 ASSERT(Console == Buffer->Header.Console);
1225
1226 CapturedWindowRect = *WindowRect;
1227
1228 if (!Absolute)
1229 {
1230 /* Relative positions given. Transform them to absolute ones */
1231 CapturedWindowRect.Left += Buffer->ViewOrigin.X;
1232 CapturedWindowRect.Top += Buffer->ViewOrigin.Y;
1233 CapturedWindowRect.Right += Buffer->ViewOrigin.X + Buffer->ViewSize.X - 1;
1234 CapturedWindowRect.Bottom += Buffer->ViewOrigin.Y + Buffer->ViewSize.Y - 1;
1235 }
1236
1237 /*
1238 * The MSDN documentation on SetConsoleWindowInfo is partially wrong about
1239 * the performed checks this API performs. While it is correct that the
1240 * 'Right'/'Bottom' members cannot be strictly smaller than the 'Left'/'Top'
1241 * members, they can be equal.
1242 * Also, if the 'Left' or 'Top' members are negative, this is automatically
1243 * corrected for, and the window rectangle coordinates are shifted accordingly.
1244 */
1245 if ((CapturedWindowRect.Right < CapturedWindowRect.Left) ||
1246 (CapturedWindowRect.Bottom < CapturedWindowRect.Top))
1247 {
1248 return STATUS_INVALID_PARAMETER;
1249 }
1250
1251 /* Shift the window rectangle coordinates if 'Left' or 'Top' are negative */
1252 if (CapturedWindowRect.Left < 0)
1253 {
1254 CapturedWindowRect.Right -= CapturedWindowRect.Left;
1255 CapturedWindowRect.Left = 0;
1256 }
1257 if (CapturedWindowRect.Top < 0)
1258 {
1259 CapturedWindowRect.Bottom -= CapturedWindowRect.Top;
1260 CapturedWindowRect.Top = 0;
1261 }
1262
1263 if ((CapturedWindowRect.Right >= Buffer->ScreenBufferSize.X) ||
1264 (CapturedWindowRect.Bottom >= Buffer->ScreenBufferSize.Y))
1265 {
1266 return STATUS_INVALID_PARAMETER;
1267 }
1268
1269 Buffer->ViewOrigin.X = CapturedWindowRect.Left;
1270 Buffer->ViewOrigin.Y = CapturedWindowRect.Top;
1271
1272 Buffer->ViewSize.X = CapturedWindowRect.Right - CapturedWindowRect.Left + 1;
1273 Buffer->ViewSize.Y = CapturedWindowRect.Bottom - CapturedWindowRect.Top + 1;
1274
1275 TermResizeTerminal(Console);
1276
1277 return STATUS_SUCCESS;
1278 }
1279
1280 /* EOF */