Upgraded GRUB2 to 2.00 release.
[AROS.git] / arch / all-pc / boot / grub2-aros / grub-core / term / gfxterm.c
blobc995b845f9254368cd24c2fe19edc0c893bc74c5
1 /*
2 * GRUB -- GRand Unified Bootloader
3 * Copyright (C) 2006,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/types.h>
21 #include <grub/dl.h>
22 #include <grub/misc.h>
23 #include <grub/font.h>
24 #include <grub/mm.h>
25 #include <grub/env.h>
26 #include <grub/video.h>
27 #include <grub/gfxterm.h>
28 #include <grub/bitmap.h>
29 #include <grub/command.h>
30 #include <grub/extcmd.h>
31 #include <grub/bitmap_scale.h>
32 #include <grub/i18n.h>
34 GRUB_MOD_LICENSE ("GPLv3+");
36 #define DEFAULT_VIDEO_MODE "auto"
37 #define DEFAULT_BORDER_WIDTH 10
39 #define DEFAULT_STANDARD_COLOR 0x07
41 struct grub_dirty_region
43 int top_left_x;
44 int top_left_y;
45 int bottom_right_x;
46 int bottom_right_y;
49 struct grub_colored_char
51 /* An Unicode codepoint. */
52 struct grub_unicode_glyph *code;
54 /* Color values. */
55 grub_video_color_t fg_color;
56 grub_video_color_t bg_color;
59 struct grub_virtual_screen
61 /* Dimensions of the virtual screen in pixels. */
62 unsigned int width;
63 unsigned int height;
65 /* Offset in the display in pixels. */
66 unsigned int offset_x;
67 unsigned int offset_y;
69 /* TTY Character sizes in pixes. */
70 unsigned int normal_char_width;
71 unsigned int normal_char_height;
73 /* Virtual screen TTY size in characters. */
74 unsigned int columns;
75 unsigned int rows;
77 /* Current cursor location in characters. */
78 unsigned int cursor_x;
79 unsigned int cursor_y;
81 /* Current cursor state. */
82 int cursor_state;
84 /* Font settings. */
85 grub_font_t font;
87 /* Terminal color settings. */
88 grub_uint8_t standard_color_setting;
89 grub_uint8_t term_color;
91 /* Color settings. */
92 grub_video_color_t fg_color;
93 grub_video_color_t bg_color;
94 grub_video_color_t bg_color_display;
96 /* Text buffer for virtual screen. Contains (columns * rows) number
97 of entries. */
98 struct grub_colored_char *text_buffer;
100 int total_scroll;
103 struct grub_gfxterm_window
105 unsigned x;
106 unsigned y;
107 unsigned width;
108 unsigned height;
109 int double_repaint;
112 static struct grub_video_render_target *render_target;
113 void (*grub_gfxterm_decorator_hook) (void) = NULL;
114 static struct grub_gfxterm_window window;
115 static struct grub_virtual_screen virtual_screen;
116 static grub_gfxterm_repaint_callback_t repaint_callback;
117 static int repaint_scheduled = 0;
118 static int repaint_was_scheduled = 0;
120 static void destroy_window (void);
122 static struct grub_video_render_target *text_layer;
124 static unsigned int bitmap_width;
125 static unsigned int bitmap_height;
126 static struct grub_video_bitmap *bitmap;
127 static int blend_text_bg;
128 static grub_video_rgba_color_t default_bg_color = { 0, 0, 0, 0 };
130 static struct grub_dirty_region dirty_region;
132 static void dirty_region_reset (void);
134 static int dirty_region_is_empty (void);
136 static void dirty_region_add (int x, int y,
137 unsigned int width, unsigned int height);
139 static unsigned int calculate_normal_character_width (grub_font_t font);
141 static unsigned char calculate_character_width (struct grub_font_glyph *glyph);
143 static void grub_gfxterm_refresh (struct grub_term_output *term __attribute__ ((unused)));
145 static grub_ssize_t
146 grub_gfxterm_getcharwidth (struct grub_term_output *term __attribute__ ((unused)),
147 const struct grub_unicode_glyph *c);
149 static void
150 set_term_color (grub_uint8_t term_color)
152 struct grub_video_render_target *old_target;
154 /* Save previous target and switch to text layer. */
155 grub_video_get_active_render_target (&old_target);
156 grub_video_set_active_render_target (text_layer);
158 /* Map terminal color to text layer compatible video colors. */
159 virtual_screen.fg_color = grub_video_map_color(term_color & 0x0f);
161 /* Special case: use black as transparent color. */
162 if (((term_color >> 4) & 0x0f) == 0)
164 virtual_screen.bg_color = grub_video_map_rgba(0, 0, 0, 0);
166 else
168 virtual_screen.bg_color = grub_video_map_color((term_color >> 4) & 0x0f);
171 /* Restore previous target. */
172 grub_video_set_active_render_target (old_target);
175 static void
176 clear_char (struct grub_colored_char *c)
178 grub_free (c->code);
179 c->code = grub_unicode_glyph_from_code (' ');
180 if (!c->code)
181 grub_errno = GRUB_ERR_NONE;
182 c->fg_color = virtual_screen.fg_color;
183 c->bg_color = virtual_screen.bg_color;
186 static void
187 grub_virtual_screen_free (void)
189 /* If virtual screen has been allocated, free it. */
190 if (virtual_screen.text_buffer != 0)
191 grub_free (virtual_screen.text_buffer);
193 /* Reset virtual screen data. */
194 grub_memset (&virtual_screen, 0, sizeof (virtual_screen));
196 /* Free render targets. */
197 grub_video_delete_render_target (text_layer);
198 text_layer = 0;
201 static grub_err_t
202 grub_virtual_screen_setup (unsigned int x, unsigned int y,
203 unsigned int width, unsigned int height,
204 const char *font_name)
206 unsigned int i;
208 /* Free old virtual screen. */
209 grub_virtual_screen_free ();
211 /* Initialize with default data. */
212 virtual_screen.font = grub_font_get (font_name);
213 if (!virtual_screen.font)
214 return grub_error (GRUB_ERR_BAD_FONT,
215 "no font loaded");
216 virtual_screen.width = width;
217 virtual_screen.height = height;
218 virtual_screen.offset_x = x;
219 virtual_screen.offset_y = y;
220 virtual_screen.normal_char_width =
221 calculate_normal_character_width (virtual_screen.font);
222 virtual_screen.normal_char_height =
223 grub_font_get_max_char_height (virtual_screen.font);
224 virtual_screen.cursor_x = 0;
225 virtual_screen.cursor_y = 0;
226 virtual_screen.cursor_state = 1;
227 virtual_screen.total_scroll = 0;
229 /* Calculate size of text buffer. */
230 virtual_screen.columns = virtual_screen.width / virtual_screen.normal_char_width;
231 virtual_screen.rows = virtual_screen.height / virtual_screen.normal_char_height;
233 /* Allocate memory for text buffer. */
234 virtual_screen.text_buffer =
235 (struct grub_colored_char *) grub_malloc (virtual_screen.columns
236 * virtual_screen.rows
237 * sizeof (*virtual_screen.text_buffer));
238 if (grub_errno != GRUB_ERR_NONE)
239 return grub_errno;
241 /* Create new render target for text layer. */
242 grub_video_create_render_target (&text_layer,
243 virtual_screen.width,
244 virtual_screen.height,
245 GRUB_VIDEO_MODE_TYPE_RGB
246 | GRUB_VIDEO_MODE_TYPE_ALPHA);
247 if (grub_errno != GRUB_ERR_NONE)
248 return grub_errno;
250 /* As we want to have colors compatible with rendering target,
251 we can only have those after mode is initialized. */
252 grub_video_set_active_render_target (text_layer);
254 virtual_screen.standard_color_setting = DEFAULT_STANDARD_COLOR;
256 virtual_screen.term_color = GRUB_TERM_DEFAULT_NORMAL_COLOR;
258 set_term_color (virtual_screen.term_color);
260 grub_video_set_active_render_target (render_target);
262 virtual_screen.bg_color_display =
263 grub_video_map_rgba_color (default_bg_color);
265 /* Clear out text buffer. */
266 for (i = 0; i < virtual_screen.columns * virtual_screen.rows; i++)
268 virtual_screen.text_buffer[i].code = 0;
269 clear_char (&(virtual_screen.text_buffer[i]));
272 return grub_errno;
275 void
276 grub_gfxterm_schedule_repaint (void)
278 repaint_scheduled = 1;
281 grub_err_t
282 grub_gfxterm_set_window (struct grub_video_render_target *target,
283 int x, int y, int width, int height,
284 int double_repaint,
285 const char *font_name, int border_width)
287 /* Clean up any prior instance. */
288 destroy_window ();
290 /* Set the render target. */
291 render_target = target;
293 /* Create virtual screen. */
294 if (grub_virtual_screen_setup (border_width, border_width,
295 width - 2 * border_width,
296 height - 2 * border_width,
297 font_name)
298 != GRUB_ERR_NONE)
300 return grub_errno;
303 /* Set window bounds. */
304 window.x = x;
305 window.y = y;
306 window.width = width;
307 window.height = height;
308 window.double_repaint = double_repaint;
310 dirty_region_reset ();
311 grub_gfxterm_schedule_repaint ();
313 return grub_errno;
316 static grub_err_t
317 grub_gfxterm_fullscreen (void)
319 const char *font_name;
320 struct grub_video_mode_info mode_info;
321 grub_video_color_t color;
322 grub_err_t err;
323 int double_redraw;
325 err = grub_video_get_info (&mode_info);
326 /* Figure out what mode we ended up. */
327 if (err)
328 return err;
330 grub_video_set_active_render_target (GRUB_VIDEO_RENDER_TARGET_DISPLAY);
332 double_redraw = mode_info.mode_type & GRUB_VIDEO_MODE_TYPE_DOUBLE_BUFFERED
333 && !(mode_info.mode_type & GRUB_VIDEO_MODE_TYPE_UPDATING_SWAP);
335 /* Make sure screen is set to the default background color. */
336 color = grub_video_map_rgba_color (default_bg_color);
337 grub_video_fill_rect (color, 0, 0, mode_info.width, mode_info.height);
338 if (double_redraw)
340 grub_video_swap_buffers ();
341 grub_video_fill_rect (color, 0, 0, mode_info.width, mode_info.height);
344 /* Select the font to use. */
345 font_name = grub_env_get ("gfxterm_font");
346 if (! font_name)
347 font_name = ""; /* Allow fallback to any font. */
349 grub_gfxterm_decorator_hook = NULL;
351 return grub_gfxterm_set_window (GRUB_VIDEO_RENDER_TARGET_DISPLAY,
352 0, 0, mode_info.width, mode_info.height,
353 double_redraw,
354 font_name, DEFAULT_BORDER_WIDTH);
357 static grub_err_t
358 grub_gfxterm_term_init (struct grub_term_output *term __attribute__ ((unused)))
360 char *tmp;
361 grub_err_t err;
362 const char *modevar;
364 /* Parse gfxmode environment variable if set. */
365 modevar = grub_env_get ("gfxmode");
366 if (! modevar || *modevar == 0)
367 err = grub_video_set_mode (DEFAULT_VIDEO_MODE,
368 GRUB_VIDEO_MODE_TYPE_PURE_TEXT, 0);
369 else
371 tmp = grub_xasprintf ("%s;" DEFAULT_VIDEO_MODE, modevar);
372 if (!tmp)
373 return grub_errno;
374 err = grub_video_set_mode (tmp, GRUB_VIDEO_MODE_TYPE_PURE_TEXT, 0);
375 grub_free (tmp);
378 if (err)
379 return err;
381 err = grub_gfxterm_fullscreen ();
382 if (err)
383 grub_video_restore ();
385 return err;
388 static void
389 destroy_window (void)
391 repaint_callback = 0;
392 grub_virtual_screen_free ();
395 static grub_err_t
396 grub_gfxterm_term_fini (struct grub_term_output *term __attribute__ ((unused)))
398 unsigned i;
399 destroy_window ();
400 grub_video_restore ();
402 for (i = 0; i < virtual_screen.columns * virtual_screen.rows; i++)
404 grub_free (virtual_screen.text_buffer[i].code);
405 virtual_screen.text_buffer[i].code = 0;
408 /* Clear error state. */
409 grub_errno = GRUB_ERR_NONE;
410 return GRUB_ERR_NONE;
413 static void
414 redraw_screen_rect (unsigned int x, unsigned int y,
415 unsigned int width, unsigned int height)
417 grub_video_color_t color;
418 grub_video_rect_t saved_view;
420 grub_video_set_active_render_target (render_target);
421 /* Save viewport and set it to our window. */
422 grub_video_get_viewport ((unsigned *) &saved_view.x,
423 (unsigned *) &saved_view.y,
424 (unsigned *) &saved_view.width,
425 (unsigned *) &saved_view.height);
426 grub_video_set_viewport (window.x, window.y, window.width, window.height);
428 if (bitmap)
430 /* Render bitmap as background. */
431 grub_video_blit_bitmap (bitmap, GRUB_VIDEO_BLIT_REPLACE, x, y,
432 x, y,
433 width, height);
435 /* If bitmap is smaller than requested blit area, use background
436 color. */
437 color = virtual_screen.bg_color_display;
439 /* Fill right side of the bitmap if needed. */
440 if ((x + width >= bitmap_width) && (y < bitmap_height))
442 int w = (x + width) - bitmap_width;
443 int h = height;
444 unsigned int tx = x;
446 if (y + height >= bitmap_height)
448 h = bitmap_height - y;
451 if (bitmap_width > tx)
453 tx = bitmap_width;
456 /* Render background layer. */
457 grub_video_fill_rect (color, tx, y, w, h);
460 /* Fill bottom side of the bitmap if needed. */
461 if (y + height >= bitmap_height)
463 int h = (y + height) - bitmap_height;
464 unsigned int ty = y;
466 if (bitmap_height > ty)
468 ty = bitmap_height;
471 /* Render background layer. */
472 grub_video_fill_rect (color, x, ty, width, h);
475 else
477 /* Render background layer. */
478 color = virtual_screen.bg_color_display;
479 grub_video_fill_rect (color, x, y, width, height);
482 if (blend_text_bg)
483 /* Render text layer as blended. */
484 grub_video_blit_render_target (text_layer, GRUB_VIDEO_BLIT_BLEND, x, y,
485 x - virtual_screen.offset_x,
486 y - virtual_screen.offset_y,
487 width, height);
488 else
489 /* Render text layer as replaced (to get texts background color). */
490 grub_video_blit_render_target (text_layer, GRUB_VIDEO_BLIT_REPLACE, x, y,
491 x - virtual_screen.offset_x,
492 y - virtual_screen.offset_y,
493 width, height);
495 /* Restore saved viewport. */
496 grub_video_set_viewport (saved_view.x, saved_view.y,
497 saved_view.width, saved_view.height);
498 grub_video_set_active_render_target (render_target);
500 if (repaint_callback)
501 repaint_callback (x, y, width, height);
504 static void
505 dirty_region_reset (void)
507 dirty_region.top_left_x = -1;
508 dirty_region.top_left_y = -1;
509 dirty_region.bottom_right_x = -1;
510 dirty_region.bottom_right_y = -1;
511 repaint_was_scheduled = 0;
514 static int
515 dirty_region_is_empty (void)
517 if ((dirty_region.top_left_x == -1)
518 || (dirty_region.top_left_y == -1)
519 || (dirty_region.bottom_right_x == -1)
520 || (dirty_region.bottom_right_y == -1))
521 return 1;
522 return 0;
525 static void
526 dirty_region_add_real (int x, int y, unsigned int width, unsigned int height)
528 if (dirty_region_is_empty ())
530 dirty_region.top_left_x = x;
531 dirty_region.top_left_y = y;
532 dirty_region.bottom_right_x = x + width - 1;
533 dirty_region.bottom_right_y = y + height - 1;
535 else
537 if (x < dirty_region.top_left_x)
538 dirty_region.top_left_x = x;
539 if (y < dirty_region.top_left_y)
540 dirty_region.top_left_y = y;
541 if ((x + (int)width - 1) > dirty_region.bottom_right_x)
542 dirty_region.bottom_right_x = x + width - 1;
543 if ((y + (int)height - 1) > dirty_region.bottom_right_y)
544 dirty_region.bottom_right_y = y + height - 1;
548 static void
549 dirty_region_add (int x, int y, unsigned int width, unsigned int height)
551 if ((width == 0) || (height == 0))
552 return;
554 if (repaint_scheduled)
556 dirty_region_add_real (virtual_screen.offset_x, virtual_screen.offset_y,
557 virtual_screen.width, virtual_screen.height);
558 repaint_scheduled = 0;
559 repaint_was_scheduled = 1;
561 dirty_region_add_real (x, y, width, height);
564 static void
565 dirty_region_add_virtualscreen (void)
567 /* Mark virtual screen as dirty. */
568 dirty_region_add (virtual_screen.offset_x, virtual_screen.offset_y,
569 virtual_screen.width, virtual_screen.height);
573 static void
574 dirty_region_redraw (void)
576 int x;
577 int y;
578 int width;
579 int height;
581 if (dirty_region_is_empty ())
582 return;
584 x = dirty_region.top_left_x;
585 y = dirty_region.top_left_y;
587 width = dirty_region.bottom_right_x - x + 1;
588 height = dirty_region.bottom_right_y - y + 1;
590 if (repaint_was_scheduled && grub_gfxterm_decorator_hook)
591 grub_gfxterm_decorator_hook ();
593 redraw_screen_rect (x, y, width, height);
596 static inline void
597 paint_char (unsigned cx, unsigned cy)
599 struct grub_colored_char *p;
600 struct grub_font_glyph *glyph;
601 grub_video_color_t color;
602 grub_video_color_t bgcolor;
603 unsigned int x;
604 unsigned int y;
605 int ascent;
606 unsigned int height;
607 unsigned int width;
609 if (cy + virtual_screen.total_scroll >= virtual_screen.rows)
610 return;
612 /* Find out active character. */
613 p = (virtual_screen.text_buffer
614 + cx + (cy * virtual_screen.columns));
616 if (!p->code)
617 return;
619 /* Get glyph for character. */
620 glyph = grub_font_construct_glyph (virtual_screen.font, p->code);
621 if (!glyph)
623 grub_errno = GRUB_ERR_NONE;
624 return;
626 ascent = grub_font_get_ascent (virtual_screen.font);
628 width = virtual_screen.normal_char_width * calculate_character_width(glyph);
629 height = virtual_screen.normal_char_height;
631 color = p->fg_color;
632 bgcolor = p->bg_color;
634 x = cx * virtual_screen.normal_char_width;
635 y = (cy + virtual_screen.total_scroll) * virtual_screen.normal_char_height;
637 /* Render glyph to text layer. */
638 grub_video_set_active_render_target (text_layer);
639 grub_video_fill_rect (bgcolor, x, y, width, height);
640 grub_font_draw_glyph (glyph, color, x, y + ascent);
641 grub_video_set_active_render_target (render_target);
643 /* Mark character to be drawn. */
644 dirty_region_add (virtual_screen.offset_x + x, virtual_screen.offset_y + y,
645 width, height);
646 grub_free (glyph);
649 static inline void
650 write_char (void)
652 paint_char (virtual_screen.cursor_x, virtual_screen.cursor_y);
655 static inline void
656 draw_cursor (int show)
658 unsigned int x;
659 unsigned int y;
660 unsigned int width;
661 unsigned int height;
662 grub_video_color_t color;
664 write_char ();
666 if (!show)
667 return;
669 if (virtual_screen.cursor_y + virtual_screen.total_scroll
670 >= virtual_screen.rows)
671 return;
673 /* Determine cursor properties and position on text layer. */
674 x = virtual_screen.cursor_x * virtual_screen.normal_char_width;
675 width = virtual_screen.normal_char_width;
676 color = virtual_screen.fg_color;
677 y = ((virtual_screen.cursor_y + virtual_screen.total_scroll)
678 * virtual_screen.normal_char_height
679 + grub_font_get_ascent (virtual_screen.font));
680 height = 2;
682 /* Render cursor to text layer. */
683 grub_video_set_active_render_target (text_layer);
684 grub_video_fill_rect (color, x, y, width, height);
685 grub_video_set_active_render_target (render_target);
687 /* Mark cursor to be redrawn. */
688 dirty_region_add (virtual_screen.offset_x + x,
689 virtual_screen.offset_y + y,
690 width, height);
693 static void
694 real_scroll (void)
696 unsigned int i, j, was_scroll;
697 grub_video_color_t color;
699 if (!virtual_screen.total_scroll)
700 return;
702 /* If we have bitmap, re-draw screen, otherwise scroll physical screen too. */
703 if (bitmap)
705 /* Scroll physical screen. */
706 grub_video_set_active_render_target (text_layer);
707 color = virtual_screen.bg_color;
708 grub_video_scroll (color, 0, -virtual_screen.normal_char_height
709 * virtual_screen.total_scroll);
711 /* Mark virtual screen to be redrawn. */
712 dirty_region_add_virtualscreen ();
714 else
716 grub_video_rect_t saved_view;
718 /* Remove cursor. */
719 draw_cursor (0);
721 grub_video_set_active_render_target (render_target);
723 i = window.double_repaint ? 2 : 1;
725 color = virtual_screen.bg_color;
727 while (i--)
729 /* Save viewport and set it to our window. */
730 grub_video_get_viewport ((unsigned *) &saved_view.x,
731 (unsigned *) &saved_view.y,
732 (unsigned *) &saved_view.width,
733 (unsigned *) &saved_view.height);
735 grub_video_set_viewport (window.x, window.y, window.width,
736 window.height);
738 /* Clear new border area. */
739 grub_video_fill_rect (color,
740 virtual_screen.offset_x,
741 virtual_screen.offset_y,
742 virtual_screen.width,
743 virtual_screen.normal_char_height
744 * virtual_screen.total_scroll);
746 grub_video_set_active_render_target (render_target);
747 dirty_region_redraw ();
749 /* Scroll physical screen. */
750 grub_video_scroll (color, 0, -virtual_screen.normal_char_height
751 * virtual_screen.total_scroll);
753 /* Restore saved viewport. */
754 grub_video_set_viewport (saved_view.x, saved_view.y,
755 saved_view.width, saved_view.height);
757 if (i)
758 grub_video_swap_buffers ();
760 dirty_region_reset ();
762 /* Scroll physical screen. */
763 grub_video_set_active_render_target (text_layer);
764 color = virtual_screen.bg_color;
765 grub_video_scroll (color, 0, -virtual_screen.normal_char_height
766 * virtual_screen.total_scroll);
768 grub_video_set_active_render_target (render_target);
772 was_scroll = virtual_screen.total_scroll;
773 virtual_screen.total_scroll = 0;
775 if (was_scroll > virtual_screen.rows)
776 was_scroll = virtual_screen.rows;
778 /* Draw shadow part. */
779 for (i = virtual_screen.rows - was_scroll;
780 i < virtual_screen.rows; i++)
781 for (j = 0; j < virtual_screen.columns; j++)
782 paint_char (j, i);
784 /* Draw cursor if visible. */
785 if (virtual_screen.cursor_state)
786 draw_cursor (1);
788 if (repaint_callback)
789 repaint_callback (window.x, window.y, window.width, window.height);
792 static void
793 scroll_up (void)
795 unsigned int i;
797 /* Clear first line in text buffer. */
798 for (i = 0; i < virtual_screen.columns; i++)
799 grub_free (virtual_screen.text_buffer[i].code);
801 /* Scroll text buffer with one line to up. */
802 grub_memmove (virtual_screen.text_buffer,
803 virtual_screen.text_buffer + virtual_screen.columns,
804 sizeof (*virtual_screen.text_buffer)
805 * virtual_screen.columns
806 * (virtual_screen.rows - 1));
808 /* Clear last line in text buffer. */
809 for (i = virtual_screen.columns * (virtual_screen.rows - 1);
810 i < virtual_screen.columns * virtual_screen.rows;
811 i++)
813 virtual_screen.text_buffer[i].code = 0;
814 clear_char (&(virtual_screen.text_buffer[i]));
817 virtual_screen.total_scroll++;
820 static void
821 grub_gfxterm_putchar (struct grub_term_output *term,
822 const struct grub_unicode_glyph *c)
824 if (c->base == '\a')
825 /* FIXME */
826 return;
828 /* Erase current cursor, if any. */
829 if (virtual_screen.cursor_state)
830 draw_cursor (0);
832 if (c->base == '\b' || c->base == '\n' || c->base == '\r')
834 switch (c->base)
836 case '\b':
837 if (virtual_screen.cursor_x > 0)
838 virtual_screen.cursor_x--;
839 break;
841 case '\n':
842 if (virtual_screen.cursor_y >= virtual_screen.rows - 1)
843 scroll_up ();
844 else
845 virtual_screen.cursor_y++;
846 break;
848 case '\r':
849 virtual_screen.cursor_x = 0;
850 break;
853 else
855 struct grub_colored_char *p;
856 unsigned char char_width;
858 /* Calculate actual character width for glyph. This is number of
859 times of normal_font_width. */
860 char_width = grub_gfxterm_getcharwidth (term, c);
862 /* If we are about to exceed line length, wrap to next line. */
863 if (virtual_screen.cursor_x + char_width > virtual_screen.columns)
865 if (virtual_screen.cursor_y >= virtual_screen.rows - 1)
866 scroll_up ();
867 else
868 virtual_screen.cursor_y++;
871 /* Find position on virtual screen, and fill information. */
872 p = (virtual_screen.text_buffer +
873 virtual_screen.cursor_x +
874 virtual_screen.cursor_y * virtual_screen.columns);
875 grub_free (p->code);
876 p->code = grub_unicode_glyph_dup (c);
877 if (!p->code)
878 grub_errno = GRUB_ERR_NONE;
879 p->fg_color = virtual_screen.fg_color;
880 p->bg_color = virtual_screen.bg_color;
882 /* If we have large glyph, add fixup info. */
883 if (char_width > 1)
885 unsigned i;
887 for (i = 1; i < char_width && p + i <
888 virtual_screen.text_buffer + virtual_screen.columns
889 * virtual_screen.rows; i++)
891 grub_free (p[i].code);
892 p[i].code = NULL;
896 /* Draw glyph. */
897 write_char ();
899 /* Make sure we scroll screen when needed and wrap line correctly. */
900 virtual_screen.cursor_x += char_width;
901 if (virtual_screen.cursor_x >= virtual_screen.columns)
903 virtual_screen.cursor_x = 0;
905 if (virtual_screen.cursor_y >= virtual_screen.rows - 1)
906 scroll_up ();
907 else
908 virtual_screen.cursor_y++;
912 /* Redraw cursor if it should be visible. */
913 /* Note: This will redraw the character as well, which means that the
914 above call to write_char is redundant when the cursor is showing. */
915 if (virtual_screen.cursor_state)
916 draw_cursor (1);
919 /* Use ASCII characters to determine normal character width. */
920 static unsigned int
921 calculate_normal_character_width (grub_font_t font)
923 struct grub_font_glyph *glyph;
924 unsigned int width = 0;
925 unsigned int i;
927 /* Get properties of every printable ASCII character. */
928 for (i = 32; i < 127; i++)
930 glyph = grub_font_get_glyph (font, i);
932 /* Skip unknown characters. Should never happen on normal conditions. */
933 if (! glyph)
934 continue;
936 if (glyph->device_width > width)
937 width = glyph->device_width;
939 if (!width)
940 return 8;
942 return width;
945 static unsigned char
946 calculate_character_width (struct grub_font_glyph *glyph)
948 if (! glyph || glyph->device_width == 0)
949 return 1;
951 return (glyph->device_width
952 + (virtual_screen.normal_char_width - 1))
953 / virtual_screen.normal_char_width;
956 static grub_ssize_t
957 grub_gfxterm_getcharwidth (struct grub_term_output *term __attribute__ ((unused)),
958 const struct grub_unicode_glyph *c)
960 int dev_width;
961 dev_width = grub_font_get_constructed_device_width (virtual_screen.font, c);
963 if (dev_width == 0)
964 return 1;
966 return (dev_width + (virtual_screen.normal_char_width - 1))
967 / virtual_screen.normal_char_width;
970 static grub_uint16_t
971 grub_virtual_screen_getwh (struct grub_term_output *term __attribute__ ((unused)))
973 return (virtual_screen.columns << 8) | virtual_screen.rows;
976 static grub_uint16_t
977 grub_virtual_screen_getxy (struct grub_term_output *term __attribute__ ((unused)))
979 return ((virtual_screen.cursor_x << 8) | virtual_screen.cursor_y);
982 static void
983 grub_gfxterm_gotoxy (struct grub_term_output *term __attribute__ ((unused)),
984 grub_uint8_t x, grub_uint8_t y)
986 if (x >= virtual_screen.columns)
987 x = virtual_screen.columns - 1;
989 if (y >= virtual_screen.rows)
990 y = virtual_screen.rows - 1;
992 /* Erase current cursor, if any. */
993 if (virtual_screen.cursor_state)
994 draw_cursor (0);
996 virtual_screen.cursor_x = x;
997 virtual_screen.cursor_y = y;
999 /* Draw cursor if visible. */
1000 if (virtual_screen.cursor_state)
1001 draw_cursor (1);
1004 static void
1005 grub_virtual_screen_cls (struct grub_term_output *term __attribute__ ((unused)))
1007 grub_uint32_t i;
1009 for (i = 0; i < virtual_screen.columns * virtual_screen.rows; i++)
1010 clear_char (&(virtual_screen.text_buffer[i]));
1012 virtual_screen.cursor_x = virtual_screen.cursor_y = 0;
1015 static void
1016 grub_gfxterm_cls (struct grub_term_output *term)
1018 grub_video_color_t color;
1020 /* Clear virtual screen. */
1021 grub_virtual_screen_cls (term);
1023 /* Clear text layer. */
1024 grub_video_set_active_render_target (text_layer);
1025 color = virtual_screen.bg_color;
1026 grub_video_fill_rect (color, 0, 0,
1027 virtual_screen.width, virtual_screen.height);
1028 grub_video_set_active_render_target (render_target);
1030 /* Mark virtual screen to be redrawn. */
1031 dirty_region_add_virtualscreen ();
1033 grub_gfxterm_refresh (term);
1036 static void
1037 grub_virtual_screen_setcolorstate (struct grub_term_output *term,
1038 grub_term_color_state state)
1040 switch (state)
1042 case GRUB_TERM_COLOR_STANDARD:
1043 virtual_screen.term_color = virtual_screen.standard_color_setting;
1044 break;
1046 case GRUB_TERM_COLOR_NORMAL:
1047 virtual_screen.term_color = term->normal_color;
1048 break;
1050 case GRUB_TERM_COLOR_HIGHLIGHT:
1051 virtual_screen.term_color = term->highlight_color;
1052 break;
1054 default:
1055 break;
1058 /* Change color to virtual terminal. */
1059 set_term_color (virtual_screen.term_color);
1062 static void
1063 grub_gfxterm_setcursor (struct grub_term_output *term __attribute__ ((unused)),
1064 int on)
1066 if (virtual_screen.cursor_state != on)
1068 if (virtual_screen.cursor_state)
1069 draw_cursor (0);
1070 else
1071 draw_cursor (1);
1073 virtual_screen.cursor_state = on;
1077 static void
1078 grub_gfxterm_refresh (struct grub_term_output *term __attribute__ ((unused)))
1080 real_scroll ();
1082 /* Redraw only changed regions. */
1083 dirty_region_redraw ();
1085 grub_video_swap_buffers ();
1087 if (window.double_repaint)
1088 dirty_region_redraw ();
1089 dirty_region_reset ();
1092 void
1093 grub_gfxterm_set_repaint_callback (grub_gfxterm_repaint_callback_t func)
1095 repaint_callback = func;
1098 /* Option array indices. */
1099 #define BACKGROUND_CMD_ARGINDEX_MODE 0
1101 static const struct grub_arg_option background_image_cmd_options[] =
1103 {"mode", 'm', 0, N_("Background image mode."),
1104 /* TRANSLATORS: This refers to background image mode (stretched or
1105 in left-top corner). Note that GRUB will accept only original
1106 keywords stretch and normal, not the translated ones.
1107 So please put both in translation
1108 e.g. stretch(=%STRETCH%)|normal(=%NORMAL%).
1109 The percents mark the translated version. Since many people
1110 may not know the word stretch or normal I recommend
1111 putting the translation either here or in "Background image mode."
1112 string. */
1113 N_("stretch|normal"),
1114 ARG_TYPE_STRING},
1115 {0, 0, 0, 0, 0, 0}
1118 static grub_err_t
1119 grub_gfxterm_background_image_cmd (grub_extcmd_context_t ctxt,
1120 int argc, char **args)
1122 struct grub_arg_list *state = ctxt->state;
1124 /* Check that we have video adapter active. */
1125 if (grub_video_get_info(NULL) != GRUB_ERR_NONE)
1126 return grub_errno;
1128 /* Destroy existing background bitmap if loaded. */
1129 if (bitmap)
1131 grub_video_bitmap_destroy (bitmap);
1132 bitmap = 0;
1133 blend_text_bg = 0;
1135 /* Mark whole screen as dirty. */
1136 dirty_region_add (0, 0, window.width, window.height);
1139 /* If filename was provided, try to load that. */
1140 if (argc >= 1)
1142 /* Try to load new one. */
1143 grub_video_bitmap_load (&bitmap, args[0]);
1144 if (grub_errno != GRUB_ERR_NONE)
1145 return grub_errno;
1147 /* Determine if the bitmap should be scaled to fit the screen. */
1148 if (!state[BACKGROUND_CMD_ARGINDEX_MODE].set
1149 || grub_strcmp (state[BACKGROUND_CMD_ARGINDEX_MODE].arg,
1150 "stretch") == 0)
1152 if (window.width != grub_video_bitmap_get_width (bitmap)
1153 || window.height != grub_video_bitmap_get_height (bitmap))
1155 struct grub_video_bitmap *scaled_bitmap;
1156 grub_video_bitmap_create_scaled (&scaled_bitmap,
1157 window.width,
1158 window.height,
1159 bitmap,
1160 GRUB_VIDEO_BITMAP_SCALE_METHOD_BEST);
1161 if (grub_errno == GRUB_ERR_NONE)
1163 /* Replace the original bitmap with the scaled one. */
1164 grub_video_bitmap_destroy (bitmap);
1165 bitmap = scaled_bitmap;
1170 /* If bitmap was loaded correctly, display it. */
1171 if (bitmap)
1173 blend_text_bg = 1;
1175 /* Determine bitmap dimensions. */
1176 bitmap_width = grub_video_bitmap_get_width (bitmap);
1177 bitmap_height = grub_video_bitmap_get_height (bitmap);
1179 /* Mark whole screen as dirty. */
1180 dirty_region_add (0, 0, window.width, window.height);
1184 /* All was ok. */
1185 grub_errno = GRUB_ERR_NONE;
1186 return grub_errno;
1189 static grub_err_t
1190 grub_gfxterm_background_color_cmd (grub_command_t cmd __attribute__ ((unused)),
1191 int argc, char **args)
1193 struct grub_video_render_target *old_target;
1195 if (argc != 1)
1196 return grub_error (GRUB_ERR_BAD_ARGUMENT, N_("one argument expected"));
1198 /* Check that we have video adapter active. */
1199 if (grub_video_get_info (NULL) != GRUB_ERR_NONE)
1200 return grub_errno;
1202 if (grub_video_parse_color (args[0], &default_bg_color) != GRUB_ERR_NONE)
1203 return grub_errno;
1205 /* Destroy existing background bitmap if loaded. */
1206 if (bitmap)
1208 grub_video_bitmap_destroy (bitmap);
1209 bitmap = 0;
1211 /* Mark whole screen as dirty. */
1212 dirty_region_add (0, 0, window.width, window.height);
1215 /* Set the background and border colors. The background color needs to be
1216 compatible with the text layer. */
1217 grub_video_get_active_render_target (&old_target);
1218 grub_video_set_active_render_target (text_layer);
1219 virtual_screen.bg_color = grub_video_map_rgba_color (default_bg_color);
1220 grub_video_set_active_render_target (old_target);
1221 virtual_screen.bg_color_display =
1222 grub_video_map_rgba_color (default_bg_color);
1223 blend_text_bg = 1;
1225 /* Mark whole screen as dirty. */
1226 dirty_region_add (0, 0, window.width, window.height);
1228 return GRUB_ERR_NONE;
1231 static struct grub_term_output grub_video_term =
1233 .name = "gfxterm",
1234 .init = grub_gfxterm_term_init,
1235 .fini = grub_gfxterm_term_fini,
1236 .putchar = grub_gfxterm_putchar,
1237 .getcharwidth = grub_gfxterm_getcharwidth,
1238 .getwh = grub_virtual_screen_getwh,
1239 .getxy = grub_virtual_screen_getxy,
1240 .gotoxy = grub_gfxterm_gotoxy,
1241 .cls = grub_gfxterm_cls,
1242 .setcolorstate = grub_virtual_screen_setcolorstate,
1243 .setcursor = grub_gfxterm_setcursor,
1244 .refresh = grub_gfxterm_refresh,
1245 .fullscreen = grub_gfxterm_fullscreen,
1246 .flags = GRUB_TERM_CODE_TYPE_VISUAL_GLYPHS,
1247 .normal_color = GRUB_TERM_DEFAULT_NORMAL_COLOR,
1248 .highlight_color = GRUB_TERM_DEFAULT_HIGHLIGHT_COLOR,
1249 .next = 0
1252 static grub_extcmd_t background_image_cmd_handle;
1253 static grub_command_t background_color_cmd_handle;
1255 #if defined (GRUB_MACHINE_MIPS_LOONGSON) || defined (GRUB_MACHINE_MIPS_QEMU_MIPS)
1256 void grub_gfxterm_init (void)
1257 #else
1258 GRUB_MOD_INIT(gfxterm)
1259 #endif
1261 grub_term_register_output ("gfxterm", &grub_video_term);
1262 background_image_cmd_handle =
1263 grub_register_extcmd ("background_image",
1264 grub_gfxterm_background_image_cmd, 0,
1265 N_("[-m (stretch|normal)] FILE"),
1266 N_("Load background image for active terminal."),
1267 background_image_cmd_options);
1268 background_color_cmd_handle =
1269 grub_register_command ("background_color",
1270 grub_gfxterm_background_color_cmd,
1271 N_("COLOR"),
1272 N_("Set background color for active terminal."));
1275 #if defined (GRUB_MACHINE_MIPS_LOONGSON) || defined (GRUB_MACHINE_MIPS_QEMU_MIPS)
1276 void grub_gfxterm_fini (void)
1277 #else
1278 GRUB_MOD_FINI(gfxterm)
1279 #endif
1281 grub_unregister_command (background_color_cmd_handle);
1282 grub_unregister_extcmd (background_image_cmd_handle);
1283 grub_term_unregister_output (&grub_video_term);