1 /** @file 2 3 Copyright (c) 2007, Intel Corporation. All rights reserved.<BR> 4 This program and the accompanying materials 5 are licensed and made available under the terms and conditions of the BSD License 6 which accompanies this distribution. The full text of the license may be found at 7 http://opensource.org/licenses/bsd-license.php 8 9 THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, 10 WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. 11 12 13 **/ 14 15 #include "Edb.h" 16 17 /** 18 Set the current coordinates of the cursor position. 19 20 @param ConOut Point to EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL. 21 @param Column The position to set the cursor to. 22 @param Row The position to set the cursor to. 23 @param LineLength Length of a line. 24 @param TotalRow Total row of a screen. 25 @param Str Point to the string. 26 @param StrPos The position of the string. 27 @param Len The length of the string. 28 29 **/ 30 VOID 31 EFIAPI 32 SetCursorPosition ( 33 IN EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL *ConOut, 34 IN UINTN Column, 35 IN INTN Row, 36 IN UINTN LineLength, 37 IN UINTN TotalRow, 38 IN CHAR16 *Str, 39 IN UINTN StrPos, 40 IN UINTN Len 41 ); 42 43 /** 44 45 Function waits for a given event to fire, or for an optional timeout to expire. 46 47 @param Event - The event to wait for 48 @param Timeout - An optional timeout value in 100 ns units. 49 50 @retval EFI_SUCCESS - Event fired before Timeout expired. 51 @retval EFI_TIME_OUT - Timout expired before Event fired.. 52 53 **/ 54 EFI_STATUS 55 EFIAPI 56 WaitForSingleEvent ( 57 IN EFI_EVENT Event, 58 IN UINT64 Timeout OPTIONAL 59 ) 60 { 61 EFI_STATUS Status; 62 UINTN Index; 63 EFI_EVENT TimerEvent; 64 EFI_EVENT WaitList[2]; 65 66 if (Timeout != 0) { 67 // 68 // Create a timer event 69 // 70 Status = gBS->CreateEvent (EVT_TIMER, 0, NULL, NULL, &TimerEvent); 71 if (!EFI_ERROR (Status)) { 72 // 73 // Set the timer event 74 // 75 gBS->SetTimer ( 76 TimerEvent, 77 TimerRelative, 78 Timeout 79 ); 80 81 // 82 // Wait for the original event or the timer 83 // 84 WaitList[0] = Event; 85 WaitList[1] = TimerEvent; 86 Status = gBS->WaitForEvent (2, WaitList, &Index); 87 gBS->CloseEvent (TimerEvent); 88 89 // 90 // If the timer expired, change the return to timed out 91 // 92 if (!EFI_ERROR (Status) && Index == 1) { 93 Status = EFI_TIMEOUT; 94 } 95 } 96 } else { 97 // 98 // No timeout... just wait on the event 99 // 100 Status = gBS->WaitForEvent (1, &Event, &Index); 101 ASSERT (!EFI_ERROR (Status)); 102 ASSERT (Index == 0); 103 } 104 105 return Status; 106 } 107 108 /** 109 110 Move the cursor position one character backward. 111 112 @param LineLength Length of a line. Get it by calling QueryMode 113 @param Column Current column of the cursor position 114 @param Row Current row of the cursor position 115 116 **/ 117 VOID 118 EFIAPI 119 ConMoveCursorBackward ( 120 IN UINTN LineLength, 121 IN OUT UINTN *Column, 122 IN OUT UINTN *Row 123 ) 124 { 125 ASSERT (Column != NULL); 126 ASSERT (Row != NULL); 127 // 128 // If current column is 0, move to the last column of the previous line, 129 // otherwise, just decrement column. 130 // 131 if (*Column == 0) { 132 (*Column) = LineLength - 1; 133 // 134 // if (*Row > 0) { 135 // 136 (*Row)--; 137 // 138 // } 139 // 140 } else { 141 (*Column)--; 142 } 143 } 144 145 /** 146 147 Move the cursor position one character backward. 148 149 @param LineLength Length of a line. Get it by calling QueryMode 150 @param TotalRow Total row of a screen, get by calling QueryMode 151 @param Column Current column of the cursor position 152 @param Row Current row of the cursor position 153 154 **/ 155 VOID 156 EFIAPI 157 ConMoveCursorForward ( 158 IN UINTN LineLength, 159 IN UINTN TotalRow, 160 IN OUT UINTN *Column, 161 IN OUT UINTN *Row 162 ) 163 { 164 ASSERT (Column != NULL); 165 ASSERT (Row != NULL); 166 // 167 // If current column is at line end, move to the first column of the nest 168 // line, otherwise, just increment column. 169 // 170 (*Column)++; 171 if (*Column >= LineLength) { 172 (*Column) = 0; 173 if ((*Row) < TotalRow - 1) { 174 (*Row)++; 175 } 176 } 177 } 178 179 CHAR16 mBackupSpace[EFI_DEBUG_INPUS_BUFFER_SIZE]; 180 CHAR16 mInputBufferHistory[EFI_DEBUG_INPUS_BUFFER_SIZE]; 181 182 /** 183 184 Get user input. 185 186 @param Prompt The prompt string. 187 @param InStr Point to the input string. 188 @param StrLength The max length of string user can input. 189 190 **/ 191 VOID 192 EFIAPI 193 Input ( 194 IN CHAR16 *Prompt OPTIONAL, 195 OUT CHAR16 *InStr, 196 IN UINTN StrLength 197 ) 198 { 199 EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL *ConOut; 200 EFI_SIMPLE_TEXT_INPUT_PROTOCOL *ConIn; 201 BOOLEAN Done; 202 UINTN Column; 203 UINTN Row; 204 UINTN StartColumn; 205 UINTN Update; 206 UINTN Delete; 207 UINTN Len; 208 UINTN StrPos; 209 UINTN Index; 210 UINTN LineLength; 211 UINTN TotalRow; 212 UINTN SkipLength; 213 UINTN OutputLength; 214 UINTN TailRow; 215 UINTN TailColumn; 216 EFI_INPUT_KEY Key; 217 BOOLEAN InsertMode; 218 BOOLEAN NeedAdjust; 219 UINTN SubIndex; 220 CHAR16 *CommandStr; 221 222 ConOut = gST->ConOut; 223 ConIn = gST->ConIn; 224 225 ASSERT (ConOut != NULL); 226 ASSERT (ConIn != NULL); 227 ASSERT (InStr != NULL); 228 229 if (Prompt != NULL) { 230 ConOut->OutputString (ConOut, Prompt); 231 } 232 // 233 // Read a line from the console 234 // 235 Len = 0; 236 StrPos = 0; 237 OutputLength = 0; 238 Update = 0; 239 Delete = 0; 240 InsertMode = TRUE; 241 NeedAdjust = FALSE; 242 243 // 244 // If buffer is not large enough to hold a CHAR16, do nothing. 245 // 246 if (StrLength < 1) { 247 return ; 248 } 249 // 250 // Get the screen setting and the current cursor location 251 // 252 StartColumn = ConOut->Mode->CursorColumn; 253 Column = StartColumn; 254 Row = ConOut->Mode->CursorRow; 255 ConOut->QueryMode (ConOut, ConOut->Mode->Mode, &LineLength, &TotalRow); 256 if (LineLength == 0) { 257 return ; 258 } 259 260 SetMem (InStr, StrLength * sizeof (CHAR16), 0); 261 Done = FALSE; 262 do { 263 // 264 // Read a key 265 // 266 WaitForSingleEvent (ConIn->WaitForKey, 0); 267 ConIn->ReadKeyStroke (ConIn, &Key); 268 269 switch (Key.UnicodeChar) { 270 case CHAR_CARRIAGE_RETURN: 271 // 272 // All done, print a newline at the end of the string 273 // 274 TailRow = Row + (Len - StrPos + Column) / LineLength; 275 TailColumn = (Len - StrPos + Column) % LineLength; 276 Done = TRUE; 277 break; 278 279 case CHAR_BACKSPACE: 280 if (StrPos != 0) { 281 // 282 // If not move back beyond string beginning, move all characters behind 283 // the current position one character forward 284 // 285 StrPos -= 1; 286 Update = StrPos; 287 Delete = 1; 288 CopyMem (InStr + StrPos, InStr + StrPos + 1, sizeof (CHAR16) * (Len - StrPos)); 289 290 // 291 // Adjust the current column and row 292 // 293 ConMoveCursorBackward (LineLength, &Column, &Row); 294 295 NeedAdjust = TRUE; 296 } 297 break; 298 299 default: 300 if (Key.UnicodeChar >= ' ') { 301 // 302 // If we are at the buffer's end, drop the key 303 // 304 if (Len == StrLength - 1 && (InsertMode || StrPos == Len)) { 305 break; 306 } 307 // 308 // If in insert mode, move all characters behind the current position 309 // one character backward to make space for this character. Then store 310 // the character. 311 // 312 if (InsertMode) { 313 for (Index = Len; Index > StrPos; Index -= 1) { 314 InStr[Index] = InStr[Index - 1]; 315 } 316 } 317 318 InStr[StrPos] = Key.UnicodeChar; 319 Update = StrPos; 320 321 StrPos += 1; 322 OutputLength = 1; 323 } 324 break; 325 326 case 0: 327 switch (Key.ScanCode) { 328 case SCAN_DELETE: 329 // 330 // Move characters behind current position one character forward 331 // 332 if (Len != 0) { 333 Update = StrPos; 334 Delete = 1; 335 CopyMem (InStr + StrPos, InStr + StrPos + 1, sizeof (CHAR16) * (Len - StrPos)); 336 337 NeedAdjust = TRUE; 338 } 339 break; 340 341 case SCAN_LEFT: 342 // 343 // Adjust current cursor position 344 // 345 if (StrPos != 0) { 346 StrPos -= 1; 347 ConMoveCursorBackward (LineLength, &Column, &Row); 348 } 349 break; 350 351 case SCAN_RIGHT: 352 // 353 // Adjust current cursor position 354 // 355 if (StrPos < Len) { 356 StrPos += 1; 357 ConMoveCursorForward (LineLength, TotalRow, &Column, &Row); 358 } 359 break; 360 361 case SCAN_HOME: 362 // 363 // Move current cursor position to the beginning of the command line 364 // 365 Row -= (StrPos + StartColumn) / LineLength; 366 Column = StartColumn; 367 StrPos = 0; 368 break; 369 370 case SCAN_END: 371 // 372 // Move current cursor position to the end of the command line 373 // 374 TailRow = Row + (Len - StrPos + Column) / LineLength; 375 TailColumn = (Len - StrPos + Column) % LineLength; 376 Row = TailRow; 377 Column = TailColumn; 378 StrPos = Len; 379 break; 380 381 case SCAN_ESC: 382 // 383 // Prepare to clear the current command line 384 // 385 InStr[0] = 0; 386 Update = 0; 387 Delete = Len; 388 Row -= (StrPos + StartColumn) / LineLength; 389 Column = StartColumn; 390 OutputLength = 0; 391 392 NeedAdjust = TRUE; 393 break; 394 395 case SCAN_INSERT: 396 // 397 // Toggle the SEnvInsertMode flag 398 // 399 InsertMode = (BOOLEAN)!InsertMode; 400 break; 401 402 case SCAN_UP: 403 case SCAN_DOWN: 404 // 405 // show history 406 // 407 CopyMem (InStr, mInputBufferHistory, StrLength * sizeof(CHAR16)); 408 StrPos = StrLen (mInputBufferHistory); 409 Update = 0; 410 Delete = 0; 411 OutputLength = 0; 412 413 TailRow = Row + (StrPos + StartColumn) / LineLength; 414 TailColumn = (StrPos + StartColumn) % LineLength; 415 Row = TailRow; 416 Column = TailColumn; 417 NeedAdjust = FALSE; 418 419 ConOut->SetCursorPosition (ConOut, StartColumn, Row); 420 for (SubIndex = 0; SubIndex < EFI_DEBUG_INPUS_BUFFER_SIZE - (StartColumn - EFI_DEBUG_PROMPT_COLUMN); SubIndex++) { 421 mBackupSpace[SubIndex] = L' '; 422 } 423 EDBPrint (mBackupSpace); 424 SetMem (mBackupSpace, (EFI_DEBUG_INPUS_BUFFER_SIZE - (StartColumn - EFI_DEBUG_PROMPT_COLUMN)) * sizeof(CHAR16), 0); 425 426 ConOut->SetCursorPosition (ConOut, StartColumn, Row); 427 Len = StrPos; 428 429 break; 430 431 case SCAN_F1: 432 case SCAN_F2: 433 case SCAN_F3: 434 case SCAN_F4: 435 case SCAN_F5: 436 case SCAN_F6: 437 case SCAN_F7: 438 case SCAN_F8: 439 case SCAN_F9: 440 case SCAN_F10: 441 case SCAN_F11: 442 case SCAN_F12: 443 CommandStr = GetCommandNameByKey (Key); 444 if (CommandStr != NULL) { 445 StrnCpyS (InStr, StrLength, CommandStr, StrLength - 1); 446 return ; 447 } 448 break; 449 } 450 } 451 452 if (Done) { 453 break; 454 } 455 // 456 // If we need to update the output do so now 457 // 458 if (Update != -1) { 459 if (NeedAdjust) { 460 ConOut->SetCursorPosition (ConOut, Column, Row); 461 for (SubIndex = 0; SubIndex < EFI_DEBUG_INPUS_BUFFER_SIZE - (Column - EFI_DEBUG_PROMPT_COLUMN); SubIndex++) { 462 mBackupSpace[SubIndex] = L' '; 463 } 464 EDBPrint (mBackupSpace); 465 SetMem (mBackupSpace, (EFI_DEBUG_INPUS_BUFFER_SIZE - (Column - EFI_DEBUG_PROMPT_COLUMN)) * sizeof(CHAR16), 0); 466 ConOut->SetCursorPosition (ConOut, Column, Row); 467 NeedAdjust = FALSE; 468 } 469 EDBPrint (InStr + Update); 470 Len = StrLen (InStr); 471 472 if (Delete != 0) { 473 SetMem (InStr + Len, Delete * sizeof (CHAR16), 0x00); 474 } 475 476 if (StrPos > Len) { 477 StrPos = Len; 478 } 479 480 Update = (UINTN) -1; 481 482 // 483 // After using print to reflect newly updates, if we're not using 484 // BACKSPACE and DELETE, we need to move the cursor position forward, 485 // so adjust row and column here. 486 // 487 if (Key.UnicodeChar != CHAR_BACKSPACE && !(Key.UnicodeChar == 0 && Key.ScanCode == SCAN_DELETE)) { 488 // 489 // Calulate row and column of the tail of current string 490 // 491 TailRow = Row + (Len - StrPos + Column + OutputLength) / LineLength; 492 TailColumn = (Len - StrPos + Column + OutputLength) % LineLength; 493 494 // 495 // If the tail of string reaches screen end, screen rolls up, so if 496 // Row does not equal TailRow, Row should be decremented 497 // 498 // (if we are recalling commands using UPPER and DOWN key, and if the 499 // old command is too long to fit the screen, TailColumn must be 79. 500 // 501 if (TailColumn == 0 && TailRow >= TotalRow && (UINTN) Row != TailRow) { 502 Row--; 503 } 504 // 505 // Calculate the cursor position after current operation. If cursor 506 // reaches line end, update both row and column, otherwise, only 507 // column will be changed. 508 // 509 if (Column + OutputLength >= LineLength) { 510 SkipLength = OutputLength - (LineLength - Column); 511 512 Row += SkipLength / LineLength + 1; 513 if ((UINTN) Row > TotalRow - 1) { 514 Row = TotalRow - 1; 515 } 516 517 Column = SkipLength % LineLength; 518 } else { 519 Column += OutputLength; 520 } 521 } 522 523 Delete = 0; 524 } 525 // 526 // Set the cursor position for this key 527 // 528 SetCursorPosition (ConOut, Column, Row, LineLength, TotalRow, InStr, StrPos, Len); 529 } while (!Done); 530 531 CopyMem (mInputBufferHistory, InStr, StrLength * sizeof(CHAR16)); 532 533 // 534 // Return the data to the caller 535 // 536 return ; 537 } 538 539 /** 540 Set the current coordinates of the cursor position. 541 542 @param ConOut Point to EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL. 543 @param Column The position to set the cursor to. 544 @param Row The position to set the cursor to. 545 @param LineLength Length of a line. 546 @param TotalRow Total row of a screen. 547 @param Str Point to the string. 548 @param StrPos The position of the string. 549 @param Len The length of the string. 550 551 **/ 552 VOID 553 EFIAPI 554 SetCursorPosition ( 555 IN EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL *ConOut, 556 IN UINTN Column, 557 IN INTN Row, 558 IN UINTN LineLength, 559 IN UINTN TotalRow, 560 IN CHAR16 *Str, 561 IN UINTN StrPos, 562 IN UINTN Len 563 ) 564 { 565 CHAR16 Backup; 566 567 ASSERT (ConOut != NULL); 568 ASSERT (Str != NULL); 569 570 Backup = 0; 571 if (Row >= 0) { 572 ConOut->SetCursorPosition (ConOut, Column, Row); 573 return ; 574 } 575 576 if (Len - StrPos > Column * Row) { 577 Backup = *(Str + StrPos + Column * Row); 578 *(Str + StrPos + Column * Row) = 0; 579 } 580 581 EDBPrint (L"%s", Str + StrPos); 582 if (Len - StrPos > Column * Row) { 583 *(Str + StrPos + Column * Row) = Backup; 584 } 585 586 ConOut->SetCursorPosition (ConOut, 0, 0); 587 } 588 589 /** 590 591 SetPageBreak. 592 593 **/ 594 BOOLEAN 595 EFIAPI 596 SetPageBreak ( 597 VOID 598 ) 599 { 600 EFI_INPUT_KEY Key; 601 CHAR16 Str[3]; 602 BOOLEAN OmitPrint; 603 604 // 605 // Check 606 // 607 if (!mDebuggerPrivate.EnablePageBreak) { 608 return FALSE; 609 } 610 611 gST->ConOut->OutputString (gST->ConOut, L"Press ENTER to continue, 'q' to exit:"); 612 613 OmitPrint = FALSE; 614 // 615 // Wait for user input 616 // 617 Str[0] = ' '; 618 Str[1] = 0; 619 Str[2] = 0; 620 for (;;) { 621 WaitForSingleEvent (gST->ConIn->WaitForKey, 0); 622 gST->ConIn->ReadKeyStroke (gST->ConIn, &Key); 623 624 // 625 // handle control keys 626 // 627 if (Key.UnicodeChar == CHAR_NULL) { 628 if (Key.ScanCode == SCAN_ESC) { 629 gST->ConOut->OutputString (gST->ConOut, L"\r\n"); 630 OmitPrint = TRUE; 631 break; 632 } 633 634 continue; 635 } 636 637 if (Key.UnicodeChar == CHAR_CARRIAGE_RETURN) { 638 gST->ConOut->OutputString (gST->ConOut, L"\r\n"); 639 break; 640 } 641 // 642 // Echo input 643 // 644 Str[1] = Key.UnicodeChar; 645 if (Str[1] == CHAR_BACKSPACE) { 646 continue; 647 } 648 649 gST->ConOut->OutputString (gST->ConOut, Str); 650 651 if ((Str[1] == L'q') || (Str[1] == L'Q')) { 652 OmitPrint = TRUE; 653 } else { 654 OmitPrint = FALSE; 655 } 656 657 Str[0] = CHAR_BACKSPACE; 658 } 659 660 return OmitPrint; 661 } 662 663 /** 664 Print a Unicode string to the output device. 665 666 @param Format A Null-terminated Unicode format string. 667 @param ... The variable argument list that contains pointers to Null- 668 terminated Unicode strings to be printed 669 670 **/ 671 UINTN 672 EFIAPI 673 EDBPrint ( 674 IN CONST CHAR16 *Format, 675 ... 676 ) 677 { 678 UINTN Return; 679 VA_LIST Marker; 680 CHAR16 Buffer[EFI_DEBUG_MAX_PRINT_BUFFER]; 681 682 VA_START (Marker, Format); 683 Return = UnicodeVSPrint (Buffer, sizeof (Buffer), Format, Marker); 684 VA_END (Marker); 685 686 if (gST->ConOut != NULL) { 687 // 688 // To be extra safe make sure ConOut has been initialized 689 // 690 gST->ConOut->OutputString (gST->ConOut, Buffer); 691 } 692 693 return Return; 694 } 695 696 /** 697 Print a Unicode string to the output buffer. 698 699 @param Buffer A pointer to the output buffer for the produced Null-terminated 700 Unicode string. 701 @param BufferSize The size, in bytes, of the output buffer specified by StartOfBuffer. 702 @param Format A Null-terminated Unicode format string. 703 @param ... The variable argument list that contains pointers to Null- 704 terminated Unicode strings to be printed 705 706 **/ 707 UINTN 708 EFIAPI 709 EDBSPrint ( 710 OUT CHAR16 *Buffer, 711 IN INTN BufferSize, 712 IN CONST CHAR16 *Format, 713 ... 714 ) 715 { 716 UINTN Return; 717 VA_LIST Marker; 718 719 ASSERT (BufferSize > 0); 720 721 VA_START (Marker, Format); 722 Return = UnicodeVSPrint (Buffer, (UINTN)BufferSize, Format, Marker); 723 VA_END (Marker); 724 725 return Return; 726 } 727 728 /** 729 Print a Unicode string to the output buffer with specified offset.. 730 731 @param Buffer A pointer to the output buffer for the produced Null-terminated 732 Unicode string. 733 @param BufferSize The size, in bytes, of the output buffer specified by StartOfBuffer. 734 @param Offset The offset of the buffer. 735 @param Format A Null-terminated Unicode format string. 736 @param ... The variable argument list that contains pointers to Null- 737 terminated Unicode strings to be printed 738 739 **/ 740 UINTN 741 EFIAPI 742 EDBSPrintWithOffset ( 743 OUT CHAR16 *Buffer, 744 IN INTN BufferSize, 745 IN UINTN Offset, 746 IN CONST CHAR16 *Format, 747 ... 748 ) 749 { 750 UINTN Return; 751 VA_LIST Marker; 752 753 ASSERT (BufferSize - (Offset * sizeof(CHAR16)) > 0); 754 755 VA_START (Marker, Format); 756 Return = UnicodeVSPrint (Buffer + Offset, (UINTN)(BufferSize - (Offset * sizeof(CHAR16))), Format, Marker); 757 VA_END (Marker); 758 759 return Return; 760 } 761