Merge branch 'dmigen' into phcoder
[grub2/phcoder.git] / term / gfxterm.c
blob79533f0a149d812fbcc3e055e5d933d2a536e553
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/bitmap_scale.h>
30 #include <grub/extcmd.h>
32 #define DEFAULT_VIDEO_MODE "1024x768,800x600,640x480"
33 #define DEFAULT_BORDER_WIDTH 10
35 #define DEFAULT_STANDARD_COLOR 0x07
36 #define DEFAULT_NORMAL_COLOR 0x07
37 #define DEFAULT_HIGHLIGHT_COLOR 0x70
39 struct grub_dirty_region
41 int top_left_x;
42 int top_left_y;
43 int bottom_right_x;
44 int bottom_right_y;
47 struct grub_colored_char
49 /* An Unicode codepoint. */
50 grub_uint32_t code;
52 /* Color values. */
53 grub_video_color_t fg_color;
54 grub_video_color_t bg_color;
56 /* The width of this character minus one. */
57 unsigned char width;
59 /* The column index of this character. */
60 unsigned char index;
63 struct grub_virtual_screen
65 /* Dimensions of the virtual screen in pixels. */
66 unsigned int width;
67 unsigned int height;
69 /* Offset in the display in pixels. */
70 unsigned int offset_x;
71 unsigned int offset_y;
73 /* TTY Character sizes in pixes. */
74 unsigned int normal_char_width;
75 unsigned int normal_char_height;
77 /* Virtual screen TTY size in characters. */
78 unsigned int columns;
79 unsigned int rows;
81 /* Current cursor location in characters. */
82 unsigned int cursor_x;
83 unsigned int cursor_y;
85 /* Current cursor state. */
86 int cursor_state;
88 /* Font settings. */
89 grub_font_t font;
91 /* Terminal color settings. */
92 grub_uint8_t standard_color_setting;
93 grub_uint8_t normal_color_setting;
94 grub_uint8_t highlight_color_setting;
95 grub_uint8_t term_color;
97 /* Color settings. */
98 grub_video_color_t fg_color;
99 grub_video_color_t bg_color;
101 /* Text buffer for virtual screen. Contains (columns * rows) number
102 of entries. */
103 struct grub_colored_char *text_buffer;
106 static int refcount;
107 static struct grub_video_render_target *render_target;
108 static grub_video_rect_t window;
109 static struct grub_virtual_screen virtual_screen;
110 static grub_gfxterm_repaint_callback_t repaint_callback;
112 static grub_err_t init_window (struct grub_video_render_target *target,
113 int x, int y, int width, int height,
114 const char *font_name, int border_width);
116 static void destroy_window (void);
119 static struct grub_video_render_target *text_layer;
121 static unsigned int bitmap_width;
122 static unsigned int bitmap_height;
123 static struct grub_video_bitmap *bitmap;
125 static struct grub_dirty_region dirty_region;
127 static void dirty_region_reset (void);
129 static int dirty_region_is_empty (void);
131 static void dirty_region_add (int x, int y,
132 unsigned int width, unsigned int height);
134 static unsigned int calculate_normal_character_width (grub_font_t font);
136 static unsigned char calculate_character_width (struct grub_font_glyph *glyph);
138 static void
139 set_term_color (grub_uint8_t term_color)
141 struct grub_video_render_target *old_target;
143 /* Save previous target and switch to text layer. */
144 grub_video_get_active_render_target (&old_target);
145 grub_video_set_active_render_target (text_layer);
147 /* Map terminal color to text layer compatible video colors. */
148 virtual_screen.fg_color = grub_video_map_color(term_color & 0x0f);
150 /* Special case: use black as transparent color. */
151 if (((term_color >> 4) & 0x0f) == 0)
153 virtual_screen.bg_color = grub_video_map_rgba(0, 0, 0, 0);
155 else
157 virtual_screen.bg_color = grub_video_map_color((term_color >> 4) & 0x0f);
160 /* Restore previous target. */
161 grub_video_set_active_render_target (old_target);
164 static void
165 grub_virtual_screen_free (void)
167 /* If virtual screen has been allocated, free it. */
168 if (virtual_screen.text_buffer != 0)
169 grub_free (virtual_screen.text_buffer);
171 /* Reset virtual screen data. */
172 grub_memset (&virtual_screen, 0, sizeof (virtual_screen));
174 /* Free render targets. */
175 grub_video_delete_render_target (text_layer);
176 text_layer = 0;
179 static grub_err_t
180 grub_virtual_screen_setup (unsigned int x, unsigned int y,
181 unsigned int width, unsigned int height,
182 const char *font_name)
184 /* Free old virtual screen. */
185 grub_virtual_screen_free ();
187 /* Initialize with default data. */
188 virtual_screen.font = grub_font_get (font_name);
189 if (!virtual_screen.font)
190 return grub_error (GRUB_ERR_BAD_FONT,
191 "No font loaded.");
192 virtual_screen.width = width;
193 virtual_screen.height = height;
194 virtual_screen.offset_x = x;
195 virtual_screen.offset_y = y;
196 virtual_screen.normal_char_width =
197 calculate_normal_character_width (virtual_screen.font);
198 virtual_screen.normal_char_height =
199 grub_font_get_max_char_height (virtual_screen.font);
200 virtual_screen.cursor_x = 0;
201 virtual_screen.cursor_y = 0;
202 virtual_screen.cursor_state = 1;
204 /* Calculate size of text buffer. */
205 virtual_screen.columns = virtual_screen.width / virtual_screen.normal_char_width;
206 virtual_screen.rows = virtual_screen.height / virtual_screen.normal_char_height;
208 /* Allocate memory for text buffer. */
209 virtual_screen.text_buffer =
210 (struct grub_colored_char *) grub_malloc (virtual_screen.columns
211 * virtual_screen.rows
212 * sizeof (*virtual_screen.text_buffer));
213 if (grub_errno != GRUB_ERR_NONE)
214 return grub_errno;
216 /* Create new render target for text layer. */
217 grub_video_create_render_target (&text_layer,
218 virtual_screen.width,
219 virtual_screen.height,
220 GRUB_VIDEO_MODE_TYPE_RGB
221 | GRUB_VIDEO_MODE_TYPE_ALPHA);
222 if (grub_errno != GRUB_ERR_NONE)
223 return grub_errno;
225 /* As we want to have colors compatible with rendering target,
226 we can only have those after mode is initialized. */
227 grub_video_set_active_render_target (text_layer);
229 virtual_screen.standard_color_setting = DEFAULT_STANDARD_COLOR;
230 virtual_screen.normal_color_setting = DEFAULT_NORMAL_COLOR;
231 virtual_screen.highlight_color_setting = DEFAULT_HIGHLIGHT_COLOR;
233 virtual_screen.term_color = virtual_screen.normal_color_setting;
235 set_term_color (virtual_screen.term_color);
237 grub_video_set_active_render_target (GRUB_VIDEO_RENDER_TARGET_DISPLAY);
239 return grub_errno;
242 static int NESTED_FUNC_ATTR video_hook (grub_video_adapter_t p __attribute__ ((unused)),
243 struct grub_video_mode_info *info)
245 return ! (info->mode_type & GRUB_VIDEO_MODE_TYPE_PURE_TEXT);
248 static grub_err_t
249 init_window (struct grub_video_render_target *target,
250 int x, int y, int width, int height,
251 const char *font_name, int border_width)
253 /* Clean up any prior instance. */
254 destroy_window ();
256 /* Create virtual screen. */
257 if (grub_virtual_screen_setup (border_width, border_width,
258 width - 2 * border_width,
259 height - 2 * border_width,
260 font_name)
261 != GRUB_ERR_NONE)
263 return grub_errno;
266 /* Set the render target. */
267 render_target = target;
269 /* Set window bounds. */
270 window.x = x;
271 window.y = y;
272 window.width = width;
273 window.height = height;
275 /* Mark whole window as dirty. */
276 dirty_region_reset ();
277 dirty_region_add (0, 0, width, height);
279 return grub_errno;
282 grub_err_t
283 grub_gfxterm_init_window (struct grub_video_render_target *target,
284 int x, int y, int width, int height,
285 const char *font_name, int border_width)
287 if (refcount++ == 0)
288 init_window (target, x, y, width, height, font_name, border_width);
289 return grub_errno;
292 static grub_err_t
293 grub_gfxterm_init (void)
295 const char *font_name;
296 const char *modevar;
297 struct grub_video_mode_info mode_info;
298 char *tmp;
299 grub_err_t err;
301 /* If gfxterm has already been initialized by calling the init_window
302 function, then leave it alone when it is set as the current terminal. */
303 if (refcount++ != 0)
304 return GRUB_ERR_NONE;
306 /* Select the font to use. */
307 font_name = grub_env_get ("gfxterm_font");
308 if (! font_name)
309 font_name = ""; /* Allow fallback to any font. */
311 /* Parse gfxmode environment variable if set. */
312 modevar = grub_env_get ("gfxmode");
313 if (! modevar || *modevar == 0)
314 err = grub_video_set_mode (DEFAULT_VIDEO_MODE, video_hook);
315 else
317 tmp = grub_malloc (grub_strlen (modevar)
318 + sizeof (DEFAULT_VIDEO_MODE) + 1);
319 if (! tmp)
320 return grub_errno;
321 grub_sprintf (tmp, "%s;" DEFAULT_VIDEO_MODE, modevar);
322 err = grub_video_set_mode (tmp, video_hook);
323 grub_free (tmp);
326 if (err)
327 return err;
329 err = grub_video_get_info (&mode_info);
330 /* Figure out what mode we ended up. */
331 if (err)
332 return err;
334 /* Make sure screen is black. */
335 grub_video_fill_rect (grub_video_map_rgb (0, 0, 0),
336 0, 0, mode_info.width, mode_info.height);
337 bitmap = 0;
339 /* Select the font to use. */
340 font_name = grub_env_get ("gfxterm_font");
341 if (! font_name)
342 font_name = ""; /* Allow fallback to any font. */
344 /* Leave borders for virtual screen. */
345 if (init_window (GRUB_VIDEO_RENDER_TARGET_DISPLAY,
346 0, 0, mode_info.width, mode_info.height,
347 font_name,
348 DEFAULT_BORDER_WIDTH) != GRUB_ERR_NONE)
350 grub_video_restore ();
351 return grub_errno;
354 return grub_errno;
357 static void
358 destroy_window (void)
360 if (bitmap)
362 grub_video_bitmap_destroy (bitmap);
363 bitmap = 0;
366 repaint_callback = 0;
367 grub_virtual_screen_free ();
370 void
371 grub_gfxterm_destroy_window (void)
373 if (--refcount == 0)
374 destroy_window ();
377 static grub_err_t
378 grub_gfxterm_fini (void)
380 /* Don't destroy an explicitly initialized terminal instance when it is
381 unset as the current terminal. */
382 if (--refcount == 0)
384 destroy_window ();
385 grub_video_restore ();
388 /* Clear error state. */
389 return (grub_errno = GRUB_ERR_NONE);
392 static void
393 redraw_screen_rect (unsigned int x, unsigned int y,
394 unsigned int width, unsigned int height)
396 grub_video_color_t color;
397 grub_video_rect_t saved_view;
399 grub_video_set_active_render_target (render_target);
400 /* Save viewport and set it to our window. */
401 grub_video_get_viewport ((unsigned *) &saved_view.x,
402 (unsigned *) &saved_view.y,
403 (unsigned *) &saved_view.width,
404 (unsigned *) &saved_view.height);
405 grub_video_set_viewport (window.x, window.y, window.width, window.height);
407 if (bitmap)
409 /* Render bitmap as background. */
410 grub_video_blit_bitmap (bitmap, GRUB_VIDEO_BLIT_REPLACE, x, y,
411 x, y,
412 width, height);
414 /* If bitmap is smaller than requested blit area, use background
415 color. */
416 color = virtual_screen.bg_color;
418 /* Fill right side of the bitmap if needed. */
419 if ((x + width >= bitmap_width) && (y < bitmap_height))
421 int w = (x + width) - bitmap_width;
422 int h = height;
423 unsigned int tx = x;
425 if (y + height >= bitmap_height)
427 h = bitmap_height - y;
430 if (bitmap_width > tx)
432 tx = bitmap_width;
435 /* Render background layer. */
436 grub_video_fill_rect (color, tx, y, w, h);
439 /* Fill bottom side of the bitmap if needed. */
440 if (y + height >= bitmap_height)
442 int h = (y + height) - bitmap_height;
443 unsigned int ty = y;
445 if (bitmap_height > ty)
447 ty = bitmap_height;
450 /* Render background layer. */
451 grub_video_fill_rect (color, x, ty, width, h);
454 /* Render text layer as blended. */
455 grub_video_blit_render_target (text_layer, GRUB_VIDEO_BLIT_BLEND, x, y,
456 x - virtual_screen.offset_x,
457 y - virtual_screen.offset_y,
458 width, height);
460 else
462 /* Render background layer. */
463 color = virtual_screen.bg_color;
464 grub_video_fill_rect (color, x, y, width, height);
466 /* Render text layer as replaced (to get texts background color). */
467 grub_video_blit_render_target (text_layer, GRUB_VIDEO_BLIT_REPLACE, x, y,
468 x - virtual_screen.offset_x,
469 y - virtual_screen.offset_y,
470 width, height);
473 /* Restore saved viewport. */
474 grub_video_set_viewport (saved_view.x, saved_view.y,
475 saved_view.width, saved_view.height);
476 grub_video_set_active_render_target (GRUB_VIDEO_RENDER_TARGET_DISPLAY);
478 if (repaint_callback)
479 repaint_callback (x, y, width, height);
482 static void
483 dirty_region_reset (void)
485 dirty_region.top_left_x = -1;
486 dirty_region.top_left_y = -1;
487 dirty_region.bottom_right_x = -1;
488 dirty_region.bottom_right_y = -1;
491 static int
492 dirty_region_is_empty (void)
494 if ((dirty_region.top_left_x == -1)
495 || (dirty_region.top_left_y == -1)
496 || (dirty_region.bottom_right_x == -1)
497 || (dirty_region.bottom_right_y == -1))
498 return 1;
499 return 0;
502 static void
503 dirty_region_add (int x, int y, unsigned int width, unsigned int height)
505 if ((width == 0) || (height == 0))
506 return;
508 if (dirty_region_is_empty ())
510 dirty_region.top_left_x = x;
511 dirty_region.top_left_y = y;
512 dirty_region.bottom_right_x = x + width - 1;
513 dirty_region.bottom_right_y = y + height - 1;
515 else
517 if (x < dirty_region.top_left_x)
518 dirty_region.top_left_x = x;
519 if (y < dirty_region.top_left_y)
520 dirty_region.top_left_y = y;
521 if ((x + (int)width - 1) > dirty_region.bottom_right_x)
522 dirty_region.bottom_right_x = x + width - 1;
523 if ((y + (int)height - 1) > dirty_region.bottom_right_y)
524 dirty_region.bottom_right_y = y + height - 1;
528 static void
529 dirty_region_add_virtualscreen (void)
531 /* Mark virtual screen as dirty. */
532 dirty_region_add (virtual_screen.offset_x, virtual_screen.offset_y,
533 virtual_screen.width, virtual_screen.height);
537 static void
538 dirty_region_redraw (void)
540 int x;
541 int y;
542 int width;
543 int height;
545 if (dirty_region_is_empty ())
546 return;
548 x = dirty_region.top_left_x;
549 y = dirty_region.top_left_y;
551 width = dirty_region.bottom_right_x - x + 1;
552 height = dirty_region.bottom_right_y - y + 1;
554 redraw_screen_rect (x, y, width, height);
556 dirty_region_reset ();
557 grub_video_swap_buffers ();
560 static void
561 write_char (void)
563 struct grub_colored_char *p;
564 struct grub_font_glyph *glyph;
565 grub_video_color_t color;
566 grub_video_color_t bgcolor;
567 unsigned int x;
568 unsigned int y;
569 int ascent;
570 unsigned int height;
571 unsigned int width;
573 /* Find out active character. */
574 p = (virtual_screen.text_buffer
575 + virtual_screen.cursor_x
576 + (virtual_screen.cursor_y * virtual_screen.columns));
578 p -= p->index;
580 /* Get glyph for character. */
581 glyph = grub_font_get_glyph (virtual_screen.font, p->code);
582 ascent = grub_font_get_ascent (virtual_screen.font);
584 width = virtual_screen.normal_char_width * calculate_character_width(glyph);
585 height = virtual_screen.normal_char_height;
587 color = p->fg_color;
588 bgcolor = p->bg_color;
590 x = virtual_screen.cursor_x * virtual_screen.normal_char_width;
591 y = virtual_screen.cursor_y * virtual_screen.normal_char_height;
593 /* Render glyph to text layer. */
594 grub_video_set_active_render_target (text_layer);
595 grub_video_fill_rect (bgcolor, x, y, width, height);
596 grub_font_draw_glyph (glyph, color, x, y + ascent);
597 grub_video_set_active_render_target (GRUB_VIDEO_RENDER_TARGET_DISPLAY);
599 /* Mark character to be drawn. */
600 dirty_region_add (virtual_screen.offset_x + x, virtual_screen.offset_y + y,
601 width, height);
604 static void
605 draw_cursor (int show)
607 write_char ();
609 if (show)
611 unsigned int x;
612 unsigned int y;
613 unsigned int width;
614 unsigned int height;
615 grub_video_color_t color;
617 /* Determine cursor properties and position on text layer. */
618 x = virtual_screen.cursor_x * virtual_screen.normal_char_width;
619 width = virtual_screen.normal_char_width;
620 color = virtual_screen.fg_color;
621 y = (virtual_screen.cursor_y * virtual_screen.normal_char_height
622 + grub_font_get_ascent (virtual_screen.font));
623 height = 2;
625 /* Render cursor to text layer. */
626 grub_video_set_active_render_target (text_layer);
627 grub_video_fill_rect (color, x, y, width, height);
628 grub_video_set_active_render_target (GRUB_VIDEO_RENDER_TARGET_DISPLAY);
630 /* Mark cursor to be redrawn. */
631 dirty_region_add (virtual_screen.offset_x + x,
632 virtual_screen.offset_y + y,
633 width, height);
637 static void
638 scroll_up (void)
640 unsigned int i;
641 grub_video_color_t color;
643 /* If we don't have background bitmap, remove cursor. */
644 if (!bitmap)
646 /* Remove cursor. */
647 draw_cursor (0);
649 /* Redraw only changed regions. */
650 dirty_region_redraw ();
653 /* Scroll text buffer with one line to up. */
654 grub_memmove (virtual_screen.text_buffer,
655 virtual_screen.text_buffer + virtual_screen.columns,
656 sizeof (*virtual_screen.text_buffer)
657 * virtual_screen.columns
658 * (virtual_screen.rows - 1));
660 /* Clear last line in text buffer. */
661 for (i = virtual_screen.columns * (virtual_screen.rows - 1);
662 i < virtual_screen.columns * virtual_screen.rows;
663 i++)
665 virtual_screen.text_buffer[i].code = ' ';
666 virtual_screen.text_buffer[i].fg_color = virtual_screen.fg_color;
667 virtual_screen.text_buffer[i].bg_color = virtual_screen.bg_color;
668 virtual_screen.text_buffer[i].width = 0;
669 virtual_screen.text_buffer[i].index = 0;
672 /* Scroll physical screen. */
673 grub_video_set_active_render_target (text_layer);
674 color = virtual_screen.bg_color;
675 grub_video_scroll (color, 0, -virtual_screen.normal_char_height);
676 grub_video_set_active_render_target (GRUB_VIDEO_RENDER_TARGET_DISPLAY);
678 /* If we have bitmap, re-draw screen, otherwise scroll physical screen too. */
679 if (bitmap)
681 /* Mark virtual screen to be redrawn. */
682 dirty_region_add_virtualscreen ();
684 else
686 grub_video_rect_t saved_view;
687 grub_video_set_active_render_target (render_target);
688 /* Save viewport and set it to our window. */
689 grub_video_get_viewport ((unsigned *) &saved_view.x,
690 (unsigned *) &saved_view.y,
691 (unsigned *) &saved_view.width,
692 (unsigned *) &saved_view.height);
693 grub_video_set_viewport (window.x, window.y, window.width, window.height);
695 /* Clear new border area. */
696 grub_video_fill_rect (color,
697 virtual_screen.offset_x, virtual_screen.offset_y,
698 virtual_screen.width, virtual_screen.normal_char_height);
700 /* Scroll physical screen. */
701 grub_video_scroll (color, 0, -virtual_screen.normal_char_height);
703 /* Restore saved viewport. */
704 grub_video_set_viewport (saved_view.x, saved_view.y,
705 saved_view.width, saved_view.height);
706 grub_video_set_active_render_target (GRUB_VIDEO_RENDER_TARGET_DISPLAY);
708 /* Draw cursor if visible. */
709 if (virtual_screen.cursor_state)
710 draw_cursor (1);
713 if (repaint_callback)
714 repaint_callback (window.x, window.y, window.width, window.height);
717 static void
718 grub_gfxterm_putchar (grub_uint32_t c)
720 if (c == '\a')
721 /* FIXME */
722 return;
724 /* Erase current cursor, if any. */
725 if (virtual_screen.cursor_state)
726 draw_cursor (0);
728 if (c == '\b' || c == '\n' || c == '\r')
730 switch (c)
732 case '\b':
733 if (virtual_screen.cursor_x > 0)
734 virtual_screen.cursor_x--;
735 break;
737 case '\n':
738 if (virtual_screen.cursor_y >= virtual_screen.rows - 1)
739 scroll_up ();
740 else
741 virtual_screen.cursor_y++;
742 break;
744 case '\r':
745 virtual_screen.cursor_x = 0;
746 break;
749 else
751 struct grub_font_glyph *glyph;
752 struct grub_colored_char *p;
753 unsigned char char_width;
755 /* Get properties of the character. */
756 glyph = grub_font_get_glyph (virtual_screen.font, c);
758 /* Calculate actual character width for glyph. This is number of
759 times of normal_font_width. */
760 char_width = calculate_character_width(glyph);
762 /* If we are about to exceed line length, wrap to next line. */
763 if (virtual_screen.cursor_x + char_width > virtual_screen.columns)
764 grub_putchar ('\n');
766 /* Find position on virtual screen, and fill information. */
767 p = (virtual_screen.text_buffer +
768 virtual_screen.cursor_x +
769 virtual_screen.cursor_y * virtual_screen.columns);
770 p->code = c;
771 p->fg_color = virtual_screen.fg_color;
772 p->bg_color = virtual_screen.bg_color;
773 p->width = char_width - 1;
774 p->index = 0;
776 /* If we have large glyph, add fixup info. */
777 if (char_width > 1)
779 unsigned i;
781 for (i = 1; i < char_width; i++)
783 p[i].code = ' ';
784 p[i].width = char_width - 1;
785 p[i].index = i;
789 /* Draw glyph. */
790 write_char ();
792 /* Make sure we scroll screen when needed and wrap line correctly. */
793 virtual_screen.cursor_x += char_width;
794 if (virtual_screen.cursor_x >= virtual_screen.columns)
796 virtual_screen.cursor_x = 0;
798 if (virtual_screen.cursor_y >= virtual_screen.rows - 1)
799 scroll_up ();
800 else
801 virtual_screen.cursor_y++;
805 /* Redraw cursor if it should be visible. */
806 /* Note: This will redraw the character as well, which means that the
807 above call to write_char is redundant when the cursor is showing. */
808 if (virtual_screen.cursor_state)
809 draw_cursor (1);
812 /* Use ASCII characters to determine normal character width. */
813 static unsigned int
814 calculate_normal_character_width (grub_font_t font)
816 struct grub_font_glyph *glyph;
817 unsigned int width = 0;
818 unsigned int i;
820 /* Get properties of every printable ASCII character. */
821 for (i = 32; i < 127; i++)
823 glyph = grub_font_get_glyph (font, i);
825 /* Skip unknown characters. Should never happen on normal conditions. */
826 if (! glyph)
827 continue;
829 if (glyph->device_width > width)
830 width = glyph->device_width;
833 return width;
836 static unsigned char
837 calculate_character_width (struct grub_font_glyph *glyph)
839 if (! glyph || glyph->device_width == 0)
840 return 1;
842 return (glyph->device_width
843 + (virtual_screen.normal_char_width - 1))
844 / virtual_screen.normal_char_width;
847 static grub_ssize_t
848 grub_gfxterm_getcharwidth (grub_uint32_t c __attribute__((unused)))
850 struct grub_font_glyph *glyph;
851 unsigned char char_width;
853 /* Get properties of the character. */
854 glyph = grub_font_get_glyph (virtual_screen.font, c);
856 /* Calculate actual character width for glyph. */
857 char_width = calculate_character_width (glyph);
859 return char_width;
862 static grub_uint16_t
863 grub_virtual_screen_getwh (void)
865 return (virtual_screen.columns << 8) | virtual_screen.rows;
868 static grub_uint16_t
869 grub_virtual_screen_getxy (void)
871 return ((virtual_screen.cursor_x << 8) | virtual_screen.cursor_y);
874 static void
875 grub_gfxterm_gotoxy (grub_uint8_t x, grub_uint8_t y)
877 if (x >= virtual_screen.columns)
878 x = virtual_screen.columns - 1;
880 if (y >= virtual_screen.rows)
881 y = virtual_screen.rows - 1;
883 /* Erase current cursor, if any. */
884 if (virtual_screen.cursor_state)
885 draw_cursor (0);
887 virtual_screen.cursor_x = x;
888 virtual_screen.cursor_y = y;
890 /* Draw cursor if visible. */
891 if (virtual_screen.cursor_state)
892 draw_cursor (1);
895 static void
896 grub_virtual_screen_cls (void)
898 grub_uint32_t i;
900 for (i = 0; i < virtual_screen.columns * virtual_screen.rows; i++)
902 virtual_screen.text_buffer[i].code = ' ';
903 virtual_screen.text_buffer[i].fg_color = virtual_screen.fg_color;
904 virtual_screen.text_buffer[i].bg_color = virtual_screen.bg_color;
905 virtual_screen.text_buffer[i].width = 0;
906 virtual_screen.text_buffer[i].index = 0;
909 virtual_screen.cursor_x = virtual_screen.cursor_y = 0;
912 static void
913 grub_gfxterm_cls (void)
915 grub_video_color_t color;
917 /* Clear virtual screen. */
918 grub_virtual_screen_cls ();
920 /* Clear text layer. */
921 grub_video_set_active_render_target (text_layer);
922 color = virtual_screen.bg_color;
923 grub_video_fill_rect (color, 0, 0,
924 virtual_screen.width, virtual_screen.height);
925 grub_video_set_active_render_target (GRUB_VIDEO_RENDER_TARGET_DISPLAY);
927 /* Mark virtual screen to be redrawn. */
928 dirty_region_add_virtualscreen ();
931 static void
932 grub_virtual_screen_setcolorstate (grub_term_color_state state)
934 switch (state)
936 case GRUB_TERM_COLOR_STANDARD:
937 virtual_screen.term_color = virtual_screen.standard_color_setting;
938 break;
940 case GRUB_TERM_COLOR_NORMAL:
941 virtual_screen.term_color = virtual_screen.normal_color_setting;
942 break;
944 case GRUB_TERM_COLOR_HIGHLIGHT:
945 virtual_screen.term_color = virtual_screen.highlight_color_setting;
946 break;
948 default:
949 break;
952 /* Change color to virtual terminal. */
953 set_term_color (virtual_screen.term_color);
956 static void
957 grub_virtual_screen_setcolor (grub_uint8_t normal_color,
958 grub_uint8_t highlight_color)
960 virtual_screen.normal_color_setting = normal_color;
961 virtual_screen.highlight_color_setting = highlight_color;
964 static void
965 grub_virtual_screen_getcolor (grub_uint8_t *normal_color,
966 grub_uint8_t *highlight_color)
968 *normal_color = virtual_screen.normal_color_setting;
969 *highlight_color = virtual_screen.highlight_color_setting;
972 static void
973 grub_gfxterm_setcursor (int on)
975 if (virtual_screen.cursor_state != on)
977 if (virtual_screen.cursor_state)
978 draw_cursor (0);
979 else
980 draw_cursor (1);
982 virtual_screen.cursor_state = on;
986 static void
987 grub_gfxterm_refresh (void)
989 /* Redraw only changed regions. */
990 dirty_region_redraw ();
993 void
994 grub_gfxterm_set_repaint_callback (grub_gfxterm_repaint_callback_t func)
996 repaint_callback = func;
999 /* Option array indices. */
1000 #define BACKGROUND_CMD_ARGINDEX_MODE 0
1002 static const struct grub_arg_option background_image_cmd_options[] =
1004 {"mode", 'm', 0, "Background image mode (`stretch', `normal').", 0,
1005 ARG_TYPE_STRING},
1006 {0, 0, 0, 0, 0, 0}
1009 static grub_err_t
1010 grub_gfxterm_background_image_cmd (grub_extcmd_t cmd,
1011 int argc,
1012 char **args)
1014 struct grub_arg_list *state = cmd->state;
1016 /* Check that we have video adapter active. */
1017 if (grub_video_get_info(NULL) != GRUB_ERR_NONE)
1018 return grub_errno;
1020 /* Destroy existing background bitmap if loaded. */
1021 if (bitmap)
1023 grub_video_bitmap_destroy (bitmap);
1024 bitmap = 0;
1026 /* Mark whole screen as dirty. */
1027 dirty_region_reset ();
1028 dirty_region_add (0, 0, window.width, window.height);
1031 /* If filename was provided, try to load that. */
1032 if (argc >= 1)
1034 /* Try to load new one. */
1035 grub_video_bitmap_load (&bitmap, args[0]);
1036 if (grub_errno != GRUB_ERR_NONE)
1037 return grub_errno;
1039 /* Determine if the bitmap should be scaled to fit the screen. */
1040 if (!state[BACKGROUND_CMD_ARGINDEX_MODE].set
1041 || grub_strcmp (state[BACKGROUND_CMD_ARGINDEX_MODE].arg,
1042 "stretch") == 0)
1044 if (window.width != (int) grub_video_bitmap_get_width (bitmap)
1045 || window.height != (int) grub_video_bitmap_get_height (bitmap))
1047 struct grub_video_bitmap *scaled_bitmap;
1048 grub_video_bitmap_create_scaled (&scaled_bitmap,
1049 window.width,
1050 window.height,
1051 bitmap,
1052 GRUB_VIDEO_BITMAP_SCALE_METHOD_BEST);
1053 if (grub_errno == GRUB_ERR_NONE)
1055 /* Replace the original bitmap with the scaled one. */
1056 grub_video_bitmap_destroy (bitmap);
1057 bitmap = scaled_bitmap;
1063 /* If bitmap was loaded correctly, display it. */
1064 if (bitmap)
1066 /* Determine bitmap dimensions. */
1067 bitmap_width = grub_video_bitmap_get_width (bitmap);
1068 bitmap_height = grub_video_bitmap_get_height (bitmap);
1070 /* Mark whole screen as dirty. */
1071 dirty_region_reset ();
1072 dirty_region_add (0, 0, window.width, window.height);
1076 /* All was ok. */
1077 grub_errno = GRUB_ERR_NONE;
1078 return grub_errno;
1081 static struct grub_term_output grub_video_term =
1083 .name = "gfxterm",
1084 .init = grub_gfxterm_init,
1085 .fini = grub_gfxterm_fini,
1086 .putchar = grub_gfxterm_putchar,
1087 .getcharwidth = grub_gfxterm_getcharwidth,
1088 .getwh = grub_virtual_screen_getwh,
1089 .getxy = grub_virtual_screen_getxy,
1090 .gotoxy = grub_gfxterm_gotoxy,
1091 .cls = grub_gfxterm_cls,
1092 .setcolorstate = grub_virtual_screen_setcolorstate,
1093 .setcolor = grub_virtual_screen_setcolor,
1094 .getcolor = grub_virtual_screen_getcolor,
1095 .setcursor = grub_gfxterm_setcursor,
1096 .refresh = grub_gfxterm_refresh,
1097 .flags = 0,
1098 .next = 0
1101 grub_term_output_t
1102 grub_gfxterm_get_term (void)
1104 return &grub_video_term;
1107 static grub_extcmd_t background_image_cmd_handle;
1109 GRUB_MOD_INIT(term_gfxterm)
1111 refcount = 0;
1113 grub_term_register_output ("gfxterm", &grub_video_term);
1114 background_image_cmd_handle =
1115 grub_register_extcmd ("background_image",
1116 grub_gfxterm_background_image_cmd,
1117 GRUB_COMMAND_FLAG_BOTH,
1118 "background_image [-m (stretch|normal)] FILE",
1119 "Load background image for active terminal.",
1120 background_image_cmd_options);
1123 GRUB_MOD_FINI(term_gfxterm)
1125 grub_unregister_extcmd (background_image_cmd_handle);
1126 grub_term_unregister_output (&grub_video_term);