2
* GRUB -- GRand Unified Bootloader
3
* Copyright (C) 2002,2003,2005,2007,2008,2009 Free Software Foundation, Inc.
5
* GRUB is free software: you can redistribute it and/or modify
6
* it under the terms of the GNU General Public License as published by
7
* the Free Software Foundation, either version 3 of the License, or
8
* (at your option) any later version.
10
* GRUB is distributed in the hope that it will be useful,
11
* but WITHOUT ANY WARRANTY; without even the implied warranty of
12
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13
* GNU General Public License for more details.
15
* You should have received a copy of the GNU General Public License
16
* along with GRUB. If not, see <http://www.gnu.org/licenses/>.
19
#include <grub/term.h>
20
#include <grub/misc.h>
22
#include <grub/file.h>
25
#include <grub/normal.h>
26
#include <grub/charset.h>
30
struct term_state *next;
31
const struct grub_unicode_glyph *backlog_glyphs;
32
const grub_uint32_t *backlog_ucs4;
33
grub_size_t backlog_len;
40
static struct term_state *term_states = NULL;
42
/* If the more pager is active. */
46
putcode_real (grub_uint32_t code, struct grub_term_output *term);
49
grub_normal_reset_more (void)
51
static struct term_state *state;
52
for (state = term_states; state; state = state->next)
61
grub_term_output_t term;
62
grub_uint32_t *unicode_str, *unicode_last_position;
64
pos = grub_term_save_pos ();
66
grub_utf8_to_ucs4_alloc ("--MORE--", &unicode_str,
67
&unicode_last_position);
71
grub_errno = GRUB_ERR_NONE;
75
grub_setcolorstate (GRUB_TERM_COLOR_HIGHLIGHT);
77
FOR_ACTIVE_TERM_OUTPUTS(term)
79
grub_print_ucs4 (unicode_str, unicode_last_position, 0, 0, term);
81
grub_setcolorstate (GRUB_TERM_COLOR_NORMAL);
83
grub_free (unicode_str);
87
/* Remove the message. */
88
grub_term_restore_pos (pos);
89
FOR_ACTIVE_TERM_OUTPUTS(term)
90
grub_print_spaces (term, 8);
91
grub_term_restore_pos (pos);
94
/* Scroll one line or an entire page, depending on the key. */
96
if (key == '\r' || key =='\n')
98
static struct term_state *state;
99
for (state = term_states; state; state = state->next)
103
grub_normal_reset_more ();
107
grub_set_more (int onoff)
113
grub_normal_reset_more ();
118
GRUB_CP437_UPARROW = 0x18,
119
GRUB_CP437_DOWNARROW = 0x19,
120
GRUB_CP437_RIGHTARROW = 0x1a,
121
GRUB_CP437_LEFTARROW = 0x1b,
122
GRUB_CP437_VLINE = 0xb3,
123
GRUB_CP437_CORNER_UR = 0xbf,
124
GRUB_CP437_CORNER_LL = 0xc0,
125
GRUB_CP437_HLINE = 0xc4,
126
GRUB_CP437_CORNER_LR = 0xd9,
127
GRUB_CP437_CORNER_UL = 0xda,
131
map_code (grub_uint32_t in, struct grub_term_output *term)
136
switch (term->flags & GRUB_TERM_CODE_TYPE_MASK)
138
case GRUB_TERM_CODE_TYPE_CP437:
141
case GRUB_UNICODE_LEFTARROW:
142
return GRUB_CP437_LEFTARROW;
143
case GRUB_UNICODE_UPARROW:
144
return GRUB_CP437_UPARROW;
145
case GRUB_UNICODE_RIGHTARROW:
146
return GRUB_CP437_RIGHTARROW;
147
case GRUB_UNICODE_DOWNARROW:
148
return GRUB_CP437_DOWNARROW;
149
case GRUB_UNICODE_HLINE:
150
return GRUB_CP437_HLINE;
151
case GRUB_UNICODE_VLINE:
152
return GRUB_CP437_VLINE;
153
case GRUB_UNICODE_CORNER_UL:
154
return GRUB_CP437_CORNER_UL;
155
case GRUB_UNICODE_CORNER_UR:
156
return GRUB_CP437_CORNER_UR;
157
case GRUB_UNICODE_CORNER_LL:
158
return GRUB_CP437_CORNER_LL;
159
case GRUB_UNICODE_CORNER_LR:
160
return GRUB_CP437_CORNER_LR;
163
case GRUB_TERM_CODE_TYPE_ASCII:
164
/* Better than nothing. */
167
case GRUB_UNICODE_LEFTARROW:
170
case GRUB_UNICODE_UPARROW:
173
case GRUB_UNICODE_RIGHTARROW:
176
case GRUB_UNICODE_DOWNARROW:
179
case GRUB_UNICODE_HLINE:
182
case GRUB_UNICODE_VLINE:
185
case GRUB_UNICODE_CORNER_UL:
186
case GRUB_UNICODE_CORNER_UR:
187
case GRUB_UNICODE_CORNER_LL:
188
case GRUB_UNICODE_CORNER_LR:
198
grub_puts_terminal (const char *str, struct grub_term_output *term)
200
grub_uint32_t *unicode_str, *unicode_last_position;
202
grub_utf8_to_ucs4_alloc (str, &unicode_str,
203
&unicode_last_position);
209
struct grub_unicode_glyph c =
215
.estimated_width = 1,
219
FOR_ACTIVE_TERM_OUTPUTS(term)
221
(term->putchar) (term, &c);
226
FOR_ACTIVE_TERM_OUTPUTS(term)
228
(term->putchar) (term, &c);
235
grub_print_ucs4 (unicode_str, unicode_last_position, 0, 0, term);
236
grub_free (unicode_str);
240
grub_term_save_pos (void)
242
struct grub_term_output *cur;
244
grub_uint16_t *ret, *ptr;
246
FOR_ACTIVE_TERM_OUTPUTS(cur)
249
ret = grub_malloc (cnt * sizeof (ret[0]));
254
FOR_ACTIVE_TERM_OUTPUTS(cur)
255
*ptr++ = grub_term_getxy (cur);
261
grub_term_restore_pos (grub_uint16_t *pos)
263
struct grub_term_output *cur;
264
grub_uint16_t *ptr = pos;
269
FOR_ACTIVE_TERM_OUTPUTS(cur)
271
grub_term_gotoxy (cur, (*ptr & 0xff00) >> 8, *ptr & 0xff);
277
grub_terminal_autoload_free (void)
279
struct grub_term_autoload *cur, *next;
281
for (i = 0; i < 2; i++)
282
for (cur = i ? grub_term_input_autoload : grub_term_output_autoload;
286
grub_free (cur->name);
287
grub_free (cur->modname);
290
grub_term_input_autoload = NULL;
291
grub_term_output_autoload = NULL;
294
/* Read the file terminal.lst for auto-loading. */
296
read_terminal_list (const char *prefix)
304
grub_errno = GRUB_ERR_NONE;
308
filename = grub_xasprintf ("%s/terminal.lst", prefix);
311
grub_errno = GRUB_ERR_NONE;
315
file = grub_file_open (filename);
316
grub_free (filename);
319
grub_errno = GRUB_ERR_NONE;
323
/* Override previous terminal.lst. */
324
grub_terminal_autoload_free ();
326
for (;; grub_free (buf))
329
struct grub_term_autoload *cur;
330
struct grub_term_autoload **target = NULL;
332
buf = grub_file_getline (file);
340
target = &grub_term_input_autoload;
344
target = &grub_term_output_autoload;
352
p = grub_strchr (name, ':');
360
cur = grub_malloc (sizeof (*cur));
363
grub_errno = GRUB_ERR_NONE;
367
cur->name = grub_strdup (name);
370
grub_errno = GRUB_ERR_NONE;
375
cur->modname = grub_strdup (p);
378
grub_errno = GRUB_ERR_NONE;
379
grub_free (cur->name);
387
grub_file_close (file);
389
grub_errno = GRUB_ERR_NONE;
393
putglyph (const struct grub_unicode_glyph *c, struct grub_term_output *term)
395
struct grub_unicode_glyph c2 =
404
if (c->base == '\t' && term->getxy)
408
n = 8 - ((term->getxy (term) >> 8) & 7);
411
(term->putchar) (term, &c2);
416
if ((term->flags & GRUB_TERM_CODE_TYPE_MASK)
417
== GRUB_TERM_CODE_TYPE_UTF8_LOGICAL
418
|| (term->flags & GRUB_TERM_CODE_TYPE_MASK)
419
== GRUB_TERM_CODE_TYPE_UTF8_VISUAL)
422
c2.estimated_width = grub_term_getcharwidth (term, c);
423
for (i = -1; i < (int) c->ncomb; i++)
425
grub_uint8_t u8[20], *ptr;
431
if ((term->flags & GRUB_TERM_CODE_TYPE_MASK)
432
== GRUB_TERM_CODE_TYPE_UTF8_VISUAL)
434
if ((c->attributes & GRUB_UNICODE_GLYPH_ATTRIBUTE_MIRROR))
435
code = grub_unicode_mirror_code (code);
436
code = grub_unicode_shape_code (code, c->attributes);
440
code = c->combining[i].code;
442
grub_ucs4_to_utf8 (&code, 1, u8, sizeof (u8));
444
for (ptr = u8; *ptr; ptr++)
447
(term->putchar) (term, &c2);
448
c2.estimated_width = 0;
451
c2.estimated_width = 1;
454
(term->putchar) (term, c);
459
(term->putchar) (term, &c2);
464
putcode_real (grub_uint32_t code, struct grub_term_output *term)
466
struct grub_unicode_glyph c =
475
c.base = map_code (code, term);
479
/* Put a Unicode character. */
481
grub_putcode (grub_uint32_t code, struct grub_term_output *term)
483
/* Combining character by itself? */
484
if (grub_unicode_get_comb_type (code) != GRUB_UNICODE_COMB_NONE)
487
putcode_real (code, term);
491
get_maxwidth (struct grub_term_output *term,
492
int margin_left, int margin_right)
494
struct grub_unicode_glyph space_glyph = {
501
return (grub_term_width (term)
502
- grub_term_getcharwidth (term, &space_glyph)
503
* (margin_left + margin_right) - 1);
507
get_startwidth (struct grub_term_output *term,
510
return ((term->getxy (term) >> 8) & 0xff) - margin_left;
514
print_ucs4_terminal (const grub_uint32_t * str,
515
const grub_uint32_t * last_position,
516
int margin_left, int margin_right,
517
struct grub_term_output *term,
518
struct term_state *state)
520
const grub_uint32_t *ptr;
521
grub_ssize_t startwidth = get_startwidth (term, margin_left);
522
grub_ssize_t line_width = startwidth;
523
grub_ssize_t lastspacewidth = 0;
524
grub_ssize_t max_width = get_maxwidth (term, margin_left, margin_right);
525
const grub_uint32_t *line_start = str, *last_space = str - 1;
527
for (ptr = str; ptr < last_position; ptr++)
529
grub_ssize_t last_width = 0;
530
if (grub_unicode_get_comb_type (*ptr) == GRUB_UNICODE_COMB_NONE)
532
struct grub_unicode_glyph c = {
539
line_width += last_width = grub_term_getcharwidth (term, &c);
544
lastspacewidth = line_width;
548
if (line_width > max_width || *ptr == '\n')
550
const grub_uint32_t *ptr2;
552
if (line_width > max_width && last_space > line_start)
554
else if (line_width > max_width
555
&& line_start == str && startwidth != 0)
558
lastspacewidth = startwidth;
561
lastspacewidth = line_width - last_width;
563
for (ptr2 = line_start; ptr2 < ptr; ptr2++)
565
/* Skip combining characters on non-UTF8 terminals. */
566
if ((term->flags & GRUB_TERM_CODE_TYPE_MASK)
567
!= GRUB_TERM_CODE_TYPE_UTF8_LOGICAL
568
&& grub_unicode_get_comb_type (*ptr2)
569
!= GRUB_UNICODE_COMB_NONE)
571
putcode_real (*ptr2, term);
574
grub_print_spaces (term, margin_right);
575
grub_putcode ('\n', term);
576
if (state && ++state->num_lines
577
>= (grub_ssize_t) grub_term_height (term) - 2)
579
state->backlog_ucs4 = (ptr == last_space || *ptr == '\n')
581
state->backlog_len = last_position - state->backlog_ucs4;
585
line_width -= lastspacewidth;
586
grub_print_spaces (term, margin_left);
587
if (ptr == last_space || *ptr == '\n')
594
const grub_uint32_t *ptr2;
595
for (ptr2 = line_start; ptr2 < last_position; ptr2++)
597
/* Skip combining characters on non-UTF8 terminals. */
598
if ((term->flags & GRUB_TERM_CODE_TYPE_MASK)
599
!= GRUB_TERM_CODE_TYPE_UTF8_LOGICAL
600
&& grub_unicode_get_comb_type (*ptr2)
601
!= GRUB_UNICODE_COMB_NONE)
603
putcode_real (*ptr2, term);
609
static struct term_state *
610
find_term_state (struct grub_term_output *term)
612
struct term_state *state;
613
for (state = term_states; state; state = state->next)
614
if (grub_strcmp (state->term_name, term->name) == 0)
617
state = grub_zalloc (sizeof (*state));
620
grub_errno = GRUB_ERR_NONE;
624
state->term_name = grub_strdup (term->name);
625
state->next = term_states;
632
put_glyphs_terminal (const struct grub_unicode_glyph *visual,
633
grub_ssize_t visual_len,
634
int margin_left, int margin_right,
635
struct grub_term_output *term,
636
struct term_state *state)
638
const struct grub_unicode_glyph *visual_ptr;
639
for (visual_ptr = visual; visual_ptr < visual + visual_len; visual_ptr++)
641
if (visual_ptr->base == '\n')
642
grub_print_spaces (term, margin_right);
643
putglyph (visual_ptr, term);
644
if (visual_ptr->base == '\n')
646
if (state && ++state->num_lines
647
>= (grub_ssize_t) grub_term_height (term) - 2)
649
state->backlog_glyphs = visual_ptr + 1;
650
state->backlog_len = visual_len - (visual_ptr - visual) - 1;
654
grub_print_spaces (term, margin_left);
656
grub_free (visual_ptr->combining);
662
print_backlog (struct grub_term_output *term,
663
int margin_left, int margin_right)
665
struct term_state *state = find_term_state (term);
670
if (state->backlog_ucs4)
673
ret = print_ucs4_terminal (state->backlog_ucs4,
674
state->backlog_ucs4 + state->backlog_len,
675
margin_left, margin_right, term, state);
678
grub_free (state->free);
680
state->backlog_len = 0;
681
state->backlog_ucs4 = 0;
686
if (state->backlog_glyphs)
689
ret = put_glyphs_terminal (state->backlog_glyphs,
691
margin_left, margin_right, term, state);
694
grub_free (state->free);
696
state->backlog_len = 0;
697
state->backlog_glyphs = 0;
706
print_ucs4_real (const grub_uint32_t * str,
707
const grub_uint32_t * last_position,
708
int margin_left, int margin_right,
709
struct grub_term_output *term, int backlog)
711
struct term_state *state = NULL;
714
state = find_term_state (term);
716
if (((term->getxy (term) >> 8) & 0xff) < margin_left)
717
grub_print_spaces (term, margin_left - ((term->getxy (term) >> 8) & 0xff));
719
if ((term->flags & GRUB_TERM_CODE_TYPE_MASK)
720
== GRUB_TERM_CODE_TYPE_VISUAL_GLYPHS
721
|| (term->flags & GRUB_TERM_CODE_TYPE_MASK)
722
== GRUB_TERM_CODE_TYPE_UTF8_VISUAL)
724
grub_ssize_t visual_len;
725
struct grub_unicode_glyph *visual;
728
auto grub_ssize_t getcharwidth (const struct grub_unicode_glyph *c);
729
grub_ssize_t getcharwidth (const struct grub_unicode_glyph *c)
731
return grub_term_getcharwidth (term, c);
734
visual_len = grub_bidi_logical_to_visual (str, last_position - str,
735
&visual, getcharwidth,
739
get_startwidth (term,
746
ret = put_glyphs_terminal (visual, visual_len, margin_left, margin_right,
751
state->free = visual;
754
return print_ucs4_terminal (str, last_position, margin_left, margin_right,
759
grub_print_ucs4 (const grub_uint32_t * str,
760
const grub_uint32_t * last_position,
761
int margin_left, int margin_right,
762
struct grub_term_output *term)
764
print_ucs4_real (str, last_position, margin_left, margin_right,
770
grub_xputs_normal (const char *str)
772
grub_uint32_t *unicode_str = NULL, *unicode_last_position;
774
grub_term_output_t term;
777
grub_utf8_to_ucs4_alloc (str, &unicode_str,
778
&unicode_last_position);
785
struct grub_unicode_glyph c =
791
.estimated_width = 1,
795
FOR_ACTIVE_TERM_OUTPUTS(term)
797
(term->putchar) (term, &c);
802
FOR_ACTIVE_TERM_OUTPUTS(term)
804
(term->putchar) (term, &c);
812
FOR_ACTIVE_TERM_OUTPUTS(term)
815
cur = print_ucs4_real (unicode_str, unicode_last_position, 0, 0,
824
FOR_ACTIVE_TERM_OUTPUTS(term)
827
cur = print_backlog (term, 0, 0);
832
grub_free (unicode_str);
838
struct grub_term_output *term;
840
FOR_ACTIVE_TERM_OUTPUTS(term)
842
if ((term->flags & GRUB_TERM_DUMB) || (grub_env_get ("debug")))
844
grub_putcode ('\n', term);
845
grub_term_refresh (term);