Initial commit from Ventoy@7cbdc5c

This commit is contained in:
2026-08-23 00:38:40 -04:00
commit e9a7a16826
2848 changed files with 439164 additions and 0 deletions
@@ -0,0 +1,165 @@
/* gfxmenu.c - Graphical menu interface controller. */
/*
* GRUB -- GRand Unified Bootloader
* Copyright (C) 2008 Free Software Foundation, Inc.
*
* GRUB is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* GRUB is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with GRUB. If not, see <http://www.gnu.org/licenses/>.
*/
#include <grub/types.h>
#include <grub/misc.h>
#include <grub/mm.h>
#include <grub/err.h>
#include <grub/dl.h>
#include <grub/command.h>
#include <grub/video.h>
#include <grub/gfxterm.h>
#include <grub/bitmap.h>
#include <grub/bitmap_scale.h>
#include <grub/term.h>
#include <grub/env.h>
#include <grub/normal.h>
#include <grub/gfxwidgets.h>
#include <grub/menu.h>
#include <grub/menu_viewer.h>
#include <grub/gfxmenu_model.h>
#include <grub/gfxmenu_view.h>
#include <grub/time.h>
#include <grub/env.h>
#include <grub/i18n.h>
GRUB_MOD_LICENSE ("GPLv3+");
extern int g_ventoy_menu_refresh;
static grub_gfxmenu_view_t cached_view;
static void
grub_gfxmenu_viewer_fini (void *data __attribute__ ((unused)))
{
}
/* FIXME: Previously 't' changed to text menu is it necessary? */
static grub_err_t
grub_gfxmenu_try (int entry, grub_menu_t menu, int nested)
{
int force_refresh = 0;
grub_gfxmenu_view_t view = NULL;
const char *theme_path;
char *full_theme_path = 0;
struct grub_menu_viewer *instance;
grub_err_t err;
struct grub_video_mode_info mode_info;
theme_path = grub_env_get ("theme");
if (! theme_path)
return grub_error (GRUB_ERR_FILE_NOT_FOUND, N_("variable `%s' isn't set"),
"theme");
err = grub_video_get_info (&mode_info);
if (err)
return err;
instance = grub_zalloc (sizeof (*instance));
if (!instance)
return grub_errno;
if (theme_path[0] != '/' && theme_path[0] != '(')
{
const char *prefix;
prefix = grub_env_get ("prefix");
full_theme_path = grub_xasprintf ("%s/themes/%s",
prefix,
theme_path);
}
if (g_ventoy_menu_refresh)
{
g_ventoy_menu_refresh = 0;
force_refresh = 1;
}
if (force_refresh ||
!cached_view || grub_strcmp (cached_view->theme_path,
full_theme_path ? : theme_path) != 0
|| cached_view->screen.width != mode_info.width
|| cached_view->screen.height != mode_info.height)
{
grub_gfxmenu_view_destroy (cached_view);
/* Create the view. */
cached_view = grub_gfxmenu_view_new (full_theme_path ? : theme_path,
mode_info.width,
mode_info.height);
}
grub_free (full_theme_path);
if (! cached_view)
{
grub_free (instance);
return grub_errno;
}
view = cached_view;
view->double_repaint = (mode_info.mode_type
& GRUB_VIDEO_MODE_TYPE_DOUBLE_BUFFERED)
&& !(mode_info.mode_type & GRUB_VIDEO_MODE_TYPE_UPDATING_SWAP);
view->selected = entry;
view->menu = menu;
view->nested = nested;
view->first_timeout = -1;
if (menu->size)
view->menu_title_offset = grub_zalloc (sizeof (*view->menu_title_offset) * menu->size);
grub_video_set_viewport (0, 0, mode_info.width, mode_info.height);
if (view->double_repaint)
{
grub_video_swap_buffers ();
grub_video_set_viewport (0, 0, mode_info.width, mode_info.height);
}
grub_gfxmenu_view_draw (view);
instance->data = view;
instance->set_chosen_entry = grub_gfxmenu_set_chosen_entry;
instance->fini = grub_gfxmenu_viewer_fini;
instance->print_timeout = grub_gfxmenu_print_timeout;
instance->clear_timeout = grub_gfxmenu_clear_timeout;
if (view->menu_title_offset)
instance->scroll_chosen_entry = grub_gfxmenu_scroll_chosen_entry;
grub_menu_register_viewer (instance);
return GRUB_ERR_NONE;
}
GRUB_MOD_INIT (gfxmenu)
{
struct grub_term_output *term;
FOR_ACTIVE_TERM_OUTPUTS(term)
if (grub_gfxmenu_try_hook && term->fullscreen)
{
term->fullscreen ();
break;
}
grub_gfxmenu_try_hook = grub_gfxmenu_try;
}
GRUB_MOD_FINI (gfxmenu)
{
grub_gfxmenu_view_destroy (cached_view);
grub_gfxmenu_try_hook = NULL;
}
@@ -0,0 +1,324 @@
/* gui_label.c - GUI component to display a line of text. */
/*
* GRUB -- GRand Unified Bootloader
* Copyright (C) 2008,2009 Free Software Foundation, Inc.
*
* GRUB is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* GRUB is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with GRUB. If not, see <http://www.gnu.org/licenses/>.
*/
#include <grub/mm.h>
#include <grub/misc.h>
#include <grub/gui.h>
#include <grub/font.h>
#include <grub/gui_string_util.h>
#include <grub/i18n.h>
#include <grub/color.h>
#include <grub/env.h>
extern int g_ventoy_memdisk_mode;
extern int g_ventoy_iso_raw;
extern int g_ventoy_grub2_mode;
extern int g_ventoy_wimboot_mode;
extern int g_ventoy_iso_uefi_drv;
extern char g_ventoy_hotkey_tip[256];
static const char *align_options[] =
{
"left",
"center",
"right",
0
};
enum align_mode {
align_left,
align_center,
align_right
};
struct grub_gui_label
{
struct grub_gui_component comp;
grub_gui_container_t parent;
grub_video_rect_t bounds;
char *id;
int visible;
char *text;
char *template;
grub_font_t font;
grub_video_rgba_color_t color;
int value;
int vtoytip;
enum align_mode align;
};
typedef struct grub_gui_label *grub_gui_label_t;
extern const char * g_ventoy_tip_msg1;
extern const char * g_ventoy_tip_msg2;
static void
label_destroy (void *vself)
{
grub_gui_label_t self = vself;
grub_gfxmenu_timeout_unregister ((grub_gui_component_t) self);
grub_free (self->text);
grub_free (self->template);
grub_free (self);
}
static const char *
label_get_id (void *vself)
{
grub_gui_label_t self = vself;
return self->id;
}
static int
label_is_instance (void *vself __attribute__((unused)), const char *type)
{
return grub_strcmp (type, "component") == 0;
}
static void
label_paint (void *vself, const grub_video_rect_t *region)
{
const char *text;
grub_gui_label_t self = vself;
if (! self->visible)
return;
if (!grub_video_have_common_points (region, &self->bounds))
return;
if (self->vtoytip == 1) {
text = g_ventoy_tip_msg1 ? g_ventoy_tip_msg1 : "";
} else if (self->vtoytip == 2) {
text = g_ventoy_tip_msg2 ? g_ventoy_tip_msg2 : "";
} else {
text = self->text;
}
/* Calculate the starting x coordinate. */
int left_x;
if (self->align == align_left)
left_x = 0;
else if (self->align == align_center)
left_x = (self->bounds.width
- grub_font_get_string_width (self->font, text)) / 2;
else if (self->align == align_right)
left_x = (self->bounds.width
- grub_font_get_string_width (self->font, text));
else
return; /* Invalid alignment. */
if (left_x < 0 || left_x > (int) self->bounds.width)
left_x = 0;
grub_video_rect_t vpsave;
grub_gui_set_viewport (&self->bounds, &vpsave);
grub_font_draw_string (text,
self->font,
grub_video_map_rgba_color (self->color),
left_x,
grub_font_get_ascent (self->font));
grub_gui_restore_viewport (&vpsave);
}
static void
label_set_parent (void *vself, grub_gui_container_t parent)
{
grub_gui_label_t self = vself;
self->parent = parent;
}
static grub_gui_container_t
label_get_parent (void *vself)
{
grub_gui_label_t self = vself;
return self->parent;
}
static void
label_set_bounds (void *vself, const grub_video_rect_t *bounds)
{
grub_gui_label_t self = vself;
self->bounds = *bounds;
}
static void
label_get_bounds (void *vself, grub_video_rect_t *bounds)
{
grub_gui_label_t self = vself;
*bounds = self->bounds;
}
static void
label_get_minimal_size (void *vself, unsigned *width, unsigned *height)
{
grub_gui_label_t self = vself;
*width = grub_font_get_string_width (self->font, self->text);
*height = (grub_font_get_ascent (self->font)
+ grub_font_get_descent (self->font));
}
#pragma GCC diagnostic ignored "-Wformat-nonliteral"
static void
label_set_state (void *vself, int visible, int start __attribute__ ((unused)),
int current, int end __attribute__ ((unused)))
{
grub_gui_label_t self = vself;
self->value = -current;
self->visible = visible;
grub_free (self->text);
self->text = grub_xasprintf (self->template ? : "%d", self->value);
}
static grub_err_t
label_set_property (void *vself, const char *name, const char *value)
{
grub_gui_label_t self = vself;
if (grub_strcmp (name, "text") == 0)
{
grub_free (self->text);
grub_free (self->template);
if (! value)
{
self->template = NULL;
self->text = grub_strdup ("");
}
else
{
if (grub_strcmp (value, "@KEYMAP_LONG@") == 0)
value = _("Press enter to boot the selected OS, "
"`e' to edit the commands before booting "
"or `c' for a command-line. ESC to return previous menu.");
else if (grub_strcmp (value, "@KEYMAP_MIDDLE@") == 0)
value = _("Press enter to boot the selected OS, "
"`e' to edit the commands before booting "
"or `c' for a command-line.");
else if (grub_strcmp (value, "@KEYMAP_SHORT@") == 0)
value = _("enter: boot, `e': options, `c': cmd-line");
/* FIXME: Add more templates here if needed. */
else if (grub_strcmp (value, "@VTOY_MEM_DISK@") == 0) {
value = g_ventoy_memdisk_mode ? grub_env_get("VTOY_MEM_DISK_STR") : " ";
}
else if (grub_strcmp (value, "@VTOY_ISO_RAW@") == 0) {
value = g_ventoy_iso_raw ? grub_env_get("VTOY_ISO_RAW_STR") : " ";
}
else if (grub_strcmp (value, "@VTOY_GRUB2_MODE@") == 0) {
value = g_ventoy_grub2_mode ? grub_env_get("VTOY_GRUB2_MODE_STR") : " ";
}
else if (grub_strcmp (value, "@VTOY_WIMBOOT_MODE@") == 0) {
value = g_ventoy_wimboot_mode ? grub_env_get("VTOY_WIMBOOT_MODE_STR") : " ";
}
else if (grub_strcmp (value, "@VTOY_ISO_UEFI_DRV@") == 0) {
value = g_ventoy_iso_uefi_drv ? grub_env_get("VTOY_ISO_UEFI_DRV_STR") : " ";
}
else if (grub_strcmp (value, "@VTOY_HOTKEY_TIP@") == 0) {
value = g_ventoy_hotkey_tip;
} else if (value[0] == '@' && value[1] == '@' && value[2]) {
value = grub_env_get(value + 2);
if (!value)
value = " ";
}
self->template = grub_strdup (value);
self->text = grub_xasprintf (value, self->value);
}
}
else if (grub_strcmp (name, "font") == 0)
{
self->font = grub_font_get (value);
}
else if (grub_strcmp (name, "color") == 0)
{
grub_video_parse_color (value, &self->color);
}
else if (grub_strcmp (name, "align") == 0)
{
int i;
for (i = 0; align_options[i]; i++)
{
if (grub_strcmp (align_options[i], value) == 0)
{
self->align = i; /* Set the alignment mode. */
break;
}
}
}
else if (grub_strcmp (name, "visible") == 0)
{
self->visible = grub_strcmp (value, "false") != 0;
}
else if (grub_strcmp (name, "id") == 0)
{
grub_gfxmenu_timeout_unregister ((grub_gui_component_t) self);
grub_free (self->id);
if (value) {
self->id = grub_strdup (value);
if (grub_strcmp(value, "VTOY_MENU_TIP_1") == 0) {
self->vtoytip = 1;
} else if (grub_strcmp(value, "VTOY_MENU_TIP_2") == 0) {
self->vtoytip = 2;
}
}
else
self->id = 0;
if (self->id && grub_strcmp (self->id, GRUB_GFXMENU_TIMEOUT_COMPONENT_ID)
== 0)
grub_gfxmenu_timeout_register ((grub_gui_component_t) self,
label_set_state);
}
return GRUB_ERR_NONE;
}
#pragma GCC diagnostic error "-Wformat-nonliteral"
static struct grub_gui_component_ops label_ops =
{
.destroy = label_destroy,
.get_id = label_get_id,
.is_instance = label_is_instance,
.paint = label_paint,
.set_parent = label_set_parent,
.get_parent = label_get_parent,
.set_bounds = label_set_bounds,
.get_bounds = label_get_bounds,
.get_minimal_size = label_get_minimal_size,
.set_property = label_set_property
};
grub_gui_component_t
grub_gui_label_new (void)
{
grub_gui_label_t label;
label = grub_zalloc (sizeof (*label));
if (! label)
return 0;
label->comp.ops = &label_ops;
label->visible = 1;
label->text = grub_strdup ("");
label->font = grub_font_get ("Unknown Regular 16");
label->color.red = 0;
label->color.green = 0;
label->color.blue = 0;
label->color.alpha = 255;
label->align = align_left;
return (grub_gui_component_t) label;
}
@@ -0,0 +1,961 @@
/* gui_list.c - GUI component to display a selectable list of items. */
/*
* GRUB -- GRand Unified Bootloader
* Copyright (C) 2008,2009 Free Software Foundation, Inc.
*
* GRUB is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* GRUB is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with GRUB. If not, see <http://www.gnu.org/licenses/>.
*/
#include <grub/mm.h>
#include <grub/misc.h>
#include <grub/gui.h>
#include <grub/gui_string_util.h>
#include <grub/gfxmenu_view.h>
#include <grub/gfxwidgets.h>
#include <grub/color.h>
#include <grub/charset.h>
enum scrollbar_slice_mode {
SCROLLBAR_SLICE_WEST,
SCROLLBAR_SLICE_CENTER,
SCROLLBAR_SLICE_EAST
};
struct grub_gui_list_impl
{
struct grub_gui_list list;
grub_gui_container_t parent;
grub_video_rect_t bounds;
char *id;
int visible;
int icon_width;
int icon_height;
int item_height;
int item_padding;
int item_icon_space;
int item_spacing;
grub_font_t item_font;
int selected_item_font_inherit;
grub_font_t selected_item_font;
grub_video_rgba_color_t item_color;
int selected_item_color_inherit;
grub_video_rgba_color_t selected_item_color;
int draw_scrollbar;
int need_to_recreate_scrollbar;
char *scrollbar_frame_pattern;
char *scrollbar_thumb_pattern;
grub_gfxmenu_box_t scrollbar_frame;
grub_gfxmenu_box_t scrollbar_thumb;
int scrollbar_thumb_overlay;
int scrollbar_width;
enum scrollbar_slice_mode scrollbar_slice;
int scrollbar_left_pad;
int scrollbar_right_pad;
int scrollbar_top_pad;
int scrollbar_bottom_pad;
int first_shown_index;
int need_to_recreate_boxes;
char *theme_dir;
char *menu_box_pattern;
char *item_box_pattern;
int selected_item_box_pattern_inherit;
char *selected_item_box_pattern;
grub_gfxmenu_box_t menu_box;
grub_gfxmenu_box_t selected_item_box;
grub_gfxmenu_box_t item_box;
grub_gfxmenu_icon_manager_t icon_manager;
grub_gfxmenu_view_t view;
};
typedef struct grub_gui_list_impl *list_impl_t;
static void
list_destroy (void *vself)
{
list_impl_t self = vself;
grub_free (self->theme_dir);
grub_free (self->menu_box_pattern);
grub_free (self->item_box_pattern);
grub_free (self->selected_item_box_pattern);
if (self->menu_box)
self->menu_box->destroy (self->menu_box);
if (self->item_box)
self->item_box->destroy (self->item_box);
if (self->selected_item_box)
self->selected_item_box->destroy (self->selected_item_box);
if (self->icon_manager)
grub_gfxmenu_icon_manager_destroy (self->icon_manager);
if (self->scrollbar_thumb)
self->scrollbar_thumb->destroy (self->scrollbar_thumb);
if (self->scrollbar_frame)
self->scrollbar_frame->destroy (self->scrollbar_frame);
grub_free (self->scrollbar_thumb_pattern);
grub_free (self->scrollbar_frame_pattern);
grub_free (self);
}
static int
get_num_shown_items (list_impl_t self)
{
int boxpad = self->item_padding;
int item_vspace = self->item_spacing;
int item_height = self->item_height;
grub_gfxmenu_box_t box = self->menu_box;
int box_top_pad = box->get_top_pad (box);
int box_bottom_pad = box->get_bottom_pad (box);
grub_gfxmenu_box_t itembox = self->item_box;
grub_gfxmenu_box_t selbox = self->selected_item_box;
int item_top_pad = itembox->get_top_pad (itembox);
int item_bottom_pad = itembox->get_bottom_pad (itembox);
int sel_top_pad = selbox->get_top_pad (selbox);
int sel_bottom_pad = selbox->get_bottom_pad (selbox);
int max_top_pad = grub_max (item_top_pad, sel_top_pad);
int max_bottom_pad = grub_max (item_bottom_pad, sel_bottom_pad);
if (item_height + item_vspace <= 0)
return 1;
return (self->bounds.height + item_vspace - 2 * boxpad
- max_top_pad - max_bottom_pad
- box_top_pad - box_bottom_pad) / (item_height + item_vspace);
}
static int
check_boxes (list_impl_t self)
{
if (self->need_to_recreate_boxes)
{
grub_gui_recreate_box (&self->menu_box,
self->menu_box_pattern,
self->theme_dir);
grub_gui_recreate_box (&self->item_box,
self->item_box_pattern,
self->theme_dir);
grub_gui_recreate_box (&self->selected_item_box,
self->selected_item_box_pattern,
self->theme_dir);
self->need_to_recreate_boxes = 0;
}
return (self->menu_box != 0 && self->selected_item_box != 0
&& self->item_box != 0);
}
static int
check_scrollbar (list_impl_t self)
{
if (self->need_to_recreate_scrollbar)
{
grub_gui_recreate_box (&self->scrollbar_frame,
self->scrollbar_frame_pattern,
self->theme_dir);
grub_gui_recreate_box (&self->scrollbar_thumb,
self->scrollbar_thumb_pattern,
self->theme_dir);
self->need_to_recreate_scrollbar = 0;
}
if (self->scrollbar_frame == 0 || self->scrollbar_thumb == 0)
return 0;
/* Sanity checks. */
grub_gfxmenu_box_t frame = self->scrollbar_frame;
grub_gfxmenu_box_t thumb = self->scrollbar_thumb;
grub_gfxmenu_box_t menu = self->menu_box;
int min_width = frame->get_left_pad (frame)
+ frame->get_right_pad (frame);
int min_height = frame->get_top_pad (frame)
+ frame->get_bottom_pad (frame)
+ self->scrollbar_top_pad + self->scrollbar_bottom_pad
+ menu->get_top_pad (menu)
+ menu->get_bottom_pad (menu);
if (!self->scrollbar_thumb_overlay)
{
min_width += thumb->get_left_pad (thumb)
+ thumb->get_right_pad (thumb);
min_height += thumb->get_top_pad (thumb)
+ thumb->get_bottom_pad (thumb);
}
if (min_width <= self->scrollbar_width
&& min_height <= (int) self->bounds.height)
return 1;
/* Unprintable dimenstions. */
self->draw_scrollbar = 0;
return 0;
}
static const char *
list_get_id (void *vself)
{
list_impl_t self = vself;
return self->id;
}
static int
list_is_instance (void *vself __attribute__((unused)), const char *type)
{
return (grub_strcmp (type, "component") == 0
|| grub_strcmp (type, "list") == 0);
}
static struct grub_video_bitmap *
get_item_icon (list_impl_t self, int item_index)
{
grub_menu_entry_t entry;
entry = grub_menu_get_entry (self->view->menu, item_index);
if (! entry)
return 0;
return grub_gfxmenu_icon_manager_get_icon (self->icon_manager, entry);
}
static void
make_selected_item_visible (list_impl_t self)
{
int selected_index = self->view->selected;
if (selected_index < 0)
return; /* No item is selected. */
int num_shown_items = get_num_shown_items (self);
int last_shown_index = self->first_shown_index + (num_shown_items - 1);
if (selected_index < self->first_shown_index)
self->first_shown_index = selected_index;
else if (selected_index > last_shown_index)
self->first_shown_index = selected_index - (num_shown_items - 1);
}
/* Draw a scrollbar on the menu. */
static void
draw_scrollbar (list_impl_t self,
int value, int extent, int min, int max,
int scrollbar_width, int scrollbar_height)
{
unsigned thumby, thumbheight;
grub_gfxmenu_box_t frame = self->scrollbar_frame;
grub_gfxmenu_box_t thumb = self->scrollbar_thumb;
int frame_vertical_pad = (frame->get_top_pad (frame)
+ frame->get_bottom_pad (frame));
int frame_horizontal_pad = (frame->get_left_pad (frame)
+ frame->get_right_pad (frame));
unsigned thumb_vertical_pad = (thumb->get_top_pad (thumb)
+ thumb->get_bottom_pad (thumb));
int thumb_horizontal_pad = (thumb->get_left_pad (thumb)
+ thumb->get_right_pad (thumb));
int tracktop = frame->get_top_pad (frame);
unsigned tracklen;
if (scrollbar_height <= frame_vertical_pad)
tracklen = 0;
else
tracklen = scrollbar_height - frame_vertical_pad;
frame->set_content_size (frame,
scrollbar_width - frame_horizontal_pad,
tracklen);
if (self->scrollbar_thumb_overlay)
{
tracklen += thumb_vertical_pad;
tracktop -= thumb->get_top_pad (thumb);
}
if (value <= min || max <= min)
thumby = 0;
else
thumby = ((unsigned) tracklen * (value - min))
/ ((unsigned) (max - min));
if (max <= min)
thumbheight = 1;
else
thumbheight = ((unsigned) (tracklen * extent)
/ ((unsigned) (max - min))) + 1;
/* Rare occasion: too many entries or too low height. */
if (thumbheight < thumb_vertical_pad)
{
thumbheight = thumb_vertical_pad;
if (value <= min || max <= extent
|| tracklen <= thumb_vertical_pad)
thumby = 0;
else
thumby = ((unsigned) ((tracklen - thumb_vertical_pad) * (value - min))
/ ((unsigned)(max - extent)));
}
thumby += tracktop;
int thumbx = frame->get_left_pad (frame);
int thumbwidth = scrollbar_width - frame_horizontal_pad;
if (!self->scrollbar_thumb_overlay)
thumbwidth -= thumb_horizontal_pad;
else
thumbx -= thumb->get_left_pad (thumb);
thumb->set_content_size (thumb, thumbwidth,
thumbheight - thumb_vertical_pad);
frame->draw (frame, 0, 0);
thumb->draw (thumb, thumbx, thumby);
}
/* Draw the list of items. */
static void
draw_menu (list_impl_t self, int num_shown_items)
{
if (! self->menu_box || ! self->selected_item_box || ! self->item_box)
return;
int boxpad = self->item_padding;
int icon_text_space = self->item_icon_space;
int item_vspace = self->item_spacing;
int ascent = grub_font_get_ascent (self->item_font);
int descent = grub_font_get_descent (self->item_font);
int selected_ascent = grub_font_get_ascent (self->selected_item_font);
int selected_descent = grub_font_get_descent (self->selected_item_font);
int text_box_height = self->item_height;
make_selected_item_visible (self);
grub_gfxmenu_box_t itembox = self->item_box;
grub_gfxmenu_box_t selbox = self->selected_item_box;
int item_leftpad = itembox->get_left_pad (itembox);
int item_rightpad = itembox->get_right_pad (itembox);
int item_border_width = item_leftpad + item_rightpad;
int item_toppad = itembox->get_top_pad (itembox);
int sel_leftpad = selbox->get_left_pad (selbox);
int sel_rightpad = selbox->get_right_pad (selbox);
int sel_border_width = sel_leftpad + sel_rightpad;
int sel_toppad = selbox->get_top_pad (selbox);
int max_leftpad = grub_max (item_leftpad, sel_leftpad);
int max_toppad = grub_max (item_toppad, sel_toppad);
int item_top = 0;
int menu_index;
int visible_index;
struct grub_video_rect oviewport;
grub_video_get_viewport (&oviewport.x, &oviewport.y,
&oviewport.width, &oviewport.height);
grub_video_set_viewport (oviewport.x + boxpad,
oviewport.y + boxpad,
oviewport.width - 2 * boxpad,
oviewport.height - 2 * boxpad);
int cwidth = oviewport.width - 2 * boxpad;
itembox->set_content_size (itembox, cwidth - item_border_width,
text_box_height);
selbox->set_content_size (selbox, cwidth - sel_border_width,
text_box_height);
int text_left_offset = self->icon_width + icon_text_space;
int item_text_top_offset = (text_box_height - (ascent + descent)) / 2 + ascent;
int sel_text_top_offset = (text_box_height - (selected_ascent
+ selected_descent)) / 2
+ selected_ascent;
grub_video_rect_t svpsave, sviewport;
sviewport.x = max_leftpad + text_left_offset;
int text_viewport_width = cwidth - sviewport.x;
sviewport.height = text_box_height;
grub_video_color_t item_color;
grub_video_color_t sel_color;
item_color = grub_video_map_rgba_color (self->item_color);
sel_color = grub_video_map_rgba_color (self->selected_item_color);
int item_box_top_offset = max_toppad - item_toppad;
int sel_box_top_offset = max_toppad - sel_toppad;
int item_viewport_width = text_viewport_width - item_rightpad;
int sel_viewport_width = text_viewport_width - sel_rightpad;
int tmp_icon_top_offset = (text_box_height - self->icon_height) / 2;
int item_icon_top_offset = item_toppad + tmp_icon_top_offset;
int sel_icon_top_offset = sel_toppad + tmp_icon_top_offset;
for (visible_index = 0, menu_index = self->first_shown_index;
visible_index < num_shown_items && menu_index < self->view->menu->size;
visible_index++, menu_index++)
{
int is_selected = (menu_index == self->view->selected);
struct grub_video_bitmap *icon;
grub_font_t font;
grub_video_color_t color;
int text_top_offset;
int top_pad;
int icon_top_offset;
int viewport_width;
if (is_selected)
{
selbox->draw (selbox, 0, item_top + sel_box_top_offset);
font = self->selected_item_font;
color = sel_color;
text_top_offset = sel_text_top_offset;
top_pad = sel_toppad;
icon_top_offset = sel_icon_top_offset;
viewport_width = sel_viewport_width;
}
else
{
itembox->draw (itembox, 0, item_top + item_box_top_offset);
font = self->item_font;
color = item_color;
text_top_offset = item_text_top_offset;
top_pad = item_toppad;
icon_top_offset = item_icon_top_offset;
viewport_width = item_viewport_width;
}
icon = get_item_icon (self, menu_index);
if (icon != 0)
grub_video_blit_bitmap (icon, GRUB_VIDEO_BLIT_BLEND,
max_leftpad,
item_top + icon_top_offset,
0, 0, self->icon_width, self->icon_height);
const char *item_title =
grub_menu_get_entry (self->view->menu, menu_index)->title;
int off = self->view->menu_title_offset[menu_index];
const char *scrolled_title =
grub_utf8_offset_code (item_title, grub_strlen (item_title), off);
if (scrolled_title)
item_title = scrolled_title;
sviewport.y = item_top + top_pad;
sviewport.width = viewport_width;
grub_gui_set_viewport (&sviewport, &svpsave);
grub_font_draw_string (item_title,
font,
color,
0,
text_top_offset);
grub_gui_restore_viewport (&svpsave);
item_top += text_box_height + item_vspace;
}
grub_video_set_viewport (oviewport.x,
oviewport.y,
oviewport.width,
oviewport.height);
}
static void
list_paint (void *vself, const grub_video_rect_t *region)
{
list_impl_t self = vself;
grub_video_rect_t vpsave;
if (! self->visible)
return;
if (!grub_video_have_common_points (region, &self->bounds))
return;
check_boxes (self);
if (! self->menu_box || ! self->selected_item_box || ! self->item_box)
return;
grub_gui_set_viewport (&self->bounds, &vpsave);
{
grub_gfxmenu_box_t box = self->menu_box;
int box_left_pad = box->get_left_pad (box);
int box_top_pad = box->get_top_pad (box);
int box_right_pad = box->get_right_pad (box);
int box_bottom_pad = box->get_bottom_pad (box);
grub_video_rect_t vpsave2, content_rect;
int num_shown_items = get_num_shown_items (self);
int drawing_scrollbar = (self->draw_scrollbar
&& (num_shown_items < self->view->menu->size)
&& check_scrollbar (self));
int scrollbar_width = self->scrollbar_width;
content_rect.x = box_left_pad;
content_rect.y = box_top_pad;
content_rect.width = self->bounds.width - box_left_pad - box_right_pad;
content_rect.height = self->bounds.height - box_top_pad - box_bottom_pad;
box->set_content_size (box, content_rect.width, content_rect.height);
box->draw (box, 0, 0);
switch (self->scrollbar_slice)
{
case SCROLLBAR_SLICE_WEST:
content_rect.x += self->scrollbar_right_pad;
content_rect.width -= self->scrollbar_right_pad;
break;
case SCROLLBAR_SLICE_CENTER:
if (drawing_scrollbar)
content_rect.width -= scrollbar_width + self->scrollbar_left_pad
+ self->scrollbar_right_pad;
break;
case SCROLLBAR_SLICE_EAST:
content_rect.width -= self->scrollbar_left_pad;
break;
}
grub_gui_set_viewport (&content_rect, &vpsave2);
draw_menu (self, num_shown_items);
grub_gui_restore_viewport (&vpsave2);
if (drawing_scrollbar)
{
content_rect.y += self->scrollbar_top_pad;
content_rect.height -= self->scrollbar_top_pad
+ self->scrollbar_bottom_pad;
content_rect.width = scrollbar_width;
switch (self->scrollbar_slice)
{
case SCROLLBAR_SLICE_WEST:
if (box_left_pad > scrollbar_width)
{
content_rect.x = box_left_pad - scrollbar_width;
content_rect.width = scrollbar_width;
}
else
{
content_rect.x = 0;
content_rect.width = box_left_pad;
}
break;
case SCROLLBAR_SLICE_CENTER:
content_rect.x = self->bounds.width - box_right_pad
- scrollbar_width - self->scrollbar_right_pad;
content_rect.width = scrollbar_width;
break;
case SCROLLBAR_SLICE_EAST:
content_rect.x = self->bounds.width - box_right_pad;
content_rect.width = box_right_pad;
break;
}
grub_gui_set_viewport (&content_rect, &vpsave2);
draw_scrollbar (self,
self->first_shown_index, num_shown_items,
0, self->view->menu->size,
scrollbar_width,
content_rect.height);
grub_gui_restore_viewport (&vpsave2);
}
}
grub_gui_restore_viewport (&vpsave);
}
static void
list_set_parent (void *vself, grub_gui_container_t parent)
{
list_impl_t self = vself;
self->parent = parent;
}
static grub_gui_container_t
list_get_parent (void *vself)
{
list_impl_t self = vself;
return self->parent;
}
static void
list_set_bounds (void *vself, const grub_video_rect_t *bounds)
{
list_impl_t self = vself;
self->bounds = *bounds;
}
static void
list_get_bounds (void *vself, grub_video_rect_t *bounds)
{
list_impl_t self = vself;
*bounds = self->bounds;
}
static void
list_get_minimal_size (void *vself, unsigned *width, unsigned *height)
{
list_impl_t self = vself;
if (check_boxes (self))
{
int boxpad = self->item_padding;
int item_vspace = self->item_spacing;
int item_height = self->item_height;
int num_items = 3;
grub_gfxmenu_box_t box = self->menu_box;
int box_left_pad = box->get_left_pad (box);
int box_top_pad = box->get_top_pad (box);
int box_right_pad = box->get_right_pad (box);
int box_bottom_pad = box->get_bottom_pad (box);
unsigned width_s;
grub_gfxmenu_box_t selbox = self->selected_item_box;
int sel_top_pad = selbox->get_top_pad (selbox);
int sel_bottom_pad = selbox->get_bottom_pad (selbox);
int sel_left_pad = selbox->get_left_pad (selbox);
int sel_right_pad = selbox->get_right_pad (selbox);
grub_gfxmenu_box_t itembox = self->item_box;
int item_top_pad = itembox->get_top_pad (itembox);
int item_bottom_pad = itembox->get_bottom_pad (itembox);
int item_left_pad = itembox->get_left_pad (itembox);
int item_right_pad = itembox->get_right_pad (itembox);
int max_left_pad = grub_max (item_left_pad, sel_left_pad);
int max_right_pad = grub_max (item_right_pad, sel_right_pad);
int max_top_pad = grub_max (item_top_pad, sel_top_pad);
int max_bottom_pad = grub_max (item_bottom_pad, sel_bottom_pad);
*width = grub_font_get_string_width (self->item_font, "Typical OS");
width_s = grub_font_get_string_width (self->selected_item_font,
"Typical OS");
if (*width < width_s)
*width = width_s;
*width += 2 * boxpad + box_left_pad + box_right_pad
+ max_left_pad + max_right_pad
+ self->item_icon_space + self->icon_width;
switch (self->scrollbar_slice)
{
case SCROLLBAR_SLICE_WEST:
*width += self->scrollbar_right_pad;
break;
case SCROLLBAR_SLICE_CENTER:
*width += self->scrollbar_width + self->scrollbar_left_pad
+ self->scrollbar_right_pad;
break;
case SCROLLBAR_SLICE_EAST:
*width += self->scrollbar_left_pad;
break;
}
/* Set the menu box height to fit the items. */
*height = (item_height * num_items
+ item_vspace * (num_items - 1)
+ 2 * boxpad
+ box_top_pad + box_bottom_pad
+ max_top_pad + max_bottom_pad);
}
else
{
*width = 0;
*height = 0;
}
}
static grub_err_t
list_set_property (void *vself, const char *name, const char *value)
{
list_impl_t self = vself;
if (grub_strcmp (name, "item_font") == 0)
{
self->item_font = grub_font_get (value);
if (self->selected_item_font_inherit)
self->selected_item_font = self->item_font;
}
else if (grub_strcmp (name, "selected_item_font") == 0)
{
if (! value || grub_strcmp (value, "inherit") == 0)
{
self->selected_item_font = self->item_font;
self->selected_item_font_inherit = 1;
}
else
{
self->selected_item_font = grub_font_get (value);
self->selected_item_font_inherit = 0;
}
}
else if (grub_strcmp (name, "item_color") == 0)
{
grub_video_rgba_color_t color;
if (grub_video_parse_color (value, &color) == GRUB_ERR_NONE)
{
self->item_color = color;
if (self->selected_item_color_inherit)
self->selected_item_color = self->item_color;
}
}
else if (grub_strcmp (name, "selected_item_color") == 0)
{
if (! value || grub_strcmp (value, "inherit") == 0)
{
self->selected_item_color = self->item_color;
self->selected_item_color_inherit = 1;
}
else
{
grub_video_rgba_color_t color;
if (grub_video_parse_color (value, &color)
== GRUB_ERR_NONE)
{
self->selected_item_color = color;
self->selected_item_color_inherit = 0;
}
}
}
else if (grub_strcmp (name, "icon_width") == 0)
{
self->icon_width = grub_strtol (value, 0, 10);
grub_gfxmenu_icon_manager_set_icon_size (self->icon_manager,
self->icon_width,
self->icon_height);
}
else if (grub_strcmp (name, "icon_height") == 0)
{
self->icon_height = grub_strtol (value, 0, 10);
grub_gfxmenu_icon_manager_set_icon_size (self->icon_manager,
self->icon_width,
self->icon_height);
}
else if (grub_strcmp (name, "item_height") == 0)
{
self->item_height = grub_strtol (value, 0, 10);
}
else if (grub_strcmp (name, "item_padding") == 0)
{
self->item_padding = grub_strtol (value, 0, 10);
}
else if (grub_strcmp (name, "item_icon_space") == 0)
{
self->item_icon_space = grub_strtol (value, 0, 10);
}
else if (grub_strcmp (name, "item_spacing") == 0)
{
self->item_spacing = grub_strtol (value, 0, 10);
}
else if (grub_strcmp (name, "visible") == 0)
{
self->visible = grub_strcmp (value, "false") != 0;
}
else if (grub_strcmp (name, "menu_pixmap_style") == 0)
{
self->need_to_recreate_boxes = 1;
grub_free (self->menu_box_pattern);
self->menu_box_pattern = value ? grub_strdup (value) : 0;
}
else if (grub_strcmp (name, "item_pixmap_style") == 0)
{
self->need_to_recreate_boxes = 1;
grub_free (self->item_box_pattern);
self->item_box_pattern = value ? grub_strdup (value) : 0;
if (self->selected_item_box_pattern_inherit)
{
grub_free (self->selected_item_box_pattern);
self->selected_item_box_pattern = value ? grub_strdup (value) : 0;
}
}
else if (grub_strcmp (name, "selected_item_pixmap_style") == 0)
{
if (!value || grub_strcmp (value, "inherit") == 0)
{
grub_free (self->selected_item_box_pattern);
char *tmp = self->item_box_pattern;
self->selected_item_box_pattern = tmp ? grub_strdup (tmp) : 0;
self->selected_item_box_pattern_inherit = 1;
}
else
{
self->need_to_recreate_boxes = 1;
grub_free (self->selected_item_box_pattern);
self->selected_item_box_pattern = value ? grub_strdup (value) : 0;
self->selected_item_box_pattern_inherit = 0;
}
}
else if (grub_strcmp (name, "scrollbar_frame") == 0)
{
self->need_to_recreate_scrollbar = 1;
grub_free (self->scrollbar_frame_pattern);
self->scrollbar_frame_pattern = value ? grub_strdup (value) : 0;
}
else if (grub_strcmp (name, "scrollbar_thumb") == 0)
{
self->need_to_recreate_scrollbar = 1;
grub_free (self->scrollbar_thumb_pattern);
self->scrollbar_thumb_pattern = value ? grub_strdup (value) : 0;
}
else if (grub_strcmp (name, "scrollbar_thumb_overlay") == 0)
{
self->scrollbar_thumb_overlay = grub_strcmp (value, "true") == 0;
}
else if (grub_strcmp (name, "scrollbar_width") == 0)
{
self->scrollbar_width = grub_strtol (value, 0, 10);
}
else if (grub_strcmp (name, "scrollbar_slice") == 0)
{
if (grub_strcmp (value, "west") == 0)
self->scrollbar_slice = SCROLLBAR_SLICE_WEST;
else if (grub_strcmp (value, "center") == 0)
self->scrollbar_slice = SCROLLBAR_SLICE_CENTER;
else if (grub_strcmp (value, "east") == 0)
self->scrollbar_slice = SCROLLBAR_SLICE_EAST;
}
else if (grub_strcmp (name, "scrollbar_left_pad") == 0)
{
self->scrollbar_left_pad = grub_strtol (value, 0, 10);
}
else if (grub_strcmp (name, "scrollbar_right_pad") == 0)
{
self->scrollbar_right_pad = grub_strtol (value, 0, 10);
}
else if (grub_strcmp (name, "scrollbar_top_pad") == 0)
{
self->scrollbar_top_pad = grub_strtol (value, 0, 10);
}
else if (grub_strcmp (name, "scrollbar_bottom_pad") == 0)
{
self->scrollbar_bottom_pad = grub_strtol (value, 0, 10);
}
else if (grub_strcmp (name, "scrollbar") == 0)
{
self->draw_scrollbar = grub_strcmp (value, "false") != 0;
}
else if (grub_strcmp (name, "theme_dir") == 0)
{
self->need_to_recreate_boxes = 1;
grub_free (self->theme_dir);
self->theme_dir = value ? grub_strdup (value) : 0;
}
else if (grub_strcmp (name, "id") == 0)
{
grub_free (self->id);
if (value)
self->id = grub_strdup (value);
else
self->id = 0;
}
return grub_errno;
}
/* Set necessary information that the gfxmenu view provides. */
static void
list_set_view_info (void *vself,
grub_gfxmenu_view_t view)
{
list_impl_t self = vself;
grub_gfxmenu_icon_manager_set_theme_path (self->icon_manager,
view->theme_path);
self->view = view;
}
/* Refresh list variables */
static void
list_refresh_info (void *vself,
grub_gfxmenu_view_t view)
{
list_impl_t self = vself;
if (view->nested)
self->first_shown_index = 0;
}
static struct grub_gui_component_ops list_comp_ops =
{
.destroy = list_destroy,
.get_id = list_get_id,
.is_instance = list_is_instance,
.paint = list_paint,
.set_parent = list_set_parent,
.get_parent = list_get_parent,
.set_bounds = list_set_bounds,
.get_bounds = list_get_bounds,
.get_minimal_size = list_get_minimal_size,
.set_property = list_set_property
};
static struct grub_gui_list_ops list_ops =
{
.set_view_info = list_set_view_info,
.refresh_list = list_refresh_info
};
grub_gui_component_t
grub_gui_list_new (void)
{
list_impl_t self;
grub_font_t default_font;
grub_video_rgba_color_t default_fg_color;
self = grub_zalloc (sizeof (*self));
if (! self)
return 0;
self->list.ops = &list_ops;
self->list.component.ops = &list_comp_ops;
self->visible = 1;
default_font = grub_font_get ("Unknown Regular 16");
default_fg_color = grub_video_rgba_color_rgb (0, 0, 0);
self->icon_width = 32;
self->icon_height = 32;
self->item_height = 42;
self->item_padding = 14;
self->item_icon_space = 4;
self->item_spacing = 16;
self->item_font = default_font;
self->selected_item_font_inherit = 1; /* Default to using the item_font. */
self->selected_item_font = default_font;
self->item_color = default_fg_color;
self->selected_item_color_inherit = 1; /* Default to using the item_color. */
self->selected_item_color = default_fg_color;
self->draw_scrollbar = 1;
self->need_to_recreate_scrollbar = 1;
self->scrollbar_frame = 0;
self->scrollbar_thumb = 0;
self->scrollbar_frame_pattern = 0;
self->scrollbar_thumb_pattern = 0;
self->scrollbar_thumb_overlay = 0;
self->scrollbar_width = 16;
self->scrollbar_slice = SCROLLBAR_SLICE_EAST;
self->scrollbar_left_pad = 2;
self->scrollbar_right_pad = 0;
self->scrollbar_top_pad = 0;
self->scrollbar_bottom_pad = 0;
self->first_shown_index = 0;
self->need_to_recreate_boxes = 0;
self->theme_dir = 0;
self->menu_box_pattern = 0;
self->item_box_pattern = 0;
self->selected_item_box_pattern_inherit = 1;/*Default to using the item_box.*/
self->selected_item_box_pattern = 0;
self->menu_box = grub_gfxmenu_create_box (0, 0);
self->item_box = grub_gfxmenu_create_box (0, 0);
self->selected_item_box = grub_gfxmenu_create_box (0, 0);
self->icon_manager = grub_gfxmenu_icon_manager_new ();
if (! self->icon_manager)
{
self->list.component.ops->destroy (self);
return 0;
}
grub_gfxmenu_icon_manager_set_icon_size (self->icon_manager,
self->icon_width,
self->icon_height);
return (grub_gui_component_t) self;
}
@@ -0,0 +1,893 @@
/* theme_loader.c - Theme file loader for gfxmenu. */
/*
* GRUB -- GRand Unified Bootloader
* Copyright (C) 2008 Free Software Foundation, Inc.
*
* GRUB is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* GRUB is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with GRUB. If not, see <http://www.gnu.org/licenses/>.
*/
#include <grub/types.h>
#include <grub/file.h>
#include <grub/misc.h>
#include <grub/mm.h>
#include <grub/err.h>
#include <grub/dl.h>
#include <grub/video.h>
#include <grub/gui_string_util.h>
#include <grub/bitmap.h>
#include <grub/bitmap_scale.h>
#include <grub/gfxwidgets.h>
#include <grub/gfxmenu_view.h>
#include <grub/gui.h>
#include <grub/color.h>
#include <grub/env.h>
static grub_err_t
parse_proportional_spec (const char *value, signed *abs, grub_fixed_signed_t *prop);
/* Construct a new box widget using ABSPATTERN to find the pixmap files for
it, storing the new box instance at *BOXPTR.
PATTERN should be of the form: "(hd0,0)/somewhere/style*.png".
The '*' then gets substituted with the various pixmap names that the
box uses. */
static grub_err_t
recreate_box_absolute (grub_gfxmenu_box_t *boxptr, const char *abspattern)
{
char *prefix;
char *suffix;
char *star;
grub_gfxmenu_box_t box;
star = grub_strchr (abspattern, '*');
if (! star)
return grub_error (GRUB_ERR_BAD_ARGUMENT,
"missing `*' in box pixmap pattern `%s'", abspattern);
/* Prefix: Get the part before the '*'. */
prefix = grub_malloc (star - abspattern + 1);
if (! prefix)
return grub_errno;
grub_memcpy (prefix, abspattern, star - abspattern);
prefix[star - abspattern] = '\0';
/* Suffix: Everything after the '*' is the suffix. */
suffix = star + 1;
box = grub_gfxmenu_create_box (prefix, suffix);
grub_free (prefix);
if (! box)
return grub_errno;
if (*boxptr)
(*boxptr)->destroy (*boxptr);
*boxptr = box;
return grub_errno;
}
/* Construct a new box widget using PATTERN to find the pixmap files for it,
storing the new widget at *BOXPTR. PATTERN should be of the form:
"somewhere/style*.png". The '*' then gets substituted with the various
pixmap names that the widget uses.
Important! The value of *BOXPTR must be initialized! It must either
(1) Be 0 (a NULL pointer), or
(2) Be a pointer to a valid 'grub_gfxmenu_box_t' instance.
In this case, the previous instance is destroyed. */
grub_err_t
grub_gui_recreate_box (grub_gfxmenu_box_t *boxptr,
const char *pattern, const char *theme_dir)
{
char *abspattern;
/* Check arguments. */
if (! pattern)
{
/* If no pixmap pattern is given, then just create an empty box. */
if (*boxptr)
(*boxptr)->destroy (*boxptr);
*boxptr = grub_gfxmenu_create_box (0, 0);
return grub_errno;
}
if (! theme_dir)
return grub_error (GRUB_ERR_BAD_ARGUMENT,
"styled box missing theme directory");
/* Resolve to an absolute path. */
abspattern = grub_resolve_relative_path (theme_dir, pattern);
if (! abspattern)
return grub_errno;
/* Create the box. */
recreate_box_absolute (boxptr, abspattern);
grub_free (abspattern);
return grub_errno;
}
static grub_err_t
theme_get_unsigned_int_from_proportional (const char *value,
unsigned absolute_value,
unsigned int *parsed_value)
{
grub_err_t err;
grub_fixed_signed_t frac;
signed pixels;
err = parse_proportional_spec (value, &pixels, &frac);
if (err != GRUB_ERR_NONE)
return err;
int result = grub_fixed_sfs_multiply (absolute_value, frac) + pixels;
if (result < 0)
result = 0;
*parsed_value = result;
return GRUB_ERR_NONE;
}
/* Set the specified property NAME on the view to the given string VALUE.
The caller is responsible for the lifetimes of NAME and VALUE. */
static grub_err_t
theme_set_string (grub_gfxmenu_view_t view,
const char *name,
const char *value,
const char *theme_dir,
const char *filename,
int line_num,
int col_num)
{
if (! grub_strcmp ("title-font", name))
view->title_font = grub_font_get (value);
else if (! grub_strcmp ("message-font", name))
view->message_font = grub_font_get (value);
else if (! grub_strcmp ("terminal-font", name))
{
grub_free (view->terminal_font_name);
view->terminal_font_name = grub_strdup (value);
if (! view->terminal_font_name)
return grub_errno;
}
else if (! grub_strcmp ("title-color", name))
grub_video_parse_color (value, &view->title_color);
else if (! grub_strcmp ("message-color", name))
grub_video_parse_color (value, &view->message_color);
else if (! grub_strcmp ("message-bg-color", name))
grub_video_parse_color (value, &view->message_bg_color);
else if (! grub_strcmp("menu-tip-left", name))
grub_env_set("VTOY_TIP_LEFT", value);
else if (! grub_strcmp("menu-tip-top", name))
grub_env_set("VTOY_TIP_TOP", value);
else if (! grub_strcmp("menu-tip-color", name))
grub_env_set("VTOY_TIP_COLOR", value);
else if (! grub_strcmp ("desktop-image", name))
{
struct grub_video_bitmap *raw_bitmap;
char *path;
path = grub_resolve_relative_path (theme_dir, value);
if (! path)
return grub_errno;
if (grub_video_bitmap_load (&raw_bitmap, path) != GRUB_ERR_NONE)
{
grub_free (path);
return grub_errno;
}
grub_free(path);
grub_video_bitmap_destroy (view->raw_desktop_image);
view->raw_desktop_image = raw_bitmap;
}
else if (! grub_strcmp ("desktop-image-scale-method", name))
{
if (! value || ! grub_strcmp ("stretch", value))
view->desktop_image_scale_method =
GRUB_VIDEO_BITMAP_SELECTION_METHOD_STRETCH;
else if (! grub_strcmp ("crop", value))
view->desktop_image_scale_method =
GRUB_VIDEO_BITMAP_SELECTION_METHOD_CROP;
else if (! grub_strcmp ("padding", value))
view->desktop_image_scale_method =
GRUB_VIDEO_BITMAP_SELECTION_METHOD_PADDING;
else if (! grub_strcmp ("fitwidth", value))
view->desktop_image_scale_method =
GRUB_VIDEO_BITMAP_SELECTION_METHOD_FITWIDTH;
else if (! grub_strcmp ("fitheight", value))
view->desktop_image_scale_method =
GRUB_VIDEO_BITMAP_SELECTION_METHOD_FITHEIGHT;
else
return grub_error (GRUB_ERR_BAD_ARGUMENT,
"Unsupported scale method: %s",
value);
}
else if (! grub_strcmp ("desktop-image-h-align", name))
{
if (! grub_strcmp ("left", value))
view->desktop_image_h_align = GRUB_VIDEO_BITMAP_H_ALIGN_LEFT;
else if (! grub_strcmp ("center", value))
view->desktop_image_h_align = GRUB_VIDEO_BITMAP_H_ALIGN_CENTER;
else if (! grub_strcmp ("right", value))
view->desktop_image_h_align = GRUB_VIDEO_BITMAP_H_ALIGN_RIGHT;
else
return grub_error (GRUB_ERR_BAD_ARGUMENT,
"Unsupported horizontal align method: %s",
value);
}
else if (! grub_strcmp ("desktop-image-v-align", name))
{
if (! grub_strcmp ("top", value))
view->desktop_image_v_align = GRUB_VIDEO_BITMAP_V_ALIGN_TOP;
else if (! grub_strcmp ("center", value))
view->desktop_image_v_align = GRUB_VIDEO_BITMAP_V_ALIGN_CENTER;
else if (! grub_strcmp ("bottom", value))
view->desktop_image_v_align = GRUB_VIDEO_BITMAP_V_ALIGN_BOTTOM;
else
return grub_error (GRUB_ERR_BAD_ARGUMENT,
"Unsupported vertical align method: %s",
value);
}
else if (! grub_strcmp ("desktop-color", name))
grub_video_parse_color (value, &view->desktop_color);
else if (! grub_strcmp ("terminal-box", name))
{
grub_err_t err;
err = grub_gui_recreate_box (&view->terminal_box, value, theme_dir);
if (err != GRUB_ERR_NONE)
return err;
}
else if (! grub_strcmp ("terminal-border", name))
{
view->terminal_border = grub_strtoul (value, 0, 10);
if (grub_errno)
return grub_errno;
}
else if (! grub_strcmp ("terminal-left", name))
{
unsigned int tmp;
int err = theme_get_unsigned_int_from_proportional (value,
view->screen.width,
&tmp);
if (err != GRUB_ERR_NONE)
return err;
view->terminal_rect.x = tmp;
}
else if (! grub_strcmp ("terminal-top", name))
{
unsigned int tmp;
int err = theme_get_unsigned_int_from_proportional (value,
view->screen.height,
&tmp);
if (err != GRUB_ERR_NONE)
return err;
view->terminal_rect.y = tmp;
}
else if (! grub_strcmp ("terminal-width", name))
{
unsigned int tmp;
int err = theme_get_unsigned_int_from_proportional (value,
view->screen.width,
&tmp);
if (err != GRUB_ERR_NONE)
return err;
view->terminal_rect.width = tmp;
}
else if (! grub_strcmp ("terminal-height", name))
{
unsigned int tmp;
int err = theme_get_unsigned_int_from_proportional (value,
view->screen.height,
&tmp);
if (err != GRUB_ERR_NONE)
return err;
view->terminal_rect.height = tmp;
}
else if (! grub_strcmp ("title-text", name))
{
grub_free (view->title_text);
view->title_text = grub_strdup (value);
if (! view->title_text)
return grub_errno;
}
else if (! grub_strcmp ("ventoy_left_top_color", name))
return grub_errno;
else
{
return grub_error (GRUB_ERR_BAD_ARGUMENT,
"%s:%d:%d unknown property `%s'",
filename, line_num, col_num, name);
}
return grub_errno;
}
struct parsebuf
{
char *buf;
int pos;
int len;
int line_num;
int col_num;
const char *filename;
char *theme_dir;
grub_gfxmenu_view_t view;
};
static int
has_more (struct parsebuf *p)
{
return p->pos < p->len;
}
static int
read_char (struct parsebuf *p)
{
if (has_more (p))
{
char c;
c = p->buf[p->pos++];
if (c == '\n')
{
p->line_num++;
p->col_num = 1;
}
else
{
p->col_num++;
}
return c;
}
else
return -1;
}
static int
peek_char (struct parsebuf *p)
{
if (has_more (p))
return p->buf[p->pos];
else
return -1;
}
static int
is_whitespace (char c)
{
return (c == ' '
|| c == '\t'
|| c == '\r'
|| c == '\n'
|| c == '\f');
}
static void
skip_whitespace (struct parsebuf *p)
{
while (has_more (p) && is_whitespace(peek_char (p)))
read_char (p);
}
static void
advance_to_next_line (struct parsebuf *p)
{
int c;
/* Eat characters up to the newline. */
do
{
c = read_char (p);
}
while (c != -1 && c != '\n');
}
static int
is_identifier_char (int c)
{
return (c != -1
&& (grub_isalpha(c)
|| grub_isdigit(c)
|| c == '_'
|| c == '-'));
}
static char *
read_identifier (struct parsebuf *p)
{
/* Index of the first character of the identifier in p->buf. */
int start;
/* Next index after the last character of the identifer in p->buf. */
int end;
skip_whitespace (p);
/* Capture the start of the identifier. */
start = p->pos;
/* Scan for the end. */
while (is_identifier_char (peek_char (p)))
read_char (p);
end = p->pos;
if (end - start < 1)
return 0;
return grub_new_substring (p->buf, start, end);
}
static char *
read_expression (struct parsebuf *p)
{
int start;
int end;
skip_whitespace (p);
if (peek_char (p) == '"')
{
/* Read as a quoted string.
The quotation marks are not included in the expression value. */
/* Skip opening quotation mark. */
read_char (p);
start = p->pos;
while (has_more (p) && peek_char (p) != '"')
read_char (p);
end = p->pos;
/* Skip the terminating quotation mark. */
read_char (p);
}
else if (peek_char (p) == '(')
{
/* Read as a parenthesized string -- for tuples/coordinates. */
/* The parentheses are included in the expression value. */
int c;
start = p->pos;
do
{
c = read_char (p);
}
while (c != -1 && c != ')');
end = p->pos;
}
else if (has_more (p))
{
/* Read as a single word -- for numeric values or words without
whitespace. */
start = p->pos;
while (has_more (p) && ! is_whitespace (peek_char (p))
&& peek_char (p) != '}')
read_char (p);
end = p->pos;
}
else
{
/* The end of the theme file has been reached. */
grub_error (GRUB_ERR_IO, "%s:%d:%d expression expected in theme file",
p->filename, p->line_num, p->col_num);
return 0;
}
return grub_new_substring (p->buf, start, end);
}
static grub_err_t
parse_proportional_spec (const char *value, signed *abs, grub_fixed_signed_t *prop)
{
signed num;
const char *ptr;
int sig = 0;
*abs = 0;
*prop = 0;
ptr = value;
while (*ptr)
{
sig = 0;
while (*ptr == '-' || *ptr == '+')
{
if (*ptr == '-')
sig = !sig;
ptr++;
}
num = grub_strtoul (ptr, (char **) &ptr, 0);
if (grub_errno)
return grub_errno;
if (sig)
num = -num;
if (*ptr == '%')
{
*prop += grub_fixed_fsf_divide (grub_signed_to_fixed (num), 100);
ptr++;
}
else
*abs += num;
}
return GRUB_ERR_NONE;
}
/* Read a GUI object specification from the theme file.
Any components created will be added to the GUI container PARENT. */
static grub_err_t
read_object (struct parsebuf *p, grub_gui_container_t parent)
{
grub_video_rect_t bounds;
char *name;
name = read_identifier (p);
if (! name)
goto cleanup;
grub_gui_component_t component = 0;
if (grub_strcmp (name, "label") == 0)
{
component = grub_gui_label_new ();
}
else if (grub_strcmp (name, "image") == 0)
{
component = grub_gui_image_new ();
}
else if (grub_strcmp (name, "vbox") == 0)
{
component = (grub_gui_component_t) grub_gui_vbox_new ();
}
else if (grub_strcmp (name, "hbox") == 0)
{
component = (grub_gui_component_t) grub_gui_hbox_new ();
}
else if (grub_strcmp (name, "canvas") == 0)
{
component = (grub_gui_component_t) grub_gui_canvas_new ();
}
else if (grub_strcmp (name, "progress_bar") == 0)
{
component = grub_gui_progress_bar_new ();
}
else if (grub_strcmp (name, "circular_progress") == 0)
{
component = grub_gui_circular_progress_new ();
}
else if (grub_strcmp (name, "boot_menu") == 0)
{
component = grub_gui_list_new ();
}
else
{
/* Unknown type. */
grub_error (GRUB_ERR_IO, "%s:%d:%d unknown object type `%s'",
p->filename, p->line_num, p->col_num, name);
goto cleanup;
}
if (! component)
goto cleanup;
/* Inform the component about the theme so it can find its resources. */
component->ops->set_property (component, "theme_dir", p->theme_dir);
component->ops->set_property (component, "theme_path", p->filename);
/* Add the component as a child of PARENT. */
bounds.x = 0;
bounds.y = 0;
bounds.width = -1;
bounds.height = -1;
component->ops->set_bounds (component, &bounds);
parent->ops->add (parent, component);
skip_whitespace (p);
if (read_char (p) != '{')
{
grub_error (GRUB_ERR_IO,
"%s:%d:%d expected `{' after object type name `%s'",
p->filename, p->line_num, p->col_num, name);
goto cleanup;
}
while (has_more (p))
{
skip_whitespace (p);
/* Check whether the end has been encountered. */
if (peek_char (p) == '}')
{
/* Skip the closing brace. */
read_char (p);
break;
}
if (peek_char (p) == '#')
{
/* Skip comments. */
advance_to_next_line (p);
continue;
}
if (peek_char (p) == '+')
{
/* Skip the '+'. */
read_char (p);
/* Check whether this component is a container. */
if (component->ops->is_instance (component, "container"))
{
/* Read the sub-object recursively and add it as a child. */
if (read_object (p, (grub_gui_container_t) component) != 0)
goto cleanup;
/* After reading the sub-object, resume parsing, expecting
another property assignment or sub-object definition. */
continue;
}
else
{
grub_error (GRUB_ERR_IO,
"%s:%d:%d attempted to add object to non-container",
p->filename, p->line_num, p->col_num);
goto cleanup;
}
}
char *property;
property = read_identifier (p);
if (! property)
{
grub_error (GRUB_ERR_IO, "%s:%d:%d identifier expected in theme file",
p->filename, p->line_num, p->col_num);
goto cleanup;
}
skip_whitespace (p);
if (read_char (p) != '=')
{
grub_error (GRUB_ERR_IO,
"%s:%d:%d expected `=' after property name `%s'",
p->filename, p->line_num, p->col_num, property);
grub_free (property);
goto cleanup;
}
skip_whitespace (p);
char *value;
value = read_expression (p);
if (! value)
{
grub_free (property);
goto cleanup;
}
/* Handle the property value. */
if (grub_strcmp (property, "left") == 0)
parse_proportional_spec (value, &component->x, &component->xfrac);
else if (grub_strcmp (property, "top") == 0)
parse_proportional_spec (value, &component->y, &component->yfrac);
else if (grub_strcmp (property, "width") == 0)
parse_proportional_spec (value, &component->w, &component->wfrac);
else if (grub_strcmp (property, "height") == 0)
parse_proportional_spec (value, &component->h, &component->hfrac);
else
/* General property handling. */
component->ops->set_property (component, property, value);
grub_free (value);
grub_free (property);
if (grub_errno != GRUB_ERR_NONE)
goto cleanup;
}
cleanup:
grub_free (name);
return grub_errno;
}
static grub_err_t
read_property (struct parsebuf *p)
{
char *name;
/* Read the property name. */
name = read_identifier (p);
if (! name)
{
advance_to_next_line (p);
return grub_errno;
}
/* Skip whitespace before separator. */
skip_whitespace (p);
/* Read separator. */
if (read_char (p) != ':')
{
grub_error (GRUB_ERR_IO,
"%s:%d:%d missing separator after property name `%s'",
p->filename, p->line_num, p->col_num, name);
goto done;
}
/* Skip whitespace after separator. */
skip_whitespace (p);
/* Get the value based on its type. */
if (peek_char (p) == '"')
{
/* String value (e.g., '"My string"'). */
char *value = read_expression (p);
if (! value)
{
grub_error (GRUB_ERR_IO, "%s:%d:%d missing property value",
p->filename, p->line_num, p->col_num);
goto done;
}
/* If theme_set_string results in an error, grub_errno will be returned
below. */
theme_set_string (p->view, name, value, p->theme_dir,
p->filename, p->line_num, p->col_num);
grub_free (value);
}
else
{
grub_error (GRUB_ERR_IO,
"%s:%d:%d property value invalid; "
"enclose literal values in quotes (\")",
p->filename, p->line_num, p->col_num);
grub_printf("File: %s\nLine:%d Column:%d\n"
"property value invalid; enclose literal values in quotes (\")\n\n",
p->filename, p->line_num, p->col_num);
goto done;
}
done:
grub_free (name);
return grub_errno;
}
extern int g_menu_update_mode;
/* Set properties on the view based on settings from the specified
theme file. */
grub_err_t
grub_gfxmenu_view_load_theme (grub_gfxmenu_view_t view, const char *theme_path)
{
int flag = 0;
grub_file_t file;
struct parsebuf p;
p.view = view;
p.theme_dir = grub_get_dirname (theme_path);
file = grub_file_open (theme_path, GRUB_FILE_TYPE_THEME);
if (! file)
{
grub_free (p.theme_dir);
return grub_errno;
}
p.len = grub_file_size (file);
p.buf = grub_malloc (p.len + 8192);
p.pos = 0;
p.line_num = 1;
p.col_num = 1;
p.filename = theme_path;
if (! p.buf)
{
grub_file_close (file);
grub_free (p.theme_dir);
return grub_errno;
}
if (grub_file_read (file, p.buf, p.len) != p.len)
{
grub_free (p.buf);
grub_file_close (file);
grub_free (p.theme_dir);
return grub_errno;
}
{
const char *checkret = grub_env_get("VTOY_CHKDEV_RESULT_STRING");
if (checkret == NULL || checkret[0] != '0')
{
p.len += grub_snprintf(p.buf + p.len, 4096, "\n+ hbox{\n left = 1%%\n top = 90%%\n"
" + label {text = \"[Unofficial Ventoy]\" color = \"red\" align = \"left\"}\n"
"}\n");
}
}
if (view->canvas)
view->canvas->component.ops->destroy (view->canvas);
view->canvas = grub_gui_canvas_new ();
if (!view->canvas)
goto fail;
((grub_gui_component_t) view->canvas)
->ops->set_bounds ((grub_gui_component_t) view->canvas,
&view->screen);
parse:
while (has_more (&p))
{
/* Skip comments (lines beginning with #). */
if (peek_char (&p) == '#')
{
advance_to_next_line (&p);
continue;
}
/* Find the first non-whitespace character. */
skip_whitespace (&p);
/* Handle the content. */
if (peek_char (&p) == '+')
{
/* Skip the '+'. */
read_char (&p);
read_object (&p, view->canvas);
}
else
{
read_property (&p);
}
if (grub_errno != GRUB_ERR_NONE)
goto fail;
}
if (flag == 0)
{
const char *tip = grub_env_get("VTOY_MENU_TIP_ENABLE");
if (tip && tip[0] == '1')
{
char tmpmsg[512];
grub_memset(tmpmsg, 'w', 500);
tmpmsg[500] = 0;
g_menu_update_mode = 1;
p.len += grub_snprintf(p.buf + p.len, 4096,
"\n+ vbox{\n left = %s\n top = %s\n"
"+ label { id=\"VTOY_MENU_TIP_1\" text = \"%s\" color = \"%s\" align = \"%s\"}\n"
"+ label { id=\"VTOY_MENU_TIP_2\" text = \"%s\" color = \"%s\" align = \"%s\"}\n"
"}\n",
grub_env_get("VTOY_TIP_LEFT"),
grub_env_get("VTOY_TIP_TOP"),
tmpmsg,
grub_env_get("VTOY_TIP_COLOR"),
grub_env_get("VTOY_TIP_ALIGN"),
tmpmsg,
grub_env_get("VTOY_TIP_COLOR"),
grub_env_get("VTOY_TIP_ALIGN")
);
flag = 1;
goto parse;
}
}
/* Set the new theme path. */
grub_free (view->theme_path);
view->theme_path = grub_strdup (theme_path);
goto cleanup;
fail:
if (view->canvas)
{
view->canvas->component.ops->destroy (view->canvas);
view->canvas = 0;
}
cleanup:
grub_free (p.buf);
grub_file_close (file);
grub_free (p.theme_dir);
return grub_errno;
}
@@ -0,0 +1,694 @@
/* view.c - Graphical menu interface MVC view. */
/*
* GRUB -- GRand Unified Bootloader
* Copyright (C) 2008 Free Software Foundation, Inc.
*
* GRUB is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* GRUB is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with GRUB. If not, see <http://www.gnu.org/licenses/>.
*/
#include <grub/types.h>
#include <grub/file.h>
#include <grub/misc.h>
#include <grub/mm.h>
#include <grub/err.h>
#include <grub/dl.h>
#include <grub/normal.h>
#include <grub/video.h>
#include <grub/gfxterm.h>
#include <grub/bitmap.h>
#include <grub/bitmap_scale.h>
#include <grub/term.h>
#include <grub/gfxwidgets.h>
#include <grub/time.h>
#include <grub/menu.h>
#include <grub/menu_viewer.h>
#include <grub/gfxmenu_view.h>
#include <grub/gui_string_util.h>
#include <grub/icon_manager.h>
#include <grub/i18n.h>
#include <grub/charset.h>
static void
init_terminal (grub_gfxmenu_view_t view);
static void
init_background (grub_gfxmenu_view_t view);
static grub_gfxmenu_view_t term_view;
/* Create a new view object, loading the theme specified by THEME_PATH and
associating MODEL with the view. */
grub_gfxmenu_view_t
grub_gfxmenu_view_new (const char *theme_path,
int width, int height)
{
grub_gfxmenu_view_t view;
grub_font_t default_font;
grub_video_rgba_color_t default_fg_color;
grub_video_rgba_color_t default_bg_color;
view = grub_malloc (sizeof (*view));
if (! view)
return 0;
while (grub_gfxmenu_timeout_notifications)
{
struct grub_gfxmenu_timeout_notify *p;
p = grub_gfxmenu_timeout_notifications;
grub_gfxmenu_timeout_notifications = grub_gfxmenu_timeout_notifications->next;
grub_free (p);
}
view->screen.x = 0;
view->screen.y = 0;
view->screen.width = width;
view->screen.height = height;
view->need_to_check_sanity = 1;
view->terminal_border = 3;
view->terminal_rect.width = view->screen.width * 7 / 10;
view->terminal_rect.height = view->screen.height * 7 / 10;
view->terminal_rect.x = view->screen.x + (view->screen.width
- view->terminal_rect.width) / 2;
view->terminal_rect.y = view->screen.y + (view->screen.height
- view->terminal_rect.height) / 2;
default_font = grub_font_get ("Unknown Regular 16");
default_fg_color = grub_video_rgba_color_rgb (0, 0, 0);
default_bg_color = grub_video_rgba_color_rgb (255, 255, 255);
view->canvas = 0;
view->title_font = default_font;
view->message_font = default_font;
view->terminal_font_name = grub_strdup ("Fixed 10");
view->title_color = default_fg_color;
view->message_color = default_bg_color;
view->message_bg_color = default_fg_color;
view->raw_desktop_image = 0;
view->scaled_desktop_image = 0;
view->desktop_image_scale_method = GRUB_VIDEO_BITMAP_SELECTION_METHOD_STRETCH;
view->desktop_image_h_align = GRUB_VIDEO_BITMAP_H_ALIGN_CENTER;
view->desktop_image_v_align = GRUB_VIDEO_BITMAP_V_ALIGN_CENTER;
view->desktop_color = default_bg_color;
view->terminal_box = grub_gfxmenu_create_box (0, 0);
view->title_text = grub_strdup (_("GRUB Boot Menu"));
view->progress_message_text = 0;
view->theme_path = 0;
/* Set the timeout bar's frame. */
view->progress_message_frame.width = view->screen.width * 4 / 5;
view->progress_message_frame.height = 50;
view->progress_message_frame.x = view->screen.x
+ (view->screen.width - view->progress_message_frame.width) / 2;
view->progress_message_frame.y = view->screen.y
+ view->screen.height - 90 - 20 - view->progress_message_frame.height;
if (grub_gfxmenu_view_load_theme (view, theme_path) != 0)
{
grub_gfxmenu_view_destroy (view);
return 0;
}
return view;
}
/* Destroy the view object. All used memory is freed. */
void
grub_gfxmenu_view_destroy (grub_gfxmenu_view_t view)
{
if (!view)
return;
while (grub_gfxmenu_timeout_notifications)
{
struct grub_gfxmenu_timeout_notify *p;
p = grub_gfxmenu_timeout_notifications;
grub_gfxmenu_timeout_notifications = grub_gfxmenu_timeout_notifications->next;
grub_free (p);
}
grub_video_bitmap_destroy (view->raw_desktop_image);
grub_video_bitmap_destroy (view->scaled_desktop_image);
if (view->terminal_box)
view->terminal_box->destroy (view->terminal_box);
grub_free (view->terminal_font_name);
grub_free (view->title_text);
grub_free (view->progress_message_text);
grub_free (view->theme_path);
if (view->menu_title_offset)
grub_free (view->menu_title_offset);
if (view->canvas)
view->canvas->component.ops->destroy (view->canvas);
grub_free (view);
}
static void
redraw_background (grub_gfxmenu_view_t view,
const grub_video_rect_t *bounds)
{
if (view->scaled_desktop_image)
{
struct grub_video_bitmap *img = view->scaled_desktop_image;
grub_video_blit_bitmap (img, GRUB_VIDEO_BLIT_REPLACE,
bounds->x, bounds->y,
bounds->x - view->screen.x,
bounds->y - view->screen.y,
bounds->width, bounds->height);
}
else
{
grub_video_fill_rect (grub_video_map_rgba_color (view->desktop_color),
bounds->x, bounds->y,
bounds->width, bounds->height);
}
}
static void
draw_title (grub_gfxmenu_view_t view)
{
if (! view->title_text)
return;
/* Center the title. */
int title_width = grub_font_get_string_width (view->title_font,
view->title_text);
int x = (view->screen.width - title_width) / 2;
int y = 40 + grub_font_get_ascent (view->title_font);
grub_font_draw_string (view->title_text,
view->title_font,
grub_video_map_rgba_color (view->title_color),
x, y);
}
struct progress_value_data
{
int visible;
int start;
int end;
int value;
};
struct grub_gfxmenu_timeout_notify *grub_gfxmenu_timeout_notifications;
static void
update_timeouts (int visible, int start, int value, int end)
{
struct grub_gfxmenu_timeout_notify *cur;
for (cur = grub_gfxmenu_timeout_notifications; cur; cur = cur->next)
cur->set_state (cur->self, visible, start, value, end);
}
static void
redraw_timeouts (struct grub_gfxmenu_view *view)
{
struct grub_gfxmenu_timeout_notify *cur;
for (cur = grub_gfxmenu_timeout_notifications; cur; cur = cur->next)
{
grub_video_rect_t bounds;
cur->self->ops->get_bounds (cur->self, &bounds);
grub_video_set_area_status (GRUB_VIDEO_AREA_ENABLED);
grub_gfxmenu_view_redraw (view, &bounds);
}
}
void
grub_gfxmenu_print_timeout (int timeout, void *data)
{
struct grub_gfxmenu_view *view = data;
if (view->first_timeout == -1)
view->first_timeout = timeout;
update_timeouts (1, -view->first_timeout, -timeout, 0);
redraw_timeouts (view);
grub_video_swap_buffers ();
if (view->double_repaint)
redraw_timeouts (view);
}
void
grub_gfxmenu_clear_timeout (void *data)
{
struct grub_gfxmenu_view *view = data;
update_timeouts (0, 1, 0, 0);
redraw_timeouts (view);
grub_video_swap_buffers ();
if (view->double_repaint)
redraw_timeouts (view);
}
static void
update_menu_visit (grub_gui_component_t component,
void *userdata)
{
grub_gfxmenu_view_t view;
view = userdata;
if (component->ops->is_instance (component, "list"))
{
grub_gui_list_t list = (grub_gui_list_t) component;
list->ops->set_view_info (list, view);
}
}
/* Update any boot menu components with the current menu model and
theme path. */
static void
update_menu_components (grub_gfxmenu_view_t view)
{
grub_gui_iterate_recursively ((grub_gui_component_t) view->canvas,
update_menu_visit, view);
}
static void
refresh_menu_visit (grub_gui_component_t component,
void *userdata)
{
grub_gfxmenu_view_t view;
view = userdata;
if (component->ops->is_instance (component, "list"))
{
grub_gui_list_t list = (grub_gui_list_t) component;
list->ops->refresh_list (list, view);
}
}
/* Refresh list information (useful for submenus) */
static void
refresh_menu_components (grub_gfxmenu_view_t view)
{
grub_gui_iterate_recursively ((grub_gui_component_t) view->canvas,
refresh_menu_visit, view);
}
static void
draw_message (grub_gfxmenu_view_t view)
{
char *text = view->progress_message_text;
grub_video_rect_t f = view->progress_message_frame;
if (! text)
return;
grub_font_t font = view->message_font;
grub_video_color_t color = grub_video_map_rgba_color (view->message_color);
/* Border. */
grub_video_fill_rect (color,
f.x-1, f.y-1, f.width+2, f.height+2);
/* Fill. */
grub_video_fill_rect (grub_video_map_rgba_color (view->message_bg_color),
f.x, f.y, f.width, f.height);
/* Center the text. */
int text_width = grub_font_get_string_width (font, text);
int x = f.x + (f.width - text_width) / 2;
int y = (f.y + (f.height - grub_font_get_descent (font)) / 2
+ grub_font_get_ascent (font) / 2);
grub_font_draw_string (text, font, color, x, y);
}
void
grub_gfxmenu_view_redraw (grub_gfxmenu_view_t view,
const grub_video_rect_t *region)
{
if (grub_video_have_common_points (&view->terminal_rect, region))
grub_gfxterm_schedule_repaint ();
grub_video_set_active_render_target (GRUB_VIDEO_RENDER_TARGET_DISPLAY);
grub_video_area_status_t area_status;
grub_video_get_area_status (&area_status);
if (area_status == GRUB_VIDEO_AREA_ENABLED)
grub_video_set_region (region->x, region->y,
region->width, region->height);
redraw_background (view, region);
if (view->canvas)
view->canvas->component.ops->paint (view->canvas, region);
draw_title (view);
if (grub_video_have_common_points (&view->progress_message_frame, region))
draw_message (view);
if (area_status == GRUB_VIDEO_AREA_ENABLED)
grub_video_set_area_status (GRUB_VIDEO_AREA_ENABLED);
}
void
grub_gfxmenu_view_draw (grub_gfxmenu_view_t view)
{
init_terminal (view);
init_background (view);
/* Clear the screen; there may be garbage left over in video memory. */
grub_video_fill_rect (grub_video_map_rgb (0, 0, 0),
view->screen.x, view->screen.y,
view->screen.width, view->screen.height);
grub_video_swap_buffers ();
if (view->double_repaint)
grub_video_fill_rect (grub_video_map_rgb (0, 0, 0),
view->screen.x, view->screen.y,
view->screen.width, view->screen.height);
refresh_menu_components (view);
update_menu_components (view);
grub_video_set_area_status (GRUB_VIDEO_AREA_DISABLED);
grub_gfxmenu_view_redraw (view, &view->screen);
grub_video_swap_buffers ();
if (view->double_repaint)
{
grub_video_set_area_status (GRUB_VIDEO_AREA_DISABLED);
grub_gfxmenu_view_redraw (view, &view->screen);
}
}
static void
redraw_menu_visit (grub_gui_component_t component,
void *userdata)
{
grub_gfxmenu_view_t view;
view = userdata;
if (component->ops->is_instance (component, "list"))
{
grub_video_rect_t bounds;
component->ops->get_bounds (component, &bounds);
grub_video_set_area_status (GRUB_VIDEO_AREA_ENABLED);
grub_gfxmenu_view_redraw (view, &bounds);
}
}
extern int g_menu_update_mode;
static void grub_gfxmenu_update_all(grub_gfxmenu_view_t view)
{
grub_video_set_area_status(GRUB_VIDEO_AREA_DISABLED);
grub_gfxmenu_view_redraw(view, &view->screen);
}
void
grub_gfxmenu_redraw_menu (grub_gfxmenu_view_t view)
{
update_menu_components (view);
if (g_menu_update_mode)
grub_gfxmenu_update_all(view);
else
grub_gui_iterate_recursively ((grub_gui_component_t) view->canvas,
redraw_menu_visit, view);
grub_video_swap_buffers ();
if (view->double_repaint)
{
if (g_menu_update_mode)
grub_gfxmenu_update_all(view);
else
grub_gui_iterate_recursively ((grub_gui_component_t) view->canvas,
redraw_menu_visit, view);
}
}
void
grub_gfxmenu_set_chosen_entry (int entry, void *data)
{
grub_gfxmenu_view_t view = data;
view->selected = entry;
grub_gfxmenu_redraw_menu (view);
}
void
grub_gfxmenu_scroll_chosen_entry (void *data, int diren)
{
grub_gfxmenu_view_t view = data;
const char *item_title;
int off;
int max;
if (!view->menu->size)
return;
item_title = grub_menu_get_entry (view->menu, view->selected)->title;
off = view->menu_title_offset[view->selected] + diren;
max = grub_utf8_get_num_code (item_title, grub_strlen(item_title));
if (diren == 1000000)
off = (max >= 20) ? (max - 20) : 0;
else if (off < 0)
off = 0;
else if (off > max)
off = max;
view->menu_title_offset[view->selected] = off;
grub_gfxmenu_redraw_menu (view);
}
static void
grub_gfxmenu_draw_terminal_box (void)
{
grub_gfxmenu_box_t term_box;
term_box = term_view->terminal_box;
if (!term_box)
return;
grub_video_set_area_status (GRUB_VIDEO_AREA_DISABLED);
term_box->set_content_size (term_box, term_view->terminal_rect.width,
term_view->terminal_rect.height);
term_box->draw (term_box,
term_view->terminal_rect.x - term_box->get_left_pad (term_box),
term_view->terminal_rect.y - term_box->get_top_pad (term_box));
}
static void
get_min_terminal (grub_font_t terminal_font,
unsigned int border_width,
unsigned int *min_terminal_width,
unsigned int *min_terminal_height)
{
struct grub_font_glyph *glyph;
glyph = grub_font_get_glyph (terminal_font, 'M');
*min_terminal_width = (glyph? glyph->device_width : 8) * 80
+ 2 * border_width;
*min_terminal_height = grub_font_get_max_char_height (terminal_font) * 24
+ 2 * border_width;
}
static void
terminal_sanity_check (grub_gfxmenu_view_t view)
{
if (!view->need_to_check_sanity)
return;
/* terminal_font was checked before in the init_terminal function. */
grub_font_t terminal_font = grub_font_get (view->terminal_font_name);
/* Non-negative numbers below. */
int scr_x = view->screen.x;
int scr_y = view->screen.y;
int scr_width = view->screen.width;
int scr_height = view->screen.height;
int term_x = view->terminal_rect.x;
int term_y = view->terminal_rect.y;
int term_width = view->terminal_rect.width;
int term_height = view->terminal_rect.height;
/* Check that border_width isn't too big. */
unsigned int border_width = view->terminal_border;
unsigned int min_terminal_width;
unsigned int min_terminal_height;
get_min_terminal (terminal_font, border_width,
&min_terminal_width, &min_terminal_height);
if (border_width > 3 && ((int) min_terminal_width >= scr_width
|| (int) min_terminal_height >= scr_height))
{
border_width = 3;
get_min_terminal (terminal_font, border_width,
&min_terminal_width, &min_terminal_height);
}
/* Sanity checks. */
if (term_width > scr_width)
term_width = scr_width;
if (term_height > scr_height)
term_height = scr_height;
if (scr_width <= (int) min_terminal_width
|| scr_height <= (int) min_terminal_height)
{
/* The screen resulution is too low. Use all space, except a small border
to show the user, that it is a window. Then center the window. */
term_width = scr_width - 6 * border_width;
term_height = scr_height - 6 * border_width;
term_x = scr_x + (scr_width - term_width) / 2;
term_y = scr_y + (scr_height - term_height) / 2;
}
else if (term_width < (int) min_terminal_width
|| term_height < (int) min_terminal_height)
{
/* The screen resolution is big enough. Make sure, that terminal screen
dimensions aren't less than minimal values. Then center the window. */
term_width = (int) min_terminal_width;
term_height = (int) min_terminal_height;
term_x = scr_x + (scr_width - term_width) / 2;
term_y = scr_y + (scr_height - term_height) / 2;
}
/* At this point w and h are satisfying. */
if (term_x + term_width > scr_width)
term_x = scr_width - term_width;
if (term_y + term_height > scr_height)
term_y = scr_height - term_height;
/* Write down corrected data. */
view->terminal_rect.x = (unsigned int) term_x;
view->terminal_rect.y = (unsigned int) term_y;
view->terminal_rect.width = (unsigned int) term_width;
view->terminal_rect.height = (unsigned int) term_height;
view->terminal_border = border_width;
view->need_to_check_sanity = 0;
}
static void
init_terminal (grub_gfxmenu_view_t view)
{
grub_font_t terminal_font;
terminal_font = grub_font_get (view->terminal_font_name);
if (!terminal_font)
{
grub_error (GRUB_ERR_BAD_FONT, "no font loaded");
return;
}
/* Check that terminal window size and position are sane. */
terminal_sanity_check (view);
term_view = view;
/* Note: currently there is no API for changing the gfxterm font
on the fly, so whatever font the initially loaded theme specifies
will be permanent. */
grub_gfxterm_set_window (GRUB_VIDEO_RENDER_TARGET_DISPLAY,
view->terminal_rect.x,
view->terminal_rect.y,
view->terminal_rect.width,
view->terminal_rect.height,
view->double_repaint,
terminal_font,
view->terminal_border);
grub_gfxterm_decorator_hook = grub_gfxmenu_draw_terminal_box;
}
static void
init_background (grub_gfxmenu_view_t view)
{
if (view->scaled_desktop_image || (!view->raw_desktop_image))
return;
struct grub_video_bitmap *scaled_bitmap;
if (view->desktop_image_scale_method ==
GRUB_VIDEO_BITMAP_SELECTION_METHOD_STRETCH)
grub_video_bitmap_create_scaled (&scaled_bitmap,
view->screen.width,
view->screen.height,
view->raw_desktop_image,
GRUB_VIDEO_BITMAP_SCALE_METHOD_BEST);
else
grub_video_bitmap_scale_proportional (&scaled_bitmap,
view->screen.width,
view->screen.height,
view->raw_desktop_image,
GRUB_VIDEO_BITMAP_SCALE_METHOD_BEST,
view->desktop_image_scale_method,
view->desktop_image_v_align,
view->desktop_image_h_align);
if (! scaled_bitmap)
return;
view->scaled_desktop_image = scaled_bitmap;
}
/* FIXME: previously notifications were displayed in special case.
Is it necessary?
*/
#if 0
/* Sets MESSAGE as the progress message for the view.
MESSAGE can be 0, in which case no message is displayed. */
static void
set_progress_message (grub_gfxmenu_view_t view, const char *message)
{
grub_free (view->progress_message_text);
if (message)
view->progress_message_text = grub_strdup (message);
else
view->progress_message_text = 0;
}
static void
notify_booting (grub_menu_entry_t entry, void *userdata)
{
grub_gfxmenu_view_t view = (grub_gfxmenu_view_t) userdata;
char *s = grub_malloc (100 + grub_strlen (entry->title));
if (!s)
return;
grub_sprintf (s, "Booting '%s'", entry->title);
set_progress_message (view, s);
grub_free (s);
grub_gfxmenu_view_redraw (view, &view->progress_message_frame);
grub_video_swap_buffers ();
if (view->double_repaint)
grub_gfxmenu_view_redraw (view, &view->progress_message_frame);
}
static void
notify_fallback (grub_menu_entry_t entry, void *userdata)
{
grub_gfxmenu_view_t view = (grub_gfxmenu_view_t) userdata;
char *s = grub_malloc (100 + grub_strlen (entry->title));
if (!s)
return;
grub_sprintf (s, "Falling back to '%s'", entry->title);
set_progress_message (view, s);
grub_free (s);
grub_gfxmenu_view_redraw (view, &view->progress_message_frame);
grub_video_swap_buffers ();
if (view->double_repaint)
grub_gfxmenu_view_redraw (view, &view->progress_message_frame);
}
static void
notify_execution_failure (void *userdata __attribute__ ((unused)))
{
}
static struct grub_menu_execute_callback execute_callback =
{
.notify_booting = notify_booting,
.notify_fallback = notify_fallback,
.notify_failure = notify_execution_failure
};
#endif