aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorMichael Brown <mcb30@ipxe.org>2022-03-14 22:38:24 +0000
committerMichael Brown <mcb30@ipxe.org>2022-03-15 17:27:18 +0000
commitba93c9134ce9d9edcba117b690fbbdd35b3e066b (patch)
tree91e226e7af8ffda69ac68e16a7be03778128e4d0
parent2ff3385e0078edda43e13ebfc9978fbcc5db311a (diff)
downloadipxe-ba93c9134ce9d9edcba117b690fbbdd35b3e066b.tar.gz
[fbcon] Support Unicode character output
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>
-rw-r--r--src/arch/x86/interface/pcbios/vesafb.c25
-rw-r--r--src/core/fbcon.c5
-rw-r--r--src/include/ipxe/fbcon.h7
-rw-r--r--src/interface/efi/efi_fbcon.c198
4 files changed, 161 insertions, 74 deletions
diff --git a/src/arch/x86/interface/pcbios/vesafb.c b/src/arch/x86/interface/pcbios/vesafb.c
index 50e485852..86edbda42 100644
--- a/src/arch/x86/interface/pcbios/vesafb.c
+++ b/src/arch/x86/interface/pcbios/vesafb.c
@@ -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);
}
diff --git a/src/core/fbcon.c b/src/core/fbcon.c
index 44a56e105..ff3132ac7 100644
--- a/src/core/fbcon.c
+++ b/src/core/fbcon.c
@@ -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':
diff --git a/src/include/ipxe/fbcon.h b/src/include/ipxe/fbcon.h
index 42ffca3d7..a4c7a9ab3 100644
--- a/src/include/ipxe/fbcon.h
+++ b/src/include/ipxe/fbcon.h
@@ -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 */
diff --git a/src/interface/efi/efi_fbcon.c b/src/interface/efi/efi_fbcon.c
index abc5a9390..d9e3e69e6 100644
--- a/src/interface/efi/efi_fbcon.c
+++ b/src/interface/efi/efi_fbcon.c
@@ -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;
}