From: Zhang Boyang <zhangboyang.id@gmail.com>
To: grub-devel@gnu.org
Cc: Zhang Boyang <zhangboyang.id@gmail.com>
Subject: [PATCH 5/5] term/gfxterm: Preliminary HiDPI support
Date: Mon, 5 Dec 2022 19:29:40 +0800 [thread overview]
Message-ID: <20221205112940.246486-6-zhangboyang.id@gmail.com> (raw)
In-Reply-To: <20221205112940.246486-1-zhangboyang.id@gmail.com>
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 ++++
| 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
--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
prev parent reply other threads:[~2022-12-05 11:30 UTC|newest]
Thread overview: 10+ messages / expand[flat|nested] mbox.gz Atom feed top
2022-12-05 11:29 [PATCH 0/5] Font fixes and preliminary HiDPI support Zhang Boyang
2022-12-05 11:29 ` [PATCH 1/5] font: Check return value of grub_malloc() in ascii_glyph_lookup() Zhang Boyang
2022-12-13 17:16 ` Daniel Kiper
2022-12-05 11:29 ` [PATCH 2/5] font: Assign null_font to unknown_glyph Zhang Boyang
2022-12-13 17:17 ` Daniel Kiper
2022-12-05 11:29 ` [PATCH 3/5] font: Reject fonts with negative max_char_width or max_char_height Zhang Boyang
2022-12-13 17:17 ` Daniel Kiper
2022-12-05 11:29 ` [PATCH 4/5] font: Add font scaling feature to grub_font_draw_glyph() Zhang Boyang
2022-12-05 11:42 ` Zhang Boyang
2022-12-05 11:29 ` Zhang Boyang [this message]
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=20221205112940.246486-6-zhangboyang.id@gmail.com \
--to=zhangboyang.id@gmail.com \
--cc=grub-devel@gnu.org \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.