]> git.ipfire.org Git - thirdparty/ipxe.git/commitdiff
[fbcon] Support Unicode character output
authorMichael Brown <mcb30@ipxe.org>
Mon, 14 Mar 2022 22:38:24 +0000 (22:38 +0000)
committerMichael Brown <mcb30@ipxe.org>
Tue, 15 Mar 2022 17:27:18 +0000 (17:27 +0000)
Accumulate UTF-8 characters in fbcon_putchar(), and require the frame
buffer console's .glyph() method to accept Unicode character values.

Signed-off-by: Michael Brown <mcb30@ipxe.org>
src/arch/x86/interface/pcbios/vesafb.c
src/core/fbcon.c
src/include/ipxe/fbcon.h
src/interface/efi/efi_fbcon.c

index 50e4858521382698d366af2fac2528cb0c415a80..86edbda42e84cdca77e710dd85148b6d93dddec7 100644 (file)
@@ -78,6 +78,15 @@ struct console_driver bios_console __attribute__ (( weak ));
 /** Font corresponding to selected character width and height */
 #define VESAFB_FONT VBE_FONT_8x16
 
+/** Number of ASCII glyphs within the font */
+#define VESAFB_ASCII 128
+
+/** Glyph to render for non-ASCII characters
+ *
+ * We choose to use one of the box-drawing glyphs.
+ */
+#define VESAFB_UNKNOWN 0xfe
+
 /* Forward declaration */
 struct console_driver vesafb_console __console_driver;
 
