summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorHenrique Dante de Almeida <hdante@gmail.com>2017-12-16 21:12:53 -0200
committerHenrique Dante de Almeida <hdante@gmail.com>2018-02-03 17:37:16 -0200
commit26e51d87f7b9c50cc215dc9ccc78fa99f5225a82 (patch)
treee8e193a619ff60dd9be98e77add3b6470a739c0a /src
parent68d4b8ac9b9673637fa198b735f6e64b78b35d3b (diff)
downloadsystemd-26e51d87f7b9c50cc215dc9ccc78fa99f5225a82.tar.gz
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.
Diffstat (limited to 'src')
-rw-r--r--src/boot/efi/console.c64
1 files changed, 62 insertions, 2 deletions
diff --git a/src/boot/efi/console.c b/src/boot/efi/console.c
index 241167b9f8..94551a89e1 100644
--- a/src/boot/efi/console.c
+++ b/src/boot/efi/console.c
@@ -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;