/*++ Copyright (c) 1990 Microsoft Corporation Module Name: console.c Abstract: Interface to the console for Win32 applications. Author: Ramon Juan San Andres (ramonsa) 30-Nov-1990 Revision History: --*/ #include #include #include #include #define FREE(x) free(x) #define MALLOC(x) malloc(x) #define REALLOC(x,y) realloc(x,y) #include "cons.h" // // EVENT BUFFER // // The event buffer is used to store event records from the input // queue. // #define INITIAL_EVENTS 32 #define MAX_EVENTS 64 #define EVENT_INCREMENT 4 #define ADVANCE TRUE #define NOADVANCE FALSE #define WAIT TRUE #define NOWAIT FALSE // // For accessing fields of an event record // #define EVENT_TYPE(p) ((p)->EventType) #define EVENT_DATA(p) ((p)->Event) // // For casting event records // #define PMOUSE_EVT(p) (&(EVENT_DATA(p).MouseEvent)) #define PWINDOW_EVT(p) (&(EVENT_DATA(p).WindowBufferSizeEvent)) #define PKEY_EVT(p) (&(EVENT_DATA(p).KeyEvent)) // // The event buffer structure // typedef struct EVENT_BUFFER { DWORD MaxEvents; // Max number of events in buffer DWORD NumberOfEvents; // Number of events in buffer DWORD EventIndex; // Event Index BOOL BusyFlag; // Busy flag CRITICAL_SECTION CriticalSection; // To maintain integrity CRITICAL_SECTION PeekCriticalSection; // While peeking PINPUT_RECORD EventBuffer; // Event Buffer } EVENT_BUFFER, *PEVENT_BUFFER; // // Screen attributes // #define BLACK_FGD 0 #define BLUE_FGD FOREGROUND_BLUE #define GREEN_FGD FOREGROUND_GREEN #define CYAN_FGD (FOREGROUND_BLUE | FOREGROUND_GREEN) #define RED_FGD FOREGROUND_RED #define MAGENTA_FGD (FOREGROUND_BLUE | FOREGROUND_RED) #define YELLOW_FGD (FOREGROUND_GREEN | FOREGROUND_RED) #define WHITE_FGD (FOREGROUND_BLUE | FOREGROUND_GREEN | FOREGROUND_RED) #define BLACK_BGD 0 #define BLUE_BGD BACKGROUND_BLUE #define GREEN_BGD BACKGROUND_GREEN #define CYAN_BGD (BACKGROUND_BLUE | BACKGROUND_GREEN) #define RED_BGD BACKGROUND_RED #define MAGENTA_BGD (BACKGROUND_BLUE | BACKGROUND_RED) #define YELLOW_BGD (BACKGROUND_GREEN | BACKGROUND_RED) #define WHITE_BGD (BACKGROUND_BLUE | BACKGROUND_GREEN | BACKGROUND_RED) // // The AttrBg and AttrFg arrays are used for mapping DOS attributes // to the new attributes. // WORD AttrBg[ ] = { BLACK_BGD, // black BLUE_BGD, // blue GREEN_BGD, // green CYAN_BGD, // cyan RED_BGD, // red MAGENTA_BGD, // magenta YELLOW_BGD, // brown WHITE_BGD, // light gray BACKGROUND_INTENSITY | BLACK_BGD, // dark gray BACKGROUND_INTENSITY | BLUE_BGD, // light blue BACKGROUND_INTENSITY | GREEN_BGD, // light green BACKGROUND_INTENSITY | CYAN_BGD, // light cyan BACKGROUND_INTENSITY | RED_BGD, // light red BACKGROUND_INTENSITY | MAGENTA_BGD, // light magenta BACKGROUND_INTENSITY | YELLOW_BGD, // light yellow BACKGROUND_INTENSITY | WHITE_BGD // white }; WORD AttrFg[ ] = { BLACK_FGD, // black BLUE_FGD, // blue GREEN_FGD, // green CYAN_FGD, // cyan RED_FGD, // red MAGENTA_FGD, // magenta YELLOW_FGD, // brown WHITE_FGD, // light gray FOREGROUND_INTENSITY | BLACK_FGD, // dark gray FOREGROUND_INTENSITY | BLUE_FGD, // light blue FOREGROUND_INTENSITY | GREEN_FGD, // light green FOREGROUND_INTENSITY | CYAN_FGD, // light cyan FOREGROUND_INTENSITY | RED_FGD, // light red FOREGROUND_INTENSITY | MAGENTA_FGD, // light magenta FOREGROUND_INTENSITY | YELLOW_FGD, // light yellow FOREGROUND_INTENSITY | WHITE_FGD // white }; // // GET_ATTRIBUTE performs the mapping from old attributes to new attributes // #define GET_ATTRIBUTE(x) (AttrFg[x & 0x000F ] | AttrBg[( x & 0x00F0 ) >> 4]) // // The LINE_INFO structure contains information about each line in the // screen buffer. // typedef struct _LINE_INFO { BOOL Dirty; // True if has not been displayed int colMinChanged; // if dirty, smallest col changed int colMaxChanged; // if dirty, biggest col changed PCHAR_INFO Line; // Pointer to the line. } LINE_INFO, *PLINE_INFO; #define ResetLineInfo(pli) \ { pli->Dirty = 0; \ pli->colMinChanged = 1000; \ pli->colMaxChanged = -1; \ } // // The SCREEN_DATA structure contains the information about individual // screens. // typedef struct SCREEN_DATA { HANDLE ScreenHandle; // Handle to screen PLINE_INFO LineInfo; // Array of line info. PCHAR_INFO ScreenBuffer; // Screen buffer ULONG MaxBufferSize; // Max. buffer size ATTRIBUTE AttributeOld; // Attribute - original WORD AttributeNew; // Attribute - converted ROW FirstRow; // First row to update ROW LastRow; // Last row to update CRITICAL_SECTION CriticalSection; // To maintain integrity DWORD CursorSize; // Cursor Size SCREEN_INFORMATION ScreenInformation; // Screen information } SCREEN_DATA, *PSCREEN_DATA; // // Static global data // static EVENT_BUFFER EventBuffer; // Event buffer static HANDLE hInput; // handle to stdin static HANDLE hOutput; // handle to stdout static HANDLE hError; // handle to stderr static PSCREEN_DATA OutputScreenData; // Screen data for hOutput static PSCREEN_DATA ActiveScreenData; // Points to current screen data static BOOL Initialized = FALSE; // Initialized flag #if defined (DEBUG) static char DbgBuffer[128]; #endif // // Local Prototypes // BOOL InitializeGlobalState ( void ); PSCREEN_DATA MakeScreenData ( HANDLE ScreenHandle ); BOOL InitLineInfo ( PSCREEN_DATA ScreenData ); PINPUT_RECORD NextEvent ( BOOL fAdvance, BOOL fWait ); void MouseEvent ( PMOUSE_EVENT_RECORD pEvent ); BOOL WindowEvent ( PWINDOW_BUFFER_SIZE_RECORD pEvent ); BOOL KeyEvent ( PKEY_EVENT_RECORD pEvent, PKBDKEY pKey ); BOOL PutEvent ( PINPUT_RECORD InputRecord ); BOOL InitializeGlobalState ( void ) /*++ Routine Description: Initializes our global state data. Arguments: None. Return Value: TRUE if success FALSE otherwise. --*/ { // // Initialize the event buffer // InitializeCriticalSection( &(EventBuffer.CriticalSection) ); InitializeCriticalSection( &(EventBuffer.PeekCriticalSection) ); EventBuffer.NumberOfEvents = 0; EventBuffer.EventIndex = 0; EventBuffer.BusyFlag = FALSE; EventBuffer.EventBuffer = MALLOC( INITIAL_EVENTS * sizeof(INPUT_RECORD) ); if ( !EventBuffer.EventBuffer ) { return FALSE; } EventBuffer.MaxEvents = INITIAL_EVENTS; // // Get handles to stdin, stdout and stderr // hInput = GetStdHandle( STD_INPUT_HANDLE ); hOutput = GetStdHandle( STD_OUTPUT_HANDLE ); hError = GetStdHandle( STD_ERROR_HANDLE ); // // Initialize the screen data for hOutput // if ( !(OutputScreenData = MakeScreenData( hOutput )) ) { return FALSE; } // // Current screen is hOutput // ActiveScreenData = OutputScreenData; return (Initialized = TRUE); } PSCREEN_DATA MakeScreenData ( HANDLE ScreenHandle ) /*++ Routine Description: Allocates memory for a SCREEN_DATA information and initializes it. Arguments: ScreenHandle - Supplies handle of screen. Return Value: POINTER to allocated SCREEN_DATA structure --*/ { PSCREEN_DATA ScreenData; // Pointer to screen data CONSOLE_SCREEN_BUFFER_INFO ScrInfo; // Screen buffer info. // // Allocate space for the screen data. // if ( !(ScreenData = (PSCREEN_DATA)MALLOC(sizeof(SCREEN_DATA))) ) { return NULL; } // // Allocate space for our copy of the screen buffer. // GetConsoleScreenBufferInfo( ScreenHandle, &ScrInfo ); ScreenData->MaxBufferSize = ScrInfo.dwSize.Y * ScrInfo.dwSize.X; ScreenData->ScreenBuffer = (PCHAR_INFO)MALLOC( ScreenData->MaxBufferSize * sizeof(CHAR_INFO)); if ( !ScreenData->ScreenBuffer ) { FREE( ScreenData ); return NULL; } // // Allocate space for the LineInfo array // ScreenData->LineInfo = (PLINE_INFO)MALLOC( ScrInfo.dwSize.Y * sizeof( LINE_INFO ) ); if ( !ScreenData->LineInfo ) { FREE( ScreenData->ScreenBuffer ); FREE( ScreenData ); return NULL; } // // Memory has been allocated, now initialize the structure // ScreenData->ScreenHandle = ScreenHandle; ScreenData->ScreenInformation.NumberOfRows = ScrInfo.dwSize.Y; ScreenData->ScreenInformation.NumberOfCols = ScrInfo.dwSize.X; ScreenData->ScreenInformation.CursorRow = ScrInfo.dwCursorPosition.Y; ScreenData->ScreenInformation.CursorCol = ScrInfo.dwCursorPosition.X; ScreenData->AttributeNew = ScrInfo.wAttributes; ScreenData->AttributeOld = 0x00; ScreenData->FirstRow = ScreenData->ScreenInformation.NumberOfRows; ScreenData->LastRow = 0; InitializeCriticalSection( &(ScreenData->CriticalSection) ); InitLineInfo( ScreenData ); return ScreenData; } BOOL InitLineInfo ( PSCREEN_DATA ScreenData ) /*++ Routine Description: Initializes the LineInfo array. Arguments: ScreenData - Supplies pointer to screen data. Return Value: TRUE if initialized, false otherwise. --*/ { ROW Row; COLUMN Cols; PLINE_INFO LineInfo; PCHAR_INFO CharInfo; LineInfo = ScreenData->LineInfo; CharInfo = ScreenData->ScreenBuffer; Row = ScreenData->ScreenInformation.NumberOfRows; Cols = ScreenData->ScreenInformation.NumberOfCols; while ( Row-- ) { // // BUGBUG Temporary // // assert( LineInfo < (ScreenData->LineInfo + ScreenData->ScreenInformation.NumberOfRows)); // assert( (CharInfo + Cols) <= (ScreenData->ScreenBuffer + ScreenData->MaxBufferSize) ); ResetLineInfo (LineInfo); LineInfo->Line = CharInfo; LineInfo++; CharInfo += Cols; } return TRUE; } PSCREEN consoleNewScreen ( void ) /*++ Routine Description: Creates a new screen. Arguments: None. Return Value: Pointer to screen data. --*/ { PSCREEN_DATA ScreenData; // Screen data HANDLE NewScreenHandle; SMALL_RECT NewSize; CONSOLE_SCREEN_BUFFER_INFO ScrInfo; // Screen buffer info. CONSOLE_CURSOR_INFO CursorInfo; if ( !Initialized ) { // // We have to initialize our global state. // if ( !InitializeGlobalState() ) { return NULL; } } // // Create a new screen buffer // NewScreenHandle = CreateConsoleScreenBuffer(GENERIC_WRITE | GENERIC_READ, FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, CONSOLE_TEXTMODE_BUFFER, NULL ); if (NewScreenHandle == INVALID_HANDLE_VALUE) { // // No luck // return NULL; } // // We want the new window to be the same size as the current one, so // we resize it. // GetConsoleScreenBufferInfo( ActiveScreenData->ScreenHandle, &ScrInfo ); NewSize.Left = 0; NewSize.Top = 0; NewSize.Right = ScrInfo.srWindow.Right - ScrInfo.srWindow.Left; NewSize.Bottom = ScrInfo.srWindow.Bottom - ScrInfo.srWindow.Top; SetConsoleWindowInfo( NewScreenHandle, TRUE, &NewSize ); // // Now we create a screen data structure for it. // if ( !(ScreenData = MakeScreenData(NewScreenHandle)) ) { CloseHandle(NewScreenHandle); return NULL; } CursorInfo.bVisible = TRUE; ScreenData->CursorSize = CursorInfo.dwSize = 25; SetConsoleCursorInfo ( ScreenData->ScreenHandle, &CursorInfo ); // // We are all set. We return a pointer to the // screen data. // return (PSCREEN)ScreenData; } BOOL consoleCloseScreen ( PSCREEN pScreen ) /*++ Routine Description: Closes a screen. Arguments: pScreen - Supplies pointer to screen data. Return Value: TRUE if screen closed. FALSE otherwise --*/ { PSCREEN_DATA ScreenData = (PSCREEN_DATA)pScreen; // // We cannot close the active screen // if ( !ScreenData || (ScreenData == ActiveScreenData) ) { return FALSE; } if (ScreenData->ScreenHandle != INVALID_HANDLE_VALUE) { CloseHandle(ScreenData->ScreenHandle); } FREE( ScreenData->LineInfo ); FREE( ScreenData->ScreenBuffer ); FREE( ScreenData ); return TRUE; } PSCREEN consoleGetCurrentScreen ( void ) /*++ Routine Description: Returns the current screen. Arguments: none. Return Value: Pointer to currently active screen data. --*/ { if ( !Initialized ) { // // We have to initialize our global state. // if (!InitializeGlobalState()) { return NULL; } } return (PSCREEN)ActiveScreenData; } BOOL consoleSetCurrentScreen ( PSCREEN pScreen ) /*++ Routine Description: Sets the active screen. Arguments: pScreen - Supplies pointer to screen data. Return Value: TRUE if the active screen set FALSE otherwise. --*/ { BOOL ScreenSet = TRUE; PSCREEN_DATA CurrentScreen = ActiveScreenData; EnterCriticalSection( &(CurrentScreen->CriticalSection) ); ScreenSet = SetConsoleActiveScreenBuffer( ((PSCREEN_DATA)pScreen)->ScreenHandle); if (ScreenSet) { ActiveScreenData = (PSCREEN_DATA)pScreen; } LeaveCriticalSection( &(CurrentScreen->CriticalSection) ); return ScreenSet; } BOOL consoleGetScreenInformation ( PSCREEN pScreen, PSCREEN_INFORMATION pScreenInfo ) /*++ Routine Description: Sets the active screen. Arguments: pScreen - Supplies pointer to screen data. pScreenInfo - Supplies pointer to screen info buffer Return Value: TRUE if the screen info returned FALSE otherwise. --*/ { PSCREEN_DATA ScreenData = (PSCREEN_DATA)pScreen; if (!ScreenData) { return FALSE; } EnterCriticalSection( &(ScreenData->CriticalSection) ); memcpy(pScreenInfo, &(ScreenData->ScreenInformation), sizeof(SCREEN_INFORMATION)); LeaveCriticalSection( &(ScreenData->CriticalSection) ); return TRUE; } BOOL consoleSetScreenSize ( PSCREEN pScreen, ROW Rows, COLUMN Cols ) /*++ Routine Description: Sets the screen size Arguments: pScreen - Supplies pointer to screen data. Rows - Number of rows Cols - Number of columns Return Value: TRUE if screen size changed successfully FALSE otherwise. --*/ { PSCREEN_DATA ScreenData = (PSCREEN_DATA)pScreen; CONSOLE_SCREEN_BUFFER_INFO ScreenBufferInfo; SMALL_RECT ScreenRect; COORD ScreenSize; USHORT MinRows; USHORT MinCols; ULONG NewBufferSize; BOOL WindowSet = FALSE; BOOL Status = FALSE; // // Won't attempt to resize larger than the largest window size // ScreenSize = GetLargestConsoleWindowSize( ScreenData->ScreenHandle ); if ( (Rows > (ROW)ScreenSize.Y) || (Cols > (COLUMN)ScreenSize.X) ) { return FALSE; } EnterCriticalSection( &(ScreenData->CriticalSection) ); // // Obtain the current screen information. // if ( GetConsoleScreenBufferInfo( ScreenData->ScreenHandle, &ScreenBufferInfo ) ) { // // If the desired buffer size is smaller than the current window // size, we have to resize the current window first. // if ( ( Rows < (ROW) (ScreenBufferInfo.srWindow.Bottom - ScreenBufferInfo.srWindow.Top + 1) ) || ( Cols < (COLUMN) (ScreenBufferInfo.srWindow.Right - ScreenBufferInfo.srWindow.Left + 1) ) ) { // // Set the window to a size that will fit in the current // screen buffer and that is no bigger than the size to // which we want to grow the screen buffer. // MinRows = (USHORT)min( (int)Rows, (int)(ScreenBufferInfo.dwSize.Y) ); MinCols = (USHORT)min( (int)Cols, (int)(ScreenBufferInfo.dwSize.X) ); ScreenRect.Top = 0; ScreenRect.Left = 0; ScreenRect.Right = (SHORT)MinCols - (SHORT)1; ScreenRect.Bottom = (SHORT)MinRows - (SHORT)1; WindowSet = (BOOL)SetConsoleWindowInfo( ScreenData->ScreenHandle, TRUE, &ScreenRect ); if ( !WindowSet ) { // // ERROR // goto Done; } } // // Set the screen buffer size to the desired size. // ScreenSize.X = (WORD)Cols; ScreenSize.Y = (WORD)Rows; if ( !SetConsoleScreenBufferSize( ScreenData->ScreenHandle, ScreenSize ) ) { // // ERROR // // // Return the window to its original size. We ignore the return // code because there is nothing we can do about it. // SetConsoleWindowInfo( ScreenData->ScreenHandle, TRUE, &(ScreenBufferInfo.srWindow) ); goto Done; } // // resize the screen buffer. Note that the contents of the screen // buffer are not valid anymore. Someone else will have to update // them. // NewBufferSize = Rows * Cols; if (ScreenData->MaxBufferSize < NewBufferSize ) { ScreenData->ScreenBuffer = REALLOC( ScreenData->ScreenBuffer, NewBufferSize * sizeof(CHAR_INFO)); ScreenData->MaxBufferSize = NewBufferSize; ScreenData->LineInfo = REALLOC( ScreenData->LineInfo, Rows * sizeof( LINE_INFO ) ); } // // Set the Window Size. We know that we can grow the window to this size // because we tested the size against the largest window size at the // beginning of the function. // ScreenRect.Top = 0; ScreenRect.Left = 0; ScreenRect.Right = (SHORT)Cols - (SHORT)1; ScreenRect.Bottom = (SHORT)Rows - (SHORT)1; WindowSet = (BOOL)SetConsoleWindowInfo( ScreenData->ScreenHandle, TRUE, &ScreenRect ); if ( !WindowSet ) { // // We could not resize the window. We will leave the // resized screen buffer. // // ERROR // goto Done; } // // Update the screen size // ScreenData->ScreenInformation.NumberOfRows = Rows; ScreenData->ScreenInformation.NumberOfCols = Cols; InitLineInfo( ScreenData ); // // Done // Status = TRUE; } else { // // ERROR // } Done: // // Invalidate the entire screen buffer // ScreenData->FirstRow = ScreenData->ScreenInformation.NumberOfRows; ScreenData->LastRow = 0; LeaveCriticalSection( &(ScreenData->CriticalSection) ); return Status; } BOOL consoleSetCursor ( PSCREEN pScreen, ROW Row, COLUMN Col ) /*++ Routine Description: Moves the cursor to a certain position. Arguments: pScreen - Supplies pointer to screen data Row - Supplies row coordinate Col - Supplies column coordinate Return Value: TRUE if moved FALSE otherwise. --*/ { PSCREEN_DATA ScreenData = (PSCREEN_DATA)pScreen; COORD Position; BOOL Moved = FALSE; EnterCriticalSection( &(ScreenData->CriticalSection) ); if ((Row != ScreenData->ScreenInformation.CursorRow) || (Col != ScreenData->ScreenInformation.CursorCol) ) { assert( Row < ScreenData->ScreenInformation.NumberOfRows); assert( Col < ScreenData->ScreenInformation.NumberOfCols); Position.Y = (SHORT)Row; Position.X = (SHORT)Col; if ( SetConsoleCursorPosition( ScreenData->ScreenHandle, Position )) { // // Cursor moved, update the data // ScreenData->ScreenInformation.CursorRow = Row; ScreenData->ScreenInformation.CursorCol = Col; Moved = TRUE; } } LeaveCriticalSection( &(ScreenData->CriticalSection) ); return Moved; } BOOL consoleSetCursorStyle ( PSCREEN pScreen, ULONG Style ) /*++ Routine Description7: Sets the cursor style. The two available styles are: underscrore and box Arguments: Style - New cursor style Return Value: True if cursor style set --*/ { PSCREEN_DATA ScreenData = (PSCREEN_DATA)pScreen; CONSOLE_CURSOR_INFO CursorInfo; CursorInfo.bVisible = TRUE; if ( Style == CURSOR_STYLE_UNDERSCORE ) { CursorInfo.dwSize = 25; } else if ( Style == CURSOR_STYLE_BOX ) { CursorInfo.dwSize = 100; } else { return FALSE; } ScreenData->CursorSize = CursorInfo.dwSize; return SetConsoleCursorInfo ( ScreenData->ScreenHandle, &CursorInfo ); } ULONG consoleWriteLine ( PSCREEN pScreen, PVOID pBuffer, ULONG BufferSize, ROW Row, COLUMN Col, ATTRIBUTE Attribute, BOOL Blank ) /*++ Routine Description7: Writes a buffer to the screen with the specified attribute and blanks to end of row. Arguments: pScreen - Supplies pointer to screen data pBuffer - Supplies pointer to buffer BufferSize - Supplies the size of the buffer Row - Supplies row coordinate Col - Supplies column coordinate Attr - Supplies the attribute Blank - TRUE if we should blank to end of last row written. Return Value: Number of bytes written --*/ { PSCREEN_DATA ScreenData = (PSCREEN_DATA)pScreen; PLINE_INFO LineInfo; PCHAR_INFO CharInfo; CHAR_INFO Char; WORD Attr; char * p = (char *)pBuffer; COLUMN ColsLeft; // Available columns COLUMN InfoCols; // Columns taken from buffer COLUMN BlankCols; // Columns to be blanked COLUMN Column; // Counter; // // We will ignore writes outside of the screen buffer // if ( ( Row >= ScreenData->ScreenInformation.NumberOfRows ) || ( Col >= ScreenData->ScreenInformation.NumberOfCols ) ) { return TRUE; } // // Ignore trivial writes // if (BufferSize == 0 && !Blank) return TRUE; EnterCriticalSection( &(ScreenData->CriticalSection) ); // // We will truncate writes that are too long // if ( (Col + BufferSize) >= ScreenData->ScreenInformation.NumberOfCols ) { BufferSize = ScreenData->ScreenInformation.NumberOfCols - Col; } LineInfo = ScreenData->LineInfo + Row; CharInfo = LineInfo->Line + Col; ColsLeft = ScreenData->ScreenInformation.NumberOfCols - Col; InfoCols = min( BufferSize, ColsLeft ); BlankCols = Blank ? (ColsLeft - InfoCols) : 0; // // Set the attribute // if ( Attribute != ScreenData->AttributeOld ) { ScreenData->AttributeOld = Attribute; ScreenData->AttributeNew = GET_ATTRIBUTE(Attribute); } Attr = ScreenData->AttributeNew; // // set up default attribute // Char.Attributes = Attr; // // set up number of columns to draw // Column = InfoCols; // // draw chars in all specified columns // while ( Column-- ) { // // use character from input string // Char.Char.AsciiChar = *p++; // // update change portions of line info // if (CharInfo->Attributes != Char.Attributes || CharInfo->Char.AsciiChar != Char.Char.AsciiChar) { LineInfo->colMinChanged = min (LineInfo->colMinChanged, CharInfo - LineInfo->Line); LineInfo->colMaxChanged = max (LineInfo->colMaxChanged, CharInfo - LineInfo->Line); LineInfo->Dirty = TRUE; } // // set up new character // *CharInfo++ = Char; } // // Blank to end of line // Char.Attributes = Attr; Char.Char.AsciiChar = ' '; Column = BlankCols; while ( Column-- ) { // // update change portions of line info // if (CharInfo->Attributes != Char.Attributes || CharInfo->Char.AsciiChar != Char.Char.AsciiChar) { LineInfo->colMinChanged = min (LineInfo->colMinChanged, CharInfo - LineInfo->Line); LineInfo->colMaxChanged = max (LineInfo->colMaxChanged, CharInfo - LineInfo->Line); LineInfo->Dirty = TRUE; } *CharInfo++ = Char; } // // Update row information // if ( Row < ScreenData->FirstRow ) { ScreenData->FirstRow = Row; } if ( Row > ScreenData->LastRow ) { ScreenData->LastRow = Row; } LeaveCriticalSection( &(ScreenData->CriticalSection) ); return (ULONG)(InfoCols + BlankCols); } BOOL consoleShowScreen ( PSCREEN pScreen ) /*++ Routine Description: Moves data from our screen buffer to the console screen buffer. Arguments: pScreen - Supplies pointer to screen data Return Value: TRUE if done FALSE otherwise --*/ { PSCREEN_DATA ScreenData = (PSCREEN_DATA)pScreen; CONSOLE_CURSOR_INFO CursorInfo; PLINE_INFO LineInfo; BOOL Shown = FALSE; ROW FirstRow; ROW LastRow; COLUMN LastCol; COORD Position; COORD Size; SMALL_RECT Rectangle; EnterCriticalSection( &(ScreenData->CriticalSection) ); if ( ScreenData->FirstRow <= ScreenData->LastRow ) { Size.X = (SHORT)(ScreenData->ScreenInformation.NumberOfCols); Size.Y = (SHORT)(ScreenData->ScreenInformation.NumberOfRows); FirstRow = ScreenData->FirstRow; LineInfo = ScreenData->LineInfo + FirstRow; LastCol = ScreenData->ScreenInformation.NumberOfCols-1; // // Find next dirty block // while ( (FirstRow <= ScreenData->LastRow) && !LineInfo->Dirty ) { FirstRow++; LineInfo++; } while ( FirstRow <= ScreenData->LastRow ) { int colLeft, colRight; // // Get the block // LastRow = FirstRow; // // set up for left/right boundary accrual // colLeft = LastCol + 1; colRight = -1; while ( (LastRow <= ScreenData->LastRow) && LineInfo->Dirty ) { // // accrue smallest bounding right/left margins // colLeft = min (colLeft, LineInfo->colMinChanged); colRight = max (colRight, LineInfo->colMaxChanged); // // reset line information // ResetLineInfo (LineInfo); // // advance to next row // LastRow++; LineInfo++; } LastRow--; // // Write the block // assert( FirstRow <= LastRow ); Position.X = (SHORT)colLeft; Position.Y = (SHORT)FirstRow; Rectangle.Top = (SHORT)FirstRow; Rectangle.Bottom = (SHORT)LastRow; Rectangle.Left = (SHORT) colLeft; Rectangle.Right = (SHORT) colRight; // // Performance hack: making the cursor invisible speeds // screen updates. // CursorInfo.bVisible = FALSE; CursorInfo.dwSize = ScreenData->CursorSize; SetConsoleCursorInfo ( ScreenData->ScreenHandle, &CursorInfo ); Shown = WriteConsoleOutput( ScreenData->ScreenHandle, ScreenData->ScreenBuffer, Size, Position, &Rectangle ); #if defined (DEBUG) if ( !Shown ) { char DbgB[128]; sprintf(DbgB, "MEP: WriteConsoleOutput Error %d\n", GetLastError() ); OutputDebugString( DbgB ); } #endif assert( Shown ); CursorInfo.bVisible = TRUE; SetConsoleCursorInfo ( ScreenData->ScreenHandle, &CursorInfo ); FirstRow = LastRow + 1; // // Find next dirty block // while ( (FirstRow <= ScreenData->LastRow) && !LineInfo->Dirty ) { FirstRow++; LineInfo++; } } ScreenData->LastRow = 0; ScreenData->FirstRow = ScreenData->ScreenInformation.NumberOfRows; } LeaveCriticalSection( &(ScreenData->CriticalSection) ); return Shown; } BOOL consoleClearScreen ( PSCREEN pScreen, BOOL ShowScreen ) /*++ Routine Description: Clears the screen Arguments: pScreen - Supplies pointer to screen data Return Value: TRUE if screen cleared FALSE otherwise --*/ { PSCREEN_DATA ScreenData = (PSCREEN_DATA)pScreen; ROW Rows; BOOL Status = TRUE; EnterCriticalSection( &(ScreenData->CriticalSection) ); Rows = ScreenData->ScreenInformation.NumberOfRows; while ( Rows-- ) { consoleWriteLine( pScreen, NULL, 0, Rows, 0, ScreenData->AttributeOld, TRUE ); } if (ShowScreen) { Status = consoleShowScreen( pScreen ); } LeaveCriticalSection( &(ScreenData->CriticalSection) ); return Status; } BOOL consoleSetAttribute ( PSCREEN pScreen, ATTRIBUTE Attribute ) /*++ Routine Description: Sets the console attribute Arguments: pScreen - Supplies pointer to screen data Attribute - Supplies the attribute Return Value: TRUE if Attribute set FALSE otherwise --*/ { PSCREEN_DATA ScreenData = (PSCREEN_DATA)pScreen; EnterCriticalSection( &(ScreenData->CriticalSection) ); if (Attribute != ScreenData->AttributeOld) { ScreenData->AttributeOld = Attribute; ScreenData->AttributeNew = GET_ATTRIBUTE(Attribute); } LeaveCriticalSection( &(ScreenData->CriticalSection) ); return TRUE; } BOOL consoleFlushInput ( void ) /*++ Routine Description: Flushes input events. Arguments: None. Return Value: TRUE if success, FALSE otherwise --*/ { EventBuffer.NumberOfEvents = 0; return FlushConsoleInputBuffer( hInput ); } BOOL consoleGetMode ( PKBDMODE pMode ) /*++ Routine Description: Get current console mode. Arguments: pMode - Supplies a pointer to the mode flag variable Return Value: TRUE if success, FALSE otherwise. --*/ { return GetConsoleMode( hInput, pMode ); } BOOL consoleSetMode ( KBDMODE Mode ) /*++ Routine Description: Sets the console mode. Arguments: Mode - Supplies the mode flags. Return Value: TRUE if success, FALSE otherwise --*/ { return SetConsoleMode( hInput, Mode ); } BOOL consoleIsKeyAvailable ( void ) /*++ Routine Description: Returns TRUE if a key is available in the event buffer. Arguments: None. Return Value: TRUE if a key is available in the event buffer FALSE otherwise --*/ { BOOL IsKey = FALSE; PINPUT_RECORD pEvent; DWORD Index; EnterCriticalSection( &(EventBuffer.CriticalSection) ); for ( Index = EventBuffer.EventIndex; Index < EventBuffer.NumberOfEvents; Index++ ) { pEvent = EventBuffer.EventBuffer + EventBuffer.EventIndex; if ( ((EVENT_TYPE(pEvent)) == KEY_EVENT) && (PKEY_EVT(pEvent))->bKeyDown ) { IsKey = TRUE; break; } } LeaveCriticalSection( &(EventBuffer.CriticalSection) ); return IsKey; } BOOL consoleDoWindow ( void ) /*++ Routine Description: Responds to a window event Arguments: None. Return Value: TRUE if window changed FALSE otherwise --*/ { PINPUT_RECORD pEvent; pEvent = NextEvent( NOADVANCE, NOWAIT ); if (( EVENT_TYPE(pEvent) ) == WINDOW_BUFFER_SIZE_EVENT) { pEvent = NextEvent( ADVANCE, WAIT ); WindowEvent(PWINDOW_EVT(pEvent)); } return FALSE; } BOOL consolePeekKey ( PKBDKEY Key ) /*++ Routine Description: Gets the next key from the input buffer if the buffer is not empty. Arguments: Key - Supplies a pointer to a key structure Return Value: TRUE if keystroke read, FALSE otherwise. --*/ { PINPUT_RECORD pEvent; BOOL Done = FALSE; BOOL IsKey = FALSE; EnterCriticalSection(&(EventBuffer.PeekCriticalSection)); do { pEvent = NextEvent( NOADVANCE, NOWAIT ); if ( pEvent ) { switch ( EVENT_TYPE(pEvent) ) { case KEY_EVENT: if (KeyEvent(PKEY_EVT(pEvent), Key)){ IsKey = TRUE; Done = TRUE; } break; case MOUSE_EVENT: Done = TRUE; break; case WINDOW_BUFFER_SIZE_EVENT: Done = TRUE; break; default: assert( FALSE ); break; } if ( !Done ) { NextEvent( ADVANCE, NOWAIT ); } } else { Done = TRUE; } } while ( !Done ); LeaveCriticalSection(&(EventBuffer.PeekCriticalSection)); return IsKey; } BOOL consoleGetKey ( PKBDKEY Key, BOOL fWait ) /*++ Routine Description: Gets the next key from the input buffer. Arguments: Key - Supplies a pointer to a key structure fWait - Supplies a flag: if TRUE, the function blocks until a key is ready. if FALSE, the function returns immediately. Return Value: TRUE if keystroke read, FALSE otherwise. --*/ { PINPUT_RECORD pEvent; do { pEvent = NextEvent( ADVANCE, fWait ); if (pEvent) { switch ( EVENT_TYPE(pEvent) ) { case KEY_EVENT: if (KeyEvent(PKEY_EVT(pEvent), Key)) { return TRUE; } break; case MOUSE_EVENT: MouseEvent(PMOUSE_EVT(pEvent)); break; case WINDOW_BUFFER_SIZE_EVENT: WindowEvent(PWINDOW_EVT(pEvent)); break; default: break; } } } while (fWait); return FALSE; } BOOL consolePutKey ( PKBDKEY Key ) /*++ Routine Description: Puts a key in the console's input buffer Arguments: Key - Supplies a pointer to a key structure Return Value: TRUE if key put, false otherwise --*/ { INPUT_RECORD InputRecord; InputRecord.EventType = KEY_EVENT; InputRecord.Event.KeyEvent.bKeyDown = FALSE; InputRecord.Event.KeyEvent.wRepeatCount = 0; InputRecord.Event.KeyEvent.wVirtualKeyCode = Key->Scancode; InputRecord.Event.KeyEvent.wVirtualScanCode = 0; InputRecord.Event.KeyEvent.uChar.UnicodeChar = Key->Unicode; InputRecord.Event.KeyEvent.dwControlKeyState = Key->Flags; if ( PutEvent( &InputRecord )) { InputRecord.Event.KeyEvent.bKeyDown = TRUE; return PutEvent( &InputRecord ); } return FALSE; } BOOL consolePutMouse( ROW Row, COLUMN Col, DWORD MouseFlags ) /*++ Routine Description: Puts a mose event in the console's input buffer Arguments: Row - Supplies the row Col - Supplies the column MouseFlags - Supplies the flags Return Value: TRUE if key put, false otherwise --*/ { INPUT_RECORD InputRecord; COORD Position; DWORD Flags; InputRecord.EventType = MOUSE_EVENT; Position.Y = (WORD)(Row - 1); Position.X = (WORD)(Col - 1); Flags = 0; InputRecord.Event.MouseEvent.dwMousePosition = Position; InputRecord.Event.MouseEvent.dwButtonState = Flags; InputRecord.Event.MouseEvent.dwControlKeyState = 0; InputRecord.Event.MouseEvent.dwEventFlags = 0; return PutEvent( &InputRecord ); } BOOL consoleIsBusyReadingKeyboard ( ) /*++ Routine Description: Determines if the console is busy reading the keyboard Arguments: None Return Value: TRUE if console is busy reading the keyboard. --*/ { BOOL Busy; EnterCriticalSection(&(EventBuffer.CriticalSection)); Busy = EventBuffer.BusyFlag; LeaveCriticalSection(&(EventBuffer.CriticalSection)); return Busy; } BOOL consoleEnterCancelEvent ( ) { INPUT_RECORD Record; Record.EventType = KEY_EVENT; Record.Event.KeyEvent.bKeyDown = TRUE; Record.Event.KeyEvent.wRepeatCount = 0; Record.Event.KeyEvent.wVirtualKeyCode = VK_CANCEL; Record.Event.KeyEvent.wVirtualScanCode = 0; Record.Event.KeyEvent.uChar.AsciiChar = 0; Record.Event.KeyEvent.dwControlKeyState = 0; return PutEvent( &Record ); } PINPUT_RECORD NextEvent ( BOOL fAdvance, BOOL fWait ) /*++ Routine Description: Returns pointer to next event record. Arguments: fAdvance - Supplies a flag: if TRUE: Advance to next event record if FALSE: Do not advance to next event record fWait - Supplies a flag: if TRUE, the blocks until an event is ready. if FALSE, return immediately. Return Value: Pointer to event record, or NULL. --*/ { PINPUT_RECORD pEvent; BOOL Success; EnterCriticalSection(&(EventBuffer.CriticalSection)); // // If the busy flag is set, then the buffer is in the process of // being read. Only one thread should want to wait, so it is // safe to simply return. // if ( EventBuffer.BusyFlag ) { assert( !fWait ); LeaveCriticalSection(&(EventBuffer.CriticalSection)); return NULL; } if (EventBuffer.NumberOfEvents == 0) { // // No events in buffer, read as many as we can // DWORD NumberOfEvents; // // If the buffer is too big, resize it // if ( EventBuffer.MaxEvents > MAX_EVENTS ) { EventBuffer.EventBuffer = REALLOC( EventBuffer.EventBuffer, MAX_EVENTS * sizeof( INPUT_RECORD ) ); EventBuffer.MaxEvents = MAX_EVENTS; assert( EventBuffer.EventBuffer ); //CleanExit( 1, 0 ); } Success = PeekConsoleInput( hInput, EventBuffer.EventBuffer, EventBuffer.MaxEvents, &NumberOfEvents); if ((!Success || (NumberOfEvents == 0)) && (!fWait)) { // // No events available and don't want to wait, // return. // LeaveCriticalSection(&(EventBuffer.CriticalSection)); return NULL; } // // Since we will block, we have to leave the critical section. // We set the Busy flag to indicate that the buffer is being // read. // EventBuffer.BusyFlag = TRUE; LeaveCriticalSection(&(EventBuffer.CriticalSection)); Success = ReadConsoleInput (hInput, EventBuffer.EventBuffer, EventBuffer.MaxEvents, &EventBuffer.NumberOfEvents); EnterCriticalSection(&(EventBuffer.CriticalSection)); EventBuffer.BusyFlag = FALSE; if (!Success) { #if defined( DEBUG ) OutputDebugString(" Error: Cannot read console events\n"); assert( Success ); #endif EventBuffer.NumberOfEvents = 0; } EventBuffer.EventIndex = 0; } pEvent = EventBuffer.EventBuffer + EventBuffer.EventIndex; // // If Advance flag is set, we advance the pointer to the next // record. // if (fAdvance) { if (--(EventBuffer.NumberOfEvents)) { switch (EVENT_TYPE(pEvent)) { case KEY_EVENT: case MOUSE_EVENT: case WINDOW_BUFFER_SIZE_EVENT: (EventBuffer.EventIndex)++; break; default: #if defined( DEBUG) sprintf(DbgBuffer, "WARNING: unknown event type %X\n", EVENT_TYPE(pEvent)); OutputDebugString(DbgBuffer); #endif (EventBuffer.EventIndex)++; break; } } } LeaveCriticalSection(&(EventBuffer.CriticalSection)); return pEvent; } void MouseEvent ( PMOUSE_EVENT_RECORD pEvent ) /*++ Routine Description: Processes mouse events. Arguments: pEvent - Supplies pointer to event record Return Value: None.. --*/ { } BOOL WindowEvent ( PWINDOW_BUFFER_SIZE_RECORD pEvent ) /*++ Routine Description: Processes window size change events. Arguments: pEvent - Supplies pointer to event record Return Value: None --*/ { return TRUE; } BOOL KeyEvent ( PKEY_EVENT_RECORD pEvent, PKBDKEY pKey ) /*++ Routine Description: Processes key events. Arguments: pEvent - Supplies pointer to event record pKey - Supplies pointer to key structure to fill out. Return Value: TRUE if key structured filled out, FALSE otherwise. --*/ { // static BOOL AltPressed = FALSE; if (pEvent->bKeyDown) { WORD Scan = pEvent->wVirtualKeyCode; // // Pressing the ALT key generates an event, but we filter this // out. // if (Scan == VK_MENU) { return FALSE; } if (Scan != VK_NUMLOCK && // NumLock Scan != VK_CAPITAL && // Caps Lock Scan != VK_SHIFT && // Shift Scan != VK_CONTROL ) { // Ctrl pKey->Unicode = pEvent->uChar.UnicodeChar; pKey->Scancode = pEvent->wVirtualKeyCode; pKey->Flags = pEvent->dwControlKeyState; //#if defined (DEBUG) // sprintf(DbgBuffer, " KEY: Scan %d '%c'\n", pKey->Scancode, pKey->Unicode ); // OutputDebugString(DbgBuffer); //#endif return TRUE; } else { return FALSE; } } else { return FALSE; } } BOOL PutEvent ( PINPUT_RECORD InputRecord ) { EnterCriticalSection(&(EventBuffer.CriticalSection)); // // If no space at beginning of buffer, resize and shift right // if ( EventBuffer.EventIndex == 0 ) { EventBuffer.EventBuffer = REALLOC( EventBuffer.EventBuffer, (EventBuffer.MaxEvents + EVENT_INCREMENT) * sizeof(INPUT_RECORD)); if ( !EventBuffer.EventBuffer ) { //CleanExit(1, 0); } memmove( EventBuffer.EventBuffer + EVENT_INCREMENT, EventBuffer.EventBuffer , EventBuffer.NumberOfEvents * sizeof(INPUT_RECORD) ); EventBuffer.EventIndex = EVENT_INCREMENT; } // // Add event // EventBuffer.EventIndex--; EventBuffer.NumberOfEvents++; memcpy( EventBuffer.EventBuffer + EventBuffer.EventIndex, InputRecord, sizeof(INPUT_RECORD )); LeaveCriticalSection(&(EventBuffer.CriticalSection)); return TRUE; }