| /* |
| * Copyright 2012 <James.Bottomley@HansenPartnership.com> |
| * |
| * see COPYING file |
| */ |
| #include <efi/efi.h> |
| #include <efi/efilib.h> |
| |
| #include <console.h> |
| #include <errors.h> |
| |
| static int min(int a, int b) |
| { |
| if (a < b) |
| return a; |
| return b; |
| } |
| |
| static int |
| count_lines(CHAR16 *str_arr[]) |
| { |
| int i = 0; |
| |
| while (str_arr[i]) |
| i++; |
| return i; |
| } |
| |
| static void |
| SetMem16(CHAR16 *dst, UINT32 n, CHAR16 c) |
| { |
| int i; |
| |
| for (i = 0; i < n/2; i++) { |
| dst[i] = c; |
| } |
| } |
| |
| EFI_INPUT_KEY |
| console_get_keystroke(void) |
| { |
| EFI_INPUT_KEY key; |
| UINTN EventIndex; |
| |
| BS->WaitForEvent(1, &ST->ConIn->WaitForKey, &EventIndex); |
| ST->ConIn->ReadKeyStroke(ST->ConIn, &key); |
| |
| return key; |
| } |
| |
| int |
| console_check_for_keystroke(CHAR16 key) |
| { |
| EFI_INPUT_KEY k; |
| EFI_STATUS status; |
| /* check for both upper and lower cases */ |
| CHAR16 key_u = key & ~0x20, key_l = key | 0x20; |
| |
| /* the assumption is the user has been holding the key down so empty |
| * the key buffer at this point because auto repeat may have filled |
| * it */ |
| |
| for(;;) { |
| status = ST->ConIn->ReadKeyStroke(ST->ConIn, &k); |
| |
| if (status != EFI_SUCCESS) |
| break; |
| |
| if (key_u == k.UnicodeChar || key_l == k.UnicodeChar) |
| return 1; |
| } |
| return 0; |
| } |
| |
| void |
| console_print_box_at(CHAR16 *str_arr[], int highlight, int start_col, int start_row, int size_cols, int size_rows, int offset, int lines) |
| { |
| int i; |
| SIMPLE_TEXT_OUTPUT_INTERFACE *co = ST->ConOut; |
| UINTN rows, cols; |
| CHAR16 *Line; |
| |
| if (lines == 0) |
| return; |
| |
| co->QueryMode(co, co->Mode->Mode, &cols, &rows); |
| |
| /* last row on screen is unusable without scrolling, so ignore it */ |
| rows--; |
| |
| if (size_rows < 0) |
| size_rows = rows + size_rows + 1; |
| if (size_cols < 0) |
| size_cols = cols + size_cols + 1; |
| |
| if (start_col < 0) |
| start_col = (cols + start_col + 2)/2; |
| if (start_row < 0) |
| start_row = (rows + start_row + 2)/2; |
| if (start_col < 0) |
| start_col = 0; |
| if (start_row < 0) |
| start_row = 0; |
| |
| if (start_col > cols || start_row > rows) { |
| Print(L"Starting Position (%d,%d) is off screen\n", |
| start_col, start_row); |
| return; |
| } |
| if (size_cols + start_col > cols) |
| size_cols = cols - start_col; |
| if (size_rows + start_row > rows) |
| size_rows = rows - start_row; |
| |
| if (lines > size_rows - 2) |
| lines = size_rows - 2; |
| |
| Line = AllocatePool((size_cols+1)*sizeof(CHAR16)); |
| if (!Line) { |
| Print(L"Failed Allocation\n"); |
| return; |
| } |
| |
| SetMem16 (Line, size_cols * 2, BOXDRAW_HORIZONTAL); |
| |
| Line[0] = BOXDRAW_DOWN_RIGHT; |
| Line[size_cols - 1] = BOXDRAW_DOWN_LEFT; |
| Line[size_cols] = L'\0'; |
| co->SetCursorPosition(co, start_col, start_row); |
| co->OutputString(co, Line); |
| |
| int start; |
| if (offset == 0) |
| /* middle */ |
| start = (size_rows - lines)/2 + start_row + offset; |
| else if (offset < 0) |
| /* from bottom */ |
| start = start_row + size_rows - lines + offset - 1; |
| else |
| /* from top */ |
| start = start_row + offset; |
| |
| for (i = start_row + 1; i < size_rows + start_row - 1; i++) { |
| int line = i - start; |
| |
| SetMem16 (Line, size_cols*2, L' '); |
| Line[0] = BOXDRAW_VERTICAL; |
| Line[size_cols - 1] = BOXDRAW_VERTICAL; |
| Line[size_cols] = L'\0'; |
| if (line >= 0 && line < lines) { |
| CHAR16 *s = str_arr[line]; |
| int len = StrLen(s); |
| int col = (size_cols - 2 - len)/2; |
| |
| if (col < 0) |
| col = 0; |
| |
| CopyMem(Line + col + 1, s, min(len, size_cols - 2)*2); |
| } |
| if (line >= 0 && line == highlight) |
| co->SetAttribute(co, EFI_LIGHTGRAY | EFI_BACKGROUND_BLACK); |
| co->SetCursorPosition(co, start_col, i); |
| co->OutputString(co, Line); |
| if (line >= 0 && line == highlight) |
| co->SetAttribute(co, EFI_LIGHTGRAY | EFI_BACKGROUND_BLUE); |
| |
| } |
| SetMem16 (Line, size_cols * 2, BOXDRAW_HORIZONTAL); |
| Line[0] = BOXDRAW_UP_RIGHT; |
| Line[size_cols - 1] = BOXDRAW_UP_LEFT; |
| Line[size_cols] = L'\0'; |
| co->SetCursorPosition(co, start_col, i); |
| co->OutputString(co, Line); |
| |
| FreePool (Line); |
| |
| } |
| |
| void |
| console_print_box(CHAR16 *str_arr[], int highlight) |
| { |
| SIMPLE_TEXT_OUTPUT_MODE SavedConsoleMode; |
| SIMPLE_TEXT_OUTPUT_INTERFACE *co = ST->ConOut; |
| CopyMem(&SavedConsoleMode, co->Mode, sizeof(SavedConsoleMode)); |
| co->EnableCursor(co, FALSE); |
| co->SetAttribute(co, EFI_LIGHTGRAY | EFI_BACKGROUND_BLUE); |
| |
| console_print_box_at(str_arr, highlight, 0, 0, -1, -1, 0, |
| count_lines(str_arr)); |
| |
| console_get_keystroke(); |
| |
| co->EnableCursor(co, SavedConsoleMode.CursorVisible); |
| |
| co->EnableCursor(co, SavedConsoleMode.CursorVisible); |
| co->SetCursorPosition(co, SavedConsoleMode.CursorColumn, SavedConsoleMode.CursorRow); |
| co->SetAttribute(co, SavedConsoleMode.Attribute); |
| } |
| |
| int |
| console_select(CHAR16 *title[], CHAR16* selectors[], int start) |
| { |
| SIMPLE_TEXT_OUTPUT_MODE SavedConsoleMode; |
| SIMPLE_TEXT_OUTPUT_INTERFACE *co = ST->ConOut; |
| EFI_INPUT_KEY k; |
| int selector; |
| int selector_lines = count_lines(selectors); |
| int selector_max_cols = 0; |
| int i, offs_col, offs_row, size_cols, size_rows, lines; |
| int selector_offset; |
| int title_lines = count_lines(title); |
| UINTN cols, rows; |
| |
| co->QueryMode(co, co->Mode->Mode, &cols, &rows); |
| |
| for (i = 0; i < selector_lines; i++) { |
| int len = StrLen(selectors[i]); |
| |
| if (len > selector_max_cols) |
| selector_max_cols = len; |
| } |
| |
| if (start < 0) |
| start = 0; |
| if (start >= selector_lines) |
| start = selector_lines - 1; |
| |
| offs_col = - selector_max_cols - 4; |
| size_cols = selector_max_cols + 4; |
| |
| if (selector_lines > rows - 6 - title_lines) { |
| offs_row = title_lines + 2; |
| size_rows = rows - 4 - title_lines; |
| lines = size_rows - 2; |
| } else { |
| offs_row = (rows + title_lines - 1 - selector_lines)/2; |
| size_rows = selector_lines + 2; |
| lines = selector_lines; |
| } |
| |
| if (start > lines) { |
| selector = lines; |
| selector_offset = start - lines; |
| } else { |
| selector = start; |
| selector_offset = 0; |
| } |
| |
| CopyMem(&SavedConsoleMode, co->Mode, sizeof(SavedConsoleMode)); |
| co->EnableCursor(co, FALSE); |
| co->SetAttribute(co, EFI_LIGHTGRAY | EFI_BACKGROUND_BLUE); |
| |
| console_print_box_at(title, -1, 0, 0, -1, -1, 1, count_lines(title)); |
| |
| console_print_box_at(selectors, selector, offs_col, offs_row, |
| size_cols, size_rows, 0, lines); |
| |
| do { |
| k = console_get_keystroke(); |
| |
| if (k.ScanCode == SCAN_ESC) { |
| selector = -1; |
| break; |
| } |
| |
| if (k.ScanCode == SCAN_UP) { |
| if (selector > 0) |
| selector--; |
| else if (selector_offset > 0) |
| selector_offset--; |
| } else if (k.ScanCode == SCAN_DOWN) { |
| if (selector < lines - 1) |
| selector++; |
| else if (selector_offset < (selector_lines - lines)) |
| selector_offset++; |
| } |
| |
| console_print_box_at(&selectors[selector_offset], selector, |
| offs_col, offs_row, |
| size_cols, size_rows, 0, lines); |
| } while (!(k.ScanCode == SCAN_NULL |
| && k.UnicodeChar == CHAR_CARRIAGE_RETURN)); |
| |
| co->EnableCursor(co, SavedConsoleMode.CursorVisible); |
| |
| co->EnableCursor(co, SavedConsoleMode.CursorVisible); |
| co->SetCursorPosition(co, SavedConsoleMode.CursorColumn, SavedConsoleMode.CursorRow); |
| co->SetAttribute(co, SavedConsoleMode.Attribute); |
| |
| if (selector < 0) |
| /* ESC pressed */ |
| return selector; |
| return selector + selector_offset; |
| } |
| |
| |
| int |
| console_yes_no(CHAR16 *str_arr[]) |
| { |
| return console_select(str_arr, (CHAR16 *[]){ L"No", L"Yes", NULL }, 0); |
| } |
| |
| void |
| console_alertbox(CHAR16 **title) |
| { |
| console_select(title, (CHAR16 *[]){ L"OK", 0 }, 0); |
| } |
| |
| void |
| console_errorbox(CHAR16 *err) |
| { |
| CHAR16 **err_arr = (CHAR16 *[]){ |
| L"ERROR", |
| L"", |
| 0, |
| 0, |
| }; |
| |
| err_arr[2] = err; |
| |
| console_alertbox(err_arr); |
| } |
| |
| #define ARRAY_SIZE(a) (sizeof (a) / sizeof ((a)[0])) |
| |
| /* Copy of gnu-efi-3.0 with the added secure boot strings */ |
| static struct { |
| EFI_STATUS Code; |
| WCHAR *Desc; |
| } error_table[] = { |
| { EFI_SUCCESS, L"Success"}, |
| { EFI_LOAD_ERROR, L"Load Error"}, |
| { EFI_INVALID_PARAMETER, L"Invalid Parameter"}, |
| { EFI_UNSUPPORTED, L"Unsupported"}, |
| { EFI_BAD_BUFFER_SIZE, L"Bad Buffer Size"}, |
| { EFI_BUFFER_TOO_SMALL, L"Buffer Too Small"}, |
| { EFI_NOT_READY, L"Not Ready"}, |
| { EFI_DEVICE_ERROR, L"Device Error"}, |
| { EFI_WRITE_PROTECTED, L"Write Protected"}, |
| { EFI_OUT_OF_RESOURCES, L"Out of Resources"}, |
| { EFI_VOLUME_CORRUPTED, L"Volume Corrupt"}, |
| { EFI_VOLUME_FULL, L"Volume Full"}, |
| { EFI_NO_MEDIA, L"No Media"}, |
| { EFI_MEDIA_CHANGED, L"Media changed"}, |
| { EFI_NOT_FOUND, L"Not Found"}, |
| { EFI_ACCESS_DENIED, L"Access Denied"}, |
| { EFI_NO_RESPONSE, L"No Response"}, |
| { EFI_NO_MAPPING, L"No mapping"}, |
| { EFI_TIMEOUT, L"Time out"}, |
| { EFI_NOT_STARTED, L"Not started"}, |
| { EFI_ALREADY_STARTED, L"Already started"}, |
| { EFI_ABORTED, L"Aborted"}, |
| { EFI_ICMP_ERROR, L"ICMP Error"}, |
| { EFI_TFTP_ERROR, L"TFTP Error"}, |
| { EFI_PROTOCOL_ERROR, L"Protocol Error"}, |
| { EFI_INCOMPATIBLE_VERSION, L"Incompatible Version"}, |
| { EFI_SECURITY_VIOLATION, L"Security Violation"}, |
| |
| // warnings |
| { EFI_WARN_UNKOWN_GLYPH, L"Warning Unknown Glyph"}, |
| { EFI_WARN_DELETE_FAILURE, L"Warning Delete Failure"}, |
| { EFI_WARN_WRITE_FAILURE, L"Warning Write Failure"}, |
| { EFI_WARN_BUFFER_TOO_SMALL, L"Warning Buffer Too Small"}, |
| { 0, NULL} |
| } ; |
| |
| |
| static CHAR16 * |
| err_string ( |
| IN EFI_STATUS Status |
| ) |
| { |
| UINTN Index; |
| |
| for (Index = 0; error_table[Index].Desc; Index +=1) { |
| if (error_table[Index].Code == Status) { |
| return error_table[Index].Desc; |
| } |
| } |
| |
| return L""; |
| } |
| |
| |
| void |
| console_error(CHAR16 *err, EFI_STATUS status) |
| { |
| CHAR16 **err_arr = (CHAR16 *[]){ |
| L"ERROR", |
| L"", |
| 0, |
| 0, |
| }; |
| CHAR16 str[512]; |
| |
| SPrint(str, sizeof(str), L"%s: (%d) %s", err, status, err_string(status)); |
| |
| err_arr[2] = str; |
| |
| console_alertbox(err_arr); |
| } |
| |
| void |
| console_reset(void) |
| { |
| SIMPLE_TEXT_OUTPUT_INTERFACE *co = ST->ConOut; |
| |
| co->Reset(co, TRUE); |
| /* set mode 0 - required to be 80x25 */ |
| co->SetMode(co, 0); |
| co->ClearScreen(co); |
| } |