Restore and enhance pager functionality

This commit is contained in:
Vladimir 'phcoder' Serbinenko 2010-03-23 17:54:31 +01:00
parent 6fcebedeed
commit 3c69fb81e1
3 changed files with 316 additions and 151 deletions

View file

@ -113,7 +113,6 @@ void read_terminal_list (void);
void grub_set_more (int onoff); void grub_set_more (int onoff);
int grub_normal_get_char_counter (void); int grub_normal_get_char_counter (void);
void grub_install_newline_hook (void);
void grub_xputs_normal (const char *str); void grub_xputs_normal (const char *str);

View file

@ -661,7 +661,6 @@ GRUB_MOD_INIT(normal)
grub_set_history (GRUB_DEFAULT_HISTORY_SIZE); grub_set_history (GRUB_DEFAULT_HISTORY_SIZE);
grub_install_newline_hook ();
grub_register_variable_hook ("pager", 0, grub_env_write_pager); grub_register_variable_hook ("pager", 0, grub_env_write_pager);
/* Register a command "normal" for the rescue mode. */ /* Register a command "normal" for the rescue mode. */

View file

@ -28,11 +28,17 @@
struct term_state struct term_state
{ {
struct term_state *next; struct term_state *next;
struct grub_unicode_glyph *backlog; const struct grub_unicode_glyph *backlog_glyphs;
int numlines; const grub_uint32_t *backlog_ucs4;
grub_size_t backlog_len;
void *free;
int num_lines;
char *term_name; char *term_name;
}; };
static struct term_state *term_states = NULL;
/* The amount of lines counted by the pager. */ /* The amount of lines counted by the pager. */
static unsigned grub_more_lines; static unsigned grub_more_lines;
@ -48,40 +54,52 @@ grub_normal_get_char_counter (void)
} }
static void static void
process_newline (void) print_more (void)
{ {
struct grub_term_output *cur; char key;
unsigned height = -1; grub_uint16_t *pos;
grub_term_output_t term;
grub_uint32_t *unicode_str, *unicode_last_position;
FOR_ACTIVE_TERM_OUTPUTS(cur) pos = grub_term_save_pos ();
if (grub_term_height (cur) < height)
height = grub_term_height (cur);
grub_more_lines++;
if (grub_more && grub_more_lines >= height - 1) grub_utf8_to_ucs4_alloc ("--MORE--", &unicode_str,
&unicode_last_position);
if (!unicode_str)
{ {
char key; grub_errno = GRUB_ERR_NONE;
grub_uint16_t *pos; return;
pos = grub_term_save_pos ();
grub_setcolorstate (GRUB_TERM_COLOR_HIGHLIGHT);
grub_printf ("--MORE--");
grub_setcolorstate (GRUB_TERM_COLOR_STANDARD);
key = grub_getkey ();
/* Remove the message. */
grub_term_restore_pos (pos);
grub_printf (" ");
grub_term_restore_pos (pos);
/* Scroll one lines or an entire page, depending on the key. */
if (key == '\r' || key =='\n')
grub_more_lines = height - 2;
else
grub_more_lines = 0;
} }
grub_setcolorstate (GRUB_TERM_COLOR_HIGHLIGHT);
FOR_ACTIVE_TERM_OUTPUTS(term)
{
grub_print_ucs4 (unicode_str, unicode_last_position, 0, 0, term);
}
grub_setcolorstate (GRUB_TERM_COLOR_STANDARD);
grub_free (unicode_str);
key = grub_getkey ();
/* Remove the message. */
grub_term_restore_pos (pos);
FOR_ACTIVE_TERM_OUTPUTS(term)
grub_print_spaces (term, 8);
grub_term_restore_pos (pos);
/* Scroll one lines or an entire page, depending on the key. */
{
static struct term_state *state;
for (state = term_states; state; state = state->next)
if (key == '\r' || key =='\n')
state->num_lines = 0;
else
state->num_lines -= 2;
}
} }
void void
@ -95,12 +113,6 @@ grub_set_more (int onoff)
grub_more_lines = 0; grub_more_lines = 0;
} }
void
grub_install_newline_hook (void)
{
grub_newline_hook = process_newline;
}
static grub_uint32_t static grub_uint32_t
map_code (grub_uint32_t in, struct grub_term_output *term) map_code (grub_uint32_t in, struct grub_term_output *term)
{ {
@ -431,33 +443,230 @@ grub_putcode (grub_uint32_t code, struct grub_term_output *term)
putcode_real (code, term); putcode_real (code, term);
} }
void static grub_ssize_t
grub_print_ucs4 (const grub_uint32_t * str, get_maxwidth (struct grub_term_output *term,
const grub_uint32_t * last_position, int margin_left, int margin_right)
int margin_left, int margin_right,
struct grub_term_output *term)
{ {
grub_ssize_t max_width; struct grub_unicode_glyph space_glyph = {
grub_ssize_t startwidth; .base = ' ',
.variant = 0,
.attributes = 0,
.ncomb = 0,
.combining = 0
};
return (grub_term_width (term)
- grub_term_getcharwidth (term, &space_glyph)
* (margin_left + margin_right) - 1);
}
static grub_ssize_t
get_startwidth (struct grub_term_output *term,
int margin_left)
{
return ((term->getxy () >> 8) & 0xff) - margin_left;
}
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)
{
const grub_uint32_t *ptr;
grub_ssize_t startwidth = get_startwidth (term, margin_left);
grub_ssize_t line_width = startwidth;
grub_ssize_t lastspacewidth = 0;
grub_ssize_t max_width = get_maxwidth (term, margin_left, margin_right);
const grub_uint32_t *line_start = str, *last_space = str - 1;
for (ptr = str; ptr < last_position; ptr++)
{
grub_ssize_t last_width = 0;
if (grub_unicode_get_comb_type (*ptr) == GRUB_UNICODE_COMB_NONE)
{
struct grub_unicode_glyph c = {
.variant = 0,
.attributes = 0,
.ncomb = 0,
.combining = 0
};
c.base = *ptr;
line_width += last_width = grub_term_getcharwidth (term, &c);
}
if (*ptr == ' ')
{
lastspacewidth = line_width;
last_space = ptr;
}
if (line_width > max_width || *ptr == '\n')
{
const grub_uint32_t *ptr2;
if (line_width > max_width && last_space > line_start)
ptr = last_space;
else if (line_width > max_width
&& line_start == str && startwidth != 0)
{
ptr = str;
lastspacewidth = startwidth;
}
else
lastspacewidth = line_width - last_width;
for (ptr2 = line_start; ptr2 < ptr; ptr2++)
{
/* Skip combining characters on non-UTF8 terminals. */
if ((term->flags & GRUB_TERM_CODE_TYPE_MASK)
!= GRUB_TERM_CODE_TYPE_UTF8_LOGICAL
&& grub_unicode_get_comb_type (*ptr2)
!= GRUB_UNICODE_COMB_NONE)
continue;
putcode_real (*ptr2, term);
}
grub_print_spaces (term, margin_right);
grub_putcode ('\n', term);
if (state && state->num_lines++
>= (grub_ssize_t) grub_term_height (term))
{
state->backlog_ucs4 = (ptr == last_space || *ptr == '\n')
? ptr + 1 : ptr;
state->backlog_len = last_position - state->backlog_ucs4;
return 1;
}
line_width -= lastspacewidth;
grub_print_spaces (term, margin_left);
if (ptr == last_space || *ptr == '\n')
ptr++;
line_start = ptr;
}
}
{ {
struct grub_unicode_glyph space_glyph = { const grub_uint32_t *ptr2;
.base = ' ', for (ptr2 = line_start; ptr2 < last_position; ptr2++)
.variant = 0, {
.attributes = 0, /* Skip combining characters on non-UTF8 terminals. */
.ncomb = 0, if ((term->flags & GRUB_TERM_CODE_TYPE_MASK)
.combining = 0 != GRUB_TERM_CODE_TYPE_UTF8_LOGICAL
}; && grub_unicode_get_comb_type (*ptr2)
max_width = grub_term_width (term) != GRUB_UNICODE_COMB_NONE)
- grub_term_getcharwidth (term, &space_glyph) continue;
* (margin_left + margin_right) - 1; putcode_real (*ptr2, term);
}
} }
return 0;
}
static struct term_state *
find_term_state (struct grub_term_output *term)
{
struct term_state *state;
for (state = term_states; state; state = state->next)
if (grub_strcmp (state->term_name, term->name) == 0)
return state;
state = grub_zalloc (sizeof (*state));
if (!state)
{
grub_errno = GRUB_ERR_NONE;
return NULL;
}
state->term_name = grub_strdup (term->name);
state->next = term_states;
term_states = state;
return state;
}
static int
put_glyphs_terminal (const struct grub_unicode_glyph *visual,
grub_ssize_t visual_len,
int margin_left, int margin_right,
struct grub_term_output *term,
struct term_state *state)
{
const struct grub_unicode_glyph *visual_ptr;
for (visual_ptr = visual; visual_ptr < visual + visual_len; visual_ptr++)
{
if (visual_ptr->base == '\n')
grub_print_spaces (term, margin_right);
putglyph (visual_ptr, term);
if (state && state->num_lines++ >= (grub_ssize_t) grub_term_height (term))
{
state->backlog_glyphs = visual_ptr + 1;
state->backlog_len = visual_len - (visual - visual_ptr) - 1;
return 1;
}
if (visual_ptr->base == '\n')
grub_print_spaces (term, margin_left);
grub_free (visual_ptr->combining);
}
return 0;
}
static int
print_backlog (struct grub_term_output *term,
int margin_left, int margin_right)
{
struct term_state *state = find_term_state (term);
if (!state)
return 0;
if (state->backlog_ucs4)
{
int ret;
ret = print_ucs4_terminal (state->backlog_ucs4,
state->backlog_ucs4 + state->backlog_len,
margin_left, margin_right, term, state);
if (!ret)
{
grub_free (state->free);
state->free = NULL;
state->backlog_len = 0;
}
return ret;
}
if (state->backlog_glyphs)
{
int ret;
ret = put_glyphs_terminal (state->backlog_glyphs,
state->backlog_len,
margin_left, margin_right, term, state);
if (!ret)
{
grub_free (state->free);
state->free = NULL;
state->backlog_len = 0;
}
return ret;
}
return 0;
}
static int
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)
{
struct term_state *state = NULL;
if (backlog)
state = find_term_state (term);
if (((term->getxy () >> 8) & 0xff) < margin_left) if (((term->getxy () >> 8) & 0xff) < margin_left)
grub_print_spaces (term, margin_left - ((term->getxy () >> 8) & 0xff)); grub_print_spaces (term, margin_left - ((term->getxy () >> 8) & 0xff));
startwidth = ((term->getxy () >> 8) & 0xff) - margin_left;
if ((term->flags & GRUB_TERM_CODE_TYPE_MASK) if ((term->flags & GRUB_TERM_CODE_TYPE_MASK)
== GRUB_TERM_CODE_TYPE_VISUAL_GLYPHS == GRUB_TERM_CODE_TYPE_VISUAL_GLYPHS
|| (term->flags & GRUB_TERM_CODE_TYPE_MASK) || (term->flags & GRUB_TERM_CODE_TYPE_MASK)
@ -465,7 +674,7 @@ grub_print_ucs4 (const grub_uint32_t * str,
{ {
grub_ssize_t visual_len; grub_ssize_t visual_len;
struct grub_unicode_glyph *visual; struct grub_unicode_glyph *visual;
struct grub_unicode_glyph *visual_ptr; int ret;
auto grub_ssize_t getcharwidth (const struct grub_unicode_glyph *c); auto grub_ssize_t getcharwidth (const struct grub_unicode_glyph *c);
grub_ssize_t getcharwidth (const struct grub_unicode_glyph *c) grub_ssize_t getcharwidth (const struct grub_unicode_glyph *c)
@ -475,115 +684,73 @@ grub_print_ucs4 (const grub_uint32_t * str,
visual_len = grub_bidi_logical_to_visual (str, last_position - str, visual_len = grub_bidi_logical_to_visual (str, last_position - str,
&visual, getcharwidth, &visual, getcharwidth,
max_width, startwidth); get_maxwidth (term,
margin_left,
margin_right),
get_startwidth (term,
margin_left));
if (visual_len < 0) if (visual_len < 0)
{ {
grub_print_error (); grub_print_error ();
return; return 0;
} }
for (visual_ptr = visual; visual_ptr < visual + visual_len; visual_ptr++) ret = put_glyphs_terminal (visual, visual_len, margin_left, margin_right,
{ term, state);
if (visual_ptr->base == '\n') if (!ret)
grub_print_spaces (term, margin_right); grub_free (visual);
putglyph (visual_ptr, term); else
if (visual_ptr->base == '\n') state->free = visual;
grub_print_spaces (term, margin_left); return ret;
grub_free (visual_ptr->combining);
}
grub_free (visual);
return;
} }
return print_ucs4_terminal (str, last_position, margin_left, margin_right,
{ term, state);
const grub_uint32_t *ptr;
grub_ssize_t line_width = startwidth;
grub_ssize_t lastspacewidth = 0;
const grub_uint32_t *line_start = str, *last_space = str - 1;
for (ptr = str; ptr < last_position; ptr++)
{
grub_ssize_t last_width = 0;
if (grub_unicode_get_comb_type (*ptr) == GRUB_UNICODE_COMB_NONE)
{
struct grub_unicode_glyph c = {
.variant = 0,
.attributes = 0,
.ncomb = 0,
.combining = 0
};
c.base = *ptr;
line_width += last_width = grub_term_getcharwidth (term, &c);
}
if (*ptr == ' ')
{
lastspacewidth = line_width;
last_space = ptr;
}
if (line_width > max_width || *ptr == '\n')
{
const grub_uint32_t *ptr2;
if (line_width > max_width && last_space > line_start)
ptr = last_space;
else if (line_width > max_width
&& line_start == str && startwidth != 0)
{
ptr = str;
lastspacewidth = startwidth;
}
else
lastspacewidth = line_width - last_width;
for (ptr2 = line_start; ptr2 < ptr; ptr2++)
{
/* Skip combining characters on non-UTF8 terminals. */
if ((term->flags & GRUB_TERM_CODE_TYPE_MASK)
!= GRUB_TERM_CODE_TYPE_UTF8_LOGICAL
&& grub_unicode_get_comb_type (*ptr2)
!= GRUB_UNICODE_COMB_NONE)
continue;
putcode_real (*ptr2, term);
}
grub_print_spaces (term, margin_right);
grub_putcode ('\n', term);
line_width -= lastspacewidth;
grub_print_spaces (term, margin_left);
if (ptr == last_space || *ptr == '\n')
ptr++;
line_start = ptr;
}
}
{
const grub_uint32_t *ptr2;
for (ptr2 = line_start; ptr2 < last_position; ptr2++)
{
/* Skip combining characters on non-UTF8 terminals. */
if ((term->flags & GRUB_TERM_CODE_TYPE_MASK)
!= GRUB_TERM_CODE_TYPE_UTF8_LOGICAL
&& grub_unicode_get_comb_type (*ptr2)
!= GRUB_UNICODE_COMB_NONE)
continue;
putcode_real (*ptr2, term);
}
}
}
} }
void
grub_print_ucs4 (const grub_uint32_t * str,
const grub_uint32_t * last_position,
int margin_left, int margin_right,
struct grub_term_output *term)
{
print_ucs4_real (str, last_position, margin_left, margin_right,
term, 0);
}
void void
grub_xputs_normal (const char *str) grub_xputs_normal (const char *str)
{ {
grub_term_output_t term; grub_term_output_t term;
grub_uint32_t *unicode_str, *unicode_last_position; grub_uint32_t *unicode_str, *unicode_last_position;
int backlog = 0;
grub_utf8_to_ucs4_alloc (str, &unicode_str, grub_utf8_to_ucs4_alloc (str, &unicode_str,
&unicode_last_position); &unicode_last_position);
if (!unicode_str)
{
grub_errno = GRUB_ERR_NONE;
return;
}
FOR_ACTIVE_TERM_OUTPUTS(term) FOR_ACTIVE_TERM_OUTPUTS(term)
{ {
grub_print_ucs4 (unicode_str, unicode_last_position, 0, 0, term); int cur;
cur = print_ucs4_real (unicode_str, unicode_last_position, 0, 0,
term, 1);
if (cur)
backlog = 1;
} }
while (backlog)
{
print_more ();
backlog = 0;
FOR_ACTIVE_TERM_OUTPUTS(term)
{
int cur;
cur = print_backlog (term, 0, 0);
if (cur)
backlog = 1;
}
}
grub_free (unicode_str); grub_free (unicode_str);
} }