/*++ | |
Copyright (c) 2007, Intel Corporation | |
All rights reserved. This program and the accompanying materials | |
are licensed and made available under the terms and conditions of the BSD License | |
which accompanies this distribution. The full text of the license may be found at | |
http://opensource.org/licenses/bsd-license.php | |
THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, | |
WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. | |
Module Name: | |
EdbSupportUI.c | |
Abstract: | |
--*/ | |
#include "Edb.h" | |
VOID | |
EFIAPI | |
SetCursorPosition ( | |
IN EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL *ConOut, | |
IN UINTN Column, | |
IN INTN Row, | |
IN UINTN LineLength, | |
IN UINTN TotalRow, | |
IN CHAR16 *Str, | |
IN UINTN StrPos, | |
IN UINTN Len | |
); | |
EFI_STATUS | |
EFIAPI | |
WaitForSingleEvent ( | |
IN EFI_EVENT Event, | |
IN UINT64 Timeout OPTIONAL | |
) | |
/*++ | |
Routine Description: | |
Function waits for a given event to fire, or for an optional timeout to expire. | |
Arguments: | |
Event - The event to wait for | |
Timeout - An optional timeout value in 100 ns units. | |
Returns: | |
EFI_SUCCESS - Event fired before Timeout expired. | |
EFI_TIME_OUT - Timout expired before Event fired.. | |
--*/ | |
{ | |
EFI_STATUS Status; | |
UINTN Index; | |
EFI_EVENT TimerEvent; | |
EFI_EVENT WaitList[2]; | |
if (Timeout) { | |
// | |
// Create a timer event | |
// | |
Status = gBS->CreateEvent (EVT_TIMER, 0, NULL, NULL, &TimerEvent); | |
if (!EFI_ERROR (Status)) { | |
// | |
// Set the timer event | |
// | |
gBS->SetTimer ( | |
TimerEvent, | |
TimerRelative, | |
Timeout | |
); | |
// | |
// Wait for the original event or the timer | |
// | |
WaitList[0] = Event; | |
WaitList[1] = TimerEvent; | |
Status = gBS->WaitForEvent (2, WaitList, &Index); | |
gBS->CloseEvent (TimerEvent); | |
// | |
// If the timer expired, change the return to timed out | |
// | |
if (!EFI_ERROR (Status) && Index == 1) { | |
Status = EFI_TIMEOUT; | |
} | |
} | |
} else { | |
// | |
// No timeout... just wait on the event | |
// | |
Status = gBS->WaitForEvent (1, &Event, &Index); | |
ASSERT (!EFI_ERROR (Status)); | |
ASSERT (Index == 0); | |
} | |
return Status; | |
} | |
VOID | |
EFIAPI | |
ConMoveCursorBackward ( | |
IN UINTN LineLength, | |
IN OUT UINTN *Column, | |
IN OUT UINTN *Row | |
) | |
/*++ | |
Routine Description: | |
Move the cursor position one character backward. | |
Arguments: | |
LineLength Length of a line. Get it by calling QueryMode | |
Column Current column of the cursor position | |
Row Current row of the cursor position | |
Returns: | |
--*/ | |
{ | |
ASSERT (Column != NULL); | |
ASSERT (Row != NULL); | |
// | |
// If current column is 0, move to the last column of the previous line, | |
// otherwise, just decrement column. | |
// | |
if (*Column == 0) { | |
(*Column) = LineLength - 1; | |
// | |
// if (*Row > 0) { | |
// | |
(*Row)--; | |
// | |
// } | |
// | |
} else { | |
(*Column)--; | |
} | |
} | |
VOID | |
EFIAPI | |
ConMoveCursorForward ( | |
IN UINTN LineLength, | |
IN UINTN TotalRow, | |
IN OUT UINTN *Column, | |
IN OUT UINTN *Row | |
) | |
/*++ | |
Routine Description: | |
Move the cursor position one character backward. | |
Arguments: | |
LineLength Length of a line. Get it by calling QueryMode | |
TotalRow Total row of a screen, get by calling QueryMode | |
Column Current column of the cursor position | |
Row Current row of the cursor position | |
Returns: | |
--*/ | |
{ | |
ASSERT (Column != NULL); | |
ASSERT (Row != NULL); | |
// | |
// If current column is at line end, move to the first column of the nest | |
// line, otherwise, just increment column. | |
// | |
(*Column)++; | |
if (*Column >= LineLength) { | |
(*Column) = 0; | |
if ((*Row) < TotalRow - 1) { | |
(*Row)++; | |
} | |
} | |
} | |
CHAR16 mBackupSpace[EFI_DEBUG_INPUS_BUFFER_SIZE]; | |
CHAR16 mInputBufferHistory[EFI_DEBUG_INPUS_BUFFER_SIZE]; | |
VOID | |
EFIAPI | |
Input ( | |
IN CHAR16 *Prompt OPTIONAL, | |
OUT CHAR16 *InStr, | |
IN UINTN StrLength | |
) | |
{ | |
EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL *ConOut; | |
EFI_SIMPLE_TEXT_INPUT_PROTOCOL *ConIn; | |
BOOLEAN Done; | |
UINTN Column; | |
UINTN Row; | |
UINTN StartColumn; | |
UINTN Update; | |
UINTN Delete; | |
UINTN Len; | |
UINTN StrPos; | |
UINTN Index; | |
UINTN LineLength; | |
UINTN TotalRow; | |
UINTN SkipLength; | |
UINTN OutputLength; | |
UINTN TailRow; | |
UINTN TailColumn; | |
EFI_INPUT_KEY Key; | |
BOOLEAN InsertMode; | |
BOOLEAN NeedAdjust; | |
UINTN SubIndex; | |
CHAR16 *CommandStr; | |
ConOut = gST->ConOut; | |
ConIn = gST->ConIn; | |
ASSERT (ConOut != NULL); | |
ASSERT (ConIn != NULL); | |
ASSERT (InStr != NULL); | |
if (Prompt) { | |
ConOut->OutputString (ConOut, Prompt); | |
} | |
// | |
// Read a line from the console | |
// | |
Len = 0; | |
StrPos = 0; | |
OutputLength = 0; | |
Update = 0; | |
Delete = 0; | |
InsertMode = TRUE; | |
NeedAdjust = FALSE; | |
// | |
// If buffer is not large enough to hold a CHAR16, do nothing. | |
// | |
if (StrLength < 1) { | |
return ; | |
} | |
// | |
// Get the screen setting and the current cursor location | |
// | |
StartColumn = ConOut->Mode->CursorColumn; | |
Column = StartColumn; | |
Row = ConOut->Mode->CursorRow; | |
ConOut->QueryMode (ConOut, ConOut->Mode->Mode, &LineLength, &TotalRow); | |
if (LineLength == 0) { | |
return ; | |
} | |
SetMem (InStr, StrLength * sizeof (CHAR16), 0); | |
Done = FALSE; | |
do { | |
// | |
// Read a key | |
// | |
WaitForSingleEvent (ConIn->WaitForKey, 0); | |
ConIn->ReadKeyStroke (ConIn, &Key); | |
switch (Key.UnicodeChar) { | |
case CHAR_CARRIAGE_RETURN: | |
// | |
// All done, print a newline at the end of the string | |
// | |
TailRow = Row + (Len - StrPos + Column) / LineLength; | |
TailColumn = (Len - StrPos + Column) % LineLength; | |
Done = TRUE; | |
break; | |
case CHAR_BACKSPACE: | |
if (StrPos) { | |
// | |
// If not move back beyond string beginning, move all characters behind | |
// the current position one character forward | |
// | |
StrPos -= 1; | |
Update = StrPos; | |
Delete = 1; | |
CopyMem (InStr + StrPos, InStr + StrPos + 1, sizeof (CHAR16) * (Len - StrPos)); | |
// | |
// Adjust the current column and row | |
// | |
ConMoveCursorBackward (LineLength, &Column, &Row); | |
NeedAdjust = TRUE; | |
} | |
break; | |
default: | |
if (Key.UnicodeChar >= ' ') { | |
// | |
// If we are at the buffer's end, drop the key | |
// | |
if (Len == StrLength - 1 && (InsertMode || StrPos == Len)) { | |
break; | |
} | |
// | |
// If in insert mode, move all characters behind the current position | |
// one character backward to make space for this character. Then store | |
// the character. | |
// | |
if (InsertMode) { | |
for (Index = Len; Index > StrPos; Index -= 1) { | |
InStr[Index] = InStr[Index - 1]; | |
} | |
} | |
InStr[StrPos] = Key.UnicodeChar; | |
Update = StrPos; | |
StrPos += 1; | |
OutputLength = 1; | |
} | |
break; | |
case 0: | |
switch (Key.ScanCode) { | |
case SCAN_DELETE: | |
// | |
// Move characters behind current position one character forward | |
// | |
if (Len) { | |
Update = StrPos; | |
Delete = 1; | |
CopyMem (InStr + StrPos, InStr + StrPos + 1, sizeof (CHAR16) * (Len - StrPos)); | |
NeedAdjust = TRUE; | |
} | |
break; | |
case SCAN_LEFT: | |
// | |
// Adjust current cursor position | |
// | |
if (StrPos) { | |
StrPos -= 1; | |
ConMoveCursorBackward (LineLength, &Column, &Row); | |
} | |
break; | |
case SCAN_RIGHT: | |
// | |
// Adjust current cursor position | |
// | |
if (StrPos < Len) { | |
StrPos += 1; | |
ConMoveCursorForward (LineLength, TotalRow, &Column, &Row); | |
} | |
break; | |
case SCAN_HOME: | |
// | |
// Move current cursor position to the beginning of the command line | |
// | |
Row -= (StrPos + StartColumn) / LineLength; | |
Column = StartColumn; | |
StrPos = 0; | |
break; | |
case SCAN_END: | |
// | |
// Move current cursor position to the end of the command line | |
// | |
TailRow = Row + (Len - StrPos + Column) / LineLength; | |
TailColumn = (Len - StrPos + Column) % LineLength; | |
Row = TailRow; | |
Column = TailColumn; | |
StrPos = Len; | |
break; | |
case SCAN_ESC: | |
// | |
// Prepare to clear the current command line | |
// | |
InStr[0] = 0; | |
Update = 0; | |
Delete = Len; | |
Row -= (StrPos + StartColumn) / LineLength; | |
Column = StartColumn; | |
OutputLength = 0; | |
NeedAdjust = TRUE; | |
break; | |
case SCAN_INSERT: | |
// | |
// Toggle the SEnvInsertMode flag | |
// | |
InsertMode = (BOOLEAN)!InsertMode; | |
break; | |
case SCAN_UP: | |
case SCAN_DOWN: | |
// | |
// show history | |
// | |
CopyMem (InStr, mInputBufferHistory, StrLength * sizeof(CHAR16)); | |
StrPos = StrLen (mInputBufferHistory); | |
Update = 0; | |
Delete = 0; | |
OutputLength = 0; | |
TailRow = Row + (StrPos + StartColumn) / LineLength; | |
TailColumn = (StrPos + StartColumn) % LineLength; | |
Row = TailRow; | |
Column = TailColumn; | |
NeedAdjust = FALSE; | |
ConOut->SetCursorPosition (ConOut, StartColumn, Row); | |
for (SubIndex = 0; SubIndex < EFI_DEBUG_INPUS_BUFFER_SIZE - (StartColumn - EFI_DEBUG_PROMPT_COLUMN); SubIndex++) { | |
mBackupSpace[SubIndex] = L' '; | |
} | |
EDBPrint (mBackupSpace); | |
SetMem (mBackupSpace, (EFI_DEBUG_INPUS_BUFFER_SIZE - (StartColumn - EFI_DEBUG_PROMPT_COLUMN)) * sizeof(CHAR16), 0); | |
ConOut->SetCursorPosition (ConOut, StartColumn, Row); | |
Len = StrPos; | |
break; | |
case SCAN_F1: | |
case SCAN_F2: | |
case SCAN_F3: | |
case SCAN_F4: | |
case SCAN_F5: | |
case SCAN_F6: | |
case SCAN_F7: | |
case SCAN_F8: | |
case SCAN_F9: | |
case SCAN_F10: | |
case SCAN_F11: | |
case SCAN_F12: | |
CommandStr = GetCommandNameByKey (Key); | |
if (CommandStr != NULL) { | |
StrnCpyS (InStr, StrLength, CommandStr, StrLength - 1); | |
return ; | |
} | |
break; | |
} | |
} | |
if (Done) { | |
break; | |
} | |
// | |
// If we need to update the output do so now | |
// | |
if (Update != -1) { | |
if (NeedAdjust) { | |
ConOut->SetCursorPosition (ConOut, Column, Row); | |
for (SubIndex = 0; SubIndex < EFI_DEBUG_INPUS_BUFFER_SIZE - (Column - EFI_DEBUG_PROMPT_COLUMN); SubIndex++) { | |
mBackupSpace[SubIndex] = L' '; | |
} | |
EDBPrint (mBackupSpace); | |
SetMem (mBackupSpace, (EFI_DEBUG_INPUS_BUFFER_SIZE - (Column - EFI_DEBUG_PROMPT_COLUMN)) * sizeof(CHAR16), 0); | |
ConOut->SetCursorPosition (ConOut, Column, Row); | |
NeedAdjust = FALSE; | |
} | |
EDBPrint (InStr + Update); | |
Len = StrLen (InStr); | |
if (Delete) { | |
SetMem (InStr + Len, Delete * sizeof (CHAR16), 0x00); | |
} | |
if (StrPos > Len) { | |
StrPos = Len; | |
} | |
Update = (UINTN) -1; | |
// | |
// After using print to reflect newly updates, if we're not using | |
// BACKSPACE and DELETE, we need to move the cursor position forward, | |
// so adjust row and column here. | |
// | |
if (Key.UnicodeChar != CHAR_BACKSPACE && !(Key.UnicodeChar == 0 && Key.ScanCode == SCAN_DELETE)) { | |
// | |
// Calulate row and column of the tail of current string | |
// | |
TailRow = Row + (Len - StrPos + Column + OutputLength) / LineLength; | |
TailColumn = (Len - StrPos + Column + OutputLength) % LineLength; | |
// | |
// If the tail of string reaches screen end, screen rolls up, so if | |
// Row does not equal TailRow, Row should be decremented | |
// | |
// (if we are recalling commands using UPPER and DOWN key, and if the | |
// old command is too long to fit the screen, TailColumn must be 79. | |
// | |
if (TailColumn == 0 && TailRow >= TotalRow && (UINTN) Row != TailRow) { | |
Row--; | |
} | |
// | |
// Calculate the cursor position after current operation. If cursor | |
// reaches line end, update both row and column, otherwise, only | |
// column will be changed. | |
// | |
if (Column + OutputLength >= LineLength) { | |
SkipLength = OutputLength - (LineLength - Column); | |
Row += SkipLength / LineLength + 1; | |
if ((UINTN) Row > TotalRow - 1) { | |
Row = TotalRow - 1; | |
} | |
Column = SkipLength % LineLength; | |
} else { | |
Column += OutputLength; | |
} | |
} | |
Delete = 0; | |
} | |
// | |
// Set the cursor position for this key | |
// | |
SetCursorPosition (ConOut, Column, Row, LineLength, TotalRow, InStr, StrPos, Len); | |
} while (!Done); | |
CopyMem (mInputBufferHistory, InStr, StrLength * sizeof(CHAR16)); | |
// | |
// Return the data to the caller | |
// | |
return ; | |
} | |
VOID | |
EFIAPI | |
SetCursorPosition ( | |
IN EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL *ConOut, | |
IN UINTN Column, | |
IN INTN Row, | |
IN UINTN LineLength, | |
IN UINTN TotalRow, | |
IN CHAR16 *Str, | |
IN UINTN StrPos, | |
IN UINTN Len | |
) | |
{ | |
CHAR16 Backup; | |
ASSERT (ConOut != NULL); | |
ASSERT (Str != NULL); | |
Backup = 0; | |
if (Row >= 0) { | |
ConOut->SetCursorPosition (ConOut, Column, Row); | |
return ; | |
} | |
if (Len - StrPos > Column * Row) { | |
Backup = *(Str + StrPos + Column * Row); | |
*(Str + StrPos + Column * Row) = 0; | |
} | |
EDBPrint (L"%s", Str + StrPos); | |
if (Len - StrPos > Column * Row) { | |
*(Str + StrPos + Column * Row) = Backup; | |
} | |
ConOut->SetCursorPosition (ConOut, 0, 0); | |
} | |
BOOLEAN | |
EFIAPI | |
SetPageBreak ( | |
VOID | |
) | |
{ | |
EFI_INPUT_KEY Key; | |
CHAR16 Str[3]; | |
BOOLEAN OmitPrint; | |
// | |
// Check | |
// | |
if (!mDebuggerPrivate.EnablePageBreak) { | |
return FALSE; | |
} | |
gST->ConOut->OutputString (gST->ConOut, L"Press ENTER to continue, 'q' to exit:"); | |
OmitPrint = FALSE; | |
// | |
// Wait for user input | |
// | |
Str[0] = ' '; | |
Str[1] = 0; | |
Str[2] = 0; | |
for (;;) { | |
WaitForSingleEvent (gST->ConIn->WaitForKey, 0); | |
gST->ConIn->ReadKeyStroke (gST->ConIn, &Key); | |
// | |
// handle control keys | |
// | |
if (Key.UnicodeChar == CHAR_NULL) { | |
if (Key.ScanCode == SCAN_ESC) { | |
gST->ConOut->OutputString (gST->ConOut, L"\r\n"); | |
OmitPrint = TRUE; | |
break; | |
} | |
continue; | |
} | |
if (Key.UnicodeChar == CHAR_CARRIAGE_RETURN) { | |
gST->ConOut->OutputString (gST->ConOut, L"\r\n"); | |
break; | |
} | |
// | |
// Echo input | |
// | |
Str[1] = Key.UnicodeChar; | |
if (Str[1] == CHAR_BACKSPACE) { | |
continue; | |
} | |
gST->ConOut->OutputString (gST->ConOut, Str); | |
if ((Str[1] == L'q') || (Str[1] == L'Q')) { | |
OmitPrint = TRUE; | |
} else { | |
OmitPrint = FALSE; | |
} | |
Str[0] = CHAR_BACKSPACE; | |
} | |
return OmitPrint; | |
} | |
UINTN | |
EFIAPI | |
EDBPrint ( | |
IN CONST CHAR16 *Format, | |
... | |
) | |
{ | |
UINTN Return; | |
VA_LIST Marker; | |
CHAR16 Buffer[EFI_DEBUG_MAX_PRINT_BUFFER]; | |
VA_START (Marker, Format); | |
Return = UnicodeVSPrint (Buffer, sizeof (Buffer), Format, Marker); | |
VA_END (Marker); | |
if (gST->ConOut != NULL) { | |
// | |
// To be extra safe make sure ConOut has been initialized | |
// | |
gST->ConOut->OutputString (gST->ConOut, Buffer); | |
} | |
return Return; | |
} | |
UINTN | |
EFIAPI | |
EDBSPrint ( | |
OUT CHAR16 *Buffer, | |
IN INTN BufferSize, | |
IN CONST CHAR16 *Format, | |
... | |
) | |
{ | |
UINTN Return; | |
VA_LIST Marker; | |
ASSERT (BufferSize > 0); | |
VA_START (Marker, Format); | |
Return = UnicodeVSPrint (Buffer, (UINTN)BufferSize, Format, Marker); | |
VA_END (Marker); | |
return Return; | |
} | |
UINTN | |
EFIAPI | |
EDBSPrintWithOffset ( | |
OUT CHAR16 *Buffer, | |
IN INTN BufferSize, | |
IN UINTN Offset, | |
IN CONST CHAR16 *Format, | |
... | |
) | |
{ | |
UINTN Return; | |
VA_LIST Marker; | |
ASSERT (BufferSize - (Offset * sizeof(CHAR16)) > 0); | |
VA_START (Marker, Format); | |
Return = UnicodeVSPrint (Buffer + Offset, (UINTN)(BufferSize - (Offset * sizeof(CHAR16))), Format, Marker); | |
VA_END (Marker); | |
return Return; | |
} |