@@ -130,12 +139,24 @@ static int vesafb_rc ( unsigned int status ) {
 /**
  * Get character glyph
  *
- * @v character                Character
+ * @v character                Unicode character
  * @v glyph            Character glyph to fill in
  */
 static void vesafb_glyph ( unsigned int character, uint8_t *glyph ) {
-       size_t offset = ( character * VESAFB_CHAR_HEIGHT );
+       unsigned int index;
+       size_t offset;
+
+       /* Identify glyph */
+       if ( character < VESAFB_ASCII ) {
+               /* ASCII character: use corresponding glyph */
+               index = character;
+       } else {
+               /* Non-ASCII character: use "unknown" glyph */
+               index = VESAFB_UNKNOWN;
+       }
 
+       /* Copy glyph from BIOS font table */
+       offset = ( index * VESAFB_CHAR_HEIGHT );
        copy_from_real ( glyph, vesafb.glyphs.segment,
                         ( vesafb.glyphs.offset + offset ), VESAFB_CHAR_HEIGHT);
 }
index 44a56e10535796582a4d4973ddf30fae8504033a..ff3132ac779452dc53803632f8f16eb8a19a9428 100644 (file)
@@ -446,6 +446,11 @@ void fbcon_putchar ( struct fbcon *fbcon, int character ) {
        if ( character < 0 )
                return;
 
+       /* Accumulate Unicode characters */
+       character = utf8_accumulate ( &fbcon->utf8, character );
+       if ( character == 0 )
+               return;
+
        /* Handle control characters */
        switch ( character ) {
        case '\r':
index 42ffca3d7dd452f0ba03494be5e8829096a8afcc..a4c7a9ab3e4a2b581e046d9724a472d4afa2ce7a 100644 (file)
@@ -11,6 +11,7 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
 
 #include <stdint.h>
 #include <ipxe/ansiesc.h>
+#include <ipxe/utf8.h>
 #include <ipxe/uaccess.h>
 #include <ipxe/console.h>
 
@@ -36,7 +37,7 @@ struct fbcon_font {
        /**
         * Get character glyph
         *
-        * @v character         Character
+        * @v character         Unicode character
         * @v glyph             Character glyph to fill in
         */
        void ( * glyph ) ( unsigned int character, uint8_t *glyph );
@@ -92,7 +93,7 @@ struct fbcon_text_cell {
        uint32_t foreground;
        /** Background colour */
        uint32_t background;
-       /** Character */
+       /** Unicode character */
        unsigned int character;
 };
 
@@ -138,6 +139,8 @@ struct fbcon {
        unsigned int ypos;
        /** ANSI escape sequence context */
        struct ansiesc_context ctx;
+       /** UTF-8 accumulator */
+       struct utf8_accumulator utf8;
        /** Text array */
        struct fbcon_text text;
        /** Background picture */
index abc5a93908a62a08fab4f292ac2509d0b296f735..d9e3e69e64b1365ef3d46c962b7a46f294368bb9 100644 (file)
@@ -62,6 +62,9 @@ struct console_driver efi_console __attribute__ (( weak ));
 #define CONSOLE_EFIFB ( CONSOLE_USAGE_ALL & ~CONSOLE_USAGE_LOG )
 #endif
 
+/** Number of ASCII glyphs in cache */
+#define EFIFB_ASCII 128
+
 /* Forward declaration */
 struct console_driver efifb_console __console_driver;
 
@@ -84,7 +87,7 @@ struct efifb {
        struct fbcon_colour_map map;
        /** Font definition */
        struct fbcon_font font;
-       /** Character glyphs */
+       /** Character glyph cache */
        userptr_t glyphs;
 };
 
@@ -92,14 +95,112 @@ struct efifb {
 static struct efifb efifb;
 
 /**
- * Get character glyph
+ * Draw character glyph
  *
  * @v character                Character
+ * @v index            Index within glyph cache
+ * @v toggle           Bits to toggle in each bitmask
+ * @ret height         Character height, or negative error
+ */
+static int efifb_draw ( unsigned int character, unsigned int index,
+                       unsigned int toggle ) {
+       EFI_BOOT_SERVICES *bs = efi_systab->BootServices;
+       EFI_IMAGE_OUTPUT *blt;
+       EFI_GRAPHICS_OUTPUT_BLT_PIXEL *pixel;
+       unsigned int height;
+       unsigned int x;
+       unsigned int y;
+       uint8_t bitmask;
+       size_t offset;
+       EFI_STATUS efirc;
+       int rc;
+
+       /* Clear existing glyph */
+       offset = ( index * efifb.font.height );
+       memset_user ( efifb.glyphs, offset, 0, efifb.font.height );
+
+       /* Get glyph */
+       blt = NULL;
+       if ( ( efirc = efifb.hiifont->GetGlyph ( efifb.hiifont, character,
+                                                NULL, &blt, NULL ) ) != 0 ) {
+               rc = -EEFI ( efirc );
+               DBGC ( &efifb, "EFIFB could not get glyph %#02x: %s\n",
+                      character, strerror ( rc ) );
+               goto err_get;
+       }
+       assert ( blt != NULL );
+
+       /* Sanity check */
+       if ( blt->Width > 8 ) {
+               DBGC ( &efifb, "EFIFB glyph %#02x invalid width %d\n",
+                      character, blt->Width );
+               rc = -EINVAL;
+               goto err_width;
+       }
+
+       /* Convert glyph to bitmap */
+       pixel = blt->Image.Bitmap;
+       height = blt->Height;
+       for ( y = 0 ; ( ( y < height ) && ( y < efifb.font.height ) ) ; y++ ) {
+               bitmask = 0;
+               for ( x = 0 ; x < blt->Width ; x++ ) {
+                       bitmask = rol8 ( bitmask, 1 );
+                       if ( pixel->Blue || pixel->Green || pixel->Red )
+                               bitmask |= 0x01;
+                       pixel++;
+               }
+               bitmask ^= toggle;
+               copy_to_user ( efifb.glyphs, offset++, &bitmask,
+                              sizeof ( bitmask ) );
+       }
+
+       /* Free glyph */
+       bs->FreePool ( blt );
+
+       return height;
+
+ err_width:
+       bs->FreePool ( blt );
+ err_get:
+       return rc;
+}
+
+/**
+ * Draw "unknown character" glyph
+ *
+ * @v index            Index within glyph cache
+ * @ret rc             Return status code
+ */
+static int efifb_draw_unknown ( unsigned int index ) {
+
+       /* Draw an inverted '?' glyph */
+       return efifb_draw ( '?', index, -1U );
+}
+
+/**
+ * Get character glyph
+ *
+ * @v character                Unicode character
  * @v glyph            Character glyph to fill in
  */
 static void efifb_glyph ( unsigned int character, uint8_t *glyph ) {
-       size_t offset = ( character * efifb.font.height );
+       unsigned int index;
+       size_t offset;
+
+       /* Identify glyph */
+       if ( character < EFIFB_ASCII ) {
+
+               /* ASCII character: use fixed cache entry */
+               index = character;
 
+       } else {
+
+               /* Non-ASCII character: use an "unknown" glyph */
+               index = 0;
+       }
+
+       /* Copy cached glyph */
+       offset = ( index * efifb.font.height );
        copy_from_user ( glyph, efifb.glyphs, offset, efifb.font.height );
 }
 
@@ -109,16 +210,10 @@ static void efifb_glyph ( unsigned int character, uint8_t *glyph ) {
  * @ret rc             Return status code
  */
 static int efifb_glyphs ( void ) {
-       EFI_BOOT_SERVICES *bs = efi_systab->BootServices;
-       EFI_IMAGE_OUTPUT *blt;
-       EFI_GRAPHICS_OUTPUT_BLT_PIXEL *pixel;
-       size_t offset;
-       size_t len;
-       uint8_t bitmask;
        unsigned int character;
-       unsigned int x;
-       unsigned int y;
-       EFI_STATUS efirc;
+       int height;
+       int max;
+       size_t len;
        int rc;
 
        /* Get font height.  The GetFontInfo() call nominally returns
@@ -128,38 +223,32 @@ static int efifb_glyphs ( void ) {
         * height.
         */
        efifb.font.height = 0;
-       for ( character = 0 ; character < 256 ; character++ ) {
+       max = 0;
+       for ( character = 0 ; character < EFIFB_ASCII ; character++ ) {
 
                /* Skip non-printable characters */
                if ( ! isprint ( character ) )
                        continue;
 
                /* Get glyph */
-               blt = NULL;
-               if ( ( efirc = efifb.hiifont->GetGlyph ( efifb.hiifont,
-                                                        character, NULL, &blt,
-                                                        NULL ) ) != 0 ) {
-                       rc = -EEFI ( efirc );
-                       DBGC ( &efifb, "EFIFB could not get glyph %d: %s\n",
-                              character, strerror ( rc ) );
-                       continue;
+               height = efifb_draw ( character, 0, 0 );
+               if ( height < 0 ) {
+                       rc = height;
+                       goto err_height;
                }
-               assert ( blt != NULL );
 
                /* Calculate maximum height */
-               if ( efifb.font.height < blt->Height )
-                       efifb.font.height = blt->Height;
-
-               /* Free glyph */
-               bs->FreePool ( blt );
+               if ( max < height )
+                       max = height;
        }
-       if ( ! efifb.font.height ) {
+       if ( ! max ) {
                DBGC ( &efifb, "EFIFB could not get font height\n" );
                return -ENOENT;
        }
+       efifb.font.height = max;
 
        /* Allocate glyph data */
-       len = ( 256 * efifb.font.height * sizeof ( bitmask ) );
+       len = ( EFIFB_ASCII * efifb.font.height );
        efifb.glyphs = umalloc ( len );
        if ( ! efifb.glyphs ) {
                rc = -ENOMEM;
@@ -168,60 +257,29 @@ static int efifb_glyphs ( void ) {
        memset_user ( efifb.glyphs, 0, 0, len );
 
        /* Get font data */
-       for ( character = 0 ; character < 256 ; character++ ) {
+       for ( character = 0 ; character < EFIFB_ASCII ; character++ ) {
 
                /* Skip non-printable characters */
-               if ( ! isprint ( character ) )
-                       continue;
-
-               /* Get glyph */
-               blt = NULL;
-               if ( ( efirc = efifb.hiifont->GetGlyph ( efifb.hiifont,
-                                                        character, NULL, &blt,
-                                                        NULL ) ) != 0 ) {
-                       rc = -EEFI ( efirc );
-                       DBGC ( &efifb, "EFIFB could not get glyph %d: %s\n",
-                              character, strerror ( rc ) );
-                       continue;
-               }
-               assert ( blt != NULL );
-
-               /* Sanity check */
-               if ( blt->Width > 8 ) {
-                       DBGC ( &efifb, "EFIFB glyph %d invalid width %d\n",
-                              character, blt->Width );
-                       continue;
-               }
-               if ( blt->Height > efifb.font.height ) {
-                       DBGC ( &efifb, "EFIFB glyph %d invalid height %d\n",
-                              character, blt->Height );
+               if ( ! isprint ( character ) ) {
+                       efifb_draw_unknown ( character );
                        continue;
                }
 
-               /* Convert glyph to bitmap */
-               pixel = blt->Image.Bitmap;
-               offset = ( character * efifb.font.height );
-               for ( y = 0 ; y < blt->Height ; y++ ) {
-                       bitmask = 0;
-                       for ( x = 0 ; x < blt->Width ; x++ ) {
-                               bitmask = rol8 ( bitmask, 1 );
-                               if ( pixel->Blue || pixel->Green || pixel->Red )
-                                       bitmask |= 0x01;
-                               pixel++;
-                       }
-                       copy_to_user ( efifb.glyphs, offset++, &bitmask,
-                                      sizeof ( bitmask ) );
+               /* Get glyph */
+               height = efifb_draw ( character, character, 0 );
+               if ( height < 0 ) {
+                       rc = height;
+                       goto err_draw;
                }
-
-               /* Free glyph */
-               bs->FreePool ( blt );
        }
 
        efifb.font.glyph = efifb_glyph;
        return 0;
 
+ err_draw:
        ufree ( efifb.glyphs );
  err_alloc:
+ err_height:
        return rc;
 }