[RFC PATCH 8/9] ui/ncurses: Allow giving certain processes control of the display

Samuel Mendoza-Jonas sam at mendozajonas.com
Wed Feb 15 15:35:40 AEDT 2017


Add cui_run_external() which temporarily suspends ncurses before calling
a supplied process. This allows the user to interact directly with the
process, and then return to same place in the Petitboot UI.
While in this 'suspended' mode petitboot-nc must ensure it does not
attempt to draw to the screen. A small helper, nc_scr_active() is added
to make this easier to detect.

Signed-off-by: Samuel Mendoza-Jonas <sam at mendozajonas.com>
---
 ui/ncurses/nc-boot-editor.c |  3 +-
 ui/ncurses/nc-config.c      |  3 +-
 ui/ncurses/nc-cui.c         | 98 +++++++++++++++++++++++++++++++++++++++++----
 ui/ncurses/nc-cui.h         |  4 ++
 ui/ncurses/nc-scr.c         |  3 ++
 ui/ncurses/nc-sysinfo.c     |  4 +-
 6 files changed, 104 insertions(+), 11 deletions(-)

diff --git a/ui/ncurses/nc-boot-editor.c b/ui/ncurses/nc-boot-editor.c
index 7fa1a42..45c8f94 100644
--- a/ui/ncurses/nc-boot-editor.c
+++ b/ui/ncurses/nc-boot-editor.c
@@ -553,7 +553,8 @@ void boot_editor_update(struct boot_editor *boot_editor,
 {
 	int height;
 
-	if (boot_editor->cui->current != boot_editor_scr(boot_editor)) {
+	if (boot_editor->cui->current != boot_editor_scr(boot_editor) ||
+			boot_editor->cui->suspended) {
 		boot_editor->need_update = true;
 		return;
 	}
diff --git a/ui/ncurses/nc-config.c b/ui/ncurses/nc-config.c
index 8349629..a80c729 100644
--- a/ui/ncurses/nc-config.c
+++ b/ui/ncurses/nc-config.c
@@ -1182,7 +1182,8 @@ void config_screen_update(struct config_screen *screen,
 		const struct config *config,
 		const struct system_info *sysinfo)
 {
-	if (screen->cui->current != config_screen_scr(screen)) {
+	if (screen->cui->current != config_screen_scr(screen) ||
+			screen->cui->suspended) {
 		screen->need_update = true;
 		return;
 	}
diff --git a/ui/ncurses/nc-cui.c b/ui/ncurses/nc-cui.c
index fbc02b9..c80d354 100644
--- a/ui/ncurses/nc-cui.c
+++ b/ui/ncurses/nc-cui.c
@@ -59,6 +59,11 @@ static bool lockdown_active(void)
 	return lockdown;
 }
 
+static bool nc_scr_active(struct cui *cui, struct nc_scr *scr)
+{
+	return (!cui->suspended && cui->current == scr);
+}
+
 static void cui_start(void)
 {
 	initscr();			/* Initialize ncurses. */
@@ -161,6 +166,12 @@ int cui_run_cmd(struct pmenu_item *item)
 	struct cui *cui = cui_from_item(item);
 	const char **cmd_argv = item->data;
 
+	if (cui->suspended) {
+		pb_log("%s: tried to run command when curses not active\n",
+				__func__);
+		return -1;
+	}
+
 	nc_scr_status_printf(cui->current, _("Running %s..."), cmd_argv[0]);
 
 	def_prog_mode();
@@ -168,7 +179,9 @@ int cui_run_cmd(struct pmenu_item *item)
 	result = process_run_simple_argv(item, cmd_argv);
 
 	reset_prog_mode();
-	redrawwin(cui->current->main_ncw);
+
+	if (cui->current)
+		redrawwin(cui->current->main_ncw);
 
 	if (result) {
 		pb_log("%s: failed: '%s'\n", __func__, cmd_argv[0]);
@@ -525,6 +538,75 @@ static void cui_handle_resize(struct cui *cui)
 }
 
 /**
+ * cui_run_external_cb - Return from running an external program and
+ * re-activate ncurses mode.
+ */
+static void cui_run_external_cb(struct process *process)
+{
+	struct cui *cui = process->data;
+
+	reset_prog_mode();
+
+	if (cui->current && !cui->suspended) {
+		pb_debug("It's like running nothing at all...\n");
+		goto out;
+	}
+
+	if (!cui->current && cui->suspended) {
+		cui->current = cui->suspended;
+		cui->suspended = NULL;
+		goto out;
+	}
+
+	/* Something like a reinit or config update occured and required
+	 * switching to a different screen - unpost the old screen and move to
+	 * the new one */
+	// FIXME Safely free/clear old screen
+	if (cui->current && cui->suspended) {
+		nc_scr_unpost(cui->suspended);
+		nc_scr_post(cui->current);
+		cui->suspended = NULL;
+	}
+
+out:
+	refresh();
+	talloc_free(process);
+}
+
+/**
+ * Temporarily drop curses mode to run a synchronous process and give it
+ * control of the display.
+ * */
+int cui_run_external(struct cui *cui, struct process *process)
+{
+	int result;
+
+	pb_debug("Leaving curses mode to run '%s'\n", process->path);
+
+	process->data = cui;
+	process->exit_cb = cui_run_external_cb;
+
+	cui->suspended = cui->current;
+	cui->current = NULL;
+
+	def_prog_mode();
+	endwin();
+
+	result = process_run_async(process);
+
+	if (result) {
+		pb_log("%s: Running '%s' failed\n",
+				__func__, process->argv[0]);
+		refresh();
+		nc_scr_status_printf(cui->current, _("%s failed"),
+				process->argv[0]);
+		talloc_free(process);
+	}
+
+	return result;
+}
+
+/**
  * cui_device_add - Client device_add callback.
  *
  * Creates menu_items for all the device boot_options and inserts those
@@ -548,7 +630,7 @@ static int cui_boot_option_add(struct device *dev, struct boot_option *opt,
 	selected = current_item(cui->main->ncm);
 	menu_format(cui->main->ncm, &rows, &cols);
 
-	if (cui->current == &cui->main->scr)
+	if (nc_scr_active(cui, &cui->main->scr))
 		nc_scr_unpost(cui->current);
 
 	/* Check if the boot device is new */
@@ -631,7 +713,7 @@ static int cui_boot_option_add(struct device *dev, struct boot_option *opt,
 		set_current_item(cui->main->ncm, selected);
 	}
 
-	if (cui->current == &cui->main->scr)
+	if (nc_scr_active(cui, &cui->main->scr))
 		nc_scr_post(cui->current);
 
 	return 0;
@@ -654,7 +736,7 @@ static void cui_device_remove(struct device *dev, void *arg)
 
 	pb_log("%s: %p %s\n", __func__, dev, dev->id);
 
-	if (cui->current == &cui->main->scr)
+	if (nc_scr_active(cui, &cui->main->scr))
 		nc_scr_unpost(cui->current);
 
 	/* This disconnects items array from menu. */
@@ -704,7 +786,7 @@ static void cui_device_remove(struct device *dev, void *arg)
 			item_count(cui->main->ncm) + 1);
 	}
 
-	if (cui->current == &cui->main->scr)
+	if (nc_scr_active(cui, &cui->main->scr))
 		nc_scr_post(cui->current);
 }
 
@@ -715,7 +797,7 @@ static void cui_update_status(struct status *status, void *arg)
 	statuslog_append_steal(cui, cui->statuslog, status);
 
 	/* Ignore status messages from the backlog */
-	if (!status->backlog)
+	if (!status->backlog && !cui->suspended)
 		nc_scr_status_printf(cui->current, "%s", status->message);
 }
 
@@ -730,7 +812,7 @@ static void cui_update_mm_title(struct cui *cui)
 		frame->rtitle = talloc_asprintf_append(frame->rtitle,
 				" %s", cui->sysinfo->identifier);
 
-	if (cui->current == &cui->main->scr)
+	if (nc_scr_active(cui, &cui->main->scr))
 		nc_scr_post(cui->current);
 }
 
