From 2598810aea74bf2f614d9e7b7cbb7c086a1fdc95 Mon Sep 17 00:00:00 2001 From: Samuel Mendoza-Jonas Date: Thu, 16 Apr 2015 11:53:20 +1000 Subject: ui/ncurses: Define Home, End, and Page Up/Down keys --- ui/ncurses/nc-cui.c | 10 ++++++++++ 1 file changed, 10 insertions(+) (limited to 'ui') diff --git a/ui/ncurses/nc-cui.c b/ui/ncurses/nc-cui.c index 3f1e0a2..8e31fe2 100644 --- a/ui/ncurses/nc-cui.c +++ b/ui/ncurses/nc-cui.c @@ -69,6 +69,16 @@ static void cui_start(void) */ define_key("\x1b[Z", KEY_BTAB); + /* We'll define a few other keys too since they're commonly + * used for navigation but the escape character will cause + * Petitboot to exit if they're left undefined */ + define_key("\x1b\x5b\x35\x7e", KEY_PPAGE); + define_key("\x1b\x5b\x36\x7e", KEY_NPAGE); + define_key("\x1b\x4f\x48", KEY_HOME); + define_key("\x1b\x4f\x46", KEY_END); + define_key("OH", KEY_HOME); + define_key("OF", KEY_END); + while (getch() != ERR) /* flush stdin */ (void)0; } -- cgit v1.2.1 From 60252961c21a0f102e69373f2bea36f8af495c29 Mon Sep 17 00:00:00 2001 From: Samuel Mendoza-Jonas Date: Wed, 21 Jan 2015 14:45:47 +1100 Subject: ui/ncurses: Add nc_widget_subset The new nc_widget_subset is similar to nc_widget_select, but hides added options until they are made active. This allows the widget to have multiple options selected and ordered without cluttering the screen. Signed-off-by: Samuel Mendoza-Jonas --- ui/ncurses/nc-widgets.c | 365 +++++++++++++++++++++++++++++++++++++++++++++++- ui/ncurses/nc-widgets.h | 19 +++ 2 files changed, 383 insertions(+), 1 deletion(-) (limited to 'ui') diff --git a/ui/ncurses/nc-widgets.c b/ui/ncurses/nc-widgets.c index f821929..5c67d82 100644 --- a/ui/ncurses/nc-widgets.c +++ b/ui/ncurses/nc-widgets.c @@ -61,6 +61,7 @@ #define to_textbox(w) container_of(w, struct nc_widget_textbox, widget) #define to_button(w) container_of(w, struct nc_widget_button, widget) #define to_select(w) container_of(w, struct nc_widget_select, widget) +#define to_subset(w) container_of(w, struct nc_widget_subset, widget) static const char *checkbox_checked_str = "[*]"; static const char *checkbox_unchecked_str = "[ ]"; @@ -112,6 +113,24 @@ struct nc_widget_textbox { struct nc_widget widget; }; +struct nc_widget_subset { + struct nc_widget widget; + int *active; + int n_active; + struct subset_option { + char *str; + int val; + FIELD *field; + } *options; + int n_options; + int top, left, size; + struct nc_widgetset *set; + void (*on_change)(void *, int); + void *on_change_arg; + void (*screen_cb)(void *, + struct nc_widget_subset *, int); +}; + struct nc_widget_select { struct nc_widget widget; struct select_option { @@ -140,6 +159,21 @@ static bool key_is_select(int key) return key == ' ' || key == '\r' || key == '\n' || key == KEY_ENTER; } +static bool key_is_minus(int key) +{ + return key == 055; +} + +static bool key_is_left(int key) +{ + return key == KEY_LEFT; +} + +static bool key_is_right(int key) +{ + return key == KEY_RIGHT; +} + static bool process_key_nop(struct nc_widget *widget __attribute__((unused)), FORM *form __attribute((unused)), int key __attribute__((unused))) @@ -155,6 +189,11 @@ static void field_set_visible(FIELD *field, bool visible) set_field_opts(field, opts); } +static bool field_visible(FIELD *field) +{ + return (field_opts(field) & O_VISIBLE) == O_VISIBLE; +} + static void field_move(FIELD *field, int y, int x) { move_field(field, y, x); @@ -414,6 +453,329 @@ void widget_textbox_set_validator_ipv4_multi(struct nc_widget_textbox *textbox) set_field_type(textbox->widget.field, textbox->set->ipv4_multi_type); } +static void subset_update_order(struct nc_widget_subset *subset) +{ + char *str; + int i, val; + + for (i = 0; i < subset->n_active; i++) { + val = subset->active[i]; + str = talloc_asprintf(subset, "(%d) %s", + i, subset->options[val].str); + set_field_buffer(subset->options[val].field, 0, + str); + talloc_free(str); + } +} + +static void widget_focus_change(struct nc_widget *widget, FIELD *field, + bool focussed); + +static void subset_delete_active(struct nc_widget_subset *subset, int idx) +{ + bool last = idx == (subset->n_active - 1); + struct nc_widgetset *set = subset->set; + struct nc_widget *widget; + size_t rem; + int i, val; + + /* Shift field focus to nearest active option or next visible field */ + if (subset->n_active > 1) { + if (last) + val = subset->active[idx - 1]; + else + val = subset->active[idx + 1]; + set->cur_field = subset->options[val].field; + } else { + for (i = 0; i < set->n_fields; i++) + if (field_visible(set->fields[i])) { + set->cur_field = set->fields[i]; + break; + } + } + + set_current_field(set->form, set->cur_field); + widget = field_userptr(set->cur_field); + widget_focus_change(widget, set->cur_field, true); + if (set->widget_focus) + set->widget_focus(widget, set->widget_focus_arg); + + /* Update active array */ + rem = sizeof(int) * (subset->n_active - idx - 1); + val = subset->active[idx]; + field_set_visible(subset->options[val].field, false); + if (rem) + memmove(&subset->active[idx], &subset->active[idx + 1], rem); + subset->n_active--; + subset->active = talloc_realloc(subset, subset->active, + int, subset->n_active); + + subset->widget.height = subset->n_active; +} + +static bool subset_process_key(struct nc_widget *w, FORM *form, int key) +{ + struct nc_widget_subset *subset = to_subset(w); + int i, val, opt_idx = -1; + FIELD *field; + + if (!key_is_minus(key) && !key_is_left(key) && !key_is_right(key)) + return false; + + field = current_field(form); + + for (i = 0; i < subset->n_active; i++) { + val = subset->active[i]; + if (subset->options[val].field == field) { + opt_idx = i; + break; + } + } + + if (opt_idx < 0) + return false; + + if (key_is_minus(key)) + subset_delete_active(subset, opt_idx); + + if (key_is_left(key)){ + if (opt_idx == 0) + return true; + + val = subset->active[opt_idx]; + subset->active[opt_idx] = subset->active[opt_idx - 1]; + subset->active[opt_idx - 1] = val; + } + + if (key_is_right(key)){ + if (opt_idx >= subset->n_active - 1) + return true; + + val = subset->active[opt_idx]; + subset->active[opt_idx] = subset->active[opt_idx + 1]; + subset->active[opt_idx + 1] = val; + } + + subset_update_order(subset); + + if (subset->on_change) + subset->on_change(subset->on_change_arg, 0); + + return true; +} + +static void subset_set_visible(struct nc_widget *widget, bool visible) +{ + struct nc_widget_subset *subset = to_subset(widget); + int i, val; + + for (i = 0; i < subset->n_active; i++) { + val = subset->active[i]; + field_set_visible(subset->options[val].field, visible); + } +} + +static void subset_move(struct nc_widget *widget, int y, int x) +{ + struct nc_widget_subset *subset = to_subset(widget); + int i, val; + + for (i = 0; i < subset->n_active; i++) { + val = subset->active[i]; + field_move(subset->options[val].field, y + i , x); + } +} + +static void subset_field_focus(struct nc_widget *widget, FIELD *field) +{ + struct nc_widget_subset *subset = to_subset(widget); + int i, val; + + for (i = 0; i < subset->n_active; i++) { + val = subset->active[i]; + if (field == subset->options[val].field) { + widget->focus_y = i; + return; + } + } +} + +static int subset_destructor(void *ptr) +{ + struct nc_widget_subset *subset = ptr; + int i; + + for (i = 0; i < subset->n_options; i++) + free_field(subset->options[i].field); + + return 0; +} + +struct nc_widget_subset *widget_new_subset(struct nc_widgetset *set, + int y, int x, int len, void *screen_cb) +{ + struct nc_widget_subset *subset; + + subset = talloc_zero(set, struct nc_widget_subset); + subset->widget.width = len; + subset->widget.height = 0; + subset->widget.x = x; + subset->widget.y = y; + subset->widget.process_key = subset_process_key; + subset->widget.set_visible = subset_set_visible; + subset->widget.move = subset_move; + subset->widget.field_focus = subset_field_focus; + subset->widget.focussed_attr = A_REVERSE; + subset->widget.unfocussed_attr = A_NORMAL; + subset->top = y; + subset->left = x; + subset->size = len; + subset->set = set; + subset->n_active = subset->n_options = 0; + subset->active = NULL; + subset->options = NULL; + subset->screen_cb = screen_cb; + + talloc_set_destructor(subset, subset_destructor); + + return subset; +} + +void widget_subset_add_option(struct nc_widget_subset *subset, const char *text) +{ + FIELD *f; + int i; + + i = subset->n_options++; + subset->options = talloc_realloc(subset, subset->options, + struct subset_option, i + 1); + + subset->options[i].str = talloc_strdup(subset->options, text); + + subset->options[i].field = f = new_field(1, subset->size, subset->top + i, + subset->left, 0, 0); + + field_opts_off(f, O_WRAP | O_EDIT); + set_field_userptr(f, &subset->widget); + set_field_buffer(f, 0, subset->options[i].str); + field_set_visible(f, false); + widgetset_add_field(subset->set, f); +} + +void widget_subset_make_active(struct nc_widget_subset *subset, int idx) +{ + int i; + + for (i = 0; i < subset->n_active; i++) + if (subset->active[i] == idx) { + pb_debug("%s: Index %d already active\n", __func__, idx); + return; + } + + i = subset->n_active++; + subset->widget.height = subset->n_active; + subset->active = talloc_realloc(subset, subset->active, + int, i + 1); + subset->active[i] = idx; + + subset_update_order(subset); +} + +int widget_subset_get_order(void *ctx, unsigned int **order, + struct nc_widget_subset *subset) +{ + unsigned int *buf = talloc_array(ctx, unsigned int, subset->n_active); + int i; + + for (i = 0; i < subset->n_active; i++) + buf[i] = subset->active[i]; + + *order = buf; + return i; +} + +void widget_subset_show_inactive(struct nc_widget_subset *subset, + struct nc_widget_select *select) +{ + bool active = false, first = true; + int i, j; + + for (i = 0; i < subset->n_options; i++) { + active = false; + for (j = 0; j < subset->n_active; j++) + if (subset->active[j] == i) + active = true; + + if (active) + continue; + + widget_select_add_option(select, i, + subset->options[i].str, first); + if (first) + first = false; + } +} + +int widget_subset_n_inactive(struct nc_widget_subset *subset) +{ + return subset->n_options - subset->n_active; +} + +int widget_subset_height(struct nc_widget_subset *subset) +{ + return subset->n_active; +} + +void widget_subset_on_change(struct nc_widget_subset *subset, + void (*on_change)(void *, int), void *arg) +{ + subset->on_change = on_change; + subset->on_change_arg = arg; +} + +void widget_subset_drop_options(struct nc_widget_subset *subset) +{ + struct nc_widgetset *set = subset->set; + int i; + + for (i = 0; i < subset->n_options; i++) { + FIELD *field = subset->options[i].field; + widgetset_remove_field(set, field); + if (field == set->cur_field) + set->cur_field = NULL; + free_field(subset->options[i].field); + } + + talloc_free(subset->options); + talloc_free(subset->active); + subset->options = NULL; + subset->active = NULL; + subset->n_options = 0; + subset->n_active = 0; + subset->widget.height = 0; + subset->widget.focus_y = 0; +} + +void widget_subset_clear_active(struct nc_widget_subset *subset) +{ + int i; + + for (i = 0; i < subset->n_options; i++) + field_set_visible(subset->options[i].field, false); + + talloc_free(subset->active); + subset->active = NULL; + subset->n_active = 0; + subset->widget.height = 0; + subset->widget.focus_y = 0; +} + +void widget_subset_callback(void *arg, + struct nc_widget_subset *subset, int idx) +{ + subset->screen_cb(arg, subset, idx); +} + static void select_option_change(struct select_option *opt, bool selected) { const char *str; @@ -679,7 +1041,7 @@ struct nc_widget_button *widget_new_button(struct nc_widgetset *set, return button; } -static void widget_focus_change(struct nc_widget *widget, FIELD *field, +void widget_focus_change(struct nc_widget *widget, FIELD *field, bool focussed) { int attr = focussed ? widget->focussed_attr : widget->unfocussed_attr; @@ -856,6 +1218,7 @@ static void widgetset_remove_field(struct nc_widgetset *set, FIELD *field) DECLARE_BASEFN(textbox); DECLARE_BASEFN(checkbox); +DECLARE_BASEFN(subset); DECLARE_BASEFN(select); DECLARE_BASEFN(label); DECLARE_BASEFN(button); diff --git a/ui/ncurses/nc-widgets.h b/ui/ncurses/nc-widgets.h index 09c3b1f..4b67da7 100644 --- a/ui/ncurses/nc-widgets.h +++ b/ui/ncurses/nc-widgets.h @@ -29,6 +29,8 @@ struct nc_widget_checkbox *widget_new_checkbox(struct nc_widgetset *set, int y, int x, bool checked); struct nc_widget_textbox *widget_new_textbox(struct nc_widgetset *set, int y, int x, int len, char *str); +struct nc_widget_subset *widget_new_subset(struct nc_widgetset *set, + int y, int x, int len, void *screen_cb); struct nc_widget_select *widget_new_select(struct nc_widgetset *set, int y, int x, int len); struct nc_widget_button *widget_new_button(struct nc_widgetset *set, @@ -41,6 +43,12 @@ void widget_textbox_set_validator_integer(struct nc_widget_textbox *textbox, void widget_textbox_set_validator_ipv4(struct nc_widget_textbox *textbox); void widget_textbox_set_validator_ipv4_multi(struct nc_widget_textbox *textbox); +void widget_subset_add_option(struct nc_widget_subset *subset, const char *text); +void widget_subset_make_active(struct nc_widget_subset *subset, int idx); + +void widget_subset_on_change(struct nc_widget_subset *subset, + void (*on_change)(void *arg, int value), void *arg); + void widget_select_add_option(struct nc_widget_select *select, int value, const char *text, bool selected); @@ -49,6 +57,16 @@ void widget_select_on_change(struct nc_widget_select *select, char *widget_textbox_get_value(struct nc_widget_textbox *textbox); bool widget_checkbox_get_value(struct nc_widget_checkbox *checkbox); +int widget_subset_get_order(void *ctx, unsigned int **order, + struct nc_widget_subset *subset); +void widget_subset_show_inactive(struct nc_widget_subset *subset, + struct nc_widget_select *select); +int widget_subset_n_inactive(struct nc_widget_subset *subset); +int widget_subset_height(struct nc_widget_subset *subset); +void widget_subset_drop_options(struct nc_widget_subset *subset); +void widget_subset_clear_active(struct nc_widget_subset *subset); +void widget_subset_callback(void *arg, + struct nc_widget_subset *subset, int idx); int widget_select_get_value(struct nc_widget_select *select); int widget_select_height(struct nc_widget_select *select); void widget_select_drop_options(struct nc_widget_select *select); @@ -56,6 +74,7 @@ void widget_select_drop_options(struct nc_widget_select *select); /* generic widget API */ struct nc_widget *widget_textbox_base(struct nc_widget_textbox *textbox); struct nc_widget *widget_checkbox_base(struct nc_widget_checkbox *checkbox); +struct nc_widget *widget_subset_base(struct nc_widget_subset *subset); struct nc_widget *widget_select_base(struct nc_widget_select *select); struct nc_widget *widget_label_base(struct nc_widget_label *label); struct nc_widget *widget_button_base(struct nc_widget_button *button); -- cgit v1.2.1 From 6bb1301835dbd6ff54e1becb5b684fedb0e4caaf Mon Sep 17 00:00:00 2001 From: Samuel Mendoza-Jonas Date: Wed, 21 Jan 2015 14:48:38 +1100 Subject: ui/ncurses: Add nc-subset selection screen The nc-subset screen is intended to be used as a sub-screen from the current screen (eg. nc-config) which passes a pointer to a nc_widget_subset struct. The nc-subset screen allows the user to select an option from a list of 'inactive' options, before returning control back to the current screen. Signed-off-by: Samuel Mendoza-Jonas --- ui/ncurses/Makefile.am | 4 +- ui/ncurses/nc-cui.c | 24 ++++ ui/ncurses/nc-cui.h | 3 + ui/ncurses/nc-scr.h | 1 + ui/ncurses/nc-subset.c | 289 +++++++++++++++++++++++++++++++++++++++++++++++++ ui/ncurses/nc-subset.h | 36 ++++++ 6 files changed, 356 insertions(+), 1 deletion(-) create mode 100644 ui/ncurses/nc-subset.c create mode 100644 ui/ncurses/nc-subset.h (limited to 'ui') diff --git a/ui/ncurses/Makefile.am b/ui/ncurses/Makefile.am index 9a4e1e4..265ae69 100644 --- a/ui/ncurses/Makefile.am +++ b/ui/ncurses/Makefile.am @@ -47,7 +47,9 @@ ui_ncurses_libpbnc_la_SOURCES = \ ui/ncurses/nc-widgets.h \ ui/ncurses/nc-add-url.c \ ui/ncurses/nc-add-url.h \ - ui/ncurses/nc-add-url-help.c + ui/ncurses/nc-add-url-help.c \ + ui/ncurses/nc-subset.c \ + ui/ncurses/nc-subset.h sbin_PROGRAMS += ui/ncurses/petitboot-nc diff --git a/ui/ncurses/nc-cui.c b/ui/ncurses/nc-cui.c index 8e31fe2..2fc779e 100644 --- a/ui/ncurses/nc-cui.c +++ b/ui/ncurses/nc-cui.c @@ -41,6 +41,7 @@ #include "nc-sysinfo.h" #include "nc-lang.h" #include "nc-helpscreen.h" +#include "nc-subset.h" extern const struct help_text main_menu_help_text; @@ -328,6 +329,29 @@ void cui_show_help(struct cui *cui, const char *title, cui_set_current(cui, help_screen_scr(cui->help_screen)); } +static void cui_subset_exit(struct cui *cui) +{ + cui_set_current(cui, subset_screen_return_scr(cui->subset_screen)); + talloc_free(cui->subset_screen); + cui->subset_screen = NULL; +} + +void cui_show_subset(struct cui *cui, const char *title, + void *arg) +{ + if (!cui->current) + return; + + if (cui->subset_screen) + return; + + cui->subset_screen = subset_screen_init(cui, cui->current, + title, arg, cui_subset_exit); + + if (cui->subset_screen) + cui_set_current(cui, subset_screen_scr(cui->subset_screen)); +} + /** * cui_set_current - Set the currently active screen and redraw it. */ diff --git a/ui/ncurses/nc-cui.h b/ui/ncurses/nc-cui.h index 694ebd1..24a0761 100644 --- a/ui/ncurses/nc-cui.h +++ b/ui/ncurses/nc-cui.h @@ -63,6 +63,7 @@ struct cui { struct boot_editor *boot_editor; struct lang_screen *lang_screen; struct help_screen *help_screen; + struct subset_screen *subset_screen; struct pjs *pjs; void *platform_info; unsigned int default_item; @@ -81,6 +82,8 @@ void cui_show_config(struct cui *cui); void cui_show_lang(struct cui *cui); void cui_show_help(struct cui *cui, const char *title, const struct help_text *text); +void cui_show_subset(struct cui *cui, const char *title, + void *arg); void cui_show_add_url(struct cui *cui); int cui_send_config(struct cui *cui, struct config *config); int cui_send_url(struct cui *cui, char *url); diff --git a/ui/ncurses/nc-scr.h b/ui/ncurses/nc-scr.h index ed87517..be99b48 100644 --- a/ui/ncurses/nc-scr.h +++ b/ui/ncurses/nc-scr.h @@ -48,6 +48,7 @@ enum pb_nc_sig { pb_config_screen_sig = 666, pb_lang_screen_sig = 777, pb_add_url_screen_sig = 888, + pb_subset_screen_sig = 101, pb_removed_sig = -999, }; diff --git a/ui/ncurses/nc-subset.c b/ui/ncurses/nc-subset.c new file mode 100644 index 0000000..d90ed20 --- /dev/null +++ b/ui/ncurses/nc-subset.c @@ -0,0 +1,289 @@ +/* + * Copyright (C) 2013 IBM Corporation + * + * This program 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; version 2 of the License. + * + * This program 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 this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#if defined(HAVE_CONFIG_H) +#include "config.h" +#endif + +#include +#include +#include + +#include +#include +#include +#include + +#include "nc-cui.h" +#include "nc-subset.h" + +#define N_FIELDS 3 + +struct subset_screen { + struct nc_scr scr; + struct cui *cui; + struct nc_scr *return_scr; + struct nc_widgetset *widgetset; + WINDOW *pad; + struct nc_widget_subset *options; + + bool exit; + void (*on_exit)(struct cui *); + + int scroll_y; + + int label_x; + int field_x; + + struct { + struct nc_widget_select *options_f; + + struct nc_widget_button *ok_b; + struct nc_widget_button *cancel_b; + } widgets; +}; + +struct nc_scr *subset_screen_return_scr(struct subset_screen *screen) +{ + return screen->return_scr; +} + +static struct subset_screen *subset_screen_from_scr(struct nc_scr *scr) +{ + struct subset_screen *subset_screen; + + assert(scr->sig == pb_subset_screen_sig); + subset_screen = (struct subset_screen *) + ((char *)scr - (size_t)&((struct subset_screen *)0)->scr); + assert(subset_screen->scr.sig == pb_subset_screen_sig); + return subset_screen; +} + +static void pad_refresh(struct subset_screen *screen) +{ + int y, x, rows, cols; + + getmaxyx(screen->scr.sub_ncw, rows, cols); + getbegyx(screen->scr.sub_ncw, y, x); + + prefresh(screen->pad, screen->scroll_y, 0, y, x, rows, cols); +} + +static void subset_screen_process_key(struct nc_scr *scr, int key) +{ + struct subset_screen *screen = subset_screen_from_scr(scr); + bool handled; + + handled = widgetset_process_key(screen->widgetset, key); + + if (!handled) { + switch (key) { + case 'x': + case 27: /* esc */ + screen->exit = true; + break; + } + } + + if (screen->exit) + screen->on_exit(screen->cui); + else if (handled) + pad_refresh(screen); +} + +static int subset_screen_post(struct nc_scr *scr) +{ + struct subset_screen *screen = subset_screen_from_scr(scr); + widgetset_post(screen->widgetset); + nc_scr_frame_draw(scr); + redrawwin(scr->main_ncw); + wrefresh(scr->main_ncw); + pad_refresh(screen); + return 0; +} + +static int subset_screen_unpost(struct nc_scr *scr) +{ + struct subset_screen *screen = subset_screen_from_scr(scr); + widgetset_unpost(screen->widgetset); + return 0; +} + +struct nc_scr *subset_screen_scr(struct subset_screen *screen) +{ + return &screen->scr; +} + +static void ok_click(void *arg) +{ + struct subset_screen *screen = arg; + int idx = widget_select_get_value(screen->widgets.options_f); + widget_subset_callback(screen->return_scr, screen->options, idx); + screen->exit = true; +} + +static void cancel_click(void *arg) +{ + struct subset_screen *screen = arg; + screen->exit = true; +} + +static void subset_screen_layout_widgets(struct subset_screen *screen) +{ + int y = 2; + + /* select */ + widget_move(widget_select_base(screen->widgets.options_f), + y, screen->label_x); + y+= widget_height(widget_select_base(screen->widgets.options_f)); + + /* ok, cancel */ + y += 1; + + widget_move(widget_button_base(screen->widgets.ok_b), + y, screen->field_x + 12); + widget_move(widget_button_base(screen->widgets.cancel_b), + y, screen->field_x + 24); +} + +static void subset_screen_option_select(void *arg, int value) +{ + struct subset_screen *screen = arg; + widgetset_unpost(screen->widgetset); + subset_screen_layout_widgets(screen); + widgetset_post(screen->widgetset); + (void)value; +} + +static void subset_screen_setup_widgets(struct subset_screen *screen) +{ + struct nc_widgetset *set = screen->widgetset; + struct nc_widget_subset *subset = screen->options; + + build_assert(sizeof(screen->widgets) / sizeof(struct widget *) + == N_FIELDS); + + screen->widgets.options_f = widget_new_select(set, 0, 0, + COLS - (2 * screen->label_x)); + + widget_select_on_change(screen->widgets.options_f, + subset_screen_option_select, screen); + + widget_subset_show_inactive(subset, screen->widgets.options_f); + + screen->widgets.ok_b = widget_new_button(set, 0, 0, 10, _("OK"), + ok_click, screen); + screen->widgets.cancel_b = widget_new_button(set, 0, 0, 10, _("Cancel"), + cancel_click, screen); +} + +static void subset_screen_widget_focus(struct nc_widget *widget, void *arg) +{ + struct subset_screen *screen = arg; + int w_y, s_max; + + w_y = widget_y(widget) + widget_focus_y(widget); + s_max = getmaxy(screen->scr.sub_ncw) - 1; + + if (w_y < screen->scroll_y) + screen->scroll_y = w_y; + + else if (w_y + screen->scroll_y + 1 > s_max) + screen->scroll_y = 1 + w_y - s_max; + + else + return; + + pad_refresh(screen); +} + +static void subset_screen_draw(struct subset_screen *screen) +{ + bool repost = false; + int height; + + /* Size of pad = top space + number of available options */ + height = 1 + N_FIELDS + widget_subset_n_inactive(screen->options); + + if (!screen->pad || getmaxy(screen->pad) < height) { + if (screen->pad) + delwin(screen->pad); + screen->pad = newpad(height, COLS); + } + + if (screen->widgetset) { + widgetset_unpost(screen->widgetset); + talloc_free(screen->widgetset); + repost = true; + } + + screen->widgetset = widgetset_create(screen, screen->scr.main_ncw, + screen->pad); + widgetset_set_widget_focus(screen->widgetset, + subset_screen_widget_focus, screen); + + subset_screen_setup_widgets(screen); + subset_screen_layout_widgets(screen); + + if (repost) + widgetset_post(screen->widgetset); +} + +static int subset_screen_destroy(void *arg) +{ + struct subset_screen *screen = arg; + if (screen->pad) + delwin(screen->pad); + return 0; +} + +struct subset_screen *subset_screen_init(struct cui *cui, + struct nc_scr *current_scr, + const char *title_suffix, + void *subset, + void (*on_exit)(struct cui *)) +{ + struct subset_screen *screen; + + screen = talloc_zero(cui, struct subset_screen); + talloc_set_destructor(screen, subset_screen_destroy); + + screen->cui = cui; + screen->on_exit = on_exit; + screen->options = (struct nc_widget_subset *) subset; + screen->label_x = 2; + screen->field_x = 22; + + screen->return_scr = current_scr; + + nc_scr_init(&screen->scr, pb_subset_screen_sig, 0, + cui, subset_screen_process_key, + subset_screen_post, subset_screen_unpost, + NULL); + + screen->scr.frame.ltitle = talloc_strdup(screen, + title_suffix); + screen->scr.frame.rtitle = NULL; + screen->scr.frame.help = talloc_strdup(screen, + _("tab=next, shift+tab=previous, x=exit")); + + scrollok(screen->scr.sub_ncw, true); + + subset_screen_draw(screen); + + return screen; +} diff --git a/ui/ncurses/nc-subset.h b/ui/ncurses/nc-subset.h new file mode 100644 index 0000000..e967866 --- /dev/null +++ b/ui/ncurses/nc-subset.h @@ -0,0 +1,36 @@ +/* + * Copyright (C) 2013 IBM Corporation + * + * This program 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; version 2 of the License. + * + * This program 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 this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#ifndef _NC_SUBSET_H +#define _NC_SUBSET_H + +#include "nc-cui.h" +#include "nc-widgets.h" + +struct subset_screen; + +struct subset_screen *subset_screen_init(struct cui *cui, + struct nc_scr *current_scr, + const char *title_suffix, + void *subset, + void (*on_exit)(struct cui *)); + +struct nc_scr *subset_screen_scr(struct subset_screen *screen); +struct nc_scr *subset_screen_return_scr(struct subset_screen *screen); +void subset_screen_update(struct subset_screen *screen); + +#endif /* defined _NC_SUBSET_H */ -- cgit v1.2.1 From ec12bbd7bbfb3dd31dbd987ecff463542d54542f Mon Sep 17 00:00:00 2001 From: Samuel Mendoza-Jonas Date: Wed, 21 Jan 2015 15:25:15 +1100 Subject: ui/ncurses: Use sorted field navigation Changing the visual order of widgets will cause their associated fields to be out of order relative to the widgetset field array. Rather than manually resorting the array, use sorted navigation to move according to a field's visual positon Signed-off-by: Samuel Mendoza-Jonas --- ui/ncurses/nc-widgets.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) (limited to 'ui') diff --git a/ui/ncurses/nc-widgets.c b/ui/ncurses/nc-widgets.c index 5c67d82..3daced1 100644 --- a/ui/ncurses/nc-widgets.c +++ b/ui/ncurses/nc-widgets.c @@ -1066,19 +1066,19 @@ bool widgetset_process_key(struct nc_widgetset *set, int key) tab = true; /* fall through */ case KEY_UP: - req = REQ_PREV_FIELD; + req = REQ_SPREV_FIELD; break; case '\t': tab = true; /* fall through */ case KEY_DOWN: - req = REQ_NEXT_FIELD; + req = REQ_SNEXT_FIELD; break; case KEY_PPAGE: - req = REQ_FIRST_FIELD; + req = REQ_SFIRST_FIELD; break; case KEY_NPAGE: - req = REQ_LAST_FIELD; + req = REQ_SLAST_FIELD; break; } -- cgit v1.2.1 From a3e3b66fbcb18174fc54ffd771ef3a38c9f8defd Mon Sep 17 00:00:00 2001 From: Samuel Mendoza-Jonas Date: Wed, 21 Jan 2015 16:12:14 +1100 Subject: petitboot: Implement ordered boot options Move petitboot to a more familiar 'boot-order' based autoboot system. The discover server now reads multiple values from the petitboot,bootdev parameter and adds them in order to config->autoboot_opts. Boot priority is determined by the options' position in the list. On the client, nc-config now recognises the new boot order, and allows the user to add, remove, and reorder the devices in the list. Signed-off-by: Samuel Mendoza-Jonas --- ui/ncurses/nc-config.c | 326 ++++++++++++++++++++++++++++++++----------------- 1 file changed, 216 insertions(+), 110 deletions(-) (limited to 'ui') diff --git a/ui/ncurses/nc-config.c b/ui/ncurses/nc-config.c index c45df34..3156b9b 100644 --- a/ui/ncurses/nc-config.c +++ b/ui/ncurses/nc-config.c @@ -33,16 +33,10 @@ #include "nc-config.h" #include "nc-widgets.h" -#define N_FIELDS 26 +#define N_FIELDS 29 extern struct help_text config_help_text; -enum autoboot_type { - AUTOBOOT_ANY, - AUTOBOOT_ONE, - AUTOBOOT_DISABLED, -}; - enum net_conf_type { NET_CONF_TYPE_DHCP_ALL, NET_CONF_TYPE_DHCP_ONE, @@ -57,7 +51,9 @@ struct config_screen { bool exit; bool show_help; + bool show_subset; bool need_redraw; + void (*on_exit)(struct cui *); int scroll_y; @@ -67,12 +63,16 @@ struct config_screen { int network_config_y; enum net_conf_type net_conf_type; - enum autoboot_type autoboot_type; + + bool autoboot_enabled; struct { - struct nc_widget_select *autoboot_f; - struct nc_widget_label *autoboot_l; - struct nc_widget_select *boot_device_f; + struct nc_widget_label *boot_order_l; + struct nc_widget_subset *boot_order_f; + struct nc_widget_label *boot_empty_l; + struct nc_widget_button *boot_add_b; + struct nc_widget_button *boot_none_b; + struct nc_widget_button *boot_any_b; struct nc_widget_textbox *timeout_f; struct nc_widget_label *timeout_l; struct nc_widget_label *timeout_help_l; @@ -151,7 +151,7 @@ static void config_screen_process_key(struct nc_scr *scr, int key) cui_show_help(screen->cui, _("System Configuration"), &config_help_text); - } else if (handled) { + } else if (handled && !screen->show_subset) { pad_refresh(screen); } } @@ -165,6 +165,7 @@ static void config_screen_resize(struct nc_scr *scr) static int config_screen_post(struct nc_scr *scr) { struct config_screen *screen = config_screen_from_scr(scr); + screen->show_subset = false; widgetset_post(screen->widgetset); nc_scr_frame_draw(scr); if (screen->need_redraw) { @@ -193,40 +194,48 @@ static int screen_process_form(struct config_screen *screen) const struct system_info *sysinfo = screen->cui->sysinfo; enum net_conf_type net_conf_type; struct interface_config *iface; - char *str, *end, *uuid; + char *str, *end; struct config *config; - int rc, idx; + int i, n_boot_opts, rc, idx; + unsigned int *order; + char mac[20]; config = config_copy(screen, screen->cui->config); - screen->autoboot_type = - widget_select_get_value(screen->widgets.autoboot_f); - - config->autoboot_enabled = screen->autoboot_type != AUTOBOOT_DISABLED; - - uuid = NULL; - if (screen->autoboot_type == AUTOBOOT_ONE) { - char mac[20]; - - /* if idx is -1 here, we have an unknown UUID selected. - * Otherwise, it's a blockdev index (idx <= n_blockdevs) or an - * interface index. - */ - idx = widget_select_get_value(screen->widgets.boot_device_f); - if (idx >= (int)sysinfo->n_blockdevs) { - struct interface_info *info = sysinfo-> - interfaces[idx - sysinfo->n_blockdevs]; - mac_str(info->hwaddr, info->hwaddr_size, - mac, sizeof(mac)); - uuid = mac; - } else if (idx != -1) { - uuid = sysinfo->blockdevs[idx]->uuid; + talloc_free(config->autoboot_opts); + config->n_autoboot_opts = 0; + + n_boot_opts = widget_subset_get_order(config, &order, + screen->widgets.boot_order_f); + + config->autoboot_enabled = n_boot_opts > 0; + + config->n_autoboot_opts = n_boot_opts; + config->autoboot_opts = talloc_array(config, struct autoboot_option, + n_boot_opts); + + for (i = 0; i < n_boot_opts; i++) { + if (order[i] < sysinfo->n_blockdevs) { + /* disk uuid */ + config->autoboot_opts[i].boot_type = BOOT_DEVICE_UUID; + config->autoboot_opts[i].uuid = talloc_strdup(config, + sysinfo->blockdevs[order[i]]->uuid); + } else if(order[i] < (sysinfo->n_blockdevs + sysinfo->n_interfaces)) { + /* net uuid */ + order[i] -= sysinfo->n_blockdevs; + config->autoboot_opts[i].boot_type = BOOT_DEVICE_UUID; + mac_str(sysinfo->interfaces[order[i]]->hwaddr, + sysinfo->interfaces[order[i]]->hwaddr_size, + mac, sizeof(mac)); + config->autoboot_opts[i].uuid = talloc_strdup(config, mac); + } else { + /* device type */ + order[i] -= (sysinfo->n_blockdevs + sysinfo->n_interfaces); + config->autoboot_opts[i].boot_type = BOOT_DEVICE_TYPE; + config->autoboot_opts[i].type = order[i]; } } - talloc_free(config->boot_device); - config->boot_device = uuid ? talloc_strdup(config, uuid) : NULL; - str = widget_textbox_get_value(screen->widgets.timeout_f); if (str) { unsigned long x; @@ -367,35 +376,55 @@ static void config_screen_layout_widgets(struct config_screen *screen) help_x = screen->field_x + 2 + widget_width(widget_textbox_base(screen->widgets.dns_f)); - y += layout_pair(screen, y, screen->widgets.autoboot_l, - widget_select_base(screen->widgets.autoboot_f)); + y += 1; + + wl = widget_label_base(screen->widgets.boot_order_l); + widget_set_visible(wl, true); + widget_move(wl, y, screen->label_x); + + wf = widget_subset_base(screen->widgets.boot_order_f); + widget_move(wf, y, screen->field_x); + wl = widget_label_base(screen->widgets.boot_empty_l); + widget_move(wl, y, screen->field_x); - wf = widget_select_base(screen->widgets.boot_device_f); - if (screen->autoboot_type == AUTOBOOT_ONE) { + if (widget_subset_height(screen->widgets.boot_order_f)) { widget_set_visible(wf, true); - widget_move(wf, y, screen->field_x + 3); + widget_set_visible(wl, false); y += widget_height(wf); } else { + widget_set_visible(wl, true); widget_set_visible(wf, false); + y += 1; } y += 1; + widget_move(widget_button_base(screen->widgets.boot_add_b), + y, screen->field_x); + widget_move(widget_button_base(screen->widgets.boot_any_b), + y, screen->field_x + 12); + widget_move(widget_button_base(screen->widgets.boot_none_b), + y, screen->field_x + 30); + + wf = widget_button_base(screen->widgets.boot_add_b); + if (widget_subset_n_inactive(screen->widgets.boot_order_f)) + widget_set_visible(wf, true); + else + widget_set_visible(wf, false); + + y += 2; + wf = widget_textbox_base(screen->widgets.timeout_f); wl = widget_label_base(screen->widgets.timeout_l); wh = widget_label_base(screen->widgets.timeout_help_l); - if (screen->autoboot_type != AUTOBOOT_DISABLED) { - widget_set_visible(wl, true); - widget_set_visible(wf, true); - widget_set_visible(wh, true); + widget_set_visible(wl, screen->autoboot_enabled); + widget_set_visible(wf, screen->autoboot_enabled); + if (screen->autoboot_enabled) { + widget_set_visible(wh, screen->autoboot_enabled); widget_move(wl, y, screen->label_x); widget_move(wf, y, screen->field_x); widget_move(wh, y, screen->field_x + widget_width(wf) + 1); y += 2; - } else { - widget_set_visible(wl, false); - widget_set_visible(wf, false); - widget_set_visible(wh, false); } y += layout_pair(screen, y, screen->widgets.network_l, @@ -500,15 +529,69 @@ static void config_screen_network_change(void *arg, int value) widgetset_post(screen->widgetset); } -static void config_screen_autoboot_change(void *arg, int value) +static void config_screen_boot_order_change(void *arg, int value) +{ + (void)value; + struct config_screen *screen = arg; + widgetset_unpost(screen->widgetset); + config_screen_layout_widgets(screen); + widgetset_post(screen->widgetset); +} + +static void config_screen_add_device(void *arg) { struct config_screen *screen = arg; - screen->autoboot_type = value; + + screen->show_subset = true; + cui_show_subset(screen->cui, _("Select an option"), + screen->widgets.boot_order_f); +} + +static void config_screen_autoboot_none(void *arg) +{ + struct config_screen *screen = arg; + struct nc_widget_subset *subset = screen->widgets.boot_order_f; + + widget_subset_clear_active(subset); + screen->autoboot_enabled = false; + widgetset_unpost(screen->widgetset); config_screen_layout_widgets(screen); widgetset_post(screen->widgetset); } +static void config_screen_autoboot_any(void *arg) +{ + struct config_screen *screen = arg; + const struct system_info *sysinfo = screen->cui->sysinfo; + struct nc_widget_subset *subset = screen->widgets.boot_order_f; + int idx; + + widget_subset_clear_active(subset); + + idx = sysinfo->n_blockdevs + sysinfo->n_interfaces + DEVICE_TYPE_ANY; + + widget_subset_make_active(screen->widgets.boot_order_f, idx); + + screen->autoboot_enabled = true; + + widgetset_unpost(screen->widgetset); + config_screen_layout_widgets(screen); + widgetset_post(screen->widgetset); +} + +static void config_screen_update_subset(void *arg, + struct nc_widget_subset *subset, int idx) +{ + struct config_screen *screen = arg; + + if (idx >= 0) + widget_subset_make_active(subset, idx); + if (!screen->autoboot_enabled) + screen->autoboot_enabled = true; + config_screen_layout_widgets(screen); +} + static struct interface_config *first_active_interface( const struct config *config) { @@ -550,6 +633,31 @@ static void config_screen_setup_empty(struct config_screen *screen) cancel_click, screen); } +static int find_autoboot_idx(const struct system_info *sysinfo, + struct autoboot_option *opt) +{ + unsigned int i; + + if (opt->boot_type == BOOT_DEVICE_TYPE) + return sysinfo->n_blockdevs + sysinfo->n_interfaces + opt->type; + + for (i = 0; i < sysinfo->n_blockdevs; i++) { + if (!strcmp(sysinfo->blockdevs[i]->uuid, opt->uuid)) + return i; + } + + for (i = 0; i < sysinfo->n_interfaces; i++) { + struct interface_info *info = sysinfo->interfaces[i]; + char mac[20]; + + mac_str(info->hwaddr, info->hwaddr_size, mac, sizeof(mac)); + + if (!strcmp(mac, opt->uuid)) + return sysinfo->n_blockdevs + i; + } + + return -1; +} static void config_screen_setup_widgets(struct config_screen *screen, const struct config *config, @@ -560,7 +668,6 @@ static void config_screen_setup_widgets(struct config_screen *screen, char *str, *ip, *mask, *gw; enum net_conf_type type; unsigned int i; - bool found; build_assert(sizeof(screen->widgets) / sizeof(struct widget *) == N_FIELDS); @@ -568,81 +675,84 @@ static void config_screen_setup_widgets(struct config_screen *screen, type = screen->net_conf_type; ifcfg = first_active_interface(config); - screen->widgets.autoboot_l = widget_new_label(set, 0, 0, - _("Autoboot:")); - screen->widgets.autoboot_f = widget_new_select(set, 0, 0, 55); - - widget_select_on_change(screen->widgets.autoboot_f, - config_screen_autoboot_change, screen); - - screen->widgets.boot_device_f = widget_new_select(set, 0, 0, 55); - - widget_select_add_option(screen->widgets.autoboot_f, - AUTOBOOT_DISABLED, - _("Don't autoboot"), - screen->autoboot_type == - AUTOBOOT_DISABLED); - widget_select_add_option(screen->widgets.autoboot_f, - AUTOBOOT_ANY, - _("Autoboot from any " - "disk/network device"), - screen->autoboot_type == - AUTOBOOT_ANY); - widget_select_add_option(screen->widgets.autoboot_f, - AUTOBOOT_ONE, - _("Only autoboot from a specific " - "disk/network device"), - screen->autoboot_type == - AUTOBOOT_ONE); - - found = false; + screen->widgets.boot_add_b = widget_new_button(set, 0, 0, 10, + _("Add Device"), config_screen_add_device, + screen); + + screen->widgets.boot_none_b = widget_new_button(set, 0, 0, 10, + _("Clear"), + config_screen_autoboot_none, screen); + + screen->widgets.boot_any_b = widget_new_button(set, 0, 0, 16, + _("Clear & Boot Any"), config_screen_autoboot_any, + screen); + + screen->widgets.boot_order_l = widget_new_label(set, 0, 0, + _("Boot order:")); + screen->widgets.boot_order_f = widget_new_subset(set, 0, 0, + COLS - screen->field_x, + config_screen_update_subset); + screen->widgets.boot_empty_l = widget_new_label(set, 0, 0, + _("(None)")); + + widget_subset_on_change(screen->widgets.boot_order_f, + config_screen_boot_order_change, screen); for (i = 0; i < sysinfo->n_blockdevs; i++) { struct blockdev_info *bd = sysinfo->blockdevs[i]; - bool selected; char *label; - selected = config->boot_device && - !strcmp(config->boot_device, bd->uuid); - if (selected) - found = true; - label = talloc_asprintf(screen, _("disk: %s [uuid: %s]"), bd->name, bd->uuid); - widget_select_add_option(screen->widgets.boot_device_f, i, - label, selected); + widget_subset_add_option(screen->widgets.boot_order_f, label); } for (i = 0; i < sysinfo->n_interfaces; i++) { struct interface_info *info = sysinfo->interfaces[i]; char *label, mac[20]; - bool selected; mac_str(info->hwaddr, info->hwaddr_size, mac, sizeof(mac)); - selected = config->boot_device && - !strcmp(config->boot_device, mac); - if (selected) - found = true; label = talloc_asprintf(screen, _("net: %s [mac: %s]"), info->name, mac); - widget_select_add_option(screen->widgets.boot_device_f, - i + sysinfo->n_blockdevs, - label, selected); + widget_subset_add_option(screen->widgets.boot_order_f, label); } - if (screen->autoboot_type == AUTOBOOT_ONE && !found) { + for (i = DEVICE_TYPE_NETWORK; i < DEVICE_TYPE_NETWORK + 4; i++) { char *label; - label = talloc_asprintf(screen, _("Unknown UUID: %s"), - config->boot_device); + if (i == DEVICE_TYPE_ANY) + label = talloc_asprintf(screen, _("Any Device")); + else + label = talloc_asprintf(screen, _("Any %s device"), + device_type_display_name(i)); - widget_select_add_option(screen->widgets.boot_device_f, -1, - label, true); + widget_subset_add_option(screen->widgets.boot_order_f, label); } + screen->autoboot_enabled = config->n_autoboot_opts; + for (i = 0; i < config->n_autoboot_opts; i++) { + struct autoboot_option *opt = &config->autoboot_opts[i]; + int idx; + + idx = find_autoboot_idx(sysinfo, opt); + + if (idx >= 0) { + widget_subset_make_active(screen->widgets.boot_order_f, + idx); + } else { + if (opt->boot_type == BOOT_DEVICE_TYPE) + pb_log("%s: Unknown autoboot option: %d\n", + __func__, opt->type); + else + pb_log("%s: Unknown autoboot UUID: %s\n", + __func__, opt->uuid); + } + } + + str = talloc_asprintf(screen, "%d", config->autoboot_timeout_sec); screen->widgets.timeout_l = widget_new_label(set, 0, 0, _("Timeout:")); screen->widgets.timeout_f = widget_new_textbox(set, 0, 0, 5, str); @@ -818,12 +928,6 @@ static void config_screen_draw(struct config_screen *screen, config_screen_setup_empty(screen); } else { screen->net_conf_type = find_net_conf_type(config); - if (!config->autoboot_enabled) - screen->autoboot_type = AUTOBOOT_DISABLED; - else - screen->autoboot_type = config->boot_device ? - AUTOBOOT_ONE : AUTOBOOT_ANY; - config_screen_setup_widgets(screen, config, sysinfo); config_screen_layout_widgets(screen); } @@ -868,6 +972,8 @@ struct config_screen *config_screen_init(struct cui *cui, screen->label_x = 2; screen->field_x = 17; + screen->show_subset = false; + screen->scr.frame.ltitle = talloc_strdup(screen, _("Petitboot System Configuration")); screen->scr.frame.rtitle = NULL; -- cgit v1.2.1 From 893cfce3e9ea91a68e678829fb8e7e37102e2b41 Mon Sep 17 00:00:00 2001 From: Samuel Mendoza-Jonas Date: Thu, 29 Jan 2015 16:06:38 +1100 Subject: ui/ncurses: Update config screen help text Signed-off-by: Samuel Mendoza-Jonas --- ui/ncurses/nc-config-help.c | 27 +++++++++++++++------------ 1 file changed, 15 insertions(+), 12 deletions(-) (limited to 'ui') diff --git a/ui/ncurses/nc-config-help.c b/ui/ncurses/nc-config-help.c index c977d1a..22ced3c 100644 --- a/ui/ncurses/nc-config-help.c +++ b/ui/ncurses/nc-config-help.c @@ -1,23 +1,26 @@ #include "nc-helpscreen.h" struct help_text config_help_text = define_help_text("\ -Autoboot: There are three possible options for automatic-boot hehaviour:\n" +Autoboot: Specify which devices to autoboot from.\n" "\n" -"Don't autoboot: boot options will be listed in the petitboot menu, but none \ -will be booted automatically. User interaction will be required to continue \ -past the petitboot menu. Use this option if you want the machine to wait for \ -an explicit boot selection, or want to interact with petitboot before \ -booting the system\n" +"By selecting the 'Add Device' button new devices can be added to the autoboot \ +list, either by UUID, MAC address, or device type. Once added to the boot \ +order, the priority of devices can be changed with the 'left' and 'right' keys \ +Devices can be individually removed from the boot order with the minus key. \ +Use this option if you have multiple operating system images installed.\n" "\n" -"Autoboot from any disk/network device: any boot option that is marked as a \ -default (by bootloader configuration) will be booted automatically after a \ +"To autoboot from any device, select the 'Clear & Boot Any' button. \ +In this case, any boot option that is marked as a default \ +(by bootloader configuration) will be booted automatically after a \ timeout. Use this option if you want to quickly boot your system without \ changing any boot option settings. This is the typical configuration.\n" "\n" -"Only autoboot from a specific disk/network device: only boot options \ -from a single device (specifed here) will be booted automatically after a \ -timeout. Use this option if you have multiple operating system images \ -installed.\n" +"To disable autoboot, select the 'Clear' button, which will clear the boot \ +order. \ +With autoboot disabled, user interaction will be required to continue past \ +the petitboot menu. Use this option if you want the machine to wait for an \ +explicit boot selection, or want to interact with petitboot before booting \ +the system\n" "\n" "Timeout: Specify the length of time, in seconds, that the main menu will be \ displayed before the default boot option is started. This option is only \ -- cgit v1.2.1 From f497317729e7b2823a953f00c8301d192cda8824 Mon Sep 17 00:00:00 2001 From: Samuel Mendoza-Jonas Date: Tue, 6 Jan 2015 13:35:11 +1100 Subject: ui/ncurses: Display current ipmi bootdev settings Display the current IPMI bootdev override if it exists, and allow the user to clear it from the configuration screen. Signed-off-by: Samuel Mendoza-Jonas --- ui/ncurses/nc-config.c | 45 ++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 44 insertions(+), 1 deletion(-) (limited to 'ui') diff --git a/ui/ncurses/nc-config.c b/ui/ncurses/nc-config.c index 3156b9b..6164873 100644 --- a/ui/ncurses/nc-config.c +++ b/ui/ncurses/nc-config.c @@ -33,7 +33,7 @@ #include "nc-config.h" #include "nc-widgets.h" -#define N_FIELDS 29 +#define N_FIELDS 32 extern struct help_text config_help_text; @@ -65,6 +65,7 @@ struct config_screen { enum net_conf_type net_conf_type; bool autoboot_enabled; + bool ipmi_override; struct { struct nc_widget_label *boot_order_l; @@ -77,6 +78,10 @@ struct config_screen { struct nc_widget_label *timeout_l; struct nc_widget_label *timeout_help_l; + struct nc_widget_label *ipmi_type_l; + struct nc_widget_label *ipmi_clear_l; + struct nc_widget_checkbox *ipmi_clear_cb; + struct nc_widget_label *network_l; struct nc_widget_select *network_f; @@ -245,6 +250,11 @@ static int screen_process_form(struct config_screen *screen) config->autoboot_timeout_sec = x; } + if (screen->ipmi_override) + if (widget_checkbox_get_value(screen->widgets.ipmi_clear_cb)) + config->ipmi_bootdev = IPMI_BOOTDEV_INVALID; + + net_conf_type = widget_select_get_value(screen->widgets.network_f); /* if we don't have any network interfaces, prevent per-interface @@ -427,6 +437,23 @@ static void config_screen_layout_widgets(struct config_screen *screen) y += 2; } + if (screen->ipmi_override) { + wl = widget_label_base(screen->widgets.ipmi_type_l); + widget_set_visible(wl, true); + widget_move(wl, y, screen->label_x); + y += 1; + + wl = widget_label_base(screen->widgets.ipmi_clear_l); + wf = widget_checkbox_base(screen->widgets.ipmi_clear_cb); + widget_set_visible(wl, true); + widget_set_visible(wf, true); + widget_move(wl, y, screen->label_x); + widget_move(wf, y, screen->field_x); + y += 1; + } + + y += 2; + y += layout_pair(screen, y, screen->widgets.network_l, widget_select_base(screen->widgets.network_f)); @@ -762,6 +789,21 @@ static void config_screen_setup_widgets(struct config_screen *screen, widget_textbox_set_fixed_size(screen->widgets.timeout_f); widget_textbox_set_validator_integer(screen->widgets.timeout_f, 0, 999); + if (config->ipmi_bootdev) { + char *label = talloc_asprintf(screen, + _("%s IPMI boot option: %s"), + config->ipmi_bootdev_persistent ? + "Persistent" : "Temporary", + ipmi_bootdev_display_name(config->ipmi_bootdev)); + screen->widgets.ipmi_type_l = widget_new_label(set, 0, 0, + label); + screen->widgets.ipmi_clear_l = widget_new_label(set, 0, 0, + _("Clear option:")); + screen->widgets.ipmi_clear_cb = widget_new_checkbox(set, 0, 0, + false); + screen->ipmi_override = true; + } + screen->widgets.network_l = widget_new_label(set, 0, 0, _("Network:")); screen->widgets.network_f = widget_new_select(set, 0, 0, 50); @@ -972,6 +1014,7 @@ struct config_screen *config_screen_init(struct cui *cui, screen->label_x = 2; screen->field_x = 17; + screen->ipmi_override = false; screen->show_subset = false; screen->scr.frame.ltitle = talloc_strdup(screen, -- cgit v1.2.1 From a1b17961420c2d95a62d9003b75431f706e96753 Mon Sep 17 00:00:00 2001 From: Samuel Mendoza-Jonas Date: Fri, 8 May 2015 13:42:00 +1000 Subject: ui/ncurses: Toggle visibility of timeout help label Signed-off-by: Samuel Mendoza-Jonas --- ui/ncurses/nc-config.c | 1 + 1 file changed, 1 insertion(+) (limited to 'ui') diff --git a/ui/ncurses/nc-config.c b/ui/ncurses/nc-config.c index 6164873..911559d 100644 --- a/ui/ncurses/nc-config.c +++ b/ui/ncurses/nc-config.c @@ -429,6 +429,7 @@ static void config_screen_layout_widgets(struct config_screen *screen) wh = widget_label_base(screen->widgets.timeout_help_l); widget_set_visible(wl, screen->autoboot_enabled); widget_set_visible(wf, screen->autoboot_enabled); + widget_set_visible(wh, screen->autoboot_enabled); if (screen->autoboot_enabled) { widget_set_visible(wh, screen->autoboot_enabled); widget_move(wl, y, screen->label_x); -- cgit v1.2.1 From 7df003a0d222ae08bff62de4fefff1cf56628123 Mon Sep 17 00:00:00 2001 From: Samuel Mendoza-Jonas Date: Mon, 20 Apr 2015 17:12:04 +1000 Subject: ui/ncurses: Properly reposition cursor after menu update The currently selected item in the main menu can be set to an item off the visible portion of the menu after the additional or removal of a boot option. Update the currently selected item and/or the current view such that the item remains in the visible area. Signed-off-by: Samuel Mendoza-Jonas --- ui/ncurses/nc-cui.c | 30 ++++++++++++++++++++++++++++-- 1 file changed, 28 insertions(+), 2 deletions(-) (limited to 'ui') diff --git a/ui/ncurses/nc-cui.c b/ui/ncurses/nc-cui.c index 2fc779e..56e7653 100644 --- a/ui/ncurses/nc-cui.c +++ b/ui/ncurses/nc-cui.c @@ -184,6 +184,7 @@ static void cui_boot_editor_on_exit(struct cui *cui, { struct pmenu *menu = cui->main; struct cui_opt_data *cod; + int idx, top, rows, cols; static int user_idx = 0; /* Was the edit cancelled? */ @@ -222,6 +223,22 @@ static void cui_boot_editor_on_exit(struct cui *cui, /* Re-attach the items array. */ set_menu_items(menu->ncm, menu->items); + + /* If our index is above the current top row, align + * us to the new top. Otherwise, align us to the new + * bottom */ + menu_format(cui->main->ncm, &rows, &cols); + top = top_row(cui->main->ncm); + idx = item_index(item->nci); + + if (top >= idx) + top = idx; + else + top = idx < rows ? 0 : idx - rows + 1; + + set_top_row(cui->main->ncm, top); + set_current_item(item->pmenu->ncm, item->nci); + nc_scr_post(&menu->scr); } else { cod = item->data; @@ -229,7 +246,6 @@ static void cui_boot_editor_on_exit(struct cui *cui, cod->bd = talloc_steal(cod, bd); - set_current_item(item->pmenu->ncm, item->nci); out: cui_set_current(cui, &cui->main->scr); talloc_free(cui->boot_editor); @@ -562,7 +578,7 @@ static int cui_boot_option_add(struct device *dev, struct boot_option *opt, /* If our index is above the current top row, align * us to the new top. Otherwise, align us to the new * bottom */ - top = top < idx ? idx - rows : idx; + top = top < idx ? idx - rows + 1 : idx; set_top_row(cui->main->ncm, top); set_current_item(cui->main->ncm, selected); @@ -586,6 +602,7 @@ static void cui_device_remove(struct device *dev, void *arg) struct cui *cui = cui_from_arg(arg); struct boot_option *opt; unsigned int i; + int rows, cols, top, last; int result; pb_log("%s: %p %s\n", __func__, dev, dev->id); @@ -622,6 +639,15 @@ static void cui_device_remove(struct device *dev, void *arg) result = set_menu_items(cui->main->ncm, cui->main->items); + /* Move cursor to 'Exit' menu entry */ + menu_format(cui->main->ncm, &rows, &cols); + last = cui->main->item_count - 1; + set_current_item(cui->main->ncm, cui->main->items[last]); + if (!item_visible(cui->main->items[last])) { + top = last < rows ? 0 : last - rows + 1; + set_top_row(cui->main->ncm, top); + } + if (result) pb_log("%s: set_menu_items failed: %d\n", __func__, result); -- cgit v1.2.1