Removed the bugged conio.c:ComputeOffsetBuffer()
[reactos.git] / reactos / subsys / csrss / api / conio.c
index e2b3914..be54212 100644 (file)
@@ -1,4 +1,4 @@
-/* $Id: conio.c,v 1.8 2000/07/06 14:34:52 dwelch Exp $
+/* $Id: conio.c,v 1.21 2001/07/31 20:28:43 ea Exp $
  *
  * reactos/subsys/csrss/api/conio.c
  *
 #include "api.h"
 #include <ntdll/rtl.h>
 #include <ddk/ntddblue.h>
+#include <debug.h>
+
+#define LOCK   RtlEnterCriticalSection(&ActiveConsoleLock)
+#define UNLOCK RtlLeaveCriticalSection(&ActiveConsoleLock)
 
 /* GLOBALS *******************************************************************/
 
@@ -50,23 +54,39 @@ NTSTATUS CsrAllocConsole(PCSRSS_PROCESS_DATA ProcessData,
        LpcReply->Status = STATUS_INSUFFICIENT_RESOURCES;
        return STATUS_INSUFFICIENT_RESOURCES;
       }
-   LpcReply->Status = CsrInitConsole( ProcessData, Console );
+   LpcReply->Status = CsrInitConsole( Console );
    if( !NT_SUCCESS( LpcReply->Status ) )
      {
        RtlFreeHeap( CsrssApiHeap, 0, Console );
        return LpcReply->Status;
      }
    ProcessData->Console = Console;
-   Console->ReferenceCount++;
-   CsrInsertObject( ProcessData, &LpcReply->Data.AllocConsoleReply.ConsoleHandle, (Object_t *)Console );
+   /* add a reference count because the process is tied to the console */
+   Console->Header.ReferenceCount++;
+   Status = CsrInsertObject( ProcessData, &LpcReply->Data.AllocConsoleReply.InputHandle, &Console->Header );
+   if( !NT_SUCCESS( Status ) )
+      {
+        CsrDeleteConsole( Console );
+        ProcessData->Console = 0;
+        return LpcReply->Status = Status;
+      }
+   Status = CsrInsertObject( ProcessData, &LpcReply->Data.AllocConsoleReply.OutputHandle, &Console->ActiveBuffer->Header );
+   if( !NT_SUCCESS( Status ) )
+      {
+        Console->Header.ReferenceCount--;
+        CsrReleaseObject( ProcessData, LpcReply->Data.AllocConsoleReply.InputHandle );
+        ProcessData->Console = 0;
+        return LpcReply->Status = Status;
+      }
    ClientId.UniqueProcess = (HANDLE)ProcessData->ProcessId;
    Status = NtOpenProcess( &Process, PROCESS_DUP_HANDLE, 0, &ClientId );
    if( !NT_SUCCESS( Status ) )
      {
        DbgPrint( "CSR: NtOpenProcess() failed for handle duplication\n" );
-       Console->ReferenceCount--;
+       Console->Header.ReferenceCount--;
        ProcessData->Console = 0;
-       CsrReleaseObject( ProcessData, LpcReply->Data.AllocConsoleReply.ConsoleHandle );
+       CsrReleaseObject( ProcessData, LpcReply->Data.AllocConsoleReply.OutputHandle );
+       CsrReleaseObject( ProcessData, LpcReply->Data.AllocConsoleReply.InputHandle );
        LpcReply->Status = Status;
        return Status;
      }
