|
|
/*++
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 <string.h>
#include <malloc.h>
#include <assert.h>
#include <windows.h>
#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; }
|