boot/efi: improve automatic mode handling

This patch improves the automatic console mode switching by avoiding
changing the mode if it can verify that the text is readable in the
default mode. The implementation avoids changing mode if we're using a
low resolution device (fixed to be smaller than 1920x1080) because we
assume the text is readable, so the initial mode should be a good one.
Also, the mode is not changed when in high resolution mode if the text
viewport area is larger than 10% of the screen. The conclusion is that
only if the text viewport is calculated to occupy less than 10% of the
screen area it is assumed to be hard to read and a new mode is selected.

With this patch the auto mode will typically avoid changing mode
hopefully in most of the cases, only changing it if completely
necessary.
This commit is contained in:
Henrique Dante de Almeida 2017-12-16 21:12:53 -02:00
parent 68d4b8ac9b
commit 26e51d87f7

View file

@ -20,6 +20,9 @@
#include "console.h"
#include "util.h"
#define SYSTEM_FONT_WIDTH 8
#define SYSTEM_FONT_HEIGHT 19
#define EFI_SIMPLE_TEXT_INPUT_EX_PROTOCOL_GUID \
{ 0xdd9e7534, 0x7762, 0x4698, { 0x8c, 0x14, 0xf5, 0x85, 0x17, 0xa6, 0x25, 0xaa } }
@ -148,9 +151,66 @@ static EFI_STATUS change_mode(UINTN mode) {
return err;
}
static UINT64 text_area_from_font_size(void) {
EFI_STATUS err;
UINT64 text_area;
UINTN rows, columns;
err = uefi_call_wrapper(ST->ConOut->QueryMode, 4, ST->ConOut, ST->ConOut->Mode->Mode, &columns, &rows);
if (EFI_ERROR(err)) {
columns = 80;
rows = 25;
}
text_area = SYSTEM_FONT_WIDTH * SYSTEM_FONT_HEIGHT * (UINT64)rows * (UINT64)columns;
return text_area;
}
static EFI_STATUS mode_auto(UINTN *mode) {
/* Mode number 2 is first non standard mode, which is provided by
* the device manufacturer, so it should be a good mode.
const UINT32 HORIZONTAL_MAX_OK = 1920;
const UINT32 VERTICAL_MAX_OK = 1080;
const UINT64 VIEWPORT_RATIO = 10;
UINT64 screen_area, text_area;
EFI_GUID GraphicsOutputProtocolGuid = EFI_GRAPHICS_OUTPUT_PROTOCOL_GUID;
EFI_GRAPHICS_OUTPUT_PROTOCOL *GraphicsOutput;
EFI_GRAPHICS_OUTPUT_MODE_INFORMATION *Info;
EFI_STATUS err;
BOOLEAN keep = FALSE;
err = LibLocateProtocol(&GraphicsOutputProtocolGuid, (VOID **)&GraphicsOutput);
if (!EFI_ERROR(err) && GraphicsOutput->Mode && GraphicsOutput->Mode->Info) {
Info = GraphicsOutput->Mode->Info;
/* Start verifying if we are in a resolution larger than Full HD
* (1920x1080). If we're not, assume we're in a good mode and do not
* try to change it. */
if (Info->HorizontalResolution <= HORIZONTAL_MAX_OK && Info->VerticalResolution <= VERTICAL_MAX_OK)
keep = TRUE;
/* For larger resolutions, calculate the ratio of the total screen
* area to the text viewport area. If it's less than 10 times bigger,
* then assume the text is readable and keep the text mode. */
else {
screen_area = (UINT64)Info->HorizontalResolution * (UINT64)Info->VerticalResolution;
text_area = text_area_from_font_size();
if (text_area != 0 && screen_area/text_area < VIEWPORT_RATIO)
keep = TRUE;
}
}
if (keep) {
/* Just clear the screen instead of changing the mode and return. */
*mode = ST->ConOut->Mode->Mode;
uefi_call_wrapper(ST->ConOut->ClearScreen, 1, ST->ConOut);
return EFI_SUCCESS;
}
/* If we reached here, then we have a high resolution screen and the text
* viewport is less than 10% the screen area, so the firmware developer
* screwed up. Try to switch to a better mode. Mode number 2 is first non
* standard mode, which is provided by the device manufacturer, so it should
* be a good mode.
* Note: MaxMode is the number of modes, not the last mode. */
if (ST->ConOut->Mode->MaxMode > 2)
*mode = 2;