[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]
[PATCH 5/5] term/gfxterm: Preliminary HiDPI support
From: |
Zhang Boyang |
Subject: |
[PATCH 5/5] term/gfxterm: Preliminary HiDPI support |
Date: |
Mon, 5 Dec 2022 19:29:40 +0800 |
Currently GRUB's default font is too small to see on a HiDPI monitor.
This patch adds preliminary HiDPI support to gfxterm. It introduces a
new environment variable 'gfxterm_scale'. If set to 0, and a high
resolution monitor is detected, it will scale the font size
automatically. If set to other number, that number will be the scale
factor, overriding automatic scale factor calculation.
Signed-off-by: Zhang Boyang <zhangboyang.id@gmail.com>
---
docs/grub.texi | 11 ++++
grub-core/gfxmenu/view.c | 1 +
grub-core/term/gfxterm.c | 120 ++++++++++++++++++++++++++++++++-------
include/grub/gfxterm.h | 8 ++-
4 files changed, 117 insertions(+), 23 deletions(-)
diff --git a/docs/grub.texi b/docs/grub.texi
index 50c811a88..b754a465b 100644
--- a/docs/grub.texi
+++ b/docs/grub.texi
@@ -3309,6 +3309,7 @@ These variables have special meaning to GRUB.
* gfxmode::
* gfxpayload::
* gfxterm_font::
+* gfxterm_scale::
* grub_cpu::
* grub_platform::
* icondir::
@@ -3588,6 +3589,16 @@ If this variable is set, it names a font to use for text
on the
available font.
+@node gfxterm_scale
+@subsection gfxterm_scale
+
+If this variable is not set, or set to @samp{0}, the @samp{gfxterm}
+graphical terminal will scale the font automatically when a high resolution
+monitor is detected. If set to other number, the font scale factor will be
+forced to that number. Set this to @samp{1} if user don't want
+@samp{gfxterm} to scale the font on screen.
+
+
@node grub_cpu
@subsection grub_cpu
diff --git a/grub-core/gfxmenu/view.c b/grub-core/gfxmenu/view.c
index 6358004b2..94b9ef4db 100644
--- a/grub-core/gfxmenu/view.c
+++ b/grub-core/gfxmenu/view.c
@@ -546,6 +546,7 @@ init_terminal (grub_gfxmenu_view_t view)
view->terminal_rect.height,
view->double_repaint,
terminal_font,
+ 1,
view->terminal_border);
grub_gfxterm_decorator_hook = grub_gfxmenu_draw_terminal_box;
}
diff --git a/grub-core/term/gfxterm.c b/grub-core/term/gfxterm.c
index 4512dee6f..d16410f5e 100644
--- a/grub-core/term/gfxterm.c
+++ b/grub-core/term/gfxterm.c
@@ -37,6 +37,13 @@ GRUB_MOD_LICENSE ("GPLv3+");
#define DEFAULT_STANDARD_COLOR 0x07
+/* Arbitrarily pick half of 80x24 as minimum number of rows and columns. */
+#define MIN_COL 40
+#define MIN_ROW 12
+
+/* For 8x16 fonts, 8x is sufficient on a 16K monitor. */
+#define MAX_SCALE 8
+
struct grub_dirty_region
{
int top_left_x;
@@ -65,6 +72,9 @@ struct grub_virtual_screen
unsigned int offset_x;
unsigned int offset_y;
+ /* Scale factor. */
+ int scale;
+
/* TTY Character sizes in pixes. */
unsigned int normal_char_width;
unsigned int normal_char_height;
@@ -201,10 +211,51 @@ grub_virtual_screen_free (void)
text_layer = 0;
}
+/*
+ * Adjust scale factor, `scale` can be any untrusted value.
+ * If `scale` is 0, determine scale factor heuristically.
+ * Returns the adjusted value, which is always in [1, MAX_SCALE].
+ */
+int
+grub_gfxterm_adjust_scale_factor (grub_font_t font, int scale,
+ int width, int height,
+ int border_width)
+{
+ int i;
+ int max_scale;
+ int normal_char_width, normal_char_height;
+ int columns, rows;
+
+ normal_char_width = calculate_normal_character_width (font);
+ normal_char_height = grub_font_get_max_char_height (font);
+
+ max_scale = 1;
+ for (i = 2; i <= MAX_SCALE; i++)
+ {
+ columns = (width - 2 * border_width * i) / (normal_char_width * i);
+ rows = (height - 2 * border_width * i) / (normal_char_height * i);
+ if (columns < MIN_COL || rows < MIN_ROW)
+ break;
+ max_scale = i;
+ }
+
+ if (scale == 0)
+ {
+ scale = 1;
+ for (i = 2; i <= MAX_SCALE; i *= 2)
+ if (width > 1920 * (i / 2) && height > 1080 * (i / 2))
+ scale = i;
+ }
+
+ scale = grub_max (scale, 1);
+ scale = grub_min (scale, max_scale);
+ return scale;
+}
+
static grub_err_t
grub_virtual_screen_setup (unsigned int x, unsigned int y,
unsigned int width, unsigned int height,
- grub_font_t font)
+ grub_font_t font, int scale)
{
unsigned int i;
@@ -213,16 +264,17 @@ grub_virtual_screen_setup (unsigned int x, unsigned int y,
/* Initialize with default data. */
virtual_screen.font = font;
+ virtual_screen.scale = scale;
virtual_screen.width = width;
virtual_screen.height = height;
virtual_screen.offset_x = x;
virtual_screen.offset_y = y;
virtual_screen.normal_char_width =
- calculate_normal_character_width (virtual_screen.font);
+ calculate_normal_character_width (virtual_screen.font) *
virtual_screen.scale;
virtual_screen.normal_char_height =
- grub_font_get_max_char_height (virtual_screen.font);
+ grub_font_get_max_char_height (virtual_screen.font) * virtual_screen.scale;
if (virtual_screen.normal_char_height == 0)
- virtual_screen.normal_char_height = 16;
+ virtual_screen.normal_char_height = 16 * virtual_screen.scale;
virtual_screen.cursor_x = 0;
virtual_screen.cursor_y = 0;
virtual_screen.cursor_state = 1;
@@ -234,10 +286,10 @@ grub_virtual_screen_setup (unsigned int x, unsigned int y,
/*
* There must be a minimum number of rows and columns for the screen to
- * make sense. Arbitrarily pick half of 80x24. If either dimensions is 0
- * we would allocate 0 bytes for the text_buffer.
+ * make sense. If either dimensions is 0 we would allocate 0 bytes for
+ * the text_buffer.
*/
- if (virtual_screen.columns < 40 || virtual_screen.rows < 12)
+ if (virtual_screen.columns < MIN_COL || virtual_screen.rows < MIN_ROW)
return grub_error (GRUB_ERR_BAD_FONT,
"font: glyphs too large to fit on screen");
@@ -297,7 +349,8 @@ grub_err_t
grub_gfxterm_set_window (struct grub_video_render_target *target,
int x, int y, int width, int height,
int double_repaint,
- grub_font_t font, int border_width)
+ grub_font_t font, int scale,
+ int border_width)
{
/* Clean up any prior instance. */
destroy_window ();
@@ -306,10 +359,10 @@ grub_gfxterm_set_window (struct grub_video_render_target
*target,
render_target = target;
/* Create virtual screen. */
- if (grub_virtual_screen_setup (border_width, border_width,
- width - 2 * border_width,
- height - 2 * border_width,
- font)
+ if (grub_virtual_screen_setup (border_width * scale, border_width * scale,
+ width - 2 * border_width * scale,
+ height - 2 * border_width * scale,
+ font, scale)
!= GRUB_ERR_NONE)
{
return grub_errno;
@@ -337,6 +390,8 @@ grub_gfxterm_fullscreen (void)
grub_err_t err;
int double_redraw;
grub_font_t font;
+ int scale;
+ const char *scale_str;
err = grub_video_get_info (&mode_info);
/* Figure out what mode we ended up. */
@@ -366,12 +421,32 @@ grub_gfxterm_fullscreen (void)
if (!font)
return grub_error (GRUB_ERR_BAD_FONT, "no font loaded");
+ /* Decide scale factor. */
+ scale = 0;
+
+ scale_str = grub_env_get ("gfxterm_scale");
+ if (scale_str)
+ {
+ const char *scale_str_end;
+ unsigned long scale_ull;
+ grub_error_push ();
+ scale_ull = grub_strtoull (scale_str, &scale_str_end, 10);
+ if (*scale_str == '\0' || *scale_str_end != '\0' || grub_errno !=
GRUB_ERR_NONE
+ || grub_cast (scale_ull, &scale))
+ scale = 0;
+ grub_error_pop ();
+ }
+
+ scale = grub_gfxterm_adjust_scale_factor (font, scale,
+ mode_info.width, mode_info.height,
+ DEFAULT_BORDER_WIDTH);
+
grub_gfxterm_decorator_hook = NULL;
return grub_gfxterm_set_window (GRUB_VIDEO_RENDER_TARGET_DISPLAY,
0, 0, mode_info.width, mode_info.height,
double_redraw,
- font, DEFAULT_BORDER_WIDTH);
+ font, scale, DEFAULT_BORDER_WIDTH);
}
static grub_err_t
@@ -642,7 +717,7 @@ paint_char (unsigned cx, unsigned cy)
grub_errno = GRUB_ERR_NONE;
return;
}
- ascent = grub_font_get_ascent (virtual_screen.font);
+ ascent = grub_font_get_ascent (virtual_screen.font) * virtual_screen.scale;
width = virtual_screen.normal_char_width * calculate_character_width(glyph);
height = virtual_screen.normal_char_height;
@@ -656,7 +731,7 @@ paint_char (unsigned cx, unsigned cy)
/* Render glyph to text layer. */
grub_video_set_active_render_target (text_layer);
grub_video_fill_rect (bgcolor, x, y, width, height);
- grub_font_draw_glyph (glyph, color, x, y + ascent, 1);
+ grub_font_draw_glyph (glyph, color, x, y + ascent, virtual_screen.scale);
grub_video_set_active_render_target (render_target);
/* Mark character to be drawn. */
@@ -690,9 +765,9 @@ draw_cursor (int show)
return;
/* Ensure that cursor doesn't go outside of character box. */
- ascent = grub_font_get_ascent(virtual_screen.font);
- if (ascent > virtual_screen.normal_char_height - 2)
- ascent = virtual_screen.normal_char_height - 2;
+ ascent = grub_font_get_ascent(virtual_screen.font) * virtual_screen.scale;
+ if (ascent > virtual_screen.normal_char_height - 2 * virtual_screen.scale)
+ ascent = virtual_screen.normal_char_height - 2 * virtual_screen.scale;
/* Determine cursor properties and position on text layer. */
x = virtual_screen.cursor_x * virtual_screen.normal_char_width;
@@ -701,7 +776,7 @@ draw_cursor (int show)
y = ((virtual_screen.cursor_y + virtual_screen.total_scroll)
* virtual_screen.normal_char_height
+ ascent);
- height = 2;
+ height = 2 * virtual_screen.scale;
/* Render cursor to text layer. */
grub_video_set_active_render_target (text_layer);
@@ -968,7 +1043,7 @@ calculate_character_width (struct grub_font_glyph *glyph)
if (! glyph || glyph->device_width == 0)
return 1;
- return (glyph->device_width
+ return (glyph->device_width * virtual_screen.scale
+ (virtual_screen.normal_char_width - 1))
/ virtual_screen.normal_char_width;
}
@@ -983,8 +1058,9 @@ grub_gfxterm_getcharwidth (struct grub_term_output *term
__attribute__ ((unused)
if (dev_width == 0)
return 1;
- return (dev_width + (virtual_screen.normal_char_width - 1))
- / virtual_screen.normal_char_width;
+ return (dev_width * virtual_screen.scale
+ + (virtual_screen.normal_char_width - 1))
+ / virtual_screen.normal_char_width;
}
static struct grub_term_coordinate
diff --git a/include/grub/gfxterm.h b/include/grub/gfxterm.h
index 7e1ff6dfc..6cd99dd54 100644
--- a/include/grub/gfxterm.h
+++ b/include/grub/gfxterm.h
@@ -25,11 +25,17 @@
#include <grub/video.h>
#include <grub/font.h>
+int
+EXPORT_FUNC (grub_gfxterm_adjust_scale_factor) (grub_font_t font, int scale,
+ int width, int height,
+ int border_width);
+
grub_err_t
EXPORT_FUNC (grub_gfxterm_set_window) (struct grub_video_render_target *target,
int x, int y, int width, int height,
int double_repaint,
- grub_font_t font, int border_width);
+ grub_font_t font, int scale,
+ int border_width);
void EXPORT_FUNC (grub_gfxterm_schedule_repaint) (void);
--
2.30.2