+ nc_scr_post(cui->current);
+ if (cui->current == &cui->plugin_menu->scr)
+ nc_scr_post(cui->current);
+}
+
+static void cui_update_status(struct status *status, void *arg)
+{
+ struct cui *cui = cui_from_arg(arg);
+
+ statuslog_append_steal(cui, cui->statuslog, status);
+
+ /* Ignore status messages from the backlog */
+ if (!status->backlog)
+ nc_scr_status_printf(cui->current, "%s", status->message);
+}
+
+/*
+ * Handle a new installed plugin option and update its associated
+ * (uninstalled) menu item if it exists.
+ */
+static int cui_plugin_option_add(struct plugin_option *opt, void *arg)
+{
+ struct cui_opt_data *cod;
+ struct cui *cui = cui_from_arg(arg);
+ struct pmenu_item *item = NULL;
+ struct boot_option *dummy_opt;
+ struct device *dev;
+ unsigned int i;
+ int result;
+
+fallback:
+ /* Find uninstalled plugin by matching on plugin_file */
+ for (i = 0; i < cui->plugin_menu->item_count; i++) {
+ item = item_userptr(cui->plugin_menu->items[i]);
+ if (!item)
+ continue;
+ cod = cod_from_item(item);
+ if (!cod || !cod->pd)
+ continue;
+ if (strncmp(cod->pd->plugin_file, opt->plugin_file,
+ strlen(cod->pd->plugin_file)) == 0)
+ break;
+ }
+
+ /*
+ * If pb-plugin was run manually there may not be an associated
+ * plugin-type boot_option. Pass a fake device and option to
+ * cui_boot_option_add() so we have an item to work with.
+ */
+ if (!item || i >= cui->plugin_menu->item_count) {
+ pb_log("New plugin option %s doesn't have a source item\n",
+ opt->id);
+ dev = talloc_zero(cui, struct device);
+ dev->id = dev->name = talloc_asprintf(dev, "(unknown)");
+ dev->type = DEVICE_TYPE_UNKNOWN;
+ dummy_opt = talloc_zero(cui, struct boot_option);
+ dummy_opt->device_id = talloc_strdup(dummy_opt, dev->id);
+ dummy_opt->id = dummy_opt->name = talloc_asprintf(dummy_opt, "dummy");
+ dummy_opt->boot_image_file = talloc_strdup(dummy_opt, opt->plugin_file);
+ dummy_opt->type = DISCOVER_PLUGIN_OPTION;
+ cui_boot_option_add(dev, dummy_opt, cui);
+ goto fallback;
+ }
+
+ /*
+ * If this option was faked above move the context under
+ * the item so it is cleaned up later in cui_plugins_remove().
+ */
+ if (strcmp(cod->opt->id, "dummy") == 0 &&
+ cod->dev->type == DEVICE_TYPE_UNKNOWN) {
+ talloc_steal(item, cod->dev);
+ talloc_steal(item, cod->opt);
+ }
+
+ talloc_free(cod->name);
+ /* Name is still tabbed across */
+ cod->name = talloc_asprintf(cod, _(" %s [installed]"), opt->name);
+
+ cod->pd->opt = opt;
+ item->on_execute = NULL;
+ item->on_edit = cui_show_plugin;
+
+ if (cui->current == &cui->plugin_menu->scr)
+ nc_scr_unpost(cui->current);
+
+ /* This disconnects items array from menu. */
+ result = set_menu_items(cui->plugin_menu->ncm, NULL);
+
+ if (result == E_OK)
+ pmenu_item_update(item, cod->name);
+
+ /* Re-attach the items array. */
+ result = set_menu_items(cui->plugin_menu->ncm, cui->plugin_menu->items);
+
+ if (cui->current == &cui->plugin_menu->scr)
+ nc_scr_post(cui->current);
+
+ return result;
+}
+
+/*
+ * Most plugin menu items will be removed via cui_device_remove(). However if
+ * pb-plugin has been run manually it is possible that there a plugin items
+ * not associated with a device - remove them here.
+ */
+static int cui_plugins_remove(void *arg)
+{
+ struct cui *cui = cui_from_arg(arg);
+ struct pmenu_item *item = NULL;
+ struct cui_opt_data *cod;
+ unsigned int i = 0;
+
+ pb_debug("%s\n", __func__);
+
+ if (cui->current == &cui->plugin_menu->scr)
+ nc_scr_unpost(cui->current);
+ if (cui->current == &cui->main->scr)
+ nc_scr_unpost(cui->current);
+
+ /* This disconnects items array from menu. */
+ set_menu_items(cui->plugin_menu->ncm, NULL);
+
+ while (i < cui->plugin_menu->item_count) {
+ item = item_userptr(cui->plugin_menu->items[i]);
+ if (!item || !item->data) {
+ i++;
+ continue;
+ }
+ cod = cod_from_item(item);
+ if (!cod->opt && !cod->dev) {
+ i++;
+ continue;
+ }
+
+ pmenu_remove(cui->plugin_menu, item);
+ /* plugin_menu entries will shift, jump to bottom to make sure
+ * we remove all plugin option items */
+ i = 0;
+ }
+
+ /* Re-attach the items array. */
+ set_menu_items(cui->plugin_menu->ncm, cui->plugin_menu->items);
+
+ set_menu_items(cui->main->ncm, NULL);
+ for (i = 0; i < cui->main->item_count; i++) {
+ item = item_userptr(cui->main->items[i]);
+ if (strncmp(item->nci->name.str, "Plugins", strlen("Plugins")))
+ continue;
+ cui->n_plugins = 0;
+ pmenu_item_update(item, "Plugins (0)");
+ break;
+ }
+
+ set_menu_items(cui->main->ncm, cui->main->items);
+
+ if (cui->current == &cui->main->scr)
+ nc_scr_post(cui->current);
+
+ /* If we're currently in a plugin screen jump back to the plugin menu */
+ if (cui->plugin_screen &&
+ cui->current == plugin_screen_scr(cui->plugin_screen))
+ cui_plugin_exit(cui);
+
+ return 0;
+}
+
+static void cui_update_mm_title(struct cui *cui)
+{
+ struct nc_frame *frame = &cui->main->scr.frame;
+
+ talloc_free(frame->rtitle);
+
+ frame->rtitle = talloc_strdup(cui->main, cui->sysinfo->type);
+ if (cui->sysinfo->identifier)
+ frame->rtitle = talloc_asprintf_append(frame->rtitle,
+ " %s", cui->sysinfo->identifier);
+
+ if (cui->current == &cui->main->scr)
+ nc_scr_post(cui->current);
+
+ frame = &cui->plugin_menu->scr.frame;
+
+ talloc_free(frame->rtitle);
+
+ frame->rtitle = talloc_strdup(cui->main, cui->sysinfo->type);
+ if (cui->sysinfo->identifier)
+ frame->rtitle = talloc_asprintf_append(frame->rtitle,
+ " %s", cui->sysinfo->identifier);
+
+ 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);
+ cui->sysinfo = talloc_steal(cui, sysinfo);
+
+ /* if we're currently displaying the system info screen, inform it
+ * of the updated information. */
+ if (cui->sysinfo_screen)
+ sysinfo_screen_update(cui->sysinfo_screen, sysinfo);
+
+ if (cui->subset_screen)
+ subset_screen_update(cui->subset_screen);
+
+ /* ... and do the same with the config screen... */
+ if (cui->config_screen)
+ config_screen_update(cui->config_screen, cui->config, sysinfo);
+
+ /* ... and the boot editor. */
+ if (cui->boot_editor)
+ boot_editor_update(cui->boot_editor, sysinfo);
+
+ cui_update_mm_title(cui);
+}
+
+static void cui_update_language(struct cui *cui, char *lang)
+{
+ bool repost_menu;
+ char *cur_lang;
+
+ cur_lang = setlocale(LC_ALL, NULL);
+ if (cur_lang && !strcmp(cur_lang, lang))
+ return;
+
+ setlocale(LC_ALL, lang);
+
+ /* we'll need to update the menu: drop all items and repopulate */
+ repost_menu = cui->current == &cui->main->scr ||
+ cui->current == &cui->plugin_menu->scr;
+ if (repost_menu)
+ nc_scr_unpost(cui->current);
+
+ talloc_free(cui->main);
+ cui->main = main_menu_init(cui);
+ cui->plugin_menu = plugin_menu_init(cui);
+
+ if (repost_menu) {
+ cui->current = &cui->main->scr;
+ nc_scr_post(cui->current);
+ }
+
+ discover_client_enumerate(cui->client);