@@ -75,8 +95,9 @@ NTSTATUS CsrAllocConsole(PCSRSS_PROCESS_DATA ProcessData,
      {
        DbgPrint( "CSR: NtDuplicateObject() failed: %x\n", Status );
        NtClose( Process );
-       Console->ReferenceCount--;
-       CsrReleaseObject( ProcessData, LpcReply->Data.AllocConsoleReply.ConsoleHandle );
+       Console->Header.ReferenceCount--;
+       CsrReleaseObject( ProcessData, LpcReply->Data.AllocConsoleReply.OutputHandle );
+       CsrReleaseObject( ProcessData, LpcReply->Data.AllocConsoleReply.InputHandle );
        ProcessData->Console = 0;
        LpcReply->Status = Status;
        return Status;
@@ -104,139 +125,206 @@ NTSTATUS CsrReadConsole(PCSRSS_PROCESS_DATA ProcessData,
 {
    ConsoleInput *Input;
    PCHAR Buffer;
-   int   i;
+   int   i = 0;
    ULONG nNumberOfCharsToRead;
    PCSRSS_CONSOLE Console;
    NTSTATUS Status;
    
+   /* truncate length to CSRSS_MAX_READ_CONSOLE_REQUEST */
    nNumberOfCharsToRead = LpcMessage->Data.ReadConsoleRequest.NrCharactersToRead > CSRSS_MAX_READ_CONSOLE_REQUEST ? CSRSS_MAX_READ_CONSOLE_REQUEST : LpcMessage->Data.ReadConsoleRequest.NrCharactersToRead;
-   
-//   DbgPrint("CSR: NrCharactersToRead %d\n", nNumberOfCharsToRead);
-   
-   LpcReply->Header.MessageSize = sizeof(CSRSS_API_REPLY) + 
-     nNumberOfCharsToRead;
+   LpcReply->Header.MessageSize = sizeof(CSRSS_API_REPLY);
    LpcReply->Header.DataSize = LpcReply->Header.MessageSize -
      sizeof(LPC_MESSAGE_HEADER);
    Buffer = LpcReply->Data.ReadConsoleReply.Buffer;
    LpcReply->Data.ReadConsoleReply.EventHandle = ProcessData->ConsoleEvent;
-   
+   LOCK;   
    Status = CsrGetObject( ProcessData, LpcMessage->Data.ReadConsoleRequest.ConsoleHandle, (Object_t **)&Console );
    if( !NT_SUCCESS( Status ) )
       {
         LpcReply->Status = Status;
+        UNLOCK;
         return Status;
       }
-   RtlEnterCriticalSection( &ActiveConsoleLock );
-   for (i=0; i<nNumberOfCharsToRead && Console->InputEvents.Flink != &Console->InputEvents; i++ )     
-     {
-       Input = (ConsoleInput *)Console->InputEvents.Flink;
-       Input->ListEntry.Blink->Flink = Input->ListEntry.Flink;
-       Input->ListEntry.Flink->Blink = Input->ListEntry.Blink;
-       if( Input->InputEvent.Event.KeyEvent.bKeyDown == TRUE && Input->InputEvent.Event.KeyEvent.uChar.AsciiChar )
-          Buffer[i] = Input->InputEvent.Event.KeyEvent.uChar.AsciiChar;
-       else i--;
-       RtlFreeHeap( CsrssApiHeap, 0, Input );
-     }
-   RtlLeaveCriticalSection( &ActiveConsoleLock );
+   if( Console->Header.Type != CSRSS_CONSOLE_MAGIC )
+      {
+        LpcReply->Status = STATUS_INVALID_HANDLE;
+        UNLOCK;
+        return STATUS_INVALID_HANDLE;
+      }
+   for (; i<nNumberOfCharsToRead && Console->InputEvents.Flink != &Console->InputEvents; i++ )     
+      {
+        // remove input event from queue
+        Input = (ConsoleInput *)Console->InputEvents.Flink;
+        
+        Input->ListEntry.Blink->Flink = Input->ListEntry.Flink;
+        Input->ListEntry.Flink->Blink = Input->ListEntry.Blink;
+        // only pay attention to valid ascii chars, on key down
+        if( Input->InputEvent.EventType == KEY_EVENT &&
+            Input->InputEvent.Event.KeyEvent.bKeyDown == TRUE &&
+            Input->InputEvent.Event.KeyEvent.uChar.AsciiChar )
+           {
+              // backspace handling
+              if( Input->InputEvent.Event.KeyEvent.uChar.AsciiChar == '\b' )
+                 {
+                    // echo if it has not already been done, and either we or the client has chars to be deleted
+                    if( !Input->Echoed && ( i || LpcMessage->Data.ReadConsoleRequest.nCharsCanBeDeleted ) )
+                       CsrpWriteConsole( Console->ActiveBuffer, &Input->InputEvent.Event.KeyEvent.uChar.AsciiChar, 1, TRUE );
+                    if( i )
+                       i-=2;        // if we already have something to return, just back it up by 2
+                    else
+                       {            // otherwise, return STATUS_NOTIFY_CLEANUP to tell client to back up its buffer
+                          LpcReply->Data.ReadConsoleReply.NrCharactersRead = 0;
+                          LpcReply->Status = STATUS_NOTIFY_CLEANUP;
+                          Console->WaitingChars--;
+                          RtlFreeHeap( CsrssApiHeap, 0, Input );
+                          UNLOCK;
+                          return STATUS_NOTIFY_CLEANUP;
+                       }
+                    LpcMessage->Data.ReadConsoleRequest.nCharsCanBeDeleted--;
+                    Input->Echoed = TRUE;   // mark as echoed so we don't echo it below
+                 }
+              // do not copy backspace to buffer
+              else Buffer[i] = Input->InputEvent.Event.KeyEvent.uChar.AsciiChar;
+              // echo to screen if enabled and we did not already echo the char
+              if( Console->Mode & ENABLE_ECHO_INPUT &&
+                  !Input->Echoed &&
+                  Input->InputEvent.Event.KeyEvent.uChar.AsciiChar != '\r' )
+                 CsrpWriteConsole( Console->ActiveBuffer, &Input->InputEvent.Event.KeyEvent.uChar.AsciiChar, 1, TRUE );
+           }
+        else i--;
+        Console->WaitingChars--;
+        RtlFreeHeap( CsrssApiHeap, 0, Input );
+      }
    LpcReply->Data.ReadConsoleReply.NrCharactersRead = i;
-   LpcReply->Status = i ? STATUS_SUCCESS : STATUS_PENDING;
-   return(Status);
+   if( !i )
+      LpcReply->Status = STATUS_PENDING;    // we didn't read anything
+   else if( Console->Mode & ENABLE_LINE_INPUT )
+      if( !Console->WaitingLines || Buffer[i-1] != '\n' )
+        {
+           LpcReply->Status = STATUS_PENDING; // line buffered, didn't get a complete line
+        }
+      else {
+        Console->WaitingLines--;
+        LpcReply->Status = STATUS_SUCCESS; // line buffered, did get a complete line
+      }
+   else LpcReply->Status = STATUS_SUCCESS;  // not line buffered, did read something
+   if( LpcReply->Status == STATUS_PENDING )
+      {
+        Console->EchoCount = nNumberOfCharsToRead - i;
+      }
+   else {
+      Console->EchoCount = 0;             // if the client is no longer waiting on input, do not echo
+   }
+   LpcReply->Header.MessageSize += i;
+   UNLOCK;
+   return LpcReply->Status;
 }
 
+#define SET_CELL_BUFFER(b,o,c,a)\
+(b)->Buffer[(o)++]=(c);\
+(b)->Buffer[(o)++]=(a);
 
+static VOID FASTCALL
+ClearLineBuffer (
+       PCSRSS_SCREEN_BUFFER    Buff,
+       DWORD                   StartX
+       )
+{
+       DWORD Offset   = 2 * ((Buff->CurrentY * Buff->MaxX) + StartX);
+       
+       for ( ; StartX < Buff->MaxX; StartX ++ )
+       {
+               /* Fill the cell: Offset is incremented by the macro */
+               SET_CELL_BUFFER(Buff,Offset,' ',Buff->DefaultAttrib)
+       }
+}
 
-NTSTATUS CsrpWriteConsole( PCSRSS_CONSOLE Console, CHAR *Buffer, DWORD Length, BOOL Attrib )
+NTSTATUS CsrpWriteConsole( PCSRSS_SCREEN_BUFFER Buff, CHAR *Buffer, DWORD Length, BOOL Attrib )
 {
    IO_STATUS_BLOCK Iosb;
    NTSTATUS Status;
    int i;
-
+   DWORD Offset;
+   
    for( i = 0; i < Length; i++ )
       {
         switch( Buffer[ i ] )
            {
-           case '\n': {
-              int c;
-              Console->CurrentX = 0;
+           /* --- LF --- */
+           case '\n':
+              Buff->CurrentX = 0;
               /* slide the viewable screen */
-              if( ((PhysicalConsoleSize.Y + Console->ShowY) % Console->MaxY) == (Console->CurrentY + 1) % Console->MaxY)
-                if( ++Console->ShowY == (Console->MaxY - 1) )
-                  Console->ShowY = 0;
-              if( ++Console->CurrentY == Console->MaxY )
-                 {
-                    Console->CurrentY = 0;
-                    for( c = 0; c < Console->MaxX; c++ )
-                       {
-                          /* clear new line */
-                          Console->Buffer[ c * 2 ] = ' ';
-                          Console->Buffer[ (c * 2) + 1 ] = Console->DefaultAttrib;
-                       }
-                 }
-              else for( c = 0; c < Console->MaxX; c++ )
+              if( ((PhysicalConsoleSize.Y + Buff->ShowY) % Buff->MaxY) == (Buff->CurrentY + 1) % Buff->MaxY)
+                if( ++Buff->ShowY == (Buff->MaxY - 1) )
+                  Buff->ShowY = 0;
+              if( ++Buff->CurrentY == Buff->MaxY )
                  {
-                    /* clear new line */
-                    Console->Buffer[ 2 * ((Console->CurrentY * Console->MaxX) + c) ] = ' ';
-                    Console->Buffer[ (2 * ((Console->CurrentY * Console->MaxX) + c)) + 1 ] = Console->DefaultAttrib;
+                    Buff->CurrentY = 0;
                  }
+              ClearLineBuffer (Buff, 0);
               break;
-           }
-           case '\b': {
-             if( Console->CurrentX == 0 )
+           /* --- BS --- */
+           case '\b':
+             if( Buff->CurrentX == 0 )
                {
                  /* slide viewable screen up */
-                 if( Console->ShowY == Console->CurrentY )
-                   if( Console->ShowY == 0 )
-                     Console->ShowY = Console->MaxY;
-                   else Console->ShowY--;
+                 if( Buff->ShowY == Buff->CurrentY )
+                   {
+                     if( Buff->ShowY == 0 )
+                       Buff->ShowY = Buff->MaxY;
+                     else
+                       Buff->ShowY--;
+                   }
                  /* slide virtual position up */
-                 Console->CurrentX = Console->MaxX;
-                 if( Console->CurrentY == 0 )
-                   Console->CurrentY = Console->MaxY;
-                 else Console->CurrentY--;
+                 Buff->CurrentX = Buff->MaxX;
+                 if( Buff->CurrentY == 0 )
+                   Buff->CurrentY = Buff->MaxY;
+                 else
+                   Buff->CurrentY--;
                }
-             else Console->CurrentX--;
-             Console->Buffer[ 2 * ((Console->CurrentY * Console->MaxX) + Console->CurrentX) ] = ' ';
-             Console->Buffer[ (2 * ((Console->CurrentY * Console->MaxX) + Console->CurrentX)) + 1 ] = Console->DefaultAttrib;
+             else
+               Buff->CurrentX--;
+             Offset = 2 * ((Buff->CurrentY * Buff->MaxX) + Buff->CurrentX);
+             SET_CELL_BUFFER(Buff,Offset,' ',Buff->DefaultAttrib);
              break;
-           }
-           default: {
-              int c;
-              Console->Buffer[ 2 * (((Console->CurrentY * Console->MaxX)) + Console->CurrentX) ] = Buffer[ i ];
+           /* --- CR --- */
+           case '\r':
+             Buff->CurrentX = 0;
+             break;
+           /* --- TAB --- */
+           case '\t':
+             CsrpWriteConsole(Buff, "        ", 8, Attrib);
+             break;
+           /* --- */
+           default:
+              Offset = 2 * (((Buff->CurrentY * Buff->MaxX)) + Buff->CurrentX);
+              Buff->Buffer[Offset ++] = Buffer[ i ];
               if( Attrib )
-                 Console->Buffer[ (2 * ((Console->CurrentY * Console->MaxX) + Console->CurrentX)) + 1 ] = Console->DefaultAttrib;
-              Console->CurrentX++;
-              if( Console->CurrentX == Console->MaxX )
+                 Buff->Buffer[Offset] = Buff->DefaultAttrib;
+              Buff->CurrentX++;
+              if( Buff->CurrentX == Buff->MaxX )
                  {
                     /* if end of line, go to next */
-                    Console->CurrentX = 0;
-                    if( ++Console->CurrentY == Console->MaxY )
+                    Buff->CurrentX = 0;
+                    if( ++Buff->CurrentY == Buff->MaxY )
                        {
                           /* if end of buffer, wrap back to beginning */
-                          Console->CurrentY = 0;
+                          Buff->CurrentY = 0;
                           /* clear new line */
-                          for( c = 0; c < Console->MaxX; c++ )
-                             {
-                                Console->Buffer[ 2 * ((Console->CurrentY * Console->MaxX) + c) ] = ' ';
-                                Console->Buffer[ (2 * ((Console->CurrentY * Console->MaxX) + c)) + 1 ] = Console->DefaultAttrib;
-                             }
+                          ClearLineBuffer (Buff, 0);
                        }
                     else {
                        /* clear new line */
-                       for( c = 0; c < Console->MaxX; c += 2 )
-                          {
-                             Console->Buffer[ 2 * ((Console->CurrentY * Console->MaxX) + c) ] = ' ';
-                             Console->Buffer[ (2 * ((Console->CurrentY * Console->MaxX) + c)) + 1 ] = Console->DefaultAttrib;
-                          }
+                       ClearLineBuffer (Buff, 0);
                     }
                     /* slide the viewable screen */
-                    if( (Console->CurrentY - Console->ShowY) == PhysicalConsoleSize.Y )
-                      if( ++Console->ShowY == Console->MaxY )
-                        Console->ShowY = 0;
+                    if( (Buff->CurrentY - Buff->ShowY) == PhysicalConsoleSize.Y )
+                      if( ++Buff->ShowY == Buff->MaxY )
+                        Buff->ShowY = 0;
                  }
            }
-           }
       }
-   if( Console == ActiveConsole )
+   if( Buff == ActiveConsole->ActiveBuffer )
      {    /* only write to screen if Console is Active, and not scrolled up */
        if( Attrib )
           {
@@ -244,7 +332,6 @@ NTSTATUS CsrpWriteConsole( PCSRSS_CONSOLE Console, CHAR *Buffer, DWORD Length, B
              if (!NT_SUCCESS(Status))
                 DbgPrint("CSR: Write failed\n");
           }
-       else CsrDrawConsole( Console );
       }
    return(STATUS_SUCCESS);
 }
@@ -254,59 +341,95 @@ NTSTATUS CsrWriteConsole(PCSRSS_PROCESS_DATA ProcessData,
                         PCSRSS_API_REPLY Reply)
 {
    BYTE *Buffer = LpcMessage->Data.WriteConsoleRequest.Buffer;
-   PCSRSS_CONSOLE Console;
+   PCSRSS_SCREEN_BUFFER Buff;
    
    Reply->Header.MessageSize = sizeof(CSRSS_API_REPLY);
    Reply->Header.DataSize = sizeof(CSRSS_API_REPLY) -
      sizeof(LPC_MESSAGE_HEADER);
 
-   if( !NT_SUCCESS( CsrGetObject( ProcessData, LpcMessage->Data.WriteConsoleRequest.ConsoleHandle, (Object_t **)&Console ) ) )
-     return Reply->Status = STATUS_INVALID_HANDLE;
-   RtlEnterCriticalSection( &ActiveConsoleLock );
-   CsrpWriteConsole( Console, Buffer, LpcMessage->Data.WriteConsoleRequest.NrCharactersToWrite, TRUE );
-   RtlLeaveCriticalSection( &ActiveConsoleLock );
+   LOCK;
+   if( !NT_SUCCESS( CsrGetObject( ProcessData, LpcMessage->Data.WriteConsoleRequest.ConsoleHandle, (Object_t **)&Buff ) ) || Buff->Header.Type != CSRSS_SCREEN_BUFFER_MAGIC )
+      {
+        UNLOCK;
+        return Reply->Status = STATUS_INVALID_HANDLE;
+      }
+   CsrpWriteConsole( Buff, Buffer, LpcMessage->Data.WriteConsoleRequest.NrCharactersToWrite, TRUE );
+   UNLOCK;
    return Reply->Status = STATUS_SUCCESS;
 }
 
 
-NTSTATUS CsrInitConsole(PCSRSS_PROCESS_DATA ProcessData,
-                   PCSRSS_CONSOLE Console)
+NTSTATUS CsrInitConsoleScreenBuffer( PCSRSS_SCREEN_BUFFER Console )
 {
-  NTSTATUS Status;
-  
+  Console->Header.Type = CSRSS_SCREEN_BUFFER_MAGIC;
+  Console->Header.ReferenceCount = 0;
   Console->MaxX = PhysicalConsoleSize.X;
   Console->MaxY = PhysicalConsoleSize.Y * 2;
   Console->ShowX = 0;
   Console->ShowY = 0;
   Console->CurrentX = 0;
   Console->CurrentY = 0;
-  Console->ReferenceCount = 0;
-  Console->Type = CSRSS_CONSOLE_MAGIC;
   Console->Buffer = RtlAllocateHeap( CsrssApiHeap, 0, Console->MaxX * Console->MaxY * 2 );
   if( Console->Buffer == 0 )
     return STATUS_INSUFFICIENT_RESOURCES;
-  Console->InputEvents.Flink = Console->InputEvents.Blink = &Console->InputEvents;
-  Status = NtCreateEvent( &Console->ActiveEvent, STANDARD_RIGHTS_ALL, 0, FALSE, FALSE );
-  if( !NT_SUCCESS( Status ) )
-    {
-      RtlFreeHeap( CsrssApiHeap, 0, Console->Buffer );
-      return Status;
-    }
   Console->DefaultAttrib = 0x17;
   /* initialize buffer to be empty with default attributes */
   for( ; Console->CurrentY < Console->MaxY; Console->CurrentY++ )
     {
-      for( ; Console->CurrentX < Console->MaxX; Console->CurrentX++ )
-       {
-         Console->Buffer[ (Console->CurrentX * 2) + (Console->CurrentY * Console->MaxX * 2) ] = ' ';
-         Console->Buffer[ (Console->CurrentX * 2) + (Console->CurrentY * Console->MaxX * 2)+ 1 ] = Console->DefaultAttrib;
-       }
-      Console->CurrentX = 0;
+           ClearLineBuffer (Console, 0);
     }
   Console->CursorInfo.bVisible = TRUE;
   Console->CursorInfo.dwSize = 5;
+  Console->Mode = ENABLE_PROCESSED_OUTPUT | ENABLE_WRAP_AT_EOL_OUTPUT;
+  Console->CurrentX = 0;
+  Console->CurrentY = 0;
+  return STATUS_SUCCESS;
+}
+
+VOID CsrDeleteScreenBuffer( PCSRSS_SCREEN_BUFFER Buffer )
+{
+  RtlFreeHeap( CsrssApiHeap, 0, Buffer->Buffer );
+  RtlFreeHeap( CsrssApiHeap, 0, Buffer );
+}
+
+NTSTATUS CsrInitConsole(PCSRSS_CONSOLE Console)
+{
+  NTSTATUS Status;
+
+  Console->Title.MaximumLength = Console->Title.Length = 0;
+  Console->Title.Buffer = 0;
+  
+  RtlCreateUnicodeString( &Console->Title, L"Command Prompt" );
+  
+  Console->Header.ReferenceCount = 0;
+  Console->WaitingChars = 0;
+  Console->WaitingLines = 0;
+  Console->EchoCount = 0;
+  Console->Header.Type = CSRSS_CONSOLE_MAGIC;
+  Console->Mode = ENABLE_LINE_INPUT | ENABLE_ECHO_INPUT | ENABLE_PROCESSED_INPUT | ENABLE_MOUSE_INPUT;
+  Console->InputEvents.Flink = Console->InputEvents.Blink = &Console->InputEvents;
+  Status = NtCreateEvent( &Console->ActiveEvent, STANDARD_RIGHTS_ALL, 0, FALSE, FALSE );
+  if( !NT_SUCCESS( Status ) )
+    {
+      return Status;
+    }
+  Console->ActiveBuffer = RtlAllocateHeap( CsrssApiHeap, 0, sizeof( CSRSS_SCREEN_BUFFER ) );
+  if( !Console->ActiveBuffer )
+     {
+       NtClose( Console->ActiveEvent );
+       return STATUS_INSUFFICIENT_RESOURCES;
+     }
+  Status = CsrInitConsoleScreenBuffer( Console->ActiveBuffer );
+  if( !NT_SUCCESS( Status ) )
+     {
+       NtClose( Console->ActiveEvent );
+       RtlFreeHeap( CsrssApiHeap, 0, Console->ActiveBuffer );
+       return Status;
+     }
+  /* add a reference count because the buffer is tied to the console */
+  Console->ActiveBuffer->Header.ReferenceCount++;
   /* make console active, and insert into console list */
-  RtlEnterCriticalSection( &ActiveConsoleLock );
+  LOCK;
   if( ActiveConsole )
      {
        Console->Prev = ActiveConsole;
@@ -318,12 +441,10 @@ NTSTATUS CsrInitConsole(PCSRSS_PROCESS_DATA ProcessData,
      Console->Prev = Console;
      Console->Next = Console;
   }
-  Console->CurrentX = 0;
-  Console->CurrentY = 0;
   ActiveConsole = Console;
   /* copy buffer contents to screen */
-  CsrDrawConsole( Console );
-  RtlLeaveCriticalSection( &ActiveConsoleLock );
+  CsrDrawConsole( Console->ActiveBuffer );
+  UNLOCK;
   return STATUS_SUCCESS;
 }
 
@@ -331,7 +452,7 @@ NTSTATUS CsrInitConsole(PCSRSS_PROCESS_DATA ProcessData,
  *  CsrDrawConsole blasts the console buffer onto the screen   *
  *  must be called while holding the active console lock       *
  **************************************************************/
-VOID CsrDrawConsole( PCSRSS_CONSOLE Console )
+VOID CsrDrawConsole( PCSRSS_SCREEN_BUFFER Buff )
 {
    IO_STATUS_BLOCK Iosb;
    NTSTATUS Status;
@@ -342,7 +463,7 @@ VOID CsrDrawConsole( PCSRSS_CONSOLE Console )
    /* first set position to 0,0 */
    ScrInfo.dwCursorPosition.X = 0;
    ScrInfo.dwCursorPosition.Y = 0;
-   ScrInfo.wAttributes = Console->DefaultAttrib;
+   ScrInfo.wAttributes = Buff->DefaultAttrib;
    Status = NtDeviceIoControlFile( ConsoleDeviceHandle, NULL, NULL, NULL, &Iosb, IOCTL_CONSOLE_SET_SCREEN_BUFFER_INFO, &ScrInfo, sizeof( ScrInfo ), 0, 0 );
    if( !NT_SUCCESS( Status ) )
      {
@@ -357,16 +478,16 @@ VOID CsrDrawConsole( PCSRSS_CONSOLE Console )
        return;
      }
    /* blast out buffer */
-   for( i = 0, y = Console->ShowY; i < PhysicalConsoleSize.Y; i++ )
+   for( i = 0, y = Buff->ShowY; i < PhysicalConsoleSize.Y; i++ )
      {
-       Status = NtWriteFile( ConsoleDeviceHandle, NULL, NULL, NULL, &Iosb, &Console->Buffer[ (Console->ShowX * 2) + (y * Console->MaxX * 2) ], PhysicalConsoleSize.X * 2, 0, 0 );
+       Status = NtWriteFile( ConsoleDeviceHandle, NULL, NULL, NULL, &Iosb, &Buff->Buffer[ (Buff->ShowX * 2) + (y * Buff->MaxX * 2) ], PhysicalConsoleSize.X * 2, 0, 0 );
        if( !NT_SUCCESS( Status ) )
         {
           DbgPrint( "CSR: Write to console failed\n" );
           return;
         }
        /* wrap back around the end of the buffer */
-       if( ++y == Console->MaxY )
+       if( ++y == Buff->MaxY )
         y = 0;
      }
    Mode.dwMode = ENABLE_PROCESSED_OUTPUT;
@@ -376,15 +497,15 @@ VOID CsrDrawConsole( PCSRSS_CONSOLE Console )
        DbgPrint( "CSR: Failed to set console mode\n" );
        return;
      }
-   ScrInfo.dwCursorPosition.X = Console->CurrentX - Console->ShowX;
-   ScrInfo.dwCursorPosition.Y = ((Console->CurrentY + Console->MaxY) - Console->ShowY) % Console->MaxY;
+   ScrInfo.dwCursorPosition.X = Buff->CurrentX - Buff->ShowX;
+   ScrInfo.dwCursorPosition.Y = ((Buff->CurrentY + Buff->MaxY) - Buff->ShowY) % Buff->MaxY;
    Status = NtDeviceIoControlFile( ConsoleDeviceHandle, NULL, NULL, NULL, &Iosb, IOCTL_CONSOLE_SET_SCREEN_BUFFER_INFO, &ScrInfo, sizeof( ScrInfo ), 0, 0 );
    if( !NT_SUCCESS( Status ) )
      {
        DbgPrint( "CSR: Failed to set console info\n" );
        return;
      }
-   Status = NtDeviceIoControlFile( ConsoleDeviceHandle, NULL, NULL, NULL, &Iosb, IOCTL_CONSOLE_SET_CURSOR_INFO, &Console->CursorInfo, sizeof( Console->CursorInfo ), 0, 0 );
+   Status = NtDeviceIoControlFile( ConsoleDeviceHandle, NULL, NULL, NULL, &Iosb, IOCTL_CONSOLE_SET_CURSOR_INFO, &Buff->CursorInfo, sizeof( Buff->CursorInfo ), 0, 0 );
    if( !NT_SUCCESS( Status ) )
      {
        DbgPrint( "CSR: Failed to set cursor info\n" );
@@ -392,12 +513,13 @@ VOID CsrDrawConsole( PCSRSS_CONSOLE Console )
      }
 }
 
-       
-VOID CsrDeleteConsole( PCSRSS_PROCESS_DATA ProcessData, PCSRSS_CONSOLE Console )
+
+VOID CsrDeleteConsole( PCSRSS_CONSOLE Console )
 {
    ConsoleInput *Event;
-   RtlEnterCriticalSection( &ActiveConsoleLock );
-   RtlFreeHeap( CsrssApiHeap, 0, Console->Buffer );
+   DPRINT1( "CsrDeleteConsole\n" );
+   LOCK;
+   /* Drain input event queue */
    while( Console->InputEvents.Flink != &Console->InputEvents )
       {
         Event = (ConsoleInput *)Console->InputEvents.Flink;
@@ -405,6 +527,7 @@ VOID CsrDeleteConsole( PCSRSS_PROCESS_DATA ProcessData, PCSRSS_CONSOLE Console )
         Console->InputEvents.Flink->Flink->Blink = &Console->InputEvents;
         RtlFreeHeap( CsrssApiHeap, 0, Event );
       }
+   /* Switch to next console */
    if( ActiveConsole == Console )
       {
         if( Console->Next != Console )
@@ -416,9 +539,13 @@ VOID CsrDeleteConsole( PCSRSS_PROCESS_DATA ProcessData, PCSRSS_CONSOLE Console )
         else ActiveConsole = 0;
       }
    if( ActiveConsole )
-     CsrDrawConsole( ActiveConsole );
-   RtlLeaveCriticalSection( &ActiveConsoleLock );
+     CsrDrawConsole( ActiveConsole->ActiveBuffer );
+   UNLOCK;
+   if( !--Console->ActiveBuffer->Header.ReferenceCount )
+     CsrDeleteScreenBuffer( Console->ActiveBuffer );
    NtClose( Console->ActiveEvent );
+   RtlFreeUnicodeString( &Console->Title );
+   RtlFreeHeap( CsrssApiHeap, 0, Console );
 }
 
 VOID CsrInitConsoleSupport(VOID)
@@ -460,7 +587,7 @@ VOID CsrInitConsoleSupport(VOID)
                       &ObjectAttributes,
                       &Iosb,
                       0,
-                      FILE_SYNCHRONOUS_IO_ALERT);
+                      0);
    if (!NT_SUCCESS(Status))
      {
        DbgPrint("CSR: Failed to open keyboard. Expect problems.\n");
@@ -477,17 +604,31 @@ VOID CsrInitConsoleSupport(VOID)
    PhysicalConsoleSize = ScrInfo.dwSize;
 }
 
-VOID Console_Api( DWORD Ignored )
+VOID Console_Api( DWORD RefreshEvent )
 {
   /* keep reading events from the keyboard and stuffing them into the current
      console's input queue */
   ConsoleInput *KeyEventRecord;
+  ConsoleInput *TempInput;
   IO_STATUS_BLOCK Iosb;
   NTSTATUS Status;
+  HANDLE Events[2];     // 0 = keyboard, 1 = refresh
+  int c;
+  int updown;
+  PCSRSS_CONSOLE SwapConsole = 0; // console we are thinking about swapping with
+
+  Events[0] = 0;
+  Status = NtCreateEvent( &Events[0], STANDARD_RIGHTS_ALL, NULL, FALSE, FALSE );
+  if( !NT_SUCCESS( Status ) )
+    {
+      DbgPrint( "CSR: NtCreateEvent failed: %x\n", Status );
+      NtTerminateProcess( NtCurrentProcess(), Status );
+    }
+  Events[1] = (HANDLE)RefreshEvent;
   while( 1 )
     {
       KeyEventRecord = RtlAllocateHeap(CsrssApiHeap, 
-                                      0, 
+                                      0,
                                       sizeof(ConsoleInput));
        if ( KeyEventRecord == 0 )
        {
@@ -495,79 +636,249 @@ VOID Console_Api( DWORD Ignored )
          continue;
        }
       KeyEventRecord->InputEvent.EventType = KEY_EVENT;
-      Status = NtReadFile( KeyboardDeviceHandle, NULL, NULL, NULL, &Iosb, &KeyEventRecord->InputEvent.Event.KeyEvent, sizeof( KEY_EVENT_RECORD ), NULL, 0 );
+      Status = NtReadFile( KeyboardDeviceHandle, Events[0], NULL, NULL, &Iosb, &KeyEventRecord->InputEvent.Event.KeyEvent, sizeof( KEY_EVENT_RECORD ), NULL, 0 );
       if( !NT_SUCCESS( Status ) )
        {
          DbgPrint( "CSR: ReadFile on keyboard device failed\n" );
          RtlFreeHeap( CsrssApiHeap, 0, KeyEventRecord );
          continue;
        }
-      //      DbgPrint( "Char: %c\n", KeyEventRecord->InputEvent.Event.KeyEvent.uChar.AsciiChar );
-      if( KeyEventRecord->InputEvent.Event.KeyEvent.dwControlKeyState & ( RIGHT_ALT_PRESSED | LEFT_ALT_PRESSED )&&  KeyEventRecord->InputEvent.Event.KeyEvent.uChar.AsciiChar == 'q' )
+      if( Status == STATUS_PENDING )
+       {
+         while( 1 )
+           {
+             Status = NtWaitForMultipleObjects( 2, Events, WaitAny, FALSE, NULL );
+             if( Status == STATUS_WAIT_0 + 1 )
+               {
+                 LOCK;
+                 CsrDrawConsole( ActiveConsole->ActiveBuffer );
+                 UNLOCK;
+                 continue;
+               }
+             else if( Status != STATUS_WAIT_0 )
+               {
+                 DbgPrint( "CSR: NtWaitForMultipleObjects failed: %x, exiting\n", Status );
+                 NtTerminateProcess( NtCurrentProcess(), Status );
+               }
+             else break;
+           }
+       }
+      if( KeyEventRecord->InputEvent.Event.KeyEvent.dwControlKeyState & ( RIGHT_ALT_PRESSED | LEFT_ALT_PRESSED )&&  KeyEventRecord->InputEvent.Event.KeyEvent.wVirtualKeyCode == VK_TAB )
         if( KeyEventRecord->InputEvent.Event.KeyEvent.bKeyDown == TRUE )
            {
+             ANSI_STRING Title;
+             void * Buffer;
+             COORD *pos;
+             unsigned int src, dst;
+             
               /* alt-tab, swap consoles */
-              RtlEnterCriticalSection( &ActiveConsoleLock );
-              if( ActiveConsole->Next != ActiveConsole )
-                 ActiveConsole = ActiveConsole->Next;
-              CsrDrawConsole( ActiveConsole );
-              RtlLeaveCriticalSection( &ActiveConsoleLock );
-              RtlFreeHeap( CsrssApiHeap, 0, KeyEventRecord );
-              continue;
+              // move SwapConsole to next console, and print its title
+             LOCK;
+             if( !SwapConsole )
+               SwapConsole = ActiveConsole;
+             
+             if( KeyEventRecord->InputEvent.Event.KeyEvent.dwControlKeyState & SHIFT_PRESSED )
+               SwapConsole = SwapConsole->Prev;
+             else SwapConsole = SwapConsole->Next;
+             Title.MaximumLength = RtlUnicodeStringToAnsiSize( &SwapConsole->Title );
+             Title.Length = 0;
+             Buffer = RtlAllocateHeap( CsrssApiHeap,
+                                       0,
+                                       sizeof( COORD ) + Title.MaximumLength );
+             pos = (COORD *)Buffer;
+             Title.Buffer = Buffer + sizeof( COORD );
+
+             /* this does not seem to work
+                RtlUnicodeStringToAnsiString( &Title, &SwapConsole->Title, FALSE ); */
+             // temp hack
+             for( src = 0, dst = 0; src < SwapConsole->Title.Length; src++, dst++ )
+               Title.Buffer[dst] = (char)SwapConsole->Title.Buffer[dst];
+             
+             pos->Y = PhysicalConsoleSize.Y / 2;
+             pos->X = ( PhysicalConsoleSize.X - Title.MaximumLength ) / 2;
+             // redraw the console to clear off old title
+             CsrDrawConsole( ActiveConsole->ActiveBuffer );
+             Status = NtDeviceIoControlFile( ConsoleDeviceHandle,
+                                             NULL,
+                                             NULL,
+                                             NULL,
+                                             &Iosb,
+                                             IOCTL_CONSOLE_WRITE_OUTPUT_CHARACTER,
+                                             0,
+                                             0,
+                                             Buffer,
+                                             sizeof (COORD) + Title.MaximumLength );
+             if( !NT_SUCCESS( Status ) )
+               {
+                 DPRINT1( "Error writing to console\n" );
+               }
+             RtlFreeHeap( CsrssApiHeap, 0, Buffer );
+             
+             UNLOCK;
+             RtlFreeHeap( CsrssApiHeap, 0, KeyEventRecord );
+             continue;
            }
         else {
            RtlFreeHeap( CsrssApiHeap, 0, KeyEventRecord );
            continue;
         }
-      else if( KeyEventRecord->InputEvent.Event.KeyEvent.dwControlKeyState & ( RIGHT_ALT_PRESSED | LEFT_ALT_PRESSED ) && (KeyEventRecord->InputEvent.Event.KeyEvent.wVirtualKeyCode == VK_UP || KeyEventRecord->InputEvent.Event.KeyEvent.wVirtualKeyCode == VK_DOWN) )
+      else if( SwapConsole &&
+              KeyEventRecord->InputEvent.Event.KeyEvent.wVirtualKeyCode == VK_MENU &&
+              KeyEventRecord->InputEvent.Event.KeyEvent.bKeyDown == FALSE )
+       {
+         // alt key released, swap consoles
+         PCSRSS_CONSOLE tmp;
+
+         LOCK;
+         if( SwapConsole != ActiveConsole )
+           {
+             // first remove swapconsole from the list
+             SwapConsole->Prev->Next = SwapConsole->Next;
+             SwapConsole->Next->Prev = SwapConsole->Prev;
+             // now insert before activeconsole
+             SwapConsole->Next = ActiveConsole;
+             SwapConsole->Prev = ActiveConsole->Prev;
+             ActiveConsole->Prev->Next = SwapConsole;
+             ActiveConsole->Prev = SwapConsole;
+           }
+         ActiveConsole = SwapConsole;
+         SwapConsole = 0;
+         CsrDrawConsole( ActiveConsole->ActiveBuffer );
+
+         UNLOCK;
+       }
+      if( KeyEventRecord->InputEvent.Event.KeyEvent.dwControlKeyState & ( RIGHT_ALT_PRESSED | LEFT_ALT_PRESSED ) && (KeyEventRecord->InputEvent.Event.KeyEvent.wVirtualKeyCode == VK_UP || KeyEventRecord->InputEvent.Event.KeyEvent.wVirtualKeyCode == VK_DOWN) )
         {
            if( KeyEventRecord->InputEvent.Event.KeyEvent.bKeyDown == TRUE )
               {
                  /* scroll up or down */
-                 RtlEnterCriticalSection( &ActiveConsoleLock );
+                 LOCK;
                  if( ActiveConsole == 0 )
                     {
                        DbgPrint( "CSR: No Active Console!\n" );
+                       UNLOCK;
                        RtlFreeHeap( CsrssApiHeap, 0, KeyEventRecord );
                        continue;
                     }
                  if( KeyEventRecord->InputEvent.Event.KeyEvent.wVirtualKeyCode == VK_UP )
                     {
                        /* only scroll up if there is room to scroll up into */
-                       if( ActiveConsole->ShowY != ((ActiveConsole->CurrentY + 1) % ActiveConsole->MaxY) )
-                          ActiveConsole->ShowY = (ActiveConsole->ShowY + ActiveConsole->MaxY - 1) % ActiveConsole->MaxY;
+                       if( ActiveConsole->ActiveBuffer->ShowY != ((ActiveConsole->ActiveBuffer->CurrentY + 1) % ActiveConsole->ActiveBuffer->MaxY) )
+                          ActiveConsole->ActiveBuffer->ShowY = (ActiveConsole->ActiveBuffer->ShowY + ActiveConsole->ActiveBuffer->MaxY - 1) % ActiveConsole->ActiveBuffer->MaxY;
                     }
-                 else if( ActiveConsole->ShowY != ActiveConsole->CurrentY )
+                 else if( ActiveConsole->ActiveBuffer->ShowY != ActiveConsole->ActiveBuffer->CurrentY )
                     /* only scroll down if there is room to scroll down into */
-                    if( ActiveConsole->ShowY % ActiveConsole->MaxY != ActiveConsole->CurrentY )
-                       if( ((ActiveConsole->CurrentY + 1) % ActiveConsole->MaxY) != (ActiveConsole->ShowY + PhysicalConsoleSize.Y) % ActiveConsole->MaxY )
-                          ActiveConsole->ShowY = (ActiveConsole->ShowY + 1) % ActiveConsole->MaxY;
-                 CsrDrawConsole( ActiveConsole );
-                 RtlLeaveCriticalSection( &ActiveConsoleLock );
+                    if( ActiveConsole->ActiveBuffer->ShowY % ActiveConsole->ActiveBuffer->MaxY != ActiveConsole->ActiveBuffer->CurrentY )
+                       if( ((ActiveConsole->ActiveBuffer->CurrentY + 1) % ActiveConsole->ActiveBuffer->MaxY) != (ActiveConsole->ActiveBuffer->ShowY + PhysicalConsoleSize.Y) % ActiveConsole->ActiveBuffer->MaxY )
+                          ActiveConsole->ActiveBuffer->ShowY = (ActiveConsole->ActiveBuffer->ShowY + 1) % ActiveConsole->ActiveBuffer->MaxY;
+                 CsrDrawConsole( ActiveConsole->ActiveBuffer );
+                 UNLOCK;
               }
            RtlFreeHeap( CsrssApiHeap, 0, KeyEventRecord );
            continue;
        }
-      RtlEnterCriticalSection( &ActiveConsoleLock );
+      LOCK;
       if( ActiveConsole == 0 )
         {
            DbgPrint( "CSR: No Active Console!\n" );
+           UNLOCK;
            RtlFreeHeap( CsrssApiHeap, 0, KeyEventRecord );
            continue;
         }
+      // process special keys if enabled
+      if( ActiveConsole->Mode & (ENABLE_PROCESSED_INPUT | ENABLE_LINE_INPUT) )
+         switch( KeyEventRecord->InputEvent.Event.KeyEvent.uChar.AsciiChar )
+           {
+           case '\r':
+             // add a \n to the queue as well
+             // first add the \r
+             updown = KeyEventRecord->InputEvent.Event.KeyEvent.bKeyDown;
+             KeyEventRecord->Echoed = FALSE;
+             KeyEventRecord->InputEvent.Event.KeyEvent.uChar.AsciiChar = '\r';
+             KeyEventRecord->ListEntry.Flink = &ActiveConsole->InputEvents;
+             KeyEventRecord->ListEntry.Blink = ActiveConsole->InputEvents.Blink;
+             ActiveConsole->InputEvents.Blink->Flink = &KeyEventRecord->ListEntry;
+             ActiveConsole->InputEvents.Blink = &KeyEventRecord->ListEntry;
+             ActiveConsole->WaitingChars++;
+             KeyEventRecord = RtlAllocateHeap( CsrssApiHeap, 0, sizeof( ConsoleInput ) );
+             if( !KeyEventRecord )
+               {
+                 DbgPrint( "CSR: Failed to allocate KeyEventRecord\n" );
+                 UNLOCK;
+                 continue;
+               }
+             KeyEventRecord->InputEvent.EventType = KEY_EVENT;
+             KeyEventRecord->InputEvent.Event.KeyEvent.bKeyDown = updown;
+             KeyEventRecord->InputEvent.Event.KeyEvent.wVirtualKeyCode = 0;
+             KeyEventRecord->InputEvent.Event.KeyEvent.wVirtualScanCode = 0;
+             KeyEventRecord->InputEvent.Event.KeyEvent.uChar.AsciiChar = '\n';
+           }
+      // add event to the queue
       KeyEventRecord->ListEntry.Flink = &ActiveConsole->InputEvents;
       KeyEventRecord->ListEntry.Blink = ActiveConsole->InputEvents.Blink;
       ActiveConsole->InputEvents.Blink->Flink = &KeyEventRecord->ListEntry;
       ActiveConsole->InputEvents.Blink = &KeyEventRecord->ListEntry;
-      NtSetEvent( ActiveConsole->ActiveEvent, 0 );
-      RtlLeaveCriticalSection( &ActiveConsoleLock );
+      // if line input mode is enabled, only wake the client on enter key down
+      if( !(ActiveConsole->Mode & ENABLE_LINE_INPUT ) ||
+         ( KeyEventRecord->InputEvent.Event.KeyEvent.uChar.AsciiChar == '\n' &&
+           KeyEventRecord->InputEvent.Event.KeyEvent.bKeyDown == TRUE ) )
+       {
+         NtSetEvent( ActiveConsole->ActiveEvent, 0 );
+         if( KeyEventRecord->InputEvent.Event.KeyEvent.uChar.AsciiChar == '\n' )
+            ActiveConsole->WaitingLines++;
+       }
+      KeyEventRecord->Echoed = FALSE;
+      if( ActiveConsole->Mode & (ENABLE_PROCESSED_INPUT | ENABLE_LINE_INPUT) &&
+         KeyEventRecord->InputEvent.Event.KeyEvent.uChar.AsciiChar == '\b' &&
+         KeyEventRecord->InputEvent.Event.KeyEvent.bKeyDown )
+        {
+           // walk the input queue looking for a char to backspace
+           for( TempInput = (ConsoleInput *)ActiveConsole->InputEvents.Blink;
+                TempInput != (ConsoleInput *)&ActiveConsole->InputEvents &&
+                (TempInput->InputEvent.EventType != KEY_EVENT ||
+                TempInput->InputEvent.Event.KeyEvent.bKeyDown == FALSE ||
+                TempInput->InputEvent.Event.KeyEvent.uChar.AsciiChar == '\b' );
+                TempInput = (ConsoleInput *)TempInput->ListEntry.Blink );
+           // if we found one, delete it, otherwise, wake the client
+           if( TempInput != (ConsoleInput *)&ActiveConsole->InputEvents )
+              {
+                 // delete previous key in queue, maybe echo backspace to screen, and do not place backspace on queue
+                 TempInput->ListEntry.Blink->Flink = TempInput->ListEntry.Flink;
+                 TempInput->ListEntry.Flink->Blink = TempInput->ListEntry.Blink;
+                 if( TempInput->Echoed )
+                    CsrpWriteConsole( ActiveConsole->ActiveBuffer, &KeyEventRecord->InputEvent.Event.KeyEvent.uChar.AsciiChar, 1, TRUE );
+                 RtlFreeHeap( CsrssApiHeap, 0, TempInput );
+                 KeyEventRecord->ListEntry.Blink->Flink = KeyEventRecord->ListEntry.Flink;
+                 KeyEventRecord->ListEntry.Flink->Blink = KeyEventRecord->ListEntry.Blink;
+                 RtlFreeHeap( CsrssApiHeap, 0, KeyEventRecord );
+                 ActiveConsole->WaitingChars -= 2;
+              }
+           else NtSetEvent( ActiveConsole->ActiveEvent, 0 );
+        }
+      else {
+        // echo chars if we are supposed to and client is waiting for some
+        if( ActiveConsole->Mode & ENABLE_ECHO_INPUT && ActiveConsole->EchoCount &&
+            KeyEventRecord->InputEvent.Event.KeyEvent.uChar.AsciiChar &&
+            KeyEventRecord->InputEvent.Event.KeyEvent.bKeyDown == TRUE &&
+            KeyEventRecord->InputEvent.Event.KeyEvent.uChar.AsciiChar != '\r' )
+           {
+              // mark the char as already echoed
+              CsrpWriteConsole( ActiveConsole->ActiveBuffer, &KeyEventRecord->InputEvent.Event.KeyEvent.uChar.AsciiChar, 1, TRUE );
+              ActiveConsole->EchoCount--;
+              KeyEventRecord->Echoed = TRUE;
+           }
+      }
+      ActiveConsole->WaitingChars++;
+      if( !(ActiveConsole->Mode & ENABLE_LINE_INPUT) )
+       NtSetEvent( ActiveConsole->ActiveEvent, 0 );
+      UNLOCK;
     }
 }
 
 NTSTATUS CsrGetScreenBufferInfo( PCSRSS_PROCESS_DATA ProcessData, PCSRSS_API_REQUEST Request, PCSRSS_API_REPLY Reply )
 {
    NTSTATUS Status;
-   PCSRSS_CONSOLE Console;
+   PCSRSS_SCREEN_BUFFER Buff;
    PCONSOLE_SCREEN_BUFFER_INFO pInfo;
    IO_STATUS_BLOCK Iosb;
    
@@ -575,37 +886,40 @@ NTSTATUS CsrGetScreenBufferInfo( PCSRSS_PROCESS_DATA ProcessData, PCSRSS_API_REQ
    Reply->Header.DataSize = sizeof(CSRSS_API_REPLY) -
      sizeof(LPC_MESSAGE_HEADER);
 
-   if( !NT_SUCCESS( CsrGetObject( ProcessData, Request->Data.ScreenBufferInfoRequest.ConsoleHandle, (Object_t **)&Console ) ) )
-     return Reply->Status = STATUS_INVALID_HANDLE;
+   LOCK;
+   if( !NT_SUCCESS( CsrGetObject( ProcessData, Request->Data.ScreenBufferInfoRequest.ConsoleHandle, (Object_t **)&Buff ) ) || Buff->Header.Type != CSRSS_SCREEN_BUFFER_MAGIC )
+      {
+        UNLOCK;
+        return Reply->Status = STATUS_INVALID_HANDLE;
+      }
    pInfo = &Reply->Data.ScreenBufferInfoReply.Info;
-   RtlEnterCriticalSection( &ActiveConsoleLock );   
-   if( Console == ActiveConsole )
+   if( Buff == ActiveConsole->ActiveBuffer )
      {
        Status = NtDeviceIoControlFile( ConsoleDeviceHandle, NULL, NULL, NULL, &Iosb, IOCTL_CONSOLE_GET_SCREEN_BUFFER_INFO, 0, 0, pInfo, sizeof( *pInfo ) );
        if( !NT_SUCCESS( Status ) )
           DbgPrint( "CSR: Failed to get console info, expect trouble\n" );
-       Reply->Status = STATUS_SUCCESS;
+       Reply->Status = Status;
      }
    else {
       pInfo->dwSize.X = PhysicalConsoleSize.X;
       pInfo->dwSize.Y = PhysicalConsoleSize.Y;
-      pInfo->dwCursorPosition.X = Console->CurrentX - Console->ShowX;
-      pInfo->dwCursorPosition.Y = (Console->CurrentY + Console->MaxY - Console->ShowY) % Console->MaxY;
-      pInfo->wAttributes = Console->DefaultAttrib;
+      pInfo->dwCursorPosition.X = Buff->CurrentX - Buff->ShowX;
+      pInfo->dwCursorPosition.Y = (Buff->CurrentY + Buff->MaxY - Buff->ShowY) % Buff->MaxY;
+      pInfo->wAttributes = Buff->DefaultAttrib;
       pInfo->srWindow.Left = 0;
       pInfo->srWindow.Right = PhysicalConsoleSize.X - 1;
       pInfo->srWindow.Top = 0;
       pInfo->srWindow.Bottom = PhysicalConsoleSize.Y - 1;
       Reply->Status = STATUS_SUCCESS;
    }
-   RtlLeaveCriticalSection( &ActiveConsoleLock );
+   UNLOCK;
    return Reply->Status;
 }
 
 NTSTATUS CsrSetCursor( PCSRSS_PROCESS_DATA ProcessData, PCSRSS_API_REQUEST Request, PCSRSS_API_REPLY Reply )
 {
    NTSTATUS Status;
-   PCSRSS_CONSOLE Console;
+   PCSRSS_SCREEN_BUFFER Buff;
    CONSOLE_SCREEN_BUFFER_INFO Info;
    IO_STATUS_BLOCK Iosb;
    
@@ -613,75 +927,104 @@ NTSTATUS CsrSetCursor( PCSRSS_PROCESS_DATA ProcessData, PCSRSS_API_REQUEST Reque
    Reply->Header.DataSize = sizeof(CSRSS_API_REPLY) -
      sizeof(LPC_MESSAGE_HEADER);
 
-   if( !NT_SUCCESS( CsrGetObject( ProcessData, Request->Data.SetCursorRequest.ConsoleHandle, (Object_t **)&Console ) ) )
-     return Reply->Status = STATUS_INVALID_HANDLE;
-   RtlEnterCriticalSection( &ActiveConsoleLock );
+   LOCK;
+   if( !NT_SUCCESS( CsrGetObject( ProcessData, Request->Data.SetCursorRequest.ConsoleHandle, (Object_t **)&Buff ) ) || Buff->Header.Type != CSRSS_SCREEN_BUFFER_MAGIC )
+      {
+        UNLOCK;
+        return Reply->Status = STATUS_INVALID_HANDLE;
+      }
    Info.dwCursorPosition = Request->Data.SetCursorRequest.Position;
-   Info.wAttributes = Console->DefaultAttrib;
-   if( Console == ActiveConsole )
+   Info.wAttributes = Buff->DefaultAttrib;
+   if( Buff == ActiveConsole->ActiveBuffer )
       {
         Status = NtDeviceIoControlFile( ConsoleDeviceHandle, NULL, NULL, NULL, &Iosb, IOCTL_CONSOLE_SET_SCREEN_BUFFER_INFO, &Info, sizeof( Info ), 0, 0 );
        if( !NT_SUCCESS( Status ) )
           DbgPrint( "CSR: Failed to set console info, expect trouble\n" );
       }
-   Console->CurrentX = Info.dwCursorPosition.X + Console->ShowX;
-   Console->CurrentY = (Info.dwCursorPosition.Y + Console->ShowY) % Console->MaxY;
-   RtlLeaveCriticalSection( &ActiveConsoleLock );
+   Buff->CurrentX = Info.dwCursorPosition.X + Buff->ShowX;
+   Buff->CurrentY = (Info.dwCursorPosition.Y + Buff->ShowY) % Buff->MaxY;
+   UNLOCK;
    return Reply->Status = Status;
 }
 
 NTSTATUS CsrWriteConsoleOutputChar( PCSRSS_PROCESS_DATA ProcessData, PCSRSS_API_REQUEST Request, PCSRSS_API_REPLY Reply )
 {
    BYTE *Buffer = Request->Data.WriteConsoleOutputCharRequest.String;
-   PCSRSS_CONSOLE Console;
+   PCSRSS_SCREEN_BUFFER Buff;
    DWORD X, Y;
+   NTSTATUS Status;
+   IO_STATUS_BLOCK Iosb;
    
    Reply->Header.MessageSize = sizeof(CSRSS_API_REPLY);
    Reply->Header.DataSize = sizeof(CSRSS_API_REPLY) -
      sizeof(LPC_MESSAGE_HEADER);
-
-   if( !NT_SUCCESS( CsrGetObject( ProcessData, Request->Data.WriteConsoleOutputCharRequest.ConsoleHandle, (Object_t **)&Console ) ) )
-     return Reply->Status = STATUS_INVALID_HANDLE;
-   RtlEnterCriticalSection( &ActiveConsoleLock );
-   X = Console->CurrentX;
-   Y = Console->CurrentY;
-   CsrpWriteConsole( Console, Buffer, Request->Data.WriteConsoleOutputCharRequest.Length, TRUE );
-   Reply->Data.WriteConsoleOutputCharReply.EndCoord.X = Console->CurrentX - Console->ShowX;
-   Reply->Data.WriteConsoleOutputCharReply.EndCoord.Y = (Console->CurrentY + Console->MaxY - Console->ShowY) % Console->MaxY;
-   Console->CurrentY = Y;
-   Console->CurrentX = X;
-   RtlLeaveCriticalSection( &ActiveConsoleLock );
+   LOCK;
+   if( !NT_SUCCESS( CsrGetObject( ProcessData, Request->Data.WriteConsoleOutputCharRequest.ConsoleHandle, (Object_t **)&Buff ) ) || Buff->Header.Type != CSRSS_SCREEN_BUFFER_MAGIC )
+      {
+        UNLOCK;
+        return Reply->Status = STATUS_INVALID_HANDLE;
+      }
+   X = Buff->CurrentX;
+   Y = Buff->CurrentY;
+   Buff->CurrentX = Request->Data.WriteConsoleOutputCharRequest.Coord.X;
+   Buff->CurrentY = Request->Data.WriteConsoleOutputCharRequest.Coord.Y;
+   Buffer[Request->Data.WriteConsoleOutputCharRequest.Length] = 0;
+   CsrpWriteConsole( Buff, Buffer, Request->Data.WriteConsoleOutputCharRequest.Length, FALSE );
+   if( ActiveConsole->ActiveBuffer == Buff )
+     {
+       Status = NtDeviceIoControlFile( ConsoleDeviceHandle,
+                                      NULL,
+                                      NULL,
+                                      NULL,
+                                      &Iosb,
+                                      IOCTL_CONSOLE_WRITE_OUTPUT_CHARACTER,
+                                      0,
+                                      0,
+                                      &Request->Data.WriteConsoleOutputCharRequest.Coord,
+                                      sizeof (COORD) + Request->Data.WriteConsoleOutputCharRequest.Length );
+       if( !NT_SUCCESS( Status ) )
+        DPRINT1( "Failed to write output chars: %x\n", Status );
+     }
+   Reply->Data.WriteConsoleOutputCharReply.EndCoord.X = Buff->CurrentX - Buff->ShowX;
+   Reply->Data.WriteConsoleOutputCharReply.EndCoord.Y = (Buff->CurrentY + Buff->MaxY - Buff->ShowY) % Buff->MaxY;
+   Buff->CurrentY = Y;
+   Buff->CurrentX = X;
+   UNLOCK;
    return Reply->Status = STATUS_SUCCESS;
 }
 
 NTSTATUS CsrFillOutputChar( PCSRSS_PROCESS_DATA ProcessData, PCSRSS_API_REQUEST Request, PCSRSS_API_REPLY Reply )
 {
-   PCSRSS_CONSOLE Console;
+   PCSRSS_SCREEN_BUFFER Buff;
    DWORD X, Y, i;
    
    Reply->Header.MessageSize = sizeof(CSRSS_API_REPLY);
    Reply->Header.DataSize = sizeof(CSRSS_API_REPLY) -
      sizeof(LPC_MESSAGE_HEADER);
 
-   if( !NT_SUCCESS( CsrGetObject( ProcessData, Request->Data.FillOutputRequest.ConsoleHandle, (Object_t **)&Console ) ) )
-     return Reply->Status = STATUS_INVALID_HANDLE;
-   RtlEnterCriticalSection( &ActiveConsoleLock );
-   X = Request->Data.FillOutputRequest.Position.X + Console->ShowX;
-   Y = Request->Data.FillOutputRequest.Position.Y + Console->ShowY;
+   LOCK;
+   if( !NT_SUCCESS( CsrGetObject( ProcessData, Request->Data.FillOutputRequest.ConsoleHandle, (Object_t **)&Buff ) ) || Buff->Header.Type != CSRSS_SCREEN_BUFFER_MAGIC )
+      {
+        UNLOCK;
+        return Reply->Status = STATUS_INVALID_HANDLE;
+      }
+   X = Request->Data.FillOutputRequest.Position.X + Buff->ShowX;
+   Y = Request->Data.FillOutputRequest.Position.Y + Buff->ShowY;
    for( i = 0; i < 20000; i++ );
    for( i = 0; i < Request->Data.FillOutputRequest.Length; i++ )
       {
-        Console->Buffer[ (Y * 2 * Console->MaxX) + (X * 2) ] = Request->Data.FillOutputRequest.Char;
-        if( ++X == Console->MaxX )
+        Buff->Buffer[ (Y * 2 * Buff->MaxX) + (X * 2) ] = Request->Data.FillOutputRequest.Char;
+        if( ++X == Buff->MaxX )
            {
-              if( ++Y == Console->MaxY )
+              if( ++Y == Buff->MaxY )
                  Y = 0;
               X = 0;
            }
       }
-   if( Console == ActiveConsole )
-      CsrDrawConsole( Console );
-   RtlLeaveCriticalSection( &ActiveConsoleLock );
+   if( Buff == ActiveConsole->ActiveBuffer )
+      CsrDrawConsole( Buff );
+   UNLOCK;
+   return Reply->Status;
 }
 
 NTSTATUS CsrReadInputEvent( PCSRSS_PROCESS_DATA ProcessData, PCSRSS_API_REQUEST Request, PCSRSS_API_REPLY Reply )
@@ -690,163 +1033,206 @@ NTSTATUS CsrReadInputEvent( PCSRSS_PROCESS_DATA ProcessData, PCSRSS_API_REQUEST
    NTSTATUS Status;
    ConsoleInput *Input;
    
-//   DbgPrint("CSR: NrCharactersToRead %d\n", nNumberOfCharsToRead);
-   
    Reply->Header.MessageSize = sizeof(CSRSS_API_REPLY);
    Reply->Header.DataSize = sizeof(CSRSS_API_REPLY) -
       sizeof(LPC_MESSAGE_HEADER);
    Reply->Data.ReadInputReply.Event = ProcessData->ConsoleEvent;
    
+   LOCK;
    Status = CsrGetObject( ProcessData, Request->Data.ReadInputRequest.ConsoleHandle, (Object_t **)&Console );
-   if( !NT_SUCCESS( Status ) )
+   if( !NT_SUCCESS( Status ) || (Status = Console->Header.Type == CSRSS_CONSOLE_MAGIC ? 0 : STATUS_INVALID_HANDLE))
       {
         Reply->Status = Status;
+        UNLOCK;
         return Status;
       }
-   RtlEnterCriticalSection( &ActiveConsoleLock );
-   if( Console->InputEvents.Flink != &Console->InputEvents )     
+   // only get input if there is input, and we are not in line input mode, or if we are, if we have a whole line
+   if( Console->InputEvents.Flink != &Console->InputEvents &&
+       ( !Console->Mode & ENABLE_LINE_INPUT || Console->WaitingLines ) )     
      {
        Input = (ConsoleInput *)Console->InputEvents.Flink;
        Input->ListEntry.Blink->Flink = Input->ListEntry.Flink;
        Input->ListEntry.Flink->Blink = Input->ListEntry.Blink;
        Reply->Data.ReadInputReply.Input = Input->InputEvent;
+       if( Console->Mode & ENABLE_LINE_INPUT &&
+           Input->InputEvent.Event.KeyEvent.bKeyDown == FALSE &&
+           Input->InputEvent.Event.KeyEvent.uChar.AsciiChar == '\n' )
+         Console->WaitingLines--;
+       Console->WaitingChars--;
        RtlFreeHeap( CsrssApiHeap, 0, Input );
        Reply->Data.ReadInputReply.MoreEvents = (Console->InputEvents.Flink != &Console->InputEvents) ? TRUE : FALSE;
        Status = STATUS_SUCCESS;
      }
    else Status = STATUS_PENDING;
-   RtlLeaveCriticalSection( &ActiveConsoleLock );
+   UNLOCK;
    return Reply->Status = Status;
 }
 
 NTSTATUS CsrWriteConsoleOutputAttrib( PCSRSS_PROCESS_DATA ProcessData, PCSRSS_API_REQUEST Request, PCSRSS_API_REPLY Reply )
 {
    int c;
-   PCSRSS_CONSOLE Console;
+   PCSRSS_SCREEN_BUFFER Buff;
    NTSTATUS Status;
    int X, Y;
+   IO_STATUS_BLOCK Iosb;
    
    Reply->Header.MessageSize = sizeof(CSRSS_API_REPLY);
    Reply->Header.DataSize = sizeof(CSRSS_API_REPLY) -
       sizeof(LPC_MESSAGE_HEADER);
-   Status = CsrGetObject( ProcessData, Request->Data.WriteConsoleOutputAttribRequest.ConsoleHandle, (Object_t **)&Console );
-   if( !NT_SUCCESS( Status ) )
+   LOCK;
+   Status = CsrGetObject( ProcessData, Request->Data.WriteConsoleOutputAttribRequest.ConsoleHandle, (Object_t **)&Buff );
+   if( !NT_SUCCESS( Status ) || (Status = Buff->Header.Type == CSRSS_SCREEN_BUFFER_MAGIC ? 0 : STATUS_INVALID_HANDLE ))
       {
         Reply->Status = Status;
+        UNLOCK;
         return Status;
       }
-   RtlEnterCriticalSection( &ActiveConsoleLock );
-   X = Console->CurrentX;
-   Y = Console->CurrentY;
-   Console->CurrentX = Request->Data.WriteConsoleOutputAttribRequest.Coord.X + Console->ShowX;
-   Console->CurrentY = (Request->Data.WriteConsoleOutputAttribRequest.Coord.Y + Console->ShowY) % Console->MaxY;
+   X = Buff->CurrentX;
+   Y = Buff->CurrentY;
+   Buff->CurrentX = Request->Data.WriteConsoleOutputAttribRequest.Coord.X + Buff->ShowX;
+   Buff->CurrentY = (Request->Data.WriteConsoleOutputAttribRequest.Coord.Y + Buff->ShowY) % Buff->MaxY;
    for( c = 0; c < Request->Data.WriteConsoleOutputAttribRequest.Length; c++ )
       {
-        Console->Buffer[(Console->CurrentY * Console->MaxX * 2) + (Console->CurrentX * 2) + 1] = Request->Data.WriteConsoleOutputAttribRequest.String[c];
-        if( ++Console->CurrentX == Console->MaxX )
+        Buff->Buffer[(Buff->CurrentY * Buff->MaxX * 2) + (Buff->CurrentX * 2) + 1] = Request->Data.WriteConsoleOutputAttribRequest.String[c];
+        if( ++Buff->CurrentX == Buff->MaxX )
            {
-              Console->CurrentX = 0;
-              if( ++Console->CurrentY == Console->MaxY )
-                 Console->CurrentY = 0;
+              Buff->CurrentX = 0;
+              if( ++Buff->CurrentY == Buff->MaxY )
+                 Buff->CurrentY = 0;
            }
       }
-   if( Console == ActiveConsole )
-      CsrDrawConsole( Console );
-   Reply->Data.WriteConsoleOutputAttribReply.EndCoord.X = Console->CurrentX - Console->ShowX;
-   Reply->Data.WriteConsoleOutputAttribReply.EndCoord.Y = ( Console->CurrentY + Console->MaxY - Console->ShowY ) % Console->MaxY;
-   Console->CurrentX = X;
-   Console->CurrentY = Y;
-   RtlLeaveCriticalSection( &ActiveConsoleLock );
+   if( Buff == ActiveConsole->ActiveBuffer )
+      {
+       Status = NtDeviceIoControlFile( ConsoleDeviceHandle,
+                                       NULL,
+                                       NULL,
+                                       NULL,
+                                       &Iosb,
+                                       IOCTL_CONSOLE_WRITE_OUTPUT_ATTRIBUTE,
+                                       0,
+                                       0,
+                                       &Request->Data.WriteConsoleOutputAttribRequest.Coord,
+                                       Request->Data.WriteConsoleOutputAttribRequest.Length +
+                                       sizeof (COORD) );
+       if( !NT_SUCCESS( Status ) )
+         DPRINT1( "Failed to write output attributes to console\n" );
+      }
+   Reply->Data.WriteConsoleOutputAttribReply.EndCoord.X = Buff->CurrentX - Buff->ShowX;
+   Reply->Data.WriteConsoleOutputAttribReply.EndCoord.Y = ( Buff->CurrentY + Buff->MaxY - Buff->ShowY ) % Buff->MaxY;
+   Buff->CurrentX = X;
+   Buff->CurrentY = Y;
+   UNLOCK;
    return Reply->Status = STATUS_SUCCESS;
 }
 
 NTSTATUS CsrFillOutputAttrib( PCSRSS_PROCESS_DATA ProcessData, PCSRSS_API_REQUEST Request, PCSRSS_API_REPLY Reply )
 {
    int c;
-   PCSRSS_CONSOLE Console;
+   PCSRSS_SCREEN_BUFFER Buff;
    NTSTATUS Status;
    int X, Y;
+   IO_STATUS_BLOCK Iosb;
+   OUTPUT_ATTRIBUTE Attr;
    
    Reply->Header.MessageSize = sizeof(CSRSS_API_REPLY);
    Reply->Header.DataSize = sizeof(CSRSS_API_REPLY) -
       sizeof(LPC_MESSAGE_HEADER);
-   Status = CsrGetObject( ProcessData, Request->Data.FillOutputAttribRequest.ConsoleHandle, (Object_t **)&Console );
-   if( !NT_SUCCESS( Status ) )
+   LOCK;
+   Status = CsrGetObject( ProcessData, Request->Data.FillOutputAttribRequest.ConsoleHandle, (Object_t **)&Buff );
+   if( !NT_SUCCESS( Status ) || (Status = Buff->Header.Type == CSRSS_SCREEN_BUFFER_MAGIC ? 0 : STATUS_INVALID_HANDLE ))
       {
         Reply->Status = Status;
+        UNLOCK;
         return Status;
       }
-   RtlEnterCriticalSection( &ActiveConsoleLock );
-   X = Console->CurrentX;
-   Y = Console->CurrentY;
-   Console->CurrentX = Request->Data.FillOutputAttribRequest.Coord.X + Console->ShowX;
-   Console->CurrentY = Request->Data.FillOutputAttribRequest.Coord.Y + Console->ShowY;
+   X = Buff->CurrentX;
+   Y = Buff->CurrentY;
+   Buff->CurrentX = Request->Data.FillOutputAttribRequest.Coord.X + Buff->ShowX;
+   Buff->CurrentY = Request->Data.FillOutputAttribRequest.Coord.Y + Buff->ShowY;
    for( c = 0; c < Request->Data.FillOutputAttribRequest.Length; c++ )
       {
-        Console->Buffer[(Console->CurrentY * Console->MaxX * 2) + (Console->CurrentX * 2) + 1] = Request->Data.FillOutputAttribRequest.Attribute;
-        if( ++Console->CurrentX == Console->MaxX )
+        Buff->Buffer[(Buff->CurrentY * Buff->MaxX * 2) + (Buff->CurrentX * 2) + 1] = Request->Data.FillOutputAttribRequest.Attribute;
+        if( ++Buff->CurrentX == Buff->MaxX )
            {
-              Console->CurrentX = 0;
-              if( ++Console->CurrentY == Console->MaxY )
-                 Console->CurrentY = 0;
+              Buff->CurrentX = 0;
+              if( ++Buff->CurrentY == Buff->MaxY )
+                 Buff->CurrentY = 0;
            }
       }
-   if( Console == ActiveConsole )
-      CsrDrawConsole( Console );
-   Console->CurrentX = X;
-   Console->CurrentY = Y;
-   RtlLeaveCriticalSection( &ActiveConsoleLock );
+   if( Buff == ActiveConsole->ActiveBuffer )
+     {
+       Attr.wAttribute = Request->Data.FillOutputAttribRequest.Attribute;
+       Attr.nLength = Request->Data.FillOutputAttribRequest.Length;
+       Attr.dwCoord = Request->Data.FillOutputAttribRequest.Coord;
+       Status = NtDeviceIoControlFile( ConsoleDeviceHandle,
+                                      NULL,
+                                      NULL,
+                                      NULL,
+                                      &Iosb,
+                                      IOCTL_CONSOLE_FILL_OUTPUT_ATTRIBUTE,
+                                      &Attr,
+                                      sizeof (Attr),
+                                      0,
+                                      0 );
+       if( !NT_SUCCESS( Status ) )
+        DPRINT1( "Failed to fill output attribute\n" );
+     }
+   Buff->CurrentX = X;
+   Buff->CurrentY = Y;
+   UNLOCK;
    return Reply->Status = STATUS_SUCCESS;
 }
 
 
 NTSTATUS CsrGetCursorInfo( PCSRSS_PROCESS_DATA ProcessData, PCSRSS_API_REQUEST Request, PCSRSS_API_REPLY Reply )
 {
-   PCSRSS_CONSOLE Console;
+   PCSRSS_SCREEN_BUFFER Buff;
    NTSTATUS Status;
    
    Reply->Header.MessageSize = sizeof(CSRSS_API_REPLY);
    Reply->Header.DataSize = sizeof(CSRSS_API_REPLY) -
       sizeof(LPC_MESSAGE_HEADER);
-   Status = CsrGetObject( ProcessData, Request->Data.GetCursorInfoRequest.ConsoleHandle, (Object_t **)&Console );
-   if( !NT_SUCCESS( Status ) )
+   LOCK;
+   Status = CsrGetObject( ProcessData, Request->Data.GetCursorInfoRequest.ConsoleHandle, (Object_t **)&Buff );
+   if( !NT_SUCCESS( Status ) || (Status = Buff->Header.Type == CSRSS_SCREEN_BUFFER_MAGIC ? 0 : STATUS_INVALID_HANDLE ))
       {
         Reply->Status = Status;
+        UNLOCK;
         return Status;
       }
-   RtlEnterCriticalSection( &ActiveConsoleLock );
-   Reply->Data.GetCursorInfoReply.Info = Console->CursorInfo;
-   RtlLeaveCriticalSection( &ActiveConsoleLock );
+   Reply->Data.GetCursorInfoReply.Info = Buff->CursorInfo;
+   UNLOCK;
    return Reply->Status = STATUS_SUCCESS;
 }
 
 NTSTATUS CsrSetCursorInfo( PCSRSS_PROCESS_DATA ProcessData, PCSRSS_API_REQUEST Request, PCSRSS_API_REPLY Reply )
 {
-   PCSRSS_CONSOLE Console;
+   PCSRSS_SCREEN_BUFFER Buff;
    NTSTATUS Status;
    IO_STATUS_BLOCK Iosb;
    
    Reply->Header.MessageSize = sizeof(CSRSS_API_REPLY);
    Reply->Header.DataSize = sizeof(CSRSS_API_REPLY) -
       sizeof(LPC_MESSAGE_HEADER);
-   Status = CsrGetObject( ProcessData, Request->Data.SetCursorInfoRequest.ConsoleHandle, (Object_t **)&Console );
-   if( !NT_SUCCESS( Status ) )
+   LOCK;
+   Status = CsrGetObject( ProcessData, Request->Data.SetCursorInfoRequest.ConsoleHandle, (Object_t **)&Buff );
+   if( !NT_SUCCESS( Status ) || (Status = Buff->Header.Type == CSRSS_SCREEN_BUFFER_MAGIC ? 0 : STATUS_INVALID_HANDLE ))
       {
         Reply->Status = Status;
+        UNLOCK;
         return Status;
       }
-   RtlEnterCriticalSection( &ActiveConsoleLock );
-   Console->CursorInfo = Request->Data.SetCursorInfoRequest.Info;
-   if( Console == ActiveConsole )
+   Buff->CursorInfo = Request->Data.SetCursorInfoRequest.Info;
+   if( Buff == ActiveConsole->ActiveBuffer )
       {
-        Status = NtDeviceIoControlFile( ConsoleDeviceHandle, NULL, NULL, NULL, &Iosb, IOCTL_CONSOLE_SET_CURSOR_INFO, &Console->CursorInfo, sizeof( Console->CursorInfo ), 0, 0 );
+        Status = NtDeviceIoControlFile( ConsoleDeviceHandle, NULL, NULL, NULL, &Iosb, IOCTL_CONSOLE_SET_CURSOR_INFO, &Buff->CursorInfo, sizeof( Buff->CursorInfo ), 0, 0 );
         if( !NT_SUCCESS( Status ) )
            {
               DbgPrint( "CSR: Failed to set cursor info\n" );
               return Reply->Status = Status;
            }
       }
-   RtlLeaveCriticalSection( &ActiveConsoleLock );
+   UNLOCK;
    return Reply->Status = STATUS_SUCCESS;
 }
 
@@ -855,34 +1241,164 @@ NTSTATUS CsrSetTextAttrib( PCSRSS_PROCESS_DATA ProcessData, PCSRSS_API_REQUEST R
    NTSTATUS Status;
    CONSOLE_SCREEN_BUFFER_INFO ScrInfo;
    IO_STATUS_BLOCK Iosb;
-   PCSRSS_CONSOLE Console;
-
+   PCSRSS_SCREEN_BUFFER Buff;
+   
    Reply->Header.MessageSize = sizeof(CSRSS_API_REPLY);
    Reply->Header.DataSize = sizeof(CSRSS_API_REPLY) -
       sizeof(LPC_MESSAGE_HEADER);
-   Status = CsrGetObject( ProcessData, Request->Data.SetAttribRequest.ConsoleHandle, (Object_t **)&Console );
-   if( !NT_SUCCESS( Status ) )
+   LOCK;
+   Status = CsrGetObject( ProcessData, Request->Data.SetAttribRequest.ConsoleHandle, (Object_t **)&Buff );
+   if( !NT_SUCCESS( Status ) || (Status = Buff->Header.Type == CSRSS_SCREEN_BUFFER_MAGIC ? 0 : STATUS_INVALID_HANDLE ))
       {
         Reply->Status = Status;
+        UNLOCK;
         return Status;
       }
-   RtlEnterCriticalSection( &ActiveConsoleLock );
-   Console->DefaultAttrib = Request->Data.SetAttribRequest.Attrib;
-   if( Console == ActiveConsole )
+   Buff->DefaultAttrib = Request->Data.SetAttribRequest.Attrib;
+   if( Buff == ActiveConsole->ActiveBuffer )
       {
-        ScrInfo.wAttributes = Console->DefaultAttrib;
-        ScrInfo.dwCursorPosition.X = Console->CurrentX - Console->ShowX;   
-        ScrInfo.dwCursorPosition.Y = ((Console->CurrentY + Console->MaxY) - Console->ShowY) % Console->MaxY;
+        ScrInfo.wAttributes = Buff->DefaultAttrib;
+        ScrInfo.dwCursorPosition.X = Buff->CurrentX - Buff->ShowX;   
+        ScrInfo.dwCursorPosition.Y = ((Buff->CurrentY + Buff->MaxY) - Buff->ShowY) % Buff->MaxY;
         Status = NtDeviceIoControlFile( ConsoleDeviceHandle, NULL, NULL, NULL, &Iosb, IOCTL_CONSOLE_SET_SCREEN_BUFFER_INFO, &ScrInfo, sizeof( ScrInfo ), 0, 0 );
         if( !NT_SUCCESS( Status ) )
            {
               DbgPrint( "CSR: Failed to set console info\n" );
+              UNLOCK;
               return Reply->Status = Status;
            }
       }
+   UNLOCK;
    return Reply->Status = STATUS_SUCCESS;
 }
 
+NTSTATUS CsrSetConsoleMode( PCSRSS_PROCESS_DATA ProcessData, PCSRSS_API_REQUEST Request, PCSRSS_API_REPLY Reply )
+{
+   NTSTATUS Status;
+   PCSRSS_CONSOLE Console;
+   PCSRSS_SCREEN_BUFFER Buff;
+
+   Reply->Header.MessageSize = sizeof(CSRSS_API_REPLY);
+   Reply->Header.DataSize = sizeof(CSRSS_API_REPLY) - sizeof(LPC_MESSAGE_HEADER);
+   LOCK;
+   Status = CsrGetObject( ProcessData, Request->Data.SetConsoleModeRequest.ConsoleHandle, (Object_t **)&Console );
+   if( !NT_SUCCESS( Status ) )
+      {
+        Reply->Status = Status;
+        UNLOCK;
+        return Status;
+      }
+   Buff = (PCSRSS_SCREEN_BUFFER)Console;
+   if( Console->Header.Type == CSRSS_CONSOLE_MAGIC )
+      Console->Mode = Request->Data.SetConsoleModeRequest.Mode & CONSOLE_INPUT_MODE_VALID;
+   else if( Console->Header.Type == CSRSS_SCREEN_BUFFER_MAGIC )
+      Buff->Mode = Request->Data.SetConsoleModeRequest.Mode & CONSOLE_OUTPUT_MODE_VALID;
+   else {
+      Reply->Status = STATUS_INVALID_HANDLE;
+      UNLOCK;
+      return Status;
+   }
+   UNLOCK;
+   Reply->Status = STATUS_SUCCESS;
+   return Reply->Status;
+}
 
+NTSTATUS CsrGetConsoleMode( PCSRSS_PROCESS_DATA ProcessData, PCSRSS_API_REQUEST Request, PCSRSS_API_REPLY Reply )
+{
+   NTSTATUS Status;
+   PCSRSS_CONSOLE Console;
+   PCSRSS_SCREEN_BUFFER Buff;   /* gee, I really wish I could use an anonymous union here */
+
+   Reply->Header.MessageSize = sizeof(CSRSS_API_REPLY);
+   Reply->Header.DataSize = sizeof(CSRSS_API_REPLY) - sizeof(LPC_MESSAGE_HEADER);
+   LOCK;
+   Status = CsrGetObject( ProcessData, Request->Data.GetConsoleModeRequest.ConsoleHandle, (Object_t **)&Console );
+   if( !NT_SUCCESS( Status ) )
+      {
+        Reply->Status = Status;
+        UNLOCK;
+        return Status;
+      }
+   Reply->Status = STATUS_SUCCESS;
+   Buff = (PCSRSS_SCREEN_BUFFER)Console;
+   if( Console->Header.Type = CSRSS_CONSOLE_MAGIC )
+      Reply->Data.GetConsoleModeReply.ConsoleMode = Console->Mode;
+   else if( Buff->Header.Type = CSRSS_SCREEN_BUFFER_MAGIC )
+      Reply->Data.GetConsoleModeReply.ConsoleMode = Buff->Mode;
+   else Status = STATUS_INVALID_HANDLE;
+   UNLOCK;
+   return Reply->Status;
+}
 
+NTSTATUS CsrCreateScreenBuffer( PCSRSS_PROCESS_DATA ProcessData, PCSRSS_API_REQUEST Request, PCSRSS_API_REPLY Reply )
+{
+   PCSRSS_SCREEN_BUFFER Buff = RtlAllocateHeap( CsrssApiHeap, 0, sizeof( CSRSS_SCREEN_BUFFER ) );
+   NTSTATUS Status;
+   
+   Reply->Header.MessageSize = sizeof( CSRSS_API_REPLY );
+   Reply->Header.DataSize = sizeof(CSRSS_API_REPLY) - sizeof(LPC_MESSAGE_HEADER);
+   if( !Buff )
+      Reply->Status = STATUS_INSUFFICIENT_RESOURCES;
+   LOCK;
+   Status = CsrInitConsoleScreenBuffer( Buff );
+   if( !NT_SUCCESS( Status ) )
+      Reply->Status = Status;
+   else {
+      Status = CsrInsertObject( ProcessData, &Reply->Data.CreateScreenBufferReply.OutputHandle, &Buff->Header );
+      if( !NT_SUCCESS( Status ) )
+        Reply->Status = Status;
+      else Reply->Status = STATUS_SUCCESS;
+   }
+   UNLOCK;
+   return Reply->Status;
+}
+
+NTSTATUS CsrSetScreenBuffer( PCSRSS_PROCESS_DATA ProcessData, PCSRSS_API_REQUEST Request, PCSRSS_API_REPLY Reply )
+{
+   NTSTATUS Status;
+   PCSRSS_SCREEN_BUFFER Buff;
+   
+   Reply->Header.MessageSize = sizeof( CSRSS_API_REPLY );
+   Reply->Header.DataSize = sizeof(CSRSS_API_REPLY) - sizeof(LPC_MESSAGE_HEADER);
+   LOCK;
+   Status = CsrGetObject( ProcessData, Request->Data.SetActiveScreenBufferRequest.OutputHandle, (Object_t **)&Buff );
+   if( !NT_SUCCESS( Status ) )
+      Reply->Status = Status;
+   else {
+      // drop reference to old buffer, maybe delete
+      if( !InterlockedDecrement( &ProcessData->Console->ActiveBuffer->Header.ReferenceCount ) )
+        CsrDeleteScreenBuffer( ProcessData->Console->ActiveBuffer );
+      // tie console to new buffer
+      ProcessData->Console->ActiveBuffer = Buff;
+      // inc ref count on new buffer
+      InterlockedIncrement( &Buff->Header.ReferenceCount );
+      // if the console is active, redraw it
+      if( ActiveConsole == ProcessData->Console )
+        CsrDrawConsole( Buff );
+      Reply->Status = STATUS_SUCCESS;
+   }
+   UNLOCK;
+   return Reply->Status;
+}
+
+NTSTATUS CsrSetTitle( PCSRSS_PROCESS_DATA ProcessData, PCSRSS_API_REQUEST Request, PCSRSS_API_REPLY Reply )
+{
+  NTSTATUS Status;
+  PCSRSS_CONSOLE Console;
+  
+  Reply->Header.MessageSize = sizeof( CSRSS_API_REPLY );
+  Reply->Header.DataSize = sizeof(CSRSS_API_REPLY) - sizeof(LPC_MESSAGE_HEADER);
+  LOCK;
+  Status = CsrGetObject( ProcessData, Request->Data.SetTitleRequest.Console, (Object_t **)&Console );
+  if( !NT_SUCCESS( Status ) )
+    Reply->Status = Status;  
+  else {
+    // copy title to console
+    RtlFreeUnicodeString( &Console->Title );
+    RtlCreateUnicodeString( &Console->Title, Request->Data.SetTitleRequest.Title );
+    Reply->Status = STATUS_SUCCESS;
+  }
+  UNLOCK;
+  return Reply->Status;
+}