diff --git a/include/grub/unicode.h b/include/grub/unicode.h index b68c7faf5..249297863 100644 --- a/include/grub/unicode.h +++ b/include/grub/unicode.h @@ -156,4 +156,7 @@ grub_unicode_glyph_from_code (grub_uint32_t code) return ret; } +grub_uint32_t +grub_unicode_mirror_code (grub_uint32_t in); + #endif diff --git a/normal/charset.c b/normal/charset.c index 2473f1b52..7c455a357 100644 --- a/normal/charset.c +++ b/normal/charset.c @@ -1022,75 +1022,8 @@ grub_bidi_logical_to_visual (const grub_uint32_t *logical, return visual_ptr - *visual_out; } -static grub_uint32_t -map_code (grub_uint32_t in, struct grub_term_output *term) -{ - if (in <= 0x7f) - return in; - - switch (term->flags & GRUB_TERM_CODE_TYPE_MASK) - { - case GRUB_TERM_CODE_TYPE_VGA: - switch (in) - { - case GRUB_TERM_DISP_LEFT: - return 0x1b; - case GRUB_TERM_DISP_UP: - return 0x18; - case GRUB_TERM_DISP_RIGHT: - return 0x1a; - case GRUB_TERM_DISP_DOWN: - return 0x19; - case GRUB_TERM_DISP_HLINE: - return 0xc4; - case GRUB_TERM_DISP_VLINE: - return 0xb3; - case GRUB_TERM_DISP_UL: - return 0xda; - case GRUB_TERM_DISP_UR: - return 0xbf; - case GRUB_TERM_DISP_LL: - return 0xc0; - case GRUB_TERM_DISP_LR: - return 0xd9; - } - return '?'; - case GRUB_TERM_CODE_TYPE_ASCII: - /* Better than nothing. */ - switch (in) - { - case GRUB_TERM_DISP_LEFT: - return '<'; - - case GRUB_TERM_DISP_UP: - return '^'; - - case GRUB_TERM_DISP_RIGHT: - return '>'; - - case GRUB_TERM_DISP_DOWN: - return 'v'; - - case GRUB_TERM_DISP_HLINE: - return '-'; - - case GRUB_TERM_DISP_VLINE: - return '|'; - - case GRUB_TERM_DISP_UL: - case GRUB_TERM_DISP_UR: - case GRUB_TERM_DISP_LL: - case GRUB_TERM_DISP_LR: - return '+'; - - } - return '?'; - } - return in; -} - -static grub_uint32_t -mirror_code (grub_uint32_t in) +grub_uint32_t +grub_unicode_mirror_code (grub_uint32_t in) { int i; for (i = 0; grub_unicode_bidi_pairs[i].key; i++) @@ -1098,255 +1031,3 @@ mirror_code (grub_uint32_t in) return grub_unicode_bidi_pairs[i].replace; return in; } - -static void -putglyph (const struct grub_unicode_glyph *c, struct grub_term_output *term) -{ - struct grub_unicode_glyph c2 = - { - .variant = 0, - .attributes = 0, - .ncomb = 0, - .combining = 0, - .estimated_width = 1 - }; - - if (c->base == '\t' && term->getxy) - { - int n; - - n = 8 - ((term->getxy () >> 8) & 7); - c2.base = ' '; - while (n--) - (term->putchar) (&c2); - - return; - } - - if ((term->flags & GRUB_TERM_CODE_TYPE_MASK) - == GRUB_TERM_CODE_TYPE_UTF8_LOGICAL - || (term->flags & GRUB_TERM_CODE_TYPE_MASK) - == GRUB_TERM_CODE_TYPE_UTF8_VISUAL) - { - int i; - c2.estimated_width = grub_term_getcharwidth (term, c); - for (i = -1; i < (int) c->ncomb; i++) - { - grub_uint8_t u8[20], *ptr; - grub_uint32_t code; - - if (i == -1) - { - code = c->base; - if ((term->flags & GRUB_TERM_CODE_TYPE_MASK) - == GRUB_TERM_CODE_TYPE_UTF8_VISUAL - && (c->attributes & GRUB_UNICODE_GLYPH_ATTRIBUTE_MIRROR)) - code = mirror_code (code); - } - else - code = c->combining[i].code; - - grub_ucs4_to_utf8 (&code, 1, u8, sizeof (u8)); - - for (ptr = u8; *ptr; ptr++) - { - c2.base = *ptr; - (term->putchar) (&c2); - c2.estimated_width = 0; - } - } - c2.estimated_width = 1; - } - else - (term->putchar) (c); - - if (c->base == '\n') - { - c2.base = '\r'; - (term->putchar) (&c2); - } -} - -static void -putcode_real (grub_uint32_t code, struct grub_term_output *term) -{ - struct grub_unicode_glyph c = - { - .variant = 0, - .attributes = 0, - .ncomb = 0, - .combining = 0, - .estimated_width = 1 - }; - - c.base = map_code (code, term); - putglyph (&c, term); -} - -/* Put a Unicode character. */ -void -grub_putcode (grub_uint32_t code, struct grub_term_output *term) -{ - /* Combining character by itself? */ - if (grub_unicode_get_comb_type (code) != GRUB_UNICODE_COMB_NONE) - return; - - putcode_real (code, term); -} - -void -grub_print_ucs4 (const grub_uint32_t * str, - const grub_uint32_t * last_position, - int margin_left, int margin_right, - struct grub_term_output *term) -{ - grub_ssize_t max_width; - grub_ssize_t startwidth; - - { - struct grub_unicode_glyph space_glyph = { - .base = ' ', - .variant = 0, - .attributes = 0, - .ncomb = 0, - .combining = 0 - }; - max_width = grub_term_width (term) - - grub_term_getcharwidth (term, &space_glyph) - * (margin_left + margin_right) - 1; - } - - if (((term->getxy () >> 8) & 0xff) < margin_left) - grub_print_spaces (term, margin_left - ((term->getxy () >> 8) & 0xff)); - - startwidth = ((term->getxy () >> 8) & 0xff) - margin_left; - - if ((term->flags & GRUB_TERM_CODE_TYPE_MASK) - == GRUB_TERM_CODE_TYPE_VISUAL_GLYPHS - || (term->flags & GRUB_TERM_CODE_TYPE_MASK) - == GRUB_TERM_CODE_TYPE_UTF8_VISUAL) - { - grub_ssize_t visual_len; - struct grub_unicode_glyph *visual; - struct grub_unicode_glyph *visual_ptr; - - auto grub_ssize_t getcharwidth (const struct grub_unicode_glyph *c); - grub_ssize_t getcharwidth (const struct grub_unicode_glyph *c) - { - return grub_term_getcharwidth (term, c); - } - - visual_len = grub_bidi_logical_to_visual (str, last_position - str, - &visual, getcharwidth, - max_width, startwidth); - if (visual_len < 0) - { - grub_print_error (); - return; - } - for (visual_ptr = visual; visual_ptr < visual + visual_len; visual_ptr++) - { - if (visual_ptr->base == '\n') - grub_print_spaces (term, margin_right); - putglyph (visual_ptr, term); - if (visual_ptr->base == '\n') - grub_print_spaces (term, margin_left); - grub_free (visual_ptr->combining); - } - grub_free (visual); - return; - } - - { - const grub_uint32_t *ptr; - grub_ssize_t line_width = startwidth; - grub_ssize_t lastspacewidth = 0; - const grub_uint32_t *line_start = str, *last_space = str - 1; - - for (ptr = str; ptr < last_position; ptr++) - { - grub_ssize_t last_width = 0; - if (grub_unicode_get_comb_type (*ptr) == GRUB_UNICODE_COMB_NONE) - { - struct grub_unicode_glyph c = { - .variant = 0, - .attributes = 0, - .ncomb = 0, - .combining = 0 - }; - c.base = *ptr; - line_width += last_width = grub_term_getcharwidth (term, &c); - } - - if (*ptr == ' ') - { - lastspacewidth = line_width; - last_space = ptr; - } - - if (line_width > max_width || *ptr == '\n') - { - const grub_uint32_t *ptr2; - - if (line_width > max_width && last_space > line_start) - ptr = last_space; - else if (line_width > max_width - && line_start == str && startwidth != 0) - { - ptr = str; - lastspacewidth = startwidth; - } - else - lastspacewidth = line_width - last_width; - - for (ptr2 = line_start; ptr2 < ptr; ptr2++) - { - /* Skip combining characters on non-UTF8 terminals. */ - if ((term->flags & GRUB_TERM_CODE_TYPE_MASK) - != GRUB_TERM_CODE_TYPE_UTF8_LOGICAL - && grub_unicode_get_comb_type (*ptr2) - != GRUB_UNICODE_COMB_NONE) - continue; - putcode_real (*ptr2, term); - } - - grub_print_spaces (term, margin_right); - grub_putcode ('\n', term); - line_width -= lastspacewidth; - grub_print_spaces (term, margin_left); - if (ptr == last_space || *ptr == '\n') - ptr++; - line_start = ptr; - } - } - - { - const grub_uint32_t *ptr2; - for (ptr2 = line_start; ptr2 < last_position; ptr2++) - { - /* Skip combining characters on non-UTF8 terminals. */ - if ((term->flags & GRUB_TERM_CODE_TYPE_MASK) - != GRUB_TERM_CODE_TYPE_UTF8_LOGICAL - && grub_unicode_get_comb_type (*ptr2) - != GRUB_UNICODE_COMB_NONE) - continue; - putcode_real (*ptr2, term); - } - } - } -} - -void -grub_xputs_normal (const char *str) -{ - grub_term_output_t term; - - FOR_ACTIVE_TERM_OUTPUTS(term) - { - grub_uint32_t *unicode_str, *unicode_last_position; - grub_utf8_to_ucs4_alloc (str, &unicode_str, - &unicode_last_position); - grub_print_ucs4 (unicode_str, unicode_last_position, 0, 0, term); - grub_free (unicode_str); - } -} diff --git a/normal/term.c b/normal/term.c index 8a974d035..a2a313779 100644 --- a/normal/term.c +++ b/normal/term.c @@ -95,6 +95,73 @@ grub_install_newline_hook (void) grub_newline_hook = process_newline; } +static grub_uint32_t +map_code (grub_uint32_t in, struct grub_term_output *term) +{ + if (in <= 0x7f) + return in; + + switch (term->flags & GRUB_TERM_CODE_TYPE_MASK) + { + case GRUB_TERM_CODE_TYPE_VGA: + switch (in) + { + case GRUB_TERM_DISP_LEFT: + return 0x1b; + case GRUB_TERM_DISP_UP: + return 0x18; + case GRUB_TERM_DISP_RIGHT: + return 0x1a; + case GRUB_TERM_DISP_DOWN: + return 0x19; + case GRUB_TERM_DISP_HLINE: + return 0xc4; + case GRUB_TERM_DISP_VLINE: + return 0xb3; + case GRUB_TERM_DISP_UL: + return 0xda; + case GRUB_TERM_DISP_UR: + return 0xbf; + case GRUB_TERM_DISP_LL: + return 0xc0; + case GRUB_TERM_DISP_LR: + return 0xd9; + } + return '?'; + case GRUB_TERM_CODE_TYPE_ASCII: + /* Better than nothing. */ + switch (in) + { + case GRUB_TERM_DISP_LEFT: + return '<'; + + case GRUB_TERM_DISP_UP: + return '^'; + + case GRUB_TERM_DISP_RIGHT: + return '>'; + + case GRUB_TERM_DISP_DOWN: + return 'v'; + + case GRUB_TERM_DISP_HLINE: + return '-'; + + case GRUB_TERM_DISP_VLINE: + return '|'; + + case GRUB_TERM_DISP_UL: + case GRUB_TERM_DISP_UR: + case GRUB_TERM_DISP_LL: + case GRUB_TERM_DISP_LR: + return '+'; + + } + return '?'; + } + return in; +} + void grub_puts_terminal (const char *str, struct grub_term_output *term) { @@ -265,3 +332,255 @@ read_terminal_list (void) grub_errno = GRUB_ERR_NONE; } + +static void +putglyph (const struct grub_unicode_glyph *c, struct grub_term_output *term) +{ + struct grub_unicode_glyph c2 = + { + .variant = 0, + .attributes = 0, + .ncomb = 0, + .combining = 0, + .estimated_width = 1 + }; + + if (c->base == '\t' && term->getxy) + { + int n; + + n = 8 - ((term->getxy () >> 8) & 7); + c2.base = ' '; + while (n--) + (term->putchar) (&c2); + + return; + } + + if ((term->flags & GRUB_TERM_CODE_TYPE_MASK) + == GRUB_TERM_CODE_TYPE_UTF8_LOGICAL + || (term->flags & GRUB_TERM_CODE_TYPE_MASK) + == GRUB_TERM_CODE_TYPE_UTF8_VISUAL) + { + int i; + c2.estimated_width = grub_term_getcharwidth (term, c); + for (i = -1; i < (int) c->ncomb; i++) + { + grub_uint8_t u8[20], *ptr; + grub_uint32_t code; + + if (i == -1) + { + code = c->base; + if ((term->flags & GRUB_TERM_CODE_TYPE_MASK) + == GRUB_TERM_CODE_TYPE_UTF8_VISUAL + && (c->attributes & GRUB_UNICODE_GLYPH_ATTRIBUTE_MIRROR)) + code = grub_unicode_mirror_code (code); + } + else + code = c->combining[i].code; + + grub_ucs4_to_utf8 (&code, 1, u8, sizeof (u8)); + + for (ptr = u8; *ptr; ptr++) + { + c2.base = *ptr; + (term->putchar) (&c2); + c2.estimated_width = 0; + } + } + c2.estimated_width = 1; + } + else + (term->putchar) (c); + + if (c->base == '\n') + { + c2.base = '\r'; + (term->putchar) (&c2); + } +} + +static void +putcode_real (grub_uint32_t code, struct grub_term_output *term) +{ + struct grub_unicode_glyph c = + { + .variant = 0, + .attributes = 0, + .ncomb = 0, + .combining = 0, + .estimated_width = 1 + }; + + c.base = map_code (code, term); + putglyph (&c, term); +} + +/* Put a Unicode character. */ +void +grub_putcode (grub_uint32_t code, struct grub_term_output *term) +{ + /* Combining character by itself? */ + if (grub_unicode_get_comb_type (code) != GRUB_UNICODE_COMB_NONE) + return; + + putcode_real (code, term); +} + +void +grub_print_ucs4 (const grub_uint32_t * str, + const grub_uint32_t * last_position, + int margin_left, int margin_right, + struct grub_term_output *term) +{ + grub_ssize_t max_width; + grub_ssize_t startwidth; + + { + struct grub_unicode_glyph space_glyph = { + .base = ' ', + .variant = 0, + .attributes = 0, + .ncomb = 0, + .combining = 0 + }; + max_width = grub_term_width (term) + - grub_term_getcharwidth (term, &space_glyph) + * (margin_left + margin_right) - 1; + } + + if (((term->getxy () >> 8) & 0xff) < margin_left) + grub_print_spaces (term, margin_left - ((term->getxy () >> 8) & 0xff)); + + startwidth = ((term->getxy () >> 8) & 0xff) - margin_left; + + if ((term->flags & GRUB_TERM_CODE_TYPE_MASK) + == GRUB_TERM_CODE_TYPE_VISUAL_GLYPHS + || (term->flags & GRUB_TERM_CODE_TYPE_MASK) + == GRUB_TERM_CODE_TYPE_UTF8_VISUAL) + { + grub_ssize_t visual_len; + struct grub_unicode_glyph *visual; + struct grub_unicode_glyph *visual_ptr; + + auto grub_ssize_t getcharwidth (const struct grub_unicode_glyph *c); + grub_ssize_t getcharwidth (const struct grub_unicode_glyph *c) + { + return grub_term_getcharwidth (term, c); + } + + visual_len = grub_bidi_logical_to_visual (str, last_position - str, + &visual, getcharwidth, + max_width, startwidth); + if (visual_len < 0) + { + grub_print_error (); + return; + } + for (visual_ptr = visual; visual_ptr < visual + visual_len; visual_ptr++) + { + if (visual_ptr->base == '\n') + grub_print_spaces (term, margin_right); + putglyph (visual_ptr, term); + if (visual_ptr->base == '\n') + grub_print_spaces (term, margin_left); + grub_free (visual_ptr->combining); + } + grub_free (visual); + return; + } + + { + const grub_uint32_t *ptr; + grub_ssize_t line_width = startwidth; + grub_ssize_t lastspacewidth = 0; + const grub_uint32_t *line_start = str, *last_space = str - 1; + + for (ptr = str; ptr < last_position; ptr++) + { + grub_ssize_t last_width = 0; + if (grub_unicode_get_comb_type (*ptr) == GRUB_UNICODE_COMB_NONE) + { + struct grub_unicode_glyph c = { + .variant = 0, + .attributes = 0, + .ncomb = 0, + .combining = 0 + }; + c.base = *ptr; + line_width += last_width = grub_term_getcharwidth (term, &c); + } + + if (*ptr == ' ') + { + lastspacewidth = line_width; + last_space = ptr; + } + + if (line_width > max_width || *ptr == '\n') + { + const grub_uint32_t *ptr2; + + if (line_width > max_width && last_space > line_start) + ptr = last_space; + else if (line_width > max_width + && line_start == str && startwidth != 0) + { + ptr = str; + lastspacewidth = startwidth; + } + else + lastspacewidth = line_width - last_width; + + for (ptr2 = line_start; ptr2 < ptr; ptr2++) + { + /* Skip combining characters on non-UTF8 terminals. */ + if ((term->flags & GRUB_TERM_CODE_TYPE_MASK) + != GRUB_TERM_CODE_TYPE_UTF8_LOGICAL + && grub_unicode_get_comb_type (*ptr2) + != GRUB_UNICODE_COMB_NONE) + continue; + putcode_real (*ptr2, term); + } + + grub_print_spaces (term, margin_right); + grub_putcode ('\n', term); + line_width -= lastspacewidth; + grub_print_spaces (term, margin_left); + if (ptr == last_space || *ptr == '\n') + ptr++; + line_start = ptr; + } + } + + { + const grub_uint32_t *ptr2; + for (ptr2 = line_start; ptr2 < last_position; ptr2++) + { + /* Skip combining characters on non-UTF8 terminals. */ + if ((term->flags & GRUB_TERM_CODE_TYPE_MASK) + != GRUB_TERM_CODE_TYPE_UTF8_LOGICAL + && grub_unicode_get_comb_type (*ptr2) + != GRUB_UNICODE_COMB_NONE) + continue; + putcode_real (*ptr2, term); + } + } + } +} + +void +grub_xputs_normal (const char *str) +{ + grub_term_output_t term; + + FOR_ACTIVE_TERM_OUTPUTS(term) + { + grub_uint32_t *unicode_str, *unicode_last_position; + grub_utf8_to_ucs4_alloc (str, &unicode_str, + &unicode_last_position); + grub_print_ucs4 (unicode_str, unicode_last_position, 0, 0, term); + grub_free (unicode_str); + } +}