1 /* view.c - Graphical menu interface MVC view. */
3 * GRUB -- GRand Unified Bootloader
4 * Copyright (C) 2008 Free Software Foundation, Inc.
6 * GRUB is free software: you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation, either version 3 of the License, or
9 * (at your option) any later version.
11 * GRUB is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
16 * You should have received a copy of the GNU General Public License
17 * along with GRUB. If not, see <http://www.gnu.org/licenses/>.
20 #include <grub/types.h>
21 #include <grub/file.h>
22 #include <grub/misc.h>
26 #include <grub/normal.h>
27 #include <grub/video.h>
28 #include <grub/gfxterm.h>
29 #include <grub/bitmap.h>
30 #include <grub/bitmap_scale.h>
31 #include <grub/term.h>
32 #include <grub/gfxwidgets.h>
33 #include <grub/time.h>
34 #include <grub/menu.h>
35 #include <grub/menu_viewer.h>
36 #include <grub/gfxmenu_view.h>
37 #include <grub/gui_string_util.h>
38 #include <grub/icon_manager.h>
40 /* The component ID identifying GUI components to be updated as the timeout
42 #define TIMEOUT_COMPONENT_ID "__timeout__"
45 init_terminal (grub_gfxmenu_view_t view
);
46 static grub_video_rect_t term_rect
;
47 static grub_gfxmenu_view_t term_view
;
49 /* Create a new view object, loading the theme specified by THEME_PATH and
50 associating MODEL with the view. */
52 grub_gfxmenu_view_new (const char *theme_path
,
53 int width
, int height
)
55 grub_gfxmenu_view_t view
;
56 grub_font_t default_font
;
57 grub_gui_color_t default_fg_color
;
58 grub_gui_color_t default_bg_color
;
60 view
= grub_malloc (sizeof (*view
));
66 view
->screen
.width
= width
;
67 view
->screen
.height
= height
;
69 default_font
= grub_font_get ("Unknown Regular 16");
70 default_fg_color
= grub_gui_color_rgb (0, 0, 0);
71 default_bg_color
= grub_gui_color_rgb (255, 255, 255);
75 view
->title_font
= default_font
;
76 view
->message_font
= default_font
;
77 view
->terminal_font_name
= grub_strdup ("Fixed 10");
78 view
->title_color
= default_fg_color
;
79 view
->message_color
= default_bg_color
;
80 view
->message_bg_color
= default_fg_color
;
81 view
->desktop_image
= 0;
82 view
->desktop_color
= default_bg_color
;
83 view
->terminal_box
= grub_gfxmenu_create_box (0, 0);
84 view
->title_text
= grub_strdup ("GRUB Boot Menu");
85 view
->progress_message_text
= 0;
88 /* Set the timeout bar's frame. */
89 view
->progress_message_frame
.width
= view
->screen
.width
* 4 / 5;
90 view
->progress_message_frame
.height
= 50;
91 view
->progress_message_frame
.x
= view
->screen
.x
92 + (view
->screen
.width
- view
->progress_message_frame
.width
) / 2;
93 view
->progress_message_frame
.y
= view
->screen
.y
94 + view
->screen
.height
- 90 - 20 - view
->progress_message_frame
.height
;
96 if (grub_gfxmenu_view_load_theme (view
, theme_path
) != 0)
98 grub_gfxmenu_view_destroy (view
);
105 /* Destroy the view object. All used memory is freed. */
107 grub_gfxmenu_view_destroy (grub_gfxmenu_view_t view
)
111 grub_video_bitmap_destroy (view
->desktop_image
);
112 if (view
->terminal_box
)
113 view
->terminal_box
->destroy (view
->terminal_box
);
114 grub_free (view
->terminal_font_name
);
115 grub_free (view
->title_text
);
116 grub_free (view
->progress_message_text
);
117 grub_free (view
->theme_path
);
119 view
->canvas
->component
.ops
->destroy (view
->canvas
);
124 redraw_background (grub_gfxmenu_view_t view
,
125 const grub_video_rect_t
*bounds
)
127 if (view
->desktop_image
)
129 struct grub_video_bitmap
*img
= view
->desktop_image
;
130 grub_video_blit_bitmap (img
, GRUB_VIDEO_BLIT_REPLACE
,
131 bounds
->x
, bounds
->y
,
132 bounds
->x
- view
->screen
.x
,
133 bounds
->y
- view
->screen
.y
,
134 bounds
->width
, bounds
->height
);
138 grub_video_fill_rect (grub_gui_map_color (view
->desktop_color
),
139 bounds
->x
, bounds
->y
,
140 bounds
->width
, bounds
->height
);
145 draw_title (grub_gfxmenu_view_t view
)
147 if (! view
->title_text
)
150 /* Center the title. */
151 int title_width
= grub_font_get_string_width (view
->title_font
,
153 int x
= (view
->screen
.width
- title_width
) / 2;
154 int y
= 40 + grub_font_get_ascent (view
->title_font
);
155 grub_font_draw_string (view
->title_text
,
157 grub_gui_map_color (view
->title_color
),
161 struct progress_value_data
170 update_timeout_visit (grub_gui_component_t component
,
173 struct progress_value_data
*pv
;
174 pv
= (struct progress_value_data
*) userdata
;
176 ((struct grub_gui_progress
*) component
)->ops
177 ->set_state ((struct grub_gui_progress
*) component
,
178 pv
->visible
, pv
->start
, pv
->value
, pv
->end
);
182 grub_gfxmenu_print_timeout (int timeout
, void *data
)
184 struct grub_gfxmenu_view
*view
= data
;
186 struct progress_value_data pv
;
188 auto void redraw_timeout_visit (grub_gui_component_t component
,
189 void *userdata
__attribute__ ((unused
)));
191 auto void redraw_timeout_visit (grub_gui_component_t component
,
192 void *userdata
__attribute__ ((unused
)))
194 grub_video_rect_t bounds
;
195 component
->ops
->get_bounds (component
, &bounds
);
196 grub_gfxmenu_view_redraw (view
, &bounds
);
199 if (view
->first_timeout
== -1)
200 view
->first_timeout
= timeout
;
203 pv
.start
= -(view
->first_timeout
+ 1);
207 grub_gui_find_by_id ((grub_gui_component_t
) view
->canvas
,
208 TIMEOUT_COMPONENT_ID
, update_timeout_visit
, &pv
);
209 grub_gui_find_by_id ((grub_gui_component_t
) view
->canvas
,
210 TIMEOUT_COMPONENT_ID
, redraw_timeout_visit
, &pv
);
211 grub_video_swap_buffers ();
212 if (view
->double_repaint
)
213 grub_gui_find_by_id ((grub_gui_component_t
) view
->canvas
,
214 TIMEOUT_COMPONENT_ID
, redraw_timeout_visit
, &pv
);
218 grub_gfxmenu_clear_timeout (void *data
)
220 struct progress_value_data pv
;
221 struct grub_gfxmenu_view
*view
= data
;
223 auto void redraw_timeout_visit (grub_gui_component_t component
,
224 void *userdata
__attribute__ ((unused
)));
226 auto void redraw_timeout_visit (grub_gui_component_t component
,
227 void *userdata
__attribute__ ((unused
)))
229 grub_video_rect_t bounds
;
230 component
->ops
->get_bounds (component
, &bounds
);
231 grub_gfxmenu_view_redraw (view
, &bounds
);
239 grub_gui_find_by_id ((grub_gui_component_t
) view
->canvas
,
240 TIMEOUT_COMPONENT_ID
, update_timeout_visit
, &pv
);
241 grub_gui_find_by_id ((grub_gui_component_t
) view
->canvas
,
242 TIMEOUT_COMPONENT_ID
, redraw_timeout_visit
, &pv
);
243 grub_video_swap_buffers ();
244 if (view
->double_repaint
)
245 grub_gui_find_by_id ((grub_gui_component_t
) view
->canvas
,
246 TIMEOUT_COMPONENT_ID
, redraw_timeout_visit
, &pv
);
250 update_menu_visit (grub_gui_component_t component
,
253 grub_gfxmenu_view_t view
;
255 if (component
->ops
->is_instance (component
, "list"))
257 grub_gui_list_t list
= (grub_gui_list_t
) component
;
258 list
->ops
->set_view_info (list
, view
);
262 /* Update any boot menu components with the current menu model and
265 update_menu_components (grub_gfxmenu_view_t view
)
267 grub_gui_iterate_recursively ((grub_gui_component_t
) view
->canvas
,
268 update_menu_visit
, view
);
272 draw_message (grub_gfxmenu_view_t view
)
274 char *text
= view
->progress_message_text
;
275 grub_video_rect_t f
= view
->progress_message_frame
;
279 grub_font_t font
= view
->message_font
;
280 grub_video_color_t color
= grub_gui_map_color (view
->message_color
);
283 grub_video_fill_rect (color
,
284 f
.x
-1, f
.y
-1, f
.width
+2, f
.height
+2);
286 grub_video_fill_rect (grub_gui_map_color (view
->message_bg_color
),
287 f
.x
, f
.y
, f
.width
, f
.height
);
289 /* Center the text. */
290 int text_width
= grub_font_get_string_width (font
, text
);
291 int x
= f
.x
+ (f
.width
- text_width
) / 2;
292 int y
= (f
.y
+ (f
.height
- grub_font_get_descent (font
)) / 2
293 + grub_font_get_ascent (font
) / 2);
294 grub_font_draw_string (text
, font
, color
, x
, y
);
298 grub_gfxmenu_view_redraw (grub_gfxmenu_view_t view
,
299 const grub_video_rect_t
*region
)
301 if (grub_video_have_common_points (&term_rect
, region
))
302 grub_gfxterm_schedule_repaint ();
304 grub_video_set_active_render_target (GRUB_VIDEO_RENDER_TARGET_DISPLAY
);
306 redraw_background (view
, region
);
308 view
->canvas
->component
.ops
->paint (view
->canvas
, region
);
310 if (grub_video_have_common_points (&view
->progress_message_frame
, region
))
315 grub_gfxmenu_view_draw (grub_gfxmenu_view_t view
)
317 init_terminal (view
);
319 /* Clear the screen; there may be garbage left over in video memory. */
320 grub_video_fill_rect (grub_video_map_rgb (0, 0, 0),
321 view
->screen
.x
, view
->screen
.y
,
322 view
->screen
.width
, view
->screen
.height
);
323 grub_video_swap_buffers ();
324 if (view
->double_repaint
)
325 grub_video_fill_rect (grub_video_map_rgb (0, 0, 0),
326 view
->screen
.x
, view
->screen
.y
,
327 view
->screen
.width
, view
->screen
.height
);
329 update_menu_components (view
);
331 grub_gfxmenu_view_redraw (view
, &view
->screen
);
332 grub_video_swap_buffers ();
333 if (view
->double_repaint
)
334 grub_gfxmenu_view_redraw (view
, &view
->screen
);
338 redraw_menu_visit (grub_gui_component_t component
,
341 grub_gfxmenu_view_t view
;
343 if (component
->ops
->is_instance (component
, "list"))
345 grub_gui_list_t list
;
346 grub_video_rect_t bounds
;
348 list
= (grub_gui_list_t
) component
;
349 component
->ops
->get_bounds (component
, &bounds
);
350 grub_gfxmenu_view_redraw (view
, &bounds
);
355 grub_gfxmenu_redraw_menu (grub_gfxmenu_view_t view
)
357 update_menu_components (view
);
359 grub_gui_iterate_recursively ((grub_gui_component_t
) view
->canvas
,
360 redraw_menu_visit
, view
);
361 grub_video_swap_buffers ();
362 if (view
->double_repaint
)
364 grub_gui_iterate_recursively ((grub_gui_component_t
) view
->canvas
,
365 redraw_menu_visit
, view
);
370 grub_gfxmenu_set_chosen_entry (int entry
, void *data
)
372 grub_gfxmenu_view_t view
= data
;
374 view
->selected
= entry
;
375 grub_gfxmenu_redraw_menu (view
);
379 grub_gfxmenu_draw_terminal_box (void)
381 grub_gfxmenu_box_t term_box
;
383 term_box
= term_view
->terminal_box
;
387 term_box
->set_content_size (term_box
, term_rect
.width
,
390 term_box
->draw (term_box
,
391 term_rect
.x
- term_box
->get_left_pad (term_box
),
392 term_rect
.y
- term_box
->get_top_pad (term_box
));
393 grub_video_swap_buffers ();
394 if (term_view
->double_repaint
)
395 term_box
->draw (term_box
,
396 term_rect
.x
- term_box
->get_left_pad (term_box
),
397 term_rect
.y
- term_box
->get_top_pad (term_box
));
401 init_terminal (grub_gfxmenu_view_t view
)
403 term_rect
.width
= view
->screen
.width
* 7 / 10;
404 term_rect
.height
= view
->screen
.height
* 7 / 10;
406 term_rect
.x
= view
->screen
.x
+ view
->screen
.width
* (10 - 7) / 10 / 2;
407 term_rect
.y
= view
->screen
.y
+ view
->screen
.height
* (10 - 7) / 10 / 2;
411 /* Note: currently there is no API for changing the gfxterm font
412 on the fly, so whatever font the initially loaded theme specifies
413 will be permanent. */
414 grub_gfxterm_set_window (GRUB_VIDEO_RENDER_TARGET_DISPLAY
, term_rect
.x
,
416 term_rect
.width
, term_rect
.height
,
417 view
->double_repaint
, view
->terminal_font_name
, 3);
418 grub_gfxterm_decorator_hook
= grub_gfxmenu_draw_terminal_box
;
421 /* FIXME: previously notifications were displayed in special case.
425 /* Sets MESSAGE as the progress message for the view.
426 MESSAGE can be 0, in which case no message is displayed. */
428 set_progress_message (grub_gfxmenu_view_t view
, const char *message
)
430 grub_free (view
->progress_message_text
);
432 view
->progress_message_text
= grub_strdup (message
);
434 view
->progress_message_text
= 0;
438 notify_booting (grub_menu_entry_t entry
, void *userdata
)
440 grub_gfxmenu_view_t view
= (grub_gfxmenu_view_t
) userdata
;
442 char *s
= grub_malloc (100 + grub_strlen (entry
->title
));
446 grub_sprintf (s
, "Booting '%s'", entry
->title
);
447 set_progress_message (view
, s
);
449 grub_gfxmenu_view_redraw (view
, &view
->progress_message_frame
);
450 grub_video_swap_buffers ();
451 if (view
->double_repaint
)
452 grub_gfxmenu_view_redraw (view
, &view
->progress_message_frame
);
456 notify_fallback (grub_menu_entry_t entry
, void *userdata
)
458 grub_gfxmenu_view_t view
= (grub_gfxmenu_view_t
) userdata
;
460 char *s
= grub_malloc (100 + grub_strlen (entry
->title
));
464 grub_sprintf (s
, "Falling back to '%s'", entry
->title
);
465 set_progress_message (view
, s
);
467 grub_gfxmenu_view_redraw (view
, &view
->progress_message_frame
);
468 grub_video_swap_buffers ();
469 if (view
->double_repaint
)
470 grub_gfxmenu_view_redraw (view
, &view
->progress_message_frame
);
474 notify_execution_failure (void *userdata
__attribute__ ((unused
)))
479 static struct grub_menu_execute_callback execute_callback
=
481 .notify_booting
= notify_booting
,
482 .notify_fallback
= notify_fallback
,
483 .notify_failure
= notify_execution_failure