You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
2200 lines
42 KiB
2200 lines
42 KiB
/*++
|
|
|
|
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;
|
|
}
|