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...@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 _______________________________________________ Grub-devel mailing list Grub-devel@gnu.org https://lists.gnu.org/mailman/listinfo/grub-devel