diff --git a/ChangeLog b/ChangeLog index a388738cb..78eebc11b 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,32 @@ +2010-05-13 Vladimir Serbinenko + + Fix gfxmenu crash. + Reported by: Thorsten Grützmacher. + + * gfxmenu/gui_circular_progress.c (circprog_destroy): Unregister + timeout hook. + (circprog_set_property): Register and unregister timeout hook. + * gfxmenu/gui_label.c (grub_gui_label): New fields template and value. + (label_destroy): Free template. and unregister hook. + (label_set_state): New function. + (label_set_property): Handle templates and hooks. + * gfxmenu/gui_progress_bar.c (progress_bar_destroy): Unregister + timeout hook. + (progress_bar_set_property): Register and unregister timeout hook. + * gfxmenu/view.c (TIMEOUT_COMPONENT_ID): Move from here ... + * include/grub/gui.h (GRUB_GFXMENU_TIMEOUT_COMPONENT_ID): ...to here + * gfxmenu/view.c (grub_gfxmenu_timeout_notifications): New variable. + (update_timeout_visit): Removed. + (update_timeouts): New function. + (redraw_timeouts): Likewise. + (grub_gfxmenu_print_timeout): Use update_timeouts and redraw_timeouts. + (grub_gfxmenu_clear_timeout): Likewise. + * include/grub/gui.h (grub_gfxmenu_set_state_t): New type. + (grub_gfxmenu_timeout_notify): Likewise. + (grub_gfxmenu_timeout_notifications): New external variable. + (grub_gfxmenu_timeout_register): New function. + (grub_gfxmenu_timeout_unregister): Likewise. + 2010-05-09 Vladimir Serbinenko Transform (broken) vga terminal into (working) vga video driver. diff --git a/gfxmenu/gui_circular_progress.c b/gfxmenu/gui_circular_progress.c index 9a859ee2e..098ae1c92 100644 --- a/gfxmenu/gui_circular_progress.c +++ b/gfxmenu/gui_circular_progress.c @@ -54,6 +54,7 @@ static void circprog_destroy (void *vself) { circular_progress_t self = vself; + grub_gfxmenu_timeout_unregister ((grub_gui_component_t) self); grub_free (self); } @@ -211,6 +212,17 @@ circprog_get_bounds (void *vself, grub_video_rect_t *bounds) *bounds = self->bounds; } +static void +circprog_set_state (void *vself, int visible, int start, + int current, int end) +{ + circular_progress_t self = vself; + self->visible = visible; + self->start = start; + self->value = current; + self->end = end; +} + static grub_err_t circprog_set_property (void *vself, const char *name, const char *value) { @@ -247,26 +259,20 @@ circprog_set_property (void *vself, const char *name, const char *value) } 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); 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, + circprog_set_state); } return grub_errno; } -static void -circprog_set_state (void *vself, int visible, int start, - int current, int end) -{ - circular_progress_t self = vself; - self->visible = visible; - self->start = start; - self->value = current; - self->end = end; -} - static struct grub_gui_component_ops circprog_ops = { .destroy = circprog_destroy, diff --git a/gfxmenu/gui_label.c b/gfxmenu/gui_label.c index a9dd575ac..15a352f84 100644 --- a/gfxmenu/gui_label.c +++ b/gfxmenu/gui_label.c @@ -46,8 +46,10 @@ struct grub_gui_label char *id; int visible; char *text; + char *template; grub_font_t font; grub_gui_color_t color; + int value; enum align_mode align; }; @@ -57,7 +59,9 @@ 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); } @@ -146,6 +150,17 @@ label_get_minimal_size (void *vself, unsigned *width, unsigned *height) + grub_font_get_descent (self->font)); } +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) { @@ -153,9 +168,17 @@ label_set_property (void *vself, const char *name, const char *value) if (grub_strcmp (name, "text") == 0) { grub_free (self->text); + grub_free (self->template); if (! value) - value = ""; - self->text = grub_strdup (value); + { + self->template = NULL; + self->text = grub_strdup (""); + } + else + { + self->template = grub_strdup (value); + self->text = grub_xasprintf (value, self->value); + } } else if (grub_strcmp (name, "font") == 0) { @@ -183,11 +206,16 @@ label_set_property (void *vself, const char *name, const char *value) } 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); 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; } diff --git a/gfxmenu/gui_progress_bar.c b/gfxmenu/gui_progress_bar.c index d786aae31..e1b31794f 100644 --- a/gfxmenu/gui_progress_bar.c +++ b/gfxmenu/gui_progress_bar.c @@ -60,6 +60,7 @@ static void progress_bar_destroy (void *vself) { grub_gui_progress_bar_t self = vself; + grub_gfxmenu_timeout_unregister ((grub_gui_component_t) self); grub_free (self); } @@ -333,11 +334,16 @@ progress_bar_set_property (void *vself, const char *name, const char *value) } 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); 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, + progress_bar_set_state); } return grub_errno; } @@ -368,6 +374,7 @@ grub_gui_progress_bar_new (void) self = grub_zalloc (sizeof (*self)); if (! self) return 0; + self->progress.ops = &progress_bar_pb_ops; self->progress.component.ops = &progress_bar_ops; self->visible = 1; diff --git a/gfxmenu/view.c b/gfxmenu/view.c index bf637a96d..9a5671cdd 100644 --- a/gfxmenu/view.c +++ b/gfxmenu/view.c @@ -37,10 +37,6 @@ #include #include -/* The component ID identifying GUI components to be updated as the timeout - status changes. */ -#define TIMEOUT_COMPONENT_ID "__timeout__" - static void init_terminal (grub_gfxmenu_view_t view); static grub_video_rect_t term_rect; @@ -166,16 +162,28 @@ struct progress_value_data int value; }; -static void -update_timeout_visit (grub_gui_component_t component, - void *userdata) -{ - struct progress_value_data *pv; - pv = (struct progress_value_data *) userdata; +struct grub_gfxmenu_timeout_notify *grub_gfxmenu_timeout_notifications; - ((struct grub_gui_progress *) component)->ops - ->set_state ((struct grub_gui_progress *) component, - pv->visible, pv->start, pv->value, pv->end); +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_gfxmenu_view_redraw (view, &bounds); + } } void @@ -183,67 +191,26 @@ grub_gfxmenu_print_timeout (int timeout, void *data) { struct grub_gfxmenu_view *view = data; - struct progress_value_data pv; - - auto void redraw_timeout_visit (grub_gui_component_t component, - void *userdata __attribute__ ((unused))); - - auto void redraw_timeout_visit (grub_gui_component_t component, - void *userdata __attribute__ ((unused))) - { - grub_video_rect_t bounds; - component->ops->get_bounds (component, &bounds); - grub_gfxmenu_view_redraw (view, &bounds); - } - if (view->first_timeout == -1) view->first_timeout = timeout; - pv.visible = 1; - pv.start = -(view->first_timeout + 1); - pv.end = 0; - pv.value = -timeout; - - grub_gui_find_by_id ((grub_gui_component_t) view->canvas, - TIMEOUT_COMPONENT_ID, update_timeout_visit, &pv); - grub_gui_find_by_id ((grub_gui_component_t) view->canvas, - TIMEOUT_COMPONENT_ID, redraw_timeout_visit, &pv); + update_timeouts (1, -(view->first_timeout + 1), -timeout, 0); + redraw_timeouts (view); grub_video_swap_buffers (); if (view->double_repaint) - grub_gui_find_by_id ((grub_gui_component_t) view->canvas, - TIMEOUT_COMPONENT_ID, redraw_timeout_visit, &pv); + redraw_timeouts (view); } void grub_gfxmenu_clear_timeout (void *data) { - struct progress_value_data pv; struct grub_gfxmenu_view *view = data; - auto void redraw_timeout_visit (grub_gui_component_t component, - void *userdata __attribute__ ((unused))); - - auto void redraw_timeout_visit (grub_gui_component_t component, - void *userdata __attribute__ ((unused))) - { - grub_video_rect_t bounds; - component->ops->get_bounds (component, &bounds); - grub_gfxmenu_view_redraw (view, &bounds); - } - - pv.visible = 0; - pv.start = 1; - pv.end = 0; - pv.value = 0; - - grub_gui_find_by_id ((grub_gui_component_t) view->canvas, - TIMEOUT_COMPONENT_ID, update_timeout_visit, &pv); - grub_gui_find_by_id ((grub_gui_component_t) view->canvas, - TIMEOUT_COMPONENT_ID, redraw_timeout_visit, &pv); + update_timeouts (0, 1, 0, 0); + redraw_timeouts (view); grub_video_swap_buffers (); if (view->double_repaint) - grub_gui_find_by_id ((grub_gui_component_t) view->canvas, - TIMEOUT_COMPONENT_ID, redraw_timeout_visit, &pv); + redraw_timeouts (view); } static void diff --git a/include/grub/gui.h b/include/grub/gui.h index 7bd71acd3..6e4a11cbe 100644 --- a/include/grub/gui.h +++ b/include/grub/gui.h @@ -22,10 +22,15 @@ #include #include #include +#include #ifndef GRUB_GUI_H #define GRUB_GUI_H 1 +/* The component ID identifying GUI components to be updated as the timeout + status changes. */ +#define GRUB_GFXMENU_TIMEOUT_COMPONENT_ID "__timeout__" + /* A representation of a color. Unlike grub_video_color_t, this representation is independent of any video mode specifics. */ typedef struct grub_gui_color @@ -79,6 +84,46 @@ struct grub_gui_progress_ops void (*set_state) (void *self, int visible, int start, int current, int end); }; +typedef void (*grub_gfxmenu_set_state_t) (void *self, int visible, int start, + int current, int end); + +struct grub_gfxmenu_timeout_notify +{ + struct grub_gfxmenu_timeout_notify *next; + grub_gfxmenu_set_state_t set_state; + grub_gui_component_t self; +}; + +extern struct grub_gfxmenu_timeout_notify *grub_gfxmenu_timeout_notifications; + +static inline grub_err_t +grub_gfxmenu_timeout_register (grub_gui_component_t self, + grub_gfxmenu_set_state_t set_state) +{ + struct grub_gfxmenu_timeout_notify *ne = grub_malloc (sizeof (*ne)); + if (!ne) + return grub_errno; + ne->set_state = set_state; + ne->self = self; + ne->next = grub_gfxmenu_timeout_notifications; + grub_gfxmenu_timeout_notifications = ne; + return GRUB_ERR_NONE; +} + +static inline void +grub_gfxmenu_timeout_unregister (grub_gui_component_t self) +{ + struct grub_gfxmenu_timeout_notify **p, *q; + + for (p = &grub_gfxmenu_timeout_notifications, q = *p; + q; p = &(q->next), q = q->next) + if (q->self == self) + { + *p = q->next; + break; + } +} + typedef signed grub_fixed_signed_t; #define GRUB_FIXED_1 0x10000