mirror of https://github.com/ipxe/ipxe.git
[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
parent
2ff3385e00
commit
ba93c9134c
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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':
|
||||
|
|
|
@ -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 */
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in New Issue