grub-devel
[Top][All Lists]
Advanced

[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




reply via email to

[Prev in Thread] Current Thread [Next in Thread]