2009-02-03 Felix Zielcke <fzielcke@z-51.de>
[grub2/phcoder/solaris.git] / term / gfxterm.c
blobabb1b9ed776050375269019002568bc0ad27a2f8
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/normal.h>
24 #include <grub/font.h>
25 #include <grub/arg.h>
26 #include <grub/mm.h>
27 #include <grub/env.h>
28 #include <grub/video.h>
29 #include <grub/bitmap.h>
31 #define DEFAULT_VIDEO_WIDTH 640
32 #define DEFAULT_VIDEO_HEIGHT 480
33 #define DEFAULT_VIDEO_FLAGS 0
35 #define DEFAULT_BORDER_WIDTH 10
37 #define DEFAULT_STANDARD_COLOR 0x07
38 #define DEFAULT_NORMAL_COLOR 0x07
39 #define DEFAULT_HIGHLIGHT_COLOR 0x70
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 grub_uint32_t code;
54 /* Color values. */
55 grub_video_color_t fg_color;
56 grub_video_color_t bg_color;
58 /* The width of this character minus one. */
59 unsigned char width;
61 /* The column index of this character. */
62 unsigned char index;
65 struct grub_virtual_screen
67 /* Dimensions of the virtual screen in pixels. */
68 unsigned int width;
69 unsigned int height;
71 /* Offset in the display in pixels. */
72 unsigned int offset_x;
73 unsigned int offset_y;
75 /* TTY Character sizes in pixes. */
76 unsigned int normal_char_width;
77 unsigned int normal_char_height;
79 /* Virtual screen TTY size in characters. */
80 unsigned int columns;
81 unsigned int rows;
83 /* Current cursor location in characters. */
84 unsigned int cursor_x;
85 unsigned int cursor_y;
87 /* Current cursor state. */
88 int cursor_state;
90 /* Font settings. */
91 grub_font_t font;
93 /* Terminal color settings. */
94 grub_uint8_t standard_color_setting;
95 grub_uint8_t normal_color_setting;
96 grub_uint8_t highlight_color_setting;
97 grub_uint8_t term_color;
99 /* Color settings. */
100 grub_video_color_t fg_color;
101 grub_video_color_t bg_color;
103 /* Text buffer for virtual screen. Contains (columns * rows) number
104 of entries. */
105 struct grub_colored_char *text_buffer;
108 static struct grub_virtual_screen virtual_screen;
110 static grub_dl_t my_mod;
111 static struct grub_video_mode_info mode_info;
113 static struct grub_video_render_target *text_layer;
115 static unsigned int bitmap_width;
116 static unsigned int bitmap_height;
117 static struct grub_video_bitmap *bitmap;
119 static struct grub_dirty_region dirty_region;
121 static void dirty_region_reset (void);
123 static int dirty_region_is_empty (void);
125 static void dirty_region_add (int x, int y,
126 unsigned int width, unsigned int height);
128 static unsigned int calculate_normal_character_width (grub_font_t font);
130 static unsigned char calculate_character_width (struct grub_font_glyph *glyph);
132 static void
133 set_term_color (grub_uint8_t term_color)
135 struct grub_video_render_target *old_target;
137 /* Save previous target and switch to text layer. */
138 grub_video_get_active_render_target (&old_target);
139 grub_video_set_active_render_target (text_layer);
141 /* Map terminal color to text layer compatible video colors. */
142 virtual_screen.fg_color = grub_video_map_color(term_color & 0x0f);
144 /* Special case: use black as transparent color. */
145 if (((term_color >> 4) & 0x0f) == 0)
147 virtual_screen.bg_color = grub_video_map_rgba(0, 0, 0, 0);
149 else
151 virtual_screen.bg_color = grub_video_map_color((term_color >> 4) & 0x0f);
154 /* Restore previous target. */
155 grub_video_set_active_render_target (old_target);
158 static void
159 grub_virtual_screen_free (void)
161 /* If virtual screen has been allocated, free it. */
162 if (virtual_screen.text_buffer != 0)
163 grub_free (virtual_screen.text_buffer);
165 /* Reset virtual screen data. */
166 grub_memset (&virtual_screen, 0, sizeof (virtual_screen));
168 /* Free render targets. */
169 grub_video_delete_render_target (text_layer);
170 text_layer = 0;
173 static grub_err_t
174 grub_virtual_screen_setup (unsigned int x, unsigned int y,
175 unsigned int width, unsigned int height,
176 const char *font_name)
178 /* Free old virtual screen. */
179 grub_virtual_screen_free ();
181 /* Initialize with default data. */
182 virtual_screen.font = grub_font_get (font_name);
183 if (!virtual_screen.font)
184 return grub_error (GRUB_ERR_BAD_FONT,
185 "No font loaded.");
186 virtual_screen.width = width;
187 virtual_screen.height = height;
188 virtual_screen.offset_x = x;
189 virtual_screen.offset_y = y;
190 virtual_screen.normal_char_width =
191 calculate_normal_character_width (virtual_screen.font);
192 virtual_screen.normal_char_height =
193 grub_font_get_max_char_height (virtual_screen.font);
194 virtual_screen.cursor_x = 0;
195 virtual_screen.cursor_y = 0;
196 virtual_screen.cursor_state = 1;
198 /* Calculate size of text buffer. */
199 virtual_screen.columns = virtual_screen.width / virtual_screen.normal_char_width;
200 virtual_screen.rows = virtual_screen.height / virtual_screen.normal_char_height;
202 /* Allocate memory for text buffer. */
203 virtual_screen.text_buffer =
204 (struct grub_colored_char *) grub_malloc (virtual_screen.columns
205 * virtual_screen.rows
206 * sizeof (*virtual_screen.text_buffer));
207 if (grub_errno != GRUB_ERR_NONE)
208 return grub_errno;
210 /* Create new render target for text layer. */
211 grub_video_create_render_target (&text_layer,
212 virtual_screen.width,
213 virtual_screen.height,
214 GRUB_VIDEO_MODE_TYPE_RGB
215 | GRUB_VIDEO_MODE_TYPE_ALPHA);
216 if (grub_errno != GRUB_ERR_NONE)
217 return grub_errno;
219 /* As we want to have colors compatible with rendering target,
220 we can only have those after mode is initialized. */
221 grub_video_set_active_render_target (text_layer);
223 virtual_screen.standard_color_setting = DEFAULT_STANDARD_COLOR;
224 virtual_screen.normal_color_setting = DEFAULT_NORMAL_COLOR;
225 virtual_screen.highlight_color_setting = DEFAULT_HIGHLIGHT_COLOR;
227 virtual_screen.term_color = virtual_screen.normal_color_setting;
229 set_term_color (virtual_screen.term_color);
231 grub_video_set_active_render_target (GRUB_VIDEO_RENDER_TARGET_DISPLAY);
233 return grub_errno;
236 static grub_err_t
237 grub_gfxterm_init (void)
239 char *font_name;
240 char *modevar;
241 int width = DEFAULT_VIDEO_WIDTH;
242 int height = DEFAULT_VIDEO_HEIGHT;
243 int depth = -1;
244 int flags = DEFAULT_VIDEO_FLAGS;
245 grub_video_color_t color;
247 /* Select the font to use. */
248 font_name = grub_env_get ("gfxterm_font");
249 if (! font_name)
250 font_name = ""; /* Allow fallback to any font. */
252 /* Parse gfxmode environment variable if set. */
253 modevar = grub_env_get ("gfxmode");
254 if (modevar)
256 char *tmp;
257 char *next_mode;
258 char *current_mode;
259 char *param;
260 char *value;
261 int mode_found = 0;
263 /* Take copy of env.var. as we don't want to modify that. */
264 tmp = grub_strdup (modevar);
265 modevar = tmp;
267 if (grub_errno != GRUB_ERR_NONE)
268 return grub_errno;
270 /* Initialize next mode. */
271 next_mode = modevar;
273 /* Loop until all modes has been tested out. */
274 while (next_mode != NULL)
276 /* Use last next_mode as current mode. */
277 tmp = next_mode;
279 /* Reset video mode settings. */
280 width = DEFAULT_VIDEO_WIDTH;
281 height = DEFAULT_VIDEO_HEIGHT;
282 depth = -1;
283 flags = DEFAULT_VIDEO_FLAGS;
285 /* Save position of next mode and separate modes. */
286 next_mode = grub_strchr(next_mode, ';');
287 if (next_mode)
289 *next_mode = 0;
290 next_mode++;
293 /* Skip whitespace. */
294 while (grub_isspace (*tmp))
295 tmp++;
297 /* Initialize token holders. */
298 current_mode = tmp;
299 param = tmp;
300 value = NULL;
302 /* Parse <width>x<height>[x<depth>]*/
304 /* Find width value. */
305 value = param;
306 param = grub_strchr(param, 'x');
307 if (param == NULL)
309 grub_err_t rc;
311 /* First setup error message. */
312 rc = grub_error (GRUB_ERR_BAD_ARGUMENT,
313 "Invalid mode: %s\n",
314 current_mode);
316 /* Free memory before returning. */
317 grub_free (modevar);
319 return rc;
322 *param = 0;
323 param++;
325 width = grub_strtoul (value, 0, 0);
326 if (grub_errno != GRUB_ERR_NONE)
328 grub_err_t rc;
330 /* First setup error message. */
331 rc = grub_error (GRUB_ERR_BAD_ARGUMENT,
332 "Invalid mode: %s\n",
333 current_mode);
335 /* Free memory before returning. */
336 grub_free (modevar);
338 return rc;
341 /* Find height value. */
342 value = param;
343 param = grub_strchr(param, 'x');
344 if (param == NULL)
346 height = grub_strtoul (value, 0, 0);
347 if (grub_errno != GRUB_ERR_NONE)
349 grub_err_t rc;
351 /* First setup error message. */
352 rc = grub_error (GRUB_ERR_BAD_ARGUMENT,
353 "Invalid mode: %s\n",
354 current_mode);
356 /* Free memory before returning. */
357 grub_free (modevar);
359 return rc;
362 else
364 /* We have optional color depth value. */
365 *param = 0;
366 param++;
368 height = grub_strtoul (value, 0, 0);
369 if (grub_errno != GRUB_ERR_NONE)
371 grub_err_t rc;
373 /* First setup error message. */
374 rc = grub_error (GRUB_ERR_BAD_ARGUMENT,
375 "Invalid mode: %s\n",
376 current_mode);
378 /* Free memory before returning. */
379 grub_free (modevar);
381 return rc;
384 /* Convert color depth value. */
385 value = param;
386 depth = grub_strtoul (value, 0, 0);
387 if (grub_errno != GRUB_ERR_NONE)
389 grub_err_t rc;
391 /* First setup error message. */
392 rc = grub_error (GRUB_ERR_BAD_ARGUMENT,
393 "Invalid mode: %s\n",
394 current_mode);
396 /* Free memory before returning. */
397 grub_free (modevar);
399 return rc;
403 /* Try out video mode. */
405 /* If we have 8 or less bits, then assume that it is indexed color mode. */
406 if ((depth <= 8) && (depth != -1))
407 flags |= GRUB_VIDEO_MODE_TYPE_INDEX_COLOR;
409 /* We have more than 8 bits, then assume that it is RGB color mode. */
410 if (depth > 8)
411 flags |= GRUB_VIDEO_MODE_TYPE_RGB;
413 /* If user requested specific depth, forward that information to driver. */
414 if (depth != -1)
415 flags |= (depth << GRUB_VIDEO_MODE_TYPE_DEPTH_POS)
416 & GRUB_VIDEO_MODE_TYPE_DEPTH_MASK;
418 /* Try to initialize requested mode. Ignore any errors. */
419 grub_error_push ();
420 if (grub_video_setup (width, height, flags) != GRUB_ERR_NONE)
422 grub_error_pop ();
423 continue;
426 /* Figure out what mode we ended up. */
427 if (grub_video_get_info (&mode_info) != GRUB_ERR_NONE)
429 /* Couldn't get video mode info, restore old mode and continue to next one. */
430 grub_error_pop ();
432 grub_video_restore ();
433 continue;
436 /* Restore state of error stack. */
437 grub_error_pop ();
439 /* Mode found! Exit loop. */
440 mode_found = 1;
441 break;
444 /* Free memory. */
445 grub_free (modevar);
447 if (!mode_found)
448 return grub_error (GRUB_ERR_BAD_ARGUMENT,
449 "No suitable mode found.");
451 else
453 /* No gfxmode variable set, use defaults. */
455 /* If we have 8 or less bits, then assume that it is indexed color mode. */
456 if ((depth <= 8) && (depth != -1))
457 flags |= GRUB_VIDEO_MODE_TYPE_INDEX_COLOR;
459 /* We have more than 8 bits, then assume that it is RGB color mode. */
460 if (depth > 8)
461 flags |= GRUB_VIDEO_MODE_TYPE_RGB;
463 /* If user requested specific depth, forward that information to driver. */
464 if (depth != -1)
465 flags |= (depth << GRUB_VIDEO_MODE_TYPE_DEPTH_POS)
466 & GRUB_VIDEO_MODE_TYPE_DEPTH_MASK;
468 /* Initialize user requested mode. */
469 if (grub_video_setup (width, height, flags) != GRUB_ERR_NONE)
470 return grub_errno;
472 /* Figure out what mode we ended up. */
473 if (grub_video_get_info (&mode_info) != GRUB_ERR_NONE)
475 grub_video_restore ();
476 return grub_errno;
480 /* Make sure screen is black. */
481 color = grub_video_map_rgb (0, 0, 0);
482 grub_video_fill_rect (color, 0, 0, mode_info.width, mode_info.height);
483 bitmap = 0;
485 /* Leave borders for virtual screen. */
486 width = mode_info.width - (2 * DEFAULT_BORDER_WIDTH);
487 height = mode_info.height - (2 * DEFAULT_BORDER_WIDTH);
489 /* Create virtual screen. */
490 if (grub_virtual_screen_setup (DEFAULT_BORDER_WIDTH, DEFAULT_BORDER_WIDTH,
491 width, height, font_name) != GRUB_ERR_NONE)
493 grub_video_restore ();
494 return grub_errno;
497 /* Mark whole screen as dirty. */
498 dirty_region_reset ();
499 dirty_region_add (0, 0, mode_info.width, mode_info.height);
501 return (grub_errno = GRUB_ERR_NONE);
504 static grub_err_t
505 grub_gfxterm_fini (void)
507 if (bitmap)
509 grub_video_bitmap_destroy (bitmap);
510 bitmap = 0;
513 grub_virtual_screen_free ();
515 grub_video_restore ();
517 return GRUB_ERR_NONE;
520 static void
521 redraw_screen_rect (unsigned int x, unsigned int y,
522 unsigned int width, unsigned int height)
524 grub_video_color_t color;
526 grub_video_set_active_render_target (GRUB_VIDEO_RENDER_TARGET_DISPLAY);
529 if (bitmap)
531 /* Render bitmap as background. */
532 grub_video_blit_bitmap (bitmap, GRUB_VIDEO_BLIT_REPLACE, x, y,
533 x, y,
534 width, height);
536 /* If bitmap is smaller than requested blit area, use background
537 color. */
538 color = virtual_screen.bg_color;
540 /* Fill right side of the bitmap if needed. */
541 if ((x + width >= bitmap_width) && (y < bitmap_height))
543 int w = (x + width) - bitmap_width;
544 int h = height;
545 unsigned int tx = x;
547 if (y + height >= bitmap_height)
549 h = bitmap_height - y;
552 if (bitmap_width > tx)
554 tx = bitmap_width;
557 /* Render background layer. */
558 grub_video_fill_rect (color, tx, y, w, h);
561 /* Fill bottom side of the bitmap if needed. */
562 if (y + height >= bitmap_height)
564 int h = (y + height) - bitmap_height;
565 unsigned int ty = y;
567 if (bitmap_height > ty)
569 ty = bitmap_height;
572 /* Render background layer. */
573 grub_video_fill_rect (color, x, ty, width, h);
576 /* Render text layer as blended. */
577 grub_video_blit_render_target (text_layer, GRUB_VIDEO_BLIT_BLEND, x, y,
578 x - virtual_screen.offset_x,
579 y - virtual_screen.offset_y,
580 width, height);
582 else
584 /* Render background layer. */
585 color = virtual_screen.bg_color;
586 grub_video_fill_rect (color, x, y, width, height);
588 /* Render text layer as replaced (to get texts background color). */
589 grub_video_blit_render_target (text_layer, GRUB_VIDEO_BLIT_REPLACE, x, y,
590 x - virtual_screen.offset_x,
591 y - virtual_screen.offset_y,
592 width, height);
596 static void
597 dirty_region_reset (void)
599 dirty_region.top_left_x = -1;
600 dirty_region.top_left_y = -1;
601 dirty_region.bottom_right_x = -1;
602 dirty_region.bottom_right_y = -1;
605 static int
606 dirty_region_is_empty (void)
608 if ((dirty_region.top_left_x == -1)
609 || (dirty_region.top_left_y == -1)
610 || (dirty_region.bottom_right_x == -1)
611 || (dirty_region.bottom_right_y == -1))
612 return 1;
613 return 0;
616 static void
617 dirty_region_add (int x, int y, unsigned int width, unsigned int height)
619 if ((width == 0) || (height == 0))
620 return;
622 if (dirty_region_is_empty ())
624 dirty_region.top_left_x = x;
625 dirty_region.top_left_y = y;
626 dirty_region.bottom_right_x = x + width - 1;
627 dirty_region.bottom_right_y = y + height - 1;
629 else
631 if (x < dirty_region.top_left_x)
632 dirty_region.top_left_x = x;
633 if (y < dirty_region.top_left_y)
634 dirty_region.top_left_y = y;
635 if ((x + (int)width - 1) > dirty_region.bottom_right_x)
636 dirty_region.bottom_right_x = x + width - 1;
637 if ((y + (int)height - 1) > dirty_region.bottom_right_y)
638 dirty_region.bottom_right_y = y + height - 1;
642 static void
643 dirty_region_add_virtualscreen (void)
645 /* Mark virtual screen as dirty. */
646 dirty_region_add (virtual_screen.offset_x, virtual_screen.offset_y,
647 virtual_screen.width, virtual_screen.height);
651 static void
652 dirty_region_redraw (void)
654 int x;
655 int y;
656 int width;
657 int height;
659 if (dirty_region_is_empty ())
660 return;
662 x = dirty_region.top_left_x;
663 y = dirty_region.top_left_y;
665 width = dirty_region.bottom_right_x - x + 1;
666 height = dirty_region.bottom_right_y - y + 1;
668 redraw_screen_rect (x, y, width, height);
670 dirty_region_reset ();
673 static void
674 write_char (void)
676 struct grub_colored_char *p;
677 struct grub_font_glyph *glyph;
678 grub_video_color_t color;
679 grub_video_color_t bgcolor;
680 unsigned int x;
681 unsigned int y;
682 int ascent;
683 unsigned int height;
684 unsigned int width;
686 /* Find out active character. */
687 p = (virtual_screen.text_buffer
688 + virtual_screen.cursor_x
689 + (virtual_screen.cursor_y * virtual_screen.columns));
691 p -= p->index;
693 /* Get glyph for character. */
694 glyph = grub_font_get_glyph (virtual_screen.font, p->code);
695 ascent = grub_font_get_ascent (virtual_screen.font);
697 width = virtual_screen.normal_char_width * calculate_character_width(glyph);
698 height = virtual_screen.normal_char_height;
700 color = p->fg_color;
701 bgcolor = p->bg_color;
703 x = virtual_screen.cursor_x * virtual_screen.normal_char_width;
704 y = virtual_screen.cursor_y * virtual_screen.normal_char_height;
706 /* Render glyph to text layer. */
707 grub_video_set_active_render_target (text_layer);
708 grub_video_fill_rect (bgcolor, x, y, width, height);
709 grub_font_draw_glyph (glyph, color, x, y + ascent);
710 grub_video_set_active_render_target (GRUB_VIDEO_RENDER_TARGET_DISPLAY);
712 /* Mark character to be drawn. */
713 dirty_region_add (virtual_screen.offset_x + x, virtual_screen.offset_y + y,
714 width, height);
717 static void
718 draw_cursor (int show)
720 unsigned int x;
721 unsigned int y;
722 unsigned int width;
723 unsigned int height;
724 grub_video_color_t color;
726 /* Determine cursor properties and position on text layer. */
727 x = virtual_screen.cursor_x * virtual_screen.normal_char_width;
728 y = (virtual_screen.cursor_y * virtual_screen.normal_char_height
729 + grub_font_get_ascent (virtual_screen.font));
730 width = virtual_screen.normal_char_width;
731 height = 2;
733 if (show)
735 color = virtual_screen.fg_color;
737 else
739 color = virtual_screen.bg_color;
740 y = virtual_screen.cursor_y * virtual_screen.normal_char_height;
741 height = virtual_screen.normal_char_height;
744 /* Render cursor to text layer. */
745 grub_video_set_active_render_target (text_layer);
746 grub_video_fill_rect (color, x, y, width, height);
747 grub_video_set_active_render_target (GRUB_VIDEO_RENDER_TARGET_DISPLAY);
749 /* Mark cursor to be redrawn. */
750 dirty_region_add (virtual_screen.offset_x + x, virtual_screen.offset_y + y,
751 width, height);
754 static void
755 scroll_up (void)
757 unsigned int i;
758 grub_video_color_t color;
760 /* If we don't have background bitmap, remove cursor. */
761 if (!bitmap)
763 /* Remove cursor. */
764 draw_cursor (0);
766 /* Redraw only changed regions. */
767 dirty_region_redraw ();
770 /* Scroll text buffer with one line to up. */
771 grub_memmove (virtual_screen.text_buffer,
772 virtual_screen.text_buffer + virtual_screen.columns,
773 sizeof (*virtual_screen.text_buffer)
774 * virtual_screen.columns
775 * (virtual_screen.rows - 1));
777 /* Clear last line in text buffer. */
778 for (i = virtual_screen.columns * (virtual_screen.rows - 1);
779 i < virtual_screen.columns * virtual_screen.rows;
780 i++)
782 virtual_screen.text_buffer[i].code = ' ';
783 virtual_screen.text_buffer[i].fg_color = virtual_screen.fg_color;
784 virtual_screen.text_buffer[i].bg_color = virtual_screen.bg_color;
785 virtual_screen.text_buffer[i].width = 0;
786 virtual_screen.text_buffer[i].index = 0;
789 /* Scroll physical screen. */
790 grub_video_set_active_render_target (text_layer);
791 color = virtual_screen.bg_color;
792 grub_video_scroll (color, 0, -virtual_screen.normal_char_height);
793 grub_video_set_active_render_target (GRUB_VIDEO_RENDER_TARGET_DISPLAY);
795 /* If we have bitmap, re-draw screen, otherwise scroll physical screen too. */
796 if (bitmap)
798 /* Mark virtual screen to be redrawn. */
799 dirty_region_add_virtualscreen ();
801 else
803 /* Clear new border area. */
804 grub_video_fill_rect (color,
805 virtual_screen.offset_x, virtual_screen.offset_y,
806 virtual_screen.width, virtual_screen.normal_char_height);
808 /* Scroll physical screen. */
809 grub_video_scroll (color, 0, -virtual_screen.normal_char_height);
811 /* Draw cursor if visible. */
812 if (virtual_screen.cursor_state)
813 draw_cursor (1);
817 static void
818 grub_gfxterm_putchar (grub_uint32_t c)
820 if (c == '\a')
821 /* FIXME */
822 return;
824 if (c == '\b' || c == '\n' || c == '\r')
826 /* Erase current cursor, if any. */
827 if (virtual_screen.cursor_state)
828 draw_cursor (0);
830 switch (c)
832 case '\b':
833 if (virtual_screen.cursor_x > 0)
834 virtual_screen.cursor_x--;
835 break;
837 case '\n':
838 if (virtual_screen.cursor_y >= virtual_screen.rows - 1)
839 scroll_up ();
840 else
841 virtual_screen.cursor_y++;
842 break;
844 case '\r':
845 virtual_screen.cursor_x = 0;
846 break;
849 /* Redraw cursor if visible. */
850 if (virtual_screen.cursor_state)
851 draw_cursor (1);
853 else
855 struct grub_font_glyph *glyph;
856 struct grub_colored_char *p;
857 unsigned char char_width;
859 /* Erase current cursor, if any. */
860 if (virtual_screen.cursor_state)
861 draw_cursor (0);
863 /* Get properties of the character. */
864 glyph = grub_font_get_glyph (virtual_screen.font, c);
866 /* Calculate actual character width for glyph. This is number of
867 times of normal_font_width. */
868 char_width = calculate_character_width(glyph);
870 /* If we are about to exceed line length, wrap to next line. */
871 if (virtual_screen.cursor_x + char_width > virtual_screen.columns)
872 grub_putchar ('\n');
874 /* Find position on virtual screen, and fill information. */
875 p = (virtual_screen.text_buffer +
876 virtual_screen.cursor_x +
877 virtual_screen.cursor_y * virtual_screen.columns);
878 p->code = c;
879 p->fg_color = virtual_screen.fg_color;
880 p->bg_color = virtual_screen.bg_color;
881 p->width = char_width - 1;
882 p->index = 0;
884 /* If we have large glyph, add fixup info. */
885 if (char_width > 1)
887 unsigned i;
889 for (i = 1; i < char_width; i++)
891 p[i].code = ' ';
892 p[i].width = char_width - 1;
893 p[i].index = i;
897 /* Draw glyph. */
898 write_char ();
900 /* Make sure we scroll screen when needed and wrap line correctly. */
901 virtual_screen.cursor_x += char_width;
902 if (virtual_screen.cursor_x >= virtual_screen.columns)
904 virtual_screen.cursor_x = 0;
906 if (virtual_screen.cursor_y >= virtual_screen.rows - 1)
907 scroll_up ();
908 else
909 virtual_screen.cursor_y++;
912 /* Draw cursor if visible. */
913 if (virtual_screen.cursor_state)
914 draw_cursor (1);
918 /* Use ASCII characters to determine normal character width. */
919 static unsigned int
920 calculate_normal_character_width (grub_font_t font)
922 struct grub_font_glyph *glyph;
923 unsigned int width = 0;
924 unsigned int i;
926 /* Get properties of every printable ASCII character. */
927 for (i = 32; i < 127; i++)
929 glyph = grub_font_get_glyph (font, i);
931 /* Skip unknown characters. Should never happen on normal conditions. */
932 if (! glyph)
933 continue;
935 if (glyph->device_width > width)
936 width = glyph->device_width;
939 return width;
942 static unsigned char
943 calculate_character_width (struct grub_font_glyph *glyph)
945 if (! glyph || glyph->device_width == 0)
946 return 1;
948 return (glyph->device_width
949 + (virtual_screen.normal_char_width - 1))
950 / virtual_screen.normal_char_width;
953 static grub_ssize_t
954 grub_gfxterm_getcharwidth (grub_uint32_t c)
956 struct grub_font_glyph *glyph;
957 unsigned char char_width;
959 /* Get properties of the character. */
960 glyph = grub_font_get_glyph (virtual_screen.font, c);
962 /* Calculate actual character width for glyph. */
963 char_width = calculate_character_width (glyph);
965 return char_width;
968 static grub_uint16_t
969 grub_virtual_screen_getwh (void)
971 return (virtual_screen.columns << 8) | virtual_screen.rows;
974 static grub_uint16_t
975 grub_virtual_screen_getxy (void)
977 return ((virtual_screen.cursor_x << 8) | virtual_screen.cursor_y);
980 static void
981 grub_gfxterm_gotoxy (grub_uint8_t x, grub_uint8_t y)
983 if (x >= virtual_screen.columns)
984 x = virtual_screen.columns - 1;
986 if (y >= virtual_screen.rows)
987 y = virtual_screen.rows - 1;
989 /* Erase current cursor, if any. */
990 if (virtual_screen.cursor_state)
991 draw_cursor (0);
993 virtual_screen.cursor_x = x;
994 virtual_screen.cursor_y = y;
996 /* Draw cursor if visible. */
997 if (virtual_screen.cursor_state)
998 draw_cursor (1);
1001 static void
1002 grub_virtual_screen_cls (void)
1004 grub_uint32_t i;
1006 for (i = 0; i < virtual_screen.columns * virtual_screen.rows; i++)
1008 virtual_screen.text_buffer[i].code = ' ';
1009 virtual_screen.text_buffer[i].fg_color = virtual_screen.fg_color;
1010 virtual_screen.text_buffer[i].bg_color = virtual_screen.bg_color;
1011 virtual_screen.text_buffer[i].width = 0;
1012 virtual_screen.text_buffer[i].index = 0;
1015 virtual_screen.cursor_x = virtual_screen.cursor_y = 0;
1018 static void
1019 grub_gfxterm_cls (void)
1021 grub_video_color_t color;
1023 /* Clear virtual screen. */
1024 grub_virtual_screen_cls ();
1026 /* Clear text layer. */
1027 grub_video_set_active_render_target (text_layer);
1028 color = virtual_screen.bg_color;
1029 grub_video_fill_rect (color, 0, 0, mode_info.width, mode_info.height);
1030 grub_video_set_active_render_target (GRUB_VIDEO_RENDER_TARGET_DISPLAY);
1032 /* Mark virtual screen to be redrawn. */
1033 dirty_region_add_virtualscreen ();
1036 static void
1037 grub_virtual_screen_setcolorstate (grub_term_color_state state)
1039 switch (state)
1041 case GRUB_TERM_COLOR_STANDARD:
1042 virtual_screen.term_color = virtual_screen.standard_color_setting;
1043 break;
1045 case GRUB_TERM_COLOR_NORMAL:
1046 virtual_screen.term_color = virtual_screen.normal_color_setting;
1047 break;
1049 case GRUB_TERM_COLOR_HIGHLIGHT:
1050 virtual_screen.term_color = virtual_screen.highlight_color_setting;
1051 break;
1053 default:
1054 break;
1057 /* Change color to virtual terminal. */
1058 set_term_color (virtual_screen.term_color);
1061 static void
1062 grub_virtual_screen_setcolor (grub_uint8_t normal_color,
1063 grub_uint8_t highlight_color)
1065 virtual_screen.normal_color_setting = normal_color;
1066 virtual_screen.highlight_color_setting = highlight_color;
1069 static void
1070 grub_virtual_screen_getcolor (grub_uint8_t *normal_color,
1071 grub_uint8_t *highlight_color)
1073 *normal_color = virtual_screen.normal_color_setting;
1074 *highlight_color = virtual_screen.highlight_color_setting;
1077 static void
1078 grub_gfxterm_setcursor (int on)
1080 if (virtual_screen.cursor_state != on)
1082 if (virtual_screen.cursor_state)
1083 draw_cursor (0);
1084 else
1085 draw_cursor (1);
1087 virtual_screen.cursor_state = on;
1091 static void
1092 grub_gfxterm_refresh (void)
1094 /* Redraw only changed regions. */
1095 dirty_region_redraw ();
1098 static grub_err_t
1099 grub_gfxterm_background_image_cmd (struct grub_arg_list *state __attribute__ ((unused)),
1100 int argc,
1101 char **args)
1103 /* Check that we have video adapter active. */
1104 if (grub_video_get_info(NULL) != GRUB_ERR_NONE)
1105 return grub_errno;
1107 /* Destroy existing background bitmap if loaded. */
1108 if (bitmap)
1110 grub_video_bitmap_destroy (bitmap);
1111 bitmap = 0;
1113 /* Mark whole screen as dirty. */
1114 dirty_region_reset ();
1115 dirty_region_add (0, 0, mode_info.width, mode_info.height);
1118 /* If filename was provided, try to load that. */
1119 if (argc >= 1)
1121 /* Try to load new one. */
1122 grub_video_bitmap_load (&bitmap, args[0]);
1123 if (grub_errno != GRUB_ERR_NONE)
1124 return grub_errno;
1126 /* If bitmap was loaded correctly, display it. */
1127 if (bitmap)
1129 /* Determine bitmap dimensions. */
1130 bitmap_width = grub_video_bitmap_get_width (bitmap);
1131 bitmap_height = grub_video_bitmap_get_width (bitmap);
1133 /* Mark whole screen as dirty. */
1134 dirty_region_reset ();
1135 dirty_region_add (0, 0, mode_info.width, mode_info.height);
1139 /* All was ok. */
1140 grub_errno = GRUB_ERR_NONE;
1141 return grub_errno;
1144 static struct grub_term_output grub_video_term =
1146 .name = "gfxterm",
1147 .init = grub_gfxterm_init,
1148 .fini = grub_gfxterm_fini,
1149 .putchar = grub_gfxterm_putchar,
1150 .getcharwidth = grub_gfxterm_getcharwidth,
1151 .getwh = grub_virtual_screen_getwh,
1152 .getxy = grub_virtual_screen_getxy,
1153 .gotoxy = grub_gfxterm_gotoxy,
1154 .cls = grub_gfxterm_cls,
1155 .setcolorstate = grub_virtual_screen_setcolorstate,
1156 .setcolor = grub_virtual_screen_setcolor,
1157 .getcolor = grub_virtual_screen_getcolor,
1158 .setcursor = grub_gfxterm_setcursor,
1159 .refresh = grub_gfxterm_refresh,
1160 .flags = 0,
1161 .next = 0
1164 GRUB_MOD_INIT(term_gfxterm)
1166 my_mod = mod;
1167 grub_term_register_output (&grub_video_term);
1169 grub_register_command ("background_image",
1170 grub_gfxterm_background_image_cmd,
1171 GRUB_COMMAND_FLAG_BOTH,
1172 "background_image",
1173 "Load background image for active terminal",
1177 GRUB_MOD_FINI(term_gfxterm)
1179 grub_unregister_command ("bgimage");
1180 grub_term_unregister_output (&grub_video_term);