update for HEAD-2003091401
[reactos.git] / subsys / csrss / api / conio.c
1 /* $Id$
2  *
3  * reactos/subsys/csrss/api/conio.c
4  *
5  * Console I/O functions
6  *
7  * ReactOS Operating System
8  */
9
10 /* INCLUDES ******************************************************************/
11
12 #include <ddk/ntddk.h>
13
14 #include <csrss/csrss.h>
15 #include "api.h"
16 #include <ntdll/rtl.h>
17 #include <ddk/ntddblue.h>
18
19 #define NDEBUG
20 #include <debug.h>
21
22 #define LOCK   RtlEnterCriticalSection(&ActiveConsoleLock)
23 #define UNLOCK RtlLeaveCriticalSection(&ActiveConsoleLock)
24
25 /* FIXME: Is there a way to create real aliasses with gcc? [CSH] */
26 #define ALIAS(Name, Target) typeof(Target) Name = Target
27 extern VOID CsrConsoleCtrlEvent(DWORD Event, PCSRSS_PROCESS_DATA ProcessData);
28
29 /* GLOBALS *******************************************************************/
30
31 static HANDLE ConsoleDeviceHandle;
32 static HANDLE KeyboardDeviceHandle;
33 static PCSRSS_CONSOLE ActiveConsole;
34 CRITICAL_SECTION ActiveConsoleLock;
35 static COORD PhysicalConsoleSize;
36 static BOOL KeyReadInhibit = FALSE;
37
38 /* FUNCTIONS *****************************************************************/
39
40 VOID CsrConsoleCtrlEvent(DWORD Event, PCSRSS_PROCESS_DATA ProcessData)
41 {
42     HANDLE Process, hThread;
43     NTSTATUS Status;
44     CLIENT_ID ClientId, ClientId1;
45         
46     DPRINT1("CsrConsoleCtrlEvent Parent ProcessId = %x\n",      ClientId.UniqueProcess);
47
48     if (ProcessData->CtrlDispatcher)
49     {
50         ClientId.UniqueProcess = (HANDLE) ProcessData->ProcessId;
51         Status = NtOpenProcess( &Process, PROCESS_DUP_HANDLE, 0, &ClientId );
52         if( !NT_SUCCESS( Status ) )
53         {
54             DPRINT("CsrConsoleCtrlEvent: Failed for handle duplication\n");
55             return;
56         }
57
58         DPRINT1("CsrConsoleCtrlEvent Process Handle = %x\n", Process);
59
60
61         Status = RtlCreateUserThread(Process, NULL, FALSE, 0, NULL, NULL,
62                                     (PTHREAD_START_ROUTINE)ProcessData->CtrlDispatcher,
63                                     (PVOID) Event, &hThread, &ClientId1);
64         if( !NT_SUCCESS( Status ) )
65         {
66             DPRINT("CsrConsoleCtrlEvent: Failed Thread creation\n");
67             NtClose(Process);
68             return;
69         }
70         DPRINT1("CsrConsoleCtrlEvent Parent ProcessId = %x, ReturnPId = %x, hT = %x\n",
71                 ClientId.UniqueProcess, ClientId1.UniqueProcess, hThread);
72         NtClose(hThread);
73         NtClose(Process);
74     }
75 }
76
77
78 CSR_API(CsrAllocConsole)
79 {
80    PCSRSS_CONSOLE Console;
81    HANDLE Process;
82    NTSTATUS Status;
83    CLIENT_ID ClientId;
84
85    Reply->Header.MessageSize = sizeof(CSRSS_API_REPLY);
86    Reply->Header.DataSize = sizeof(CSRSS_API_REPLY) -
87      sizeof(LPC_MESSAGE);
88
89    if (ProcessData == NULL)
90    {
91      return(Reply->Status = STATUS_INVALID_PARAMETER);
92    }
93
94    if( ProcessData->Console )
95       {
96          Reply->Status = STATUS_INVALID_PARAMETER;
97          return STATUS_INVALID_PARAMETER;
98       }
99    Reply->Status = STATUS_SUCCESS;
100    Console = RtlAllocateHeap( CsrssApiHeap, 0, sizeof( CSRSS_CONSOLE ) );
101    if( Console == 0 )
102       {
103         Reply->Status = STATUS_INSUFFICIENT_RESOURCES;
104         return STATUS_INSUFFICIENT_RESOURCES;
105       }
106    Reply->Status = CsrInitConsole( Console );
107    if( !NT_SUCCESS( Reply->Status ) )
108      {
109        RtlFreeHeap( CsrssApiHeap, 0, Console );
110        return Reply->Status;
111      }
112    ProcessData->Console = Console;
113    /* add a reference count because the process is tied to the console */
114    Console->Header.ReferenceCount++;
115    Status = CsrInsertObject( ProcessData, &Reply->Data.AllocConsoleReply.InputHandle, &Console->Header );
116    if( !NT_SUCCESS( Status ) )
117       {
118          CsrDeleteConsole( Console );
119          ProcessData->Console = 0;
120          return Reply->Status = Status;
121       }
122    Status = CsrInsertObject( ProcessData, &Reply->Data.AllocConsoleReply.OutputHandle, &Console->ActiveBuffer->Header );
123    if( !NT_SUCCESS( Status ) )
124       {
125          Console->Header.ReferenceCount--;
126          CsrReleaseObject( ProcessData, Reply->Data.AllocConsoleReply.InputHandle );
127          ProcessData->Console = 0;
128          return Reply->Status = Status;
129       }
130
131    ClientId.UniqueProcess = (HANDLE)ProcessData->ProcessId;
132    Status = NtOpenProcess( &Process, PROCESS_DUP_HANDLE, 0, &ClientId );
133    if( !NT_SUCCESS( Status ) )
134      {
135        DbgPrint( "CSR: NtOpenProcess() failed for handle duplication\n" );
136        Console->Header.ReferenceCount--;
137        ProcessData->Console = 0;
138        CsrReleaseObject( ProcessData, Reply->Data.AllocConsoleReply.OutputHandle );
139        CsrReleaseObject( ProcessData, Reply->Data.AllocConsoleReply.InputHandle );
140        Reply->Status = Status;
141        return Status;
142      }
143    Status = NtDuplicateObject( NtCurrentProcess(), ProcessData->Console->ActiveEvent, Process, &ProcessData->ConsoleEvent, SYNCHRONIZE, FALSE, 0 );
144    if( !NT_SUCCESS( Status ) )
145      {
146        DbgPrint( "CSR: NtDuplicateObject() failed: %x\n", Status );
147        NtClose( Process );
148        Console->Header.ReferenceCount--;
149        CsrReleaseObject( ProcessData, Reply->Data.AllocConsoleReply.OutputHandle );
150        CsrReleaseObject( ProcessData, Reply->Data.AllocConsoleReply.InputHandle );
151        ProcessData->Console = 0;
152        Reply->Status = Status;
153        return Status;
154      }
155    NtClose( Process );
156    LOCK;
157    ProcessData->CtrlDispatcher = Request->Data.AllocConsoleRequest.CtrlDispatcher;
158    DPRINT("CSRSS:CtrlDispatcher address: %x\n", ProcessData->CtrlDispatcher);      
159    InsertHeadList(&ProcessData->Console->ProcessList, &ProcessData->ProcessEntry);
160    UNLOCK;
161
162    return STATUS_SUCCESS;
163 }
164
165 CSR_API(CsrFreeConsole)
166 {
167    Reply->Header.MessageSize = sizeof(CSRSS_API_REPLY);
168    Reply->Header.DataSize = sizeof(CSRSS_API_REPLY) -
169      sizeof(LPC_MESSAGE);
170
171    if (ProcessData == NULL)
172    {
173      return(Reply->Status = STATUS_INVALID_PARAMETER);
174    }
175
176    Reply->Status = STATUS_NOT_IMPLEMENTED;
177    
178    return(STATUS_NOT_IMPLEMENTED);
179 }
180
181 CSR_API(CsrReadConsole)
182 {
183    PLIST_ENTRY CurrentEntry;
184    ConsoleInput *Input;
185    PCHAR Buffer;
186    int   i = 0;
187    ULONG nNumberOfCharsToRead;
188    PCSRSS_CONSOLE Console;
189    NTSTATUS Status;
190    
191    /* truncate length to CSRSS_MAX_READ_CONSOLE_REQUEST */
192    nNumberOfCharsToRead = Request->Data.ReadConsoleRequest.NrCharactersToRead > CSRSS_MAX_READ_CONSOLE_REQUEST ? CSRSS_MAX_READ_CONSOLE_REQUEST : Request->Data.ReadConsoleRequest.NrCharactersToRead;
193    Reply->Header.MessageSize = sizeof(CSRSS_API_REPLY);
194    Reply->Header.DataSize = Reply->Header.MessageSize -
195      sizeof(LPC_MESSAGE);
196
197    Buffer = Reply->Data.ReadConsoleReply.Buffer;
198    LOCK;   
199    Status = CsrGetObject( ProcessData, Request->Data.ReadConsoleRequest.ConsoleHandle, (Object_t **)&Console );
200    if( !NT_SUCCESS( Status ) )
201       {
202          Reply->Status = Status;
203          UNLOCK;
204          return Status;
205       }
206    if( Console->Header.Type != CSRSS_CONSOLE_MAGIC )
207       {
208          Reply->Status = STATUS_INVALID_HANDLE;
209          UNLOCK;
210          return STATUS_INVALID_HANDLE;
211       }
212    Reply->Data.ReadConsoleReply.EventHandle = ProcessData->ConsoleEvent;
213    for (; i<nNumberOfCharsToRead && Console->InputEvents.Flink != &Console->InputEvents; i++ )     
214       {
215          // remove input event from queue
216         CurrentEntry = RemoveHeadList(&Console->InputEvents);
217         Input = CONTAINING_RECORD(CurrentEntry, ConsoleInput, ListEntry);
218
219          // only pay attention to valid ascii chars, on key down
220          if( Input->InputEvent.EventType == KEY_EVENT &&
221              Input->InputEvent.Event.KeyEvent.bKeyDown == TRUE &&
222              Input->InputEvent.Event.KeyEvent.uChar.AsciiChar )
223             {
224                // backspace handling
225                if( Input->InputEvent.Event.KeyEvent.uChar.AsciiChar == '\b' )
226                   {
227                      // echo if it has not already been done, and either we or the client has chars to be deleted
228                      if( !Input->Echoed && ( i || Request->Data.ReadConsoleRequest.nCharsCanBeDeleted ) )
229                         CsrpWriteConsole( Console->ActiveBuffer, &Input->InputEvent.Event.KeyEvent.uChar.AsciiChar, 1, TRUE );
230                      if( i )
231                         i-=2;        // if we already have something to return, just back it up by 2
232                      else
233                         {            // otherwise, return STATUS_NOTIFY_CLEANUP to tell client to back up its buffer
234                            Reply->Data.ReadConsoleReply.NrCharactersRead = 0;
235                            Reply->Status = STATUS_NOTIFY_CLEANUP;
236                            Console->WaitingChars--;
237                            RtlFreeHeap( CsrssApiHeap, 0, Input );
238                            UNLOCK;
239                            return STATUS_NOTIFY_CLEANUP;
240                         }
241                      Request->Data.ReadConsoleRequest.nCharsCanBeDeleted--;
242                      Input->Echoed = TRUE;   // mark as echoed so we don't echo it below
243                   }
244                // do not copy backspace to buffer
245                else Buffer[i] = Input->InputEvent.Event.KeyEvent.uChar.AsciiChar;
246                // echo to screen if enabled and we did not already echo the char
247                if( Console->Mode & ENABLE_ECHO_INPUT &&
248                    !Input->Echoed &&
249                    Input->InputEvent.Event.KeyEvent.uChar.AsciiChar != '\r' )
250                   CsrpWriteConsole( Console->ActiveBuffer, &Input->InputEvent.Event.KeyEvent.uChar.AsciiChar, 1, TRUE );
251             }
252          else i--;
253          Console->WaitingChars--;
254          RtlFreeHeap( CsrssApiHeap, 0, Input );
255       }
256    Reply->Data.ReadConsoleReply.NrCharactersRead = i;
257    if( !i )
258       Reply->Status = STATUS_PENDING;    // we didn't read anything
259    else if( Console->Mode & ENABLE_LINE_INPUT )
260       if( !Console->WaitingLines || Buffer[i-1] != '\n' )
261          {
262             Reply->Status = STATUS_PENDING; // line buffered, didn't get a complete line
263          }
264       else {
265          Console->WaitingLines--;
266          Reply->Status = STATUS_SUCCESS; // line buffered, did get a complete line
267       }
268    else Reply->Status = STATUS_SUCCESS;  // not line buffered, did read something
269    if( Reply->Status == STATUS_PENDING )
270       {
271          Console->EchoCount = nNumberOfCharsToRead - i;
272       }
273    else {
274       Console->EchoCount = 0;             // if the client is no longer waiting on input, do not echo
275    }
276    Reply->Header.MessageSize += i;
277    UNLOCK;
278    return Reply->Status;
279 }
280
281 #define GET_CELL_BUFFER(b,o)\
282 (b)->Buffer[(o)++];
283
284 #define SET_CELL_BUFFER(b,o,c,a)\
285 (b)->Buffer[(o)++]=(c);\
286 (b)->Buffer[(o)++]=(a);
287
288 static VOID FASTCALL
289 ClearLineBuffer (
290         PCSRSS_SCREEN_BUFFER    Buff,
291         DWORD                   StartX
292         )
293 {
294         DWORD Offset   = 2 * ((Buff->CurrentY * Buff->MaxX) + StartX);
295         
296         for ( ; StartX < Buff->MaxX; StartX ++ )
297         {
298                 /* Fill the cell: Offset is incremented by the macro */
299                 SET_CELL_BUFFER(Buff,Offset,' ',Buff->DefaultAttrib)
300         }
301 }
302
303 NTSTATUS STDCALL CsrpWriteConsole( PCSRSS_SCREEN_BUFFER Buff, CHAR *Buffer, DWORD Length, BOOL Attrib )
304 {
305    IO_STATUS_BLOCK Iosb;
306    NTSTATUS Status;
307    int i;
308    DWORD Offset;
309    
310    for( i = 0; i < Length; i++ )
311       {
312          switch( Buffer[ i ] )
313             {
314             /* --- LF --- */
315             case '\n':
316                Buff->CurrentX = 0;
317                /* slide the viewable screen */
318                if (((Buff->CurrentY-Buff->ShowY + Buff->MaxY) % Buff->MaxY) == PhysicalConsoleSize.Y - 1)
319                  if (++Buff->ShowY == Buff->MaxY)
320                    Buff->ShowY = 0;
321                if( ++Buff->CurrentY == Buff->MaxY )
322                   {
323                      Buff->CurrentY = 0;
324                   }
325                ClearLineBuffer (Buff, 0);
326                break;
327             /* --- BS --- */
328             case '\b':
329               if( Buff->CurrentX == 0 )
330                 {
331                   /* slide viewable screen up */
332                   if( Buff->ShowY == Buff->CurrentY )
333                     {
334                       if( Buff->ShowY == 0 )
335                         Buff->ShowY = Buff->MaxY;
336                       else
337                         Buff->ShowY--;
338                     }
339                   /* slide virtual position up */
340                   Buff->CurrentX = Buff->MaxX;
341                   if( Buff->CurrentY == 0 )
342                     Buff->CurrentY = Buff->MaxY;
343                   else
344                     Buff->CurrentY--;
345                 }
346               else
347                 Buff->CurrentX--;
348               Offset = 2 * ((Buff->CurrentY * Buff->MaxX) + Buff->CurrentX);
349               SET_CELL_BUFFER(Buff,Offset,' ',Buff->DefaultAttrib);
350               break;
351             /* --- CR --- */
352             case '\r':
353               Buff->CurrentX = 0;
354               break;
355             /* --- TAB --- */
356             case '\t':
357               CsrpWriteConsole(Buff, "        ", (8 - (Buff->CurrentX % 8)), FALSE);
358               break;
359             /* --- */
360             default:
361                Offset = 2 * (((Buff->CurrentY * Buff->MaxX)) + Buff->CurrentX);
362                Buff->Buffer[Offset ++] = Buffer[ i ];
363                if( Attrib )
364                   Buff->Buffer[Offset] = Buff->DefaultAttrib;
365                Buff->CurrentX++;
366                if( Buff->CurrentX == Buff->MaxX )
367                   {
368                      /* if end of line, go to next */
369                      Buff->CurrentX = 0;
370                      if( ++Buff->CurrentY == Buff->MaxY )
371                         {
372                            /* if end of buffer, wrap back to beginning */
373                            Buff->CurrentY = 0;
374                         }
375                      /* clear new line */
376                      ClearLineBuffer (Buff, 0);
377                      /* slide the viewable screen */
378                      if( (Buff->CurrentY - Buff->ShowY + Buff->MaxY - 1) % Buff->MaxY == PhysicalConsoleSize.Y -1)
379                        if( ++Buff->ShowY == Buff->MaxY )
380                          Buff->ShowY = 0;
381                   }
382             }
383       }
384    if( Buff == ActiveConsole->ActiveBuffer )
385      {    /* only write to screen if Console is Active, and not scrolled up */
386         if( Attrib )
387            {
388               Status = NtWriteFile( ConsoleDeviceHandle, NULL, NULL, NULL, &Iosb, Buffer, Length, NULL, 0);
389               if (!NT_SUCCESS(Status))
390                  DbgPrint("CSR: Write failed\n");
391            }
392       }
393    return(STATUS_SUCCESS);
394 }
395
396 #define CsrpInitRect(_Rect, _Top, _Left, _Bottom, _Right) \
397 { \
398   ((_Rect).Top) = _Top; \
399   ((_Rect).Left) = _Left; \
400   ((_Rect).Bottom) = _Bottom; \
401   ((_Rect).Right) = _Right; \
402 }
403
404 #define CsrpRectHeight(Rect) \
405     ((Rect.Top) > (Rect.Bottom) ? 0 : (Rect.Bottom) - (Rect.Top) + 1)
406
407 #define CsrpRectWidth(Rect) \
408     ((Rect.Left) > (Rect.Right) ? 0 : (Rect.Right) - (Rect.Left) + 1)
409
410 #define CsrpIsRectEmpty(Rect) \
411   ((Rect.Left > Rect.Right) || (Rect.Top > Rect.Bottom))
412
413
414 inline BOOLEAN CsrpIsEqualRect(
415   SMALL_RECT Rect1,
416   SMALL_RECT Rect2)
417 {
418   return ((Rect1.Left == Rect2.Left) && (Rect1.Right == Rect2.Right) &&
419           (Rect1.Top == Rect2.Top) && (Rect1.Bottom == Rect2.Bottom));
420 }
421
422 inline BOOLEAN CsrpGetIntersection(
423   PSMALL_RECT Intersection,
424   SMALL_RECT Rect1,
425   SMALL_RECT Rect2)
426 {
427   if (CsrpIsRectEmpty(Rect1) ||
428     (CsrpIsRectEmpty(Rect2)) ||
429     (Rect1.Top > Rect2.Bottom) ||
430     (Rect1.Left > Rect2.Right) ||
431     (Rect1.Bottom < Rect2.Top) ||
432     (Rect1.Right < Rect2.Left))
433   {
434     /* The rectangles do not intersect */
435     CsrpInitRect(*Intersection, 0, -1, 0, -1)
436     return FALSE;
437   }
438
439   CsrpInitRect(
440     *Intersection,
441     RtlMax(Rect1.Top, Rect2.Top),
442     RtlMax(Rect1.Left, Rect2.Left),
443     RtlMin(Rect1.Bottom, Rect2.Bottom),
444     RtlMin(Rect1.Right, Rect2.Right));
445   return TRUE;
446 }
447
448 inline BOOLEAN CsrpGetUnion(
449   PSMALL_RECT Union,
450   SMALL_RECT Rect1,
451   SMALL_RECT Rect2)
452 {
453   if (CsrpIsRectEmpty(Rect1))
454     {
455             if (CsrpIsRectEmpty(Rect2))
456             {
457               CsrpInitRect(*Union, 0, -1, 0, -1);
458               return FALSE;
459             }
460           else
461       *Union = Rect2;
462     }
463   else
464     {
465             if (CsrpIsRectEmpty(Rect2))
466         {
467         *Union = Rect1;
468         }
469             else
470               {
471           CsrpInitRect(
472             *Union,
473             RtlMin(Rect1.Top, Rect2.Top),
474             RtlMin(Rect1.Left, Rect2.Left),
475             RtlMax(Rect1.Bottom, Rect2.Bottom),
476             RtlMax(Rect1.Right, Rect2.Right));
477               }
478     }
479   return TRUE;
480 }
481
482 inline BOOLEAN CsrpSubtractRect(
483   PSMALL_RECT Subtraction,
484   SMALL_RECT Rect1,
485   SMALL_RECT Rect2)
486 {
487   SMALL_RECT tmp;
488
489   if (CsrpIsRectEmpty(Rect1))
490     {
491             CsrpInitRect(*Subtraction, 0, -1, 0, -1);
492             return FALSE;
493     }
494   *Subtraction = Rect1;
495   if (CsrpGetIntersection(&tmp, Rect1, Rect2))
496     {
497             if (CsrpIsEqualRect(tmp, *Subtraction))
498               {
499                 CsrpInitRect(*Subtraction, 0, -1, 0, -1);
500                 return FALSE;
501               }
502             if ((tmp.Top == Subtraction->Top) && (tmp.Bottom == Subtraction->Bottom))
503               {
504                 if (tmp.Left == Subtraction->Left)
505             Subtraction->Left = tmp.Right;
506                 else if (tmp.Right == Subtraction->Right)
507             Subtraction->Right = tmp.Left;
508               }
509             else if ((tmp.Left == Subtraction->Left) && (tmp.Right == Subtraction->Right))
510               {
511                 if (tmp.Top == Subtraction->Top)
512             Subtraction->Top = tmp.Bottom;
513                 else if (tmp.Bottom == Subtraction->Bottom)
514             Subtraction->Bottom = tmp.Top;
515               }
516     }
517   return TRUE;
518 }
519
520 /*
521  * Screen buffer must be locked when this function is called
522  */
523 static VOID CsrpCopyRegion(
524   PCSRSS_SCREEN_BUFFER ScreenBuffer,
525   SMALL_RECT SrcRegion,
526   SMALL_RECT DstRegion)
527 {
528   SHORT SrcY, DstY;
529   DWORD SrcOffset;
530   DWORD DstOffset;
531   DWORD BytesPerLine;
532   ULONG i;
533
534   DstY = DstRegion.Top;
535   BytesPerLine = CsrpRectWidth(DstRegion) * 2;
536
537   SrcY = (SrcRegion.Top + ScreenBuffer->ShowY) % ScreenBuffer->MaxY;
538   DstY = (DstRegion.Top + ScreenBuffer->ShowY) % ScreenBuffer->MaxY;
539   SrcOffset = (SrcY * ScreenBuffer->MaxX + SrcRegion.Left + ScreenBuffer->ShowX) * 2;
540   DstOffset = (DstY * ScreenBuffer->MaxX + DstRegion.Left + ScreenBuffer->ShowX) * 2;
541   
542   for (i = SrcRegion.Top; i <= SrcRegion.Bottom; i++)
543   {
544     RtlCopyMemory(
545       &ScreenBuffer->Buffer[DstOffset],
546       &ScreenBuffer->Buffer[SrcOffset],
547       BytesPerLine);
548
549     if (++DstY == ScreenBuffer->MaxY)
550     {
551       DstY = 0;
552       DstOffset = (DstRegion.Left + ScreenBuffer->ShowX) * 2;
553     }
554     else
555     {
556       DstOffset += ScreenBuffer->MaxX * 2;
557     }
558
559     if (++SrcY == ScreenBuffer->MaxY)
560     {
561       SrcY = 0;
562       SrcOffset = (SrcRegion.Left + ScreenBuffer->ShowX) * 2;
563     }
564     else
565     {
566       SrcOffset += ScreenBuffer->MaxX * 2;
567     }
568   }
569 }
570
571 /*
572  * Screen buffer must be locked when this function is called
573  */
574 static VOID CsrpFillRegion(
575   PCSRSS_SCREEN_BUFFER ScreenBuffer,
576   SMALL_RECT Region,
577   CHAR_INFO CharInfo)
578 {
579   SHORT X, Y;
580   DWORD Offset;
581   DWORD Delta;
582   ULONG i;
583
584   Y = (Region.Top + ScreenBuffer->ShowY) % ScreenBuffer->MaxY;
585   Offset = (Y * ScreenBuffer->MaxX + Region.Left + ScreenBuffer->ShowX) * 2;
586   Delta = (ScreenBuffer->MaxX - CsrpRectWidth(Region)) * 2;
587
588   for (i = Region.Top; i <= Region.Bottom; i++)
589   {
590     for (X = Region.Left; X <= Region.Right; X++)
591     {
592       SET_CELL_BUFFER(ScreenBuffer, Offset, CharInfo.Char.AsciiChar, CharInfo.Attributes);
593     }
594     if (++Y == ScreenBuffer->MaxY)
595     {
596       Y = 0;
597       Offset = (Region.Left + ScreenBuffer->ShowX) * 2;
598     }
599     else
600     {
601       Offset += Delta;
602     }
603   }
604 }
605
606 /*
607  * Screen buffer must be locked when this function is called
608  */
609 inline NTSTATUS CsrpSetConsoleDeviceCursor(PCSRSS_SCREEN_BUFFER ScreenBuffer, SHORT X, SHORT Y)
610 {
611    CONSOLE_SCREEN_BUFFER_INFO ScrInfo;
612    IO_STATUS_BLOCK Iosb;
613
614    ScrInfo.dwCursorPosition.X = X;
615    ScrInfo.dwCursorPosition.Y = Y;
616    ScrInfo.wAttributes = ScreenBuffer->DefaultAttrib;
617
618    return NtDeviceIoControlFile( ConsoleDeviceHandle, NULL, NULL, NULL, &Iosb,
619      IOCTL_CONSOLE_SET_SCREEN_BUFFER_INFO, &ScrInfo, sizeof( ScrInfo ), 0, 0 );
620 }
621
622 /*
623  * Region - Region of virtual screen buffer to draw onto the physical console
624  * Screen buffer must be locked when this function is called
625  */
626 static VOID CsrpDrawRegion(
627   PCSRSS_SCREEN_BUFFER ScreenBuffer,
628   SMALL_RECT Region)
629 {
630    IO_STATUS_BLOCK Iosb;
631    NTSTATUS Status;
632    CONSOLE_SCREEN_BUFFER_INFO ScrInfo;
633    CONSOLE_MODE Mode;
634    int i, y;
635    DWORD BytesPerLine;
636    DWORD SrcOffset;
637    DWORD SrcDelta;
638
639    Mode.dwMode = 0; /* clear ENABLE_PROCESSED_OUTPUT mode */
640    Status = NtDeviceIoControlFile( ConsoleDeviceHandle, NULL, NULL, NULL, &Iosb,
641      IOCTL_CONSOLE_SET_MODE, &Mode, sizeof( Mode ), 0, 0 );
642    if( !NT_SUCCESS( Status ) )
643      {
644        DbgPrint( "CSR: Failed to set console mode\n" );
645        return;
646      }
647
648    /* blast out buffer */
649    BytesPerLine = CsrpRectWidth(Region) * 2;
650    SrcOffset = (((Region.Top + ScreenBuffer->ShowY) % ScreenBuffer->MaxY) * ScreenBuffer->MaxX + Region.Left + ScreenBuffer->ShowX) * 2;
651    SrcDelta = ScreenBuffer->MaxX * 2;
652    for( i = Region.Top, y = ScreenBuffer->ShowY; i <= Region.Bottom; i++ )
653      {
654         /* Position the cursor correctly */
655         Status = CsrpSetConsoleDeviceCursor(ScreenBuffer, Region.Left, i);
656         if( !NT_SUCCESS( Status ) )
657           {
658             DbgPrint( "CSR: Failed to set console info\n" );
659             return;
660           }
661
662         Status = NtWriteFile( ConsoleDeviceHandle, NULL, NULL, NULL, &Iosb,
663          &ScreenBuffer->Buffer[ SrcOffset ],
664          BytesPerLine, 0, 0 );
665        if( !NT_SUCCESS( Status ) )
666               {
667                 DbgPrint( "CSR: Write to console failed\n" );
668                 return;
669               }
670
671        /* wrap back around the end of the buffer */
672        if( ++y == ScreenBuffer->MaxY )
673        {
674          y = 0;
675          SrcOffset = (Region.Left + ScreenBuffer->ShowX) * 2;
676        }
677        else
678        {
679          SrcOffset += SrcDelta;
680        }
681      }
682    Mode.dwMode = ENABLE_PROCESSED_OUTPUT;
683    Status = NtDeviceIoControlFile( ConsoleDeviceHandle, NULL, NULL, NULL, &Iosb,
684      IOCTL_CONSOLE_SET_MODE, &Mode, sizeof( Mode ), 0, 0 );
685    if( !NT_SUCCESS( Status ) )
686      {
687        DbgPrint( "CSR: Failed to set console mode\n" );
688        return;
689      }
690    Status = CsrpSetConsoleDeviceCursor(
691      ScreenBuffer,
692      ScreenBuffer->CurrentX - ScreenBuffer->ShowX,
693      ((ScreenBuffer->CurrentY + ScreenBuffer->MaxY) - ScreenBuffer->ShowY) % ScreenBuffer->MaxY);
694    if( !NT_SUCCESS( Status ) )
695      {
696        DbgPrint( "CSR: Failed to set console info\n" );
697        return;
698      }
699    Status = NtDeviceIoControlFile( ConsoleDeviceHandle, NULL, NULL, NULL, &Iosb,
700      IOCTL_CONSOLE_SET_CURSOR_INFO, &ScreenBuffer->CursorInfo,
701      sizeof( ScreenBuffer->CursorInfo ), 0, 0 );
702    if( !NT_SUCCESS( Status ) )
703      {
704        DbgPrint( "CSR: Failed to set cursor info\n" );
705        return;
706      }
707 }
708
709
710 CSR_API(CsrWriteConsole)
711 {
712    BYTE *Buffer = Request->Data.WriteConsoleRequest.Buffer;
713    PCSRSS_SCREEN_BUFFER Buff;
714
715    DPRINT("CsrWriteConsole\n");
716    
717    Reply->Header.MessageSize = sizeof(CSRSS_API_REPLY);
718    Reply->Header.DataSize = sizeof(CSRSS_API_REPLY) -
719      sizeof(LPC_MESSAGE);
720
721    LOCK;
722    if( !NT_SUCCESS( CsrGetObject( ProcessData, Request->Data.WriteConsoleRequest.ConsoleHandle,
723      (Object_t **)&Buff ) ) || Buff->Header.Type != CSRSS_SCREEN_BUFFER_MAGIC )
724       {
725          UNLOCK;
726          return Reply->Status = STATUS_INVALID_HANDLE;
727       }
728    CsrpWriteConsole( Buff, Buffer, Request->Data.WriteConsoleRequest.NrCharactersToWrite, TRUE );
729    UNLOCK;
730    return Reply->Status = STATUS_SUCCESS;
731 }
732
733
734 NTSTATUS STDCALL CsrInitConsoleScreenBuffer( PCSRSS_SCREEN_BUFFER Console )
735 {
736   Console->Header.Type = CSRSS_SCREEN_BUFFER_MAGIC;
737   Console->Header.ReferenceCount = 0;
738   Console->MaxX = PhysicalConsoleSize.X;
739   Console->MaxY = PhysicalConsoleSize.Y;
740   Console->ShowX = 0;
741   Console->ShowY = 0;
742   Console->CurrentX = 0;
743   Console->CurrentY = 0;
744   Console->Buffer = RtlAllocateHeap( CsrssApiHeap, 0, Console->MaxX * Console->MaxY * 2 );
745   if( Console->Buffer == 0 )
746     return STATUS_INSUFFICIENT_RESOURCES;
747   Console->DefaultAttrib = 0x17;
748   /* initialize buffer to be empty with default attributes */
749   for( ; Console->CurrentY < Console->MaxY; Console->CurrentY++ )
750     {
751             ClearLineBuffer (Console, 0);
752     }
753   Console->CursorInfo.bVisible = TRUE;
754   Console->CursorInfo.dwSize = 5;
755   Console->Mode = ENABLE_PROCESSED_OUTPUT | ENABLE_WRAP_AT_EOL_OUTPUT;
756   Console->CurrentX = 0;
757   Console->CurrentY = 0;
758   return STATUS_SUCCESS;
759 }
760
761 VOID STDCALL CsrDeleteScreenBuffer( PCSRSS_SCREEN_BUFFER Buffer )
762 {
763   RtlFreeHeap( CsrssApiHeap, 0, Buffer->Buffer );
764   RtlFreeHeap( CsrssApiHeap, 0, Buffer );
765 }
766
767 NTSTATUS STDCALL CsrInitConsole(PCSRSS_CONSOLE Console)
768 {
769   NTSTATUS Status;
770   OBJECT_ATTRIBUTES ObjectAttributes;
771
772   Console->Title.MaximumLength = Console->Title.Length = 0;
773   Console->Title.Buffer = 0;
774   
775   RtlCreateUnicodeString( &Console->Title, L"Command Prompt" );
776   
777   Console->Header.ReferenceCount = 0;
778   Console->WaitingChars = 0;
779   Console->WaitingLines = 0;
780   Console->EchoCount = 0;
781   Console->Header.Type = CSRSS_CONSOLE_MAGIC;
782   Console->Mode = ENABLE_LINE_INPUT | ENABLE_ECHO_INPUT | ENABLE_PROCESSED_INPUT | ENABLE_MOUSE_INPUT;
783   Console->EarlyReturn = FALSE;
784   InitializeListHead(&Console->InputEvents);
785   InitializeListHead(&Console->ProcessList);
786
787   InitializeObjectAttributes(&ObjectAttributes, NULL, OBJ_INHERIT, NULL, NULL);
788
789   Status = NtCreateEvent( &Console->ActiveEvent, STANDARD_RIGHTS_ALL, &ObjectAttributes, FALSE, FALSE );
790   if( !NT_SUCCESS( Status ) )
791     {
792       return Status;
793     }
794   Console->ActiveBuffer = RtlAllocateHeap( CsrssApiHeap, 0, sizeof( CSRSS_SCREEN_BUFFER ) );
795   if( !Console->ActiveBuffer )
796      {
797         NtClose( Console->ActiveEvent );
798         return STATUS_INSUFFICIENT_RESOURCES;
799      }
800   Status = CsrInitConsoleScreenBuffer( Console->ActiveBuffer );
801   if( !NT_SUCCESS( Status ) )
802      {
803         NtClose( Console->ActiveEvent );
804         RtlFreeHeap( CsrssApiHeap, 0, Console->ActiveBuffer );
805         return Status;
806      }
807   /* Create the GDI Window to be used in windowed mode */
808   /* FIXME: create window; now write NULL */
809   Console->hWindow = (HWND) NULL;
810   /* add a reference count because the buffer is tied to the console */
811   Console->ActiveBuffer->Header.ReferenceCount++;
812   /* make console active, and insert into console list */
813   LOCK;
814   if( ActiveConsole )
815      {
816         Console->Prev = ActiveConsole;
817         Console->Next = ActiveConsole->Next;
818         ActiveConsole->Next->Prev = Console;
819         ActiveConsole->Next = Console;
820      }
821   else {
822      Console->Prev = Console;
823      Console->Next = Console;
824   }
825   ActiveConsole = Console;
826   /* copy buffer contents to screen */
827   CsrDrawConsole( Console->ActiveBuffer );
828   UNLOCK;
829   return STATUS_SUCCESS;
830 }
831
832 /***************************************************************
833  *  CsrDrawConsole blasts the console buffer onto the screen   *
834  *  must be called while holding the active console lock       *
835  **************************************************************/
836 VOID STDCALL CsrDrawConsole( PCSRSS_SCREEN_BUFFER Buff )
837 {
838    SMALL_RECT Region;
839
840    CsrpInitRect(
841      Region,
842      0,
843      0,
844      PhysicalConsoleSize.Y - 1,
845      PhysicalConsoleSize.X - 1);
846
847    CsrpDrawRegion(Buff, Region);
848 }
849
850
851 VOID STDCALL CsrDeleteConsole( PCSRSS_CONSOLE Console )
852 {
853    ConsoleInput *Event;
854    DPRINT( "CsrDeleteConsole\n" );
855    LOCK;
856    /* Drain input event queue */
857    while( Console->InputEvents.Flink != &Console->InputEvents )
858       {
859          Event = (ConsoleInput *)Console->InputEvents.Flink;
860          Console->InputEvents.Flink = Console->InputEvents.Flink->Flink;
861          Console->InputEvents.Flink->Flink->Blink = &Console->InputEvents;
862          RtlFreeHeap( CsrssApiHeap, 0, Event );
863       }
864    /* Switch to next console */
865    if( ActiveConsole == Console )
866       {
867          if( Console->Next != Console )
868             {
869                ActiveConsole = Console->Next;
870             }
871          else ActiveConsole = 0;
872       }
873    if (Console->Next != Console)
874    {
875       Console->Prev->Next = Console->Next;
876       Console->Next->Prev = Console->Prev;
877    }
878    
879    if( ActiveConsole )
880      CsrDrawConsole( ActiveConsole->ActiveBuffer );
881    if( !--Console->ActiveBuffer->Header.ReferenceCount )
882      CsrDeleteScreenBuffer( Console->ActiveBuffer );
883    UNLOCK;
884    NtClose( Console->ActiveEvent );
885    RtlFreeUnicodeString( &Console->Title );
886    RtlFreeHeap( CsrssApiHeap, 0, Console );
887 }
888
889 VOID STDCALL CsrInitConsoleSupport(VOID)
890 {
891    OBJECT_ATTRIBUTES ObjectAttributes;
892    UNICODE_STRING DeviceName;
893    NTSTATUS Status;
894    IO_STATUS_BLOCK Iosb;
895    CONSOLE_SCREEN_BUFFER_INFO ScrInfo;
896    
897    DPRINT("CSR: CsrInitConsoleSupport()\n");
898    
899    RtlInitUnicodeStringFromLiteral(&DeviceName, L"\\??\\BlueScreen");
900    InitializeObjectAttributes(&ObjectAttributes,
901                               &DeviceName,
902                               0,
903                               NULL,
904                               NULL);
905    Status = NtOpenFile(&ConsoleDeviceHandle,
906                        FILE_ALL_ACCESS,
907                        &ObjectAttributes,
908                        &Iosb,
909                        0,
910                        FILE_SYNCHRONOUS_IO_ALERT);
911    if (!NT_SUCCESS(Status))
912      {
913         DbgPrint("CSR: Failed to open console. Expect problems.\n");
914      }
915
916    RtlInitUnicodeStringFromLiteral(&DeviceName, L"\\??\\Keyboard");
917    InitializeObjectAttributes(&ObjectAttributes,
918                               &DeviceName,
919                               0,
920                               NULL,
921                               NULL);
922    Status = NtOpenFile(&KeyboardDeviceHandle,
923                        FILE_ALL_ACCESS,
924                        &ObjectAttributes,
925                        &Iosb,
926                        0,
927                        0);
928    if (!NT_SUCCESS(Status))
929      {
930         DbgPrint("CSR: Failed to open keyboard. Expect problems.\n");
931      }
932    
933    ActiveConsole = 0;
934    RtlInitializeCriticalSection( &ActiveConsoleLock );
935    Status = NtDeviceIoControlFile( ConsoleDeviceHandle, NULL, NULL, NULL, &Iosb, IOCTL_CONSOLE_GET_SCREEN_BUFFER_INFO, 0, 0, &ScrInfo, sizeof( ScrInfo ) );
936    if( !NT_SUCCESS( Status ) )
937      {
938        DbgPrint( "CSR: Failed to get console info, expect trouble\n" );
939        return;
940      }
941    PhysicalConsoleSize = ScrInfo.dwSize;
942 }
943
944 VOID Console_Api( DWORD RefreshEvent )
945 {
946   /* keep reading events from the keyboard and stuffing them into the current
947      console's input queue */
948   ConsoleInput *KeyEventRecord;
949   ConsoleInput *TempInput;
950   IO_STATUS_BLOCK Iosb;
951   NTSTATUS Status;
952   HANDLE Events[2];     // 0 = keyboard, 1 = refresh
953   int c;
954   int updown;
955   PCSRSS_CONSOLE SwapConsole = 0; // console we are thinking about swapping with
956
957   Events[0] = 0;
958   Status = NtCreateEvent( &Events[0], STANDARD_RIGHTS_ALL, NULL, FALSE, FALSE );
959   if( !NT_SUCCESS( Status ) )
960     {
961       DbgPrint( "CSR: NtCreateEvent failed: %x\n", Status );
962       NtTerminateProcess( NtCurrentProcess(), Status );
963     }
964   Events[1] = (HANDLE)RefreshEvent;
965   while( 1 )
966     {
967       KeyEventRecord = RtlAllocateHeap(CsrssApiHeap, 
968                                        0,
969                                        sizeof(ConsoleInput));
970        if ( KeyEventRecord == 0 )
971         {
972           DbgPrint( "CSR: Memory allocation failure!" );
973           continue;
974         }
975       KeyEventRecord->InputEvent.EventType = KEY_EVENT;
976       if( !KeyReadInhibit ) {
977         Status = NtReadFile( KeyboardDeviceHandle, Events[0], NULL, NULL, &Iosb,
978                              &KeyEventRecord->InputEvent.Event.KeyEvent, sizeof( KEY_EVENT_RECORD ), NULL, 0 );
979       } else {
980         Status = STATUS_PENDING;
981       }
982       if( !NT_SUCCESS( Status ) )
983         {
984           DbgPrint( "CSR: ReadFile on keyboard device failed\n" );
985           RtlFreeHeap( CsrssApiHeap, 0, KeyEventRecord );
986           continue;
987         }
988       if( Status == STATUS_PENDING )
989         {
990           while( 1 )
991             {
992               Status = NtWaitForMultipleObjects( 2, Events, WaitAny, FALSE, NULL );
993               if( Status == STATUS_WAIT_0 + 1 )
994                 {
995                   LOCK;
996                   CsrDrawConsole( ActiveConsole->ActiveBuffer );
997                   UNLOCK;
998                   continue;
999                 }
1000               else if( Status != STATUS_WAIT_0 )
1001                 {
1002                   DbgPrint( "CSR: NtWaitForMultipleObjects failed: %x, exiting\n", Status );
1003                   NtTerminateProcess( NtCurrentProcess(), Status );
1004                 }
1005               else break;
1006             }
1007         }
1008       if( KeyEventRecord->InputEvent.Event.KeyEvent.dwControlKeyState &
1009         ( RIGHT_ALT_PRESSED | LEFT_ALT_PRESSED )&&
1010           KeyEventRecord->InputEvent.Event.KeyEvent.wVirtualKeyCode == VK_TAB )
1011          if( KeyEventRecord->InputEvent.Event.KeyEvent.bKeyDown == TRUE )
1012             {
1013               ANSI_STRING Title;
1014               void * Buffer;
1015               COORD *pos;
1016               
1017                /* alt-tab, swap consoles */
1018                // move SwapConsole to next console, and print its title
1019               LOCK;
1020               if( !SwapConsole )
1021                 SwapConsole = ActiveConsole;
1022               
1023               if( KeyEventRecord->InputEvent.Event.KeyEvent.dwControlKeyState & SHIFT_PRESSED )
1024                 SwapConsole = SwapConsole->Prev;
1025               else SwapConsole = SwapConsole->Next;
1026               Title.MaximumLength = RtlUnicodeStringToAnsiSize( &SwapConsole->Title );
1027               Title.Length = 0;
1028               Buffer = RtlAllocateHeap( CsrssApiHeap,
1029                                         0,
1030                                         sizeof( COORD ) + Title.MaximumLength);
1031               pos = (COORD *)Buffer;
1032               Title.Buffer = Buffer + sizeof( COORD );
1033
1034               RtlUnicodeStringToAnsiString(&Title, &SwapConsole->Title, FALSE);
1035               pos->Y = PhysicalConsoleSize.Y / 2;
1036               pos->X = ( PhysicalConsoleSize.X - Title.Length ) / 2;
1037               // redraw the console to clear off old title
1038               CsrDrawConsole( ActiveConsole->ActiveBuffer );
1039               Status = NtDeviceIoControlFile( ConsoleDeviceHandle,
1040                                               NULL,
1041                                               NULL,
1042                                               NULL,
1043                                               &Iosb,
1044                                               IOCTL_CONSOLE_WRITE_OUTPUT_CHARACTER,
1045                                               Buffer,
1046                                               sizeof (COORD) + Title.Length,
1047                                               NULL,
1048                                               0);
1049               if( !NT_SUCCESS( Status ) )
1050                 {
1051                   DPRINT1( "Error writing to console\n" );
1052                 }
1053               RtlFreeHeap( CsrssApiHeap, 0, Buffer );
1054               
1055               UNLOCK;
1056               RtlFreeHeap( CsrssApiHeap, 0, KeyEventRecord );
1057               continue;
1058             }
1059          else {
1060             RtlFreeHeap( CsrssApiHeap, 0, KeyEventRecord );
1061             continue;
1062          }
1063       else if( SwapConsole &&
1064                KeyEventRecord->InputEvent.Event.KeyEvent.wVirtualKeyCode == VK_MENU &&
1065                KeyEventRecord->InputEvent.Event.KeyEvent.bKeyDown == FALSE )
1066         {
1067           // alt key released, swap consoles
1068           PCSRSS_CONSOLE tmp;
1069
1070           LOCK;
1071           if( SwapConsole != ActiveConsole )
1072             {
1073               // first remove swapconsole from the list
1074               SwapConsole->Prev->Next = SwapConsole->Next;
1075               SwapConsole->Next->Prev = SwapConsole->Prev;
1076               // now insert before activeconsole
1077               SwapConsole->Next = ActiveConsole;
1078               SwapConsole->Prev = ActiveConsole->Prev;
1079               ActiveConsole->Prev->Next = SwapConsole;
1080               ActiveConsole->Prev = SwapConsole;
1081             }
1082           ActiveConsole = SwapConsole;
1083           SwapConsole = 0;
1084           CsrDrawConsole( ActiveConsole->ActiveBuffer );
1085
1086           UNLOCK;
1087         }
1088      /* process Ctrl-C and Ctrl-Break */
1089      if (ActiveConsole->Mode & ENABLE_PROCESSED_INPUT &&
1090          KeyEventRecord->InputEvent.Event.KeyEvent.bKeyDown &&
1091          ((KeyEventRecord->InputEvent.Event.KeyEvent.wVirtualKeyCode == VK_PAUSE) || 
1092          (KeyEventRecord->InputEvent.Event.KeyEvent.wVirtualKeyCode == 'C')) &&
1093          (KeyEventRecord->InputEvent.Event.KeyEvent.dwControlKeyState & (LEFT_CTRL_PRESSED | RIGHT_CTRL_PRESSED)))
1094          {
1095             PCSRSS_PROCESS_DATA current;
1096             PLIST_ENTRY current_entry;
1097             DPRINT1("Console_Api Ctrl-C\n");
1098             LOCK;
1099             current_entry = ActiveConsole->ProcessList.Flink;
1100             while (current_entry != &ActiveConsole->ProcessList)
1101             {
1102                 current = CONTAINING_RECORD(current_entry, CSRSS_PROCESS_DATA, ProcessEntry);
1103                 current_entry = current_entry->Flink;
1104                 CsrConsoleCtrlEvent((DWORD)CTRL_C_EVENT, current);
1105             }
1106             UNLOCK;
1107             RtlFreeHeap( CsrssApiHeap, 0, KeyEventRecord );
1108             continue;
1109          }
1110      if( KeyEventRecord->InputEvent.Event.KeyEvent.dwControlKeyState &
1111         ( RIGHT_ALT_PRESSED | LEFT_ALT_PRESSED ) &&
1112         ( KeyEventRecord->InputEvent.Event.KeyEvent.wVirtualKeyCode == VK_UP ||
1113           KeyEventRecord->InputEvent.Event.KeyEvent.wVirtualKeyCode == VK_DOWN) )
1114          {
1115             if( KeyEventRecord->InputEvent.Event.KeyEvent.bKeyDown == TRUE )
1116                {
1117                   /* scroll up or down */
1118                   LOCK;
1119                   if( ActiveConsole == 0 )
1120                      {
1121                         DbgPrint( "CSR: No Active Console!\n" );
1122                         UNLOCK;
1123                         RtlFreeHeap( CsrssApiHeap, 0, KeyEventRecord );
1124                         continue;
1125                      }
1126                   if( KeyEventRecord->InputEvent.Event.KeyEvent.wVirtualKeyCode == VK_UP )
1127                      {
1128                         /* only scroll up if there is room to scroll up into */
1129                         if( ActiveConsole->ActiveBuffer->ShowY != ((ActiveConsole->ActiveBuffer->CurrentY + 1) %
1130         ActiveConsole->ActiveBuffer->MaxY) )
1131                            ActiveConsole->ActiveBuffer->ShowY = (ActiveConsole->ActiveBuffer->ShowY +
1132          ActiveConsole->ActiveBuffer->MaxY - 1) % ActiveConsole->ActiveBuffer->MaxY;
1133                      }
1134                   else if( ActiveConsole->ActiveBuffer->ShowY != ActiveConsole->ActiveBuffer->CurrentY )
1135                      /* only scroll down if there is room to scroll down into */
1136                      if( ActiveConsole->ActiveBuffer->ShowY % ActiveConsole->ActiveBuffer->MaxY != 
1137            ActiveConsole->ActiveBuffer->CurrentY )
1138
1139                         if( ((ActiveConsole->ActiveBuffer->CurrentY + 1) % ActiveConsole->ActiveBuffer->MaxY) != 
1140         (ActiveConsole->ActiveBuffer->ShowY + PhysicalConsoleSize.Y) % ActiveConsole->ActiveBuffer->MaxY )
1141                            ActiveConsole->ActiveBuffer->ShowY = (ActiveConsole->ActiveBuffer->ShowY + 1) %
1142          ActiveConsole->ActiveBuffer->MaxY;
1143                   CsrDrawConsole( ActiveConsole->ActiveBuffer );
1144                   UNLOCK;
1145                }
1146             RtlFreeHeap( CsrssApiHeap, 0, KeyEventRecord );
1147             continue;
1148         }
1149       LOCK;
1150       if( ActiveConsole == 0 )
1151          {
1152             DbgPrint( "CSR: No Active Console!\n" );
1153             UNLOCK;
1154             RtlFreeHeap( CsrssApiHeap, 0, KeyEventRecord );
1155             continue;
1156          }
1157       // process special keys if enabled
1158       if( ActiveConsole->Mode & (ENABLE_PROCESSED_INPUT | ENABLE_LINE_INPUT) )
1159           switch( KeyEventRecord->InputEvent.Event.KeyEvent.uChar.AsciiChar )
1160             {
1161             case '\r':
1162               // add a \n to the queue as well
1163               // first add the \r
1164               updown = KeyEventRecord->InputEvent.Event.KeyEvent.bKeyDown;
1165               KeyEventRecord->Echoed = FALSE;
1166               KeyEventRecord->InputEvent.Event.KeyEvent.uChar.AsciiChar = '\r';
1167         InsertTailList(&ActiveConsole->InputEvents, &KeyEventRecord->ListEntry);
1168               ActiveConsole->WaitingChars++;
1169               KeyEventRecord = RtlAllocateHeap( CsrssApiHeap, 0, sizeof( ConsoleInput ) );
1170               if( !KeyEventRecord )
1171                 {
1172                   DbgPrint( "CSR: Failed to allocate KeyEventRecord\n" );
1173                   UNLOCK;
1174                   continue;
1175                 }
1176               KeyEventRecord->InputEvent.EventType = KEY_EVENT;
1177               KeyEventRecord->InputEvent.Event.KeyEvent.bKeyDown = updown;
1178               KeyEventRecord->InputEvent.Event.KeyEvent.wVirtualKeyCode = 0;
1179               KeyEventRecord->InputEvent.Event.KeyEvent.wVirtualScanCode = 0;
1180               KeyEventRecord->InputEvent.Event.KeyEvent.uChar.AsciiChar = '\n';
1181             }
1182       // add event to the queue
1183       InsertTailList(&ActiveConsole->InputEvents, &KeyEventRecord->ListEntry);
1184       // if line input mode is enabled, only wake the client on enter key down
1185       if( !(ActiveConsole->Mode & ENABLE_LINE_INPUT ) ||
1186           ActiveConsole->EarlyReturn ||
1187           ( KeyEventRecord->InputEvent.Event.KeyEvent.uChar.AsciiChar == '\n' &&
1188             KeyEventRecord->InputEvent.Event.KeyEvent.bKeyDown == TRUE ) )
1189         {
1190           NtSetEvent( ActiveConsole->ActiveEvent, 0 );
1191           if( KeyEventRecord->InputEvent.Event.KeyEvent.uChar.AsciiChar == '\n' )
1192              ActiveConsole->WaitingLines++;
1193         }
1194       KeyEventRecord->Echoed = FALSE;
1195       if( ActiveConsole->Mode & (ENABLE_PROCESSED_INPUT | ENABLE_LINE_INPUT) &&
1196           KeyEventRecord->InputEvent.Event.KeyEvent.uChar.AsciiChar == '\b' &&
1197           KeyEventRecord->InputEvent.Event.KeyEvent.bKeyDown )
1198          {
1199             // walk the input queue looking for a char to backspace
1200             for( TempInput = (ConsoleInput *)ActiveConsole->InputEvents.Blink;
1201                   TempInput != (ConsoleInput *)&ActiveConsole->InputEvents &&
1202                   (TempInput->InputEvent.EventType != KEY_EVENT ||
1203                   TempInput->InputEvent.Event.KeyEvent.bKeyDown == FALSE ||
1204                   TempInput->InputEvent.Event.KeyEvent.uChar.AsciiChar == '\b' );
1205                   TempInput = (ConsoleInput *)TempInput->ListEntry.Blink );
1206             // if we found one, delete it, otherwise, wake the client
1207             if( TempInput != (ConsoleInput *)&ActiveConsole->InputEvents )
1208                {
1209                   // delete previous key in queue, maybe echo backspace to screen, and do not place backspace on queue
1210       RemoveEntryList(&TempInput->ListEntry);
1211                   if( TempInput->Echoed )
1212                      CsrpWriteConsole( ActiveConsole->ActiveBuffer, &KeyEventRecord->InputEvent.Event.KeyEvent.uChar.AsciiChar, 1, TRUE );
1213                   RtlFreeHeap( CsrssApiHeap, 0, TempInput );
1214       RemoveEntryList(&KeyEventRecord->ListEntry);
1215                   RtlFreeHeap( CsrssApiHeap, 0, KeyEventRecord );
1216                   ActiveConsole->WaitingChars -= 2;
1217                }
1218             else NtSetEvent( ActiveConsole->ActiveEvent, 0 );
1219    }
1220       else {
1221          // echo chars if we are supposed to and client is waiting for some
1222          if( ( ActiveConsole->Mode & ENABLE_ECHO_INPUT ) && ActiveConsole->EchoCount &&
1223              KeyEventRecord->InputEvent.Event.KeyEvent.uChar.AsciiChar &&
1224              KeyEventRecord->InputEvent.Event.KeyEvent.bKeyDown == TRUE &&
1225              KeyEventRecord->InputEvent.Event.KeyEvent.uChar.AsciiChar != '\r' )
1226             {
1227                // mark the char as already echoed
1228                CsrpWriteConsole( ActiveConsole->ActiveBuffer, &KeyEventRecord->InputEvent.Event.KeyEvent.uChar.AsciiChar, 1, TRUE );
1229                ActiveConsole->EchoCount--;
1230                KeyEventRecord->Echoed = TRUE;
1231             }
1232       }
1233
1234       
1235       ActiveConsole->WaitingChars++;
1236       if( !(ActiveConsole->Mode & ENABLE_LINE_INPUT) )
1237         NtSetEvent( ActiveConsole->ActiveEvent, 0 );
1238       UNLOCK;
1239     }
1240 }
1241
1242 CSR_API(CsrGetScreenBufferInfo)
1243 {
1244    NTSTATUS Status;
1245    PCSRSS_SCREEN_BUFFER Buff;
1246    PCONSOLE_SCREEN_BUFFER_INFO pInfo;
1247    IO_STATUS_BLOCK Iosb;
1248    
1249    Reply->Header.MessageSize = sizeof(CSRSS_API_REPLY);
1250    Reply->Header.DataSize = sizeof(CSRSS_API_REPLY) -
1251      sizeof(LPC_MESSAGE);
1252
1253    LOCK;
1254    if( !NT_SUCCESS( CsrGetObject( ProcessData, Request->Data.ScreenBufferInfoRequest.ConsoleHandle,
1255      (Object_t **)&Buff ) ) || Buff->Header.Type != CSRSS_SCREEN_BUFFER_MAGIC )
1256       {
1257          UNLOCK;
1258          return Reply->Status = STATUS_INVALID_HANDLE;
1259       }
1260    pInfo = &Reply->Data.ScreenBufferInfoReply.Info;
1261    if( Buff == ActiveConsole->ActiveBuffer )
1262      {
1263         Status = NtDeviceIoControlFile( ConsoleDeviceHandle, NULL, NULL, NULL, &Iosb,
1264     IOCTL_CONSOLE_GET_SCREEN_BUFFER_INFO, 0, 0, pInfo, sizeof( *pInfo ) );
1265         if( !NT_SUCCESS( Status ) )
1266            DbgPrint( "CSR: Failed to get console info, expect trouble\n" );
1267         Reply->Status = Status;
1268      }
1269    else {
1270       pInfo->dwSize.X = PhysicalConsoleSize.X;
1271       pInfo->dwSize.Y = PhysicalConsoleSize.Y;
1272       pInfo->dwCursorPosition.X = Buff->CurrentX - Buff->ShowX;
1273       pInfo->dwCursorPosition.Y = (Buff->CurrentY + Buff->MaxY - Buff->ShowY) % Buff->MaxY;
1274       pInfo->wAttributes = Buff->DefaultAttrib;
1275       pInfo->srWindow.Left = 0;
1276       pInfo->srWindow.Right = PhysicalConsoleSize.X - 1;
1277       pInfo->srWindow.Top = 0;
1278       pInfo->srWindow.Bottom = PhysicalConsoleSize.Y - 1;
1279       Reply->Status = STATUS_SUCCESS;
1280    }
1281    UNLOCK;
1282    return Reply->Status;
1283 }
1284
1285 CSR_API(CsrSetCursor)
1286 {
1287    NTSTATUS Status;
1288    PCSRSS_SCREEN_BUFFER Buff;
1289    CONSOLE_SCREEN_BUFFER_INFO Info;
1290    IO_STATUS_BLOCK Iosb;
1291    
1292    Reply->Header.MessageSize = sizeof(CSRSS_API_REPLY);
1293    Reply->Header.DataSize = sizeof(CSRSS_API_REPLY) -
1294      sizeof(LPC_MESSAGE);
1295
1296    LOCK;
1297    if( !NT_SUCCESS( CsrGetObject( ProcessData, Request->Data.SetCursorRequest.ConsoleHandle,
1298      (Object_t **)&Buff ) ) || Buff->Header.Type != CSRSS_SCREEN_BUFFER_MAGIC )
1299       {
1300          UNLOCK;
1301          return Reply->Status = STATUS_INVALID_HANDLE;
1302       }
1303    Info.dwCursorPosition = Request->Data.SetCursorRequest.Position;
1304    Info.wAttributes = Buff->DefaultAttrib;
1305    if( Buff == ActiveConsole->ActiveBuffer )
1306       {
1307          Status = NtDeviceIoControlFile( ConsoleDeviceHandle, NULL, NULL, NULL, &Iosb,
1308      IOCTL_CONSOLE_SET_SCREEN_BUFFER_INFO, &Info, sizeof( Info ), 0, 0 );
1309         if( !NT_SUCCESS( Status ) )
1310            DbgPrint( "CSR: Failed to set console info, expect trouble\n" );
1311       }
1312
1313    Buff->CurrentX = Info.dwCursorPosition.X + Buff->ShowX;
1314    Buff->CurrentY = (Info.dwCursorPosition.Y + Buff->ShowY) % Buff->MaxY;
1315    UNLOCK;
1316    return Reply->Status = Status;
1317 }
1318
1319 CSR_API(CsrWriteConsoleOutputChar)
1320 {
1321    PBYTE String = Request->Data.WriteConsoleOutputCharRequest.String;
1322    PBYTE Buffer;
1323    PCSRSS_SCREEN_BUFFER Buff;
1324    DWORD X, Y, Length;
1325    NTSTATUS Status;
1326    IO_STATUS_BLOCK Iosb;
1327
1328    DPRINT("CsrWriteConsoleOutputChar\n");
1329    
1330    Reply->Header.MessageSize = sizeof(CSRSS_API_REPLY);
1331    Reply->Header.DataSize = sizeof(CSRSS_API_REPLY) -
1332      sizeof(LPC_MESSAGE);
1333    LOCK;
1334    if( !NT_SUCCESS( CsrGetObject( ProcessData, Request->Data.WriteConsoleOutputCharRequest.ConsoleHandle, (Object_t **)&Buff ) ) || Buff->Header.Type != CSRSS_SCREEN_BUFFER_MAGIC )
1335       {
1336          UNLOCK;
1337          return Reply->Status = STATUS_INVALID_HANDLE;
1338       }
1339
1340
1341    X = Request->Data.WriteConsoleOutputCharRequest.Coord.X + Buff->ShowX;
1342    Y = (Request->Data.WriteConsoleOutputCharRequest.Coord.Y + Buff->ShowY) % Buff->MaxY;
1343    Length = Request->Data.WriteConsoleOutputCharRequest.Length;
1344    Buffer = &Buff->Buffer[2 * (Y * Buff->MaxX + X)];
1345    while(Length--)
1346    {
1347       *Buffer = *String++;
1348       Buffer += 2;
1349       if (++X == Buff->MaxX)
1350       {
1351          if (++Y == Buff->MaxY)
1352          {
1353             Y = 0;
1354             Buffer = Buff->Buffer;
1355          }
1356          X = 0;
1357       }
1358    }
1359    if( ActiveConsole->ActiveBuffer == Buff )
1360      {
1361        Status = NtDeviceIoControlFile( ConsoleDeviceHandle,
1362                                        NULL,
1363                                        NULL,
1364                                        NULL,
1365                                        &Iosb,
1366                                        IOCTL_CONSOLE_WRITE_OUTPUT_CHARACTER,
1367                                        &Request->Data.WriteConsoleOutputCharRequest.Coord,
1368                                        sizeof (COORD) + Request->Data.WriteConsoleOutputCharRequest.Length,
1369                                        NULL,
1370                                        0);
1371        if( !NT_SUCCESS( Status ) )
1372          DPRINT1( "Failed to write output chars: %x\n", Status );
1373      }
1374    Reply->Data.WriteConsoleOutputCharReply.EndCoord.X = X - Buff->ShowX;
1375    Reply->Data.WriteConsoleOutputCharReply.EndCoord.Y = (Y + Buff->MaxY - Buff->ShowY) % Buff->MaxY;
1376    UNLOCK;
1377    return Reply->Status = STATUS_SUCCESS;
1378 }
1379
1380 CSR_API(CsrFillOutputChar)
1381 {
1382    PCSRSS_SCREEN_BUFFER Buff;
1383    DWORD X, Y, Length;
1384    BYTE Char;
1385    OUTPUT_CHARACTER Character;
1386    PBYTE Buffer;
1387    NTSTATUS Status;
1388    IO_STATUS_BLOCK Iosb;
1389
1390    DPRINT("CsrFillOutputChar\n");
1391    
1392    Reply->Header.MessageSize = sizeof(CSRSS_API_REPLY);
1393    Reply->Header.DataSize = sizeof(CSRSS_API_REPLY) -
1394      sizeof(LPC_MESSAGE);
1395
1396    LOCK;
1397    if( !NT_SUCCESS( CsrGetObject( ProcessData, Request->Data.FillOutputRequest.ConsoleHandle, (Object_t **)&Buff ) ) || Buff->Header.Type != CSRSS_SCREEN_BUFFER_MAGIC )
1398       {
1399          UNLOCK;
1400          return Reply->Status = STATUS_INVALID_HANDLE;
1401       }
1402    X = Request->Data.FillOutputRequest.Position.X + Buff->ShowX;
1403    Y = (Request->Data.FillOutputRequest.Position.Y + Buff->ShowY) % Buff->MaxY;
1404    Buffer = &Buff->Buffer[2 * (Y * Buff->MaxX + X)];
1405    Char = Request->Data.FillOutputRequest.Char;
1406    Length = Request->Data.FillOutputRequest.Length;
1407    while (Length--)
1408    {
1409       *Buffer = Char;
1410       Buffer += 2;
1411       if( ++X == Buff->MaxX )
1412       {
1413          if( ++Y == Buff->MaxY )
1414          {
1415             Y = 0;
1416             Buffer = Buff->Buffer;
1417          }
1418          X = 0;
1419       }
1420    }
1421    if( Buff == ActiveConsole->ActiveBuffer )
1422    {
1423       Character.dwCoord = Request->Data.FillOutputRequest.Position;
1424       Character.cCharacter = Char;
1425       Character.nLength = Request->Data.FillOutputRequest.Length;
1426       Status = NtDeviceIoControlFile( ConsoleDeviceHandle, 
1427                                       NULL, 
1428                                       NULL, 
1429                                       NULL, 
1430                                       &Iosb,
1431                                       IOCTL_CONSOLE_FILL_OUTPUT_CHARACTER,
1432                                       &Character,
1433                                       sizeof(Character),
1434                                       NULL,
1435                                       0);
1436       if (!NT_SUCCESS(Status))
1437           DPRINT1( "Failed to write output characters to console\n" );
1438    }
1439    UNLOCK;
1440    return Reply->Status;
1441 }
1442
1443 CSR_API(CsrReadInputEvent)
1444 {
1445    PLIST_ENTRY CurrentEntry;
1446    PCSRSS_CONSOLE Console;
1447    NTSTATUS Status;
1448    ConsoleInput *Input;
1449    
1450    Reply->Header.MessageSize = sizeof(CSRSS_API_REPLY);
1451    Reply->Header.DataSize = sizeof(CSRSS_API_REPLY) -
1452       sizeof(LPC_MESSAGE);
1453    Reply->Data.ReadInputReply.Event = ProcessData->ConsoleEvent;
1454    
1455    LOCK;
1456    Status = CsrGetObject( ProcessData, Request->Data.ReadInputRequest.ConsoleHandle, (Object_t **)&Console );
1457    if( !NT_SUCCESS( Status ) || (Status = Console->Header.Type == CSRSS_CONSOLE_MAGIC ? 0 : STATUS_INVALID_HANDLE))
1458       {
1459          Reply->Status = Status;
1460          UNLOCK;
1461          return Status;
1462       }
1463
1464    // only get input if there is any
1465    if( Console->InputEvents.Flink != &Console->InputEvents )
1466      {
1467        CurrentEntry = RemoveHeadList(&Console->InputEvents);
1468        Input = CONTAINING_RECORD(CurrentEntry, ConsoleInput, ListEntry);
1469        Reply->Data.ReadInputReply.Input = Input->InputEvent;
1470
1471        if( Input->InputEvent.EventType == KEY_EVENT )
1472          {
1473            if( Console->Mode & ENABLE_LINE_INPUT &&
1474                Input->InputEvent.Event.KeyEvent.bKeyDown == FALSE &&
1475                Input->InputEvent.Event.KeyEvent.uChar.AsciiChar == '\n' )
1476              Console->WaitingLines--;
1477            Console->WaitingChars--;
1478          }
1479        RtlFreeHeap( CsrssApiHeap, 0, Input );
1480
1481        if (Console->InputEvents.Flink != &Console->InputEvents &&
1482            Reply->Data.ReadInputReply.Input.EventType == KEY_EVENT &&
1483            Reply->Data.ReadInputReply.Input.Event.KeyEvent.uChar.AsciiChar == '\r')
1484        {
1485           Input = CONTAINING_RECORD(Console->InputEvents.Flink, ConsoleInput, ListEntry);
1486           if (Input->InputEvent.EventType == KEY_EVENT &&
1487               Input->InputEvent.Event.KeyEvent.uChar.AsciiChar == '\n' && 
1488               ((Input->InputEvent.Event.KeyEvent.bKeyDown && Reply->Data.ReadInputReply.Input.Event.KeyEvent.bKeyDown) ||
1489               (Input->InputEvent.Event.KeyEvent.bKeyDown==FALSE && Reply->Data.ReadInputReply.Input.Event.KeyEvent.bKeyDown==FALSE)))
1490           {
1491             if(Console->Mode & ENABLE_LINE_INPUT &&
1492                Input->InputEvent.Event.KeyEvent.bKeyDown == FALSE &&
1493                Input->InputEvent.Event.KeyEvent.uChar.AsciiChar == '\n' )
1494              Console->WaitingLines--;
1495             Console->WaitingChars--;
1496             RemoveHeadList(&Console->InputEvents);
1497             RtlFreeHeap( CsrssApiHeap, 0, Input );
1498           }
1499        }
1500
1501        Reply->Data.ReadInputReply.MoreEvents = (Console->InputEvents.Flink != &Console->InputEvents) ? TRUE : FALSE;
1502        Status = STATUS_SUCCESS;
1503        Console->EarlyReturn = FALSE; // clear early return
1504      }
1505    else {
1506       Status = STATUS_PENDING;
1507       Console->EarlyReturn = TRUE;  // mark for early return
1508    }
1509    UNLOCK;
1510    return Reply->Status = Status;
1511 }
1512
1513 CSR_API(CsrWriteConsoleOutputAttrib)
1514 {
1515    PCSRSS_SCREEN_BUFFER Buff;
1516    PUCHAR Buffer, Attribute;
1517    NTSTATUS Status;
1518    int X, Y, Length;
1519    IO_STATUS_BLOCK Iosb;
1520
1521    DPRINT("CsrWriteConsoleOutputAttrib\n");
1522    
1523    Reply->Header.MessageSize = sizeof(CSRSS_API_REPLY);
1524    Reply->Header.DataSize = sizeof(CSRSS_API_REPLY) -
1525       sizeof(LPC_MESSAGE);
1526    LOCK;
1527    Status = CsrGetObject( ProcessData, Request->Data.WriteConsoleOutputAttribRequest.ConsoleHandle, (Object_t **)&Buff );
1528    if( !NT_SUCCESS( Status ) || (Status = Buff->Header.Type == CSRSS_SCREEN_BUFFER_MAGIC ? 0 : STATUS_INVALID_HANDLE ))
1529       {
1530          Reply->Status = Status;
1531          UNLOCK;
1532          return Status;
1533       }
1534    X = Request->Data.WriteConsoleOutputAttribRequest.Coord.X + Buff->ShowX;
1535    Y = (Request->Data.WriteConsoleOutputAttribRequest.Coord.Y + Buff->ShowY) % Buff->MaxY;
1536    Length = Request->Data.WriteConsoleOutputAttribRequest.Length;
1537    Buffer = &Buff->Buffer[2 * (Y * Buff->MaxX + X) + 1];
1538    Attribute = Request->Data.WriteConsoleOutputAttribRequest.String;
1539    while (Length--)
1540    {
1541       *Buffer = *Attribute++;
1542       Buffer += 2;
1543       if( ++X == Buff->MaxX )
1544       {
1545          X = 0;
1546          if( ++Y == Buff->MaxY )
1547          {
1548             Y = 0;
1549             Buffer = Buff->Buffer + 1;
1550          }
1551       }
1552    }
1553    if( Buff == ActiveConsole->ActiveBuffer )
1554       {
1555         Status = NtDeviceIoControlFile( ConsoleDeviceHandle,
1556                                         NULL,
1557                                         NULL,
1558                                         NULL,
1559                                         &Iosb,
1560                                         IOCTL_CONSOLE_WRITE_OUTPUT_ATTRIBUTE,
1561                                         &Request->Data.WriteConsoleOutputAttribRequest.Coord,
1562                                         Request->Data.WriteConsoleOutputAttribRequest.Length +
1563                                         sizeof (COORD),
1564                                         NULL,
1565                                         0 );
1566         if( !NT_SUCCESS( Status ) )
1567           DPRINT1( "Failed to write output attributes to console\n" );
1568       }
1569    Reply->Data.WriteConsoleOutputAttribReply.EndCoord.X = Buff->CurrentX - Buff->ShowX;
1570    Reply->Data.WriteConsoleOutputAttribReply.EndCoord.Y = ( Buff->CurrentY + Buff->MaxY - Buff->ShowY ) % Buff->MaxY;
1571    UNLOCK;
1572    return Reply->Status = STATUS_SUCCESS;
1573 }
1574
1575 CSR_API(CsrFillOutputAttrib)
1576 {
1577    OUTPUT_ATTRIBUTE Attribute;
1578    PCSRSS_SCREEN_BUFFER Buff;
1579    PCHAR Buffer;
1580    NTSTATUS Status;
1581    int X, Y, Length;
1582    IO_STATUS_BLOCK Iosb;
1583    UCHAR Attr;
1584
1585    DPRINT("CsrFillOutputAttrib\n");
1586    
1587    Reply->Header.MessageSize = sizeof(CSRSS_API_REPLY);
1588    Reply->Header.DataSize = sizeof(CSRSS_API_REPLY) -
1589       sizeof(LPC_MESSAGE);
1590    LOCK;
1591    Status = CsrGetObject( ProcessData, Request->Data.FillOutputAttribRequest.ConsoleHandle, (Object_t **)&Buff );
1592    if( !NT_SUCCESS( Status ) || (Status = Buff->Header.Type == CSRSS_SCREEN_BUFFER_MAGIC ? 0 : STATUS_INVALID_HANDLE ))
1593       {
1594          Reply->Status = Status;
1595          UNLOCK;
1596          return Status;
1597       }
1598    X = Request->Data.FillOutputAttribRequest.Coord.X + Buff->ShowX;
1599    Y = (Request->Data.FillOutputAttribRequest.Coord.Y + Buff->ShowY) % Buff->MaxY;
1600    Length = Request->Data.FillOutputAttribRequest.Length;
1601    Attr = Request->Data.FillOutputAttribRequest.Attribute;
1602    Buffer = &Buff->Buffer[(Y * Buff->MaxX * 2) + (X * 2) + 1];
1603    while(Length--)
1604    {
1605       *Buffer = Attr;
1606       Buffer += 2;
1607       if( ++X == Buff->MaxX )
1608       {
1609          X = 0;
1610          if( ++Y == Buff->MaxY )
1611          {
1612             Y = 0;
1613             Buffer = Buff->Buffer + 1;
1614          }
1615       }
1616    }
1617    if( Buff == ActiveConsole->ActiveBuffer )
1618    {
1619       Attribute.wAttribute = Attr;
1620       Attribute.nLength = Request->Data.FillOutputAttribRequest.Length;
1621       Attribute.dwCoord = Request->Data.FillOutputAttribRequest.Coord;
1622       Status = NtDeviceIoControlFile( ConsoleDeviceHandle, 
1623                                       NULL, 
1624                                       NULL, 
1625                                       NULL, 
1626                                       &Iosb,
1627                                       IOCTL_CONSOLE_FILL_OUTPUT_ATTRIBUTE, 
1628                                       &Attribute,
1629                                       sizeof(OUTPUT_ATTRIBUTE), 
1630                                       NULL, 
1631                                       0);
1632       if( !NT_SUCCESS( Status ) )
1633          DPRINT1( "Failed to fill output attributes to console\n" );
1634    }
1635    UNLOCK;
1636    return Reply->Status = STATUS_SUCCESS;
1637 }
1638
1639
1640 CSR_API(CsrGetCursorInfo)
1641 {
1642    PCSRSS_SCREEN_BUFFER Buff;
1643    NTSTATUS Status;
1644    
1645    Reply->Header.MessageSize = sizeof(CSRSS_API_REPLY);
1646    Reply->Header.DataSize = sizeof(CSRSS_API_REPLY) -
1647       sizeof(LPC_MESSAGE);
1648    LOCK;
1649    Status = CsrGetObject( ProcessData, Request->Data.GetCursorInfoRequest.ConsoleHandle, (Object_t **)&Buff );
1650    if( !NT_SUCCESS( Status ) || (Status = Buff->Header.Type == CSRSS_SCREEN_BUFFER_MAGIC ? 0 : STATUS_INVALID_HANDLE ))
1651       {
1652          Reply->Status = Status;
1653          UNLOCK;
1654          return Status;
1655       }
1656    Reply->Data.GetCursorInfoReply.Info = Buff->CursorInfo;
1657    UNLOCK;
1658    return Reply->Status = STATUS_SUCCESS;
1659 }
1660
1661 CSR_API(CsrSetCursorInfo)
1662 {
1663    PCSRSS_SCREEN_BUFFER Buff;
1664    NTSTATUS Status;
1665    IO_STATUS_BLOCK Iosb;
1666    
1667    Reply->Header.MessageSize = sizeof(CSRSS_API_REPLY);
1668    Reply->Header.DataSize = sizeof(CSRSS_API_REPLY) -
1669       sizeof(LPC_MESSAGE);
1670    LOCK;
1671    Status = CsrGetObject( ProcessData,
1672      Request->Data.SetCursorInfoRequest.ConsoleHandle, (Object_t **)&Buff );
1673
1674    if( !NT_SUCCESS( Status ) || (Status = Buff->Header.Type == CSRSS_SCREEN_BUFFER_MAGIC ? 0 : STATUS_INVALID_HANDLE ))
1675       {
1676          Reply->Status = Status;
1677          UNLOCK;
1678          return Status;
1679       }
1680    Buff->CursorInfo = Request->Data.SetCursorInfoRequest.Info;
1681    if( Buff == ActiveConsole->ActiveBuffer )
1682       {
1683          Status = NtDeviceIoControlFile( ConsoleDeviceHandle, NULL, NULL, NULL, &Iosb, IOCTL_CONSOLE_SET_CURSOR_INFO, &Buff->CursorInfo, sizeof( Buff->CursorInfo ), 0, 0 );
1684          if( !NT_SUCCESS( Status ) )
1685             {
1686                DbgPrint( "CSR: Failed to set cursor info\n" );
1687                return Reply->Status = Status;
1688             }
1689       }
1690    UNLOCK;
1691    return Reply->Status = STATUS_SUCCESS;
1692 }
1693
1694 CSR_API(CsrSetTextAttrib)
1695 {
1696    NTSTATUS Status;
1697    CONSOLE_SCREEN_BUFFER_INFO ScrInfo;
1698    IO_STATUS_BLOCK Iosb;
1699    PCSRSS_SCREEN_BUFFER Buff;
1700
1701    DPRINT("CsrSetTextAttrib\n");
1702    
1703    Reply->Header.MessageSize = sizeof(CSRSS_API_REPLY);
1704    Reply->Header.DataSize = sizeof(CSRSS_API_REPLY) -
1705       sizeof(LPC_MESSAGE);
1706    LOCK;
1707    Status = CsrGetObject( ProcessData, Request->Data.SetAttribRequest.ConsoleHandle, (Object_t **)&Buff );
1708    if( !NT_SUCCESS( Status ) || (Status = Buff->Header.Type == CSRSS_SCREEN_BUFFER_MAGIC ? 0 : STATUS_INVALID_HANDLE ))
1709       {
1710          Reply->Status = Status;
1711          UNLOCK;
1712          return Status;
1713       }
1714    Buff->DefaultAttrib = Request->Data.SetAttribRequest.Attrib;
1715    if( Buff == ActiveConsole->ActiveBuffer )
1716       {
1717          ScrInfo.wAttributes = Buff->DefaultAttrib;
1718          ScrInfo.dwCursorPosition.X = Buff->CurrentX - Buff->ShowX;   
1719          ScrInfo.dwCursorPosition.Y = ((Buff->CurrentY + Buff->MaxY) - Buff->ShowY) % Buff->MaxY;
1720          Status = NtDeviceIoControlFile( ConsoleDeviceHandle, NULL, NULL, NULL, &Iosb, IOCTL_CONSOLE_SET_SCREEN_BUFFER_INFO, &ScrInfo, sizeof( ScrInfo ), 0, 0 );
1721          if( !NT_SUCCESS( Status ) )
1722             {
1723                DbgPrint( "CSR: Failed to set console info\n" );
1724                UNLOCK;
1725                return Reply->Status = Status;
1726             }
1727       }
1728    UNLOCK;
1729    return Reply->Status = STATUS_SUCCESS;
1730 }
1731
1732 CSR_API(CsrSetConsoleMode)
1733 {
1734    NTSTATUS Status;
1735    PCSRSS_CONSOLE Console;
1736    PCSRSS_SCREEN_BUFFER Buff;
1737
1738    Reply->Header.MessageSize = sizeof(CSRSS_API_REPLY);
1739    Reply->Header.DataSize = sizeof(CSRSS_API_REPLY) - sizeof(LPC_MESSAGE);
1740    LOCK;
1741    Status = CsrGetObject( ProcessData,
1742      Request->Data.SetConsoleModeRequest.ConsoleHandle,
1743      (Object_t **)&Console );
1744    if( !NT_SUCCESS( Status ) )
1745       {
1746          Reply->Status = Status;
1747          UNLOCK;
1748          return Status;
1749       }
1750
1751    Buff = (PCSRSS_SCREEN_BUFFER)Console;
1752    if( Console->Header.Type == CSRSS_CONSOLE_MAGIC )
1753       Console->Mode = Request->Data.SetConsoleModeRequest.Mode & CONSOLE_INPUT_MODE_VALID;
1754    else if( Console->Header.Type == CSRSS_SCREEN_BUFFER_MAGIC )
1755       Buff->Mode = Request->Data.SetConsoleModeRequest.Mode & CONSOLE_OUTPUT_MODE_VALID;
1756    else {
1757       Reply->Status = STATUS_INVALID_HANDLE;
1758       UNLOCK;
1759       return Status;
1760    }
1761    UNLOCK;
1762    Reply->Status = STATUS_SUCCESS;
1763    return Reply->Status;
1764 }
1765
1766 CSR_API(CsrGetConsoleMode)
1767 {
1768    NTSTATUS Status;
1769    PCSRSS_CONSOLE Console;
1770    PCSRSS_SCREEN_BUFFER Buff;   /* gee, I really wish I could use an anonymous union here */
1771
1772    Reply->Header.MessageSize = sizeof(CSRSS_API_REPLY);
1773    Reply->Header.DataSize = sizeof(CSRSS_API_REPLY) - sizeof(LPC_MESSAGE);
1774    LOCK;
1775    Status = CsrGetObject( ProcessData,
1776      Request->Data.GetConsoleModeRequest.ConsoleHandle,
1777      (Object_t **)&Console );
1778    if( !NT_SUCCESS( Status ) )
1779       {
1780          Reply->Status = Status;
1781          UNLOCK;
1782          return Status;
1783       }
1784    Reply->Status = STATUS_SUCCESS;
1785    Buff = (PCSRSS_SCREEN_BUFFER)Console;
1786    if( Console->Header.Type == CSRSS_CONSOLE_MAGIC )
1787       Reply->Data.GetConsoleModeReply.ConsoleMode = Console->Mode;
1788    else if( Buff->Header.Type == CSRSS_SCREEN_BUFFER_MAGIC )
1789       Reply->Data.GetConsoleModeReply.ConsoleMode = Buff->Mode;
1790    else Status = STATUS_INVALID_HANDLE;
1791    UNLOCK;
1792    return Reply->Status;
1793 }
1794
1795 CSR_API(CsrCreateScreenBuffer)
1796 {
1797    PCSRSS_SCREEN_BUFFER Buff;
1798    NTSTATUS Status;
1799    
1800    Reply->Header.MessageSize = sizeof( CSRSS_API_REPLY );
1801    Reply->Header.DataSize = sizeof(CSRSS_API_REPLY) - sizeof(LPC_MESSAGE);
1802
1803    if (ProcessData == NULL)
1804    {
1805      return(Reply->Status = STATUS_INVALID_PARAMETER);
1806    }
1807
1808    Buff = RtlAllocateHeap( CsrssApiHeap, 0, sizeof( CSRSS_SCREEN_BUFFER ) );
1809    if( !Buff )
1810       Reply->Status = STATUS_INSUFFICIENT_RESOURCES;
1811    LOCK;
1812    Status = CsrInitConsoleScreenBuffer( Buff );
1813    if( !NT_SUCCESS( Status ) )
1814       Reply->Status = Status;
1815    else {
1816       Status = CsrInsertObject( ProcessData, &Reply->Data.CreateScreenBufferReply.OutputHandle, &Buff->Header );
1817       if( !NT_SUCCESS( Status ) )
1818          Reply->Status = Status;
1819       else Reply->Status = STATUS_SUCCESS;
1820    }
1821    UNLOCK;
1822    return Reply->Status;
1823 }
1824
1825 CSR_API(CsrSetScreenBuffer)
1826 {
1827    NTSTATUS Status;
1828    PCSRSS_SCREEN_BUFFER Buff;
1829    
1830    Reply->Header.MessageSize = sizeof( CSRSS_API_REPLY );
1831    Reply->Header.DataSize = sizeof(CSRSS_API_REPLY) - sizeof(LPC_MESSAGE);
1832    LOCK;
1833    Status = CsrGetObject( ProcessData, Request->Data.SetActiveScreenBufferRequest.OutputHandle, (Object_t **)&Buff );
1834    if( !NT_SUCCESS( Status ) )
1835       Reply->Status = Status;
1836    else {
1837       // drop reference to old buffer, maybe delete
1838       if( !InterlockedDecrement( &ProcessData->Console->ActiveBuffer->Header.ReferenceCount ) )
1839          CsrDeleteScreenBuffer( ProcessData->Console->ActiveBuffer );
1840       // tie console to new buffer
1841       ProcessData->Console->ActiveBuffer = Buff;
1842       // inc ref count on new buffer
1843       InterlockedIncrement( &Buff->Header.ReferenceCount );
1844       // if the console is active, redraw it
1845       if( ActiveConsole == ProcessData->Console )
1846          CsrDrawConsole( Buff );
1847       Reply->Status = STATUS_SUCCESS;
1848    }
1849    UNLOCK;
1850    return Reply->Status;
1851 }
1852
1853 CSR_API(CsrSetTitle)
1854 {
1855   NTSTATUS Status;
1856   PCSRSS_CONSOLE Console;
1857   
1858   Reply->Header.MessageSize = sizeof( CSRSS_API_REPLY );
1859   Reply->Header.DataSize = sizeof(CSRSS_API_REPLY) - sizeof(LPC_MESSAGE);
1860   LOCK;
1861   Status = CsrGetObject( ProcessData, Request->Data.SetTitleRequest.Console, (Object_t **)&Console );
1862   if( !NT_SUCCESS( Status ) )
1863     Reply->Status = Status;  
1864   else {
1865     // copy title to console
1866     RtlFreeUnicodeString( &Console->Title );
1867     RtlCreateUnicodeString( &Console->Title, Request->Data.SetTitleRequest.Title );
1868     Reply->Status = STATUS_SUCCESS;
1869   }
1870   UNLOCK;
1871   return Reply->Status;
1872 }
1873
1874 CSR_API(CsrGetTitle)
1875 {
1876         NTSTATUS        Status;
1877         PCSRSS_CONSOLE  Console;
1878   
1879         Reply->Header.MessageSize = sizeof (CSRSS_API_REPLY);
1880         Reply->Header.DataSize =
1881                 sizeof (CSRSS_API_REPLY)
1882                 - sizeof(LPC_MESSAGE);
1883         LOCK;
1884         Status = CsrGetObject (
1885                         ProcessData,
1886                         Request->Data.GetTitleRequest.ConsoleHandle,
1887                         (Object_t **) & Console
1888                         );
1889    if ( !NT_SUCCESS( Status ) )
1890    {
1891       Reply->Status = Status;
1892    }
1893         else
1894         {
1895                 HANDLE ConsoleHandle = Request->Data.GetTitleRequest.ConsoleHandle;
1896                 
1897                 /* Copy title of the console to the user title buffer */
1898                 RtlZeroMemory (
1899                         & Reply->Data.GetTitleReply,
1900                         sizeof (CSRSS_GET_TITLE_REPLY)
1901                         );
1902                 Reply->Data.GetTitleReply.ConsoleHandle = ConsoleHandle;
1903                 Reply->Data.GetTitleReply.Length = Console->Title.Length;
1904                 wcscpy (Reply->Data.GetTitleReply.Title, Console->Title.Buffer);
1905                 Reply->Header.MessageSize += Console->Title.Length;
1906                 Reply->Header.DataSize += Console->Title.Length;
1907                 Reply->Status = STATUS_SUCCESS;
1908         }
1909         UNLOCK;
1910         return Reply->Status;
1911 }
1912
1913 CSR_API(CsrWriteConsoleOutput)
1914 {
1915    SHORT i, X, Y, SizeX, SizeY;
1916    PCSRSS_SCREEN_BUFFER Buff;
1917    SMALL_RECT ScreenBuffer;
1918    CHAR_INFO* CurCharInfo;
1919    SMALL_RECT WriteRegion;
1920    CHAR_INFO* CharInfo;
1921    COORD BufferCoord;
1922    COORD BufferSize;
1923    NTSTATUS Status;
1924    DWORD Offset;
1925    DWORD PSize;
1926
1927    DPRINT("CsrWriteConsoleOutput\n");
1928
1929    Reply->Header.MessageSize = sizeof(CSRSS_API_REPLY);
1930    Reply->Header.DataSize = sizeof(CSRSS_API_REPLY) - sizeof(LPC_MESSAGE);
1931    LOCK;
1932    Status = CsrGetObject( ProcessData, Request->Data.WriteConsoleOutputRequest.ConsoleHandle, (Object_t **)&Buff );
1933    if( !NT_SUCCESS( Status ) || (Status = Buff->Header.Type == CSRSS_SCREEN_BUFFER_MAGIC ? STATUS_SUCCESS : STATUS_INVALID_HANDLE ))
1934       {
1935          Reply->Status = Status;
1936          UNLOCK;
1937          return Status;
1938       }
1939
1940    BufferSize = Request->Data.WriteConsoleOutputRequest.BufferSize;
1941    PSize = BufferSize.X * BufferSize.Y * sizeof(CHAR_INFO);
1942    BufferCoord = Request->Data.WriteConsoleOutputRequest.BufferCoord;
1943    CharInfo = Request->Data.WriteConsoleOutputRequest.CharInfo;
1944    if (((PVOID)CharInfo < ProcessData->CsrSectionViewBase) ||
1945        (((PVOID)CharInfo + PSize) > 
1946         (ProcessData->CsrSectionViewBase + ProcessData->CsrSectionViewSize)))
1947      {
1948        UNLOCK;
1949        Reply->Status = STATUS_ACCESS_VIOLATION;
1950        return(Reply->Status);
1951      }
1952    WriteRegion = Request->Data.WriteConsoleOutputRequest.WriteRegion;
1953
1954    SizeY = RtlMin(BufferSize.Y - BufferCoord.Y, CsrpRectHeight(WriteRegion));
1955    SizeX = RtlMin(BufferSize.X - BufferCoord.X, CsrpRectWidth(WriteRegion));
1956    WriteRegion.Bottom = WriteRegion.Top + SizeY - 1;
1957    WriteRegion.Right = WriteRegion.Left + SizeX - 1;
1958
1959    /* Make sure WriteRegion is inside the screen buffer */
1960    CsrpInitRect(ScreenBuffer, 0, 0, Buff->MaxY - 1, Buff->MaxX - 1);
1961    if (!CsrpGetIntersection(&WriteRegion, ScreenBuffer, WriteRegion))
1962       {
1963          UNLOCK;
1964          /* It is okay to have a WriteRegion completely outside the screen buffer.
1965             No data is written then. */
1966          return (Reply->Status = STATUS_SUCCESS);
1967       }
1968
1969    for ( i = 0, Y = WriteRegion.Top; Y <= WriteRegion.Bottom; i++, Y++ )
1970    {
1971      CurCharInfo = CharInfo + (i + BufferCoord.Y) * BufferSize.X + BufferCoord.X;
1972      Offset = (((Y + Buff->ShowY) % Buff->MaxY) * Buff->MaxX + WriteRegion.Left) * 2;
1973      for ( X = WriteRegion.Left; X <= WriteRegion.Right; X++ )
1974       {
1975         SET_CELL_BUFFER(Buff, Offset, CurCharInfo->Char.AsciiChar, CurCharInfo->Attributes);
1976         CurCharInfo++;
1977       }
1978    }
1979
1980    if( Buff == ActiveConsole->ActiveBuffer )
1981      {
1982         CsrpDrawRegion( ActiveConsole->ActiveBuffer, WriteRegion );
1983      }
1984
1985    UNLOCK;
1986    Reply->Data.WriteConsoleOutputReply.WriteRegion.Right = WriteRegion.Left + SizeX - 1;
1987    Reply->Data.WriteConsoleOutputReply.WriteRegion.Bottom = WriteRegion.Top + SizeY - 1;
1988    Reply->Data.WriteConsoleOutputReply.WriteRegion.Left = WriteRegion.Left;
1989    Reply->Data.WriteConsoleOutputReply.WriteRegion.Top = WriteRegion.Top;
1990    return (Reply->Status = STATUS_SUCCESS);
1991 }
1992
1993 CSR_API(CsrFlushInputBuffer)
1994 {
1995   PLIST_ENTRY CurrentEntry;
1996   PLIST_ENTRY NextEntry;
1997   PCSRSS_CONSOLE Console;
1998   ConsoleInput* Input;
1999   NTSTATUS Status;
2000
2001   Reply->Header.MessageSize = sizeof(CSRSS_API_REPLY);
2002   Reply->Header.DataSize = sizeof(CSRSS_API_REPLY) - sizeof(LPC_MESSAGE);
2003   LOCK;
2004   Status = CsrGetObject( ProcessData, Request->Data.FlushInputBufferRequest.ConsoleInput, (Object_t **)&Console );
2005   if( !NT_SUCCESS( Status ) || (Status = Console->Header.Type == CSRSS_CONSOLE_MAGIC ? STATUS_SUCCESS : STATUS_INVALID_HANDLE ))
2006     {
2007         Reply->Status = Status;
2008         UNLOCK;
2009         return Status;
2010     }
2011
2012   /* Discard all entries in the input event queue */
2013   while (!IsListEmpty(&Console->InputEvents))
2014     {
2015       CurrentEntry = RemoveHeadList(&Console->InputEvents);
2016       Input = CONTAINING_RECORD(CurrentEntry, ConsoleInput, ListEntry);
2017       /* Destroy the event */
2018       RtlFreeHeap( CsrssApiHeap, 0, Input );
2019     }
2020   Console->WaitingChars=0;
2021   UNLOCK;
2022
2023   return (Reply->Status = STATUS_SUCCESS);
2024 }
2025
2026 CSR_API(CsrScrollConsoleScreenBuffer)
2027 {
2028   SHORT i, X, Y, SizeX, SizeY;
2029   PCSRSS_SCREEN_BUFFER Buff;
2030   SMALL_RECT ScreenBuffer;
2031   SMALL_RECT SrcRegion;
2032   SMALL_RECT DstRegion;
2033   SMALL_RECT FillRegion;
2034   IO_STATUS_BLOCK Iosb;
2035   CHAR_INFO* CharInfo;
2036   NTSTATUS Status;
2037   DWORD SrcOffset;
2038   DWORD DstOffset;
2039   BOOLEAN DoFill;
2040
2041   ALIAS(ConsoleHandle,Request->Data.ScrollConsoleScreenBufferRequest.ConsoleHandle);
2042   ALIAS(ScrollRectangle,Request->Data.ScrollConsoleScreenBufferRequest.ScrollRectangle);
2043   ALIAS(UseClipRectangle,Request->Data.ScrollConsoleScreenBufferRequest.UseClipRectangle);
2044   ALIAS(ClipRectangle,Request->Data.ScrollConsoleScreenBufferRequest.ClipRectangle);
2045   ALIAS(DestinationOrigin,Request->Data.ScrollConsoleScreenBufferRequest.DestinationOrigin);
2046   ALIAS(Fill,Request->Data.ScrollConsoleScreenBufferRequest.Fill);
2047
2048   Reply->Header.MessageSize = sizeof(CSRSS_API_REPLY);
2049   Reply->Header.DataSize = sizeof(CSRSS_API_REPLY) - sizeof(LPC_MESSAGE);
2050   LOCK;
2051   Status = CsrGetObject( ProcessData, ConsoleHandle, (Object_t **)&Buff );
2052   if( !NT_SUCCESS( Status ) || (Status = Buff->Header.Type == CSRSS_SCREEN_BUFFER_MAGIC ? STATUS_SUCCESS : STATUS_INVALID_HANDLE ))
2053   {
2054     Reply->Status = Status;
2055     UNLOCK;
2056     return Status;
2057   }
2058
2059   /* Make sure source rectangle is inside the screen buffer */
2060   CsrpInitRect(ScreenBuffer, 0, 0, Buff->MaxY - 1, Buff->MaxX - 1);
2061   if (!CsrpGetIntersection(&SrcRegion, ScreenBuffer, ScrollRectangle))
2062     {
2063       UNLOCK;
2064       return (Reply->Status = STATUS_INVALID_PARAMETER);
2065     }
2066
2067   if (UseClipRectangle)
2068     {
2069       if (!CsrpGetIntersection(&SrcRegion, SrcRegion, ClipRectangle))
2070         {
2071           UNLOCK;
2072           return (Reply->Status = STATUS_SUCCESS);
2073         }
2074     }
2075
2076
2077   CsrpInitRect(
2078     DstRegion,
2079     DestinationOrigin.Y,
2080     DestinationOrigin.X,
2081     DestinationOrigin.Y + CsrpRectHeight(ScrollRectangle) - 1,
2082     DestinationOrigin.X + CsrpRectWidth(ScrollRectangle) - 1)
2083
2084   /* Make sure destination rectangle is inside the screen buffer */
2085   if (!CsrpGetIntersection(&DstRegion, DstRegion, ScreenBuffer))
2086     {
2087       UNLOCK;
2088       return (Reply->Status = STATUS_INVALID_PARAMETER);
2089     }
2090
2091   CsrpCopyRegion(Buff, SrcRegion, DstRegion);
2092
2093
2094   /* Get the region that should be filled with the specified character and attributes */
2095
2096   DoFill = FALSE;
2097
2098   CsrpGetUnion(&FillRegion, SrcRegion, DstRegion);
2099
2100   if (CsrpSubtractRect(&FillRegion, FillRegion, DstRegion))
2101     {
2102       /* FIXME: The subtracted rectangle is off by one line */
2103       FillRegion.Top += 1;
2104
2105       CsrpFillRegion(Buff, FillRegion, Fill);
2106       DoFill = TRUE;
2107     }
2108
2109   if (Buff == ActiveConsole->ActiveBuffer)
2110     {
2111       /* Draw destination region */
2112       CsrpDrawRegion(ActiveConsole->ActiveBuffer, DstRegion);
2113
2114       if (DoFill)
2115         {
2116           /* Draw filled region */
2117           CsrpDrawRegion(ActiveConsole->ActiveBuffer, FillRegion);
2118         }
2119     }
2120
2121   UNLOCK;
2122   return(Reply->Status = STATUS_SUCCESS);
2123 }
2124
2125
2126 CSR_API(CsrReadConsoleOutputChar)
2127 {
2128   NTSTATUS Status;
2129   PCSRSS_SCREEN_BUFFER ScreenBuffer;
2130   DWORD Xpos, Ypos;
2131   BYTE* ReadBuffer;
2132   DWORD i;
2133
2134   Reply->Header.MessageSize = sizeof(CSRSS_API_REPLY);
2135   Reply->Header.DataSize = Reply->Header.MessageSize - sizeof(LPC_MESSAGE);
2136   ReadBuffer = Reply->Data.ReadConsoleOutputCharReply.String;
2137
2138   LOCK;
2139
2140   Status = CsrGetObject(ProcessData, Request->Data.ReadConsoleOutputCharRequest.ConsoleHandle, (Object_t**)&ScreenBuffer);
2141   if (!NT_SUCCESS(Status))
2142     {
2143       Reply->Status = Status;
2144       UNLOCK;
2145       return(Reply->Status);
2146     }
2147
2148   if (ScreenBuffer->Header.Type != CSRSS_SCREEN_BUFFER_MAGIC)
2149     {
2150       Reply->Status = STATUS_INVALID_HANDLE;
2151       UNLOCK;
2152       return(Reply->Status);
2153     }
2154
2155   Xpos = Request->Data.ReadConsoleOutputCharRequest.ReadCoord.X + ScreenBuffer->ShowX;
2156   Ypos = (Request->Data.ReadConsoleOutputCharRequest.ReadCoord.Y + ScreenBuffer->ShowY) % ScreenBuffer->MaxY;
2157
2158   for (i = 0; i < Request->Data.ReadConsoleOutputCharRequest.NumCharsToRead; ++i)
2159     {
2160       *ReadBuffer = ScreenBuffer->Buffer[(Xpos * 2) + (Ypos * 2 * ScreenBuffer->MaxX)];
2161
2162       ReadBuffer++;
2163       Xpos++;
2164
2165       if (Xpos == ScreenBuffer->MaxX)
2166         {
2167           Xpos = 0;
2168           Ypos++;
2169
2170           if (Ypos == ScreenBuffer->MaxY)
2171             Ypos = 0;
2172         }
2173     }
2174
2175   *ReadBuffer = 0;
2176
2177   Reply->Status = STATUS_SUCCESS;
2178   Reply->Data.ReadConsoleOutputCharReply.EndCoord.X = Xpos - ScreenBuffer->ShowX;
2179   Reply->Data.ReadConsoleOutputCharReply.EndCoord.Y = (Ypos - ScreenBuffer->ShowY + ScreenBuffer->MaxY) % ScreenBuffer->MaxY;
2180   Reply->Header.MessageSize += Request->Data.ReadConsoleOutputCharRequest.NumCharsToRead;
2181   Reply->Header.DataSize += Request->Data.ReadConsoleOutputCharRequest.NumCharsToRead;
2182
2183   UNLOCK;
2184
2185   return(Reply->Status);
2186 }
2187
2188
2189 CSR_API(CsrReadConsoleOutputAttrib)
2190 {
2191   NTSTATUS Status;
2192   PCSRSS_SCREEN_BUFFER ScreenBuffer;
2193   DWORD Xpos, Ypos;
2194   CHAR* ReadBuffer;
2195   DWORD i;
2196
2197   Reply->Header.MessageSize = sizeof(CSRSS_API_REPLY);
2198   Reply->Header.DataSize = Reply->Header.MessageSize - sizeof(LPC_MESSAGE);
2199   ReadBuffer = Reply->Data.ReadConsoleOutputAttribReply.String;
2200
2201   LOCK;
2202
2203   Status = CsrGetObject(ProcessData, Request->Data.ReadConsoleOutputAttribRequest.ConsoleHandle, (Object_t**)&ScreenBuffer);
2204   if (!NT_SUCCESS(Status))
2205     {
2206       Reply->Status = Status;
2207       UNLOCK;
2208       return(Reply->Status);
2209     }
2210
2211   if (ScreenBuffer->Header.Type != CSRSS_SCREEN_BUFFER_MAGIC)
2212     {
2213       Reply->Status = STATUS_INVALID_HANDLE;
2214       UNLOCK;
2215       return(Reply->Status);
2216     }
2217
2218   Xpos = Request->Data.ReadConsoleOutputAttribRequest.ReadCoord.X + ScreenBuffer->ShowX;
2219   Ypos = (Request->Data.ReadConsoleOutputAttribRequest.ReadCoord.Y + ScreenBuffer->ShowY) % ScreenBuffer->MaxY;
2220
2221   for (i = 0; i < Request->Data.ReadConsoleOutputAttribRequest.NumAttrsToRead; ++i)
2222     {
2223       *ReadBuffer = ScreenBuffer->Buffer[(Xpos * 2) + (Ypos * 2 * ScreenBuffer->MaxX) + 1];
2224
2225       ReadBuffer++;
2226       Xpos++;
2227
2228       if (Xpos == ScreenBuffer->MaxX)
2229         {
2230           Xpos = 0;
2231           Ypos++;
2232
2233           if (Ypos == ScreenBuffer->MaxY)
2234             Ypos = 0;
2235         }
2236     }
2237
2238   *ReadBuffer = 0;
2239
2240   Reply->Status = STATUS_SUCCESS;
2241   Reply->Data.ReadConsoleOutputAttribReply.EndCoord.X = Xpos - ScreenBuffer->ShowX;
2242   Reply->Data.ReadConsoleOutputAttribReply.EndCoord.Y = (Ypos - ScreenBuffer->ShowY + ScreenBuffer->MaxY) % ScreenBuffer->MaxY;
2243   Reply->Header.MessageSize += Request->Data.ReadConsoleOutputAttribRequest.NumAttrsToRead;
2244   Reply->Header.DataSize += Request->Data.ReadConsoleOutputAttribRequest.NumAttrsToRead;
2245
2246   UNLOCK;
2247
2248   return(Reply->Status);
2249 }
2250
2251
2252 CSR_API(CsrGetNumberOfConsoleInputEvents)
2253 {
2254   NTSTATUS Status;
2255   PCSRSS_CONSOLE Console;
2256   PLIST_ENTRY CurrentItem;
2257   DWORD NumEvents;
2258   
2259   Reply->Header.MessageSize = sizeof(CSRSS_API_REPLY);
2260   Reply->Header.DataSize = Reply->Header.MessageSize - sizeof(LPC_MESSAGE);
2261
2262   LOCK;
2263
2264   Status = CsrGetObject(ProcessData, Request->Data.GetNumInputEventsRequest.ConsoleHandle, (Object_t**)&Console);
2265   if (!NT_SUCCESS(Status))
2266     {
2267       Reply->Status = Status;
2268       UNLOCK;
2269       return(Reply->Status);
2270     }
2271
2272   if (Console->Header.Type != CSRSS_CONSOLE_MAGIC)
2273     {
2274       Reply->Status = STATUS_INVALID_HANDLE;
2275       UNLOCK;
2276       return(Reply->Status);
2277     }
2278   
2279   CurrentItem = &Console->InputEvents;
2280   NumEvents = 0;
2281   
2282   // If there are any events ...
2283   if(CurrentItem->Flink != CurrentItem)
2284   {
2285     do
2286     {
2287       CurrentItem = CurrentItem->Flink;
2288       ++NumEvents;
2289     }while(CurrentItem != &Console->InputEvents);
2290   }
2291   
2292   UNLOCK;
2293   
2294   Reply->Status = STATUS_SUCCESS;
2295   Reply->Data.GetNumInputEventsReply.NumInputEvents = NumEvents;
2296    
2297   return Reply->Status;
2298 }
2299
2300
2301 CSR_API(CsrPeekConsoleInput)
2302 {
2303    NTSTATUS Status;
2304    PCSRSS_CONSOLE Console;
2305    DWORD Size;
2306    DWORD Length;
2307    PLIST_ENTRY CurrentItem;
2308    PLIST_ENTRY NextItem;
2309    PINPUT_RECORD InputRecord;
2310    ConsoleInput* Item;
2311    UINT NumItems;
2312    
2313    Reply->Header.MessageSize = sizeof(CSRSS_API_REPLY);
2314    Reply->Header.DataSize = sizeof(CSRSS_API_REPLY) - sizeof(LPC_MESSAGE);
2315    
2316    LOCK;
2317    
2318    Status = CsrGetObject(ProcessData, Request->Data.GetNumInputEventsRequest.ConsoleHandle, (Object_t**)&Console);
2319    if(!NT_SUCCESS(Status))
2320    {
2321       Reply->Status = Status;
2322       UNLOCK;
2323       return Reply->Status;
2324    }
2325
2326    if(Console->Header.Type != CSRSS_CONSOLE_MAGIC)
2327    {
2328       Reply->Status = STATUS_INVALID_HANDLE;
2329       UNLOCK;
2330       return Reply->Status;
2331    }
2332    
2333    InputRecord = Request->Data.PeekConsoleInputRequest.InputRecord;
2334    Length = Request->Data.PeekConsoleInputRequest.Length;
2335    Size = Length * sizeof(INPUT_RECORD);
2336    
2337     if(((PVOID)InputRecord < ProcessData->CsrSectionViewBase)
2338          || (((PVOID)InputRecord + Size) > (ProcessData->CsrSectionViewBase + ProcessData->CsrSectionViewSize)))
2339    {
2340       UNLOCK;
2341       Reply->Status = STATUS_ACCESS_VIOLATION;
2342       return Reply->Status ;
2343    }
2344    
2345    NumItems = 0;
2346    
2347    if(!IsListEmpty(&Console->InputEvents))
2348    {
2349       CurrentItem = &Console->InputEvents;
2350    
2351       while(NumItems < Length)
2352       {
2353          ++NumItems;
2354          Item = CONTAINING_RECORD(CurrentItem, ConsoleInput, ListEntry);
2355          *InputRecord++ = Item->InputEvent;
2356          
2357          if(CurrentItem->Flink == &Console->InputEvents)
2358             break;
2359          else
2360             CurrentItem = CurrentItem->Flink;
2361       }
2362    }
2363
2364    UNLOCK;
2365    
2366    Reply->Status = STATUS_SUCCESS;
2367    Reply->Data.PeekConsoleInputReply.Length = NumItems;
2368    return Reply->Status;
2369 }
2370
2371
2372 CSR_API(CsrReadConsoleOutput)
2373 {
2374    PCHAR_INFO CharInfo;
2375    PCHAR_INFO CurCharInfo;
2376    PCSRSS_SCREEN_BUFFER ScreenBuffer;
2377    DWORD Size;
2378    DWORD Length;
2379    DWORD SizeX, SizeY;
2380    NTSTATUS Status;
2381    COORD BufferSize;
2382    COORD BufferCoord;
2383    SMALL_RECT ReadRegion;
2384    SMALL_RECT ScreenRect;
2385    DWORD i, Y, X, Offset;
2386       
2387    Reply->Header.MessageSize = sizeof(CSRSS_API_REPLY);
2388    Reply->Header.DataSize = sizeof(CSRSS_API_REPLY) - sizeof(LPC_MESSAGE);
2389   
2390    LOCK;
2391   
2392    Status = CsrGetObject(ProcessData, Request->Data.ReadConsoleOutputRequest.ConsoleHandle, (Object_t**)&ScreenBuffer);
2393    if(!NT_SUCCESS(Status))
2394    {
2395       Reply->Status = Status;
2396       UNLOCK;
2397       return Reply->Status;
2398    }
2399
2400    if(ScreenBuffer->Header.Type != CSRSS_SCREEN_BUFFER_MAGIC)
2401    {
2402       Reply->Status = STATUS_INVALID_HANDLE;
2403       UNLOCK;
2404       return Reply->Status;
2405    }
2406    
2407    CharInfo = Request->Data.ReadConsoleOutputRequest.CharInfo;
2408    ReadRegion = Request->Data.ReadConsoleOutputRequest.ReadRegion;
2409    BufferSize = Request->Data.ReadConsoleOutputRequest.BufferSize;
2410    BufferCoord = Request->Data.ReadConsoleOutputRequest.BufferCoord;
2411    Length = BufferSize.X * BufferSize.Y;
2412    Size = Length * sizeof(CHAR_INFO);
2413    
2414    if(((PVOID)CharInfo < ProcessData->CsrSectionViewBase)
2415          || (((PVOID)CharInfo + Size) > (ProcessData->CsrSectionViewBase + ProcessData->CsrSectionViewSize)))
2416    {
2417       UNLOCK;
2418       Reply->Status = STATUS_ACCESS_VIOLATION;
2419       return Reply->Status ;
2420    }
2421    
2422    SizeY = RtlMin(BufferSize.Y - BufferCoord.Y, CsrpRectHeight(ReadRegion));
2423    SizeX = RtlMin(BufferSize.X - BufferCoord.X, CsrpRectWidth(ReadRegion));
2424    ReadRegion.Bottom = ReadRegion.Top + SizeY;
2425    ReadRegion.Right = ReadRegion.Left + SizeX;
2426
2427    CsrpInitRect(ScreenRect, 0, 0, ScreenBuffer->MaxY - 1, ScreenBuffer->MaxX - 1);
2428    if (!CsrpGetIntersection(&ReadRegion, ScreenRect, ReadRegion))
2429    {
2430       UNLOCK;
2431       Reply->Status = STATUS_SUCCESS;
2432       return Reply->Status;
2433    }
2434    
2435    for(i = 0, Y = ReadRegion.Top; Y < ReadRegion.Bottom; ++i, ++Y)
2436    {
2437      CurCharInfo = CharInfo + (i * BufferSize.X);
2438      
2439      Offset = (((Y + ScreenBuffer->ShowY) % ScreenBuffer->MaxY) * ScreenBuffer->MaxX + ReadRegion.Left) * 2;
2440      for(X = ReadRegion.Left; X < ReadRegion.Right; ++X)
2441      {
2442         CurCharInfo->Char.AsciiChar = GET_CELL_BUFFER(ScreenBuffer, Offset);
2443         CurCharInfo->Attributes = GET_CELL_BUFFER(ScreenBuffer, Offset);
2444         ++CurCharInfo;
2445      }
2446   }
2447   
2448    UNLOCK;
2449    
2450    Reply->Status = STATUS_SUCCESS;
2451    Reply->Data.ReadConsoleOutputReply.ReadRegion.Right = ReadRegion.Left + SizeX - 1;
2452    Reply->Data.ReadConsoleOutputReply.ReadRegion.Bottom = ReadRegion.Top + SizeY - 1;
2453    Reply->Data.ReadConsoleOutputReply.ReadRegion.Left = ReadRegion.Left;
2454    Reply->Data.ReadConsoleOutputReply.ReadRegion.Top = ReadRegion.Top;
2455    
2456    return Reply->Status;
2457 }
2458
2459
2460 CSR_API(CsrWriteConsoleInput)
2461 {
2462    PINPUT_RECORD InputRecord;
2463    PCSRSS_CONSOLE Console;
2464    NTSTATUS Status;
2465    DWORD Length;
2466    DWORD Size;
2467    DWORD i;
2468    PLIST_ENTRY NextItem;
2469    ConsoleInput* Record;
2470    
2471    Reply->Header.MessageSize = sizeof(CSRSS_API_REPLY);
2472    Reply->Header.DataSize = sizeof(CSRSS_API_REPLY) - sizeof(LPC_MESSAGE);
2473    
2474    LOCK;
2475    
2476    Status = CsrGetObject(ProcessData, Request->Data.WriteConsoleInputRequest.ConsoleHandle, (Object_t**)&Console);
2477    if(!NT_SUCCESS(Status))
2478    {
2479       Reply->Status = Status;
2480       UNLOCK;
2481       return Reply->Status;
2482    }
2483
2484    if(Console->Header.Type != CSRSS_CONSOLE_MAGIC)
2485    {
2486       Reply->Status = STATUS_INVALID_HANDLE;
2487       UNLOCK;
2488       return Reply->Status;
2489    }
2490    
2491    InputRecord = Request->Data.WriteConsoleInputRequest.InputRecord;
2492    Length = Request->Data.WriteConsoleInputRequest.Length;
2493    Size = Length * sizeof(INPUT_RECORD);
2494    
2495     if(((PVOID)InputRecord < ProcessData->CsrSectionViewBase)
2496          || (((PVOID)InputRecord + Size) > (ProcessData->CsrSectionViewBase + ProcessData->CsrSectionViewSize)))
2497    {
2498       UNLOCK;
2499       Reply->Status = STATUS_ACCESS_VIOLATION;
2500       return Reply->Status ;
2501    }
2502    
2503    for(i = 0; i < Length; ++i)
2504    {
2505       Record = RtlAllocateHeap(CsrssApiHeap, 0, sizeof(ConsoleInput));
2506       if(Record == NULL)
2507       {
2508          UNLOCK;
2509          Reply->Status = STATUS_INSUFFICIENT_RESOURCES;
2510          return Reply->Status;
2511       }
2512       
2513       Record->InputEvent = *InputRecord++;
2514       InsertTailList(&Console->InputEvents, &Record->ListEntry);
2515    }
2516       
2517    UNLOCK;
2518    
2519    Reply->Status = STATUS_SUCCESS;
2520    Reply->Data.WriteConsoleInputReply.Length = i;
2521    return Reply->Status;
2522 }
2523
2524 /**********************************************************************
2525  *      HardwareStateProperty
2526  *
2527  *      DESCRIPTION
2528  *              Set/Get the value of the HardwareState and switch
2529  *              between direct video buffer ouput and GDI windowed
2530  *              output.
2531  *      ARGUMENTS
2532  *              Client hands us a CSRSS_CONSOLE_HARDWARE_STATE
2533  *              object. We use the same object to reply.
2534  *      NOTE
2535  *              ConsoleHwState has the correct size to be compatible
2536  *              with NT's, but values are not.
2537  */
2538 static NTSTATUS FASTCALL SetConsoleHardwareState (PCSRSS_CONSOLE Console, DWORD ConsoleHwState)
2539 {
2540    DbgPrint( "Console Hardware State: %d\n", ConsoleHwState );
2541
2542    if ( (CONSOLE_HARDWARE_STATE_GDI_MANAGED == ConsoleHwState)
2543       ||(CONSOLE_HARDWARE_STATE_DIRECT == ConsoleHwState))
2544    {
2545       /* Inhibit keyboard input when hardware state ==
2546        * CONSOLE_HARDWARE_STATE_GDI_MANAGED */
2547       if (CONSOLE_HARDWARE_STATE_GDI_MANAGED == ConsoleHwState) {
2548          DbgPrint( "Keyboard Inhibited.\n" );
2549          KeyReadInhibit = TRUE;
2550       } else {
2551          DbgPrint( "Keyboard Enabled.\n" );
2552          KeyReadInhibit = FALSE;
2553       }
2554       if (Console->HardwareState != ConsoleHwState)
2555       {
2556          /* TODO: implement switching from full screen to windowed mode */
2557          /* TODO: or back; now simply store the hardware state */
2558          Console->HardwareState = ConsoleHwState;
2559       }
2560       return STATUS_SUCCESS;    
2561    }
2562    return STATUS_INVALID_PARAMETER_3; // Client: (handle, set_get, [mode])
2563 }
2564
2565 CSR_API(CsrHardwareStateProperty)
2566 {
2567    PCSRSS_CONSOLE Console;
2568    NTSTATUS       Status;
2569  
2570    Reply->Header.MessageSize = sizeof(CSRSS_API_REPLY);
2571    Reply->Header.DataSize = sizeof(CSRSS_API_REPLY) - sizeof(LPC_MESSAGE);
2572
2573    LOCK;
2574    
2575    Status = CsrGetObject (
2576                    ProcessData,
2577                    Request->Data.ConsoleHardwareStateRequest.ConsoleHandle,
2578                    (Object_t**) & Console
2579                    );
2580    if (!NT_SUCCESS(Status))
2581    {
2582       DbgPrint( "Failed to get console handle in SetConsoleHardwareState\n" );
2583       Reply->Status = Status;
2584    }
2585    else
2586    {
2587       if(Console->Header.Type != CSRSS_CONSOLE_MAGIC)
2588       {
2589         DbgPrint( "Bad magic on Console: %08x\n", Console->Header.Type );
2590         Reply->Status = STATUS_INVALID_HANDLE;
2591       }
2592       else
2593       {
2594          switch (Request->Data.ConsoleHardwareStateRequest.SetGet)
2595          {
2596          case CONSOLE_HARDWARE_STATE_GET:
2597             Reply->Data.ConsoleHardwareStateReply.State = Console->HardwareState;
2598             break;
2599       
2600          case CONSOLE_HARDWARE_STATE_SET:
2601             DbgPrint( "Setting console hardware state.\n" );
2602             Reply->Status = SetConsoleHardwareState (Console, Request->Data.ConsoleHardwareStateRequest.State);
2603             break;
2604
2605          default:
2606             Reply->Status = STATUS_INVALID_PARAMETER_2; // Client: (handle, [set_get], mode)
2607             break;
2608          }
2609       }
2610    }
2611
2612    UNLOCK;
2613
2614    return Reply->Status;
2615 }
2616
2617 CSR_API(CsrGetConsoleWindow)
2618 {
2619    PCSRSS_CONSOLE Console;
2620    NTSTATUS       Status;
2621  
2622    Reply->Header.MessageSize = sizeof(CSRSS_API_REPLY);
2623    Reply->Header.DataSize = sizeof(CSRSS_API_REPLY) - sizeof(LPC_MESSAGE);
2624
2625    LOCK;
2626    
2627    Status = CsrGetObject (
2628                    ProcessData,
2629                    Request->Data.ConsoleWindowRequest.ConsoleHandle,
2630                    (Object_t**) & Console
2631                    );
2632    if (!NT_SUCCESS(Status))
2633    {
2634       Reply->Status = Status;
2635    }
2636    else
2637    {
2638       if(Console->Header.Type != CSRSS_CONSOLE_MAGIC)
2639       {
2640          Reply->Status = STATUS_INVALID_HANDLE;
2641       }
2642       else
2643       {
2644          // Is this GDI handle valid in the client's context?
2645          Reply->Data.ConsoleWindowReply.WindowHandle = Console->hWindow;
2646       }
2647    }
2648
2649    UNLOCK;
2650
2651    return Reply->Status;
2652 }
2653
2654 /* EOF */