]>
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 CB |
38 | #include <grub/icon_manager.h> |
39 | ||
40 | /* The component ID identifying GUI components to be updated as the timeout | |
41 | status changes. */ | |
42 | #define TIMEOUT_COMPONENT_ID "__timeout__" | |
43 | ||
d3ee2d20 VS |
44 | static void |
45 | init_terminal (grub_gfxmenu_view_t view); | |
46 | static grub_video_rect_t term_rect; | |
93fd2dd8 VS |
47 | static grub_gfxmenu_view_t term_view; |
48 | ||
d920a32a CB |
49 | /* Create a new view object, loading the theme specified by THEME_PATH and |
50 | associating MODEL with the view. */ | |
51 | grub_gfxmenu_view_t | |
9b1209ba VS |
52 | grub_gfxmenu_view_new (const char *theme_path, |
53 | int width, int height) | |
d920a32a CB |
54 | { |
55 | grub_gfxmenu_view_t view; | |
9b1209ba VS |
56 | grub_font_t default_font; |
57 | grub_gui_color_t default_fg_color; | |
58 | grub_gui_color_t default_bg_color; | |
d920a32a CB |
59 | |
60 | view = grub_malloc (sizeof (*view)); | |
61 | if (! view) | |
62 | return 0; | |
63 | ||
9b1209ba VS |
64 | view->screen.x = 0; |
65 | view->screen.y = 0; | |
66 | view->screen.width = width; | |
67 | view->screen.height = height; | |
d920a32a | 68 | |
d9f31a41 | 69 | default_font = grub_font_get ("Unknown Regular 16"); |
d920a32a CB |
70 | default_fg_color = grub_gui_color_rgb (0, 0, 0); |
71 | default_bg_color = grub_gui_color_rgb (255, 255, 255); | |
72 | ||
d920a32a CB |
73 | view->canvas = 0; |
74 | ||
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; | |
86 | view->theme_path = 0; | |
947fa16c VS |
87 | |
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; | |
d920a32a CB |
95 | |
96 | if (grub_gfxmenu_view_load_theme (view, theme_path) != 0) | |
97 | { | |
98 | grub_gfxmenu_view_destroy (view); | |
99 | return 0; | |
100 | } | |
101 | ||
d920a32a CB |
102 | return view; |
103 | } | |
104 | ||
105 | /* Destroy the view object. All used memory is freed. */ | |
106 | void | |
107 | grub_gfxmenu_view_destroy (grub_gfxmenu_view_t view) | |
108 | { | |
a2b4c09b VS |
109 | if (!view) |
110 | return; | |
d920a32a CB |
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); | |
118 | if (view->canvas) | |
9a175884 | 119 | view->canvas->component.ops->destroy (view->canvas); |
d920a32a | 120 | grub_free (view); |
d920a32a CB |
121 | } |
122 | ||
d920a32a | 123 | static void |
947fa16c VS |
124 | redraw_background (grub_gfxmenu_view_t view, |
125 | const grub_video_rect_t *bounds) | |
d920a32a CB |
126 | { |
127 | if (view->desktop_image) | |
128 | { | |
129 | struct grub_video_bitmap *img = view->desktop_image; | |
130 | grub_video_blit_bitmap (img, GRUB_VIDEO_BLIT_REPLACE, | |
947fa16c VS |
131 | bounds->x, bounds->y, |
132 | bounds->x - view->screen.x, | |
133 | bounds->y - view->screen.y, | |
134 | bounds->width, bounds->height); | |
d920a32a CB |
135 | } |
136 | else | |
137 | { | |
138 | grub_video_fill_rect (grub_gui_map_color (view->desktop_color), | |
947fa16c VS |
139 | bounds->x, bounds->y, |
140 | bounds->width, bounds->height); | |
d920a32a CB |
141 | } |
142 | } | |
143 | ||
144 | static void | |
145 | draw_title (grub_gfxmenu_view_t view) | |
146 | { | |
147 | if (! view->title_text) | |
148 | return; | |
149 | ||
150 | /* Center the title. */ | |
151 | int title_width = grub_font_get_string_width (view->title_font, | |
152 | view->title_text); | |
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, | |
156 | view->title_font, | |
157 | grub_gui_map_color (view->title_color), | |
158 | x, y); | |
159 | } | |
160 | ||
161 | struct progress_value_data | |
162 | { | |
4d253049 VS |
163 | int visible; |
164 | int start; | |
165 | int end; | |
166 | int value; | |
d920a32a CB |
167 | }; |
168 | ||
169 | static void | |
170 | update_timeout_visit (grub_gui_component_t component, | |
171 | void *userdata) | |
172 | { | |
173 | struct progress_value_data *pv; | |
174 | pv = (struct progress_value_data *) userdata; | |
947fa16c | 175 | |
4d253049 VS |
176 | ((struct grub_gui_progress *) component)->ops |
177 | ->set_state ((struct grub_gui_progress *) component, | |
178 | pv->visible, pv->start, pv->value, pv->end); | |
d920a32a CB |
179 | } |
180 | ||
bee14068 VS |
181 | void |
182 | grub_gfxmenu_print_timeout (int timeout, void *data) | |
d920a32a | 183 | { |
bee14068 | 184 | struct grub_gfxmenu_view *view = data; |
d920a32a | 185 | |
d920a32a | 186 | struct progress_value_data pv; |
947fa16c VS |
187 | |
188 | auto void redraw_timeout_visit (grub_gui_component_t component, | |
189 | void *userdata __attribute__ ((unused))); | |
190 | ||
191 | auto void redraw_timeout_visit (grub_gui_component_t component, | |
192 | void *userdata __attribute__ ((unused))) | |
193 | { | |
194 | grub_video_rect_t bounds; | |
195 | component->ops->get_bounds (component, &bounds); | |
196 | grub_gfxmenu_view_redraw (view, &bounds); | |
197 | } | |
198 | ||
bee14068 VS |
199 | if (view->first_timeout == -1) |
200 | view->first_timeout = timeout; | |
947fa16c | 201 | |
4d253049 VS |
202 | pv.visible = 1; |
203 | pv.start = -(view->first_timeout + 1); | |
204 | pv.end = 0; | |
205 | pv.value = -timeout; | |
d920a32a CB |
206 | |
207 | grub_gui_find_by_id ((grub_gui_component_t) view->canvas, | |
208 | TIMEOUT_COMPONENT_ID, update_timeout_visit, &pv); | |
bee14068 VS |
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); | |
947fa16c VS |
215 | } |
216 | ||
bee14068 VS |
217 | void |
218 | grub_gfxmenu_clear_timeout (void *data) | |
947fa16c | 219 | { |
bee14068 VS |
220 | struct progress_value_data pv; |
221 | struct grub_gfxmenu_view *view = data; | |
222 | ||
223 | auto void redraw_timeout_visit (grub_gui_component_t component, | |
224 | void *userdata __attribute__ ((unused))); | |
225 | ||
226 | auto void redraw_timeout_visit (grub_gui_component_t component, | |
227 | void *userdata __attribute__ ((unused))) | |
228 | { | |
229 | grub_video_rect_t bounds; | |
230 | component->ops->get_bounds (component, &bounds); | |
231 | grub_gfxmenu_view_redraw (view, &bounds); | |
232 | } | |
233 | ||
4d253049 VS |
234 | pv.visible = 0; |
235 | pv.start = 1; | |
236 | pv.end = 0; | |
237 | pv.value = 0; | |
bee14068 VS |
238 | |
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); | |
d920a32a CB |
247 | } |
248 | ||
249 | static void | |
250 | update_menu_visit (grub_gui_component_t component, | |
251 | void *userdata) | |
252 | { | |
253 | grub_gfxmenu_view_t view; | |
254 | view = userdata; | |
255 | if (component->ops->is_instance (component, "list")) | |
256 | { | |
257 | grub_gui_list_t list = (grub_gui_list_t) component; | |
bee14068 | 258 | list->ops->set_view_info (list, view); |
d920a32a CB |
259 | } |
260 | } | |
261 | ||
262 | /* Update any boot menu components with the current menu model and | |
263 | theme path. */ | |
264 | static void | |
265 | update_menu_components (grub_gfxmenu_view_t view) | |
266 | { | |
267 | grub_gui_iterate_recursively ((grub_gui_component_t) view->canvas, | |
268 | update_menu_visit, view); | |
269 | } | |
270 | ||
271 | static void | |
272 | draw_message (grub_gfxmenu_view_t view) | |
273 | { | |
274 | char *text = view->progress_message_text; | |
947fa16c | 275 | grub_video_rect_t f = view->progress_message_frame; |
d920a32a CB |
276 | if (! text) |
277 | return; | |
278 | ||
279 | grub_font_t font = view->message_font; | |
280 | grub_video_color_t color = grub_gui_map_color (view->message_color); | |
281 | ||
d920a32a CB |
282 | /* Border. */ |
283 | grub_video_fill_rect (color, | |
284 | f.x-1, f.y-1, f.width+2, f.height+2); | |
285 | /* Fill. */ | |
286 | grub_video_fill_rect (grub_gui_map_color (view->message_bg_color), | |
287 | f.x, f.y, f.width, f.height); | |
288 | ||
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); | |
295 | } | |
296 | ||
d920a32a | 297 | void |
947fa16c VS |
298 | grub_gfxmenu_view_redraw (grub_gfxmenu_view_t view, |
299 | const grub_video_rect_t *region) | |
d920a32a | 300 | { |
d3ee2d20 VS |
301 | if (grub_video_have_common_points (&term_rect, region)) |
302 | grub_gfxterm_schedule_repaint (); | |
303 | ||
d920a32a | 304 | grub_video_set_active_render_target (GRUB_VIDEO_RENDER_TARGET_DISPLAY); |
d920a32a | 305 | |
947fa16c | 306 | redraw_background (view, region); |
d920a32a | 307 | if (view->canvas) |
9a175884 | 308 | view->canvas->component.ops->paint (view->canvas, region); |
d920a32a | 309 | draw_title (view); |
947fa16c VS |
310 | if (grub_video_have_common_points (&view->progress_message_frame, region)) |
311 | draw_message (view); | |
312 | } | |
313 | ||
314 | void | |
315 | grub_gfxmenu_view_draw (grub_gfxmenu_view_t view) | |
316 | { | |
d3ee2d20 VS |
317 | init_terminal (view); |
318 | ||
9b1209ba VS |
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 (); | |
d3ee2d20 VS |
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); | |
9b1209ba | 328 | |
947fa16c VS |
329 | update_menu_components (view); |
330 | ||
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); | |
335 | } | |
336 | ||
337 | static void | |
338 | redraw_menu_visit (grub_gui_component_t component, | |
339 | void *userdata) | |
340 | { | |
341 | grub_gfxmenu_view_t view; | |
342 | view = userdata; | |
343 | if (component->ops->is_instance (component, "list")) | |
344 | { | |
345 | grub_gui_list_t list; | |
346 | grub_video_rect_t bounds; | |
347 | ||
348 | list = (grub_gui_list_t) component; | |
349 | component->ops->get_bounds (component, &bounds); | |
350 | grub_gfxmenu_view_redraw (view, &bounds); | |
351 | } | |
352 | } | |
353 | ||
354 | void | |
355 | grub_gfxmenu_redraw_menu (grub_gfxmenu_view_t view) | |
356 | { | |
357 | update_menu_components (view); | |
358 | ||
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) | |
363 | { | |
364 | grub_gui_iterate_recursively ((grub_gui_component_t) view->canvas, | |
365 | redraw_menu_visit, view); | |
366 | } | |
d920a32a CB |
367 | } |
368 | ||
bee14068 VS |
369 | void |
370 | grub_gfxmenu_set_chosen_entry (int entry, void *data) | |
371 | { | |
372 | grub_gfxmenu_view_t view = data; | |
373 | ||
374 | view->selected = entry; | |
375 | grub_gfxmenu_redraw_menu (view); | |
376 | } | |
377 | ||
d920a32a | 378 | static void |
d3ee2d20 | 379 | grub_gfxmenu_draw_terminal_box (void) |
d920a32a | 380 | { |
93fd2dd8 | 381 | grub_gfxmenu_box_t term_box; |
d920a32a | 382 | |
93fd2dd8 VS |
383 | term_box = term_view->terminal_box; |
384 | if (!term_box) | |
385 | return; | |
93fd2dd8 | 386 | |
d3ee2d20 VS |
387 | term_box->set_content_size (term_box, term_rect.width, |
388 | term_rect.height); | |
93fd2dd8 VS |
389 | |
390 | term_box->draw (term_box, | |
d3ee2d20 VS |
391 | term_rect.x - term_box->get_left_pad (term_box), |
392 | term_rect.y - term_box->get_top_pad (term_box)); | |
947fa16c | 393 | grub_video_swap_buffers (); |
d3ee2d20 | 394 | if (term_view->double_repaint) |
947fa16c | 395 | term_box->draw (term_box, |
d3ee2d20 VS |
396 | term_rect.x - term_box->get_left_pad (term_box), |
397 | term_rect.y - term_box->get_top_pad (term_box)); | |
d920a32a CB |
398 | } |
399 | ||
400 | static void | |
401 | init_terminal (grub_gfxmenu_view_t view) | |
402 | { | |
d3ee2d20 VS |
403 | term_rect.width = view->screen.width * 7 / 10; |
404 | term_rect.height = view->screen.height * 7 / 10; | |
93fd2dd8 | 405 | |
d3ee2d20 VS |
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; | |
d920a32a | 408 | |
d3ee2d20 | 409 | term_view = view; |
93fd2dd8 | 410 | |
d920a32a CB |
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. */ | |
d3ee2d20 VS |
414 | grub_gfxterm_set_window (GRUB_VIDEO_RENDER_TARGET_DISPLAY, term_rect.x, |
415 | term_rect.y, | |
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; | |
d920a32a CB |
419 | } |
420 | ||
4cc972be VS |
421 | /* FIXME: previously notifications were displayed in special case. |
422 | Is it necessary? | |
423 | */ | |
d3ee2d20 VS |
424 | #if 0 |
425 | /* Sets MESSAGE as the progress message for the view. | |
426 | MESSAGE can be 0, in which case no message is displayed. */ | |
427 | static void | |
428 | set_progress_message (grub_gfxmenu_view_t view, const char *message) | |
d920a32a | 429 | { |
d3ee2d20 VS |
430 | grub_free (view->progress_message_text); |
431 | if (message) | |
432 | view->progress_message_text = grub_strdup (message); | |
433 | else | |
434 | view->progress_message_text = 0; | |
d920a32a CB |
435 | } |
436 | ||
d920a32a CB |
437 | static void |
438 | notify_booting (grub_menu_entry_t entry, void *userdata) | |
439 | { | |
440 | grub_gfxmenu_view_t view = (grub_gfxmenu_view_t) userdata; | |
441 | ||
442 | char *s = grub_malloc (100 + grub_strlen (entry->title)); | |
443 | if (!s) | |
444 | return; | |
445 | ||
446 | grub_sprintf (s, "Booting '%s'", entry->title); | |
447 | set_progress_message (view, s); | |
448 | grub_free (s); | |
947fa16c | 449 | grub_gfxmenu_view_redraw (view, &view->progress_message_frame); |
d920a32a | 450 | grub_video_swap_buffers (); |
947fa16c VS |
451 | if (view->double_repaint) |
452 | grub_gfxmenu_view_redraw (view, &view->progress_message_frame); | |
d920a32a CB |
453 | } |
454 | ||
455 | static void | |
456 | notify_fallback (grub_menu_entry_t entry, void *userdata) | |
457 | { | |
458 | grub_gfxmenu_view_t view = (grub_gfxmenu_view_t) userdata; | |
459 | ||
460 | char *s = grub_malloc (100 + grub_strlen (entry->title)); | |
461 | if (!s) | |
462 | return; | |
463 | ||
464 | grub_sprintf (s, "Falling back to '%s'", entry->title); | |
465 | set_progress_message (view, s); | |
466 | grub_free (s); | |
947fa16c | 467 | grub_gfxmenu_view_redraw (view, &view->progress_message_frame); |
d920a32a | 468 | grub_video_swap_buffers (); |
947fa16c VS |
469 | if (view->double_repaint) |
470 | grub_gfxmenu_view_redraw (view, &view->progress_message_frame); | |
d920a32a CB |
471 | } |
472 | ||
473 | static void | |
474 | notify_execution_failure (void *userdata __attribute__ ((unused))) | |
475 | { | |
476 | } | |
477 | ||
478 | ||
479 | static struct grub_menu_execute_callback execute_callback = | |
480 | { | |
481 | .notify_booting = notify_booting, | |
482 | .notify_fallback = notify_fallback, | |
483 | .notify_failure = notify_execution_failure | |
484 | }; | |
485 | ||
bee14068 | 486 | #endif |