From 1ce5f411b75c2bcd899e1a838d1d6292d6c76972 Mon Sep 17 00:00:00 2001 From: Vladimir 'phcoder' Serbinenko Date: Wed, 16 Jan 2013 13:41:16 +0100 Subject: [PATCH 110/471] Improve bidi handling in entry editor. --- ChangeLog | 4 + grub-core/gfxmenu/font.c | 2 +- grub-core/normal/charset.c | 98 ++++++++++- grub-core/normal/menu_entry.c | 394 ++++++++++++++++++------------------------ grub-core/normal/term.c | 186 +++++++++++++++++--- include/grub/normal.h | 8 + include/grub/term.h | 6 +- include/grub/unicode.h | 22 ++- 8 files changed, 453 insertions(+), 267 deletions(-) diff --git a/ChangeLog b/ChangeLog index 76a9f68..96aca66 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,5 +1,9 @@ 2013-01-16 Vladimir Serbinenko + Improve bidi handling in entry editor. + +2013-01-16 Vladimir Serbinenko + * grub-core/script/lexer.c (grub_script_lexer_init): Rename getline argument to prevent name collision. diff --git a/grub-core/gfxmenu/font.c b/grub-core/gfxmenu/font.c index 3c15e19..81a689d 100644 --- a/grub-core/gfxmenu/font.c +++ b/grub-core/gfxmenu/font.c @@ -52,7 +52,7 @@ grub_font_draw_string (const char *str, grub_font_t font, return grub_errno; visual_len = grub_bidi_logical_to_visual (logical, logical_len, &visual, - 0, 0, 0); + 0, 0, 0, 0, 0, 0); grub_free (logical); if (visual_len < 0) return grub_errno; diff --git a/grub-core/normal/charset.c b/grub-core/normal/charset.c index 25593ce..bd9fbf4 100644 --- a/grub-core/normal/charset.c +++ b/grub-core/normal/charset.c @@ -513,7 +513,10 @@ bidi_line_wrap (struct grub_unicode_glyph *visual_out, struct grub_unicode_glyph *visual, grub_size_t visual_len, unsigned *levels, grub_ssize_t (*getcharwidth) (const struct grub_unicode_glyph *visual), - grub_size_t maxwidth, grub_size_t startwidth) + grub_size_t maxwidth, grub_size_t startwidth, + grub_uint32_t contchar, + struct grub_term_pos *pos, int primitive_wrap, + grub_size_t log_end) { struct grub_unicode_glyph *outptr = visual_out; unsigned line_start = 0; @@ -521,17 +524,30 @@ bidi_line_wrap (struct grub_unicode_glyph *visual_out, unsigned k; grub_ssize_t last_space = -1; grub_ssize_t last_space_width = 0; + int lines = 0; auto void revert (unsigned start, unsigned end); void revert (unsigned start, unsigned end) { struct grub_unicode_glyph t; unsigned i, tl; + int a, b; + if (pos) + { + a = pos[visual[start].orig_pos].x; + b = pos[visual[end].orig_pos].x; + } for (i = 0; i < (end - start) / 2 + 1; i++) { t = visual[start + i]; visual[start + i] = visual[end - i]; visual[end - i] = t; + + if (pos) + { + pos[visual[start + i].orig_pos].x = a + b - pos[visual[start + i].orig_pos].x; + pos[visual[end - i].orig_pos].x = a + b - pos[visual[end - i].orig_pos].x; + } tl = levels[start + i]; levels[start + i] = levels[end - i]; levels[end - i] = tl; @@ -545,11 +561,27 @@ bidi_line_wrap (struct grub_unicode_glyph *visual_out, { grub_ssize_t last_width = 0; + + if (pos && k != visual_len) + { + pos[visual[k].orig_pos].x = line_width; + pos[visual[k].orig_pos].y = lines; + pos[visual[k].orig_pos].valid = 1; + } + + if (k == visual_len && pos) + { + pos[log_end].x = line_width; + pos[log_end].y = lines; + pos[log_end].valid = 1; + } + if (getcharwidth && k != visual_len) line_width += last_width = getcharwidth (&visual[k]); if (k != visual_len && (visual[k].base == ' ' - || visual[k].base == '\t')) + || visual[k].base == '\t') + && !primitive_wrap) { last_space = k; last_space_width = line_width; @@ -561,9 +593,12 @@ bidi_line_wrap (struct grub_unicode_glyph *visual_out, unsigned min_odd_level = 0xffffffff; unsigned max_level = 0; + lines++; + if (k != visual_len && last_space > (signed) line_start) k = last_space; - else if (k != visual_len && line_start == 0 && startwidth != 0) + else if (k != visual_len && line_start == 0 && startwidth != 0 + && !primitive_wrap) { k = 0; last_space_width = startwidth; @@ -685,6 +720,12 @@ bidi_line_wrap (struct grub_unicode_glyph *visual_out, outptr += k - line_start; if (k != visual_len) { + if (contchar) + { + grub_memset (outptr, 0, sizeof (visual[0])); + outptr->base = contchar; + outptr++; + } grub_memset (outptr, 0, sizeof (visual[0])); outptr->base = '\n'; outptr++; @@ -695,6 +736,11 @@ bidi_line_wrap (struct grub_unicode_glyph *visual_out, line_start = k; line_width -= last_space_width; + if (pos && k != visual_len) + { + pos[visual[k].orig_pos].x = 0; + pos[visual[k].orig_pos].y = lines; + } } } @@ -707,7 +753,11 @@ 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, grub_size_t startwidth) + grub_size_t maxwidth, grub_size_t startwidth, + grub_uint32_t contchar, + struct grub_term_pos *pos, + int primitive_wrap, + grub_size_t log_end) { enum grub_bidi_type type = GRUB_BIDI_TYPE_L; enum override_status {OVERRIDE_NEUTRAL = 0, OVERRIDE_R, OVERRIDE_L}; @@ -832,7 +882,7 @@ grub_bidi_line_logical_to_visual (const grub_uint32_t *logical, p = grub_unicode_aglomerate_comb (lptr, logical + logical_len - lptr, &visual[visual_len]); - + visual[visual_len].orig_pos = lptr - logical; type = get_bidi_type (visual[visual_len].base); switch (type) { @@ -1066,7 +1116,8 @@ grub_bidi_line_logical_to_visual (const grub_uint32_t *logical, { grub_ssize_t ret; ret = bidi_line_wrap (visual_out, visual, visual_len, levels, - getcharwidth, maxwidth, startwidth); + getcharwidth, maxwidth, startwidth, contchar, + pos, primitive_wrap, log_end); grub_free (levels); grub_free (visual); return ret; @@ -1078,11 +1129,12 @@ 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, grub_size_t startwidth) + grub_size_t max_length, grub_size_t startwidth, + grub_uint32_t contchar, struct grub_term_pos *pos, int primitive_wrap) { 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]) + *visual_out = visual_ptr = grub_malloc (3 * sizeof (visual_ptr[0]) * logical_len); if (!visual_ptr) return -1; @@ -1096,7 +1148,11 @@ grub_bidi_logical_to_visual (const grub_uint32_t *logical, visual_ptr, getcharwidth, max_length, - startwidth); + startwidth, + contchar, + pos, + primitive_wrap, + logical_len); startwidth = 0; if (ret < 0) @@ -1188,3 +1244,27 @@ grub_unicode_get_comb_start (const grub_uint32_t *str, return str; } +const grub_uint32_t * +grub_unicode_get_comb_end (const grub_uint32_t *end, + const grub_uint32_t *cur) +{ + const grub_uint32_t *ptr; + for (ptr = cur; ptr < end; ptr++) + { + if (*ptr >= GRUB_UNICODE_VARIATION_SELECTOR_1 + && *ptr <= GRUB_UNICODE_VARIATION_SELECTOR_16) + continue; + + if (*ptr >= GRUB_UNICODE_VARIATION_SELECTOR_17 + && *ptr <= GRUB_UNICODE_VARIATION_SELECTOR_256) + continue; + + enum grub_comb_type comb_type; + comb_type = grub_unicode_get_comb_type (*ptr); + if (comb_type) + continue; + return ptr; + } + return end; +} + diff --git a/grub-core/normal/menu_entry.c b/grub-core/normal/menu_entry.c index 33b644b..7cd67f3 100644 --- a/grub-core/normal/menu_entry.c +++ b/grub-core/normal/menu_entry.c @@ -43,15 +43,12 @@ struct line int len; /* The maximum length of the line. */ int max_len; + struct grub_term_pos **pos; }; struct per_term_screen { struct grub_term_output *term; - /* The X coordinate. */ - int x; - /* The Y coordinate. */ - int y; int y_line_start; /* Number of entries. */ int num_entries; @@ -90,13 +87,18 @@ static int completion_type; /* Initialize a line. */ static int -init_line (struct line *linep) +init_line (struct screen *screen, struct line *linep) { linep->len = 0; linep->max_len = 80; linep->buf = grub_malloc ((linep->max_len + 1) * sizeof (linep->buf[0])); - if (! linep->buf) - return 0; + linep->pos = grub_zalloc (screen->nterms * sizeof (linep->pos[0])); + if (! linep->buf || !linep->pos) + { + grub_free (linep->buf); + grub_free (linep->pos); + return 0; + } return 1; } @@ -126,93 +128,16 @@ get_logical_num_lines (struct line *linep, struct per_term_screen *term_screen) } static void -advance (struct screen *screen) -{ - unsigned i; - struct grub_unicode_glyph glyph; - - screen->column += grub_unicode_aglomerate_comb (screen->lines[screen->line].buf + screen->column, - screen->lines[screen->line].len - screen->column, - &glyph); - - for (i = 0; i < screen->nterms; i++) - { - grub_ssize_t width; - width = grub_term_getcharwidth (screen->terms[i].term, &glyph); - screen->terms[i].x += width; - if (screen->terms[i].x - == grub_term_entry_width (screen->terms[i].term)) - { - screen->terms[i].x = 0; - screen->terms[i].y++; - } - if (screen->terms[i].x - > grub_term_entry_width (screen->terms[i].term)) - { - screen->terms[i].x = width; - screen->terms[i].y++; - } - } - grub_free (glyph.combining); -} - -static void advance_to (struct screen *screen, int c) { if (c > screen->lines[screen->line].len) c = screen->lines[screen->line].len; - while (screen->column < c) - advance (screen); -} - -/* Print a line. */ -static int -print_line (struct line *linep, int offset, int y, - struct per_term_screen *term_screen, int dry_run) -{ - int x; - int i; - - grub_term_gotoxy (term_screen->term, - GRUB_TERM_LEFT_BORDER_X + GRUB_TERM_MARGIN + 1, - y + GRUB_TERM_FIRST_ENTRY_Y); - - x = 0; - for (i = 0; i + offset < (int) linep->len;) - { - grub_ssize_t width; - grub_size_t delta = 0; - struct grub_unicode_glyph glyph; - - delta = grub_unicode_aglomerate_comb (linep->buf + offset + i, - linep->len - offset - i, - &glyph); - width = grub_term_getcharwidth (term_screen->term, &glyph); - grub_free (glyph.combining); - - if (x + width > grub_term_entry_width (term_screen->term) && x != 0) - break; - x += width; - i += delta; - } - - if (dry_run) - return i; - - grub_print_ucs4 (linep->buf + offset, - linep->buf + offset + i, 0, 0, term_screen->term); - - if (i + offset != linep->len) - grub_putcode ('\\', term_screen->term); - else - { - for (; - x <= (int) grub_term_entry_width (term_screen->term); - x++) - grub_putcode (' ', term_screen->term); - } - return i; + screen->column = grub_unicode_get_comb_end (screen->lines[screen->line].buf + + screen->lines[screen->line].len, + screen->lines[screen->line].buf + + c) + - screen->lines[screen->line].buf; } /* Print an empty line. */ @@ -225,7 +150,7 @@ print_empty_line (int y, struct per_term_screen *term_screen) GRUB_TERM_LEFT_BORDER_X + GRUB_TERM_MARGIN + 1, y + GRUB_TERM_FIRST_ENTRY_Y); - for (i = 0; i < grub_term_entry_width (term_screen->term) + 1; i++) + for (i = 0; i < grub_term_entry_width (term_screen->term); i++) grub_putcode (' ', term_screen->term); } @@ -261,7 +186,7 @@ print_down (int flag, struct per_term_screen *term_screen) /* Draw the lines of the screen SCREEN. */ static void update_screen (struct screen *screen, struct per_term_screen *term_screen, - int region_start, int region_column, + int region_start, int region_column __attribute__ ((unused)), int up, int down, enum update_mode mode) { int up_flag = 0; @@ -270,19 +195,26 @@ update_screen (struct screen *screen, struct per_term_screen *term_screen, int i; struct line *linep; + y = term_screen->y_line_start; + linep = screen->lines; + + for (i = 0; i < screen->line; i++, linep++) + y += get_logical_num_lines (linep, term_screen); + linep = screen->lines + screen->line; + y += grub_getstringwidth (linep->buf, linep->buf + screen->column, + term_screen->term) / grub_term_entry_width (term_screen->term); + /* Check if scrolling is necessary. */ - if (term_screen->y < 0 || term_screen->y >= term_screen->num_entries) + if (y < 0 || y >= term_screen->num_entries) { int delta; - if (term_screen->y < 0) - delta = -term_screen->y; + if (y < 0) + delta = -y; else - delta = term_screen->num_entries - 1 - term_screen->y; - term_screen->y += delta; + delta = term_screen->num_entries - 1 - y; term_screen->y_line_start += delta; region_start = 0; - region_column = 0; up = 1; down = 1; mode = ALL_LINES; @@ -293,58 +225,81 @@ update_screen (struct screen *screen, struct per_term_screen *term_screen, /* Draw lines. This code is tricky, because this must calculate logical positions. */ y = term_screen->y_line_start; - i = screen->line; - linep = screen->lines + i; - while (y > 0) - { - i--; - linep--; - y -= get_logical_num_lines (linep, term_screen); - } + i = 0; + linep = screen->lines; + while (1) + { + int add; + add = get_logical_num_lines (linep, term_screen); + if (y + add > 0) + break; + i++; + linep++; + y += add; + } if (y < 0 || i > 0) up_flag = 1; do { - int column; int off = 0; - int full_len; + struct grub_term_pos **pos; if (linep >= screen->lines + screen->num_lines) break; - full_len = grub_getstringwidth (linep->buf, linep->buf + linep->len, - term_screen->term); + pos = linep->pos + (term_screen - screen->terms); - for (column = 0; - column <= full_len - && y < term_screen->num_entries; - column += grub_term_entry_width (term_screen->term), y++) + if (!*pos) + *pos = grub_zalloc ((linep->len + 1) * sizeof (**pos)); + + if (i == region_start || linep == screen->lines + screen->line) + { + int sp; + grub_term_gotoxy (term_screen->term, GRUB_TERM_LEFT_BORDER_X + + GRUB_TERM_MARGIN + 1, (y < 0 ? 0 : y) + + GRUB_TERM_FIRST_ENTRY_Y); + grub_print_ucs4_menu (linep->buf, + linep->buf + linep->len, + GRUB_TERM_LEFT_BORDER_X + GRUB_TERM_MARGIN + + 1, + GRUB_TERM_MARGIN + + GRUB_TERM_SCROLL_WIDTH + 2, + term_screen->term, + (y < 0) ? -y : 0, + term_screen->num_entries + - ((y > 0) ? y : 0), '\\', + *pos); + sp = grub_term_entry_width (term_screen->term) + - (*pos)[linep->len].x; + if (sp > 0) + grub_print_spaces (term_screen->term, sp); + } + else if (i > region_start && mode == ALL_LINES) { - if (y < 0) - { - off += print_line (linep, off, y, term_screen, 1); - continue; - } - - if (i == region_start) - { - if (region_column >= column - && region_column - < (column - + grub_term_entry_width (term_screen->term))) - off += print_line (linep, off, y, term_screen, 0); - else if (region_column < column) - off += print_line (linep, off, y, term_screen, 0); - else - off += print_line (linep, off, y, term_screen, 1); - } - else if (i > region_start && mode == ALL_LINES) - off += print_line (linep, off, y, term_screen, 0); + int sp; + grub_term_gotoxy (term_screen->term, GRUB_TERM_LEFT_BORDER_X + + GRUB_TERM_MARGIN + 1, (y < 0 ? 0 : y) + + GRUB_TERM_FIRST_ENTRY_Y); + grub_print_ucs4_menu (linep->buf, + linep->buf + linep->len, + GRUB_TERM_LEFT_BORDER_X + + GRUB_TERM_MARGIN + 1, + GRUB_TERM_MARGIN + + GRUB_TERM_SCROLL_WIDTH + 2, + term_screen->term, + (y < 0) ? -y : 0, + term_screen->num_entries + - ((y > 0) ? y : 0), '\\', + *pos); + sp = grub_term_entry_width (term_screen->term) + - (*pos)[linep->len].x; + if (sp > 0) + grub_print_spaces (term_screen->term, sp); } - - if (y == term_screen->num_entries) + y += get_logical_num_lines (linep, term_screen); + if (y >= term_screen->num_entries) { if (off <= linep->len || i + 1 < screen->num_lines) down_flag = 1; @@ -367,10 +322,30 @@ update_screen (struct screen *screen, struct per_term_screen *term_screen, } /* Place the cursor. */ - grub_term_gotoxy (term_screen->term, - GRUB_TERM_LEFT_BORDER_X + GRUB_TERM_MARGIN + 1 - + term_screen->x, - GRUB_TERM_FIRST_ENTRY_Y + term_screen->y); + if (screen->lines[screen->line].pos[term_screen - screen->terms]) + { + const struct grub_term_pos *cpos; + for (cpos = &(screen->lines[screen->line].pos[term_screen - screen->terms])[screen->column]; + cpos >= &(screen->lines[screen->line].pos[term_screen - screen->terms])[0]; + cpos--) + if (cpos->valid) + break; + y = term_screen->y_line_start; + for (i = 0; i < screen->line; i++) + y += get_logical_num_lines (screen->lines + i, term_screen); + if (cpos >= &(screen->lines[screen->line].pos[term_screen - screen->terms])[0]) + grub_term_gotoxy (term_screen->term, + cpos->x + GRUB_TERM_LEFT_BORDER_X + + GRUB_TERM_MARGIN + 1, + cpos->y + y + + GRUB_TERM_FIRST_ENTRY_Y); + else + grub_term_gotoxy (term_screen->term, + GRUB_TERM_LEFT_BORDER_X + + GRUB_TERM_MARGIN + 1, + y + GRUB_TERM_FIRST_ENTRY_Y); + + } grub_term_refresh (term_screen->term); } @@ -424,7 +399,7 @@ insert_string (struct screen *screen, const char *s, int update) ((screen->num_lines - screen->line - 2) * sizeof (struct line))); - if (! init_line (screen->lines + screen->line + 1)) + if (! init_line (screen, screen->lines + screen->line + 1)) return 0; /* Fold the line. */ @@ -439,6 +414,11 @@ insert_string (struct screen *screen, const char *s, int update) current_linep->buf + screen->column, size * sizeof (next_linep->buf[0])); current_linep->len = screen->column; + for (i = 0; i < screen->nterms; i++) + { + grub_free (current_linep->pos[i]); + current_linep->pos[i] = 0; + } next_linep->len = size; /* Update a dirty region. */ @@ -457,12 +437,6 @@ insert_string (struct screen *screen, const char *s, int update) /* Move the cursor. */ screen->column = screen->real_column = 0; screen->line++; - for (i = 0; i < screen->nterms; i++) - { - screen->terms[i].x = 0; - screen->terms[i].y++; - screen->terms[i].y_line_start = screen->terms[i].y; - } s++; } else @@ -502,6 +476,12 @@ insert_string (struct screen *screen, const char *s, int update) grub_free (unicode_msg); for (i = 0; i < screen->nterms; i++) + { + grub_free (current_linep->pos[i]); + current_linep->pos[i] = 0; + } + + for (i = 0; i < screen->nterms; i++) orig_num[i] = get_logical_num_lines (current_linep, &screen->terms[i]); current_linep->len += size; @@ -582,7 +562,7 @@ make_screen (grub_menu_entry_t entry) goto fail; /* Initialize the first line which must be always present. */ - if (! init_line (screen->lines)) + if (! init_line (screen, screen->lines)) goto fail; insert_string (screen, (char *) entry->sourcecode, 0); @@ -593,9 +573,7 @@ make_screen (grub_menu_entry_t entry) screen->line = 0; for (i = 0; i < screen->nterms; i++) { - screen->terms[i].x = 0; - screen->terms[i].y = 0; - screen->terms[i].y_line_start = screen->terms[i].y; + screen->terms[i].y_line_start = 0; } return screen; @@ -609,21 +587,20 @@ static int forward_char (struct screen *screen, int update) { struct line *linep; - unsigned i; linep = screen->lines + screen->line; if (screen->column < linep->len) - advance (screen); + { + screen->column = grub_unicode_get_comb_end (screen->lines[screen->line].buf + + screen->lines[screen->line].len, + screen->lines[screen->line].buf + + screen->column + 1) + - screen->lines[screen->line].buf; + } else if (screen->num_lines > screen->line + 1) { screen->column = 0; screen->line++; - for (i = 0; i < screen->nterms; i++) - { - screen->terms[i].x = 0; - screen->terms[i].y++; - screen->terms[i].y_line_start = screen->terms[i].y; - } } screen->real_column = screen->column; @@ -636,8 +613,6 @@ forward_char (struct screen *screen, int update) static int backward_char (struct screen *screen, int update) { - unsigned i; - if (screen->column > 0) { struct grub_unicode_glyph glyph; @@ -653,36 +628,16 @@ backward_char (struct screen *screen, int update) grub_unicode_aglomerate_comb (screen->lines[screen->line].buf + screen->column, screen->lines[screen->line].len - screen->column, &glyph); + screen->column = grub_unicode_get_comb_start (linep->buf, + linep->buf + screen->column) + - linep->buf; - for (i = 0; i < screen->nterms; i++) - { - grub_ssize_t width; - width = grub_term_getcharwidth (screen->terms[i].term, &glyph); - screen->terms[i].x -= width; - if (screen->terms[i].x < 0) - { - screen->terms[i].x - = grub_term_entry_width (screen->terms[i].term) - 1; - screen->terms[i].y--; - } - } grub_free (glyph.combining); } else if (screen->line > 0) { - struct line *linep; - - screen->column = 0; screen->line--; - linep = screen->lines + screen->line; - - for (i = 0; i < screen->nterms; i++) - { - screen->terms[i].y_line_start -= get_logical_num_lines (linep, &screen->terms[i]); - screen->terms[i].y = screen->terms[i].y_line_start; - screen->terms[i].x = 0; - } - advance_to (screen, screen->lines[screen->line].len); + screen->column = screen->lines[screen->line].len; } screen->real_column = screen->column; @@ -696,8 +651,6 @@ backward_char (struct screen *screen, int update) static int previous_line (struct screen *screen, int update) { - unsigned i; - if (screen->line > 0) { struct line *linep; @@ -712,22 +665,10 @@ previous_line (struct screen *screen, int update) col = screen->real_column; screen->column = 0; - - for (i = 0; i < screen->nterms; i++) - { - screen->terms[i].y_line_start -= get_logical_num_lines (linep, &screen->terms[i]); - screen->terms[i].y = screen->terms[i].y_line_start; - screen->terms[i].x = 0; - } advance_to (screen, col); } else { - for (i = 0; i < screen->nterms; i++) - { - screen->terms[i].y = screen->terms[i].y_line_start; - screen->terms[i].x = 0; - } screen->column = 0; } @@ -740,8 +681,6 @@ previous_line (struct screen *screen, int update) static int next_line (struct screen *screen, int update) { - unsigned i; - if (screen->line < screen->num_lines - 1) { struct line *linep; @@ -758,12 +697,6 @@ next_line (struct screen *screen, int update) c = screen->real_column; screen->column = 0; - for (i = 0; i < screen->nterms; i++) - { - screen->terms[i].y_line_start += get_logical_num_lines (linep, &screen->terms[i]); - screen->terms[i].x = 0; - screen->terms[i].y = screen->terms[i].y_line_start; - } advance_to (screen, c); } else @@ -778,14 +711,7 @@ next_line (struct screen *screen, int update) static int beginning_of_line (struct screen *screen, int update) { - unsigned i; - screen->column = screen->real_column = 0; - for (i = 0; i < screen->nterms; i++) - { - screen->terms[i].x = 0; - screen->terms[i].y = screen->terms[i].y_line_start; - } if (update) update_screen_all (screen, screen->num_lines, 0, 0, 0, NO_LINE); @@ -826,6 +752,11 @@ delete_char (struct screen *screen, int update) * sizeof (linep->buf[0])); linep->len--; + for (i = 0; i < screen->nterms; i++) + { + grub_free (linep->pos[i]); + linep->pos[i] = 0; + } start = screen->line; column = screen->column; @@ -848,6 +779,7 @@ delete_char (struct screen *screen, int update) else if (screen->num_lines > screen->line + 1) { struct line *next_linep; + unsigned i; next_linep = linep + 1; if (! ensure_space (linep, next_linep->len)) @@ -856,6 +788,11 @@ delete_char (struct screen *screen, int update) grub_memmove (linep->buf + linep->len, next_linep->buf, next_linep->len * sizeof (linep->buf[0])); linep->len += next_linep->len; + for (i = 0; i < screen->nterms; i++) + { + grub_free (linep->pos[i]); + linep->pos[i] = 0; + } grub_free (next_linep->buf); grub_memmove (next_linep, @@ -975,24 +912,12 @@ yank (struct screen *screen, int update) static int open_line (struct screen *screen, int update) { - int saved_y[screen->nterms]; - unsigned i; - - for (i = 0; i < screen->nterms; i++) - saved_y[i] = screen->terms[i].y; - if (! insert_string (screen, "\n", 0)) return 0; if (! backward_char (screen, 0)) return 0; - for (i = 0; i < screen->nterms; i++) - { - screen->terms[i].y = saved_y[i]; - screen->terms[i].y_line_start = screen->terms[i].y; - } - if (update) update_screen_all (screen, screen->line, screen->column, 0, 1, ALL_LINES); @@ -1303,7 +1228,20 @@ grub_menu_entry_run (grub_menu_entry_t entry) screen->nterms = 0; FOR_ACTIVE_TERM_OUTPUTS(term) screen->nterms++; - screen->terms = grub_malloc (screen->nterms * sizeof (screen->terms[0])); + + for (i = 0; i < (unsigned) screen->num_lines; i++) + { + grub_free (screen->lines[i].pos); + screen->lines[i].pos = grub_zalloc (screen->nterms * sizeof (screen->lines[i].pos[0])); + if (! screen->lines[i].pos) + { + grub_print_error (); + grub_errno = GRUB_ERR_NONE; + return; + } + } + + screen->terms = grub_zalloc (screen->nterms * sizeof (screen->terms[0])); if (!screen->terms) { grub_print_error (); @@ -1314,9 +1252,7 @@ grub_menu_entry_run (grub_menu_entry_t entry) FOR_ACTIVE_TERM_OUTPUTS(term) { screen->terms[i].term = term; - screen->terms[i].x = 0; - screen->terms[i].y = 0; - screen->terms[i].y_line_start = screen->terms[i].y; + screen->terms[i].y_line_start = 0; i++; } /* Draw the screen. */ diff --git a/grub-core/normal/term.c b/grub-core/normal/term.c index dc5fdcb..43622be 100644 --- a/grub-core/normal/term.c +++ b/grub-core/normal/term.c @@ -34,6 +34,10 @@ struct term_state int backlog_fixed_tab; grub_size_t backlog_len; + int bidi_stack_depth; + grub_uint8_t bidi_stack[GRUB_BIDI_MAX_EXPLICIT_LEVEL]; + int invalid_pushes; + void *free; int num_lines; char *term_name; @@ -44,7 +48,9 @@ print_ucs4_real (const grub_uint32_t * str, const grub_uint32_t * last_position, int margin_left, int margin_right, struct grub_term_output *term, int backlog, - int dry_run, int fixed_tab); + int dry_run, int fixed_tab, unsigned skip_lines, + unsigned max_lines, + grub_uint32_t contchar, struct grub_term_pos *pos); static struct term_state *term_states = NULL; @@ -243,7 +249,8 @@ grub_puts_terminal (const char *str, struct grub_term_output *term) return; } - print_ucs4_real (unicode_str, unicode_last_position, 0, 0, term, 0, 0, 0); + print_ucs4_real (unicode_str, unicode_last_position, 0, 0, term, + 0, 0, 0, 0, -1, 0, 0); grub_free (unicode_str); } @@ -541,13 +548,25 @@ get_startwidth (struct grub_term_output *term, return ((term->getxy (term) >> 8) & 0xff) - margin_left; } +static void +fill_margin (struct grub_term_output *term, int r) +{ + int sp = (term->getwh (term) >> 8) + - (term->getxy (term) >> 8) - r; + if (sp > 0) + grub_print_spaces (term, sp); +} + static int print_ucs4_terminal (const grub_uint32_t * str, const grub_uint32_t * last_position, int margin_left, int margin_right, struct grub_term_output *term, struct term_state *state, - int dry_run, int fixed_tab) + int dry_run, int fixed_tab, unsigned skip_lines, + unsigned max_lines, + grub_uint32_t contchar, + int primitive_wrap, struct grub_term_pos *pos) { const grub_uint32_t *ptr; grub_ssize_t startwidth = dry_run ? 0 : get_startwidth (term, margin_left); @@ -556,10 +575,40 @@ print_ucs4_terminal (const grub_uint32_t * str, grub_ssize_t max_width = get_maxwidth (term, margin_left, margin_right); const grub_uint32_t *line_start = str, *last_space = str - 1; int lines = 0; + int i; + struct term_state local_state; + + if (!state) + { + grub_memset (&local_state, 0, sizeof (local_state)); + state = &local_state; + } + + for (i = 0; i < state->bidi_stack_depth; i++) + putcode_real (state->bidi_stack[i] | (GRUB_UNICODE_LRE & ~0xff), + term, fixed_tab); for (ptr = str; ptr < last_position; ptr++) { grub_ssize_t last_width = 0; + switch (*ptr) + { + case GRUB_UNICODE_LRE: + case GRUB_UNICODE_RLE: + case GRUB_UNICODE_LRO: + case GRUB_UNICODE_RLO: + if (state->bidi_stack_depth >= (int) ARRAY_SIZE (state->bidi_stack)) + state->invalid_pushes++; + else + state->bidi_stack[state->bidi_stack_depth++] = *ptr; + break; + case GRUB_UNICODE_PDF: + if (state->invalid_pushes) + state->invalid_pushes--; + else if (state->bidi_stack_depth) + state->bidi_stack_depth--; + break; + } if (grub_unicode_get_comb_type (*ptr) == GRUB_UNICODE_COMB_NONE) { struct grub_unicode_glyph c = { @@ -569,10 +618,16 @@ print_ucs4_terminal (const grub_uint32_t * str, .combining = 0 }; c.base = *ptr; + if (pos) + { + pos[ptr - str].x = line_width; + pos[ptr - str].y = lines; + pos[ptr - str].valid = 1; + } line_width += last_width = grub_term_getcharwidth (term, &c); } - if (*ptr == ' ') + if (*ptr == ' ' && !primitive_wrap) { lastspacewidth = line_width; last_space = ptr; @@ -581,6 +636,13 @@ print_ucs4_terminal (const grub_uint32_t * str, if (line_width > max_width || *ptr == '\n') { const grub_uint32_t *ptr2; + int wasn = (*ptr == '\n'); + + if (wasn) + { + state->bidi_stack_depth = 0; + state->invalid_pushes = 0; + } if (line_width > max_width && last_space > line_start) ptr = last_space; @@ -595,7 +657,7 @@ print_ucs4_terminal (const grub_uint32_t * str, lines++; - if (!dry_run) + if (!skip_lines && !dry_run) { for (ptr2 = line_start; ptr2 < ptr; ptr2++) { @@ -608,9 +670,12 @@ print_ucs4_terminal (const grub_uint32_t * str, putcode_real (*ptr2, term, fixed_tab); } - grub_print_spaces (term, margin_right); + if (!wasn && contchar) + putcode_real (contchar, term, fixed_tab); + fill_margin (term, contchar ? margin_right : 1); + grub_putcode ('\n', term); - if (state && ++state->num_lines + if (state != &local_state && ++state->num_lines >= (grub_ssize_t) grub_term_height (term) - 2) { state->backlog_ucs4 = (ptr == last_space || *ptr == '\n') @@ -622,17 +687,42 @@ print_ucs4_terminal (const grub_uint32_t * str, } line_width -= lastspacewidth; - if (!dry_run) - grub_print_spaces (term, margin_left); if (ptr == last_space || *ptr == '\n') ptr++; line_start = ptr; + + if (skip_lines) + skip_lines--; + else if (max_lines != (unsigned) -1) + { + max_lines--; + if (!max_lines) + break; + } + if (!skip_lines && !dry_run) + { + if (!contchar) + grub_print_spaces (term, margin_left); + else + grub_term_gotoxy (term, margin_left, + grub_term_getxy (term) & 0xff); + for (i = 0; i < state->bidi_stack_depth; i++) + putcode_real (state->bidi_stack[i] | (GRUB_UNICODE_LRE & ~0xff), + term, fixed_tab); + } } } + if (pos) + { + pos[ptr - str].x = line_width; + pos[ptr - str].y = lines; + pos[ptr - str].valid = 1; + } + if (line_start < last_position) lines++; - if (!dry_run) + if (!dry_run && !skip_lines && max_lines) { const grub_uint32_t *ptr2; for (ptr2 = line_start; ptr2 < last_position; ptr2++) @@ -717,7 +807,8 @@ print_backlog (struct grub_term_output *term, ret = print_ucs4_terminal (state->backlog_ucs4, state->backlog_ucs4 + state->backlog_len, margin_left, margin_right, term, state, 0, - state->backlog_fixed_tab); + state->backlog_fixed_tab, 0, -1, 0, 0, + 0); if (!ret) { grub_free (state->free); @@ -753,17 +844,28 @@ print_ucs4_real (const grub_uint32_t * str, const grub_uint32_t * last_position, int margin_left, int margin_right, struct grub_term_output *term, int backlog, - int dry_run, int fixed_tab) + int dry_run, int fixed_tab, unsigned skip_lines, + unsigned max_lines, + grub_uint32_t contchar, struct grub_term_pos *pos) { struct term_state *state = NULL; if (!dry_run) { + int xy; if (backlog) state = find_term_state (term); - if (((term->getxy (term) >> 8) & 0xff) < margin_left) - grub_print_spaces (term, margin_left - ((term->getxy (term) >> 8) & 0xff)); + xy = term->getxy (term); + + if (((xy >> 8) & 0xff) < margin_left) + { + if (!contchar) + grub_print_spaces (term, margin_left - ((xy >> 8) & 0xff)); + else + grub_term_gotoxy (term, margin_left, + xy & 0xff); + } } if ((term->flags & GRUB_TERM_CODE_TYPE_MASK) @@ -773,7 +875,10 @@ print_ucs4_real (const grub_uint32_t * str, { grub_ssize_t visual_len; struct grub_unicode_glyph *visual; + grub_ssize_t visual_len_show; + struct grub_unicode_glyph *visual_show; int ret; + struct grub_unicode_glyph *vptr; auto grub_ssize_t getcharwidth (const struct grub_unicode_glyph *c); grub_ssize_t getcharwidth (const struct grub_unicode_glyph *c) @@ -787,27 +892,45 @@ print_ucs4_real (const grub_uint32_t * str, margin_left, margin_right), get_startwidth (term, - margin_left)); + margin_left), + contchar, pos, !!contchar); if (visual_len < 0) { grub_print_error (); return 0; } + visual_show = visual; + for (; skip_lines && visual_show < visual + visual_len; visual_show++) + if (visual_show->base == '\n') + skip_lines--; + if (max_lines != (unsigned) -1) + { + for (vptr = visual_show; + max_lines && vptr < visual + visual_len; vptr++) + if (visual_show->base == '\n') + max_lines--; + + visual_len_show = vptr - visual_show; + } + else + visual_len_show = visual + visual_len - visual_show; + if (dry_run) { - struct grub_unicode_glyph *vptr; ret = 0; - for (vptr = visual; vptr < visual + visual_len; vptr++) + for (vptr = visual_show; vptr < visual_show + visual_len_show; vptr++) if (vptr->base == '\n') ret++; - if (visual_len && visual[visual_len - 1].base != '\n') + if (visual_len_show && visual[visual_len_show - 1].base != '\n') ret++; grub_free (visual); } else { - ret = put_glyphs_terminal (visual, visual_len, margin_left, - margin_right, term, state, fixed_tab); + ret = put_glyphs_terminal (visual_show, visual_len_show, margin_left, + contchar ? margin_right : 1, + term, state, fixed_tab); + if (!ret) grub_free (visual); else @@ -816,7 +939,22 @@ print_ucs4_real (const grub_uint32_t * str, return ret; } return print_ucs4_terminal (str, last_position, margin_left, margin_right, - term, state, dry_run, fixed_tab); + term, state, dry_run, fixed_tab, skip_lines, + max_lines, contchar, !!contchar, pos); +} + +void +grub_print_ucs4_menu (const grub_uint32_t * str, + const grub_uint32_t * last_position, + int margin_left, int margin_right, + struct grub_term_output *term, + int skip_lines, int max_lines, + grub_uint32_t contchar, + struct grub_term_pos *pos) +{ + print_ucs4_real (str, last_position, margin_left, margin_right, + term, 0, 0, 1, skip_lines, max_lines, + contchar, pos); } void @@ -826,7 +964,7 @@ grub_print_ucs4 (const grub_uint32_t * str, struct grub_term_output *term) { print_ucs4_real (str, last_position, margin_left, margin_right, - term, 0, 0, 1); + term, 0, 0, 1, 0, -1, 0, 0); } int @@ -836,7 +974,7 @@ grub_ucs4_count_lines (const grub_uint32_t * str, struct grub_term_output *term) { return print_ucs4_real (str, last_position, margin_left, margin_right, - term, 0, 1, 1); + term, 0, 1, 1, 0, -1, 0, 0); } void @@ -886,7 +1024,7 @@ grub_xputs_normal (const char *str) { int cur; cur = print_ucs4_real (unicode_str, unicode_last_position, 0, 0, - term, grub_more, 0, 0); + term, grub_more, 0, 0, 0, -1, 0, 0); if (cur) backlog = 1; } diff --git a/include/grub/normal.h b/include/grub/normal.h index e88cd26..416faa4 100644 --- a/include/grub/normal.h +++ b/include/grub/normal.h @@ -77,6 +77,14 @@ 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); + +void +grub_print_ucs4_menu (const grub_uint32_t * str, + const grub_uint32_t * last_position, + int margin_left, int margin_right, + struct grub_term_output *term, + int skip_lines, int max_lines, grub_uint32_t contchar, + struct grub_term_pos *pos); int grub_ucs4_count_lines (const grub_uint32_t * str, const grub_uint32_t * last_position, diff --git a/include/grub/term.h b/include/grub/term.h index bf4dcb4..39c3d5a 100644 --- a/include/grub/term.h +++ b/include/grub/term.h @@ -104,7 +104,7 @@ grub_term_color_state; #define GRUB_TERM_CODE_TYPE_CP437 (1 << GRUB_TERM_CODE_TYPE_SHIFT) /* UTF-8 stream in logical order. Usually used for terminals which just forward the stream to another computer. */ -#define GRUB_TERM_CODE_TYPE_UTF8_LOGICAL (2 << GRUB_TERM_CODE_TYPE_SHIFT) +#define GRUB_TERM_CODE_TYPE_UTF8_LOGICAL (2 << GRUB_TERM_CODE_TYPE_SHIFT) /* UTF-8 in visual order. Like UTF-8 logical but for buggy endpoints. */ #define GRUB_TERM_CODE_TYPE_UTF8_VISUAL (3 << GRUB_TERM_CODE_TYPE_SHIFT) /* Glyph description in visual order. */ @@ -344,7 +344,7 @@ static inline unsigned grub_term_height (struct grub_term_output *term) static inline unsigned grub_term_border_width (struct grub_term_output *term) { - return grub_term_width (term) - GRUB_TERM_MARGIN * 3 - GRUB_TERM_SCROLL_WIDTH; + return grub_term_width (term) - GRUB_TERM_MARGIN * 2; } /* The max column number of an entry. The last "-1" is for a @@ -352,7 +352,7 @@ grub_term_border_width (struct grub_term_output *term) static inline int grub_term_entry_width (struct grub_term_output *term) { - return grub_term_border_width (term) - 2 - GRUB_TERM_MARGIN * 2 - 1; + return grub_term_border_width (term) - GRUB_TERM_MARGIN * 2 - 1; } static inline grub_uint16_t diff --git a/include/grub/unicode.h b/include/grub/unicode.h index 763e25e..eb5051a 100644 --- a/include/grub/unicode.h +++ b/include/grub/unicode.h @@ -139,6 +139,7 @@ struct grub_unicode_glyph grub_uint16_t variant:9; grub_uint8_t attributes:5; grub_size_t ncomb; + grub_size_t orig_pos; struct grub_unicode_combining { grub_uint32_t code; enum grub_comb_type type; @@ -186,6 +187,13 @@ enum GRUB_UNICODE_THAANA_SUKUN = 0x07b0, GRUB_UNICODE_ZWNJ = 0x200c, GRUB_UNICODE_ZWJ = 0x200d, + GRUB_UNICODE_LRM = 0x200e, + GRUB_UNICODE_RLM = 0x200f, + GRUB_UNICODE_LRE = 0x202a, + GRUB_UNICODE_RLE = 0x202b, + GRUB_UNICODE_PDF = 0x202c, + GRUB_UNICODE_LRO = 0x202d, + GRUB_UNICODE_RLO = 0x202e, GRUB_UNICODE_LEFTARROW = 0x2190, GRUB_UNICODE_UPARROW = 0x2191, GRUB_UNICODE_RIGHTARROW = 0x2192, @@ -222,13 +230,21 @@ extern struct grub_unicode_bidi_pair grub_unicode_bidi_pairs[]; /* Unicode mandates an arbitrary limit. */ #define GRUB_BIDI_MAX_EXPLICIT_LEVEL 61 +struct grub_term_pos +{ + unsigned valid:1; + unsigned x:15, y:16; +}; + 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_width, - grub_size_t start_width); + grub_size_t start_width, grub_uint32_t codechar, + struct grub_term_pos *pos, + int primitive_wrap); enum grub_comb_type grub_unicode_get_comb_type (grub_uint32_t c); @@ -275,4 +291,8 @@ grub_unicode_mirror_code (grub_uint32_t in); grub_uint32_t grub_unicode_shape_code (grub_uint32_t in, grub_uint8_t attr); +const grub_uint32_t * +grub_unicode_get_comb_end (const grub_uint32_t *end, + const grub_uint32_t *cur); + #endif -- 1.8.2.1