mirror of
https://gitlab.com/qemu-project/ipxe.git
synced 2025-11-03 07:59:06 +08:00
Compare commits
3 Commits
e7595fe88d
...
utf8
| Author | SHA1 | Date | |
|---|---|---|---|
| 253987b182 | |||
| 46a855283a | |||
| 682c4cc517 |
@ -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 0xb1
|
||||
|
||||
/* 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,10 @@ void fbcon_putchar ( struct fbcon *fbcon, int character ) {
|
||||
if ( character < 0 )
|
||||
return;
|
||||
|
||||
/* Accumulate Unicode characters */
|
||||
if ( ( character = utf8_accumulate ( &fbcon->utf8, 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 */
|
||||
|
||||
@ -26,6 +26,7 @@ FILE_LICENCE ( GPL2_OR_LATER );
|
||||
#include <ipxe/efi/efi.h>
|
||||
#include <ipxe/efi/Protocol/ConsoleControl/ConsoleControl.h>
|
||||
#include <ipxe/ansiesc.h>
|
||||
#include <ipxe/utf8.h>
|
||||
#include <ipxe/console.h>
|
||||
#include <ipxe/keymap.h>
|
||||
#include <ipxe/init.h>
|
||||
@ -201,6 +202,9 @@ static struct ansiesc_context efi_ansiesc_ctx = {
|
||||
.handlers = efi_ansiesc_handlers,
|
||||
};
|
||||
|
||||
/** EFI console UTF-8 accumulator */
|
||||
static struct utf8_accumulator efi_utf8_acc;
|
||||
|
||||
/**
|
||||
* Print a character to EFI console
|
||||
*
|
||||
@ -208,13 +212,25 @@ static struct ansiesc_context efi_ansiesc_ctx = {
|
||||
*/
|
||||
static void efi_putchar ( int character ) {
|
||||
EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL *conout = efi_systab->ConOut;
|
||||
wchar_t wstr[] = { character, 0 };
|
||||
wchar_t wstr[2];
|
||||
|
||||
/* Intercept ANSI escape sequences */
|
||||
character = ansiesc_process ( &efi_ansiesc_ctx, character );
|
||||
if ( character < 0 )
|
||||
return;
|
||||
|
||||
/* Accumulate Unicode characters */
|
||||
character = utf8_accumulate ( &efi_utf8_acc, character );
|
||||
if ( character == 0 )
|
||||
return;
|
||||
|
||||
/* Treat unrepresentable (non-UCS2) characters as invalid */
|
||||
if ( character & ~( ( wchar_t ) -1UL ) )
|
||||
character = UTF8_INVALID;
|
||||
|
||||
/* Output character */
|
||||
wstr[0] = character;
|
||||
wstr[1] = L'\0';
|
||||
conout->OutputString ( conout, wstr );
|
||||
}
|
||||
|
||||
|
||||
@ -62,6 +62,12 @@ 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
|
||||
|
||||
/** Number of dynamic non-ASCII glyphs in cache */
|
||||
#define EFIFB_DYNAMIC 32
|
||||
|
||||
/* Forward declaration */
|
||||
struct console_driver efifb_console __console_driver;
|
||||
|
||||
@ -84,22 +90,159 @@ struct efifb {
|
||||
struct fbcon_colour_map map;
|
||||
/** Font definition */
|
||||
struct fbcon_font font;
|
||||
/** Character glyphs */
|
||||
/** Character glyph cache */
|
||||
userptr_t glyphs;
|
||||
/** Dynamic characters in cache */
|
||||
unsigned int dynamic[EFIFB_DYNAMIC];
|
||||
/** Next dynamic character cache entry to evict */
|
||||
unsigned int next;
|
||||
};
|
||||
|
||||
/** The EFI frame buffer */
|
||||
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 dynamic glyph index
|
||||
*
|
||||
* @v character Unicode character
|
||||
* @ret index Glyph cache index
|
||||
*/
|
||||
static unsigned int efifb_dynamic ( unsigned int character ) {
|
||||
unsigned int dynamic;
|
||||
unsigned int index;
|
||||
unsigned int i;
|
||||
int height;
|
||||
|
||||
/* Search existing cached entries */
|
||||
for ( i = 0 ; i < EFIFB_DYNAMIC ; i++ ) {
|
||||
if ( character == efifb.dynamic[i] )
|
||||
return ( EFIFB_ASCII + i );
|
||||
}
|
||||
|
||||
/* Overwrite the oldest cache entry */
|
||||
dynamic = ( efifb.next++ % EFIFB_DYNAMIC );
|
||||
index = ( EFIFB_ASCII + dynamic );
|
||||
DBGC2 ( &efifb, "EFIFB dynamic %#02x is glyph %#02x\n",
|
||||
dynamic, character );
|
||||
|
||||
/* Draw glyph */
|
||||
height = efifb_draw ( character, index, 0 );
|
||||
if ( height < 0 )
|
||||
efifb_draw_unknown ( index );
|
||||
|
||||
/* Record cached character */
|
||||
efifb.dynamic[dynamic] = character;
|
||||
|
||||
return index;
|
||||
}
|
||||
|
||||
/**
|
||||
* 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 dynamic glyph cache */
|
||||
index = efifb_dynamic ( character );
|
||||
}
|
||||
|
||||
/* Copy cached glyph */
|
||||
offset = ( index * efifb.font.height );
|
||||
copy_from_user ( glyph, efifb.glyphs, offset, efifb.font.height );
|
||||
}
|
||||
|
||||
@ -109,16 +252,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 +265,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_DYNAMIC ) * efifb.font.height );
|
||||
efifb.glyphs = umalloc ( len );
|
||||
if ( ! efifb.glyphs ) {
|
||||
rc = -ENOMEM;
|
||||
@ -168,60 +299,32 @@ 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 );
|
||||
}
|
||||
|
||||
/* Clear dynamic glyph character cache */
|
||||
memset ( efifb.dynamic, 0, sizeof ( efifb.dynamic ) );
|
||||
|
||||
efifb.font.glyph = efifb_glyph;
|
||||
return 0;
|
||||
|
||||
err_draw:
|
||||
ufree ( efifb.glyphs );
|
||||
err_alloc:
|
||||
err_height:
|
||||
return rc;
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user