| /** @file | |
| Copyright (c) 2007, Intel Corporation. All rights reserved.<BR> | |
| 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; | |
| } |