From 0a239a8211b54a38b4ed428b562f6e66d0daeab2 Mon Sep 17 00:00:00 2001 From: Vladimir 'phcoder' Serbinenko Date: Mon, 15 Mar 2010 21:14:11 +0100 Subject: [PATCH] bidi works in terminal in grub-emu --- commands/help.c | 19 +- conf/common.rmk | 7 +- font/font.c | 576 +++--------------------- include/grub/charset.h | 6 +- include/grub/font.h | 8 + include/grub/i386/vga_common.h | 6 +- include/grub/normal.h | 2 + include/grub/term.h | 11 +- include/grub/unicode.h | 18 + kern/term.c | 30 +- lib/charset.c | 783 +++++++++++++++++++++++++++++++++ normal/main.c | 7 + normal/menu_text.c | 74 ++-- term/gfxterm.c | 86 ++-- term/i386/pc/console.c | 3 +- term/i386/pc/vesafb.c | 42 +- term/i386/pc/vga.c | 26 +- term/i386/pc/vga_text.c | 1 + term/i386/vga_common.c | 54 +-- term/serial.c | 60 +-- term/terminfo.c | 12 +- util/console.c | 50 +-- util/grub-emu.c | 19 +- 23 files changed, 1101 insertions(+), 799 deletions(-) diff --git a/commands/help.c b/commands/help.c index 1181c3bfb..d56ad49a1 100644 --- a/commands/help.c +++ b/commands/help.c @@ -53,23 +53,27 @@ grub_cmd_help (grub_extcmd_t ext __attribute__ ((unused)), int argc, grub_utf8_to_ucs4_alloc (command_help, &unicode_command_help, &unicode_last_position); - FOR_ACTIVE_TERM_OUTPUTS(term) { unsigned stringwidth; grub_uint32_t *unicode_last_screen_position; - + unicode_last_screen_position = unicode_command_help; - + stringwidth = 0; - + while (unicode_last_screen_position < unicode_last_position && stringwidth < ((grub_term_width (term) / 2) - 2)) { + struct grub_unicode_glyph glyph; + unicode_last_screen_position + += grub_unicode_aglomerate_comb (unicode_last_screen_position, + unicode_last_position + - unicode_last_screen_position, + &glyph); + stringwidth - += grub_term_getcharwidth (term, - *unicode_last_screen_position); - unicode_last_screen_position++; + += grub_term_getcharwidth (term, &glyph); } grub_print_ucs4 (unicode_command_help, @@ -78,6 +82,7 @@ grub_cmd_help (grub_extcmd_t ext __attribute__ ((unused)), int argc, grub_print_spaces (term, grub_term_width (term) / 2 - stringwidth); } + if (cnt % 2) grub_printf ("\n"); cnt++; diff --git a/conf/common.rmk b/conf/common.rmk index 80d97706e..3a7dd4f6c 100644 --- a/conf/common.rmk +++ b/conf/common.rmk @@ -636,7 +636,7 @@ keystatus_mod_LDFLAGS = $(COMMON_LDFLAGS) normal_mod_SOURCES = normal/main.c normal/cmdline.c normal/dyncmd.c \ normal/auth.c normal/autofs.c normal/handler.c \ normal/color.c normal/completion.c normal/datetime.c normal/menu.c \ - normal/menu_entry.c normal/menu_text.c \ + normal/menu_entry.c normal/menu_text.c lib/charset.c \ normal/misc.c normal/crypto.c normal/term.c normal/context.c normal_mod_CFLAGS = $(COMMON_CFLAGS) normal_mod_LDFLAGS = $(COMMON_LDFLAGS) @@ -769,11 +769,6 @@ setjmp_mod_SOURCES = lib/$(target_cpu)/setjmp.S setjmp_mod_ASFLAGS = $(COMMON_ASFLAGS) setjmp_mod_LDFLAGS = $(COMMON_LDFLAGS) -pkglib_MODULES += charset.mod -charset_mod_SOURCES = lib/charset.c -charset_mod_CFLAGS = $(COMMON_CFLAGS) -charset_mod_LDFLAGS = $(COMMON_LDFLAGS) - pkglib_MODULES += terminal.mod terminal_mod_SOURCES = commands/terminal.c terminal_mod_CFLAGS = $(COMMON_CFLAGS) diff --git a/font/font.c b/font/font.c index c4e7093d6..a94fe8ed1 100644 --- a/font/font.c +++ b/font/font.c @@ -1135,41 +1135,13 @@ grub_font_blit_glyph_mirror (struct grub_font_glyph *target, } } -static inline enum grub_comb_type -get_comb_type (grub_uint32_t c) -{ - static grub_uint8_t *comb_types = NULL; - struct grub_unicode_compact_range *cur; - - if (!comb_types) - { - unsigned i; - comb_types = grub_zalloc (GRUB_UNICODE_MAX_CACHED_CHAR); - if (comb_types) - for (cur = grub_unicode_compact; cur->end; cur++) - for (i = cur->start; i <= cur->end - && i < GRUB_UNICODE_MAX_CACHED_CHAR; i++) - comb_types[i] = cur->comb_type; - else - grub_errno = GRUB_ERR_NONE; - } - - if (comb_types && c < GRUB_UNICODE_MAX_CACHED_CHAR) - return comb_types[c]; - - for (cur = grub_unicode_compact; cur->end; cur++) - if (cur->start <= c && c <= cur->end) - return cur->comb_type; - - return GRUB_BIDI_TYPE_L; -} - static void blit_comb (const struct grub_unicode_glyph *glyph_id, struct grub_font_glyph *glyph, struct grub_video_signed_rect *bounds_out, struct grub_font_glyph *main_glyph, - struct grub_font_glyph **combining_glyphs) + struct grub_font_glyph **combining_glyphs, + int *device_width) { struct grub_video_signed_rect bounds; unsigned i; @@ -1203,14 +1175,23 @@ blit_comb (const struct grub_unicode_glyph *glyph_id, { if (glyph && glyph->device_width < val) glyph->device_width = val; + if (device_width && *device_width < val) + *device_width = val; } auto void add_device_width (int val); void add_device_width (int val) { if (glyph) glyph->device_width += val; + if (device_width) + *device_width += val; } + if (glyph) + glyph->device_width = main_glyph->device_width; + if (device_width) + *device_width = main_glyph->device_width; + bounds.x = main_glyph->offset_x; bounds.y = main_glyph->offset_y; bounds.width = main_glyph->width; @@ -1231,7 +1212,7 @@ blit_comb (const struct grub_unicode_glyph *glyph_id, /* CGJ is to avoid diacritics reordering. */ if (glyph_id->combining[i] == GRUB_UNICODE_COMBINING_GRAPHEME_JOINER) continue; - combtype = get_comb_type (glyph_id->combining[i]); + combtype = grub_unicode_get_comb_type (glyph_id->combining[i]); switch (combtype) { case GRUB_UNICODE_COMB_OVERLAY: @@ -1302,14 +1283,17 @@ blit_comb (const struct grub_unicode_glyph *glyph_id, } static struct grub_font_glyph * -grub_font_construct_glyph (grub_font_t hinted_font, - const struct grub_unicode_glyph *glyph_id) +grub_font_construct_dry_run (grub_font_t hinted_font, + const struct grub_unicode_glyph *glyph_id, + struct grub_video_signed_rect *bounds, + struct grub_font_glyph ***combining_glyphs_out, + int *device_width) { - grub_font_t font; - struct grub_video_signed_rect bounds; struct grub_font_glyph *main_glyph; struct grub_font_glyph **combining_glyphs; - struct grub_font_glyph *glyph; + + if (combining_glyphs_out) + *combining_glyphs_out = NULL; main_glyph = grub_font_get_glyph_with_fallback (hinted_font, glyph_id->base); @@ -1319,30 +1303,70 @@ grub_font_construct_glyph (grub_font_t hinted_font, /* Glyph not available in any font. Return unknown glyph. */ if (!main_glyph) - return grub_font_dup_glyph (unknown_glyph); + return NULL; + + if (device_width) + *device_width = main_glyph->device_width; if (!glyph_id->ncomb && !glyph_id->attributes) - return grub_font_dup_glyph (main_glyph); + return main_glyph; combining_glyphs = grub_malloc (sizeof (combining_glyphs[0]) * glyph_id->ncomb); if (glyph_id->ncomb && !combining_glyphs) { grub_errno = GRUB_ERR_NONE; - return grub_font_dup_glyph (main_glyph); + return main_glyph; } - font = main_glyph->font; - { unsigned i; for (i = 0; i < glyph_id->ncomb; i++) combining_glyphs[i] - = grub_font_get_glyph_with_fallback (font, glyph_id->combining[i]); + = grub_font_get_glyph_with_fallback (main_glyph->font, + glyph_id->combining[i]); } - blit_comb (glyph_id, NULL, &bounds, main_glyph, combining_glyphs); + blit_comb (glyph_id, NULL, bounds, main_glyph, combining_glyphs, device_width); + if (combining_glyphs_out) + *combining_glyphs_out = combining_glyphs; + else + grub_free (combining_glyphs); + return main_glyph; +} + +int +grub_font_get_constructed_device_width (grub_font_t hinted_font, + const struct grub_unicode_glyph *glyph_id) +{ + int ret; + struct grub_font_glyph *main_glyph; + main_glyph = grub_font_construct_dry_run (hinted_font, glyph_id, NULL, + NULL, &ret); + if (!main_glyph) + return unknown_glyph->device_width; + return ret; +} + +struct grub_font_glyph * +grub_font_construct_glyph (grub_font_t hinted_font, + const struct grub_unicode_glyph *glyph_id) +{ + struct grub_font_glyph *main_glyph; + struct grub_video_signed_rect bounds; + struct grub_font_glyph *glyph; + struct grub_font_glyph **combining_glyphs; + + main_glyph = grub_font_construct_dry_run (hinted_font, glyph_id, + &bounds, &combining_glyphs, NULL); + + if (!main_glyph) + return grub_font_dup_glyph (unknown_glyph); + + if (!combining_glyphs) + return grub_font_dup_glyph (main_glyph); + glyph = grub_zalloc (sizeof (*glyph) + (bounds.width * bounds.height + 7) / 8); if (!glyph) { @@ -1350,12 +1374,11 @@ grub_font_construct_glyph (grub_font_t hinted_font, return grub_font_dup_glyph (main_glyph); } - glyph->font = font; + glyph->font = main_glyph->font; glyph->width = bounds.width; glyph->height = bounds.height; glyph->offset_x = bounds.x; glyph->offset_y = bounds.y; - glyph->device_width = main_glyph->device_width; if (glyph_id->attributes & GRUB_UNICODE_GLYPH_ATTRIBUTE_MIRROR) grub_font_blit_glyph_mirror (glyph, main_glyph, @@ -1368,7 +1391,7 @@ grub_font_construct_glyph (grub_font_t hinted_font, (glyph->height + glyph->offset_y) - (main_glyph->height + main_glyph->offset_y)); - blit_comb (glyph_id, glyph, NULL, main_glyph, combining_glyphs); + blit_comb (glyph_id, glyph, NULL, main_glyph, combining_glyphs, NULL); return glyph; } @@ -1423,465 +1446,6 @@ grub_font_draw_glyph (struct grub_font_glyph *glyph, glyph->width, glyph->height); } -static grub_uint8_t *bidi_types = NULL; - -static void -unpack_bidi (void) -{ - unsigned i; - struct grub_unicode_compact_range *cur; - - bidi_types = grub_zalloc (GRUB_UNICODE_MAX_CACHED_CHAR); - if (!bidi_types) - { - grub_errno = GRUB_ERR_NONE; - return; - } - for (cur = grub_unicode_compact; cur->end; cur++) - for (i = cur->start; i <= cur->end - && i < GRUB_UNICODE_MAX_CACHED_CHAR; i++) - if (cur->bidi_mirror) - bidi_types[i] = cur->bidi_type | 0x80; - else - bidi_types[i] = cur->bidi_type | 0x00; -} - -static inline enum grub_bidi_type -get_bidi_type (grub_uint32_t c) -{ - struct grub_unicode_compact_range *cur; - - if (!bidi_types) - unpack_bidi (); - - if (bidi_types && c < GRUB_UNICODE_MAX_CACHED_CHAR) - return bidi_types[c] & 0x7f; - - for (cur = grub_unicode_compact; cur->end; cur++) - if (cur->start <= c && c <= cur->end) - return cur->bidi_type; - - return GRUB_BIDI_TYPE_L; -} - -static inline int -is_mirrored (grub_uint32_t c) -{ - struct grub_unicode_compact_range *cur; - - if (!bidi_types) - unpack_bidi (); - - if (bidi_types && c < GRUB_UNICODE_MAX_CACHED_CHAR) - return !!(bidi_types[c] & 0x80); - - for (cur = grub_unicode_compact; cur->end; cur++) - if (cur->start <= c && c <= cur->end) - return cur->bidi_mirror; - - return 0; -} - -static grub_ssize_t -grub_err_bidi_logical_to_visual (grub_uint32_t *logical, - grub_size_t logical_len, - struct grub_unicode_glyph **visual_out) -{ - enum grub_bidi_type type = GRUB_BIDI_TYPE_L; - enum override_status {OVERRIDE_NEUTRAL = 0, OVERRIDE_R, OVERRIDE_L}; - unsigned *levels; - enum grub_bidi_type *resolved_types; - unsigned base_level; - enum override_status cur_override; - unsigned i; - unsigned stack_level[GRUB_BIDI_MAX_EXPLICIT_LEVEL + 3]; - enum override_status stack_override[GRUB_BIDI_MAX_EXPLICIT_LEVEL + 3]; - unsigned stack_depth = 0; - unsigned invalid_pushes = 0; - unsigned visual_len = 0; - unsigned run_start, run_end; - struct grub_unicode_glyph *visual; - unsigned cur_level; - - auto void push_stack (unsigned new_override, unsigned new_level); - void push_stack (unsigned new_override, unsigned new_level) - { - if (new_level > GRUB_BIDI_MAX_EXPLICIT_LEVEL) - { - invalid_pushes++; - return; - } - stack_level[stack_depth] = cur_level; - stack_override[stack_depth] = cur_override; - stack_depth++; - cur_level = new_level; - cur_override = new_override; - } - - auto void pop_stack (void); - void pop_stack (void) - { - if (invalid_pushes) - { - invalid_pushes--; - return; - } - if (!stack_depth) - return; - stack_depth--; - cur_level = stack_level[stack_depth]; - cur_override = stack_override[stack_depth]; - } - - auto void revert (unsigned start, unsigned end); - void revert (unsigned start, unsigned end) - { - struct grub_unicode_glyph t; - unsigned k, tl; - for (k = 0; k <= (end - start) / 2; k++) - { - t = visual[start+k]; - visual[start+k] = visual[end-k]; - visual[end-k] = t; - tl = levels[start+k]; - levels[start+k] = levels[end-k]; - levels[end-k] = tl; - } - } - - levels = grub_malloc (sizeof (levels[0]) * logical_len); - if (!levels) - return -1; - - resolved_types = grub_malloc (sizeof (resolved_types[0]) * logical_len); - if (!resolved_types) - { - grub_free (levels); - return -1; - } - - visual = grub_malloc (sizeof (visual[0]) * logical_len); - if (!visual) - { - grub_free (resolved_types); - grub_free (levels); - return -1; - } - - for (i = 0; i < logical_len; i++) - { - type = get_bidi_type (logical[i]); - if (type == GRUB_BIDI_TYPE_L || type == GRUB_BIDI_TYPE_AL - || type == GRUB_BIDI_TYPE_R) - break; - } - if (type == GRUB_BIDI_TYPE_R || type == GRUB_BIDI_TYPE_AL) - base_level = 1; - else - base_level = 0; - - cur_level = base_level; - cur_override = OVERRIDE_NEUTRAL; - { - unsigned last_comb_pointer = 0; - for (i = 0; i < logical_len; i++) - { - /* Variation selectors >= 17 are outside of BMP and SMP. - Handle variation selectors first to avoid potentially costly lookups. - */ - if (logical[i] >= GRUB_UNICODE_VARIATION_SELECTOR_1 - && logical[i] <= GRUB_UNICODE_VARIATION_SELECTOR_16) - { - if (!visual_len) - continue; - visual[visual_len - 1].variant - = logical[i] - GRUB_UNICODE_VARIATION_SELECTOR_1 + 1; - } - if (logical[i] >= GRUB_UNICODE_VARIATION_SELECTOR_17 - && logical[i] <= GRUB_UNICODE_VARIATION_SELECTOR_256) - { - if (!visual_len) - continue; - visual[visual_len - 1].variant - = logical[i] - GRUB_UNICODE_VARIATION_SELECTOR_17 + 17; - continue; - } - - type = get_bidi_type (logical[i]); - switch (type) - { - case GRUB_BIDI_TYPE_RLE: - push_stack (cur_override, (cur_level | 1) + 1); - break; - case GRUB_BIDI_TYPE_RLO: - push_stack (OVERRIDE_R, (cur_level | 1) + 1); - break; - case GRUB_BIDI_TYPE_LRE: - push_stack (cur_override, (cur_level & ~1) + 2); - break; - case GRUB_BIDI_TYPE_LRO: - push_stack (OVERRIDE_L, (cur_level & ~1) + 2); - break; - case GRUB_BIDI_TYPE_PDF: - pop_stack (); - break; - case GRUB_BIDI_TYPE_BN: - break; - default: - { - enum grub_comb_type comb_type; - comb_type = get_comb_type (logical[i]); - if (comb_type) - { - grub_uint32_t *n; - unsigned j; - - if (!visual_len) - break; - if (comb_type == GRUB_UNICODE_COMB_MC - || comb_type == GRUB_UNICODE_COMB_ME - || comb_type == GRUB_UNICODE_COMB_MN) - last_comb_pointer = visual[visual_len - 1].ncomb; - n = grub_realloc (visual[visual_len - 1].combining, - sizeof (grub_uint32_t) - * (visual[visual_len - 1].ncomb + 1)); - if (!n) - { - grub_errno = GRUB_ERR_NONE; - break; - } - - for (j = last_comb_pointer; - j < visual[visual_len - 1].ncomb; j++) - if (get_comb_type (visual[visual_len - 1].combining[j]) - > comb_type) - break; - grub_memmove (visual[visual_len - 1].combining + j + 1, - visual[visual_len - 1].combining + j, - (visual[visual_len - 1].ncomb - j) - * sizeof (visual[visual_len - 1].combining[0])); - visual[visual_len - 1].combining = n; - visual[visual_len - 1].combining[j] = logical[i]; - visual[visual_len - 1].ncomb++; - break; - } - last_comb_pointer = 0; - levels[visual_len] = cur_level; - if (cur_override != OVERRIDE_NEUTRAL) - resolved_types[visual_len] = - (cur_override == OVERRIDE_L) ? GRUB_BIDI_TYPE_L - : GRUB_BIDI_TYPE_R; - else - resolved_types[visual_len] = type; - visual[visual_len].base = logical[i]; - visual[visual_len].variant = 0; - visual[visual_len].attributes = 0; - visual[visual_len].ncomb = 0; - visual[visual_len].combining = NULL; - visual_len++; - } - } - } - } - - for (run_start = 0; run_start < visual_len; run_start = run_end) - { - unsigned prev_level, next_level, cur_run_level; - unsigned last_type, last_strong_type; - for (run_end = run_start; run_end < visual_len && - levels[run_end] == levels[run_start]; run_end++); - if (run_start == 0) - prev_level = base_level; - else - prev_level = levels[run_start - 1]; - if (run_end == visual_len) - next_level = base_level; - else - next_level = levels[run_end]; - cur_run_level = levels[run_start]; - if (prev_level & 1) - last_type = GRUB_BIDI_TYPE_R; - else - last_type = GRUB_BIDI_TYPE_L; - last_strong_type = last_type; - for (i = run_start; i < run_end; i++) - { - switch (resolved_types[i]) - { - case GRUB_BIDI_TYPE_NSM: - resolved_types[i] = last_type; - break; - case GRUB_BIDI_TYPE_EN: - if (last_strong_type == GRUB_BIDI_TYPE_AL) - resolved_types[i] = GRUB_BIDI_TYPE_AN; - break; - case GRUB_BIDI_TYPE_L: - case GRUB_BIDI_TYPE_R: - last_strong_type = resolved_types[i]; - break; - case GRUB_BIDI_TYPE_ES: - if (last_type == GRUB_BIDI_TYPE_EN - && i + 1 < run_end - && resolved_types[i + 1] == GRUB_BIDI_TYPE_EN) - resolved_types[i] = GRUB_BIDI_TYPE_EN; - else - resolved_types[i] = GRUB_BIDI_TYPE_ON; - break; - case GRUB_BIDI_TYPE_ET: - { - unsigned j; - if (last_type == GRUB_BIDI_TYPE_EN) - { - resolved_types[i] = GRUB_BIDI_TYPE_EN; - break; - } - for (j = i; j < run_end - && resolved_types[j] == GRUB_BIDI_TYPE_ET; j++); - if (j != run_end && resolved_types[j] == GRUB_BIDI_TYPE_EN) - { - for (; i < run_end - && resolved_types[i] == GRUB_BIDI_TYPE_ET; i++) - resolved_types[i] = GRUB_BIDI_TYPE_EN; - i--; - break; - } - for (; i < run_end - && resolved_types[i] == GRUB_BIDI_TYPE_ET; i++) - resolved_types[i] = GRUB_BIDI_TYPE_ON; - i--; - break; - } - break; - case GRUB_BIDI_TYPE_CS: - if (last_type == GRUB_BIDI_TYPE_EN - && i + 1 < run_end - && resolved_types[i + 1] == GRUB_BIDI_TYPE_EN) - { - resolved_types[i] = GRUB_BIDI_TYPE_EN; - break; - } - if (last_type == GRUB_BIDI_TYPE_AN - && i + 1 < run_end - && (resolved_types[i + 1] == GRUB_BIDI_TYPE_AN - || (resolved_types[i + 1] == GRUB_BIDI_TYPE_EN - && last_strong_type == GRUB_BIDI_TYPE_AL))) - { - resolved_types[i] = GRUB_BIDI_TYPE_EN; - break; - } - resolved_types[i] = GRUB_BIDI_TYPE_ON; - break; - case GRUB_BIDI_TYPE_AL: - last_strong_type = resolved_types[i]; - resolved_types[i] = GRUB_BIDI_TYPE_R; - break; - default: /* Make GCC happy. */ - break; - } - last_type = resolved_types[i]; - if (resolved_types[i] == GRUB_BIDI_TYPE_EN - && last_strong_type == GRUB_BIDI_TYPE_L) - resolved_types[i] = GRUB_BIDI_TYPE_L; - } - if (prev_level & 1) - last_type = GRUB_BIDI_TYPE_R; - else - last_type = GRUB_BIDI_TYPE_L; - for (i = run_start; i < run_end; ) - { - unsigned j; - unsigned next_type; - for (j = i; j < run_end && - (resolved_types[j] == GRUB_BIDI_TYPE_B - || resolved_types[j] == GRUB_BIDI_TYPE_S - || resolved_types[j] == GRUB_BIDI_TYPE_WS - || resolved_types[j] == GRUB_BIDI_TYPE_ON); j++); - if (j == i) - { - if (resolved_types[i] == GRUB_BIDI_TYPE_L) - last_type = GRUB_BIDI_TYPE_L; - else - last_type = GRUB_BIDI_TYPE_R; - i++; - continue; - } - if (j == run_end) - next_type = (next_level & 1) ? GRUB_BIDI_TYPE_R : GRUB_BIDI_TYPE_L; - else - { - if (resolved_types[j] == GRUB_BIDI_TYPE_L) - next_type = GRUB_BIDI_TYPE_L; - else - next_type = GRUB_BIDI_TYPE_R; - } - if (next_type == last_type) - for (; i < j; i++) - resolved_types[i] = last_type; - else - for (; i < j; i++) - resolved_types[i] = (cur_run_level & 1) ? GRUB_BIDI_TYPE_R - : GRUB_BIDI_TYPE_L; - } - } - - for (i = 0; i < visual_len; i++) - { - if (!(levels[i] & 1) && resolved_types[i] == GRUB_BIDI_TYPE_R) - { - levels[i]++; - continue; - } - if (!(levels[i] & 1) && (resolved_types[i] == GRUB_BIDI_TYPE_AN - || resolved_types[i] == GRUB_BIDI_TYPE_EN)) - { - levels[i] += 2; - continue; - } - if ((levels[i] & 1) && (resolved_types[i] == GRUB_BIDI_TYPE_L - || resolved_types[i] == GRUB_BIDI_TYPE_AN - || resolved_types[i] == GRUB_BIDI_TYPE_EN)) - { - levels[i]++; - continue; - } - } - grub_free (resolved_types); - /* TODO: put line-wrapping here. */ - { - unsigned min_odd_level = 0xffffffff; - unsigned max_level = 0; - unsigned j; - for (i = 0; i < visual_len; i++) - { - if (levels[i] > max_level) - max_level = levels[i]; - if (levels[i] < min_odd_level && (levels[i] & 1)) - min_odd_level = levels[i]; - } - /* FIXME: can be optimized. */ - for (j = max_level; j >= min_odd_level; j--) - { - unsigned in = 0; - for (i = 0; i < visual_len; i++) - { - if (i != 0 && levels[i] >= j && levels[i-1] < j) - in = i; - if (levels[i] >= j && (i + 1 == visual_len || levels[i+1] < j)) - revert (in, i); - } - } - } - - for (i = 0; i < visual_len; i++) - if (is_mirrored (visual[i].base) && levels[i]) - visual[i].attributes |= GRUB_UNICODE_GLYPH_ATTRIBUTE_MIRROR; - - grub_free (levels); - - *visual_out = visual; - return visual_len; -} - /* Draw a UTF-8 string of text on the current video render target. The x coordinate specifies the starting x position for the first character, while the y coordinate specifies the baseline position. @@ -1902,7 +1466,7 @@ grub_font_draw_string (const char *str, grub_font_t font, if (logical_len < 0) return grub_errno; - visual_len = grub_err_bidi_logical_to_visual (logical, logical_len, &visual); + visual_len = grub_bidi_logical_to_visual (logical, logical_len, &visual, 0, 0); grub_free (logical); if (visual_len < 0) return grub_errno; diff --git a/include/grub/charset.h b/include/grub/charset.h index 834705a99..6fdc2aa37 100644 --- a/include/grub/charset.h +++ b/include/grub/charset.h @@ -118,10 +118,8 @@ grub_is_valid_utf8 (const grub_uint8_t *src, grub_size_t srcsize); int grub_utf8_to_ucs4_alloc (const char *msg, grub_uint32_t **unicode_msg, grub_uint32_t **last_position); -grub_size_t grub_utf8_to_ucs4 (grub_uint32_t *dest, - grub_size_t destsize, - const grub_uint8_t *src, - grub_size_t srcsize, +grub_size_t grub_utf8_to_ucs4 (grub_uint32_t *dest, grub_size_t destsize, + const grub_uint8_t *src, grub_size_t srcsize, const grub_uint8_t **srcend); #endif diff --git a/include/grub/font.h b/include/grub/font.h index 54314936a..4df4c2279 100644 --- a/include/grub/font.h +++ b/include/grub/font.h @@ -22,6 +22,7 @@ #include #include #include +#include /* Forward declaration of opaque structure grub_font. Users only pass struct grub_font pointers to the font module functions, @@ -117,4 +118,11 @@ grub_err_t EXPORT_FUNC (grub_font_draw_string) (const char *str, grub_video_color_t color, int left_x, int baseline_y); +int +EXPORT_FUNC (grub_font_get_constructed_device_width) (grub_font_t hinted_font, + const struct grub_unicode_glyph *glyph_id); +struct grub_font_glyph * +EXPORT_FUNC (grub_font_construct_glyph) (grub_font_t hinted_font, + const struct grub_unicode_glyph *glyph_id); + #endif /* ! GRUB_FONT_HEADER */ diff --git a/include/grub/i386/vga_common.h b/include/grub/i386/vga_common.h index f17fc018a..19f14ebed 100644 --- a/include/grub/i386/vga_common.h +++ b/include/grub/i386/vga_common.h @@ -25,8 +25,10 @@ extern grub_uint8_t grub_console_cur_color; -void grub_console_putchar (grub_uint32_t c); -grub_ssize_t grub_console_getcharwidth (grub_uint32_t c); +void +grub_console_putchar (const struct grub_unicode_glyph *c); +grub_ssize_t +grub_console_getcharwidth (const struct grub_unicode_glyph *c); grub_uint16_t grub_console_getwh (void); void grub_console_setcolorstate (grub_term_color_state state); void grub_console_setcolor (grub_uint8_t normal_color, grub_uint8_t highlight_color); diff --git a/include/grub/normal.h b/include/grub/normal.h index fe45ddf71..b53c6c121 100644 --- a/include/grub/normal.h +++ b/include/grub/normal.h @@ -113,4 +113,6 @@ void grub_set_more (int onoff); int grub_normal_get_line_counter (void); void grub_install_newline_hook (void); +void grub_xputs_normal (const char *str); + #endif /* ! GRUB_NORMAL_HEADER */ diff --git a/include/grub/term.h b/include/grub/term.h index 319196b7b..c999b2bd3 100644 --- a/include/grub/term.h +++ b/include/grub/term.h @@ -39,6 +39,7 @@ #include #include #include +#include /* These are used to represent the various color states we use. */ typedef enum @@ -164,11 +165,11 @@ struct grub_term_output grub_err_t (*fini) (void); /* Put a character. C is encoded in Unicode. */ - void (*putchar) (grub_uint32_t c); + void (*putchar) (const struct grub_unicode_glyph *c); /* Get the number of columns occupied by a given character C. C is encoded in Unicode. */ - grub_ssize_t (*getcharwidth) (grub_uint32_t c); + grub_ssize_t (*getcharwidth) (const struct grub_unicode_glyph *c); /* Get the screen size. The return value is ((Width << 8) | Height). */ grub_uint16_t (*getwh) (void); @@ -261,8 +262,7 @@ grub_term_unregister_output (grub_term_output_t term) #define FOR_ACTIVE_TERM_OUTPUTS(var) for (var = grub_term_outputs; var; var = var->next) #define FOR_DISABLED_TERM_OUTPUTS(var) for (var = grub_term_outputs_disabled; var; var = var->next) -void EXPORT_FUNC(grub_putcode) (grub_uint32_t code, - struct grub_term_output *term); +void grub_putcode (grub_uint32_t code, struct grub_term_output *term); int EXPORT_FUNC(grub_getkey) (void); int EXPORT_FUNC(grub_checkkey) (void); int EXPORT_FUNC(grub_getkeystatus) (void); @@ -379,7 +379,8 @@ grub_term_cls (struct grub_term_output *term) } static inline grub_ssize_t -grub_term_getcharwidth (struct grub_term_output *term, grub_uint32_t c) +grub_term_getcharwidth (struct grub_term_output *term, + const struct grub_unicode_glyph *c) { if (term->getcharwidth) return term->getcharwidth (c); diff --git a/include/grub/unicode.h b/include/grub/unicode.h index 59649c3b5..1f6c4df38 100644 --- a/include/grub/unicode.h +++ b/include/grub/unicode.h @@ -55,6 +55,7 @@ enum grub_bidi_type enum grub_comb_type { + GRUB_UNICODE_COMB_NONE = 0, GRUB_UNICODE_COMB_OVERLAY = 1, GRUB_UNICODE_STACK_ATTACHED_BELOW = 202, GRUB_UNICODE_STACK_ATTACHED_ABOVE = 214, @@ -93,4 +94,21 @@ extern struct grub_unicode_compact_range grub_unicode_compact[]; /* Unicode mandates an arbitrary limit. */ #define GRUB_BIDI_MAX_EXPLICIT_LEVEL 61 +grub_ssize_t +grub_bidi_logical_to_visual (const grub_uint32_t *logical, + grub_size_t logical_len, + struct grub_unicode_glyph **visual_out, + grub_ssize_t (*getcharwidth) (const struct grub_unicode_glyph *visual), + grub_size_t max_length); + +enum grub_comb_type +grub_unicode_get_comb_type (grub_uint32_t c); +grub_size_t +grub_unicode_aglomerate_comb (const grub_uint32_t *in, grub_size_t inlen, + struct grub_unicode_glyph *out); +struct grub_unicode_glyph * +grub_unicode_glyph_from_code (grub_uint32_t code); +struct grub_unicode_glyph * +grub_unicode_glyph_dup (const struct grub_unicode_glyph *in); + #endif diff --git a/kern/term.c b/kern/term.c index bd1dfc65f..8139e8118 100644 --- a/kern/term.c +++ b/kern/term.c @@ -31,23 +31,33 @@ struct grub_term_input *grub_term_inputs; void (*grub_newline_hook) (void) = NULL; /* Put a Unicode character. */ -void -grub_putcode (grub_uint32_t code, struct grub_term_output *term) +static void +grub_putcode_dumb (grub_uint32_t code, + struct grub_term_output *term) { + struct grub_unicode_glyph c = + { + .base = code, + .variant = 0, + .attributes = 0, + .ncomb = 0, + .combining = 0 + }; + if (code == '\t' && term->getxy) { int n; n = 8 - ((term->getxy () >> 8) & 7); while (n--) - grub_putcode (' ', term); + grub_putcode_dumb (' ', term); return; } - (term->putchar) (code); + (term->putchar) (&c); if (code == '\n') - (term->putchar) ('\r'); + grub_putcode_dumb ('\r', term); } static void @@ -56,12 +66,12 @@ grub_xputs_dumb (const char *str) for (; *str; str++) { grub_term_output_t term; + grub_uint32_t code = *str; + if (code > 0x7f) + code = '?'; - char c = *str; - if ((unsigned char) c > 0x7f) - c = '?'; FOR_ACTIVE_TERM_OUTPUTS(term) - grub_putcode (c, term); + grub_putcode_dumb (code, term); } } @@ -126,7 +136,7 @@ grub_cls (void) { if ((term->flags & GRUB_TERM_DUMB) || (grub_env_get ("debug"))) { - grub_putcode ('\n', term); + grub_putcode_dumb ('\n', term); grub_term_refresh (term); } else diff --git a/lib/charset.c b/lib/charset.c index 52810da48..c71de7b5a 100644 --- a/lib/charset.c +++ b/lib/charset.c @@ -26,6 +26,9 @@ #include #include #include +#include +#include +#include grub_ssize_t grub_utf8_to_utf16 (grub_uint16_t *dest, grub_size_t destsize, @@ -361,3 +364,783 @@ grub_utf8_to_ucs4 (grub_uint32_t *dest, grub_size_t destsize, *srcend = src; return p - dest; } + +static grub_uint8_t *bidi_types = NULL; + +static void +unpack_bidi (void) +{ + unsigned i; + struct grub_unicode_compact_range *cur; + + bidi_types = grub_zalloc (GRUB_UNICODE_MAX_CACHED_CHAR); + if (!bidi_types) + { + grub_errno = GRUB_ERR_NONE; + return; + } + for (cur = grub_unicode_compact; cur->end; cur++) + for (i = cur->start; i <= cur->end + && i < GRUB_UNICODE_MAX_CACHED_CHAR; i++) + if (cur->bidi_mirror) + bidi_types[i] = cur->bidi_type | 0x80; + else + bidi_types[i] = cur->bidi_type | 0x00; +} + +static inline enum grub_bidi_type +get_bidi_type (grub_uint32_t c) +{ + struct grub_unicode_compact_range *cur; + + if (!bidi_types) + unpack_bidi (); + + if (bidi_types && c < GRUB_UNICODE_MAX_CACHED_CHAR) + return bidi_types[c] & 0x7f; + + for (cur = grub_unicode_compact; cur->end; cur++) + if (cur->start <= c && c <= cur->end) + return cur->bidi_type; + + return GRUB_BIDI_TYPE_L; +} + +static inline int +is_mirrored (grub_uint32_t c) +{ + struct grub_unicode_compact_range *cur; + + if (!bidi_types) + unpack_bidi (); + + if (bidi_types && c < GRUB_UNICODE_MAX_CACHED_CHAR) + return !!(bidi_types[c] & 0x80); + + for (cur = grub_unicode_compact; cur->end; cur++) + if (cur->start <= c && c <= cur->end) + return cur->bidi_mirror; + + return 0; +} + +enum grub_comb_type +grub_unicode_get_comb_type (grub_uint32_t c) +{ + static grub_uint8_t *comb_types = NULL; + struct grub_unicode_compact_range *cur; + + if (!comb_types) + { + unsigned i; + comb_types = grub_zalloc (GRUB_UNICODE_MAX_CACHED_CHAR); + if (comb_types) + for (cur = grub_unicode_compact; cur->end; cur++) + for (i = cur->start; i <= cur->end + && i < GRUB_UNICODE_MAX_CACHED_CHAR; i++) + comb_types[i] = cur->comb_type; + else + grub_errno = GRUB_ERR_NONE; + } + + if (comb_types && c < GRUB_UNICODE_MAX_CACHED_CHAR) + return comb_types[c]; + + for (cur = grub_unicode_compact; cur->end; cur++) + if (cur->start <= c && c <= cur->end) + return cur->comb_type; + + return GRUB_UNICODE_COMB_NONE; +} + +grub_size_t +grub_unicode_aglomerate_comb (const grub_uint32_t *in, grub_size_t inlen, + struct grub_unicode_glyph *out) +{ + int haveout = 0; + const grub_uint32_t *ptr; + unsigned last_comb_pointer = 0; + + grub_memset (out, 0, sizeof (*out)); + + for (ptr = in; ptr < in + inlen; ptr++) + { + /* Variation selectors >= 17 are outside of BMP and SMP. + Handle variation selectors first to avoid potentially costly lookups. + */ + if (*ptr >= GRUB_UNICODE_VARIATION_SELECTOR_1 + && *ptr <= GRUB_UNICODE_VARIATION_SELECTOR_16) + { + if (haveout) + out->variant = *ptr - GRUB_UNICODE_VARIATION_SELECTOR_1 + 1; + continue; + + } + if (*ptr >= GRUB_UNICODE_VARIATION_SELECTOR_17 + && *ptr <= GRUB_UNICODE_VARIATION_SELECTOR_256) + { + if (haveout) + out->variant = *ptr - GRUB_UNICODE_VARIATION_SELECTOR_17 + 17; + continue; + } + + enum grub_comb_type comb_type; + comb_type = grub_unicode_get_comb_type (*ptr); + if (comb_type) + { + grub_uint32_t *n; + unsigned j; + + if (!haveout) + continue; + + if (comb_type == GRUB_UNICODE_COMB_MC + || comb_type == GRUB_UNICODE_COMB_ME + || comb_type == GRUB_UNICODE_COMB_MN) + last_comb_pointer = out->ncomb; + n = grub_realloc (out->combining, + sizeof (grub_uint32_t) * (out->ncomb + 1)); + if (!n) + { + grub_errno = GRUB_ERR_NONE; + continue; + } + + for (j = last_comb_pointer; j < out->ncomb; j++) + if (grub_unicode_get_comb_type (out->combining[j]) > comb_type) + break; + grub_memmove (out->combining + j + 1, + out->combining + j, + (out->ncomb - j) + * sizeof (out->combining[0])); + out->combining = n; + out->combining[j] = *ptr; + out->ncomb++; + continue; + } + if (haveout) + return ptr - in; + haveout = 1; + out->base = *ptr; + out->variant = 0; + out->attributes = 0; + out->ncomb = 0; + out->combining = NULL; + } + return ptr - in; +} + +static grub_ssize_t +grub_bidi_line_logical_to_visual (const grub_uint32_t *logical, + grub_size_t logical_len, + struct grub_unicode_glyph *visual_out, + grub_ssize_t (*getcharwidth) (const struct grub_unicode_glyph *visual), + grub_size_t maxwidth) +{ + enum grub_bidi_type type = GRUB_BIDI_TYPE_L; + enum override_status {OVERRIDE_NEUTRAL = 0, OVERRIDE_R, OVERRIDE_L}; + unsigned *levels; + enum grub_bidi_type *resolved_types; + unsigned base_level; + enum override_status cur_override; + unsigned i; + unsigned stack_level[GRUB_BIDI_MAX_EXPLICIT_LEVEL + 3]; + enum override_status stack_override[GRUB_BIDI_MAX_EXPLICIT_LEVEL + 3]; + unsigned stack_depth = 0; + unsigned invalid_pushes = 0; + unsigned visual_len = 0; + unsigned run_start, run_end; + struct grub_unicode_glyph *visual; + unsigned cur_level; + + auto void push_stack (unsigned new_override, unsigned new_level); + void push_stack (unsigned new_override, unsigned new_level) + { + if (new_level > GRUB_BIDI_MAX_EXPLICIT_LEVEL) + { + invalid_pushes++; + return; + } + stack_level[stack_depth] = cur_level; + stack_override[stack_depth] = cur_override; + stack_depth++; + cur_level = new_level; + cur_override = new_override; + } + + auto void pop_stack (void); + void pop_stack (void) + { + if (invalid_pushes) + { + invalid_pushes--; + return; + } + if (!stack_depth) + return; + stack_depth--; + cur_level = stack_level[stack_depth]; + cur_override = stack_override[stack_depth]; + } + + auto void revert (unsigned start, unsigned end); + void revert (unsigned start, unsigned end) + { + struct grub_unicode_glyph t; + unsigned k, tl; + for (k = 0; k <= (end - start) / 2; k++) + { + t = visual[start+k]; + visual[start+k] = visual[end-k]; + visual[end-k] = t; + tl = levels[start+k]; + levels[start+k] = levels[end-k]; + levels[end-k] = tl; + } + } + + levels = grub_malloc (sizeof (levels[0]) * logical_len); + if (!levels) + return -1; + + resolved_types = grub_malloc (sizeof (resolved_types[0]) * logical_len); + if (!resolved_types) + { + grub_free (levels); + return -1; + } + + visual = grub_malloc (sizeof (visual[0]) * logical_len); + if (!visual) + { + grub_free (resolved_types); + grub_free (levels); + return -1; + } + + for (i = 0; i < logical_len; i++) + { + type = get_bidi_type (logical[i]); + if (type == GRUB_BIDI_TYPE_L || type == GRUB_BIDI_TYPE_AL + || type == GRUB_BIDI_TYPE_R) + break; + } + if (type == GRUB_BIDI_TYPE_R || type == GRUB_BIDI_TYPE_AL) + base_level = 1; + else + base_level = 0; + + cur_level = base_level; + cur_override = OVERRIDE_NEUTRAL; + { + const grub_uint32_t *lptr; + for (lptr = logical; lptr < logical + logical_len;) + { + grub_size_t p = grub_unicode_aglomerate_comb (lptr, logical + + logical_len - lptr, + &visual[visual_len]); + + type = get_bidi_type (visual[visual_len].base); + switch (type) + { + case GRUB_BIDI_TYPE_RLE: + push_stack (cur_override, (cur_level | 1) + 1); + break; + case GRUB_BIDI_TYPE_RLO: + push_stack (OVERRIDE_R, (cur_level | 1) + 1); + break; + case GRUB_BIDI_TYPE_LRE: + push_stack (cur_override, (cur_level & ~1) + 2); + break; + case GRUB_BIDI_TYPE_LRO: + push_stack (OVERRIDE_L, (cur_level & ~1) + 2); + break; + case GRUB_BIDI_TYPE_PDF: + pop_stack (); + break; + case GRUB_BIDI_TYPE_BN: + break; + default: + { + levels[visual_len] = cur_level; + if (cur_override != OVERRIDE_NEUTRAL) + resolved_types[visual_len] = + (cur_override == OVERRIDE_L) ? GRUB_BIDI_TYPE_L + : GRUB_BIDI_TYPE_R; + else + resolved_types[visual_len] = type; + visual_len++; + } + } + lptr += p; + } + } + + for (run_start = 0; run_start < visual_len; run_start = run_end) + { + unsigned prev_level, next_level, cur_run_level; + unsigned last_type, last_strong_type; + for (run_end = run_start; run_end < visual_len && + levels[run_end] == levels[run_start]; run_end++); + if (run_start == 0) + prev_level = base_level; + else + prev_level = levels[run_start - 1]; + if (run_end == visual_len) + next_level = base_level; + else + next_level = levels[run_end]; + cur_run_level = levels[run_start]; + if (prev_level & 1) + last_type = GRUB_BIDI_TYPE_R; + else + last_type = GRUB_BIDI_TYPE_L; + last_strong_type = last_type; + for (i = run_start; i < run_end; i++) + { + switch (resolved_types[i]) + { + case GRUB_BIDI_TYPE_NSM: + resolved_types[i] = last_type; + break; + case GRUB_BIDI_TYPE_EN: + if (last_strong_type == GRUB_BIDI_TYPE_AL) + resolved_types[i] = GRUB_BIDI_TYPE_AN; + break; + case GRUB_BIDI_TYPE_L: + case GRUB_BIDI_TYPE_R: + last_strong_type = resolved_types[i]; + break; + case GRUB_BIDI_TYPE_ES: + if (last_type == GRUB_BIDI_TYPE_EN + && i + 1 < run_end + && resolved_types[i + 1] == GRUB_BIDI_TYPE_EN) + resolved_types[i] = GRUB_BIDI_TYPE_EN; + else + resolved_types[i] = GRUB_BIDI_TYPE_ON; + break; + case GRUB_BIDI_TYPE_ET: + { + unsigned j; + if (last_type == GRUB_BIDI_TYPE_EN) + { + resolved_types[i] = GRUB_BIDI_TYPE_EN; + break; + } + for (j = i; j < run_end + && resolved_types[j] == GRUB_BIDI_TYPE_ET; j++); + if (j != run_end && resolved_types[j] == GRUB_BIDI_TYPE_EN) + { + for (; i < run_end + && resolved_types[i] == GRUB_BIDI_TYPE_ET; i++) + resolved_types[i] = GRUB_BIDI_TYPE_EN; + i--; + break; + } + for (; i < run_end + && resolved_types[i] == GRUB_BIDI_TYPE_ET; i++) + resolved_types[i] = GRUB_BIDI_TYPE_ON; + i--; + break; + } + break; + case GRUB_BIDI_TYPE_CS: + if (last_type == GRUB_BIDI_TYPE_EN + && i + 1 < run_end + && resolved_types[i + 1] == GRUB_BIDI_TYPE_EN) + { + resolved_types[i] = GRUB_BIDI_TYPE_EN; + break; + } + if (last_type == GRUB_BIDI_TYPE_AN + && i + 1 < run_end + && (resolved_types[i + 1] == GRUB_BIDI_TYPE_AN + || (resolved_types[i + 1] == GRUB_BIDI_TYPE_EN + && last_strong_type == GRUB_BIDI_TYPE_AL))) + { + resolved_types[i] = GRUB_BIDI_TYPE_EN; + break; + } + resolved_types[i] = GRUB_BIDI_TYPE_ON; + break; + case GRUB_BIDI_TYPE_AL: + last_strong_type = resolved_types[i]; + resolved_types[i] = GRUB_BIDI_TYPE_R; + break; + default: /* Make GCC happy. */ + break; + } + last_type = resolved_types[i]; + if (resolved_types[i] == GRUB_BIDI_TYPE_EN + && last_strong_type == GRUB_BIDI_TYPE_L) + resolved_types[i] = GRUB_BIDI_TYPE_L; + } + if (prev_level & 1) + last_type = GRUB_BIDI_TYPE_R; + else + last_type = GRUB_BIDI_TYPE_L; + for (i = run_start; i < run_end; ) + { + unsigned j; + unsigned next_type; + for (j = i; j < run_end && + (resolved_types[j] == GRUB_BIDI_TYPE_B + || resolved_types[j] == GRUB_BIDI_TYPE_S + || resolved_types[j] == GRUB_BIDI_TYPE_WS + || resolved_types[j] == GRUB_BIDI_TYPE_ON); j++); + if (j == i) + { + if (resolved_types[i] == GRUB_BIDI_TYPE_L) + last_type = GRUB_BIDI_TYPE_L; + else + last_type = GRUB_BIDI_TYPE_R; + i++; + continue; + } + if (j == run_end) + next_type = (next_level & 1) ? GRUB_BIDI_TYPE_R : GRUB_BIDI_TYPE_L; + else + { + if (resolved_types[j] == GRUB_BIDI_TYPE_L) + next_type = GRUB_BIDI_TYPE_L; + else + next_type = GRUB_BIDI_TYPE_R; + } + if (next_type == last_type) + for (; i < j; i++) + resolved_types[i] = last_type; + else + for (; i < j; i++) + resolved_types[i] = (cur_run_level & 1) ? GRUB_BIDI_TYPE_R + : GRUB_BIDI_TYPE_L; + } + } + + for (i = 0; i < visual_len; i++) + { + if (!(levels[i] & 1) && resolved_types[i] == GRUB_BIDI_TYPE_R) + { + levels[i]++; + continue; + } + if (!(levels[i] & 1) && (resolved_types[i] == GRUB_BIDI_TYPE_AN + || resolved_types[i] == GRUB_BIDI_TYPE_EN)) + { + levels[i] += 2; + continue; + } + if ((levels[i] & 1) && (resolved_types[i] == GRUB_BIDI_TYPE_L + || resolved_types[i] == GRUB_BIDI_TYPE_AN + || resolved_types[i] == GRUB_BIDI_TYPE_EN)) + { + levels[i]++; + continue; + } + } + grub_free (resolved_types); + + if (!visual_len) + { + grub_free (levels); + grub_free (visual); + return 0; + } + { + struct grub_unicode_glyph *outptr = visual_out; + unsigned line_start = 0; + grub_ssize_t line_width = 0; + unsigned k; + + for (k = 0; k <= visual_len; k++) + { + grub_ssize_t last_width = 0; + if (getcharwidth && k != visual_len) + line_width += last_width = getcharwidth (&visual[k]); + if (((grub_ssize_t) maxwidth > 0 + && line_width > (grub_ssize_t) maxwidth) || k == visual_len) + { + unsigned min_odd_level = 0xffffffff; + unsigned max_level = 0; + unsigned j; + for (i = line_start; i < k; i++) + { + if (levels[i] > max_level) + max_level = levels[i]; + if (levels[i] < min_odd_level && (levels[i] & 1)) + min_odd_level = levels[i]; + } + /* FIXME: can be optimized. */ + for (j = max_level; j >= min_odd_level; j--) + { + unsigned in = 0; + for (i = line_start; i < k; i++) + { + if (i != line_start && levels[i] >= j && levels[i-1] < j) + in = i; + if (levels[i] >= j && (i + 1 == k || levels[i+1] < j)) + revert (in, i); + } + } + + for (i = line_start; i < k; i++) + if (is_mirrored (visual[i].base) && levels[i]) + visual[i].attributes |= GRUB_UNICODE_GLYPH_ATTRIBUTE_MIRROR; + + grub_memcpy (outptr, &visual[line_start], + (k - line_start) * sizeof (visual[0])); + outptr += k - line_start; + if (k != visual_len) + { + grub_memset (outptr, 0, sizeof (visual[0])); + outptr->base = '\n'; + outptr++; + } + + line_start = k; + line_width = last_width; + } + } + grub_free (levels); + grub_free (visual); + + return outptr - visual_out; + } +} + +grub_ssize_t +grub_bidi_logical_to_visual (const grub_uint32_t *logical, + grub_size_t logical_len, + struct grub_unicode_glyph **visual_out, + grub_ssize_t (*getcharwidth) (const struct grub_unicode_glyph *visual), + grub_size_t max_length) +{ + const grub_uint32_t *line_start = logical, *ptr; + struct grub_unicode_glyph *visual_ptr; + *visual_out = visual_ptr = grub_malloc (2 * sizeof (visual_ptr[0]) + * logical_len); + for (ptr = logical; ptr <= logical + logical_len; ptr++) + { + if (ptr == logical + logical_len || *ptr == '\n') + { + grub_ssize_t ret; + ret = grub_bidi_line_logical_to_visual (line_start, + ptr - line_start, + visual_ptr, + getcharwidth, + max_length); + if (ret < 0) + { + grub_free (*visual_out); + return ret; + } + visual_ptr += ret; + line_start = ptr; + if (ptr != logical + logical_len) + { + grub_memset (visual_ptr, 0, sizeof (visual_ptr[0])); + visual_ptr->base = '\n'; + visual_ptr++; + } + } + } + return visual_ptr - *visual_out; +} + +struct grub_unicode_glyph * +grub_unicode_glyph_dup (const struct grub_unicode_glyph *in) +{ + struct grub_unicode_glyph *out = grub_malloc (sizeof (*out)); + if (!out) + return NULL; + grub_memcpy (out, in, sizeof (*in)); + out->combining = grub_malloc (in->ncomb * sizeof (*in)); + if (!out->combining) + { + grub_free (out); + return NULL; + } + grub_memcpy (out->combining, in->combining, in->ncomb * sizeof (*in)); + return out; +} + +struct grub_unicode_glyph * +grub_unicode_glyph_from_code (grub_uint32_t code) +{ + struct grub_unicode_glyph *ret; + ret = grub_malloc (sizeof (*ret)); + if (!ret) + return NULL; + + ret->base = code; + ret->variant = 0; + ret->attributes = 0; + ret->ncomb = 0; + ret->combining = 0; + + return ret; +} + +static grub_uint32_t +map_code (grub_uint32_t in, struct grub_term_output *term) +{ + if (in <= 0x7f) + return in; + + if ((term->flags & GRUB_TERM_CODE_TYPE_MASK) == 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 '?'; + } + else + { + /* 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; +} + +/* Put a Unicode character. */ +void +grub_putcode (grub_uint32_t code, + struct grub_term_output *term) +{ + struct grub_unicode_glyph c = + { + .variant = 0, + .attributes = 0, + .ncomb = 0, + .combining = 0 + }; + + if (grub_unicode_get_comb_type (code) != GRUB_UNICODE_COMB_NONE) + return; + + if (code == '\t' && term->getxy) + { + int n; + + n = 8 - ((term->getxy () >> 8) & 7); + while (n--) + grub_putcode (' ', term); + + return; + } + + c.base = map_code (code, term); + + (term->putchar) (&c); + if (code == '\n') + grub_putcode ('\r', term); +} + + +void +grub_print_ucs4 (const grub_uint32_t * str, + const grub_uint32_t * last_position, + struct grub_term_output *term) +{ + if ((term->flags & GRUB_TERM_CODE_TYPE_MASK) + == GRUB_TERM_CODE_TYPE_UCS4_VISUAL) + { + grub_ssize_t visual_len; + struct grub_unicode_glyph *visual; + struct grub_unicode_glyph *visual_ptr; + visual_len = grub_bidi_logical_to_visual (str, + last_position - str, + &visual, + term->getcharwidth, + grub_term_width (term)); + if (visual_len < 0) + { + grub_print_error (); + return; + } + for (visual_ptr = visual; visual_ptr < visual + visual_len; visual_ptr++) + { + struct grub_unicode_glyph glyph_r = { + .base = '\r', + .variant = 0, + .attributes = 0, + .ncomb = 0, + .combining = 0 + }; + term->putchar (visual_ptr); + if (visual_ptr->base == '\n') + term->putchar (&glyph_r); + grub_free (visual_ptr->combining); + } + grub_free (visual); + return; + } + + /* FIXME: UTF-8 terminals. */ + { + const grub_uint32_t *ptr; + for (ptr = str; ptr < last_position; ptr++) + grub_putcode (*ptr, 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, term); + grub_free (unicode_str); + } +} diff --git a/normal/main.c b/normal/main.c index 5a5467485..334be00e9 100644 --- a/normal/main.c +++ b/normal/main.c @@ -644,10 +644,15 @@ grub_env_write_pager (struct grub_env_var *var __attribute__ ((unused)), return grub_strdup (val); } +static void (*grub_xputs_saved) (const char *str); + GRUB_MOD_INIT(normal) { grub_context_init (); + grub_xputs_saved = grub_xputs; + grub_xputs = grub_xputs_normal; + /* Normal mode shouldn't be unloaded. */ if (mod) grub_dl_ref (mod); @@ -676,6 +681,8 @@ GRUB_MOD_FINI(normal) { grub_context_fini (); + grub_xputs = grub_xputs_saved; + grub_set_history (0); grub_register_variable_hook ("pager", 0, 0); grub_fs_autoload_hook = 0; diff --git a/normal/menu_text.c b/normal/menu_text.c index 53aed35b7..1504a8e60 100644 --- a/normal/menu_text.c +++ b/normal/menu_text.c @@ -46,18 +46,6 @@ print_spaces (int number_spaces, struct grub_term_output *term) grub_putcode (' ', term); } -void -grub_print_ucs4 (const grub_uint32_t * str, - const grub_uint32_t * last_position, - struct grub_term_output *term) -{ - while (str < last_position) - { - grub_putcode (*str, term); - str++; - } -} - grub_ssize_t grub_getstringwidth (grub_uint32_t * str, const grub_uint32_t * last_position, struct grub_term_output *term) @@ -66,8 +54,9 @@ grub_getstringwidth (grub_uint32_t * str, const grub_uint32_t * last_position, while (str < last_position) { - width += grub_term_getcharwidth (term, *str); - str++; + struct grub_unicode_glyph glyph; + str += grub_unicode_aglomerate_comb (str, last_position - str, &glyph); + width += grub_term_getcharwidth (term, &glyph); } return width; } @@ -83,8 +72,18 @@ grub_print_message_indented (const char *msg, int margin_left, int margin_right, int msg_len; - line_len = grub_term_width (term) - grub_term_getcharwidth (term, 'm') * - (margin_left + margin_right); + { + struct grub_unicode_glyph pseudo_glyph = { + .base = ' ', + .variant = 0, + .attributes = 0, + .ncomb = 0, + .combining = 0 + }; + line_len = grub_term_width (term) + - grub_term_getcharwidth (term, &pseudo_glyph) + * (margin_left + margin_right); + } msg_len = grub_utf8_to_ucs4_alloc (msg, &unicode_msg, &last_position); @@ -252,34 +251,53 @@ print_entry (int y, int highlight, grub_menu_entry_t entry, grub_term_gotoxy (term, GRUB_TERM_LEFT_BORDER_X + GRUB_TERM_MARGIN, y); + int last_printed = 0; for (x = GRUB_TERM_LEFT_BORDER_X + GRUB_TERM_MARGIN + 1, i = 0; x < (int) (GRUB_TERM_LEFT_BORDER_X + grub_term_border_width (term) - - GRUB_TERM_MARGIN); - i++) + - GRUB_TERM_MARGIN);) { if (i < len && x <= (int) (GRUB_TERM_LEFT_BORDER_X + grub_term_border_width (term) - GRUB_TERM_MARGIN - 1)) { grub_ssize_t width; + struct grub_unicode_glyph glyph; - width = grub_term_getcharwidth (term, unicode_title[i]); + i += grub_unicode_aglomerate_comb (unicode_title + i, + len - i, &glyph); - if (x + width > (int) (GRUB_TERM_LEFT_BORDER_X + width = grub_term_getcharwidth (term, &glyph); + + if (x + width <= (int) (GRUB_TERM_LEFT_BORDER_X + grub_term_border_width (term) - GRUB_TERM_MARGIN - 1)) - grub_putcode (GRUB_TERM_DISP_RIGHT, term); - else - grub_putcode (unicode_title[i], term); - + last_printed = i; x += width; } else - { - grub_putcode (' ', term); - x++; - } + break; } + + grub_print_ucs4 (unicode_title, + unicode_title + last_printed, term); + + if (last_printed != len) + { + grub_putcode (GRUB_TERM_DISP_RIGHT, term); + struct grub_unicode_glyph pseudo_glyph = { + .base = GRUB_TERM_DISP_RIGHT, + .variant = 0, + .attributes = 0, + .ncomb = 0, + .combining = 0 + }; + x += grub_term_getcharwidth (term, &pseudo_glyph); + } + + for (; x < (int) (GRUB_TERM_LEFT_BORDER_X + grub_term_border_width (term) + - GRUB_TERM_MARGIN); x++) + grub_putcode (' ', term); + grub_term_setcolorstate (term, GRUB_TERM_COLOR_NORMAL); grub_putcode (' ', term); diff --git a/term/gfxterm.c b/term/gfxterm.c index e9d343317..797e714d2 100644 --- a/term/gfxterm.c +++ b/term/gfxterm.c @@ -48,7 +48,7 @@ struct grub_dirty_region struct grub_colored_char { /* An Unicode codepoint. */ - grub_uint32_t code; + struct grub_unicode_glyph *code; /* Color values. */ grub_video_color_t fg_color; @@ -147,6 +147,9 @@ static unsigned char calculate_character_width (struct grub_font_glyph *glyph); static void grub_gfxterm_refresh (void); +static grub_ssize_t +grub_gfxterm_getcharwidth (const struct grub_unicode_glyph *c); + static void set_term_color (grub_uint8_t term_color) { @@ -176,7 +179,10 @@ set_term_color (grub_uint8_t term_color) static void clear_char (struct grub_colored_char *c) { - c->code = ' '; + grub_free (c->code); + c->code = grub_unicode_glyph_from_code (' '); + if (!c->code) + grub_errno = GRUB_ERR_NONE; c->fg_color = virtual_screen.fg_color; c->bg_color = virtual_screen.bg_color; c->width = 0; @@ -265,7 +271,10 @@ grub_virtual_screen_setup (unsigned int x, unsigned int y, /* Clear out text buffer. */ for (i = 0; i < virtual_screen.columns * virtual_screen.rows; i++) - clear_char (&(virtual_screen.text_buffer[i])); + { + virtual_screen.text_buffer[i].code = 0; + clear_char (&(virtual_screen.text_buffer[i])); + } return grub_errno; } @@ -610,7 +619,12 @@ paint_char (unsigned cx, unsigned cy) p -= p->index; /* Get glyph for character. */ - glyph = grub_font_get_glyph (virtual_screen.font, p->code); + glyph = grub_font_construct_glyph (virtual_screen.font, p->code); + if (!glyph) + { + grub_errno = GRUB_ERR_NONE; + return; + } ascent = grub_font_get_ascent (virtual_screen.font); width = virtual_screen.normal_char_width * calculate_character_width(glyph); @@ -631,6 +645,7 @@ paint_char (unsigned cx, unsigned cy) /* Mark character to be drawn. */ dirty_region_add (virtual_screen.offset_x + x, virtual_screen.offset_y + y, width, height); + grub_free (glyph); } static inline void @@ -777,6 +792,15 @@ scroll_up (void) { unsigned int i; + /* Clear first line in text buffer. */ + for (i = 0; + i < virtual_screen.columns; + i++) + { + virtual_screen.text_buffer[i].code = 0; + clear_char (&(virtual_screen.text_buffer[i])); + } + /* Scroll text buffer with one line to up. */ grub_memmove (virtual_screen.text_buffer, virtual_screen.text_buffer + virtual_screen.columns, @@ -788,15 +812,18 @@ scroll_up (void) for (i = virtual_screen.columns * (virtual_screen.rows - 1); i < virtual_screen.columns * virtual_screen.rows; i++) - clear_char (&(virtual_screen.text_buffer[i])); + { + virtual_screen.text_buffer[i].code = 0; + clear_char (&(virtual_screen.text_buffer[i])); + } virtual_screen.total_scroll++; } static void -grub_gfxterm_putchar (grub_uint32_t c) +grub_gfxterm_putchar (const struct grub_unicode_glyph *c) { - if (c == '\a') + if (c->base == '\a') /* FIXME */ return; @@ -804,9 +831,9 @@ grub_gfxterm_putchar (grub_uint32_t c) if (virtual_screen.cursor_state) draw_cursor (0); - if (c == '\b' || c == '\n' || c == '\r') + if (c->base == '\b' || c->base == '\n' || c->base == '\r') { - switch (c) + switch (c->base) { case '\b': if (virtual_screen.cursor_x > 0) @@ -827,26 +854,30 @@ grub_gfxterm_putchar (grub_uint32_t c) } else { - struct grub_font_glyph *glyph; struct grub_colored_char *p; unsigned char char_width; - /* Get properties of the character. */ - glyph = grub_font_get_glyph (virtual_screen.font, c); - /* Calculate actual character width for glyph. This is number of times of normal_font_width. */ - char_width = calculate_character_width(glyph); + char_width = grub_gfxterm_getcharwidth (c); /* If we are about to exceed line length, wrap to next line. */ if (virtual_screen.cursor_x + char_width > virtual_screen.columns) - grub_gfxterm_putchar ('\n'); + { + if (virtual_screen.cursor_y >= virtual_screen.rows - 1) + scroll_up (); + else + virtual_screen.cursor_y++; + } /* Find position on virtual screen, and fill information. */ p = (virtual_screen.text_buffer + virtual_screen.cursor_x + virtual_screen.cursor_y * virtual_screen.columns); - p->code = c; + grub_free (p->code); + p->code = grub_unicode_glyph_dup (c); + if (!p->code) + grub_errno = GRUB_ERR_NONE; p->fg_color = virtual_screen.fg_color; p->bg_color = virtual_screen.bg_color; p->width = char_width - 1; @@ -859,7 +890,10 @@ grub_gfxterm_putchar (grub_uint32_t c) for (i = 1; i < char_width; i++) { - p[i].code = ' '; + grub_free (p[i].code); + p[i].code = grub_unicode_glyph_from_code (' '); + if (!p[i].code) + grub_errno = GRUB_ERR_NONE; p[i].width = char_width - 1; p[i].index = i; } @@ -924,18 +958,16 @@ calculate_character_width (struct grub_font_glyph *glyph) } static grub_ssize_t -grub_gfxterm_getcharwidth (grub_uint32_t c) +grub_gfxterm_getcharwidth (const struct grub_unicode_glyph *c) { - struct grub_font_glyph *glyph; - unsigned char char_width; + int dev_width; + dev_width = grub_font_get_constructed_device_width (virtual_screen.font, c); - /* Get properties of the character. */ - glyph = grub_font_get_glyph (virtual_screen.font, c); + if (dev_width == 0) + return 1; - /* Calculate actual character width for glyph. */ - char_width = calculate_character_width (glyph); - - return char_width; + return (dev_width + (virtual_screen.normal_char_width - 1)) + / virtual_screen.normal_char_width; } static grub_uint16_t @@ -1174,7 +1206,7 @@ static struct grub_term_output grub_video_term = .getcolor = grub_virtual_screen_getcolor, .setcursor = grub_gfxterm_setcursor, .refresh = grub_gfxterm_refresh, - .flags = 0, + .flags = GRUB_TERM_CODE_TYPE_UCS4_VISUAL, .next = 0 }; diff --git a/term/i386/pc/console.c b/term/i386/pc/console.c index 43cfe2f2a..5def6fca0 100644 --- a/term/i386/pc/console.c +++ b/term/i386/pc/console.c @@ -65,7 +65,8 @@ static struct grub_term_output grub_console_term_output = .setcolorstate = grub_console_setcolorstate, .setcolor = grub_console_setcolor, .getcolor = grub_console_getcolor, - .setcursor = grub_console_setcursor + .setcursor = grub_console_setcursor, + .flags = GRUB_TERM_CODE_TYPE_VGA }; void diff --git a/term/i386/pc/vesafb.c b/term/i386/pc/vesafb.c index 52694ed10..31333d7ea 100644 --- a/term/i386/pc/vesafb.c +++ b/term/i386/pc/vesafb.c @@ -214,45 +214,7 @@ grub_virtual_screen_get_glyph (grub_uint32_t code, unsigned *width) { if (code > 0x7f) - { - /* Map some unicode characters to the VGA font, if possible. */ - switch (code) - { - case 0x2190: /* left arrow */ - code = 0x1b; - break; - case 0x2191: /* up arrow */ - code = 0x18; - break; - case 0x2192: /* right arrow */ - code = 0x1a; - break; - case 0x2193: /* down arrow */ - code = 0x19; - break; - case 0x2501: /* horizontal line */ - code = 0xc4; - break; - case 0x2503: /* vertical line */ - code = 0xb3; - break; - case 0x250F: /* upper-left corner */ - code = 0xda; - break; - case 0x2513: /* upper-right corner */ - code = 0xbf; - break; - case 0x2517: /* lower-left corner */ - code = 0xc0; - break; - case 0x251B: /* lower-right corner */ - code = 0xd9; - break; - - default: - return grub_font_get_glyph_any (code, bitmap, width); - } - } + return grub_font_get_glyph_any (code, bitmap, width); /* TODO This is wrong for the new font module. Should it be fixed? */ if (bitmap) @@ -592,7 +554,7 @@ static struct grub_term_output grub_vesafb_term = .cls = grub_vesafb_cls, .setcolorstate = grub_virtual_screen_setcolorstate, .setcursor = grub_vesafb_setcursor, - .flags = 0, + .flags = GRUB_TERM_CODE_TYPE_VGA }; GRUB_MOD_INIT(vesafb) diff --git a/term/i386/pc/vga.c b/term/i386/pc/vga.c index addb0bcbb..448a773ef 100644 --- a/term/i386/pc/vga.c +++ b/term/i386/pc/vga.c @@ -287,23 +287,23 @@ scroll_up (void) } static void -grub_vga_putchar (grub_uint32_t c) +grub_vga_putchar (const struct grub_unicode_glyph *c) { #if DEBUG_VGA static int show = 1; #endif - if (c == '\a') + if (c->base == '\a') /* FIXME */ return; - if (c == '\b' || c == '\n' || c == '\r') + if (c->base == '\b' || c->base == '\n' || c->base == '\r') { /* Erase current cursor, if any. */ if (cursor_state) write_char (); - switch (c) + switch (c->base) { case '\b': if (xpos > 0) @@ -331,13 +331,19 @@ grub_vga_putchar (grub_uint32_t c) struct colored_char *p; unsigned char_width = 1; - glyph = grub_font_get_glyph(font, c); + glyph = grub_font_get_glyph(font, c->base); if (xpos + char_width > TEXT_WIDTH) - grub_vga_putchar ('\n'); + { + xpos = 0; + if (ypos >= TEXT_HEIGHT - 1) + scroll_up (); + else + ypos++; + } p = text_buf + xpos + ypos * TEXT_WIDTH; - p->code = c; + p->code = c->base; p->fg_color = fg_color; p->bg_color = bg_color; p->width = char_width - 1; @@ -387,12 +393,12 @@ grub_vga_putchar (grub_uint32_t c) } static grub_ssize_t -grub_vga_getcharwidth (grub_uint32_t c) +grub_vga_getcharwidth (const struct grub_unicode_glyph *c) { #if 0 struct grub_font_glyph glyph; - glyph = grub_font_get_glyph (c); + glyph = grub_font_get_glyph (c->base); return glyph.char_width; #else @@ -499,7 +505,7 @@ static struct grub_term_output grub_vga_term = .cls = grub_vga_cls, .setcolorstate = grub_vga_setcolorstate, .setcursor = grub_vga_setcursor, - .flags = 0, + .flags = GRUB_TERM_CODE_TYPE_UCS4_VISUAL, }; GRUB_MOD_INIT(vga) diff --git a/term/i386/pc/vga_text.c b/term/i386/pc/vga_text.c index f954cab43..82cc11967 100644 --- a/term/i386/pc/vga_text.c +++ b/term/i386/pc/vga_text.c @@ -164,6 +164,7 @@ static struct grub_term_output grub_vga_text_term = .setcolor = grub_console_setcolor, .getcolor = grub_console_getcolor, .setcursor = grub_vga_text_setcursor, + .flags = GRUB_TERM_CODE_TYPE_VGA }; GRUB_MOD_INIT(vga_text) diff --git a/term/i386/vga_common.c b/term/i386/vga_common.c index 131b43ab6..30458b034 100644 --- a/term/i386/vga_common.c +++ b/term/i386/vga_common.c @@ -25,62 +25,14 @@ static grub_uint8_t grub_console_standard_color = 0x7; static grub_uint8_t grub_console_normal_color = 0x7; static grub_uint8_t grub_console_highlight_color = 0x70; -static grub_uint32_t -map_char (grub_uint32_t c) -{ - if (c > 0x7f) - { - /* Map some unicode characters to the VGA font, if possible. */ - switch (c) - { - case 0x2190: /* left arrow */ - c = 0x1b; - break; - case 0x2191: /* up arrow */ - c = 0x18; - break; - case 0x2192: /* right arrow */ - c = 0x1a; - break; - case 0x2193: /* down arrow */ - c = 0x19; - break; - case 0x2501: /* horizontal line */ - c = 0xc4; - break; - case 0x2503: /* vertical line */ - c = 0xb3; - break; - case 0x250F: /* upper-left corner */ - c = 0xda; - break; - case 0x2513: /* upper-right corner */ - c = 0xbf; - break; - case 0x2517: /* lower-left corner */ - c = 0xc0; - break; - case 0x251B: /* lower-right corner */ - c = 0xd9; - break; - - default: - c = '?'; - break; - } - } - - return c; -} - void -grub_console_putchar (grub_uint32_t c) +grub_console_putchar (const struct grub_unicode_glyph *c) { - grub_console_real_putchar (map_char (c)); + grub_console_real_putchar (c->base); } grub_ssize_t -grub_console_getcharwidth (grub_uint32_t c __attribute__ ((unused))) +grub_console_getcharwidth (const struct grub_unicode_glyph *c __attribute__ ((unused))) { /* For now, every printable character has the width 1. */ return 1; diff --git a/term/serial.c b/term/serial.c index 1ef881f63..d98ca4c9e 100644 --- a/term/serial.c +++ b/term/serial.c @@ -317,55 +317,12 @@ serial_hw_init (void) /* The serial version of putchar. */ static void -grub_serial_putchar (grub_uint32_t c) +grub_serial_putchar (const struct grub_unicode_glyph *c) { /* Keep track of the cursor. */ if (keep_track) { - /* The serial terminal does not have VGA fonts. */ - if (c > 0x7F) - { - /* Better than nothing. */ - switch (c) - { - case GRUB_TERM_DISP_LEFT: - c = '<'; - break; - - case GRUB_TERM_DISP_UP: - c = '^'; - break; - - case GRUB_TERM_DISP_RIGHT: - c = '>'; - break; - - case GRUB_TERM_DISP_DOWN: - c = 'v'; - break; - - case GRUB_TERM_DISP_HLINE: - c = '-'; - break; - - case GRUB_TERM_DISP_VLINE: - c = '|'; - break; - - case GRUB_TERM_DISP_UL: - case GRUB_TERM_DISP_UR: - case GRUB_TERM_DISP_LL: - case GRUB_TERM_DISP_LR: - c = '+'; - break; - - default: - c = '?'; - break; - } - } - - switch (c) + switch (c->base) { case '\a': break; @@ -388,19 +345,22 @@ grub_serial_putchar (grub_uint32_t c) default: if (xpos >= TEXT_WIDTH) { - grub_serial_putchar ('\r'); - grub_serial_putchar ('\n'); + xpos = 0; + if (ypos < TEXT_HEIGHT - 1) + ypos++; + serial_hw_put ('\r'); + serial_hw_put ('\n'); } xpos++; break; } } - serial_hw_put (c); + serial_hw_put (c->base); } static grub_ssize_t -grub_serial_getcharwidth (grub_uint32_t c __attribute__ ((unused))) +grub_serial_getcharwidth (const struct grub_unicode_glyph *c __attribute__ ((unused))) { return 1; } @@ -491,7 +451,7 @@ static struct grub_term_output grub_serial_term_output = .cls = grub_serial_cls, .setcolorstate = grub_serial_setcolorstate, .setcursor = grub_serial_setcursor, - .flags = 0, + .flags = GRUB_TERM_CODE_TYPE_UTF8_LOGICAL, }; diff --git a/term/terminfo.c b/term/terminfo.c index 9a5979b1c..48a49f092 100644 --- a/term/terminfo.c +++ b/term/terminfo.c @@ -111,7 +111,17 @@ static void putstr (const char *str, grub_term_output_t oterm) { while (*str) - grub_putcode (*str++, oterm); + { + struct grub_unicode_glyph c = + { + .base = *str++, + .variant = 0, + .attributes = 0, + .ncomb = 0, + .combining = 0 + }; + oterm->putchar (&c); + } } /* Move the cursor to the given position starting with "0". */ diff --git a/util/console.c b/util/console.c index 382fd7f89..91480a810 100644 --- a/util/console.c +++ b/util/console.c @@ -64,54 +64,13 @@ static grub_uint8_t color_map[NUM_COLORS] = static int use_color; static void -grub_ncurses_putchar (grub_uint32_t c) +grub_ncurses_putchar (const struct grub_unicode_glyph *c) { - /* Better than nothing. */ - switch (c) - { - case GRUB_TERM_DISP_LEFT: - c = '<'; - break; - - case GRUB_TERM_DISP_UP: - c = '^'; - break; - - case GRUB_TERM_DISP_RIGHT: - c = '>'; - break; - - case GRUB_TERM_DISP_DOWN: - c = 'v'; - break; - - case GRUB_TERM_DISP_HLINE: - c = '-'; - break; - - case GRUB_TERM_DISP_VLINE: - c = '|'; - break; - - case GRUB_TERM_DISP_UL: - case GRUB_TERM_DISP_UR: - case GRUB_TERM_DISP_LL: - case GRUB_TERM_DISP_LR: - c = '+'; - break; - - default: - /* ncurses does not support Unicode. */ - if (c > 0x7f) - c = '?'; - break; - } - - addch (c | grub_console_attr); + addch (c->base | grub_console_attr); } static grub_ssize_t -grub_ncurses_getcharwidth (grub_uint32_t code __attribute__ ((unused))) +grub_ncurses_getcharwidth (const struct grub_unicode_glyph * c __attribute__ ((unused))) { return 1; } @@ -367,7 +326,8 @@ static struct grub_term_output grub_ncurses_term_output = .setcolor = grub_ncurses_setcolor, .getcolor = grub_ncurses_getcolor, .setcursor = grub_ncurses_setcursor, - .refresh = grub_ncurses_refresh + .refresh = grub_ncurses_refresh, + .flags = GRUB_TERM_CODE_TYPE_ASCII }; void diff --git a/util/grub-emu.c b/util/grub-emu.c index 7d4544509..debf53b1d 100644 --- a/util/grub-emu.c +++ b/util/grub-emu.c @@ -63,6 +63,11 @@ grub_arch_dl_check_header (void *ehdr) return GRUB_ERR_BAD_MODULE; } +void grub_hostfs_init (void); +void grub_hostfs_fini (void); +void grub_host_init (void); +void grub_host_fini (void); + grub_err_t grub_arch_dl_relocate_symbols (grub_dl_t mod, void *ehdr) { @@ -75,7 +80,14 @@ grub_arch_dl_relocate_symbols (grub_dl_t mod, void *ehdr) void grub_reboot (void) { - longjmp (main_env, 1); + grub_fini_all (); + grub_hostfs_fini (); + grub_host_fini (); + + grub_machine_fini (); + + exit (0); + // longjmp (main_env, 1); } void @@ -146,11 +158,6 @@ usage (int status) } -void grub_hostfs_init (void); -void grub_hostfs_fini (void); -void grub_host_init (void); -void grub_host_fini (void); - int main (int argc, char *argv[]) {