/** @file Copyright (c) 2007, Intel Corporation. All rights reserved.
SPDX-License-Identifier: BSD-2-Clause-Patent **/ #include "Edb.h" /** Set the current coordinates of the cursor position. @param ConOut Point to EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL. @param Column The position to set the cursor to. @param Row The position to set the cursor to. @param LineLength Length of a line. @param TotalRow Total row of a screen. @param Str Point to the string. @param StrPos The position of the string. @param Len The length of the string. **/ 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 ); /** Function waits for a given event to fire, or for an optional timeout to expire. @param Event - The event to wait for @param Timeout - An optional timeout value in 100 ns units. @retval EFI_SUCCESS - Event fired before Timeout expired. @retval EFI_TIME_OUT - Timout expired before Event fired.. **/ EFI_STATUS EFIAPI WaitForSingleEvent ( IN EFI_EVENT Event, IN UINT64 Timeout OPTIONAL ) { EFI_STATUS Status; UINTN Index; EFI_EVENT TimerEvent; EFI_EVENT WaitList[2]; if (Timeout != 0) { // // 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; } /** Move the cursor position one character backward. @param LineLength Length of a line. Get it by calling QueryMode @param Column Current column of the cursor position @param Row Current row of the cursor position **/ VOID EFIAPI ConMoveCursorBackward ( IN UINTN LineLength, IN OUT UINTN *Column, IN OUT UINTN *Row ) { 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)--; } } /** Move the cursor position one character backward. @param LineLength Length of a line. Get it by calling QueryMode @param TotalRow Total row of a screen, get by calling QueryMode @param Column Current column of the cursor position @param Row Current row of the cursor position **/ VOID EFIAPI ConMoveCursorForward ( IN UINTN LineLength, IN UINTN TotalRow, IN OUT UINTN *Column, IN OUT UINTN *Row ) { 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]; /** Get user input. @param Prompt The prompt string. @param InStr Point to the input string. @param StrLength The max length of string user can input. **/ 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 != NULL) { 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 != 0) { // // 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 != 0) { 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 != 0) { 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 != 0) { 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 ; } /** Set the current coordinates of the cursor position. @param ConOut Point to EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL. @param Column The position to set the cursor to. @param Row The position to set the cursor to. @param LineLength Length of a line. @param TotalRow Total row of a screen. @param Str Point to the string. @param StrPos The position of the string. @param Len The length of the string. **/ 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); } /** SetPageBreak. **/ 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; } /** Print a Unicode string to the output device. @param Format A Null-terminated Unicode format string. @param ... The variable argument list that contains pointers to Null- terminated Unicode strings to be printed **/ 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; } /** Print a Unicode string to the output buffer. @param Buffer A pointer to the output buffer for the produced Null-terminated Unicode string. @param BufferSize The size, in bytes, of the output buffer specified by StartOfBuffer. @param Format A Null-terminated Unicode format string. @param ... The variable argument list that contains pointers to Null- terminated Unicode strings to be printed **/ 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; } /** Print a Unicode string to the output buffer with specified offset.. @param Buffer A pointer to the output buffer for the produced Null-terminated Unicode string. @param BufferSize The size, in bytes, of the output buffer specified by StartOfBuffer. @param Offset The offset of the buffer. @param Format A Null-terminated Unicode format string. @param ... The variable argument list that contains pointers to Null- terminated Unicode strings to be printed **/ 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; }