Merge branch 'makefile' into haiku
[grub2/phcoder.git] / term / gfxterm.c
blobfba32eb5157f60f3be1e1bc7690d1ea1a7f8964a
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 unsigned int i;
186 /* Free old virtual screen. */
187 grub_virtual_screen_free ();
189 /* Initialize with default data. */
190 virtual_screen.font = grub_font_get (font_name);
191 if (!virtual_screen.font)
192 return grub_error (GRUB_ERR_BAD_FONT,
193 "No font loaded.");
194 virtual_screen.width = width;
195 virtual_screen.height = height;
196 virtual_screen.offset_x = x;
197 virtual_screen.offset_y = y;
198 virtual_screen.normal_char_width =
199 calculate_normal_character_width (virtual_screen.font);
200 virtual_screen.normal_char_height =
201 grub_font_get_max_char_height (virtual_screen.font);
202 virtual_screen.cursor_x = 0;
203 virtual_screen.cursor_y = 0;
204 virtual_screen.cursor_state = 1;
206 /* Calculate size of text buffer. */
207 virtual_screen.columns = virtual_screen.width / virtual_screen.normal_char_width;
208 virtual_screen.rows = virtual_screen.height / virtual_screen.normal_char_height;
210 /* Allocate memory for text buffer. */
211 virtual_screen.text_buffer =
212 (struct grub_colored_char *) grub_malloc (virtual_screen.columns
213 * virtual_screen.rows
214 * sizeof (*virtual_screen.text_buffer));
215 if (grub_errno != GRUB_ERR_NONE)
216 return grub_errno;
218 /* Create new render target for text layer. */
219 grub_video_create_render_target (&text_layer,
220 virtual_screen.width,
221 virtual_screen.height,
222 GRUB_VIDEO_MODE_TYPE_RGB
223 | GRUB_VIDEO_MODE_TYPE_ALPHA);
224 if (grub_errno != GRUB_ERR_NONE)
225 return grub_errno;
227 /* As we want to have colors compatible with rendering target,
228 we can only have those after mode is initialized. */
229 grub_video_set_active_render_target (text_layer);
231 virtual_screen.standard_color_setting = DEFAULT_STANDARD_COLOR;
232 virtual_screen.normal_color_setting = DEFAULT_NORMAL_COLOR;
233 virtual_screen.highlight_color_setting = DEFAULT_HIGHLIGHT_COLOR;
235 virtual_screen.term_color = virtual_screen.normal_color_setting;
237 set_term_color (virtual_screen.term_color);
239 grub_video_set_active_render_target (GRUB_VIDEO_RENDER_TARGET_DISPLAY);
241 /* Clear out text buffer. */
242 for(i = 0; i < virtual_screen.columns * virtual_screen.rows; i++)
244 virtual_screen.text_buffer[i].code = ' ';
245 virtual_screen.text_buffer[i].fg_color = virtual_screen.fg_color;
246 virtual_screen.text_buffer[i].bg_color = virtual_screen.bg_color;
247 virtual_screen.text_buffer[i].width = 0;
248 virtual_screen.text_buffer[i].index = 0;
251 return grub_errno;
254 static int NESTED_FUNC_ATTR video_hook (grub_video_adapter_t p __attribute__ ((unused)),
255 struct grub_video_mode_info *info)
257 return ! (info->mode_type & GRUB_VIDEO_MODE_TYPE_PURE_TEXT);
260 static grub_err_t
261 init_window (struct grub_video_render_target *target,
262 int x, int y, int width, int height,
263 const char *font_name, int border_width)
265 /* Clean up any prior instance. */
266 destroy_window ();
268 /* Create virtual screen. */
269 if (grub_virtual_screen_setup (border_width, border_width,
270 width - 2 * border_width,
271 height - 2 * border_width,
272 font_name)
273 != GRUB_ERR_NONE)
275 return grub_errno;
278 /* Set the render target. */
279 render_target = target;
281 /* Set window bounds. */
282 window.x = x;
283 window.y = y;
284 window.width = width;
285 window.height = height;
287 /* Mark whole window as dirty. */
288 dirty_region_reset ();
289 dirty_region_add (0, 0, width, height);
291 return grub_errno;
294 grub_err_t
295 grub_gfxterm_init_window (struct grub_video_render_target *target,
296 int x, int y, int width, int height,
297 const char *font_name, int border_width)
299 if (refcount++ == 0)
300 init_window (target, x, y, width, height, font_name, border_width);
301 return grub_errno;
304 static grub_err_t
305 grub_gfxterm_init (void)
307 const char *font_name;
308 const char *modevar;
309 struct grub_video_mode_info mode_info;
310 char *tmp;
311 grub_err_t err;
313 /* If gfxterm has already been initialized by calling the init_window
314 function, then leave it alone when it is set as the current terminal. */
315 if (refcount++ != 0)
316 return GRUB_ERR_NONE;
318 /* Select the font to use. */
319 font_name = grub_env_get ("gfxterm_font");
320 if (! font_name)
321 font_name = ""; /* Allow fallback to any font. */
323 /* Parse gfxmode environment variable if set. */
324 modevar = grub_env_get ("gfxmode");
325 if (! modevar || *modevar == 0)
326 err = grub_video_set_mode (DEFAULT_VIDEO_MODE, video_hook);
327 else
329 tmp = grub_malloc (grub_strlen (modevar)
330 + sizeof (DEFAULT_VIDEO_MODE) + 1);
331 if (! tmp)
332 return grub_errno;
333 grub_sprintf (tmp, "%s;" DEFAULT_VIDEO_MODE, modevar);
334 err = grub_video_set_mode (tmp, video_hook);
335 grub_free (tmp);
338 if (err)
339 return err;
341 err = grub_video_get_info (&mode_info);
342 /* Figure out what mode we ended up. */
343 if (err)
344 return err;
346 /* Make sure screen is black. */
347 grub_video_fill_rect (grub_video_map_rgb (0, 0, 0),
348 0, 0, mode_info.width, mode_info.height);
349 bitmap = 0;
351 /* Select the font to use. */
352 font_name = grub_env_get ("gfxterm_font");
353 if (! font_name)
354 font_name = ""; /* Allow fallback to any font. */
356 /* Leave borders for virtual screen. */
357 if (init_window (GRUB_VIDEO_RENDER_TARGET_DISPLAY,
358 0, 0, mode_info.width, mode_info.height,
359 font_name,
360 DEFAULT_BORDER_WIDTH) != GRUB_ERR_NONE)
362 grub_video_restore ();
363 return grub_errno;
366 return grub_errno;
369 static void
370 destroy_window (void)
372 if (bitmap)
374 grub_video_bitmap_destroy (bitmap);
375 bitmap = 0;
378 repaint_callback = 0;
379 grub_virtual_screen_free ();
382 void
383 grub_gfxterm_destroy_window (void)
385 if (--refcount == 0)
386 destroy_window ();
389 static grub_err_t
390 grub_gfxterm_fini (void)
392 /* Don't destroy an explicitly initialized terminal instance when it is
393 unset as the current terminal. */
394 if (--refcount == 0)
396 destroy_window ();
397 grub_video_restore ();
400 /* Clear error state. */
401 return (grub_errno = GRUB_ERR_NONE);
404 static void
405 redraw_screen_rect (unsigned int x, unsigned int y,
406 unsigned int width, unsigned int height)
408 grub_video_color_t color;
409 grub_video_rect_t saved_view;
411 grub_video_set_active_render_target (render_target);
412 /* Save viewport and set it to our window. */
413 grub_video_get_viewport ((unsigned *) &saved_view.x,
414 (unsigned *) &saved_view.y,
415 (unsigned *) &saved_view.width,
416 (unsigned *) &saved_view.height);
417 grub_video_set_viewport (window.x, window.y, window.width, window.height);
419 if (bitmap)
421 /* Render bitmap as background. */
422 grub_video_blit_bitmap (bitmap, GRUB_VIDEO_BLIT_REPLACE, x, y,
423 x, y,
424 width, height);
426 /* If bitmap is smaller than requested blit area, use background
427 color. */
428 color = virtual_screen.bg_color;
430 /* Fill right side of the bitmap if needed. */
431 if ((x + width >= bitmap_width) && (y < bitmap_height))
433 int w = (x + width) - bitmap_width;
434 int h = height;
435 unsigned int tx = x;
437 if (y + height >= bitmap_height)
439 h = bitmap_height - y;
442 if (bitmap_width > tx)
444 tx = bitmap_width;
447 /* Render background layer. */
448 grub_video_fill_rect (color, tx, y, w, h);
451 /* Fill bottom side of the bitmap if needed. */
452 if (y + height >= bitmap_height)
454 int h = (y + height) - bitmap_height;
455 unsigned int ty = y;
457 if (bitmap_height > ty)
459 ty = bitmap_height;
462 /* Render background layer. */
463 grub_video_fill_rect (color, x, ty, width, h);
466 /* Render text layer as blended. */
467 grub_video_blit_render_target (text_layer, GRUB_VIDEO_BLIT_BLEND, x, y,
468 x - virtual_screen.offset_x,
469 y - virtual_screen.offset_y,
470 width, height);
472 else
474 /* Render background layer. */
475 color = virtual_screen.bg_color;
476 grub_video_fill_rect (color, x, y, width, height);
478 /* Render text layer as replaced (to get texts background color). */
479 grub_video_blit_render_target (text_layer, GRUB_VIDEO_BLIT_REPLACE, x, y,
480 x - virtual_screen.offset_x,
481 y - virtual_screen.offset_y,
482 width, height);
485 /* Restore saved viewport. */
486 grub_video_set_viewport (saved_view.x, saved_view.y,
487 saved_view.width, saved_view.height);
488 grub_video_set_active_render_target (GRUB_VIDEO_RENDER_TARGET_DISPLAY);
490 if (repaint_callback)
491 repaint_callback (x, y, width, height);
494 static void
495 dirty_region_reset (void)
497 dirty_region.top_left_x = -1;
498 dirty_region.top_left_y = -1;
499 dirty_region.bottom_right_x = -1;
500 dirty_region.bottom_right_y = -1;
503 static int
504 dirty_region_is_empty (void)
506 if ((dirty_region.top_left_x == -1)
507 || (dirty_region.top_left_y == -1)
508 || (dirty_region.bottom_right_x == -1)
509 || (dirty_region.bottom_right_y == -1))
510 return 1;
511 return 0;
514 static void
515 dirty_region_add (int x, int y, unsigned int width, unsigned int height)
517 if ((width == 0) || (height == 0))
518 return;
520 if (dirty_region_is_empty ())
522 dirty_region.top_left_x = x;
523 dirty_region.top_left_y = y;
524 dirty_region.bottom_right_x = x + width - 1;
525 dirty_region.bottom_right_y = y + height - 1;
527 else
529 if (x < dirty_region.top_left_x)
530 dirty_region.top_left_x = x;
531 if (y < dirty_region.top_left_y)
532 dirty_region.top_left_y = y;
533 if ((x + (int)width - 1) > dirty_region.bottom_right_x)
534 dirty_region.bottom_right_x = x + width - 1;
535 if ((y + (int)height - 1) > dirty_region.bottom_right_y)
536 dirty_region.bottom_right_y = y + height - 1;
540 static void
541 dirty_region_add_virtualscreen (void)
543 /* Mark virtual screen as dirty. */
544 dirty_region_add (virtual_screen.offset_x, virtual_screen.offset_y,
545 virtual_screen.width, virtual_screen.height);
549 static void
550 dirty_region_redraw (void)
552 int x;
553 int y;
554 int width;
555 int height;
557 if (dirty_region_is_empty ())
558 return;
560 x = dirty_region.top_left_x;
561 y = dirty_region.top_left_y;
563 width = dirty_region.bottom_right_x - x + 1;
564 height = dirty_region.bottom_right_y - y + 1;
566 redraw_screen_rect (x, y, width, height);
568 dirty_region_reset ();
569 grub_video_swap_buffers ();
572 static void
573 write_char (void)
575 struct grub_colored_char *p;
576 struct grub_font_glyph *glyph;
577 grub_video_color_t color;
578 grub_video_color_t bgcolor;
579 unsigned int x;
580 unsigned int y;
581 int ascent;
582 unsigned int height;
583 unsigned int width;
585 /* Find out active character. */
586 p = (virtual_screen.text_buffer
587 + virtual_screen.cursor_x
588 + (virtual_screen.cursor_y * virtual_screen.columns));
590 p -= p->index;
592 /* Get glyph for character. */
593 glyph = grub_font_get_glyph (virtual_screen.font, p->code);
594 ascent = grub_font_get_ascent (virtual_screen.font);
596 width = virtual_screen.normal_char_width * calculate_character_width(glyph);
597 height = virtual_screen.normal_char_height;
599 color = p->fg_color;
600 bgcolor = p->bg_color;
602 x = virtual_screen.cursor_x * virtual_screen.normal_char_width;
603 y = virtual_screen.cursor_y * virtual_screen.normal_char_height;
605 /* Render glyph to text layer. */
606 grub_video_set_active_render_target (text_layer);
607 grub_video_fill_rect (bgcolor, x, y, width, height);
608 grub_font_draw_glyph (glyph, color, x, y + ascent);
609 grub_video_set_active_render_target (GRUB_VIDEO_RENDER_TARGET_DISPLAY);
611 /* Mark character to be drawn. */
612 dirty_region_add (virtual_screen.offset_x + x, virtual_screen.offset_y + y,
613 width, height);
616 static void
617 draw_cursor (int show)
619 write_char ();
621 if (show)
623 unsigned int x;
624 unsigned int y;
625 unsigned int width;
626 unsigned int height;
627 grub_video_color_t color;
629 /* Determine cursor properties and position on text layer. */
630 x = virtual_screen.cursor_x * virtual_screen.normal_char_width;
631 width = virtual_screen.normal_char_width;
632 color = virtual_screen.fg_color;
633 y = (virtual_screen.cursor_y * virtual_screen.normal_char_height
634 + grub_font_get_ascent (virtual_screen.font));
635 height = 2;
637 /* Render cursor to text layer. */
638 grub_video_set_active_render_target (text_layer);
639 grub_video_fill_rect (color, x, y, width, height);
640 grub_video_set_active_render_target (GRUB_VIDEO_RENDER_TARGET_DISPLAY);
642 /* Mark cursor to be redrawn. */
643 dirty_region_add (virtual_screen.offset_x + x,
644 virtual_screen.offset_y + y,
645 width, height);
649 static void
650 scroll_up (void)
652 unsigned int i;
653 grub_video_color_t color;
655 /* If we don't have background bitmap, remove cursor. */
656 if (!bitmap)
658 /* Remove cursor. */
659 draw_cursor (0);
661 /* Redraw only changed regions. */
662 dirty_region_redraw ();
665 /* Scroll text buffer with one line to up. */
666 grub_memmove (virtual_screen.text_buffer,
667 virtual_screen.text_buffer + virtual_screen.columns,
668 sizeof (*virtual_screen.text_buffer)
669 * virtual_screen.columns
670 * (virtual_screen.rows - 1));
672 /* Clear last line in text buffer. */
673 for (i = virtual_screen.columns * (virtual_screen.rows - 1);
674 i < virtual_screen.columns * virtual_screen.rows;
675 i++)
677 virtual_screen.text_buffer[i].code = ' ';
678 virtual_screen.text_buffer[i].fg_color = virtual_screen.fg_color;
679 virtual_screen.text_buffer[i].bg_color = virtual_screen.bg_color;
680 virtual_screen.text_buffer[i].width = 0;
681 virtual_screen.text_buffer[i].index = 0;
684 /* Scroll physical screen. */
685 grub_video_set_active_render_target (text_layer);
686 color = virtual_screen.bg_color;
687 grub_video_scroll (color, 0, -virtual_screen.normal_char_height);
688 grub_video_set_active_render_target (GRUB_VIDEO_RENDER_TARGET_DISPLAY);
690 /* If we have bitmap, re-draw screen, otherwise scroll physical screen too. */
691 if (bitmap)
693 /* Mark virtual screen to be redrawn. */
694 dirty_region_add_virtualscreen ();
696 else
698 grub_video_rect_t saved_view;
699 grub_video_set_active_render_target (render_target);
700 /* Save viewport and set it to our window. */
701 grub_video_get_viewport ((unsigned *) &saved_view.x,
702 (unsigned *) &saved_view.y,
703 (unsigned *) &saved_view.width,
704 (unsigned *) &saved_view.height);
705 grub_video_set_viewport (window.x, window.y, window.width, window.height);
707 /* Clear new border area. */
708 grub_video_fill_rect (color,
709 virtual_screen.offset_x, virtual_screen.offset_y,
710 virtual_screen.width, virtual_screen.normal_char_height);
712 /* Scroll physical screen. */
713 grub_video_scroll (color, 0, -virtual_screen.normal_char_height);
715 /* Restore saved viewport. */
716 grub_video_set_viewport (saved_view.x, saved_view.y,
717 saved_view.width, saved_view.height);
718 grub_video_set_active_render_target (GRUB_VIDEO_RENDER_TARGET_DISPLAY);
720 /* Draw cursor if visible. */
721 if (virtual_screen.cursor_state)
722 draw_cursor (1);
725 if (repaint_callback)
726 repaint_callback (window.x, window.y, window.width, window.height);
729 static void
730 grub_gfxterm_putchar (grub_uint32_t c)
732 if (c == '\a')
733 /* FIXME */
734 return;
736 /* Erase current cursor, if any. */
737 if (virtual_screen.cursor_state)
738 draw_cursor (0);
740 if (c == '\b' || c == '\n' || c == '\r')
742 switch (c)
744 case '\b':
745 if (virtual_screen.cursor_x > 0)
746 virtual_screen.cursor_x--;
747 break;
749 case '\n':
750 if (virtual_screen.cursor_y >= virtual_screen.rows - 1)
751 scroll_up ();
752 else
753 virtual_screen.cursor_y++;
754 break;
756 case '\r':
757 virtual_screen.cursor_x = 0;
758 break;
761 else
763 struct grub_font_glyph *glyph;
764 struct grub_colored_char *p;
765 unsigned char char_width;
767 /* Get properties of the character. */
768 glyph = grub_font_get_glyph (virtual_screen.font, c);
770 /* Calculate actual character width for glyph. This is number of
771 times of normal_font_width. */
772 char_width = calculate_character_width(glyph);
774 /* If we are about to exceed line length, wrap to next line. */
775 if (virtual_screen.cursor_x + char_width > virtual_screen.columns)
776 grub_putchar ('\n');
778 /* Find position on virtual screen, and fill information. */
779 p = (virtual_screen.text_buffer +
780 virtual_screen.cursor_x +
781 virtual_screen.cursor_y * virtual_screen.columns);
782 p->code = c;
783 p->fg_color = virtual_screen.fg_color;
784 p->bg_color = virtual_screen.bg_color;
785 p->width = char_width - 1;
786 p->index = 0;
788 /* If we have large glyph, add fixup info. */
789 if (char_width > 1)
791 unsigned i;
793 for (i = 1; i < char_width; i++)
795 p[i].code = ' ';
796 p[i].width = char_width - 1;
797 p[i].index = i;
801 /* Draw glyph. */
802 write_char ();
804 /* Make sure we scroll screen when needed and wrap line correctly. */
805 virtual_screen.cursor_x += char_width;
806 if (virtual_screen.cursor_x >= virtual_screen.columns)
808 virtual_screen.cursor_x = 0;
810 if (virtual_screen.cursor_y >= virtual_screen.rows - 1)
811 scroll_up ();
812 else
813 virtual_screen.cursor_y++;
817 /* Redraw cursor if it should be visible. */
818 /* Note: This will redraw the character as well, which means that the
819 above call to write_char is redundant when the cursor is showing. */
820 if (virtual_screen.cursor_state)
821 draw_cursor (1);
824 /* Use ASCII characters to determine normal character width. */
825 static unsigned int
826 calculate_normal_character_width (grub_font_t font)
828 struct grub_font_glyph *glyph;
829 unsigned int width = 0;
830 unsigned int i;
832 /* Get properties of every printable ASCII character. */
833 for (i = 32; i < 127; i++)
835 glyph = grub_font_get_glyph (font, i);
837 /* Skip unknown characters. Should never happen on normal conditions. */
838 if (! glyph)
839 continue;
841 if (glyph->device_width > width)
842 width = glyph->device_width;
845 return width;
848 static unsigned char
849 calculate_character_width (struct grub_font_glyph *glyph)
851 if (! glyph || glyph->device_width == 0)
852 return 1;
854 return (glyph->device_width
855 + (virtual_screen.normal_char_width - 1))
856 / virtual_screen.normal_char_width;
859 static grub_ssize_t
860 grub_gfxterm_getcharwidth (grub_uint32_t c __attribute__((unused)))
862 struct grub_font_glyph *glyph;
863 unsigned char char_width;
865 /* Get properties of the character. */
866 glyph = grub_font_get_glyph (virtual_screen.font, c);
868 /* Calculate actual character width for glyph. */
869 char_width = calculate_character_width (glyph);
871 return char_width;
874 static grub_uint16_t
875 grub_virtual_screen_getwh (void)
877 return (virtual_screen.columns << 8) | virtual_screen.rows;
880 static grub_uint16_t
881 grub_virtual_screen_getxy (void)
883 return ((virtual_screen.cursor_x << 8) | virtual_screen.cursor_y);
886 static void
887 grub_gfxterm_gotoxy (grub_uint8_t x, grub_uint8_t y)
889 if (x >= virtual_screen.columns)
890 x = virtual_screen.columns - 1;
892 if (y >= virtual_screen.rows)
893 y = virtual_screen.rows - 1;
895 /* Erase current cursor, if any. */
896 if (virtual_screen.cursor_state)
897 draw_cursor (0);
899 virtual_screen.cursor_x = x;
900 virtual_screen.cursor_y = y;
902 /* Draw cursor if visible. */
903 if (virtual_screen.cursor_state)
904 draw_cursor (1);
907 static void
908 grub_virtual_screen_cls (void)
910 grub_uint32_t i;
912 for (i = 0; i < virtual_screen.columns * virtual_screen.rows; i++)
914 virtual_screen.text_buffer[i].code = ' ';
915 virtual_screen.text_buffer[i].fg_color = virtual_screen.fg_color;
916 virtual_screen.text_buffer[i].bg_color = virtual_screen.bg_color;
917 virtual_screen.text_buffer[i].width = 0;
918 virtual_screen.text_buffer[i].index = 0;
921 virtual_screen.cursor_x = virtual_screen.cursor_y = 0;
924 static void
925 grub_gfxterm_cls (void)
927 grub_video_color_t color;
929 /* Clear virtual screen. */
930 grub_virtual_screen_cls ();
932 /* Clear text layer. */
933 grub_video_set_active_render_target (text_layer);
934 color = virtual_screen.bg_color;
935 grub_video_fill_rect (color, 0, 0,
936 virtual_screen.width, virtual_screen.height);
937 grub_video_set_active_render_target (GRUB_VIDEO_RENDER_TARGET_DISPLAY);
939 /* Mark virtual screen to be redrawn. */
940 dirty_region_add_virtualscreen ();
943 static void
944 grub_virtual_screen_setcolorstate (grub_term_color_state state)
946 switch (state)
948 case GRUB_TERM_COLOR_STANDARD:
949 virtual_screen.term_color = virtual_screen.standard_color_setting;
950 break;
952 case GRUB_TERM_COLOR_NORMAL:
953 virtual_screen.term_color = virtual_screen.normal_color_setting;
954 break;
956 case GRUB_TERM_COLOR_HIGHLIGHT:
957 virtual_screen.term_color = virtual_screen.highlight_color_setting;
958 break;
960 default:
961 break;
964 /* Change color to virtual terminal. */
965 set_term_color (virtual_screen.term_color);
968 static void
969 grub_virtual_screen_setcolor (grub_uint8_t normal_color,
970 grub_uint8_t highlight_color)
972 virtual_screen.normal_color_setting = normal_color;
973 virtual_screen.highlight_color_setting = highlight_color;
976 static void
977 grub_virtual_screen_getcolor (grub_uint8_t *normal_color,
978 grub_uint8_t *highlight_color)
980 *normal_color = virtual_screen.normal_color_setting;
981 *highlight_color = virtual_screen.highlight_color_setting;
984 static void
985 grub_gfxterm_setcursor (int on)
987 if (virtual_screen.cursor_state != on)
989 if (virtual_screen.cursor_state)
990 draw_cursor (0);
991 else
992 draw_cursor (1);
994 virtual_screen.cursor_state = on;
998 static void
999 grub_gfxterm_refresh (void)
1001 /* Redraw only changed regions. */
1002 dirty_region_redraw ();
1005 void
1006 grub_gfxterm_set_repaint_callback (grub_gfxterm_repaint_callback_t func)
1008 repaint_callback = func;
1011 /* Option array indices. */
1012 #define BACKGROUND_CMD_ARGINDEX_MODE 0
1014 static const struct grub_arg_option background_image_cmd_options[] =
1016 {"mode", 'm', 0, "Background image mode (`stretch', `normal').", 0,
1017 ARG_TYPE_STRING},
1018 {0, 0, 0, 0, 0, 0}
1021 static grub_err_t
1022 grub_gfxterm_background_image_cmd (grub_extcmd_t cmd,
1023 int argc,
1024 char **args)
1026 struct grub_arg_list *state = cmd->state;
1028 /* Check that we have video adapter active. */
1029 if (grub_video_get_info(NULL) != GRUB_ERR_NONE)
1030 return grub_errno;
1032 /* Destroy existing background bitmap if loaded. */
1033 if (bitmap)
1035 grub_video_bitmap_destroy (bitmap);
1036 bitmap = 0;
1038 /* Mark whole screen as dirty. */
1039 dirty_region_reset ();
1040 dirty_region_add (0, 0, window.width, window.height);
1043 /* If filename was provided, try to load that. */
1044 if (argc >= 1)
1046 /* Try to load new one. */
1047 grub_video_bitmap_load (&bitmap, args[0]);
1048 if (grub_errno != GRUB_ERR_NONE)
1049 return grub_errno;
1051 /* Determine if the bitmap should be scaled to fit the screen. */
1052 if (!state[BACKGROUND_CMD_ARGINDEX_MODE].set
1053 || grub_strcmp (state[BACKGROUND_CMD_ARGINDEX_MODE].arg,
1054 "stretch") == 0)
1056 if (window.width != (int) grub_video_bitmap_get_width (bitmap)
1057 || window.height != (int) grub_video_bitmap_get_height (bitmap))
1059 struct grub_video_bitmap *scaled_bitmap;
1060 grub_video_bitmap_create_scaled (&scaled_bitmap,
1061 window.width,
1062 window.height,
1063 bitmap,
1064 GRUB_VIDEO_BITMAP_SCALE_METHOD_BEST);
1065 if (grub_errno == GRUB_ERR_NONE)
1067 /* Replace the original bitmap with the scaled one. */
1068 grub_video_bitmap_destroy (bitmap);
1069 bitmap = scaled_bitmap;
1075 /* If bitmap was loaded correctly, display it. */
1076 if (bitmap)
1078 /* Determine bitmap dimensions. */
1079 bitmap_width = grub_video_bitmap_get_width (bitmap);
1080 bitmap_height = grub_video_bitmap_get_height (bitmap);
1082 /* Mark whole screen as dirty. */
1083 dirty_region_reset ();
1084 dirty_region_add (0, 0, window.width, window.height);
1088 /* All was ok. */
1089 grub_errno = GRUB_ERR_NONE;
1090 return grub_errno;
1093 static struct grub_term_output grub_video_term =
1095 .name = "gfxterm",
1096 .init = grub_gfxterm_init,
1097 .fini = grub_gfxterm_fini,
1098 .putchar = grub_gfxterm_putchar,
1099 .getcharwidth = grub_gfxterm_getcharwidth,
1100 .getwh = grub_virtual_screen_getwh,
1101 .getxy = grub_virtual_screen_getxy,
1102 .gotoxy = grub_gfxterm_gotoxy,
1103 .cls = grub_gfxterm_cls,
1104 .setcolorstate = grub_virtual_screen_setcolorstate,
1105 .setcolor = grub_virtual_screen_setcolor,
1106 .getcolor = grub_virtual_screen_getcolor,
1107 .setcursor = grub_gfxterm_setcursor,
1108 .refresh = grub_gfxterm_refresh,
1109 .flags = 0,
1110 .next = 0
1113 grub_term_output_t
1114 grub_gfxterm_get_term (void)
1116 return &grub_video_term;
1119 static grub_extcmd_t background_image_cmd_handle;
1121 GRUB_MOD_INIT(term_gfxterm)
1123 refcount = 0;
1125 grub_term_register_output ("gfxterm", &grub_video_term);
1126 background_image_cmd_handle =
1127 grub_register_extcmd ("background_image",
1128 grub_gfxterm_background_image_cmd,
1129 GRUB_COMMAND_FLAG_BOTH,
1130 "background_image [-m (stretch|normal)] FILE",
1131 "Load background image for active terminal.",
1132 background_image_cmd_options);
1135 GRUB_MOD_FINI(term_gfxterm)
1137 grub_unregister_extcmd (background_image_cmd_handle);
1138 grub_term_unregister_output (&grub_video_term);