ignore grub-dumpbios
[grub2/phcoder.git] / term / gfxterm.c
blob4f74346b7bd7bfa5ca4da2a0f4945f9c958ede2c
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/bitmap.h>
28 #include <grub/command.h>
30 #define DEFAULT_VIDEO_WIDTH 640
31 #define DEFAULT_VIDEO_HEIGHT 480
32 #define DEFAULT_VIDEO_FLAGS 0
34 #define DEFAULT_BORDER_WIDTH 10
36 #define DEFAULT_STANDARD_COLOR 0x07
37 #define DEFAULT_NORMAL_COLOR 0x07
38 #define DEFAULT_HIGHLIGHT_COLOR 0x70
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 grub_uint32_t code;
53 /* Color values. */
54 grub_video_color_t fg_color;
55 grub_video_color_t bg_color;
57 /* The width of this character minus one. */
58 unsigned char width;
60 /* The column index of this character. */
61 unsigned char index;
64 struct grub_virtual_screen
66 /* Dimensions of the virtual screen in pixels. */
67 unsigned int width;
68 unsigned int height;
70 /* Offset in the display in pixels. */
71 unsigned int offset_x;
72 unsigned int offset_y;
74 /* TTY Character sizes in pixes. */
75 unsigned int normal_char_width;
76 unsigned int normal_char_height;
78 /* Virtual screen TTY size in characters. */
79 unsigned int columns;
80 unsigned int rows;
82 /* Current cursor location in characters. */
83 unsigned int cursor_x;
84 unsigned int cursor_y;
86 /* Current cursor state. */
87 int cursor_state;
89 /* Font settings. */
90 grub_font_t font;
92 /* Terminal color settings. */
93 grub_uint8_t standard_color_setting;
94 grub_uint8_t normal_color_setting;
95 grub_uint8_t highlight_color_setting;
96 grub_uint8_t term_color;
98 /* Color settings. */
99 grub_video_color_t fg_color;
100 grub_video_color_t bg_color;
102 /* Text buffer for virtual screen. Contains (columns * rows) number
103 of entries. */
104 struct grub_colored_char *text_buffer;
107 static struct grub_virtual_screen virtual_screen;
109 static grub_dl_t my_mod;
110 static struct grub_video_mode_info mode_info;
112 static struct grub_video_render_target *text_layer;
114 static unsigned int bitmap_width;
115 static unsigned int bitmap_height;
116 static struct grub_video_bitmap *bitmap;
118 static struct grub_dirty_region dirty_region;
120 static void dirty_region_reset (void);
122 static int dirty_region_is_empty (void);
124 static void dirty_region_add (int x, int y,
125 unsigned int width, unsigned int height);
127 static unsigned int calculate_normal_character_width (grub_font_t font);
129 static unsigned char calculate_character_width (struct grub_font_glyph *glyph);
131 static void
132 set_term_color (grub_uint8_t term_color)
134 struct grub_video_render_target *old_target;
136 /* Save previous target and switch to text layer. */
137 grub_video_get_active_render_target (&old_target);
138 grub_video_set_active_render_target (text_layer);
140 /* Map terminal color to text layer compatible video colors. */
141 virtual_screen.fg_color = grub_video_map_color(term_color & 0x0f);
143 /* Special case: use black as transparent color. */
144 if (((term_color >> 4) & 0x0f) == 0)
146 virtual_screen.bg_color = grub_video_map_rgba(0, 0, 0, 0);
148 else
150 virtual_screen.bg_color = grub_video_map_color((term_color >> 4) & 0x0f);
153 /* Restore previous target. */
154 grub_video_set_active_render_target (old_target);
157 static void
158 grub_virtual_screen_free (void)
160 /* If virtual screen has been allocated, free it. */
161 if (virtual_screen.text_buffer != 0)
162 grub_free (virtual_screen.text_buffer);
164 /* Reset virtual screen data. */
165 grub_memset (&virtual_screen, 0, sizeof (virtual_screen));
167 /* Free render targets. */
168 grub_video_delete_render_target (text_layer);
169 text_layer = 0;
172 static grub_err_t
173 grub_virtual_screen_setup (unsigned int x, unsigned int y,
174 unsigned int width, unsigned int height,
175 const char *font_name)
177 /* Free old virtual screen. */
178 grub_virtual_screen_free ();
180 /* Initialize with default data. */
181 virtual_screen.font = grub_font_get (font_name);
182 if (!virtual_screen.font)
183 return grub_error (GRUB_ERR_BAD_FONT,
184 "No font loaded.");
185 virtual_screen.width = width;
186 virtual_screen.height = height;
187 virtual_screen.offset_x = x;
188 virtual_screen.offset_y = y;
189 virtual_screen.normal_char_width =
190 calculate_normal_character_width (virtual_screen.font);
191 virtual_screen.normal_char_height =
192 grub_font_get_max_char_height (virtual_screen.font);
193 virtual_screen.cursor_x = 0;
194 virtual_screen.cursor_y = 0;
195 virtual_screen.cursor_state = 1;
197 /* Calculate size of text buffer. */
198 virtual_screen.columns = virtual_screen.width / virtual_screen.normal_char_width;
199 virtual_screen.rows = virtual_screen.height / virtual_screen.normal_char_height;
201 /* Allocate memory for text buffer. */
202 virtual_screen.text_buffer =
203 (struct grub_colored_char *) grub_malloc (virtual_screen.columns
204 * virtual_screen.rows
205 * sizeof (*virtual_screen.text_buffer));
206 if (grub_errno != GRUB_ERR_NONE)
207 return grub_errno;
209 /* Create new render target for text layer. */
210 grub_video_create_render_target (&text_layer,
211 virtual_screen.width,
212 virtual_screen.height,
213 GRUB_VIDEO_MODE_TYPE_RGB
214 | GRUB_VIDEO_MODE_TYPE_ALPHA);
215 if (grub_errno != GRUB_ERR_NONE)
216 return grub_errno;
218 /* As we want to have colors compatible with rendering target,
219 we can only have those after mode is initialized. */
220 grub_video_set_active_render_target (text_layer);
222 virtual_screen.standard_color_setting = DEFAULT_STANDARD_COLOR;
223 virtual_screen.normal_color_setting = DEFAULT_NORMAL_COLOR;
224 virtual_screen.highlight_color_setting = DEFAULT_HIGHLIGHT_COLOR;
226 virtual_screen.term_color = virtual_screen.normal_color_setting;
228 set_term_color (virtual_screen.term_color);
230 grub_video_set_active_render_target (GRUB_VIDEO_RENDER_TARGET_DISPLAY);
232 return grub_errno;
235 static grub_err_t
236 grub_gfxterm_init (void)
238 char *font_name;
239 char *modevar;
240 int width = DEFAULT_VIDEO_WIDTH;
241 int height = DEFAULT_VIDEO_HEIGHT;
242 int depth = -1;
243 int flags = DEFAULT_VIDEO_FLAGS;
244 grub_video_color_t color;
246 /* Select the font to use. */
247 font_name = grub_env_get ("gfxterm_font");
248 if (! font_name)
249 font_name = ""; /* Allow fallback to any font. */
251 /* Parse gfxmode environment variable if set. */
252 modevar = grub_env_get ("gfxmode");
253 if (modevar)
255 char *tmp;
256 char *next_mode;
257 char *current_mode;
258 char *param;
259 char *value;
260 int mode_found = 0;
262 /* Take copy of env.var. as we don't want to modify that. */
263 tmp = grub_strdup (modevar);
264 modevar = tmp;
266 if (grub_errno != GRUB_ERR_NONE)
267 return grub_errno;
269 /* Initialize next mode. */
270 next_mode = modevar;
272 /* Loop until all modes has been tested out. */
273 while (next_mode != NULL)
275 /* Use last next_mode as current mode. */
276 tmp = next_mode;
278 /* Reset video mode settings. */
279 width = DEFAULT_VIDEO_WIDTH;
280 height = DEFAULT_VIDEO_HEIGHT;
281 depth = -1;
282 flags = DEFAULT_VIDEO_FLAGS;
284 /* Save position of next mode and separate modes. */
285 next_mode = grub_strchr(next_mode, ';');
286 if (next_mode)
288 *next_mode = 0;
289 next_mode++;
292 /* Skip whitespace. */
293 while (grub_isspace (*tmp))
294 tmp++;
296 /* Initialize token holders. */
297 current_mode = tmp;
298 param = tmp;
299 value = NULL;
301 /* Parse <width>x<height>[x<depth>]*/
303 /* Find width value. */
304 value = param;
305 param = grub_strchr(param, 'x');
306 if (param == NULL)
308 grub_err_t rc;
310 /* First setup error message. */
311 rc = grub_error (GRUB_ERR_BAD_ARGUMENT,
312 "Invalid mode: %s\n",
313 current_mode);
315 /* Free memory before returning. */
316 grub_free (modevar);
318 return rc;
321 *param = 0;
322 param++;
324 width = grub_strtoul (value, 0, 0);
325 if (grub_errno != GRUB_ERR_NONE)
327 grub_err_t rc;
329 /* First setup error message. */
330 rc = grub_error (GRUB_ERR_BAD_ARGUMENT,
331 "Invalid mode: %s\n",
332 current_mode);
334 /* Free memory before returning. */
335 grub_free (modevar);
337 return rc;
340 /* Find height value. */
341 value = param;
342 param = grub_strchr(param, 'x');
343 if (param == NULL)
345 height = grub_strtoul (value, 0, 0);
346 if (grub_errno != GRUB_ERR_NONE)
348 grub_err_t rc;
350 /* First setup error message. */
351 rc = grub_error (GRUB_ERR_BAD_ARGUMENT,
352 "Invalid mode: %s\n",
353 current_mode);
355 /* Free memory before returning. */
356 grub_free (modevar);
358 return rc;
361 else
363 /* We have optional color depth value. */
364 *param = 0;
365 param++;
367 height = grub_strtoul (value, 0, 0);
368 if (grub_errno != GRUB_ERR_NONE)
370 grub_err_t rc;
372 /* First setup error message. */
373 rc = grub_error (GRUB_ERR_BAD_ARGUMENT,
374 "Invalid mode: %s\n",
375 current_mode);
377 /* Free memory before returning. */
378 grub_free (modevar);
380 return rc;
383 /* Convert color depth value. */
384 value = param;
385 depth = grub_strtoul (value, 0, 0);
386 if (grub_errno != GRUB_ERR_NONE)
388 grub_err_t rc;
390 /* First setup error message. */
391 rc = grub_error (GRUB_ERR_BAD_ARGUMENT,
392 "Invalid mode: %s\n",
393 current_mode);
395 /* Free memory before returning. */
396 grub_free (modevar);
398 return rc;
402 /* Try out video mode. */
404 /* If we have 8 or less bits, then assume that it is indexed color mode. */
405 if ((depth <= 8) && (depth != -1))
406 flags |= GRUB_VIDEO_MODE_TYPE_INDEX_COLOR;
408 /* We have more than 8 bits, then assume that it is RGB color mode. */
409 if (depth > 8)
410 flags |= GRUB_VIDEO_MODE_TYPE_RGB;
412 /* If user requested specific depth, forward that information to driver. */
413 if (depth != -1)
414 flags |= (depth << GRUB_VIDEO_MODE_TYPE_DEPTH_POS)
415 & GRUB_VIDEO_MODE_TYPE_DEPTH_MASK;
417 /* Try to initialize requested mode. Ignore any errors. */
418 grub_error_push ();
419 if (grub_video_setup (width, height, flags) != GRUB_ERR_NONE)
421 grub_error_pop ();
422 continue;
425 /* Figure out what mode we ended up. */
426 if (grub_video_get_info (&mode_info) != GRUB_ERR_NONE)
428 /* Couldn't get video mode info, restore old mode and continue to next one. */
429 grub_error_pop ();
431 grub_video_restore ();
432 continue;
435 /* Restore state of error stack. */
436 grub_error_pop ();
438 /* Mode found! Exit loop. */
439 mode_found = 1;
440 break;
443 /* Free memory. */
444 grub_free (modevar);
446 if (!mode_found)
447 return grub_error (GRUB_ERR_BAD_ARGUMENT,
448 "No suitable mode found.");
450 else
452 /* No gfxmode variable set, use defaults. */
454 /* If we have 8 or less bits, then assume that it is indexed color mode. */
455 if ((depth <= 8) && (depth != -1))
456 flags |= GRUB_VIDEO_MODE_TYPE_INDEX_COLOR;
458 /* We have more than 8 bits, then assume that it is RGB color mode. */
459 if (depth > 8)
460 flags |= GRUB_VIDEO_MODE_TYPE_RGB;
462 /* If user requested specific depth, forward that information to driver. */
463 if (depth != -1)
464 flags |= (depth << GRUB_VIDEO_MODE_TYPE_DEPTH_POS)
465 & GRUB_VIDEO_MODE_TYPE_DEPTH_MASK;
467 /* Initialize user requested mode. */
468 if (grub_video_setup (width, height, flags) != GRUB_ERR_NONE)
469 return grub_errno;
471 /* Figure out what mode we ended up. */
472 if (grub_video_get_info (&mode_info) != GRUB_ERR_NONE)
474 grub_video_restore ();
475 return grub_errno;
479 /* Make sure screen is black. */
480 color = grub_video_map_rgb (0, 0, 0);
481 grub_video_fill_rect (color, 0, 0, mode_info.width, mode_info.height);
482 bitmap = 0;
484 /* Leave borders for virtual screen. */
485 width = mode_info.width - (2 * DEFAULT_BORDER_WIDTH);
486 height = mode_info.height - (2 * DEFAULT_BORDER_WIDTH);
488 /* Create virtual screen. */
489 if (grub_virtual_screen_setup (DEFAULT_BORDER_WIDTH, DEFAULT_BORDER_WIDTH,
490 width, height, font_name) != GRUB_ERR_NONE)
492 grub_video_restore ();
493 return grub_errno;
496 /* Mark whole screen as dirty. */
497 dirty_region_reset ();
498 dirty_region_add (0, 0, mode_info.width, mode_info.height);
500 return (grub_errno = GRUB_ERR_NONE);
503 static grub_err_t
504 grub_gfxterm_fini (void)
506 if (bitmap)
508 grub_video_bitmap_destroy (bitmap);
509 bitmap = 0;
512 grub_virtual_screen_free ();
514 grub_video_restore ();
516 return GRUB_ERR_NONE;
519 static void
520 redraw_screen_rect (unsigned int x, unsigned int y,
521 unsigned int width, unsigned int height)
523 grub_video_color_t color;
525 grub_video_set_active_render_target (GRUB_VIDEO_RENDER_TARGET_DISPLAY);
528 if (bitmap)
530 /* Render bitmap as background. */
531 grub_video_blit_bitmap (bitmap, GRUB_VIDEO_BLIT_REPLACE, x, y,
532 x, y,
533 width, height);
535 /* If bitmap is smaller than requested blit area, use background
536 color. */
537 color = virtual_screen.bg_color;
539 /* Fill right side of the bitmap if needed. */
540 if ((x + width >= bitmap_width) && (y < bitmap_height))
542 int w = (x + width) - bitmap_width;
543 int h = height;
544 unsigned int tx = x;
546 if (y + height >= bitmap_height)
548 h = bitmap_height - y;
551 if (bitmap_width > tx)
553 tx = bitmap_width;
556 /* Render background layer. */
557 grub_video_fill_rect (color, tx, y, w, h);
560 /* Fill bottom side of the bitmap if needed. */
561 if (y + height >= bitmap_height)
563 int h = (y + height) - bitmap_height;
564 unsigned int ty = y;
566 if (bitmap_height > ty)
568 ty = bitmap_height;
571 /* Render background layer. */
572 grub_video_fill_rect (color, x, ty, width, h);
575 /* Render text layer as blended. */
576 grub_video_blit_render_target (text_layer, GRUB_VIDEO_BLIT_BLEND, x, y,
577 x - virtual_screen.offset_x,
578 y - virtual_screen.offset_y,
579 width, height);
581 else
583 /* Render background layer. */
584 color = virtual_screen.bg_color;
585 grub_video_fill_rect (color, x, y, width, height);
587 /* Render text layer as replaced (to get texts background color). */
588 grub_video_blit_render_target (text_layer, GRUB_VIDEO_BLIT_REPLACE, x, y,
589 x - virtual_screen.offset_x,
590 y - virtual_screen.offset_y,
591 width, height);
595 static void
596 dirty_region_reset (void)
598 dirty_region.top_left_x = -1;
599 dirty_region.top_left_y = -1;
600 dirty_region.bottom_right_x = -1;
601 dirty_region.bottom_right_y = -1;
604 static int
605 dirty_region_is_empty (void)
607 if ((dirty_region.top_left_x == -1)
608 || (dirty_region.top_left_y == -1)
609 || (dirty_region.bottom_right_x == -1)
610 || (dirty_region.bottom_right_y == -1))
611 return 1;
612 return 0;
615 static void
616 dirty_region_add (int x, int y, unsigned int width, unsigned int height)
618 if ((width == 0) || (height == 0))
619 return;
621 if (dirty_region_is_empty ())
623 dirty_region.top_left_x = x;
624 dirty_region.top_left_y = y;
625 dirty_region.bottom_right_x = x + width - 1;
626 dirty_region.bottom_right_y = y + height - 1;
628 else
630 if (x < dirty_region.top_left_x)
631 dirty_region.top_left_x = x;
632 if (y < dirty_region.top_left_y)
633 dirty_region.top_left_y = y;
634 if ((x + (int)width - 1) > dirty_region.bottom_right_x)
635 dirty_region.bottom_right_x = x + width - 1;
636 if ((y + (int)height - 1) > dirty_region.bottom_right_y)
637 dirty_region.bottom_right_y = y + height - 1;
641 static void
642 dirty_region_add_virtualscreen (void)
644 /* Mark virtual screen as dirty. */
645 dirty_region_add (virtual_screen.offset_x, virtual_screen.offset_y,
646 virtual_screen.width, virtual_screen.height);
650 static void
651 dirty_region_redraw (void)
653 int x;
654 int y;
655 int width;
656 int height;
658 if (dirty_region_is_empty ())
659 return;
661 x = dirty_region.top_left_x;
662 y = dirty_region.top_left_y;
664 width = dirty_region.bottom_right_x - x + 1;
665 height = dirty_region.bottom_right_y - y + 1;
667 redraw_screen_rect (x, y, width, height);
669 dirty_region_reset ();
672 static void
673 write_char (void)
675 struct grub_colored_char *p;
676 struct grub_font_glyph *glyph;
677 grub_video_color_t color;
678 grub_video_color_t bgcolor;
679 unsigned int x;
680 unsigned int y;
681 int ascent;
682 unsigned int height;
683 unsigned int width;
685 /* Find out active character. */
686 p = (virtual_screen.text_buffer
687 + virtual_screen.cursor_x
688 + (virtual_screen.cursor_y * virtual_screen.columns));
690 p -= p->index;
692 /* Get glyph for character. */
693 glyph = grub_font_get_glyph (virtual_screen.font, p->code);
694 ascent = grub_font_get_ascent (virtual_screen.font);
696 width = virtual_screen.normal_char_width * calculate_character_width(glyph);
697 height = virtual_screen.normal_char_height;
699 color = p->fg_color;
700 bgcolor = p->bg_color;
702 x = virtual_screen.cursor_x * virtual_screen.normal_char_width;
703 y = virtual_screen.cursor_y * virtual_screen.normal_char_height;
705 /* Render glyph to text layer. */
706 grub_video_set_active_render_target (text_layer);
707 grub_video_fill_rect (bgcolor, x, y, width, height);
708 grub_font_draw_glyph (glyph, color, x, y + ascent);
709 grub_video_set_active_render_target (GRUB_VIDEO_RENDER_TARGET_DISPLAY);
711 /* Mark character to be drawn. */
712 dirty_region_add (virtual_screen.offset_x + x, virtual_screen.offset_y + y,
713 width, height);
716 static void
717 draw_cursor (int show)
719 write_char ();
721 if (show)
723 unsigned int x;
724 unsigned int y;
725 unsigned int width;
726 unsigned int height;
727 grub_video_color_t color;
729 /* Determine cursor properties and position on text layer. */
730 x = virtual_screen.cursor_x * virtual_screen.normal_char_width;
731 width = virtual_screen.normal_char_width;
732 color = virtual_screen.fg_color;
733 y = (virtual_screen.cursor_y * virtual_screen.normal_char_height
734 + grub_font_get_ascent (virtual_screen.font));
735 height = 2;
737 /* Render cursor to text layer. */
738 grub_video_set_active_render_target (text_layer);
739 grub_video_fill_rect (color, x, y, width, height);
740 grub_video_set_active_render_target (GRUB_VIDEO_RENDER_TARGET_DISPLAY);
742 /* Mark cursor to be redrawn. */
743 dirty_region_add (virtual_screen.offset_x + x,
744 virtual_screen.offset_y + y,
745 width, height);
749 static void
750 scroll_up (void)
752 unsigned int i;
753 grub_video_color_t color;
755 /* If we don't have background bitmap, remove cursor. */
756 if (!bitmap)
758 /* Remove cursor. */
759 draw_cursor (0);
761 /* Redraw only changed regions. */
762 dirty_region_redraw ();
765 /* Scroll text buffer with one line to up. */
766 grub_memmove (virtual_screen.text_buffer,
767 virtual_screen.text_buffer + virtual_screen.columns,
768 sizeof (*virtual_screen.text_buffer)
769 * virtual_screen.columns
770 * (virtual_screen.rows - 1));
772 /* Clear last line in text buffer. */
773 for (i = virtual_screen.columns * (virtual_screen.rows - 1);
774 i < virtual_screen.columns * virtual_screen.rows;
775 i++)
777 virtual_screen.text_buffer[i].code = ' ';
778 virtual_screen.text_buffer[i].fg_color = virtual_screen.fg_color;
779 virtual_screen.text_buffer[i].bg_color = virtual_screen.bg_color;
780 virtual_screen.text_buffer[i].width = 0;
781 virtual_screen.text_buffer[i].index = 0;
784 /* Scroll physical screen. */
785 grub_video_set_active_render_target (text_layer);
786 color = virtual_screen.bg_color;
787 grub_video_scroll (color, 0, -virtual_screen.normal_char_height);
788 grub_video_set_active_render_target (GRUB_VIDEO_RENDER_TARGET_DISPLAY);
790 /* If we have bitmap, re-draw screen, otherwise scroll physical screen too. */
791 if (bitmap)
793 /* Mark virtual screen to be redrawn. */
794 dirty_region_add_virtualscreen ();
796 else
798 /* Clear new border area. */
799 grub_video_fill_rect (color,
800 virtual_screen.offset_x, virtual_screen.offset_y,
801 virtual_screen.width, virtual_screen.normal_char_height);
803 /* Scroll physical screen. */
804 grub_video_scroll (color, 0, -virtual_screen.normal_char_height);
806 /* Draw cursor if visible. */
807 if (virtual_screen.cursor_state)
808 draw_cursor (1);
812 static void
813 grub_gfxterm_putchar (grub_uint32_t c)
815 if (c == '\a')
816 /* FIXME */
817 return;
819 /* Erase current cursor, if any. */
820 if (virtual_screen.cursor_state)
821 draw_cursor (0);
823 if (c == '\b' || c == '\n' || c == '\r')
825 switch (c)
827 case '\b':
828 if (virtual_screen.cursor_x > 0)
829 virtual_screen.cursor_x--;
830 break;
832 case '\n':
833 if (virtual_screen.cursor_y >= virtual_screen.rows - 1)
834 scroll_up ();
835 else
836 virtual_screen.cursor_y++;
837 break;
839 case '\r':
840 virtual_screen.cursor_x = 0;
841 break;
844 else
846 struct grub_font_glyph *glyph;
847 struct grub_colored_char *p;
848 unsigned char char_width;
850 /* Get properties of the character. */
851 glyph = grub_font_get_glyph (virtual_screen.font, c);
853 /* Calculate actual character width for glyph. This is number of
854 times of normal_font_width. */
855 char_width = calculate_character_width(glyph);
857 /* If we are about to exceed line length, wrap to next line. */
858 if (virtual_screen.cursor_x + char_width > virtual_screen.columns)
859 grub_putchar ('\n');
861 /* Find position on virtual screen, and fill information. */
862 p = (virtual_screen.text_buffer +
863 virtual_screen.cursor_x +
864 virtual_screen.cursor_y * virtual_screen.columns);
865 p->code = c;
866 p->fg_color = virtual_screen.fg_color;
867 p->bg_color = virtual_screen.bg_color;
868 p->width = char_width - 1;
869 p->index = 0;
871 /* If we have large glyph, add fixup info. */
872 if (char_width > 1)
874 unsigned i;
876 for (i = 1; i < char_width; i++)
878 p[i].code = ' ';
879 p[i].width = char_width - 1;
880 p[i].index = i;
884 /* Draw glyph. */
885 write_char ();
887 /* Make sure we scroll screen when needed and wrap line correctly. */
888 virtual_screen.cursor_x += char_width;
889 if (virtual_screen.cursor_x >= virtual_screen.columns)
891 virtual_screen.cursor_x = 0;
893 if (virtual_screen.cursor_y >= virtual_screen.rows - 1)
894 scroll_up ();
895 else
896 virtual_screen.cursor_y++;
900 /* Redraw cursor if it should be visible. */
901 /* Note: This will redraw the character as well, which means that the
902 above call to write_char is redundant when the cursor is showing. */
903 if (virtual_screen.cursor_state)
904 draw_cursor (1);
907 /* Use ASCII characters to determine normal character width. */
908 static unsigned int
909 calculate_normal_character_width (grub_font_t font)
911 struct grub_font_glyph *glyph;
912 unsigned int width = 0;
913 unsigned int i;
915 /* Get properties of every printable ASCII character. */
916 for (i = 32; i < 127; i++)
918 glyph = grub_font_get_glyph (font, i);
920 /* Skip unknown characters. Should never happen on normal conditions. */
921 if (! glyph)
922 continue;
924 if (glyph->device_width > width)
925 width = glyph->device_width;
928 return width;
931 static unsigned char
932 calculate_character_width (struct grub_font_glyph *glyph)
934 if (! glyph || glyph->device_width == 0)
935 return 1;
937 return (glyph->device_width
938 + (virtual_screen.normal_char_width - 1))
939 / virtual_screen.normal_char_width;
942 static grub_ssize_t
943 grub_gfxterm_getcharwidth (grub_uint32_t c)
945 struct grub_font_glyph *glyph;
946 unsigned char char_width;
948 /* Get properties of the character. */
949 glyph = grub_font_get_glyph (virtual_screen.font, c);
951 /* Calculate actual character width for glyph. */
952 char_width = calculate_character_width (glyph);
954 return char_width;
957 static grub_uint16_t
958 grub_virtual_screen_getwh (void)
960 return (virtual_screen.columns << 8) | virtual_screen.rows;
963 static grub_uint16_t
964 grub_virtual_screen_getxy (void)
966 return ((virtual_screen.cursor_x << 8) | virtual_screen.cursor_y);
969 static void
970 grub_gfxterm_gotoxy (grub_uint8_t x, grub_uint8_t y)
972 if (x >= virtual_screen.columns)
973 x = virtual_screen.columns - 1;
975 if (y >= virtual_screen.rows)
976 y = virtual_screen.rows - 1;
978 /* Erase current cursor, if any. */
979 if (virtual_screen.cursor_state)
980 draw_cursor (0);
982 virtual_screen.cursor_x = x;
983 virtual_screen.cursor_y = y;
985 /* Draw cursor if visible. */
986 if (virtual_screen.cursor_state)
987 draw_cursor (1);
990 static void
991 grub_virtual_screen_cls (void)
993 grub_uint32_t i;
995 for (i = 0; i < virtual_screen.columns * virtual_screen.rows; i++)
997 virtual_screen.text_buffer[i].code = ' ';
998 virtual_screen.text_buffer[i].fg_color = virtual_screen.fg_color;
999 virtual_screen.text_buffer[i].bg_color = virtual_screen.bg_color;
1000 virtual_screen.text_buffer[i].width = 0;
1001 virtual_screen.text_buffer[i].index = 0;
1004 virtual_screen.cursor_x = virtual_screen.cursor_y = 0;
1007 static void
1008 grub_gfxterm_cls (void)
1010 grub_video_color_t color;
1012 /* Clear virtual screen. */
1013 grub_virtual_screen_cls ();
1015 /* Clear text layer. */
1016 grub_video_set_active_render_target (text_layer);
1017 color = virtual_screen.bg_color;
1018 grub_video_fill_rect (color, 0, 0, mode_info.width, mode_info.height);
1019 grub_video_set_active_render_target (GRUB_VIDEO_RENDER_TARGET_DISPLAY);
1021 /* Mark virtual screen to be redrawn. */
1022 dirty_region_add_virtualscreen ();
1025 static void
1026 grub_virtual_screen_setcolorstate (grub_term_color_state state)
1028 switch (state)
1030 case GRUB_TERM_COLOR_STANDARD:
1031 virtual_screen.term_color = virtual_screen.standard_color_setting;
1032 break;
1034 case GRUB_TERM_COLOR_NORMAL:
1035 virtual_screen.term_color = virtual_screen.normal_color_setting;
1036 break;
1038 case GRUB_TERM_COLOR_HIGHLIGHT:
1039 virtual_screen.term_color = virtual_screen.highlight_color_setting;
1040 break;
1042 default:
1043 break;
1046 /* Change color to virtual terminal. */
1047 set_term_color (virtual_screen.term_color);
1050 static void
1051 grub_virtual_screen_setcolor (grub_uint8_t normal_color,
1052 grub_uint8_t highlight_color)
1054 virtual_screen.normal_color_setting = normal_color;
1055 virtual_screen.highlight_color_setting = highlight_color;
1058 static void
1059 grub_virtual_screen_getcolor (grub_uint8_t *normal_color,
1060 grub_uint8_t *highlight_color)
1062 *normal_color = virtual_screen.normal_color_setting;
1063 *highlight_color = virtual_screen.highlight_color_setting;
1066 static void
1067 grub_gfxterm_setcursor (int on)
1069 if (virtual_screen.cursor_state != on)
1071 if (virtual_screen.cursor_state)
1072 draw_cursor (0);
1073 else
1074 draw_cursor (1);
1076 virtual_screen.cursor_state = on;
1080 static void
1081 grub_gfxterm_refresh (void)
1083 /* Redraw only changed regions. */
1084 dirty_region_redraw ();
1087 static grub_err_t
1088 grub_gfxterm_background_image_cmd (grub_command_t cmd __attribute__ ((unused)),
1089 int argc,
1090 char **args)
1092 /* Check that we have video adapter active. */
1093 if (grub_video_get_info(NULL) != GRUB_ERR_NONE)
1094 return grub_errno;
1096 /* Destroy existing background bitmap if loaded. */
1097 if (bitmap)
1099 grub_video_bitmap_destroy (bitmap);
1100 bitmap = 0;
1102 /* Mark whole screen as dirty. */
1103 dirty_region_reset ();
1104 dirty_region_add (0, 0, mode_info.width, mode_info.height);
1107 /* If filename was provided, try to load that. */
1108 if (argc >= 1)
1110 /* Try to load new one. */
1111 grub_video_bitmap_load (&bitmap, args[0]);
1112 if (grub_errno != GRUB_ERR_NONE)
1113 return grub_errno;
1115 /* If bitmap was loaded correctly, display it. */
1116 if (bitmap)
1118 /* Determine bitmap dimensions. */
1119 bitmap_width = grub_video_bitmap_get_width (bitmap);
1120 bitmap_height = grub_video_bitmap_get_width (bitmap);
1122 /* Mark whole screen as dirty. */
1123 dirty_region_reset ();
1124 dirty_region_add (0, 0, mode_info.width, mode_info.height);
1128 /* All was ok. */
1129 grub_errno = GRUB_ERR_NONE;
1130 return grub_errno;
1133 static struct grub_term_output grub_video_term =
1135 .name = "gfxterm",
1136 .init = grub_gfxterm_init,
1137 .fini = grub_gfxterm_fini,
1138 .putchar = grub_gfxterm_putchar,
1139 .getcharwidth = grub_gfxterm_getcharwidth,
1140 .getwh = grub_virtual_screen_getwh,
1141 .getxy = grub_virtual_screen_getxy,
1142 .gotoxy = grub_gfxterm_gotoxy,
1143 .cls = grub_gfxterm_cls,
1144 .setcolorstate = grub_virtual_screen_setcolorstate,
1145 .setcolor = grub_virtual_screen_setcolor,
1146 .getcolor = grub_virtual_screen_getcolor,
1147 .setcursor = grub_gfxterm_setcursor,
1148 .refresh = grub_gfxterm_refresh,
1149 .flags = 0,
1150 .next = 0
1153 static grub_command_t cmd;
1155 GRUB_MOD_INIT(term_gfxterm)
1157 my_mod = mod;
1158 grub_term_register_output ("gfxterm", &grub_video_term);
1159 cmd = grub_register_command ("background_image",
1160 grub_gfxterm_background_image_cmd,
1161 0, "Load background image for active terminal");
1164 GRUB_MOD_FINI(term_gfxterm)
1166 grub_unregister_command (cmd);
1167 grub_term_unregister_output (&grub_video_term);