[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>
pull/631/head
Michael Brown 2022-03-14 22:38:24 +00:00
parent 2ff3385e00
commit ba93c9134c
4 changed files with 161 additions and 74 deletions

View 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);
}

View 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':

View 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 */

View 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 ) )
if ( ! isprint ( character ) ) {
efifb_draw_unknown ( 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, character, 0 );
if ( height < 0 ) {
rc = height;
goto err_draw;
}
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 );
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 ) );
}
/* Free glyph */
bs->FreePool ( blt );
}
efifb.font.glyph = efifb_glyph;
return 0;
err_draw:
ufree ( efifb.glyphs );
err_alloc:
err_height:
return rc;
}