tpm2_key_protector: Enable build for powerpc_ieee1275
[grub.git] / grub-core / term / gfxterm.c
blob3c468f459c034261c0101d6e139746f60ce0baca
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/i18n.h>
33 GRUB_MOD_LICENSE ("GPLv3+");
35 #define DEFAULT_VIDEO_MODE "auto"
36 #define DEFAULT_BORDER_WIDTH 10
38 #define DEFAULT_STANDARD_COLOR 0x07
40 struct grub_dirty_region
42 int top_left_x;
43 int top_left_y;
44 int bottom_right_x;
45 int bottom_right_y;
48 struct grub_colored_char
50 /* An Unicode codepoint. */
51 struct grub_unicode_glyph code;
53 /* Color values. */
54 grub_video_color_t fg_color;
55 grub_video_color_t bg_color;
58 struct grub_virtual_screen
60 /* Dimensions of the virtual screen in pixels. */
61 unsigned int width;
62 unsigned int height;
64 /* Offset in the display in pixels. */
65 unsigned int offset_x;
66 unsigned int offset_y;
68 /* TTY Character sizes in pixes. */
69 unsigned int normal_char_width;
70 unsigned int normal_char_height;
72 /* Virtual screen TTY size in characters. */
73 unsigned int columns;
74 unsigned int rows;
76 /* Current cursor location in characters. */
77 unsigned int cursor_x;
78 unsigned int cursor_y;
80 /* Current cursor state. */
81 int cursor_state;
83 /* Font settings. */
84 grub_font_t font;
86 /* Terminal color settings. */
87 grub_uint8_t standard_color_setting;
88 grub_uint8_t term_color;
90 /* Color settings. */
91 grub_video_color_t fg_color;
92 grub_video_color_t bg_color;
93 grub_video_color_t bg_color_display;
95 /* Text buffer for virtual screen. Contains (columns * rows) number
96 of entries. */
97 struct grub_colored_char *text_buffer;
99 int total_scroll;
101 int functional;
104 struct grub_gfxterm_window
106 unsigned x;
107 unsigned y;
108 unsigned width;
109 unsigned height;
110 int double_repaint;
113 static struct grub_video_render_target *render_target;
114 void (*grub_gfxterm_decorator_hook) (void) = NULL;
115 static struct grub_gfxterm_window window;
116 static struct grub_virtual_screen virtual_screen;
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 struct grub_gfxterm_background grub_gfxterm_background;
126 static struct grub_dirty_region dirty_region;
128 static void dirty_region_reset (void);
130 static int dirty_region_is_empty (void);
132 static void dirty_region_add (int x, int y,
133 unsigned int width, unsigned int height);
135 static unsigned int calculate_normal_character_width (grub_font_t font);
137 static unsigned char calculate_character_width (struct grub_font_glyph *glyph);
139 static void grub_gfxterm_refresh (struct grub_term_output *term __attribute__ ((unused)));
141 static grub_size_t
142 grub_gfxterm_getcharwidth (struct grub_term_output *term __attribute__ ((unused)),
143 const struct grub_unicode_glyph *c);
145 static void
146 set_term_color (grub_uint8_t term_color)
148 struct grub_video_render_target *old_target;
150 /* Save previous target and switch to text layer. */
151 grub_video_get_active_render_target (&old_target);
152 grub_video_set_active_render_target (text_layer);
154 /* Map terminal color to text layer compatible video colors. */
155 virtual_screen.fg_color = grub_video_map_color(term_color & 0x0f);
157 /* Special case: use black as transparent color. */
158 if (((term_color >> 4) & 0x0f) == 0)
160 virtual_screen.bg_color = grub_video_map_rgba(0, 0, 0, 0);
162 else
164 virtual_screen.bg_color = grub_video_map_color((term_color >> 4) & 0x0f);
167 /* Restore previous target. */
168 grub_video_set_active_render_target (old_target);
171 static void
172 clear_char (struct grub_colored_char *c)
174 grub_unicode_destroy_glyph (&c->code);
175 grub_unicode_set_glyph_from_code (&c->code, ' ');
176 c->fg_color = virtual_screen.fg_color;
177 c->bg_color = virtual_screen.bg_color;
180 static void
181 grub_virtual_screen_free (void)
183 virtual_screen.functional = 0;
185 /* If virtual screen has been allocated, free it. */
186 if (virtual_screen.text_buffer != 0)
188 unsigned i;
189 for (i = 0;
190 i < virtual_screen.columns * virtual_screen.rows;
191 i++)
192 grub_unicode_destroy_glyph (&virtual_screen.text_buffer[i].code);
193 grub_free (virtual_screen.text_buffer);
196 /* Reset virtual screen data. */
197 grub_memset (&virtual_screen, 0, sizeof (virtual_screen));
199 /* Free render targets. */
200 grub_video_delete_render_target (text_layer);
201 text_layer = 0;
204 static grub_err_t
205 grub_virtual_screen_setup (unsigned int x, unsigned int y,
206 unsigned int width, unsigned int height,
207 grub_font_t font)
209 unsigned int i;
211 /* Free old virtual screen. */
212 grub_virtual_screen_free ();
214 /* Initialize with default data. */
215 virtual_screen.font = font;
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 if (virtual_screen.normal_char_height == 0)
225 virtual_screen.normal_char_height = 16;
226 virtual_screen.cursor_x = 0;
227 virtual_screen.cursor_y = 0;
228 virtual_screen.cursor_state = 1;
229 virtual_screen.total_scroll = 0;
231 /* Calculate size of text buffer. */
232 virtual_screen.columns = virtual_screen.width / virtual_screen.normal_char_width;
233 virtual_screen.rows = virtual_screen.height / virtual_screen.normal_char_height;
236 * There must be a minimum number of rows and columns for the screen to
237 * make sense. Arbitrarily pick half of 80x24. If either dimensions is 0
238 * we would allocate 0 bytes for the text_buffer.
240 if (virtual_screen.columns < 40 || virtual_screen.rows < 12)
241 return grub_error (GRUB_ERR_BAD_FONT,
242 "font: glyphs too large to fit on screen");
244 /* Allocate memory for text buffer. */
245 virtual_screen.text_buffer =
246 (struct grub_colored_char *) grub_malloc (virtual_screen.columns
247 * virtual_screen.rows
248 * sizeof (*virtual_screen.text_buffer));
249 if (grub_errno != GRUB_ERR_NONE)
250 return grub_errno;
252 /* Create new render target for text layer. */
253 grub_video_create_render_target (&text_layer,
254 virtual_screen.width,
255 virtual_screen.height,
256 GRUB_VIDEO_MODE_TYPE_INDEX_COLOR
257 | GRUB_VIDEO_MODE_TYPE_ALPHA);
258 if (grub_errno != GRUB_ERR_NONE)
259 return grub_errno;
261 /* As we want to have colors compatible with rendering target,
262 we can only have those after mode is initialized. */
263 grub_video_set_active_render_target (text_layer);
265 virtual_screen.standard_color_setting = DEFAULT_STANDARD_COLOR;
267 virtual_screen.term_color = virtual_screen.standard_color_setting;
269 set_term_color (virtual_screen.term_color);
271 grub_video_set_active_render_target (render_target);
273 virtual_screen.bg_color_display =
274 grub_video_map_rgba_color (grub_gfxterm_background.default_bg_color);
276 /* Clear out text buffer. */
277 for (i = 0; i < virtual_screen.columns * virtual_screen.rows; i++)
279 virtual_screen.text_buffer[i].code.ncomb = 0;
280 clear_char (&(virtual_screen.text_buffer[i]));
282 if (grub_errno)
283 return grub_errno;
285 virtual_screen.functional = 1;
287 return GRUB_ERR_NONE;
290 void
291 grub_gfxterm_schedule_repaint (void)
293 repaint_scheduled = 1;
296 grub_err_t
297 grub_gfxterm_set_window (struct grub_video_render_target *target,
298 int x, int y, int width, int height,
299 int double_repaint,
300 grub_font_t font, int border_width)
302 /* Clean up any prior instance. */
303 destroy_window ();
305 /* Set the render target. */
306 render_target = target;
308 /* Create virtual screen. */
309 if (grub_virtual_screen_setup (border_width, border_width,
310 width - 2 * border_width,
311 height - 2 * border_width,
312 font)
313 != GRUB_ERR_NONE)
315 return grub_errno;
318 /* Set window bounds. */
319 window.x = x;
320 window.y = y;
321 window.width = width;
322 window.height = height;
323 window.double_repaint = double_repaint;
325 dirty_region_reset ();
326 grub_gfxterm_schedule_repaint ();
328 return grub_errno;
331 static grub_err_t
332 grub_gfxterm_fullscreen (void)
334 const char *font_name;
335 struct grub_video_mode_info mode_info;
336 grub_video_color_t color;
337 grub_err_t err;
338 int double_redraw;
339 grub_font_t font;
341 err = grub_video_get_info (&mode_info);
342 /* Figure out what mode we ended up. */
343 if (err)
344 return err;
346 grub_video_set_active_render_target (GRUB_VIDEO_RENDER_TARGET_DISPLAY);
348 double_redraw = mode_info.mode_type & GRUB_VIDEO_MODE_TYPE_DOUBLE_BUFFERED
349 && !(mode_info.mode_type & GRUB_VIDEO_MODE_TYPE_UPDATING_SWAP);
351 /* Make sure screen is set to the default background color. */
352 color = grub_video_map_rgba_color (grub_gfxterm_background.default_bg_color);
353 grub_video_fill_rect (color, 0, 0, mode_info.width, mode_info.height);
354 if (double_redraw)
356 grub_video_swap_buffers ();
357 grub_video_fill_rect (color, 0, 0, mode_info.width, mode_info.height);
360 /* Select the font to use. */
361 font_name = grub_env_get ("gfxterm_font");
362 if (! font_name)
363 font_name = ""; /* Allow fallback to any font. */
365 font = grub_font_get (font_name);
366 if (!font)
367 return grub_error (GRUB_ERR_BAD_FONT, "no font loaded");
369 grub_gfxterm_decorator_hook = NULL;
371 return grub_gfxterm_set_window (GRUB_VIDEO_RENDER_TARGET_DISPLAY,
372 0, 0, mode_info.width, mode_info.height,
373 double_redraw,
374 font, DEFAULT_BORDER_WIDTH);
377 static grub_err_t
378 grub_gfxterm_term_init (struct grub_term_output *term __attribute__ ((unused)))
380 char *tmp;
381 grub_err_t err;
382 const char *modevar;
384 /* Parse gfxmode environment variable if set. */
385 modevar = grub_env_get ("gfxmode");
386 if (! modevar || *modevar == 0)
387 err = grub_video_set_mode (DEFAULT_VIDEO_MODE,
388 GRUB_VIDEO_MODE_TYPE_PURE_TEXT, 0);
389 else
391 tmp = grub_xasprintf ("%s;" DEFAULT_VIDEO_MODE, modevar);
392 if (!tmp)
393 return grub_errno;
394 err = grub_video_set_mode (tmp, GRUB_VIDEO_MODE_TYPE_PURE_TEXT, 0);
395 grub_free (tmp);
398 if (err)
399 return err;
401 err = grub_gfxterm_fullscreen ();
402 if (err)
403 grub_video_restore ();
405 return err;
408 static void
409 destroy_window (void)
411 grub_virtual_screen_free ();
414 static grub_err_t
415 grub_gfxterm_term_fini (struct grub_term_output *term __attribute__ ((unused)))
417 unsigned i;
418 destroy_window ();
419 grub_video_restore ();
421 for (i = 0; i < virtual_screen.columns * virtual_screen.rows; i++)
423 grub_unicode_destroy_glyph (&virtual_screen.text_buffer[i].code);
424 virtual_screen.text_buffer[i].code.ncomb = 0;
425 virtual_screen.text_buffer[i].code.base = 0;
428 /* Clear error state. */
429 grub_errno = GRUB_ERR_NONE;
430 return GRUB_ERR_NONE;
433 static void
434 redraw_screen_rect (unsigned int x, unsigned int y,
435 unsigned int width, unsigned int height)
437 grub_video_color_t color;
438 grub_video_rect_t saved_view;
440 grub_video_set_active_render_target (render_target);
441 /* Save viewport and set it to our window. */
442 grub_video_get_viewport ((unsigned *) &saved_view.x,
443 (unsigned *) &saved_view.y,
444 (unsigned *) &saved_view.width,
445 (unsigned *) &saved_view.height);
446 grub_video_set_viewport (window.x, window.y, window.width, window.height);
448 if (grub_gfxterm_background.bitmap)
450 /* Render bitmap as background. */
451 grub_video_blit_bitmap (grub_gfxterm_background.bitmap,
452 GRUB_VIDEO_BLIT_REPLACE, x, y,
453 x, y,
454 width, height);
456 /* If bitmap is smaller than requested blit area, use background
457 color. */
458 color = virtual_screen.bg_color_display;
460 /* Fill right side of the bitmap if needed. */
461 if ((x + width >= grub_gfxterm_background.bitmap->mode_info.width)
462 && (y < grub_gfxterm_background.bitmap->mode_info.height))
464 int w = (x + width) - grub_gfxterm_background.bitmap->mode_info.width;
465 int h = height;
466 unsigned int tx = x;
468 if (y + height >= grub_gfxterm_background.bitmap->mode_info.height)
470 h = grub_gfxterm_background.bitmap->mode_info.height - y;
473 if (grub_gfxterm_background.bitmap->mode_info.width > tx)
475 tx = grub_gfxterm_background.bitmap->mode_info.width;
478 /* Render background layer. */
479 grub_video_fill_rect (color, tx, y, w, h);
482 /* Fill bottom side of the bitmap if needed. */
483 if (y + height >= grub_gfxterm_background.bitmap->mode_info.height)
485 int h = (y + height) - grub_gfxterm_background.bitmap->mode_info.height;
486 unsigned int ty = y;
488 if (grub_gfxterm_background.bitmap->mode_info.height > ty)
490 ty = grub_gfxterm_background.bitmap->mode_info.height;
493 /* Render background layer. */
494 grub_video_fill_rect (color, x, ty, width, h);
497 else
499 /* Render background layer. */
500 color = virtual_screen.bg_color_display;
501 grub_video_fill_rect (color, x, y, width, height);
504 if (grub_gfxterm_background.blend_text_bg)
505 /* Render text layer as blended. */
506 grub_video_blit_render_target (text_layer, GRUB_VIDEO_BLIT_BLEND, x, y,
507 x - virtual_screen.offset_x,
508 y - virtual_screen.offset_y,
509 width, height);
510 else
511 /* Render text layer as replaced (to get texts background color). */
512 grub_video_blit_render_target (text_layer, GRUB_VIDEO_BLIT_REPLACE, x, y,
513 x - virtual_screen.offset_x,
514 y - virtual_screen.offset_y,
515 width, height);
517 /* Restore saved viewport. */
518 grub_video_set_viewport (saved_view.x, saved_view.y,
519 saved_view.width, saved_view.height);
520 grub_video_set_active_render_target (render_target);
523 static void
524 dirty_region_reset (void)
526 dirty_region.top_left_x = -1;
527 dirty_region.top_left_y = -1;
528 dirty_region.bottom_right_x = -1;
529 dirty_region.bottom_right_y = -1;
530 repaint_was_scheduled = 0;
533 static int
534 dirty_region_is_empty (void)
536 if ((dirty_region.top_left_x == -1)
537 || (dirty_region.top_left_y == -1)
538 || (dirty_region.bottom_right_x == -1)
539 || (dirty_region.bottom_right_y == -1))
540 return 1;
541 return 0;
544 static void
545 dirty_region_add_real (int x, int y, unsigned int width, unsigned int height)
547 if (dirty_region_is_empty ())
549 dirty_region.top_left_x = x;
550 dirty_region.top_left_y = y;
551 dirty_region.bottom_right_x = x + width - 1;
552 dirty_region.bottom_right_y = y + height - 1;
554 else
556 if (x < dirty_region.top_left_x)
557 dirty_region.top_left_x = x;
558 if (y < dirty_region.top_left_y)
559 dirty_region.top_left_y = y;
560 if ((x + (int)width - 1) > dirty_region.bottom_right_x)
561 dirty_region.bottom_right_x = x + width - 1;
562 if ((y + (int)height - 1) > dirty_region.bottom_right_y)
563 dirty_region.bottom_right_y = y + height - 1;
567 static void
568 dirty_region_add (int x, int y, unsigned int width, unsigned int height)
570 if ((width == 0) || (height == 0))
571 return;
573 if (repaint_scheduled)
575 dirty_region_add_real (0, 0,
576 window.width, window.height);
577 repaint_scheduled = 0;
578 repaint_was_scheduled = 1;
580 dirty_region_add_real (x, y, width, height);
583 static void
584 dirty_region_add_virtualscreen (void)
586 /* Mark virtual screen as dirty. */
587 dirty_region_add (virtual_screen.offset_x, virtual_screen.offset_y,
588 virtual_screen.width, virtual_screen.height);
592 static void
593 dirty_region_redraw (void)
595 int x;
596 int y;
597 int width;
598 int height;
600 if (dirty_region_is_empty ())
601 return;
603 x = dirty_region.top_left_x;
604 y = dirty_region.top_left_y;
606 width = dirty_region.bottom_right_x - x + 1;
607 height = dirty_region.bottom_right_y - y + 1;
609 if (repaint_was_scheduled && grub_gfxterm_decorator_hook)
610 grub_gfxterm_decorator_hook ();
612 redraw_screen_rect (x, y, width, height);
615 static inline void
616 paint_char (unsigned cx, unsigned cy)
618 struct grub_colored_char *p;
619 struct grub_font_glyph *glyph;
620 grub_video_color_t color;
621 grub_video_color_t bgcolor;
622 unsigned int x;
623 unsigned int y;
624 int ascent;
625 unsigned int height;
626 unsigned int width;
628 if (cy + virtual_screen.total_scroll >= virtual_screen.rows)
629 return;
631 /* Find out active character. */
632 p = (virtual_screen.text_buffer
633 + cx + (cy * virtual_screen.columns));
635 if (!p->code.base)
636 return;
638 /* Get glyph for character. */
639 glyph = grub_font_construct_glyph (virtual_screen.font, &p->code);
640 if (!glyph)
642 grub_errno = GRUB_ERR_NONE;
643 return;
645 ascent = grub_font_get_ascent (virtual_screen.font);
647 width = virtual_screen.normal_char_width * calculate_character_width(glyph);
648 height = virtual_screen.normal_char_height;
650 color = p->fg_color;
651 bgcolor = p->bg_color;
653 x = cx * virtual_screen.normal_char_width;
654 y = (cy + virtual_screen.total_scroll) * virtual_screen.normal_char_height;
656 /* Render glyph to text layer. */
657 grub_video_set_active_render_target (text_layer);
658 grub_video_fill_rect (bgcolor, x, y, width, height);
659 grub_font_draw_glyph (glyph, color, x, y + ascent);
660 grub_video_set_active_render_target (render_target);
662 /* Mark character to be drawn. */
663 dirty_region_add (virtual_screen.offset_x + x, virtual_screen.offset_y + y,
664 width, height);
667 static inline void
668 write_char (void)
670 paint_char (virtual_screen.cursor_x, virtual_screen.cursor_y);
673 static inline void
674 draw_cursor (int show)
676 unsigned int x;
677 unsigned int y;
678 unsigned int width;
679 unsigned int height;
680 unsigned int ascent;
681 grub_video_color_t color;
683 write_char ();
685 if (!show)
686 return;
688 if (virtual_screen.cursor_y + virtual_screen.total_scroll
689 >= virtual_screen.rows)
690 return;
692 /* Ensure that cursor doesn't go outside of character box. */
693 ascent = grub_font_get_ascent(virtual_screen.font);
694 if (ascent > virtual_screen.normal_char_height - 2)
695 ascent = virtual_screen.normal_char_height - 2;
697 /* Determine cursor properties and position on text layer. */
698 x = virtual_screen.cursor_x * virtual_screen.normal_char_width;
699 width = virtual_screen.normal_char_width;
700 color = virtual_screen.fg_color;
701 y = ((virtual_screen.cursor_y + virtual_screen.total_scroll)
702 * virtual_screen.normal_char_height
703 + ascent);
704 height = 2;
706 /* Render cursor to text layer. */
707 grub_video_set_active_render_target (text_layer);
708 grub_video_fill_rect (color, x, y, width, height);
709 grub_video_set_active_render_target (render_target);
711 /* Mark cursor to be redrawn. */
712 dirty_region_add (virtual_screen.offset_x + x,
713 virtual_screen.offset_y + y,
714 width, height);
717 static void
718 real_scroll (void)
720 unsigned int i, j, was_scroll;
721 grub_video_color_t color;
723 if (!virtual_screen.total_scroll)
724 return;
726 /* If we have bitmap, re-draw screen, otherwise scroll physical screen too. */
727 if (grub_gfxterm_background.bitmap)
729 /* Scroll physical screen. */
730 grub_video_set_active_render_target (text_layer);
731 color = virtual_screen.bg_color;
732 grub_video_scroll (color, 0, -virtual_screen.normal_char_height
733 * virtual_screen.total_scroll);
735 /* Mark virtual screen to be redrawn. */
736 dirty_region_add_virtualscreen ();
738 else
740 grub_video_rect_t saved_view;
742 /* Remove cursor. */
743 draw_cursor (0);
745 grub_video_set_active_render_target (render_target);
747 i = window.double_repaint ? 2 : 1;
749 color = virtual_screen.bg_color_display;
751 while (i--)
753 /* Save viewport and set it to our window. */
754 grub_video_get_viewport ((unsigned *) &saved_view.x,
755 (unsigned *) &saved_view.y,
756 (unsigned *) &saved_view.width,
757 (unsigned *) &saved_view.height);
759 grub_video_set_viewport (window.x, window.y, window.width,
760 window.height);
762 /* Clear new border area. */
763 grub_video_fill_rect (color,
764 virtual_screen.offset_x,
765 virtual_screen.offset_y,
766 virtual_screen.width,
767 virtual_screen.normal_char_height
768 * virtual_screen.total_scroll);
770 grub_video_set_active_render_target (render_target);
771 dirty_region_redraw ();
773 /* Scroll physical screen. */
774 grub_video_scroll (color, 0, -virtual_screen.normal_char_height
775 * virtual_screen.total_scroll);
777 /* Restore saved viewport. */
778 grub_video_set_viewport (saved_view.x, saved_view.y,
779 saved_view.width, saved_view.height);
781 if (i)
782 grub_video_swap_buffers ();
784 dirty_region_reset ();
786 /* Scroll physical screen. */
787 grub_video_set_active_render_target (text_layer);
788 color = virtual_screen.bg_color;
789 grub_video_scroll (color, 0, -virtual_screen.normal_char_height
790 * virtual_screen.total_scroll);
792 grub_video_set_active_render_target (render_target);
796 was_scroll = virtual_screen.total_scroll;
797 virtual_screen.total_scroll = 0;
799 if (was_scroll > virtual_screen.rows)
800 was_scroll = virtual_screen.rows;
802 /* Draw shadow part. */
803 for (i = virtual_screen.rows - was_scroll;
804 i < virtual_screen.rows; i++)
805 for (j = 0; j < virtual_screen.columns; j++)
806 paint_char (j, i);
808 /* Draw cursor if visible. */
809 if (virtual_screen.cursor_state)
810 draw_cursor (1);
813 static void
814 scroll_up (void)
816 unsigned int i;
818 /* Clear first line in text buffer. */
819 for (i = 0; i < virtual_screen.columns; i++)
820 grub_unicode_destroy_glyph (&virtual_screen.text_buffer[i].code);
822 /* Scroll text buffer with one line to up. */
823 grub_memmove (virtual_screen.text_buffer,
824 virtual_screen.text_buffer + virtual_screen.columns,
825 sizeof (*virtual_screen.text_buffer)
826 * virtual_screen.columns
827 * (virtual_screen.rows - 1));
829 /* Clear last line in text buffer. */
830 for (i = virtual_screen.columns * (virtual_screen.rows - 1);
831 i < virtual_screen.columns * virtual_screen.rows;
832 i++)
833 clear_char (&(virtual_screen.text_buffer[i]));
835 virtual_screen.total_scroll++;
838 static void
839 grub_gfxterm_putchar (struct grub_term_output *term,
840 const struct grub_unicode_glyph *c)
842 if (!virtual_screen.functional)
843 return;
845 if (c->base == '\a')
846 /* FIXME */
847 return;
849 /* Erase current cursor, if any. */
850 if (virtual_screen.cursor_state)
851 draw_cursor (0);
853 if (c->base == '\b' || c->base == '\n' || c->base == '\r')
855 switch (c->base)
857 case '\b':
858 if (virtual_screen.cursor_x > 0)
859 virtual_screen.cursor_x--;
860 break;
862 case '\n':
863 if (virtual_screen.cursor_y >= virtual_screen.rows - 1)
864 scroll_up ();
865 else
866 virtual_screen.cursor_y++;
867 break;
869 case '\r':
870 virtual_screen.cursor_x = 0;
871 break;
874 else
876 struct grub_colored_char *p;
877 unsigned char char_width;
879 /* Calculate actual character width for glyph. This is number of
880 times of normal_font_width. */
881 char_width = grub_gfxterm_getcharwidth (term, c);
883 /* If we are about to exceed line length, wrap to next line. */
884 if (virtual_screen.cursor_x + char_width > virtual_screen.columns)
886 if (virtual_screen.cursor_y >= virtual_screen.rows - 1)
887 scroll_up ();
888 else
889 virtual_screen.cursor_y++;
892 /* Find position on virtual screen, and fill information. */
893 p = (virtual_screen.text_buffer +
894 virtual_screen.cursor_x +
895 virtual_screen.cursor_y * virtual_screen.columns);
896 grub_unicode_destroy_glyph (&p->code);
897 grub_unicode_set_glyph (&p->code, c);
898 grub_errno = GRUB_ERR_NONE;
899 p->fg_color = virtual_screen.fg_color;
900 p->bg_color = virtual_screen.bg_color;
902 /* If we have large glyph, add fixup info. */
903 if (char_width > 1)
905 unsigned i;
907 for (i = 1; i < char_width && p + i <
908 virtual_screen.text_buffer + virtual_screen.columns
909 * virtual_screen.rows; i++)
911 grub_unicode_destroy_glyph (&p[i].code);
912 p[i].code.base = 0;
916 /* Draw glyph. */
917 write_char ();
919 /* Make sure we scroll screen when needed and wrap line correctly. */
920 virtual_screen.cursor_x += char_width;
921 if (virtual_screen.cursor_x >= virtual_screen.columns)
923 virtual_screen.cursor_x = 0;
925 if (virtual_screen.cursor_y >= virtual_screen.rows - 1)
926 scroll_up ();
927 else
928 virtual_screen.cursor_y++;
932 /* Redraw cursor if it should be visible. */
933 /* Note: This will redraw the character as well, which means that the
934 above call to write_char is redundant when the cursor is showing. */
935 if (virtual_screen.cursor_state)
936 draw_cursor (1);
939 /* Use ASCII characters to determine normal character width. */
940 static unsigned int
941 calculate_normal_character_width (grub_font_t font)
943 struct grub_font_glyph *glyph;
944 unsigned int width = 0;
945 unsigned int i;
947 /* Get properties of every printable ASCII character. */
948 for (i = 32; i < 127; i++)
950 glyph = grub_font_get_glyph (font, i);
952 /* Skip unknown characters. Should never happen on normal conditions. */
953 if (! glyph)
954 continue;
956 if (glyph->device_width > width)
957 width = glyph->device_width;
959 if (!width)
960 return 8;
962 return width;
965 static unsigned char
966 calculate_character_width (struct grub_font_glyph *glyph)
968 if (! glyph || glyph->device_width == 0)
969 return 1;
971 return (glyph->device_width
972 + (virtual_screen.normal_char_width - 1))
973 / virtual_screen.normal_char_width;
976 static grub_size_t
977 grub_gfxterm_getcharwidth (struct grub_term_output *term __attribute__ ((unused)),
978 const struct grub_unicode_glyph *c)
980 int dev_width;
981 dev_width = grub_font_get_constructed_device_width (virtual_screen.font, c);
983 if (dev_width == 0)
984 return 1;
986 return (dev_width + (virtual_screen.normal_char_width - 1))
987 / virtual_screen.normal_char_width;
990 static struct grub_term_coordinate
991 grub_virtual_screen_getwh (struct grub_term_output *term __attribute__ ((unused)))
993 return (struct grub_term_coordinate) { virtual_screen.columns, virtual_screen.rows };
996 static struct grub_term_coordinate
997 grub_virtual_screen_getxy (struct grub_term_output *term __attribute__ ((unused)))
999 return (struct grub_term_coordinate) { virtual_screen.cursor_x, virtual_screen.cursor_y };
1002 static void
1003 grub_gfxterm_gotoxy (struct grub_term_output *term __attribute__ ((unused)),
1004 struct grub_term_coordinate pos)
1006 if (pos.x >= virtual_screen.columns)
1007 pos.x = virtual_screen.columns - 1;
1009 if (pos.y >= virtual_screen.rows)
1010 pos.y = virtual_screen.rows - 1;
1012 /* Erase current cursor, if any. */
1013 if (virtual_screen.cursor_state)
1014 draw_cursor (0);
1016 virtual_screen.cursor_x = pos.x;
1017 virtual_screen.cursor_y = pos.y;
1019 /* Draw cursor if visible. */
1020 if (virtual_screen.cursor_state)
1021 draw_cursor (1);
1024 static void
1025 grub_virtual_screen_cls (struct grub_term_output *term __attribute__ ((unused)))
1027 grub_uint32_t i;
1029 for (i = 0; i < virtual_screen.columns * virtual_screen.rows; i++)
1030 clear_char (&(virtual_screen.text_buffer[i]));
1032 virtual_screen.cursor_x = virtual_screen.cursor_y = 0;
1035 static void
1036 grub_gfxterm_cls (struct grub_term_output *term)
1038 grub_video_color_t color;
1040 /* Clear virtual screen. */
1041 grub_virtual_screen_cls (term);
1043 /* Clear text layer. */
1044 grub_video_set_active_render_target (text_layer);
1045 color = virtual_screen.bg_color;
1046 grub_video_fill_rect (color, 0, 0,
1047 virtual_screen.width, virtual_screen.height);
1048 grub_video_set_active_render_target (render_target);
1050 /* Mark virtual screen to be redrawn. */
1051 dirty_region_add_virtualscreen ();
1053 grub_gfxterm_refresh (term);
1056 static void
1057 grub_virtual_screen_setcolorstate (struct grub_term_output *term __attribute__ ((unused)),
1058 grub_term_color_state state)
1060 switch (state)
1062 case GRUB_TERM_COLOR_STANDARD:
1063 virtual_screen.term_color = virtual_screen.standard_color_setting;
1064 break;
1066 case GRUB_TERM_COLOR_NORMAL:
1067 virtual_screen.term_color = grub_term_normal_color;
1068 break;
1070 case GRUB_TERM_COLOR_HIGHLIGHT:
1071 virtual_screen.term_color = grub_term_highlight_color;
1072 break;
1074 default:
1075 break;
1078 /* Change color to virtual terminal. */
1079 set_term_color (virtual_screen.term_color);
1082 static void
1083 grub_gfxterm_setcursor (struct grub_term_output *term __attribute__ ((unused)),
1084 int on)
1086 if (virtual_screen.cursor_state != on)
1088 if (virtual_screen.cursor_state)
1089 draw_cursor (0);
1090 else
1091 draw_cursor (1);
1093 virtual_screen.cursor_state = on;
1097 static void
1098 grub_gfxterm_refresh (struct grub_term_output *term __attribute__ ((unused)))
1100 real_scroll ();
1102 /* Redraw only changed regions. */
1103 dirty_region_redraw ();
1105 grub_video_swap_buffers ();
1107 if (window.double_repaint)
1108 dirty_region_redraw ();
1109 dirty_region_reset ();
1112 static struct grub_term_output grub_video_term =
1114 .name = "gfxterm",
1115 .init = grub_gfxterm_term_init,
1116 .fini = grub_gfxterm_term_fini,
1117 .putchar = grub_gfxterm_putchar,
1118 .getcharwidth = grub_gfxterm_getcharwidth,
1119 .getwh = grub_virtual_screen_getwh,
1120 .getxy = grub_virtual_screen_getxy,
1121 .gotoxy = grub_gfxterm_gotoxy,
1122 .cls = grub_gfxterm_cls,
1123 .setcolorstate = grub_virtual_screen_setcolorstate,
1124 .setcursor = grub_gfxterm_setcursor,
1125 .refresh = grub_gfxterm_refresh,
1126 .fullscreen = grub_gfxterm_fullscreen,
1127 .flags = GRUB_TERM_CODE_TYPE_VISUAL_GLYPHS,
1128 .progress_update_divisor = GRUB_PROGRESS_SLOW,
1129 .next = 0
1132 void
1133 grub_gfxterm_video_update_color (void)
1135 struct grub_video_render_target *old_target;
1137 grub_video_get_active_render_target (&old_target);
1138 grub_video_set_active_render_target (text_layer);
1139 virtual_screen.bg_color = grub_video_map_rgba_color (grub_gfxterm_background.default_bg_color);
1140 grub_video_set_active_render_target (old_target);
1141 virtual_screen.bg_color_display =
1142 grub_video_map_rgba_color (grub_gfxterm_background.default_bg_color);
1145 void
1146 grub_gfxterm_get_dimensions (unsigned *width, unsigned *height)
1148 *width = window.width;
1149 *height = window.height;
1152 GRUB_MOD_INIT(gfxterm)
1154 grub_term_register_output ("gfxterm", &grub_video_term);
1157 GRUB_MOD_FINI(gfxterm)
1159 grub_term_unregister_output (&grub_video_term);