@@ -799,7 +881,7 @@ static void cui_update_config(struct config *config, void *arg)
 	if (cui->config_screen)
 		config_screen_update(cui->config_screen, config, cui->sysinfo);
 
-	if (config->safe_mode)
+	if (config->safe_mode && !cui->suspended)
 		nc_scr_status_printf(cui->current,
 				_("SAFE MODE: select '%s' to continue"),
 				_("Rescan devices"));
diff --git a/ui/ncurses/nc-cui.h b/ui/ncurses/nc-cui.h
index 418df71..2171304 100644
--- a/ui/ncurses/nc-cui.h
+++ b/ui/ncurses/nc-cui.h
@@ -25,6 +25,8 @@
 #include "nc-menu.h"
 #include "nc-helpscreen.h"
 
+struct process;
+
 struct cui_opt_data {
 	const char *name;
 	struct pb_boot_data *bd;
@@ -52,6 +54,7 @@ struct cui {
 	sig_atomic_t abort;
 	sig_atomic_t resize;
 	struct nc_scr *current;
+	struct nc_scr *suspended;
 	struct pmenu *main;
 	struct waitset *waitset;
 	struct discover_client *client;
@@ -91,6 +94,7 @@ 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);
 void cui_send_reinit(struct cui *cui);
+int cui_run_external(struct cui *cui, struct process *process);
 
 /* convenience routines */
 
diff --git a/ui/ncurses/nc-scr.c b/ui/ncurses/nc-scr.c
index a02627b..ff5eb2d 100644
--- a/ui/ncurses/nc-scr.c
+++ b/ui/ncurses/nc-scr.c
@@ -100,6 +100,9 @@ void nc_scr_status_printf(struct nc_scr *scr, const char *format, ...)
 {
 	va_list ap;
 
+	if (!scr)
+		return;
+
 	nc_scr_status_free(scr);
 
 	va_start(ap, format);
diff --git a/ui/ncurses/nc-sysinfo.c b/ui/ncurses/nc-sysinfo.c
index 8252b45..7319a0a 100644
--- a/ui/ncurses/nc-sysinfo.c
+++ b/ui/ncurses/nc-sysinfo.c
@@ -143,9 +143,11 @@ static void sysinfo_screen_populate(struct sysinfo_screen *screen,
 void sysinfo_screen_update(struct sysinfo_screen *screen,
 		const struct system_info *sysinfo)
 {
+	struct cui *cui = screen->text_scr.cui;
+
 	sysinfo_screen_populate(screen, sysinfo);
 
-	if (screen->text_scr.cui->help_screen)
+	if (cui->help_screen || cui->suspended)
 		screen->text_scr.need_update = true;
 	else
 		text_screen_draw(&screen->text_scr);
-- 
2.11.1



More information about the Petitboot mailing list