]>
Commit | Line | Data |
---|---|---|
d920a32a CB |
1 | /* view.c - Graphical menu interface MVC view. */ |
2 | /* | |
3 | * GRUB -- GRand Unified Bootloader | |
4 | * Copyright (C) 2008 Free Software Foundation, Inc. | |
5 | * | |
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. | |
10 | * | |
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. | |
15 | * | |
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/>. | |
18 | */ | |
19 | ||
20 | #include <grub/types.h> | |
21 | #include <grub/file.h> | |
22 | #include <grub/misc.h> | |
23 | #include <grub/mm.h> | |
24 | #include <grub/err.h> | |
25 | #include <grub/dl.h> | |
26 | #include <grub/normal.h> | |
27 | #include <grub/video.h> | |
d920a32a CB |
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> | |
bee14068 | 37 | #include <grub/gui_string_util.h> |
d920a32a | 38 | #include <grub/icon_manager.h> |
6e0632e2 | 39 | #include <grub/i18n.h> |
d920a32a | 40 | |
d3ee2d20 VS |
41 | static void |
42 | init_terminal (grub_gfxmenu_view_t view); | |
43 | static grub_video_rect_t term_rect; | |
93fd2dd8 VS |
44 | static grub_gfxmenu_view_t term_view; |
45 | ||
d920a32a CB |
46 | /* Create a new view object, loading the theme specified by THEME_PATH and |
47 | associating MODEL with the view. */ | |
48 | grub_gfxmenu_view_t | |
9b1209ba VS |
49 | grub_gfxmenu_view_new (const char *theme_path, |
50 | int width, int height) | |
d920a32a CB |
51 | { |
52 | grub_gfxmenu_view_t view; | |
9b1209ba | 53 | grub_font_t default_font; |
52832c55 CW |
54 | grub_video_rgba_color_t default_fg_color; |
55 | grub_video_rgba_color_t default_bg_color; | |
d920a32a CB |
56 | |
57 | view = grub_malloc (sizeof (*view)); | |
58 | if (! view) | |
59 | return 0; | |
60 | ||
9b1209ba VS |
61 | view->screen.x = 0; |
62 | view->screen.y = 0; | |
63 | view->screen.width = width; | |
64 | view->screen.height = height; | |
d920a32a | 65 | |
d9f31a41 | 66 | default_font = grub_font_get ("Unknown Regular 16"); |
52832c55 CW |
67 | default_fg_color = grub_video_rgba_color_rgb (0, 0, 0); |
68 | default_bg_color = grub_video_rgba_color_rgb (255, 255, 255); | |
d920a32a | 69 | |
d920a32a CB |
70 | view->canvas = 0; |
71 | ||
72 | view->title_font = default_font; | |
73 | view->message_font = default_font; | |
74 | view->terminal_font_name = grub_strdup ("Fixed 10"); | |
75 | view->title_color = default_fg_color; | |
76 | view->message_color = default_bg_color; | |
77 | view->message_bg_color = default_fg_color; | |
78 | view->desktop_image = 0; | |
79 | view->desktop_color = default_bg_color; | |
80 | view->terminal_box = grub_gfxmenu_create_box (0, 0); | |
6e0632e2 | 81 | view->title_text = grub_strdup (_("GRUB Boot Menu")); |
d920a32a CB |
82 | view->progress_message_text = 0; |
83 | view->theme_path = 0; | |
947fa16c VS |
84 | |
85 | /* Set the timeout bar's frame. */ | |
86 | view->progress_message_frame.width = view->screen.width * 4 / 5; | |
87 | view->progress_message_frame.height = 50; | |
88 | view->progress_message_frame.x = view->screen.x | |
89 | + (view->screen.width - view->progress_message_frame.width) / 2; | |
90 | view->progress_message_frame.y = view->screen.y | |
91 | + view->screen.height - 90 - 20 - view->progress_message_frame.height; | |
d920a32a CB |
92 | |
93 | if (grub_gfxmenu_view_load_theme (view, theme_path) != 0) | |
94 | { | |
95 | grub_gfxmenu_view_destroy (view); | |
96 | return 0; | |
97 | } | |
98 | ||
d920a32a CB |
99 | return view; |
100 | } | |
101 | ||
102 | /* Destroy the view object. All used memory is freed. */ | |
103 | void | |
104 | grub_gfxmenu_view_destroy (grub_gfxmenu_view_t view) | |
105 | { | |
a2b4c09b VS |
106 | if (!view) |
107 | return; | |
d920a32a CB |
108 | grub_video_bitmap_destroy (view->desktop_image); |
109 | if (view->terminal_box) | |
110 | view->terminal_box->destroy (view->terminal_box); | |
111 | grub_free (view->terminal_font_name); | |
112 | grub_free (view->title_text); | |
113 | grub_free (view->progress_message_text); | |
114 | grub_free (view->theme_path); | |
115 | if (view->canvas) | |
9a175884 | 116 | view->canvas->component.ops->destroy (view->canvas); |
d920a32a | 117 | grub_free (view); |
d920a32a CB |
118 | } |
119 | ||
d920a32a | 120 | static void |
947fa16c VS |
121 | redraw_background (grub_gfxmenu_view_t view, |
122 | const grub_video_rect_t *bounds) | |
d920a32a CB |
123 | { |
124 | if (view->desktop_image) | |
125 | { | |
126 | struct grub_video_bitmap *img = view->desktop_image; | |
127 | grub_video_blit_bitmap (img, GRUB_VIDEO_BLIT_REPLACE, | |
947fa16c VS |
128 | bounds->x, bounds->y, |
129 | bounds->x - view->screen.x, | |
130 | bounds->y - view->screen.y, | |
131 | bounds->width, bounds->height); | |
d920a32a CB |
132 | } |
133 | else | |
134 | { | |
52832c55 | 135 | grub_video_fill_rect (grub_video_map_rgba_color (view->desktop_color), |
947fa16c VS |
136 | bounds->x, bounds->y, |
137 | bounds->width, bounds->height); | |
d920a32a CB |
138 | } |
139 | } | |
140 | ||
141 | static void | |
142 | draw_title (grub_gfxmenu_view_t view) | |
143 | { | |
144 | if (! view->title_text) | |
145 | return; | |
146 | ||
147 | /* Center the title. */ | |
148 | int title_width = grub_font_get_string_width (view->title_font, | |
149 | view->title_text); | |
150 | int x = (view->screen.width - title_width) / 2; | |
151 | int y = 40 + grub_font_get_ascent (view->title_font); | |
152 | grub_font_draw_string (view->title_text, | |
153 | view->title_font, | |
52832c55 | 154 | grub_video_map_rgba_color (view->title_color), |
d920a32a CB |
155 | x, y); |
156 | } | |
157 | ||
158 | struct progress_value_data | |
159 | { | |
4d253049 VS |
160 | int visible; |
161 | int start; | |
162 | int end; | |
163 | int value; | |
d920a32a CB |
164 | }; |
165 | ||
cced9145 VS |
166 | struct grub_gfxmenu_timeout_notify *grub_gfxmenu_timeout_notifications; |
167 | ||
168 | static void | |
169 | update_timeouts (int visible, int start, int value, int end) | |
170 | { | |
171 | struct grub_gfxmenu_timeout_notify *cur; | |
172 | ||
173 | for (cur = grub_gfxmenu_timeout_notifications; cur; cur = cur->next) | |
174 | cur->set_state (cur->self, visible, start, value, end); | |
175 | } | |
176 | ||
d920a32a | 177 | static void |
cced9145 | 178 | redraw_timeouts (struct grub_gfxmenu_view *view) |
d920a32a | 179 | { |
cced9145 | 180 | struct grub_gfxmenu_timeout_notify *cur; |
947fa16c | 181 | |
cced9145 VS |
182 | for (cur = grub_gfxmenu_timeout_notifications; cur; cur = cur->next) |
183 | { | |
184 | grub_video_rect_t bounds; | |
185 | cur->self->ops->get_bounds (cur->self, &bounds); | |
186 | grub_gfxmenu_view_redraw (view, &bounds); | |
187 | } | |
d920a32a CB |
188 | } |
189 | ||
bee14068 VS |
190 | void |
191 | grub_gfxmenu_print_timeout (int timeout, void *data) | |
d920a32a | 192 | { |
bee14068 | 193 | struct grub_gfxmenu_view *view = data; |
d920a32a | 194 | |
bee14068 VS |
195 | if (view->first_timeout == -1) |
196 | view->first_timeout = timeout; | |
947fa16c | 197 | |
9efd73ec | 198 | update_timeouts (1, -view->first_timeout, -timeout, 0); |
cced9145 | 199 | redraw_timeouts (view); |
bee14068 VS |
200 | grub_video_swap_buffers (); |
201 | if (view->double_repaint) | |
cced9145 | 202 | redraw_timeouts (view); |
947fa16c VS |
203 | } |
204 | ||
bee14068 VS |
205 | void |
206 | grub_gfxmenu_clear_timeout (void *data) | |
947fa16c | 207 | { |
bee14068 VS |
208 | struct grub_gfxmenu_view *view = data; |
209 | ||
cced9145 VS |
210 | update_timeouts (0, 1, 0, 0); |
211 | redraw_timeouts (view); | |
bee14068 VS |
212 | grub_video_swap_buffers (); |
213 | if (view->double_repaint) | |
cced9145 | 214 | redraw_timeouts (view); |
d920a32a CB |
215 | } |
216 | ||
217 | static void | |
218 | update_menu_visit (grub_gui_component_t component, | |
219 | void *userdata) | |
220 | { | |
221 | grub_gfxmenu_view_t view; | |
222 | view = userdata; | |
223 | if (component->ops->is_instance (component, "list")) | |
224 | { | |
225 | grub_gui_list_t list = (grub_gui_list_t) component; | |
bee14068 | 226 | list->ops->set_view_info (list, view); |
d920a32a CB |
227 | } |
228 | } | |
229 | ||
230 | /* Update any boot menu components with the current menu model and | |
231 | theme path. */ | |
232 | static void | |
233 | update_menu_components (grub_gfxmenu_view_t view) | |
234 | { | |
235 | grub_gui_iterate_recursively ((grub_gui_component_t) view->canvas, | |
236 | update_menu_visit, view); | |
237 | } | |
238 | ||
239 | static void | |
240 | draw_message (grub_gfxmenu_view_t view) | |
241 | { | |
242 | char *text = view->progress_message_text; | |
947fa16c | 243 | grub_video_rect_t f = view->progress_message_frame; |
d920a32a CB |
244 | if (! text) |
245 | return; | |
246 | ||
247 | grub_font_t font = view->message_font; | |
52832c55 | 248 | grub_video_color_t color = grub_video_map_rgba_color (view->message_color); |
d920a32a | 249 | |
d920a32a CB |
250 | /* Border. */ |
251 | grub_video_fill_rect (color, | |
252 | f.x-1, f.y-1, f.width+2, f.height+2); | |
253 | /* Fill. */ | |
52832c55 | 254 | grub_video_fill_rect (grub_video_map_rgba_color (view->message_bg_color), |
d920a32a CB |
255 | f.x, f.y, f.width, f.height); |
256 | ||
257 | /* Center the text. */ | |
258 | int text_width = grub_font_get_string_width (font, text); | |
259 | int x = f.x + (f.width - text_width) / 2; | |
260 | int y = (f.y + (f.height - grub_font_get_descent (font)) / 2 | |
261 | + grub_font_get_ascent (font) / 2); | |
262 | grub_font_draw_string (text, font, color, x, y); | |
263 | } | |
264 | ||
d920a32a | 265 | void |
947fa16c VS |
266 | grub_gfxmenu_view_redraw (grub_gfxmenu_view_t view, |
267 | const grub_video_rect_t *region) | |
d920a32a | 268 | { |
d3ee2d20 VS |
269 | if (grub_video_have_common_points (&term_rect, region)) |
270 | grub_gfxterm_schedule_repaint (); | |
271 | ||
d920a32a | 272 | grub_video_set_active_render_target (GRUB_VIDEO_RENDER_TARGET_DISPLAY); |
d920a32a | 273 | |
947fa16c | 274 | redraw_background (view, region); |
d920a32a | 275 | if (view->canvas) |
9a175884 | 276 | view->canvas->component.ops->paint (view->canvas, region); |
d920a32a | 277 | draw_title (view); |
947fa16c VS |
278 | if (grub_video_have_common_points (&view->progress_message_frame, region)) |
279 | draw_message (view); | |
280 | } | |
281 | ||
282 | void | |
283 | grub_gfxmenu_view_draw (grub_gfxmenu_view_t view) | |
284 | { | |
d3ee2d20 VS |
285 | init_terminal (view); |
286 | ||
9b1209ba VS |
287 | /* Clear the screen; there may be garbage left over in video memory. */ |
288 | grub_video_fill_rect (grub_video_map_rgb (0, 0, 0), | |
289 | view->screen.x, view->screen.y, | |
290 | view->screen.width, view->screen.height); | |
291 | grub_video_swap_buffers (); | |
d3ee2d20 VS |
292 | if (view->double_repaint) |
293 | grub_video_fill_rect (grub_video_map_rgb (0, 0, 0), | |
294 | view->screen.x, view->screen.y, | |
295 | view->screen.width, view->screen.height); | |
9b1209ba | 296 | |
947fa16c VS |
297 | update_menu_components (view); |
298 | ||
299 | grub_gfxmenu_view_redraw (view, &view->screen); | |
300 | grub_video_swap_buffers (); | |
301 | if (view->double_repaint) | |
302 | grub_gfxmenu_view_redraw (view, &view->screen); | |
303 | } | |
304 | ||
305 | static void | |
306 | redraw_menu_visit (grub_gui_component_t component, | |
307 | void *userdata) | |
308 | { | |
309 | grub_gfxmenu_view_t view; | |
310 | view = userdata; | |
311 | if (component->ops->is_instance (component, "list")) | |
312 | { | |
947fa16c VS |
313 | grub_video_rect_t bounds; |
314 | ||
947fa16c VS |
315 | component->ops->get_bounds (component, &bounds); |
316 | grub_gfxmenu_view_redraw (view, &bounds); | |
317 | } | |
318 | } | |
319 | ||
320 | void | |
321 | grub_gfxmenu_redraw_menu (grub_gfxmenu_view_t view) | |
322 | { | |
323 | update_menu_components (view); | |
324 | ||
325 | grub_gui_iterate_recursively ((grub_gui_component_t) view->canvas, | |
326 | redraw_menu_visit, view); | |
327 | grub_video_swap_buffers (); | |
328 | if (view->double_repaint) | |
329 | { | |
330 | grub_gui_iterate_recursively ((grub_gui_component_t) view->canvas, | |
331 | redraw_menu_visit, view); | |
332 | } | |
d920a32a CB |
333 | } |
334 | ||
bee14068 VS |
335 | void |
336 | grub_gfxmenu_set_chosen_entry (int entry, void *data) | |
337 | { | |
338 | grub_gfxmenu_view_t view = data; | |
339 | ||
340 | view->selected = entry; | |
341 | grub_gfxmenu_redraw_menu (view); | |
342 | } | |
343 | ||
d920a32a | 344 | static void |
d3ee2d20 | 345 | grub_gfxmenu_draw_terminal_box (void) |
d920a32a | 346 | { |
93fd2dd8 | 347 | grub_gfxmenu_box_t term_box; |
d920a32a | 348 | |
93fd2dd8 VS |
349 | term_box = term_view->terminal_box; |
350 | if (!term_box) | |
351 | return; | |
93fd2dd8 | 352 | |
d3ee2d20 VS |
353 | term_box->set_content_size (term_box, term_rect.width, |
354 | term_rect.height); | |
93fd2dd8 VS |
355 | |
356 | term_box->draw (term_box, | |
d3ee2d20 VS |
357 | term_rect.x - term_box->get_left_pad (term_box), |
358 | term_rect.y - term_box->get_top_pad (term_box)); | |
d920a32a CB |
359 | } |
360 | ||
361 | static void | |
362 | init_terminal (grub_gfxmenu_view_t view) | |
363 | { | |
74b2fe3e DTB |
364 | const int border_width = 3; |
365 | ||
969b804f VS |
366 | grub_font_t terminal_font; |
367 | ||
74b2fe3e DTB |
368 | unsigned int line_width; |
369 | ||
370 | struct grub_font_glyph *glyph; | |
371 | ||
969b804f VS |
372 | terminal_font = grub_font_get (view->terminal_font_name); |
373 | if (!terminal_font) | |
374 | { | |
375 | grub_error (GRUB_ERR_BAD_FONT, "no font loaded"); | |
376 | return; | |
377 | } | |
378 | ||
74b2fe3e DTB |
379 | glyph = grub_font_get_glyph (terminal_font, 'M'); |
380 | ||
381 | line_width = ((glyph ? glyph->device_width : 8) * 80 + 2 * border_width); | |
382 | ||
383 | if (view->screen.width <= line_width) | |
384 | /* The screen is too small. Use all space, except a small border | |
385 | to show the user, it is a window and not full screen: */ | |
386 | term_rect.width = view->screen.width - 6 * border_width; | |
387 | else | |
388 | { | |
389 | /* The screen is big enough. Try 70% of the screen width: */ | |
390 | term_rect.width = view->screen.width * 7 / 10; | |
391 | /* Make sure, that we use at least the line_width: */ | |
392 | if ( term_rect.width < line_width ) | |
393 | term_rect.width = line_width; | |
394 | } | |
395 | ||
d3ee2d20 | 396 | term_rect.height = view->screen.height * 7 / 10; |
93fd2dd8 | 397 | |
74b2fe3e DTB |
398 | term_rect.x = view->screen.x + (view->screen.width - term_rect.width) / 2; |
399 | term_rect.y = view->screen.y + (view->screen.height - term_rect.height) / 2; | |
d920a32a | 400 | |
d3ee2d20 | 401 | term_view = view; |
93fd2dd8 | 402 | |
d920a32a CB |
403 | /* Note: currently there is no API for changing the gfxterm font |
404 | on the fly, so whatever font the initially loaded theme specifies | |
405 | will be permanent. */ | |
d3ee2d20 VS |
406 | grub_gfxterm_set_window (GRUB_VIDEO_RENDER_TARGET_DISPLAY, term_rect.x, |
407 | term_rect.y, | |
408 | term_rect.width, term_rect.height, | |
74b2fe3e DTB |
409 | view->double_repaint, terminal_font, |
410 | border_width); | |
d3ee2d20 | 411 | grub_gfxterm_decorator_hook = grub_gfxmenu_draw_terminal_box; |
d920a32a CB |
412 | } |
413 | ||
4cc972be VS |
414 | /* FIXME: previously notifications were displayed in special case. |
415 | Is it necessary? | |
416 | */ | |
d3ee2d20 VS |
417 | #if 0 |
418 | /* Sets MESSAGE as the progress message for the view. | |
419 | MESSAGE can be 0, in which case no message is displayed. */ | |
420 | static void | |
421 | set_progress_message (grub_gfxmenu_view_t view, const char *message) | |
d920a32a | 422 | { |
d3ee2d20 VS |
423 | grub_free (view->progress_message_text); |
424 | if (message) | |
425 | view->progress_message_text = grub_strdup (message); | |
426 | else | |
427 | view->progress_message_text = 0; | |
d920a32a CB |
428 | } |
429 | ||
d920a32a CB |
430 | static void |
431 | notify_booting (grub_menu_entry_t entry, void *userdata) | |
432 | { | |
433 | grub_gfxmenu_view_t view = (grub_gfxmenu_view_t) userdata; | |
434 | ||
435 | char *s = grub_malloc (100 + grub_strlen (entry->title)); | |
436 | if (!s) | |
437 | return; | |
438 | ||
439 | grub_sprintf (s, "Booting '%s'", entry->title); | |
440 | set_progress_message (view, s); | |
441 | grub_free (s); | |
947fa16c | 442 | grub_gfxmenu_view_redraw (view, &view->progress_message_frame); |
d920a32a | 443 | grub_video_swap_buffers (); |
947fa16c VS |
444 | if (view->double_repaint) |
445 | grub_gfxmenu_view_redraw (view, &view->progress_message_frame); | |
d920a32a CB |
446 | } |
447 | ||
448 | static void | |
449 | notify_fallback (grub_menu_entry_t entry, void *userdata) | |
450 | { | |
451 | grub_gfxmenu_view_t view = (grub_gfxmenu_view_t) userdata; | |
452 | ||
453 | char *s = grub_malloc (100 + grub_strlen (entry->title)); | |
454 | if (!s) | |
455 | return; | |
456 | ||
457 | grub_sprintf (s, "Falling back to '%s'", entry->title); | |
458 | set_progress_message (view, s); | |
459 | grub_free (s); | |
947fa16c | 460 | grub_gfxmenu_view_redraw (view, &view->progress_message_frame); |
d920a32a | 461 | grub_video_swap_buffers (); |
947fa16c VS |
462 | if (view->double_repaint) |
463 | grub_gfxmenu_view_redraw (view, &view->progress_message_frame); | |
d920a32a CB |
464 | } |
465 | ||
466 | static void | |
467 | notify_execution_failure (void *userdata __attribute__ ((unused))) | |
468 | { | |
469 | } | |
470 | ||
471 | ||
472 | static struct grub_menu_execute_callback execute_callback = | |
473 | { | |
474 | .notify_booting = notify_booting, | |
475 | .notify_fallback = notify_fallback, | |
476 | .notify_failure = notify_execution_failure | |
477 | }; | |
478 | ||
bee14068 | 479 | #endif |