From patchwork Fri Aug 25 05:59:38 2017 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Sam Mendoza-Jonas X-Patchwork-Id: 805759 Return-Path: X-Original-To: incoming@patchwork.ozlabs.org Delivered-To: patchwork-incoming@bilbo.ozlabs.org Received: from lists.ozlabs.org (lists.ozlabs.org [103.22.144.68]) (using TLSv1.2 with cipher ADH-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by ozlabs.org (Postfix) with ESMTPS id 3xdrBV3bnHz9sR9 for ; Fri, 25 Aug 2017 16:01:06 +1000 (AEST) Authentication-Results: ozlabs.org; dkim=fail reason="signature verification failed" (2048-bit key; unprotected) header.d=mendozajonas.com header.i=@mendozajonas.com header.b="VJ5Wy0ui"; dkim=fail reason="signature verification failed" (2048-bit key; unprotected) header.d=messagingengine.com header.i=@messagingengine.com header.b="j1Luu3Z/"; dkim-atps=neutral Received: from lists.ozlabs.org (lists.ozlabs.org [IPv6:2401:3900:2:1::3]) by lists.ozlabs.org (Postfix) with ESMTP id 3xdrBV29pmzDrVR for ; Fri, 25 Aug 2017 16:01:06 +1000 (AEST) Authentication-Results: lists.ozlabs.org; dkim=fail reason="signature verification failed" (2048-bit key; unprotected) header.d=mendozajonas.com header.i=@mendozajonas.com header.b="VJ5Wy0ui"; dkim=fail reason="signature verification failed" (2048-bit key; unprotected) header.d=messagingengine.com header.i=@messagingengine.com header.b="j1Luu3Z/"; dkim-atps=neutral X-Original-To: petitboot@lists.ozlabs.org Delivered-To: petitboot@lists.ozlabs.org Received: from out2-smtp.messagingengine.com (out2-smtp.messagingengine.com [66.111.4.26]) (using TLSv1.2 with cipher AECDH-AES256-SHA (256/256 bits)) (No client certificate requested) by lists.ozlabs.org (Postfix) with ESMTPS id 3xdr9L4kDbzDrSf for ; Fri, 25 Aug 2017 16:00:06 +1000 (AEST) Authentication-Results: lists.ozlabs.org; dkim=pass (2048-bit key; unprotected) header.d=mendozajonas.com header.i=@mendozajonas.com header.b="VJ5Wy0ui"; dkim=pass (2048-bit key; unprotected) header.d=messagingengine.com header.i=@messagingengine.com header.b="j1Luu3Z/"; dkim-atps=neutral Received: from compute2.internal (compute2.nyi.internal [10.202.2.42]) by mailout.nyi.internal (Postfix) with ESMTP id F1B8E2144E; Fri, 25 Aug 2017 02:00:03 -0400 (EDT) Received: from frontend2 ([10.202.2.161]) by compute2.internal (MEProxy); Fri, 25 Aug 2017 02:00:03 -0400 DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d= mendozajonas.com; h=cc:date:from:in-reply-to:message-id :references:subject:to:x-me-sender:x-me-sender:x-sasl-enc :x-sasl-enc; s=fm1; bh=o28eqgW1N4RYI2jwpK8/81e7Ra7UHCJtfwCqL70c6 Sw=; b=VJ5Wy0uiE7+L5zwB7h9yUvHhu1+9IJOuE9NRgP3YJ0xFHEdeuZt0KgWMW S/pdtyUotZVBT86HSsljsLvOzTSPNaSCpJtzQSg9pEeN4kwyMLycMNdTHhjLb6sY OMNRdNwnvu05zM3AaDPLLoIPaCro0t8rsCBbpfPCHvbj2tFBf20KyFvMdl2cxarG syY8tVu+xxjNsOYVZ18wCz2TYlzjZ5I5FyMTReUuHSmFEEup1VXIt90zp1srNe85 77RXdLtFyOQrN2CgAthns2yjt2qifqaGRp352qkIl4vO2FaS/3RBHndwu2y41EQ2 37ynKth6UKqMr0Tc+oQ2mRF96/ZAQ== DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d= messagingengine.com; h=cc:date:from:in-reply-to:message-id :references:subject:to:x-me-sender:x-me-sender:x-sasl-enc :x-sasl-enc; s=fm1; bh=o28eqgW1N4RYI2jwpK8/81e7Ra7UHCJtfwCqL70c6 Sw=; b=j1Luu3Z/CGFEXh1ucQIqkCgeqgziefKajbHBFkdV7wmpcb5OEoH1qTp3e LrxYmdbFCyChIENHsU7X6Susb/R1K65ueXKQ/4n9Kk8LyARpdSVU9TLYpArY1VbR B6ri7V7NNCht/ywcRNBnVc+jDtr2/+vvcjG/zMLuvKZWYxtYcH9fm+y1PMv+uMio MkICMzgLEEYsq5QjLCcz+MFjFFnCnusjUpZU+/5uQ3NzKga8yB6+BQ7dYi3+U0tX gbSzzxyLxbfviIxlOBemdaNjxtQdGKNBJBXzG6Op0m09xau6q6IJ1e9XG6hajo3u JiaE2eLx2Bn7yaoP0OglmSp5Buz2g== X-ME-Sender: X-Sasl-enc: Rf+DDY86anhIwI4xQJq/tVPH9AEjyeP8D8rOLiIMXyf3 1503640803 Received: from v4.ozlabs.ibm.com (unknown [122.99.82.10]) by mail.messagingengine.com (Postfix) with ESMTPA id 73F1B240B1; Fri, 25 Aug 2017 02:00:02 -0400 (EDT) From: Samuel Mendoza-Jonas To: petitboot@lists.ozlabs.org Subject: [PATCH 08/10] ui/ncurses: "Firmware Updates" screen Date: Fri, 25 Aug 2017 15:59:38 +1000 Message-Id: <20170825055940.24134-9-sam@mendozajonas.com> X-Mailer: git-send-email 2.14.0 In-Reply-To: <20170825055940.24134-1-sam@mendozajonas.com> References: <20170825055940.24134-1-sam@mendozajonas.com> X-BeenThere: petitboot@lists.ozlabs.org X-Mailman-Version: 2.1.23 Precedence: list List-Id: Petitboot bootloader development List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Cc: Samuel Mendoza-Jonas MIME-Version: 1.0 Errors-To: petitboot-bounces+incoming=patchwork.ozlabs.org@lists.ozlabs.org Sender: "Petitboot" The new "Firmware Updates" screen provides an interface for the user to specify where to retrieve metadata information from, optionally specify a particular update source, visually compare firmware versions, and execute an update. If the platform does not support updates or updates are otherwise disabled, this screen is empty aside from an informational message. Signed-off-by: Samuel Mendoza-Jonas --- ui/common/discover-client.c | 18 ++ ui/common/discover-client.h | 4 + ui/ncurses/Makefile.am | 5 +- ui/ncurses/nc-cui.c | 87 ++++++- ui/ncurses/nc-cui.h | 3 + ui/ncurses/nc-firmware-help.c | 4 + ui/ncurses/nc-firmware.c | 553 ++++++++++++++++++++++++++++++++++++++++++ ui/ncurses/nc-firmware.h | 34 +++ ui/ncurses/nc-scr.h | 1 + ui/ncurses/nc-widgets.c | 9 +- ui/ncurses/nc-widgets.h | 1 + 11 files changed, 715 insertions(+), 4 deletions(-) create mode 100644 ui/ncurses/nc-firmware-help.c create mode 100644 ui/ncurses/nc-firmware.c create mode 100644 ui/ncurses/nc-firmware.h diff --git a/ui/common/discover-client.c b/ui/common/discover-client.c index 8ad4611..ed51afc 100644 --- a/ui/common/discover-client.c +++ b/ui/common/discover-client.c @@ -452,3 +452,21 @@ int discover_client_send_plugin_install(struct discover_client *client, return pb_protocol_write_message(client->fd, message); } + +int discover_client_send_firmware_update(struct discover_client *client, + char *url) +{ + struct pb_protocol_message *message; + int len; + + len = pb_protocol_url_len(url); + + message = pb_protocol_create_message(client, + PB_PROTOCOL_ACTION_FIRMWARE_UPDATE, len); + if (!message) + return -1; + + pb_protocol_serialise_url(url, message->payload, len); + + return pb_protocol_write_message(client->fd, message); +} diff --git a/ui/common/discover-client.h b/ui/common/discover-client.h index 7224691..9352de1 100644 --- a/ui/common/discover-client.h +++ b/ui/common/discover-client.h @@ -102,4 +102,8 @@ int discover_client_send_url(struct discover_client *client, char *url); int discover_client_send_plugin_install(struct discover_client *client, char *file); +/* Send url to firmware image to the discover server */ +int discover_client_send_firmware_update(struct discover_client *client, + char *url); + #endif diff --git a/ui/ncurses/Makefile.am b/ui/ncurses/Makefile.am index 40e11b8..4418961 100644 --- a/ui/ncurses/Makefile.am +++ b/ui/ncurses/Makefile.am @@ -55,7 +55,10 @@ ui_ncurses_libpbnc_la_SOURCES = \ ui/ncurses/nc-plugin.c \ ui/ncurses/nc-plugin.h \ ui/ncurses/nc-plugin-help.c \ - ui/ncurses/nc-plugin-menu-help.c + ui/ncurses/nc-plugin-menu-help.c \ + ui/ncurses/nc-firmware.c \ + ui/ncurses/nc-firmware.h \ + ui/ncurses/nc-firmware-help.c sbin_PROGRAMS += ui/ncurses/petitboot-nc diff --git a/ui/ncurses/nc-cui.c b/ui/ncurses/nc-cui.c index 46b7839..835cef3 100644 --- a/ui/ncurses/nc-cui.c +++ b/ui/ncurses/nc-cui.c @@ -45,6 +45,7 @@ #include "nc-statuslog.h" #include "nc-subset.h" #include "nc-plugin.h" +#include "nc-firmware.h" extern const struct help_text main_menu_help_text; extern const struct help_text plugin_menu_help_text; @@ -392,6 +393,13 @@ static void cui_plugin_menu_exit(struct pmenu *menu) cui_set_current(cui, &cui->main->scr); } +static void cui_firmware_exit(struct cui *cui) +{ + cui_set_current(cui, &cui->main->scr); + talloc_free(cui->firmware_screen); + cui->firmware_screen = NULL; +} + void cui_show_add_url(struct cui *cui) { cui->add_url_screen = add_url_screen_init(cui, cui_add_url_exit); @@ -410,6 +418,13 @@ void cui_show_plugin(struct pmenu_item *item) cui_set_current(cui, plugin_screen_scr(cui->plugin_screen)); } +void cui_show_firmware(struct cui *cui) +{ + cui->firmware_screen = firmware_screen_init(cui, cui->config, + cui->sysinfo, cui_firmware_exit); + cui_set_current(cui, firmware_screen_scr(cui->firmware_screen)); +} + static void cui_help_exit(struct cui *cui) { cui_set_current(cui, help_screen_return_scr(cui->help_screen)); @@ -1018,6 +1033,44 @@ static void cui_update_mm_title(struct cui *cui) nc_scr_post(cui->current); } +static void cui_update_menu_item_name(struct cui *cui, const char *prefix, + const char *suffix) +{ + struct pmenu_item *item; + bool found = false; + unsigned int i; + char *label; + + if (cui->current == &cui->main->scr) + nc_scr_unpost(cui->current); + + if (set_menu_items(cui->main->ncm, NULL)) { + pb_log("%s: failed to unset menu items\n", __func__); + return; + } + + for (i = 0 ; i < cui->main->item_count; i++) { + item = item_userptr(cui->main->items[i]); + if (strncmp(item->nci->name.str, prefix, strlen(prefix))) + continue; + label = talloc_asprintf(item, "%s %s", prefix, suffix); + pmenu_item_update(item, label); + talloc_free(label); + found = true; + break; + } + + if (!found) + pb_log("Could not find item '%s' to apply suffix '%s'\n", + prefix, suffix); + + if (set_menu_items(cui->main->ncm, cui->main->items)) + pb_log("%s: failed to set menu items\n", __func__); + + if (cui->current == &cui->main->scr) + nc_scr_post(cui->current); +} + static void cui_update_sysinfo(struct system_info *sysinfo, void *arg) { struct cui *cui = cui_from_arg(arg); @@ -1039,6 +1092,16 @@ static void cui_update_sysinfo(struct system_info *sysinfo, void *arg) if (cui->boot_editor) boot_editor_update(cui->boot_editor, sysinfo); + if (cui->firmware_screen) + firmware_screen_update(cui->firmware_screen, cui->config, + sysinfo); + + /* Update the Firmware title if update occured */ + if (sysinfo->update_status == UPDATE_COMPLETED) + cui_update_menu_item_name(cui, "Firmware Updates", + "(Update Applied!)"); + + cui_update_mm_title(cui); } @@ -1085,6 +1148,10 @@ static void cui_update_config(struct config *config, void *arg) if (cui->config_screen) config_screen_update(cui->config_screen, config, cui->sysinfo); + if (cui->firmware_screen) + firmware_screen_update(cui->firmware_screen, config, + cui->sysinfo); + if (config->safe_mode) nc_scr_status_printf(cui->current, _("SAFE MODE: select '%s' to continue"), @@ -1106,6 +1173,11 @@ int cui_send_plugin_install(struct cui *cui, char *file) return discover_client_send_plugin_install(cui->client, file); } +int cui_send_firmware_update(struct cui *cui, char *url) +{ + return discover_client_send_firmware_update(cui->client, url); +} + void cui_send_reinit(struct cui *cui) { discover_client_send_reinit(cui->client); @@ -1156,6 +1228,13 @@ static int menu_plugin_execute(struct pmenu_item *item) return 0; } +static int menu_firmware_execute(struct pmenu_item *item) +{ + if (cui_from_item(item)->client) + cui_show_firmware(cui_from_item(item)); + return 0; +} + /** * pb_mm_init - Setup the main menu instance. */ @@ -1166,7 +1245,7 @@ static struct pmenu *main_menu_init(struct cui *cui) int result; bool lockdown = lockdown_active(); - m = pmenu_init(cui, 9, cui_on_exit); + m = pmenu_init(cui, 10, cui_on_exit); if (!m) { pb_log("%s: failed\n", __func__); return NULL; @@ -1216,12 +1295,16 @@ static struct pmenu *main_menu_init(struct cui *cui) i->on_execute = menu_plugin_execute; pmenu_item_insert(m, i, 7); + i = pmenu_item_create(m, _("Firmware Updates")); + i->on_execute = menu_firmware_execute; + pmenu_item_insert(m, i, 8); + if (lockdown) i = pmenu_item_create(m, _("Reboot")); else i = pmenu_item_create(m, _("Exit to shell")); i->on_execute = pmenu_exit_cb; - pmenu_item_insert(m, i, 8); + pmenu_item_insert(m, i, 9); result = pmenu_setup(m); diff --git a/ui/ncurses/nc-cui.h b/ui/ncurses/nc-cui.h index b310f4a..711c7f8 100644 --- a/ui/ncurses/nc-cui.h +++ b/ui/ncurses/nc-cui.h @@ -72,6 +72,7 @@ struct cui { struct help_screen *help_screen; struct subset_screen *subset_screen; struct statuslog_screen *statuslog_screen; + struct firmware_screen *firmware_screen; struct pjs *pjs; void *platform_info; unsigned int default_item; @@ -99,6 +100,8 @@ void cui_show_plugin_menu(struct cui *cui); int cui_send_config(struct cui *cui, struct config *config); int cui_send_url(struct cui *cui, char *url); int cui_send_plugin_install(struct cui *cui, char *file); +void cui_show_firmware(struct cui *item); +int cui_send_firmware_update(struct cui *cui, char *url); void cui_send_reinit(struct cui *cui); /* convenience routines */ diff --git a/ui/ncurses/nc-firmware-help.c b/ui/ncurses/nc-firmware-help.c new file mode 100644 index 0000000..7b9d072 --- /dev/null +++ b/ui/ncurses/nc-firmware-help.c @@ -0,0 +1,4 @@ +#include "nc-helpscreen.h" + +struct help_text firmware_help_text = define_help_text("\ +Useful help text here"); diff --git a/ui/ncurses/nc-firmware.c b/ui/ncurses/nc-firmware.c new file mode 100644 index 0000000..55fbcc9 --- /dev/null +++ b/ui/ncurses/nc-firmware.c @@ -0,0 +1,553 @@ +/* + * Copyright (C) 2017 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 + +#include "nc-cui.h" +#include "nc-firmware.h" +#include "nc-widgets.h" +#include "nc-menu.h" +#include "ui/common/discover-client.h" +#include "process/process.h" +#include "system/system.h" + +#define N_FIELDS 15 + +extern const struct help_text firmware_help_text; + +struct package_versions { + struct nc_widget_label *name; + struct nc_widget_label *current; + struct nc_widget_label *update; +}; + +struct firmware_screen { + struct nc_scr scr; + struct cui *cui; + struct nc_widgetset *widgetset; + WINDOW *pad; + + bool exit; + bool show_help; + bool need_redraw; + bool need_update; + void (*on_exit)(struct cui *); + + int label_x; + int field_x; + int scroll_y; + + unsigned int n_versions; + int current_x; + int update_x; + + struct { + struct nc_widget_label *source_l; + struct nc_widget_textbox *source_f; + struct nc_widget_button *source_update_b; + + struct nc_widget_label *custom_l; + struct nc_widget_textbox *custom_f; + struct nc_widget_button *custom_update_b; + + struct nc_widget_label *package_l; + struct nc_widget_label *current_l; + struct nc_widget_label *update_l; + struct package_versions *versions; + + struct nc_widget_label *status_l; + struct nc_widget_label *status_f; + struct nc_widget_label *safe_mode; + + struct nc_widget_button *help_b; + struct nc_widget_button *cancel_b; + } widgets; +}; + +static struct firmware_screen *firmware_screen_from_scr(struct nc_scr *scr) +{ + struct firmware_screen *firmware_screen; + + assert(scr->sig == pb_firmware_screen_sig); + firmware_screen = (struct firmware_screen *) + ((char *)scr - (size_t)&((struct firmware_screen *)0)->scr); + assert(firmware_screen->scr.sig == pb_firmware_screen_sig); + return firmware_screen; +} + +struct nc_scr *firmware_screen_scr(struct firmware_screen *screen) +{ + return &screen->scr; +} + +static void pad_refresh(struct firmware_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 firmware_screen_widget_focus(struct nc_widget *widget, void *arg) +{ + struct firmware_screen *screen = arg; + int w_y, w_height, w_focus, s_max, adjust; + + w_height = widget_height(widget); + w_focus = widget_focus_y(widget); + w_y = widget_y(widget) + w_focus; + 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) { + /* Fit as much of the widget into the screen as possible */ + adjust = min(s_max - 1, w_height - w_focus); + if (w_y + adjust >= screen->scroll_y + s_max) + screen->scroll_y = max(0, 1 + w_y + adjust - s_max); + } else + return; + + pad_refresh(screen); +} + +static void firmware_screen_process_key(struct nc_scr *scr, int key) +{ + struct firmware_screen *screen = firmware_screen_from_scr(scr); + bool handled; + + pb_log("%s\n", __func__); + + handled = widgetset_process_key(screen->widgetset, key); + + if (!handled) { + pb_log("Not handled by widgetset\n"); + switch (key) { + case 'x': + case 27: /* esc */ + screen->exit = true; + break; + case 'h': + screen->show_help = true; + break; + } + } + + if (screen->exit) { + screen->on_exit(screen->cui); + + } else if (screen->show_help) { + screen->show_help = false; + screen->need_redraw = true; + cui_show_help(screen->cui, _("Firmware Updates"), + &firmware_help_text); + + } else if (handled) { + pad_refresh(screen); + } +} + +static void help_click(void *arg) +{ + struct firmware_screen *screen = arg; + screen->show_help = true; +} + +static void cancel_click(void *arg) +{ + struct firmware_screen *screen = arg; + screen->exit = true; +} + +static void update_source_click(void *arg) +{ + struct firmware_screen *screen = arg; + struct config *config; + char *url; + int rc; + + config = config_copy(screen, screen->cui->config); + + url = widget_textbox_get_value(screen->widgets.source_f); + + talloc_free(config->metadata_source); + config->metadata_source = talloc_strdup(config, url); + + config->safe_mode = false; + + rc = cui_send_config(screen->cui, config); + talloc_free(config); + + if (rc) + pb_log("nc-firmware: Failed to send config!\n"); + + screen->exit = true; +} + +static void update_custom_click(void *arg) +{ + struct firmware_screen *screen = arg; + char *url = widget_textbox_get_value(screen->widgets.custom_f); + cui_send_firmware_update(screen->cui, url); + screen->exit = true; +} + +static void firmware_screen_setup_empty(struct firmware_screen *screen, + const struct config *config, const struct system_info *sysinfo) +{ + if (sysinfo && !sysinfo->update_support) + widget_new_label(screen->widgetset, 2, screen->field_x, + _("Firmware update not supported on this platform")); + else if (config && !config->platform_update) + widget_new_label(screen->widgetset, 2, screen->field_x, + _("Firmware update not enabled on this platform")); + else + widget_new_label(screen->widgetset, 2, screen->field_x, + _("Waiting for configuration data...")); + screen->widgets.cancel_b = widget_new_button(screen->widgetset, + 4, screen->field_x, 9, _("Cancel"), + cancel_click, screen); +} + +static void firmware_screen_setup_widgets(struct firmware_screen *screen, + const struct config *config, const struct system_info *sysinfo) +{ + struct nc_widgetset *set = screen->widgetset; + int len_package, len_current; + unsigned int i, j; + char *label; + + build_assert(sizeof(screen->widgets) / sizeof(struct widget *) + == N_FIELDS); + + screen->widgets.source_l = widget_new_label(set, 0, 0, + _("Metadata source:")); + screen->widgets.source_f = widget_new_textbox(set, 0, 0, + COLS - screen->field_x - 10, config->metadata_source); + + screen->widgets.source_update_b = widget_new_button(set, 0, 0, 40, + _("Set and check remote metadata file"), + update_source_click, screen); + + screen->widgets.custom_l = widget_new_label(set, 0, 0, "Update File:"); + screen->widgets.custom_f = widget_new_textbox(set, 0, 0, + COLS - screen->field_x - 10, config->update_source); + + screen->widgets.custom_update_b = widget_new_button(set, 0, 0, 30, + _("Update firmware"), + update_custom_click, screen); + + label = talloc_strdup(screen, + update_status_display_name(sysinfo->update_status)); + screen->widgets.status_l = widget_new_label(set, 0, 0, + _("Update Status:")); + screen->widgets.status_f = widget_new_label(set, 0, 0, label); + + screen->widgets.package_l = widget_new_label(set, 0, 0, + _("Packages")); + screen->widgets.current_l = widget_new_label(set, 0, 0, + _("Current")); + screen->widgets.update_l = widget_new_label(set, 0, 0, + _("Update")); + screen->widgets.versions = talloc_array(screen, + struct package_versions, sysinfo->n_primary); + if (!screen->widgets.versions) { + pb_log("Failed to alloc versions array!\n"); + goto skip_versions; + } + + screen->n_versions = sysinfo->n_primary; + len_package = len_current = 0; + /* Create current version package name and version labels */ + for (i = 0; i < sysinfo->n_primary; i++) { + screen->widgets.versions[i].name = widget_new_label(set, + 0, 0, sysinfo->platform_primary[i].name); + widget_set_selectable(widget_label_base( + screen->widgets.versions[i].name), + true); + screen->widgets.versions[i].current = widget_new_label(set, + 0, 0, sysinfo->platform_primary[i].version); + screen->widgets.versions[i].update = NULL; + if (strncols(sysinfo->platform_primary[i].name) > len_package) + len_package = strncols(sysinfo->platform_primary[i].name); + if (strncols(sysinfo->platform_primary[i].version) > len_current) + len_current = strncols(sysinfo->platform_primary[i].version); + } + + screen->current_x = screen->label_x + len_package + 2; + screen->update_x += screen->current_x + len_current + 2; + + /* Create update version labels, checking for missing packages */ + for (i = 0; i < sysinfo->n_new; i++) { + for (j = 0; j < sysinfo->n_primary; j++) { + if ((strncmp(sysinfo->platform_primary[j].name, + sysinfo->platform_new[i].name, + strlen(sysinfo->platform_new[i].name)) == 0) && + (strlen(sysinfo->platform_primary[j].name) == + strlen(sysinfo->platform_new[i].name))) + break; + } + if (j < sysinfo->n_primary) { + /* Corresponding current exists */ + screen->widgets.versions[j].update = widget_new_label(set, + 0, 0, sysinfo->platform_new[i].version); + continue; + } + + /* Package exists only in update versions */ + screen->widgets.versions = talloc_realloc(screen, + screen->widgets.versions, + struct package_versions, + j + 1); + if (!screen->widgets.versions) { + pb_log("Failed to alloc new update package!\n"); + break; + } + screen->n_versions++; + screen->widgets.versions[j].name = widget_new_label(set, + 0, 0, sysinfo->platform_new[i].name); + widget_set_selectable(widget_label_base( + screen->widgets.versions[j].name), + true); + screen->widgets.versions[j].current = NULL; + screen->widgets.versions[j].update = widget_new_label(set, + 0, 0, sysinfo->platform_new[i].version); + } + +skip_versions: + if (config->safe_mode) + screen->widgets.safe_mode = widget_new_label(set, 0, 0, + _("Selecting either option will exit safe mode")); + + screen->widgets.help_b = widget_new_button(set, 0, 0, 10, + _("Help"), help_click, screen); + screen->widgets.cancel_b = widget_new_button(set, 0, 0, 10, + _("Cancel"), cancel_click, screen); +} + +static int layout_pair(struct firmware_screen *screen, int y, + struct nc_widget_label *label, + struct nc_widget *field) +{ + struct nc_widget *label_w = widget_label_base(label); + widget_move(label_w, y, screen->label_x); + widget_move(field, y, screen->field_x); + return max(widget_height(label_w), widget_height(field)); +} + +static void firmware_screen_layout_widgets(struct firmware_screen *screen) +{ + unsigned int y, i; + + y = 1; + + y += layout_pair(screen, y, screen->widgets.source_l, + widget_textbox_base(screen->widgets.source_f)); + widget_move(widget_button_base(screen->widgets.source_update_b), y++, + screen->field_x); + + y +=1 ; + + y += layout_pair(screen, y, screen->widgets.custom_l, + widget_textbox_base(screen->widgets.custom_f)); + widget_move(widget_button_base(screen->widgets.custom_update_b), y++, + screen->field_x); + + y +=1 ; + + y += layout_pair(screen, y, screen->widgets.status_l, + widget_label_base(screen->widgets.status_f)); + + y += 1; + + widget_move(widget_label_base(screen->widgets.package_l), y, + screen->label_x); + widget_move(widget_label_base(screen->widgets.current_l), y, + screen->current_x); + widget_move(widget_label_base(screen->widgets.update_l), y, + screen->update_x); + + y += 1; + for (i = 0; i < screen->n_versions; i++) { + widget_move(widget_label_base(screen->widgets.versions[i].name), + y, screen->label_x); + if (screen->widgets.versions[i].current) + widget_move(widget_label_base( + screen->widgets.versions[i].current), + y, screen->current_x); + if (screen->widgets.versions[i].update) + widget_move(widget_label_base( + screen->widgets.versions[i].update), + y, screen->update_x); + y += 1; + + } + + y += 1; + + if (screen->cui->config->safe_mode) { + widget_move(widget_label_base(screen->widgets.safe_mode), + y, screen->label_x); + widget_set_visible(widget_label_base(screen->widgets.safe_mode), + true); + y += 1; + } + + y += 1; + + widget_move(widget_button_base(screen->widgets.help_b), y, + screen->field_x); + widget_move(widget_button_base(screen->widgets.cancel_b), y, + screen->field_x + 20); + + y += 1; +} + +static void firmware_screen_draw(struct firmware_screen *screen, + const struct config *config, const struct system_info *sysinfo) +{ + bool repost = false; + int height; + + height = N_FIELDS + 6 + (sysinfo->n_primary + 2) + (sysinfo->n_new + 2); + 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, + firmware_screen_widget_focus, screen); + + if (!config || !sysinfo || !sysinfo->update_support || + !config->platform_update) { + firmware_screen_setup_empty(screen, config, sysinfo); + } else { + firmware_screen_setup_widgets(screen, config, sysinfo); + firmware_screen_layout_widgets(screen); + } + + if (repost) + widgetset_post(screen->widgetset); +} + +void firmware_screen_update(struct firmware_screen *screen, + const struct config *config, const struct system_info *sysinfo) +{ + if (screen->cui->current != firmware_screen_scr(screen)) { + screen->need_update = true; + return; + } + + firmware_screen_draw(screen, config, sysinfo); + pad_refresh(screen); +} + +static int firmware_screen_post(struct nc_scr *scr) +{ + struct firmware_screen *screen = firmware_screen_from_scr(scr); + + widgetset_post(screen->widgetset); + + nc_scr_frame_draw(scr); + if (screen->need_redraw) { + redrawwin(scr->main_ncw); + screen->need_redraw = false; + } + wrefresh(screen->scr.main_ncw); + pad_refresh(screen); + return 0; +} + +static int firmware_screen_unpost(struct nc_scr *scr) +{ + widgetset_unpost(firmware_screen_from_scr(scr)->widgetset); + return 0; +} + +static void firmware_screen_resize(struct nc_scr *scr) +{ + /* FIXME: forms can't be resized, need to recreate here */ + firmware_screen_unpost(scr); + firmware_screen_post(scr); +} + +static int firmware_screen_destroy(void *arg) +{ + struct firmware_screen *screen = arg; + if (screen->pad) + delwin(screen->pad); + return 0; +} + +struct firmware_screen *firmware_screen_init(struct cui *cui, + const struct config *config, + const struct system_info *sysinfo, + void (*on_exit)(struct cui *)) +{ + struct firmware_screen *screen = talloc_zero(cui, struct firmware_screen); + talloc_set_destructor(screen, firmware_screen_destroy); + + nc_scr_init(&screen->scr, pb_firmware_screen_sig, 0, + cui, firmware_screen_process_key, + firmware_screen_post, firmware_screen_unpost, + firmware_screen_resize); + + + screen->cui = cui; + screen->on_exit = on_exit; + screen->label_x = 2; + screen->field_x = 25; + screen->need_redraw = false; + + screen->scr.frame.ltitle = talloc_strdup(screen, + _("Firmware Updates")); + screen->scr.frame.rtitle = NULL; + screen->scr.frame.help = talloc_strdup(screen, + _("tab=next, shift+tab=previous, x=exit, h=help")); + nc_scr_frame_draw(&screen->scr); + + scrollok(screen->scr.sub_ncw, true); + + firmware_screen_draw(screen, config, sysinfo); + + return screen; +} + diff --git a/ui/ncurses/nc-firmware.h b/ui/ncurses/nc-firmware.h new file mode 100644 index 0000000..38891c4 --- /dev/null +++ b/ui/ncurses/nc-firmware.h @@ -0,0 +1,34 @@ +/* + * Copyright (C) 2016 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_FIRMWARE_H +#define _NC_FIRMWARE_H + +#include "nc-cui.h" + +struct firmware_screen; + +struct firmware_screen *firmware_screen_init(struct cui *cui, + const struct config *config, + const struct system_info *sysinfo, + void (*on_exit)(struct cui *)); + +struct nc_scr *firmware_screen_scr(struct firmware_screen *screen); +void firmware_screen_update(struct firmware_screen *screen, + const struct config *config, const struct system_info *sysinfo); + +#endif /* defined _NC_FIRMWARE_H */ diff --git a/ui/ncurses/nc-scr.h b/ui/ncurses/nc-scr.h index 5671a6b..35ede11 100644 --- a/ui/ncurses/nc-scr.h +++ b/ui/ncurses/nc-scr.h @@ -50,6 +50,7 @@ enum pb_nc_sig { pb_add_url_screen_sig = 888, pb_subset_screen_sig = 101, pb_plugin_screen_sig = 202, + pb_firmware_screen_sig = 303, pb_removed_sig = -999, }; diff --git a/ui/ncurses/nc-widgets.c b/ui/ncurses/nc-widgets.c index 15cec80..7a318cb 100644 --- a/ui/ncurses/nc-widgets.c +++ b/ui/ncurses/nc-widgets.c @@ -194,7 +194,6 @@ static int label_destructor(void *ptr) return 0; } - struct nc_widget_label *widget_new_label(struct nc_widgetset *set, int y, int x, char *str) { @@ -1337,3 +1336,11 @@ int widget_focus_y(struct nc_widget *widget) return widget->focus_y; } +void widget_set_selectable(struct nc_widget *widget, bool selectable) +{ + if (selectable) + field_opts_on(widget->field, O_ACTIVE); + else + field_opts_off(widget->field, O_ACTIVE); +} + diff --git a/ui/ncurses/nc-widgets.h b/ui/ncurses/nc-widgets.h index 4b67da7..7d7e8b5 100644 --- a/ui/ncurses/nc-widgets.h +++ b/ui/ncurses/nc-widgets.h @@ -86,6 +86,7 @@ int widget_width(struct nc_widget *widget); int widget_y(struct nc_widget *widget); int widget_x(struct nc_widget *widget); int widget_focus_y(struct nc_widget *widget); +void widget_set_selectable(struct nc_widget *widget, bool selectable); /* widgetset API */ typedef void (*widget_focus_cb)(struct nc_widget *widget, void *arg);