]> git.proxmox.com Git - fwupd.git/commitdiff
Split out the console handling to a new module
authorRichard Hughes <richard@hughsie.com>
Fri, 10 Feb 2023 16:18:02 +0000 (16:18 +0000)
committerRichard Hughes <richard@hughsie.com>
Wed, 15 Feb 2023 16:16:30 +0000 (16:16 +0000)
This simplifies a lot of confusion.

13 files changed:
po/POTFILES.in
src/fu-console.c [new file with mode: 0644]
src/fu-console.h [new file with mode: 0644]
src/fu-progressbar.c [deleted file]
src/fu-progressbar.h [deleted file]
src/fu-self-test.c
src/fu-tool.c
src/fu-util-bios-setting.c
src/fu-util-bios-setting.h
src/fu-util-common.c
src/fu-util-common.h
src/fu-util.c
src/meson.build

index fb1a1d115cfdcac8a60d83436008669dff18d6ab..7feb0fecee82eefac1b642136d65f4397d2524ce 100644 (file)
@@ -8,11 +8,11 @@ plugins/tpm/fu-tpm-eventlog.c
 plugins/uefi-capsule/fu-uefi-capsule-plugin.c
 plugins/uefi-capsule/fu-uefi-tool.c
 plugins/uefi-dbx/fu-dbxtool.c
+src/fu-console.c
 src/fu-debug.c
 src/fu-engine-helper.c
 src/fu-main.c
 src/fu-offline.c
-src/fu-progressbar.c
 src/fu-remote-list.c
 src/fu-security-attr-common.c
 src/fu-tool.c
diff --git a/src/fu-console.c b/src/fu-console.c
new file mode 100644 (file)
index 0000000..933d292
--- /dev/null
@@ -0,0 +1,779 @@
+/*
+ * Copyright (C) 2017 Richard Hughes <richard@hughsie.com>
+ *
+ * SPDX-License-Identifier: LGPL-2.1+
+ */
+
+#define G_LOG_DOMAIN "FuProgressBar"
+
+#include "config.h"
+
+#include <glib/gi18n.h>
+#include <stdio.h>
+
+#include "fu-console.h"
+
+#ifdef _WIN32
+#include <wchar.h>
+#include <windows.h>
+#endif
+
+struct _FuConsole {
+       GObject parent_instance;
+       GMainContext *main_ctx;
+       FwupdStatus status;
+       gboolean spinner_count_up; /* width in visible chars */
+       guint spinner_idx;         /* width in visible chars */
+       guint length_percentage;   /* width in visible chars */
+       guint length_status;       /* width in visible chars */
+       guint percentage;
+       GSource *timer_source;
+       gint64 last_animated; /* monotonic */
+       GTimer *time_elapsed;
+       gdouble last_estimate;
+       gboolean interactive;
+       gboolean contents_to_clear;
+};
+
+G_DEFINE_TYPE(FuConsole, fu_console, G_TYPE_OBJECT)
+
+gboolean
+fu_console_setup(FuConsole *self, GError **error)
+{
+#ifdef _WIN32
+       HANDLE hOut;
+       DWORD dwMode = 0;
+
+       /* enable VT sequences */
+       hOut = GetStdHandle(STD_OUTPUT_HANDLE);
+       if (hOut == INVALID_HANDLE_VALUE) {
+               g_set_error(error,
+                           G_IO_ERROR,
+                           G_IO_ERROR_NOT_SUPPORTED,
+                           "failed to get stdout [%u]",
+                           (guint)GetLastError());
+               return FALSE;
+       }
+       if (!GetConsoleMode(hOut, &dwMode)) {
+               g_set_error(error,
+                           G_IO_ERROR,
+                           G_IO_ERROR_NOT_SUPPORTED,
+                           "failed to get mode [%u]",
+                           (guint)GetLastError());
+               return FALSE;
+       }
+       dwMode |= ENABLE_VIRTUAL_TERMINAL_PROCESSING;
+       if (!SetConsoleMode(hOut, dwMode)) {
+               g_set_error(error,
+                           G_IO_ERROR,
+                           G_IO_ERROR_NOT_SUPPORTED,
+                           "failed to set mode [%u]",
+                           (guint)GetLastError());
+               return FALSE;
+       }
+       if (!SetConsoleOutputCP(CP_UTF8)) {
+               g_set_error(error,
+                           G_IO_ERROR,
+                           G_IO_ERROR_NOT_SUPPORTED,
+                           "failed to set output UTF-8 [%u]",
+                           (guint)GetLastError());
+               return FALSE;
+       }
+       if (!SetConsoleCP(CP_UTF8)) {
+               g_set_error(error,
+                           G_IO_ERROR,
+                           G_IO_ERROR_NOT_SUPPORTED,
+                           "failed to set UTF-8 [%u]",
+                           (guint)GetLastError());
+               return FALSE;
+       }
+#else
+       if (isatty(fileno(stdout)) == 0) {
+               g_set_error_literal(error, G_IO_ERROR, G_IO_ERROR_NOT_SUPPORTED, "not a TTY");
+               return FALSE;
+       }
+#endif
+       /* success */
+       return TRUE;
+}
+
+static void
+fu_console_erase_line(FuConsole *self)
+{
+       if (!self->interactive)
+               return;
+       g_print("\033[G");
+}
+
+static void
+fu_console_reset_line(FuConsole *self)
+{
+       if (self->contents_to_clear) {
+               fu_console_erase_line(self);
+               g_print("\n");
+               self->contents_to_clear = FALSE;
+       }
+}
+
+void
+fu_console_print_kv(FuConsole *self, const gchar *title, const gchar *msg)
+{
+       gsize title_len;
+       g_auto(GStrv) lines = NULL;
+
+       if (msg == NULL)
+               return;
+       fu_console_reset_line(self);
+       g_print("%s:", title);
+
+       /* pad */
+       title_len = fu_strwidth(title) + 1;
+       lines = g_strsplit(msg, "\n", -1);
+       for (guint j = 0; lines[j] != NULL; j++) {
+               for (gsize i = title_len; i < 25; i++)
+                       g_print(" ");
+               g_print("%s\n", lines[j]);
+               title_len = 0;
+       }
+}
+
+guint
+fu_console_input_uint(FuConsole *self, guint maxnum)
+{
+       gint retval;
+       guint answer = 0;
+
+       do {
+               char buffer[64];
+
+               /* swallow the \n at end of line too */
+               if (!fgets(buffer, sizeof(buffer), stdin))
+                       break;
+               if (strlen(buffer) == sizeof(buffer) - 1)
+                       continue;
+
+               /* get a number */
+               retval = sscanf(buffer, "%u", &answer);
+
+               /* positive */
+               if (retval == 1 && answer <= maxnum)
+                       break;
+
+               /* TRANSLATORS: the user isn't reading the question */
+               fu_console_print_full(self,
+                                     FU_CONSOLE_PRINT_FLAG_NONE,
+                                     _("Please enter a number from 0 to %u: "),
+                                     maxnum);
+       } while (TRUE);
+       return answer;
+}
+
+gboolean
+fu_console_input_bool(FuConsole *self, gboolean def, const gchar *format, ...)
+{
+       va_list args;
+       g_autofree gchar *tmp = NULL;
+       g_autoptr(GString) str = g_string_new(NULL);
+
+       va_start(args, format);
+       tmp = g_strdup_vprintf(format, args);
+       va_end(args);
+
+       g_string_append_printf(str, "%s [%s]: ", tmp, def ? "Y|n" : "y|N");
+       fu_console_print_literal(self, str->str);
+
+       do {
+               char buffer[4];
+               if (!fgets(buffer, sizeof(buffer), stdin))
+                       continue;
+               if (strlen(buffer) == sizeof(buffer) - 1)
+                       continue;
+               if (g_strcmp0(buffer, "\n") == 0)
+                       return def;
+               buffer[0] = g_ascii_toupper(buffer[0]);
+               if (g_strcmp0(buffer, "Y\n") == 0)
+                       return TRUE;
+               if (g_strcmp0(buffer, "N\n") == 0)
+                       return FALSE;
+       } while (TRUE);
+       return FALSE;
+}
+
+static GPtrArray *
+fu_console_strsplit_words(const gchar *text, guint line_len)
+{
+       g_auto(GStrv) tokens = NULL;
+       g_autoptr(GPtrArray) lines = g_ptr_array_new_with_free_func(g_free);
+       g_autoptr(GString) curline = g_string_new(NULL);
+
+       /* sanity check */
+       if (text == NULL || text[0] == '\0')
+               return NULL;
+       if (line_len == 0)
+               return NULL;
+
+       /* tokenize the string */
+       tokens = g_strsplit(text, " ", -1);
+       for (guint i = 0; tokens[i] != NULL; i++) {
+               /* current line plus new token is okay */
+               if (curline->len + fu_strwidth(tokens[i]) < line_len) {
+                       g_string_append_printf(curline, "%s ", tokens[i]);
+                       continue;
+               }
+
+               /* too long, so remove space, add newline and dump */
+               if (curline->len > 0)
+                       g_string_truncate(curline, curline->len - 1);
+               g_ptr_array_add(lines, g_strdup(curline->str));
+               g_string_truncate(curline, 0);
+               g_string_append_printf(curline, "%s ", tokens[i]);
+       }
+
+       /* any incomplete line? */
+       if (curline->len > 0) {
+               g_string_truncate(curline, curline->len - 1);
+               g_ptr_array_add(lines, g_strdup(curline->str));
+       }
+       return g_steal_pointer(&lines);
+}
+
+static void
+fu_console_box_line(const gchar *start,
+                   const gchar *text,
+                   const gchar *end,
+                   const gchar *padding,
+                   guint width)
+{
+       guint offset = 0;
+       if (start != NULL) {
+               offset += fu_strwidth(start);
+               g_print("%s", start);
+       }
+       if (text != NULL) {
+               offset += fu_strwidth(text);
+               g_print("%s", text);
+       }
+       if (end != NULL)
+               offset += fu_strwidth(end);
+       for (guint i = offset; i < width; i++)
+               g_print("%s", padding);
+       if (end != NULL)
+               g_print("%s\n", end);
+}
+
+void
+fu_console_line(FuConsole *self, guint width)
+{
+       g_autoptr(GString) str = g_string_new_len(NULL, width);
+       for (guint i = 0; i < width; i++)
+               g_string_append(str, "─");
+       fu_console_print_literal(self, str->str);
+}
+
+void
+fu_console_box(FuConsole *self, const gchar *title, const gchar *body, guint width)
+{
+       /* nothing to do */
+       if (title == NULL && body == NULL)
+               return;
+
+       /* header */
+       fu_console_reset_line(self);
+       fu_console_box_line("╔", NULL, "╗", "═", width);
+
+       /* optional title */
+       if (title != NULL) {
+               g_autoptr(GPtrArray) lines = fu_console_strsplit_words(title, width - 4);
+               for (guint j = 0; j < lines->len; j++) {
+                       const gchar *line = g_ptr_array_index(lines, j);
+                       fu_console_box_line("║ ", line, " ║", " ", width);
+               }
+       }
+
+       /* join */
+       if (title != NULL && body != NULL)
+               fu_console_box_line("╠", NULL, "╣", "═", width);
+
+       /* optional body */
+       if (body != NULL) {
+               gboolean has_nonempty = FALSE;
+               g_auto(GStrv) split = g_strsplit(body, "\n", -1);
+               for (guint i = 0; split[i] != NULL; i++) {
+                       g_autoptr(GPtrArray) lines = fu_console_strsplit_words(split[i], width - 4);
+                       if (lines == NULL) {
+                               if (has_nonempty) {
+                                       fu_console_box_line("║ ", NULL, " ║", " ", width);
+                                       has_nonempty = FALSE;
+                               }
+                               continue;
+                       }
+                       for (guint j = 0; j < lines->len; j++) {
+                               const gchar *line = g_ptr_array_index(lines, j);
+                               fu_console_box_line("║ ", line, " ║", " ", width);
+                       }
+                       has_nonempty = TRUE;
+               }
+       }
+
+       /* footer */
+       fu_console_box_line("╚", NULL, "╝", "═", width);
+}
+
+static const gchar *
+fu_console_status_to_string(FwupdStatus status)
+{
+       switch (status) {
+       case FWUPD_STATUS_IDLE:
+               /* TRANSLATORS: daemon is inactive */
+               return _("Idle…");
+               break;
+       case FWUPD_STATUS_DECOMPRESSING:
+               /* TRANSLATORS: decompressing the firmware file */
+               return _("Decompressing…");
+               break;
+       case FWUPD_STATUS_LOADING:
+               /* TRANSLATORS: parsing the firmware information */
+               return _("Loading…");
+               break;
+       case FWUPD_STATUS_DEVICE_RESTART:
+               /* TRANSLATORS: restarting the device to pick up new F/W */
+               return _("Restarting device…");
+               break;
+       case FWUPD_STATUS_DEVICE_READ:
+               /* TRANSLATORS: reading from the flash chips */
+               return _("Reading…");
+               break;
+       case FWUPD_STATUS_DEVICE_WRITE:
+               /* TRANSLATORS: writing to the flash chips */
+               return _("Writing…");
+               break;
+       case FWUPD_STATUS_DEVICE_ERASE:
+               /* TRANSLATORS: erasing contents of the flash chips */
+               return _("Erasing…");
+               break;
+       case FWUPD_STATUS_DEVICE_VERIFY:
+               /* TRANSLATORS: verifying we wrote the firmware correctly */
+               return _("Verifying…");
+               break;
+       case FWUPD_STATUS_SCHEDULING:
+               /* TRANSLATORS: scheduling an update to be done on the next boot */
+               return _("Scheduling…");
+               break;
+       case FWUPD_STATUS_DOWNLOADING:
+               /* TRANSLATORS: downloading from a remote server */
+               return _("Downloading…");
+               break;
+       case FWUPD_STATUS_WAITING_FOR_AUTH:
+               /* TRANSLATORS: waiting for user to authenticate */
+               return _("Authenticating…");
+               break;
+       case FWUPD_STATUS_DEVICE_BUSY:
+               /* TRANSLATORS: waiting for device to do something */
+               return _("Waiting…");
+               break;
+       default:
+               break;
+       }
+
+       /* TRANSLATORS: current daemon status is unknown */
+       return _("Unknown");
+}
+
+static gboolean
+_fu_status_is_predictable(FwupdStatus status)
+{
+       if (status == FWUPD_STATUS_DEVICE_ERASE)
+               return TRUE;
+       if (status == FWUPD_STATUS_DEVICE_VERIFY)
+               return TRUE;
+       if (status == FWUPD_STATUS_DEVICE_READ)
+               return TRUE;
+       if (status == FWUPD_STATUS_DEVICE_WRITE)
+               return TRUE;
+       if (status == FWUPD_STATUS_DOWNLOADING)
+               return TRUE;
+       return FALSE;
+}
+
+static gboolean
+fu_console_estimate_ready(FuConsole *self, guint percentage)
+{
+       gdouble old;
+       gdouble elapsed;
+
+       /* now invalid */
+       if (percentage == 0 || percentage == 100) {
+               g_timer_start(self->time_elapsed);
+               self->last_estimate = 0;
+               return FALSE;
+       }
+
+       /* allow-list things that make sense... */
+       if (!_fu_status_is_predictable(self->status))
+               return FALSE;
+
+       old = self->last_estimate;
+       elapsed = g_timer_elapsed(self->time_elapsed, NULL);
+       self->last_estimate = elapsed / percentage * (100 - percentage);
+
+       /* estimate is ready if we have decreased */
+       return old > self->last_estimate;
+}
+
+static gchar *
+fu_console_time_remaining_str(FuConsole *self)
+{
+       /* less than 5 seconds remaining */
+       if (self->last_estimate < 5)
+               return NULL;
+
+       /* less than 60 seconds remaining */
+       if (self->last_estimate < 60) {
+               /* TRANSLATORS: time remaining for completing firmware flash */
+               return g_strdup(_("Less than one minute remaining"));
+       }
+
+       return g_strdup_printf(
+           /* TRANSLATORS: more than a minute */
+           ngettext("%.0f minute remaining", "%.0f minutes remaining", self->last_estimate / 60),
+           self->last_estimate / 60);
+}
+
+static void
+fu_console_refresh(FuConsole *self)
+{
+       const gchar *title;
+       guint i;
+       g_autoptr(GString) str = g_string_new(NULL);
+
+       /* sanity check */
+       if (self->status == FWUPD_STATUS_IDLE || self->status == FWUPD_STATUS_UNKNOWN)
+               return;
+
+       /* erase previous line */
+       fu_console_erase_line(self);
+
+       /* add status */
+       title = fu_console_status_to_string(self->status);
+       g_string_append(str, title);
+       for (i = fu_strwidth(str->str); i < self->length_status; i++)
+               g_string_append_c(str, ' ');
+
+       /* add console */
+       g_string_append(str, "[");
+       if (self->percentage > 0) {
+               for (i = 0; i < (self->length_percentage - 1) * self->percentage / 100; i++)
+                       g_string_append_c(str, '*');
+               for (i = i + 1; i < self->length_percentage; i++)
+                       g_string_append_c(str, ' ');
+       } else {
+               const gchar chars[] = {
+                   '-',
+                   '\\',
+                   '|',
+                   '/',
+               };
+               for (i = 0; i < self->spinner_idx; i++)
+                       g_string_append_c(str, ' ');
+               g_string_append_c(str, chars[i / 4 % G_N_ELEMENTS(chars)]);
+               for (i = i + 1; i < self->length_percentage - 1; i++)
+                       g_string_append_c(str, ' ');
+       }
+       g_string_append_c(str, ']');
+
+       /* once we have good data show an estimate of time remaining */
+       if (fu_console_estimate_ready(self, self->percentage)) {
+               g_autofree gchar *remaining = fu_console_time_remaining_str(self);
+               if (remaining != NULL)
+                       g_string_append_printf(str, " %s…", remaining);
+       }
+
+       /* dump to screen */
+       g_print("%s", str->str);
+       self->contents_to_clear = TRUE;
+}
+
+/**
+ * fu_console_print_full:
+ * @self: a #FuConsole
+ * @flags; a #FuConsolePrintFlags, e.g. %FU_CONSOLE_PRINT_FLAG_STDERR
+ * @text: string
+ *
+ * Clears the console, and prints the text.
+ **/
+void
+fu_console_print_full(FuConsole *self, FuConsolePrintFlags flags, const gchar *format, ...)
+{
+       va_list args;
+       g_autoptr(GString) str = g_string_new(NULL);
+
+       va_start(args, format);
+       g_string_append_vprintf(str, format, args);
+       va_end(args);
+
+       if (flags & FU_CONSOLE_PRINT_FLAG_WARNING) {
+               /* TRANSLATORS: this is a prefix on the console */
+               g_autofree gchar *fmt = fu_console_color_format(_("WARNING"), FU_CONSOLE_COLOR_RED);
+               g_string_prepend(str, ": ");
+               g_string_prepend(str, fmt);
+               flags |= FU_CONSOLE_PRINT_FLAG_STDERR;
+       }
+
+       fu_console_reset_line(self);
+       if (flags & FU_CONSOLE_PRINT_FLAG_STDERR) {
+               g_printerr("%s", str->str);
+       } else {
+               g_print("%s", str->str);
+       }
+}
+
+void
+fu_console_print_literal(FuConsole *self, const gchar *text)
+{
+       fu_console_reset_line(self);
+       g_print("%s\n", text);
+}
+
+/**
+ * fu_console_print:
+ * @self: a #FuConsole
+ * @text: string
+ *
+ * Clears the console, prints the text and prints a newline.
+ **/
+void
+fu_console_print(FuConsole *self, const gchar *format, ...)
+{
+       va_list args;
+       g_autofree gchar *tmp = NULL;
+
+       va_start(args, format);
+       tmp = g_strdup_vprintf(format, args);
+       va_end(args);
+       fu_console_print_literal(self, tmp);
+}
+
+/**
+ * fu_console_set_progress_title:
+ * @self: A #FuConsole
+ * @title: A string
+ *
+ * Sets console title
+ **/
+void
+fu_console_set_progress_title(FuConsole *self, const gchar *title)
+{
+       fu_console_erase_line(self);
+       g_print("%s\n", title);
+       fu_console_refresh(self);
+}
+
+/**
+ * fu_console_set_main_context:
+ * @self: A #FuConsole
+ * @main_ctx: (nullable): main context
+ *
+ * Sets console main context to use for animations.
+ **/
+void
+fu_console_set_main_context(FuConsole *self, GMainContext *main_ctx)
+{
+       self->main_ctx = g_main_context_ref(main_ctx);
+}
+
+static void
+fu_console_spin_inc(FuConsole *self)
+{
+       /* reset */
+       self->last_animated = g_get_monotonic_time();
+
+       /* up to down */
+       if (self->spinner_count_up) {
+               if (++self->spinner_idx > self->length_percentage - 3)
+                       self->spinner_count_up = FALSE;
+       } else {
+               if (--self->spinner_idx == 0)
+                       self->spinner_count_up = TRUE;
+       }
+}
+
+static gboolean
+fu_console_spin_cb(gpointer user_data)
+{
+       FuConsole *self = FU_CONSOLE(user_data);
+
+       /* move the spinner index up to down */
+       fu_console_spin_inc(self);
+
+       /* update the terminal */
+       fu_console_refresh(self);
+
+       return G_SOURCE_CONTINUE;
+}
+
+static void
+fu_console_spin_end(FuConsole *self)
+{
+       if (self->timer_source != NULL) {
+               g_source_destroy(self->timer_source);
+               self->timer_source = NULL;
+
+               /* reset when the spinner has been stopped */
+               g_timer_start(self->time_elapsed);
+       }
+
+       /* go back to the start when we next go into unknown percentage mode */
+       self->spinner_idx = 0;
+       self->spinner_count_up = TRUE;
+}
+
+static void
+fu_console_spin_start(FuConsole *self)
+{
+       if (self->timer_source != NULL)
+               g_source_destroy(self->timer_source);
+       self->timer_source = g_timeout_source_new(40);
+       g_source_set_callback(self->timer_source, fu_console_spin_cb, self, NULL);
+       g_source_attach(self->timer_source, self->main_ctx);
+}
+
+/**
+ * fu_console_set_progress:
+ * @self: A #FuConsole
+ * @status: A #FwupdStatus
+ * @percentage: unsigned integer
+ *
+ * Refreshes the progress bar with the new percentage and status.
+ **/
+void
+fu_console_set_progress(FuConsole *self, FwupdStatus status, guint percentage)
+{
+       g_return_if_fail(FU_IS_CONSOLE(self));
+
+       /* not useful */
+       if (status == FWUPD_STATUS_UNKNOWN)
+               return;
+
+       /* ignore duplicates */
+       if (self->status == status && self->percentage == percentage)
+               return;
+
+       /* cache */
+       self->status = status;
+       self->percentage = percentage;
+
+       /* dumb */
+       if (!self->interactive) {
+               g_printerr("%s: %u%%\n", fu_console_status_to_string(status), percentage);
+               return;
+       }
+
+       /* if the main loop isn't spinning and we've not had a chance to
+        * execute the callback just do the refresh now manually */
+       if (percentage == 0 && status != FWUPD_STATUS_IDLE &&
+           self->status != FWUPD_STATUS_UNKNOWN) {
+               if ((g_get_monotonic_time() - self->last_animated) / 1000 > 40) {
+                       fu_console_spin_inc(self);
+                       fu_console_refresh(self);
+               }
+       }
+
+       /* enable or disable the spinner timeout */
+       if (percentage > 0) {
+               fu_console_spin_end(self);
+       } else {
+               fu_console_spin_start(self);
+       }
+
+       /* update the terminal */
+       fu_console_refresh(self);
+}
+
+/**
+ * fu_console_set_interactive:
+ * @self: A #FuConsole
+ * @interactive: #gboolean
+ *
+ * Marks the console as interactive or not
+ **/
+void
+fu_console_set_interactive(FuConsole *self, gboolean interactive)
+{
+       g_return_if_fail(FU_IS_CONSOLE(self));
+       self->interactive = interactive;
+}
+
+/**
+ * fu_console_set_status_length:
+ * @self: A #FuConsole
+ * @len: unsigned integer
+ *
+ * Sets the width of the progressbar status, which must be greater that 3.
+ **/
+void
+fu_console_set_status_length(FuConsole *self, guint len)
+{
+       g_return_if_fail(FU_IS_CONSOLE(self));
+       g_return_if_fail(len > 3);
+       self->length_status = len;
+}
+
+/**
+ * fu_console_set_percentage_length:
+ * @self: A #FuConsole
+ * @len: unsigned integer
+ *
+ * Sets the width of the progressbar percentage, which must be greater that 3.
+ **/
+void
+fu_console_set_percentage_length(FuConsole *self, guint len)
+{
+       g_return_if_fail(FU_IS_CONSOLE(self));
+       g_return_if_fail(len > 3);
+       self->length_percentage = len;
+}
+
+static void
+fu_console_init(FuConsole *self)
+{
+       self->length_percentage = 40;
+       self->length_status = 25;
+       self->spinner_count_up = TRUE;
+       self->time_elapsed = g_timer_new();
+       self->interactive = TRUE;
+}
+
+static void
+fu_console_finalize(GObject *obj)
+{
+       FuConsole *self = FU_CONSOLE(obj);
+
+       fu_console_reset_line(self);
+       if (self->timer_source != 0)
+               g_source_destroy(self->timer_source);
+       if (self->main_ctx != NULL)
+               g_main_context_unref(self->main_ctx);
+       g_timer_destroy(self->time_elapsed);
+
+       G_OBJECT_CLASS(fu_console_parent_class)->finalize(obj);
+}
+
+static void
+fu_console_class_init(FuConsoleClass *klass)
+{
+       GObjectClass *object_class = G_OBJECT_CLASS(klass);
+       object_class->finalize = fu_console_finalize;
+}
+
+/**
+ * fu_console_new:
+ *
+ * Creates a new #FuConsole
+ **/
+FuConsole *
+fu_console_new(void)
+{
+       FuConsole *self;
+       self = g_object_new(FU_TYPE_CONSOLE, NULL);
+       return FU_CONSOLE(self);
+}
diff --git a/src/fu-console.h b/src/fu-console.h
new file mode 100644 (file)
index 0000000..d310661
--- /dev/null
@@ -0,0 +1,69 @@
+/*
+ * Copyright (C) 2017 Richard Hughes <richard@hughsie.com>
+ *
+ * SPDX-License-Identifier: LGPL-2.1+
+ */
+
+#pragma once
+
+#include <fwupdplugin.h>
+
+#define FU_TYPE_CONSOLE (fu_console_get_type())
+G_DECLARE_FINAL_TYPE(FuConsole, fu_console, FU, CONSOLE, GObject)
+
+typedef enum {
+       FU_CONSOLE_COLOR_BLACK = 30,
+       FU_CONSOLE_COLOR_RED = 31,
+       FU_CONSOLE_COLOR_GREEN = 32,
+       FU_CONSOLE_COLOR_YELLOW = 33,
+       FU_CONSOLE_COLOR_BLUE = 34,
+       FU_CONSOLE_COLOR_MAGENTA = 35,
+       FU_CONSOLE_COLOR_CYAN = 36,
+       FU_CONSOLE_COLOR_WHITE = 37,
+} FuConsoleColor;
+
+typedef enum {
+       FU_CONSOLE_PRINT_FLAG_NONE = 0,
+       FU_CONSOLE_PRINT_FLAG_STDERR = 1 << 0,
+       FU_CONSOLE_PRINT_FLAG_WARNING = 1 << 1,
+} FuConsolePrintFlags;
+
+gchar *
+fu_console_color_format(const gchar *text, FuConsoleColor fg_color);
+
+FuConsole *
+fu_console_new(void);
+gboolean
+fu_console_setup(FuConsole *self, GError **error);
+
+guint
+fu_console_input_uint(FuConsole *self, guint maxnum);
+gboolean
+fu_console_input_bool(FuConsole *self, gboolean def, const gchar *format, ...) G_GNUC_PRINTF(3, 4);
+
+void
+fu_console_print_full(FuConsole *self, FuConsolePrintFlags flags, const gchar *format, ...)
+    G_GNUC_PRINTF(3, 4);
+void
+fu_console_print(FuConsole *self, const gchar *format, ...) G_GNUC_PRINTF(2, 3);
+void
+fu_console_print_literal(FuConsole *self, const gchar *text);
+void
+fu_console_print_kv(FuConsole *self, const gchar *title, const gchar *msg);
+void
+fu_console_line(FuConsole *self, guint width);
+void
+fu_console_box(FuConsole *self, const gchar *title, const gchar *body, guint width);
+
+void
+fu_console_set_progress(FuConsole *self, FwupdStatus status, guint percentage);
+void
+fu_console_set_status_length(FuConsole *self, guint len);
+void
+fu_console_set_percentage_length(FuConsole *self, guint len);
+void
+fu_console_set_progress_title(FuConsole *self, const gchar *title);
+void
+fu_console_set_interactive(FuConsole *self, gboolean interactive);
+void
+fu_console_set_main_context(FuConsole *self, GMainContext *main_ctx);
diff --git a/src/fu-progressbar.c b/src/fu-progressbar.c
deleted file mode 100644 (file)
index f747c75..0000000
+++ /dev/null
@@ -1,475 +0,0 @@
-/*
- * Copyright (C) 2017 Richard Hughes <richard@hughsie.com>
- *
- * SPDX-License-Identifier: LGPL-2.1+
- */
-
-#define G_LOG_DOMAIN "FuProgressBar"
-
-#include "config.h"
-
-#include <glib/gi18n.h>
-
-#include "fu-progressbar.h"
-
-static void
-fu_progressbar_finalize(GObject *obj);
-
-struct _FuProgressbar {
-       GObject parent_instance;
-       GMainContext *main_ctx;
-       FwupdStatus status;
-       gboolean spinner_count_up; /* chars */
-       guint spinner_idx;         /* chars */
-       guint length_percentage;   /* chars */
-       guint length_status;       /* chars */
-       guint percentage;
-       GSource *timer_source;
-       gint64 last_animated; /* monotonic */
-       GTimer *time_elapsed;
-       gdouble last_estimate;
-       gboolean interactive;
-};
-
-G_DEFINE_TYPE(FuProgressbar, fu_progressbar, G_TYPE_OBJECT)
-
-static const gchar *
-fu_progressbar_status_to_string(FwupdStatus status)
-{
-       switch (status) {
-       case FWUPD_STATUS_IDLE:
-               /* TRANSLATORS: daemon is inactive */
-               return _("Idle…");
-               break;
-       case FWUPD_STATUS_DECOMPRESSING:
-               /* TRANSLATORS: decompressing the firmware file */
-               return _("Decompressing…");
-               break;
-       case FWUPD_STATUS_LOADING:
-               /* TRANSLATORS: parsing the firmware information */
-               return _("Loading…");
-               break;
-       case FWUPD_STATUS_DEVICE_RESTART:
-               /* TRANSLATORS: restarting the device to pick up new F/W */
-               return _("Restarting device…");
-               break;
-       case FWUPD_STATUS_DEVICE_READ:
-               /* TRANSLATORS: reading from the flash chips */
-               return _("Reading…");
-               break;
-       case FWUPD_STATUS_DEVICE_WRITE:
-               /* TRANSLATORS: writing to the flash chips */
-               return _("Writing…");
-               break;
-       case FWUPD_STATUS_DEVICE_ERASE:
-               /* TRANSLATORS: erasing contents of the flash chips */
-               return _("Erasing…");
-               break;
-       case FWUPD_STATUS_DEVICE_VERIFY:
-               /* TRANSLATORS: verifying we wrote the firmware correctly */
-               return _("Verifying…");
-               break;
-       case FWUPD_STATUS_SCHEDULING:
-               /* TRANSLATORS: scheduling an update to be done on the next boot */
-               return _("Scheduling…");
-               break;
-       case FWUPD_STATUS_DOWNLOADING:
-               /* TRANSLATORS: downloading from a remote server */
-               return _("Downloading…");
-               break;
-       case FWUPD_STATUS_WAITING_FOR_AUTH:
-               /* TRANSLATORS: waiting for user to authenticate */
-               return _("Authenticating…");
-               break;
-       case FWUPD_STATUS_DEVICE_BUSY:
-               /* TRANSLATORS: waiting for device to do something */
-               return _("Waiting…");
-               break;
-       default:
-               break;
-       }
-
-       /* TRANSLATORS: current daemon status is unknown */
-       return _("Unknown");
-}
-
-static void
-fu_progressbar_erase_line(FuProgressbar *self)
-{
-       if (!self->interactive)
-               return;
-       g_print("\033[G");
-}
-
-static gboolean
-_fu_status_is_predictable(FwupdStatus status)
-{
-       if (status == FWUPD_STATUS_DEVICE_ERASE)
-               return TRUE;
-       if (status == FWUPD_STATUS_DEVICE_VERIFY)
-               return TRUE;
-       if (status == FWUPD_STATUS_DEVICE_READ)
-               return TRUE;
-       if (status == FWUPD_STATUS_DEVICE_WRITE)
-               return TRUE;
-       if (status == FWUPD_STATUS_DOWNLOADING)
-               return TRUE;
-       return FALSE;
-}
-
-static gboolean
-fu_progressbar_estimate_ready(FuProgressbar *self, guint percentage)
-{
-       gdouble old;
-       gdouble elapsed;
-
-       /* now invalid */
-       if (percentage == 0 || percentage == 100) {
-               g_timer_start(self->time_elapsed);
-               self->last_estimate = 0;
-               return FALSE;
-       }
-
-       /* allow-list things that make sense... */
-       if (!_fu_status_is_predictable(self->status))
-               return FALSE;
-
-       old = self->last_estimate;
-       elapsed = g_timer_elapsed(self->time_elapsed, NULL);
-       self->last_estimate = elapsed / percentage * (100 - percentage);
-
-       /* estimate is ready if we have decreased */
-       return old > self->last_estimate;
-}
-
-static gchar *
-fu_progressbar_time_remaining_str(FuProgressbar *self)
-{
-       /* less than 5 seconds remaining */
-       if (self->last_estimate < 5)
-               return NULL;
-
-       /* less than 60 seconds remaining */
-       if (self->last_estimate < 60) {
-               /* TRANSLATORS: time remaining for completing firmware flash */
-               return g_strdup(_("Less than one minute remaining"));
-       }
-
-       return g_strdup_printf(
-           /* TRANSLATORS: more than a minute */
-           ngettext("%.0f minute remaining", "%.0f minutes remaining", self->last_estimate / 60),
-           self->last_estimate / 60);
-}
-
-static void
-fu_progressbar_refresh(FuProgressbar *self, FwupdStatus status, guint percentage)
-{
-       const gchar *title;
-       guint i;
-       gboolean is_idle_newline = FALSE;
-       g_autoptr(GString) str = g_string_new(NULL);
-
-       g_return_if_fail(percentage <= 100);
-
-       /* erase previous line */
-       fu_progressbar_erase_line(self);
-
-       /* add status */
-       if (status == FWUPD_STATUS_IDLE || status == FWUPD_STATUS_UNKNOWN) {
-               status = self->status;
-               is_idle_newline = TRUE;
-       }
-       if (percentage == 100)
-               is_idle_newline = TRUE;
-       title = fu_progressbar_status_to_string(status);
-       g_string_append(str, title);
-       for (i = fu_strwidth(str->str); i < self->length_status; i++)
-               g_string_append_c(str, ' ');
-
-       /* add progressbar */
-       g_string_append(str, "[");
-       if (percentage > 0) {
-               for (i = 0; i < (self->length_percentage - 1) * percentage / 100; i++)
-                       g_string_append_c(str, '*');
-               for (i = i + 1; i < self->length_percentage; i++)
-                       g_string_append_c(str, ' ');
-       } else {
-               const gchar chars[] = {
-                   '-',
-                   '\\',
-                   '|',
-                   '/',
-               };
-               for (i = 0; i < self->spinner_idx; i++)
-                       g_string_append_c(str, ' ');
-               g_string_append_c(str, chars[i / 4 % G_N_ELEMENTS(chars)]);
-               for (i = i + 1; i < self->length_percentage - 1; i++)
-                       g_string_append_c(str, ' ');
-       }
-       g_string_append_c(str, ']');
-
-       /* once we have good data show an estimate of time remaining */
-       if (fu_progressbar_estimate_ready(self, percentage)) {
-               g_autofree gchar *remaining = fu_progressbar_time_remaining_str(self);
-               if (remaining != NULL)
-                       g_string_append_printf(str, " %s…", remaining);
-       }
-
-       /* dump to screen */
-       g_print("%s", str->str);
-
-       /* done */
-       if (is_idle_newline) {
-               g_print("\n");
-               return;
-       }
-}
-
-/**
- * fu_progressbar_set_title:
- * @self: A #FuProgressbar
- * @title: A string
- *
- * Sets progressbar title
- *
- * Since: 0.9.7
- **/
-void
-fu_progressbar_set_title(FuProgressbar *self, const gchar *title)
-{
-       fu_progressbar_erase_line(self);
-       g_print("%s\n", title);
-       fu_progressbar_refresh(self, self->status, self->percentage);
-}
-
-/**
- * fu_progressbar_set_main_context:
- * @self: A #FuProgressbar
- * @main_ctx: (nullable): main context
- *
- * Sets progressbar main context to use for animations.
- *
- * Since: 1.6.2
- **/
-void
-fu_progressbar_set_main_context(FuProgressbar *self, GMainContext *main_ctx)
-{
-       self->main_ctx = g_main_context_ref(main_ctx);
-}
-
-static void
-fu_progressbar_spin_inc(FuProgressbar *self)
-{
-       /* reset */
-       self->last_animated = g_get_monotonic_time();
-
-       /* up to down */
-       if (self->spinner_count_up) {
-               if (++self->spinner_idx > self->length_percentage - 3)
-                       self->spinner_count_up = FALSE;
-       } else {
-               if (--self->spinner_idx == 0)
-                       self->spinner_count_up = TRUE;
-       }
-}
-
-static gboolean
-fu_progressbar_spin_cb(gpointer user_data)
-{
-       FuProgressbar *self = FU_PROGRESSBAR(user_data);
-
-       /* ignore */
-       if (self->status == FWUPD_STATUS_IDLE || self->status == FWUPD_STATUS_UNKNOWN)
-               return G_SOURCE_CONTINUE;
-
-       /* move the spinner index up to down */
-       fu_progressbar_spin_inc(self);
-
-       /* update the terminal */
-       fu_progressbar_refresh(self, self->status, self->percentage);
-
-       return G_SOURCE_CONTINUE;
-}
-
-static void
-fu_progressbar_spin_end(FuProgressbar *self)
-{
-       if (self->timer_source != NULL) {
-               g_source_destroy(self->timer_source);
-               self->timer_source = NULL;
-
-               /* reset when the spinner has been stopped */
-               g_timer_start(self->time_elapsed);
-       }
-
-       /* go back to the start when we next go into unknown percentage mode */
-       self->spinner_idx = 0;
-       self->spinner_count_up = TRUE;
-}
-
-static void
-fu_progressbar_spin_start(FuProgressbar *self)
-{
-       if (self->timer_source != NULL)
-               g_source_destroy(self->timer_source);
-       self->timer_source = g_timeout_source_new(40);
-       g_source_set_callback(self->timer_source, fu_progressbar_spin_cb, self, NULL);
-       g_source_attach(self->timer_source, self->main_ctx);
-}
-
-/**
- * fu_progressbar_update:
- * @self: A #FuProgressbar
- * @status: A #FwupdStatus
- * @percentage: unsigned integer
- *
- * Refreshes a progressbar
- *
- * Since: 0.9.7
- **/
-void
-fu_progressbar_update(FuProgressbar *self, FwupdStatus status, guint percentage)
-{
-       g_return_if_fail(FU_IS_PROGRESSBAR(self));
-
-       /* not useful */
-       if (status == FWUPD_STATUS_UNKNOWN)
-               return;
-
-       /* ignore initial client connection */
-       if (self->status == FWUPD_STATUS_UNKNOWN && status == FWUPD_STATUS_IDLE) {
-               self->status = status;
-               return;
-       }
-
-       if (!self->interactive) {
-               g_print("%s: %u%%\n", fu_progressbar_status_to_string(status), percentage);
-               self->status = status;
-               self->percentage = percentage;
-               return;
-       }
-
-       /* if the main loop isn't spinning and we've not had a chance to
-        * execute the callback just do the refresh now manually */
-       if (percentage == 0 && status != FWUPD_STATUS_IDLE &&
-           self->status != FWUPD_STATUS_UNKNOWN) {
-               if ((g_get_monotonic_time() - self->last_animated) / 1000 > 40) {
-                       fu_progressbar_spin_inc(self);
-                       fu_progressbar_refresh(self, status, percentage);
-               }
-       }
-
-       /* ignore duplicates */
-       if (self->status == status && self->percentage == percentage)
-               return;
-
-       /* enable or disable the spinner timeout */
-       if (percentage > 0) {
-               fu_progressbar_spin_end(self);
-       } else {
-               fu_progressbar_spin_start(self);
-       }
-
-       /* update the terminal */
-       fu_progressbar_refresh(self, status, percentage);
-
-       /* cache */
-       self->status = status;
-       self->percentage = percentage;
-}
-
-/**
- * fu_progressbar_set_interactive:
- * @self: A #FuProgressbar
- * @interactive: #gboolean
- *
- * Marks the progressbar as interactive or not
- *
- * Since: 0.9.7
- **/
-void
-fu_progressbar_set_interactive(FuProgressbar *self, gboolean interactive)
-{
-       g_return_if_fail(FU_IS_PROGRESSBAR(self));
-       self->interactive = interactive;
-}
-
-/**
- * fu_progressbar_set_length_status:
- * @self: A #FuProgressbar
- * @len: unsigned integer
- *
- * Sets the length of the progressbar status
- *
- * Since: 0.9.7
- **/
-void
-fu_progressbar_set_length_status(FuProgressbar *self, guint len)
-{
-       g_return_if_fail(FU_IS_PROGRESSBAR(self));
-       g_return_if_fail(len > 3);
-       self->length_status = len;
-}
-
-/**
- * fu_progressbar_set_length_percentage:
- * @self: A #FuProgressbar
- * @len: unsigned integer
- *
- * Sets the length of the progressba percentage
- *
- * Since: 0.9.7
- **/
-void
-fu_progressbar_set_length_percentage(FuProgressbar *self, guint len)
-{
-       g_return_if_fail(FU_IS_PROGRESSBAR(self));
-       g_return_if_fail(len > 3);
-       self->length_percentage = len;
-}
-
-static void
-fu_progressbar_class_init(FuProgressbarClass *klass)
-{
-       GObjectClass *object_class = G_OBJECT_CLASS(klass);
-       object_class->finalize = fu_progressbar_finalize;
-}
-
-static void
-fu_progressbar_init(FuProgressbar *self)
-{
-       self->length_percentage = 40;
-       self->length_status = 25;
-       self->spinner_count_up = TRUE;
-       self->time_elapsed = g_timer_new();
-       self->interactive = TRUE;
-}
-
-static void
-fu_progressbar_finalize(GObject *obj)
-{
-       FuProgressbar *self = FU_PROGRESSBAR(obj);
-
-       if (self->timer_source != 0)
-               g_source_destroy(self->timer_source);
-       if (self->main_ctx != NULL)
-               g_main_context_unref(self->main_ctx);
-       g_timer_destroy(self->time_elapsed);
-
-       G_OBJECT_CLASS(fu_progressbar_parent_class)->finalize(obj);
-}
-
-/**
- * fu_progressbar_new:
- *
- * Creates a new #FuProgressbar
- *
- * Since: 0.9.7
- **/
-FuProgressbar *
-fu_progressbar_new(void)
-{
-       FuProgressbar *self;
-       self = g_object_new(FU_TYPE_PROGRESSBAR, NULL);
-       return FU_PROGRESSBAR(self);
-}
diff --git a/src/fu-progressbar.h b/src/fu-progressbar.h
deleted file mode 100644 (file)
index 1abee0b..0000000
+++ /dev/null
@@ -1,27 +0,0 @@
-/*
- * Copyright (C) 2017 Richard Hughes <richard@hughsie.com>
- *
- * SPDX-License-Identifier: LGPL-2.1+
- */
-
-#pragma once
-
-#include <fwupdplugin.h>
-
-#define FU_TYPE_PROGRESSBAR (fu_progressbar_get_type())
-G_DECLARE_FINAL_TYPE(FuProgressbar, fu_progressbar, FU, PROGRESSBAR, GObject)
-
-FuProgressbar *
-fu_progressbar_new(void);
-void
-fu_progressbar_update(FuProgressbar *self, FwupdStatus status, guint percentage);
-void
-fu_progressbar_set_length_status(FuProgressbar *self, guint len);
-void
-fu_progressbar_set_length_percentage(FuProgressbar *self, guint len);
-void
-fu_progressbar_set_title(FuProgressbar *self, const gchar *title);
-void
-fu_progressbar_set_interactive(FuProgressbar *self, gboolean interactive);
-void
-fu_progressbar_set_main_context(FuProgressbar *self, GMainContext *main_ctx);
index 94b20051ce11374e969ed4cf74818e70b6d78cbd..dd6d683fb57e5155791682831e4ec541905ddceb 100644 (file)
@@ -21,6 +21,7 @@
 #include "fu-bios-settings-private.h"
 #include "fu-cabinet-common.h"
 #include "fu-config.h"
+#include "fu-console.h"
 #include "fu-context-private.h"
 #include "fu-device-list.h"
 #include "fu-device-private.h"
@@ -28,7 +29,6 @@
 #include "fu-history.h"
 #include "fu-plugin-list.h"
 #include "fu-plugin-private.h"
-#include "fu-progressbar.h"
 #include "fu-release-common.h"
 #include "fu-security-attr-common.h"
 #include "fu-smbios-private.h"
@@ -4089,31 +4089,31 @@ fu_memcpy_func(gconstpointer user_data)
 }
 
 static void
-fu_progressbar_func(gconstpointer user_data)
+fu_console_func(gconstpointer user_data)
 {
-       g_autoptr(FuProgressbar) progressbar = fu_progressbar_new();
+       g_autoptr(FuConsole) console = fu_console_new();
 
-       fu_progressbar_set_length_status(progressbar, 20);
-       fu_progressbar_set_length_percentage(progressbar, 50);
+       fu_console_set_status_length(console, 20);
+       fu_console_set_percentage_length(console, 50);
 
        g_print("\n");
        for (guint i = 0; i < 100; i++) {
-               fu_progressbar_update(progressbar, FWUPD_STATUS_DECOMPRESSING, i);
+               fu_console_set_progress(console, FWUPD_STATUS_DECOMPRESSING, i);
                g_usleep(10000);
        }
-       fu_progressbar_update(progressbar, FWUPD_STATUS_IDLE, 0);
+       fu_console_set_progress(console, FWUPD_STATUS_IDLE, 0);
        for (guint i = 0; i < 100; i++) {
                guint pc = (i > 25 && i < 75) ? 0 : i;
-               fu_progressbar_update(progressbar, FWUPD_STATUS_LOADING, pc);
+               fu_console_set_progress(console, FWUPD_STATUS_LOADING, pc);
                g_usleep(10000);
        }
-       fu_progressbar_update(progressbar, FWUPD_STATUS_IDLE, 0);
+       fu_console_set_progress(console, FWUPD_STATUS_IDLE, 0);
 
        for (guint i = 0; i < 5000; i++) {
-               fu_progressbar_update(progressbar, FWUPD_STATUS_LOADING, 0);
+               fu_console_set_progress(console, FWUPD_STATUS_LOADING, 0);
                g_usleep(1000);
        }
-       fu_progressbar_update(progressbar, FWUPD_STATUS_IDLE, 0);
+       fu_console_set_progress(console, FWUPD_STATUS_IDLE, 0);
 }
 
 static gint
@@ -4889,7 +4889,7 @@ main(int argc, char **argv)
 
        /* tests go here */
        if (g_test_slow()) {
-               g_test_add_data_func("/fwupd/progressbar", self, fu_progressbar_func);
+               g_test_add_data_func("/fwupd/console", self, fu_console_func);
        }
        g_test_add_data_func("/fwupd/backend{usb}", self, fu_backend_usb_func);
        g_test_add_data_func("/fwupd/backend{usb-invalid}", self, fu_backend_usb_invalid_func);
index acbcfe1be302fb7f0981d97de423434ad5502ca9..c97ba350266298bb4fb4ce806d943deac23d1812 100644 (file)
 
 #include "fu-bios-settings-private.h"
 #include "fu-cabinet.h"
+#include "fu-console.h"
 #include "fu-context-private.h"
 #include "fu-debug.h"
 #include "fu-device-private.h"
 #include "fu-engine.h"
 #include "fu-history.h"
 #include "fu-plugin-private.h"
-#include "fu-progressbar.h"
 #include "fu-security-attr-common.h"
 #include "fu-security-attrs-private.h"
 #include "fu-smbios-private.h"
@@ -66,7 +66,7 @@ struct FuUtilPrivate {
        FuEngine *engine;
        FuEngineRequest *request;
        FuProgress *progress;
-       FuProgressbar *progressbar;
+       FuConsole *console;
        FwupdClient *client;
        gboolean as_json;
        gboolean no_reboot_check;
@@ -94,9 +94,9 @@ fu_util_client_notify_cb(GObject *object, GParamSpec *pspec, FuUtilPrivate *priv
 {
        if (priv->as_json)
                return;
-       fu_progressbar_update(priv->progressbar,
-                             fwupd_client_get_status(priv->client),
-                             fwupd_client_get_percentage(priv->client));
+       fu_console_set_progress(priv->console,
+                               fwupd_client_get_status(priv->client),
+                               fwupd_client_get_percentage(priv->client));
 }
 
 static void
@@ -125,7 +125,6 @@ fu_util_show_plugin_warnings(FuUtilPrivate *priv)
        for (guint i = 0; i < 64; i++) {
                FwupdPluginFlags flag = (guint64)1 << i;
                const gchar *tmp;
-               g_autofree gchar *fmt = NULL;
                g_autofree gchar *url = NULL;
                g_autoptr(GString) str = g_string_new(NULL);
                if ((flags & flag) == 0)
@@ -133,17 +132,11 @@ fu_util_show_plugin_warnings(FuUtilPrivate *priv)
                tmp = fu_util_plugin_flag_to_string((guint64)1 << i);
                if (tmp == NULL)
                        continue;
-               /* TRANSLATORS: this is a prefix on the console */
-               fmt = fu_util_term_format(_("WARNING:"), FU_UTIL_TERM_COLOR_RED);
-               g_string_append_printf(str, "%s %s\n", fmt, tmp);
-
+               fu_console_print_full(priv->console, FU_CONSOLE_PRINT_FLAG_WARNING, "%s\n", tmp);
                url = g_strdup_printf("https://github.com/fwupd/fwupd/wiki/PluginFlag:%s",
                                      fwupd_plugin_flag_to_string(flag));
-               g_string_append(str, "  ");
                /* TRANSLATORS: %s is a link to a website */
-               g_string_append_printf(str, _("See %s for more information."), url);
-               g_string_append(str, "\n");
-               g_printerr("%s", str->str);
+               fu_console_print(priv->console, _("See %s for more information."), url);
        }
 }
 
@@ -234,7 +227,7 @@ fu_util_start_engine(FuUtilPrivate *priv,
        if (!fu_engine_load(priv->engine, flags, progress, error))
                return FALSE;
        fu_util_show_plugin_warnings(priv);
-       fu_util_show_unsupported_warn();
+       fu_util_show_unsupported_warning(priv->console);
 
        /* copy properties from engine to client */
        g_object_set(priv->client,
@@ -270,7 +263,7 @@ fu_util_cancelled_cb(GCancellable *cancellable, gpointer user_data)
 {
        FuUtilPrivate *priv = (FuUtilPrivate *)user_data;
        /* TRANSLATORS: this is when a device ctrl+c's a watch */
-       g_print("%s\n", _("Cancelled"));
+       fu_console_print_literal(priv->console, _("Cancelled"));
        g_main_loop_quit(priv->loop);
 }
 
@@ -290,7 +283,7 @@ fu_util_smbios_dump(FuUtilPrivate *priv, gchar **values, GError **error)
        if (!fu_smbios_setup_from_file(smbios, values[0], error))
                return FALSE;
        tmp = fu_firmware_to_string(FU_FIRMWARE(smbios));
-       g_print("%s\n", tmp);
+       fu_console_print_literal(priv->console, tmp);
        return TRUE;
 }
 
@@ -332,8 +325,8 @@ fu_util_private_free(FuUtilPrivate *priv)
                g_main_loop_unref(priv->loop);
        if (priv->cancellable != NULL)
                g_object_unref(priv->cancellable);
-       if (priv->progressbar != NULL)
-               g_object_unref(priv->progressbar);
+       if (priv->console != NULL)
+               g_object_unref(priv->console);
        if (priv->progress != NULL)
                g_object_unref(priv->progress);
        if (priv->context != NULL)
@@ -366,9 +359,9 @@ fu_util_update_device_request_cb(FwupdClient *client, FwupdRequest *request, FuU
                g_autofree gchar *tmp = NULL;
 
                /* TRANSLATORS: the user needs to do something, e.g. remove the device */
-               fmt = fu_util_term_format(_("Action Required:"), FU_UTIL_TERM_COLOR_RED);
+               fmt = fu_console_color_format(_("Action Required:"), FU_CONSOLE_COLOR_RED);
                tmp = g_strdup_printf("%s %s", fmt, fwupd_request_get_message(request));
-               fu_progressbar_set_title(priv->progressbar, tmp);
+               fu_console_set_progress_title(priv->console, tmp);
        }
 
        /* save for later */
@@ -395,7 +388,7 @@ fu_main_engine_status_changed_cb(FuEngine *engine, FwupdStatus status, FuUtilPri
 {
        if (priv->as_json)
                return;
-       fu_progressbar_update(priv->progressbar, status, 0);
+       fu_console_set_progress(priv->console, status, 0);
 }
 
 static void
@@ -403,7 +396,7 @@ fu_util_progress_percentage_changed_cb(FuProgress *progress, guint percentage, F
 {
        if (priv->as_json)
                return;
-       fu_progressbar_update(priv->progressbar, fu_progress_get_status(progress), percentage);
+       fu_console_set_progress(priv->console, fu_progress_get_status(progress), percentage);
 }
 
 static void
@@ -411,7 +404,7 @@ fu_util_progress_status_changed_cb(FuProgress *progress, FwupdStatus status, FuU
 {
        if (priv->as_json)
                return;
-       fu_progressbar_update(priv->progressbar, status, fu_progress_get_percentage(progress));
+       fu_console_set_progress(priv->console, status, fu_progress_get_percentage(progress));
 }
 
 static gboolean
@@ -445,7 +438,7 @@ fu_util_get_plugins_as_json(FuUtilPrivate *priv, GPtrArray *plugins, GError **er
        }
        json_builder_end_array(builder);
        json_builder_end_object(builder);
-       return fu_util_print_builder(builder, error);
+       return fu_util_print_builder(priv->console, builder, error);
 }
 
 static gboolean
@@ -467,11 +460,11 @@ fu_util_get_plugins(FuUtilPrivate *priv, gchar **values, GError **error)
        for (guint i = 0; i < plugins->len; i++) {
                FuPlugin *plugin = g_ptr_array_index(plugins, i);
                g_autofree gchar *str = fu_util_plugin_to_string(FWUPD_PLUGIN(plugin), 0);
-               g_print("%s\n", str);
+               fu_console_print_literal(priv->console, str);
        }
        if (plugins->len == 0) {
                /* TRANSLATORS: nothing found */
-               g_print("%s\n", _("No plugins found"));
+               fu_console_print_literal(priv->console, _("No plugins found"));
        }
 
        return TRUE;
@@ -531,8 +524,12 @@ fu_util_prompt_for_device(FuUtilPrivate *priv, GPtrArray *devices_opt, GError **
        if (devices_filtered->len == 1) {
                dev = g_ptr_array_index(devices_filtered, 0);
                if (!priv->as_json) {
-                       /* TRANSLATORS: device has been chosen by the daemon for the user */
-                       g_print("%s: %s\n", _("Selected device"), fu_device_get_name(dev));
+                       fu_console_print(
+                           priv->console,
+                           "%s: %s",
+                           /* TRANSLATORS: device has been chosen by the daemon for the user */
+                           _("Selected device"),
+                           fu_device_get_name(dev));
                }
                return g_object_ref(dev);
        }
@@ -547,14 +544,18 @@ fu_util_prompt_for_device(FuUtilPrivate *priv, GPtrArray *devices_opt, GError **
        }
 
        /* TRANSLATORS: get interactive prompt */
-       g_print("%s\n", _("Choose a device:"));
+       fu_console_print_literal(priv->console, _("Choose a device:"));
        /* TRANSLATORS: this is to abort the interactive prompt */
-       g_print("0.\t%s\n", _("Cancel"));
+       fu_console_print(priv->console, "0.\t%s", _("Cancel"));
        for (guint i = 0; i < devices_filtered->len; i++) {
                dev = g_ptr_array_index(devices_filtered, i);
-               g_print("%u.\t%s (%s)\n", i + 1, fu_device_get_id(dev), fu_device_get_name(dev));
+               fu_console_print(priv->console,
+                                "%u.\t%s (%s)",
+                                i + 1,
+                                fu_device_get_id(dev),
+                                fu_device_get_name(dev));
        }
-       idx = fu_util_prompt_for_number(devices_filtered->len);
+       idx = fu_console_input_uint(priv->console, devices_filtered->len);
        if (idx == 0) {
                g_set_error_literal(error,
                                    FWUPD_ERROR,
@@ -666,20 +667,23 @@ fu_util_get_updates(FuUtilPrivate *priv, gchar **values, GError **error)
 
        /* devices that have no updates available for whatever reason */
        if (devices_no_support->len > 0) {
-               /* TRANSLATORS: message letting the user know no device upgrade
-                * available due to missing on LVFS */
-               g_printerr("%s\n", _("Devices with no available firmware updates: "));
+               fu_console_print_literal(priv->console,
+                                        /* TRANSLATORS: message letting the user know no device
+                                         * upgrade available due to missing on LVFS */
+                                        _("Devices with no available firmware updates: "));
                for (guint i = 0; i < devices_no_support->len; i++) {
                        FwupdDevice *dev = g_ptr_array_index(devices_no_support, i);
-                       g_printerr(" • %s\n", fwupd_device_get_name(dev));
+                       fu_console_print(priv->console, " • %s", fwupd_device_get_name(dev));
                }
        }
        if (devices_no_upgrades->len > 0) {
-               /* TRANSLATORS: message letting the user know no device upgrade available */
-               g_printerr("%s\n", _("Devices with the latest available firmware version:"));
+               fu_console_print_literal(
+                   priv->console,
+                   /* TRANSLATORS: message letting the user know no device upgrade available */
+                   _("Devices with the latest available firmware version:"));
                for (guint i = 0; i < devices_no_upgrades->len; i++) {
                        FwupdDevice *dev = g_ptr_array_index(devices_no_upgrades, i);
-                       g_printerr(" • %s\n", fwupd_device_get_name(dev));
+                       fu_console_print(priv->console, " • %s", fwupd_device_get_name(dev));
                }
        }
 
@@ -692,7 +696,7 @@ fu_util_get_updates(FuUtilPrivate *priv, gchar **values, GError **error)
                return FALSE;
        }
 
-       fu_util_print_tree(priv->client, root);
+       fu_util_print_tree(priv->console, priv->client, root);
        return TRUE;
 }
 
@@ -750,7 +754,7 @@ fu_util_get_details(FuUtilPrivate *priv, gchar **values, GError **error)
                if (rel != NULL)
                        g_node_append_data(child, rel);
        }
-       fu_util_print_tree(priv->client, root);
+       fu_util_print_tree(priv->console, priv->client, root);
 
        return TRUE;
 }
@@ -771,7 +775,7 @@ fu_util_get_device_flags(FuUtilPrivate *priv, gchar **values, GError **error)
                g_string_append(str, " ~");
                g_string_append(str, tmp);
        }
-       g_print("%s\n", str->str);
+       fu_console_print_literal(priv->console, str->str);
 
        return TRUE;
 }
@@ -817,11 +821,12 @@ fu_util_get_devices(FuUtilPrivate *priv, gchar **values, GError **error)
 
        /* print */
        if (g_node_n_children(root) == 0) {
-               /* TRANSLATORS: nothing attached that can be upgraded */
-               g_print("%s\n", _("No hardware detected with firmware update capability"));
+               fu_console_print_literal(priv->console,
+                                        /* TRANSLATORS: nothing attached that can be upgraded */
+                                        _("No hardware detected with firmware update capability"));
                return TRUE;
        }
-       fu_util_print_tree(priv->client, root);
+       fu_util_print_tree(priv->console, priv->client, root);
 
        return TRUE;
 }
@@ -854,19 +859,19 @@ fu_util_update_device_changed_cb(FwupdClient *client, FwupdDevice *device, FuUti
                return;
        }
 
-       /* show message in progressbar */
+       /* show message in console */
        if (priv->current_operation == FU_UTIL_OPERATION_UPDATE) {
                /* TRANSLATORS: %1 is a device name */
                str = g_strdup_printf(_("Updating %s…"), fwupd_device_get_name(device));
-               fu_progressbar_set_title(priv->progressbar, str);
+               fu_console_set_progress_title(priv->console, str);
        } else if (priv->current_operation == FU_UTIL_OPERATION_INSTALL) {
                /* TRANSLATORS: %1 is a device name  */
                str = g_strdup_printf(_("Installing on %s…"), fwupd_device_get_name(device));
-               fu_progressbar_set_title(priv->progressbar, str);
+               fu_console_set_progress_title(priv->console, str);
        } else if (priv->current_operation == FU_UTIL_OPERATION_READ) {
                /* TRANSLATORS: %1 is a device name  */
                str = g_strdup_printf(_("Reading from %s…"), fwupd_device_get_name(device));
-               fu_progressbar_set_title(priv->progressbar, str);
+               fu_console_set_progress_title(priv->console, str);
        } else {
                g_warning("no FuUtilOperation set");
        }
@@ -879,7 +884,7 @@ fu_util_display_current_message(FuUtilPrivate *priv)
        /* print all POST requests */
        for (guint i = 0; i < priv->post_requests->len; i++) {
                FwupdRequest *request = g_ptr_array_index(priv->post_requests, i);
-               g_print("%s\n", fu_util_request_get_message(request));
+               fu_console_print_literal(priv->console, fu_util_request_get_message(request));
        }
 }
 
@@ -984,7 +989,7 @@ fu_util_install_blob(FuUtilPrivate *priv, gchar **values, GError **error)
        fu_util_display_current_message(priv);
 
        /* success */
-       return fu_util_prompt_complete(priv->completion_flags, TRUE, error);
+       return fu_util_prompt_complete(priv->console, priv->completion_flags, TRUE, error);
 }
 
 static gboolean
@@ -1251,7 +1256,7 @@ fu_util_install(FuUtilPrivate *priv, gchar **values, GError **error)
                if (device == NULL)
                        return FALSE;
                if (!priv->no_safety_check) {
-                       if (!fu_util_prompt_warning_fde(FWUPD_DEVICE(device), error))
+                       if (!fu_util_prompt_warning_fde(priv->console, FWUPD_DEVICE(device), error))
                                return FALSE;
                }
                devices_possible =
@@ -1381,7 +1386,7 @@ fu_util_install(FuUtilPrivate *priv, gchar **values, GError **error)
        }
 
        /* success */
-       return fu_util_prompt_complete(priv->completion_flags, TRUE, error);
+       return fu_util_prompt_complete(priv->console, priv->completion_flags, TRUE, error);
 }
 
 static gboolean
@@ -1510,13 +1515,14 @@ fu_util_update(FuUtilPrivate *priv, gchar **values, GError **error)
                        continue;
                if (!fwupd_device_has_flag(dev, FWUPD_DEVICE_FLAG_SUPPORTED)) {
                        if (!no_updates_header) {
-                               g_printerr("%s\n",
-                                          /* TRANSLATORS: message letting the user know no device
-                                           * upgrade available due to missing on LVFS */
-                                          _("Devices with no available firmware updates: "));
+                               fu_console_print_literal(
+                                   priv->console,
+                                   /* TRANSLATORS: message letting the user know no
+                                    * device upgrade available due to missing on LVFS */
+                                   _("Devices with no available firmware updates: "));
                                no_updates_header = TRUE;
                        }
-                       g_printerr(" • %s\n", fwupd_device_get_name(dev));
+                       fu_console_print(priv->console, " • %s", fwupd_device_get_name(dev));
                        continue;
                }
                if (!fu_util_filter_device(priv, dev))
@@ -1525,14 +1531,14 @@ fu_util_update(FuUtilPrivate *priv, gchar **values, GError **error)
                rels = fu_engine_get_upgrades(priv->engine, priv->request, device_id, &error_local);
                if (rels == NULL) {
                        if (!latest_header) {
-                               g_printerr(
-                                   "%s\n",
+                               fu_console_print_literal(
+                                   priv->console,
                                    /* TRANSLATORS: message letting the user know no device upgrade
                                     * available */
                                    _("Devices with the latest available firmware version:"));
                                latest_header = TRUE;
                        }
-                       g_printerr(" • %s\n", fwupd_device_get_name(dev));
+                       fu_console_print(priv->console, " • %s", fwupd_device_get_name(dev));
                        /* discard the actual reason from user, but leave for debugging */
                        g_debug("%s", error_local->message);
                        continue;
@@ -1544,14 +1550,14 @@ fu_util_update(FuUtilPrivate *priv, gchar **values, GError **error)
                            g_strdup_printf("%s %s",
                                            fu_engine_get_host_vendor(priv->engine),
                                            fu_engine_get_host_product(priv->engine));
-                       if (!fu_util_prompt_warning(dev, rel, title, error))
+                       if (!fu_util_prompt_warning(priv->console, dev, rel, title, error))
                                return FALSE;
-                       if (!fu_util_prompt_warning_fde(dev, error))
+                       if (!fu_util_prompt_warning_fde(priv->console, dev, error))
                                return FALSE;
                }
 
                if (!fu_util_install_release(priv, rel, &error_local)) {
-                       g_printerr("%s\n", error_local->message);
+                       fu_console_print_literal(priv->console, error_local->message);
                        continue;
                }
                fu_util_display_current_message(priv);
@@ -1563,7 +1569,7 @@ fu_util_update(FuUtilPrivate *priv, gchar **values, GError **error)
                return TRUE;
        }
 
-       return fu_util_prompt_complete(priv->completion_flags, TRUE, error);
+       return fu_util_prompt_complete(priv->console, priv->completion_flags, TRUE, error);
 }
 
 static gboolean
@@ -1633,7 +1639,7 @@ fu_util_reinstall(FuUtilPrivate *priv, gchar **values, GError **error)
                return TRUE;
        }
 
-       return fu_util_prompt_complete(priv->completion_flags, TRUE, error);
+       return fu_util_prompt_complete(priv->console, priv->completion_flags, TRUE, error);
 }
 
 static gboolean
@@ -1861,7 +1867,7 @@ fu_util_get_report_metadata(FuUtilPrivate *priv, gchar **values, GError **error)
        fu_progress_step_done(priv->progress);
 
        /* display */
-       g_print("\n%s", str->str);
+       fu_console_print_literal(priv->console, str->str);
 
        /* success */
        return TRUE;
@@ -1951,8 +1957,12 @@ fu_util_activate(FuUtilPrivate *priv, gchar **values, GError **error)
                if (!fu_device_has_flag(device, FWUPD_DEVICE_FLAG_NEEDS_ACTIVATION))
                        continue;
                has_pending = TRUE;
-               /* TRANSLATORS: shown when shutting down to switch to the new version */
-               g_print("%s %s…\n", _("Activating firmware update"), fu_device_get_name(device));
+               fu_console_print(
+                   priv->console,
+                   "%s %s…",
+                   /* TRANSLATORS: shown when shutting down to switch to the new version */
+                   _("Activating firmware update"),
+                   fu_device_get_name(device));
                if (!fu_engine_activate(priv->engine,
                                        fu_device_get_id(device),
                                        fu_progress_get_child(priv->progress),
@@ -2030,8 +2040,8 @@ fu_util_hwids(FuUtilPrivate *priv, gchar **values, GError **error)
                return FALSE;
 
        /* show debug output */
-       g_print("Computer Information\n");
-       g_print("--------------------\n");
+       fu_console_print_literal(priv->console, "Computer Information");
+       fu_console_print_literal(priv->console, "--------------------");
        for (guint i = 0; i < hwid_keys->len; i++) {
                const gchar *hwid_key = g_ptr_array_index(hwid_keys, i);
                const gchar *value = fu_hwids_get_value(hwids, hwid_key);
@@ -2040,15 +2050,15 @@ fu_util_hwids(FuUtilPrivate *priv, gchar **values, GError **error)
                if (g_strcmp0(hwid_key, FU_HWIDS_KEY_BIOS_MAJOR_RELEASE) == 0 ||
                    g_strcmp0(hwid_key, FU_HWIDS_KEY_BIOS_MINOR_RELEASE) == 0) {
                        guint64 val = g_ascii_strtoull(value, NULL, 16);
-                       g_print("%s: %" G_GUINT64_FORMAT "\n", hwid_key, val);
+                       fu_console_print(priv->console, "%s: %" G_GUINT64_FORMAT, hwid_key, val);
                } else {
-                       g_print("%s: %s\n", hwid_key, value);
+                       fu_console_print(priv->console, "%s: %s", hwid_key, value);
                }
        }
 
        /* show GUIDs */
-       g_print("\nHardware IDs\n");
-       g_print("------------\n");
+       fu_console_print_literal(priv->console, "Hardware IDs");
+       fu_console_print_literal(priv->console, "------------");
        for (guint i = 0; i < 15; i++) {
                const gchar *keys = NULL;
                g_autofree gchar *guid = NULL;
@@ -2062,14 +2072,14 @@ fu_util_hwids(FuUtilPrivate *priv, gchar **values, GError **error)
                keys = fu_hwids_get_replace_keys(hwids, key);
                guid = fu_hwids_get_guid(hwids, key, &error_local);
                if (guid == NULL) {
-                       g_print("%s\n", error_local->message);
+                       fu_console_print_literal(priv->console, error_local->message);
                        continue;
                }
 
                /* show what makes up the GUID */
                keysv = g_strsplit(keys, "&", -1);
                keys_str = g_strjoinv(" + ", keysv);
-               g_print("{%s}   <- %s\n", guid, keys_str);
+               fu_console_print(priv->console, "{%s}   <- %s", guid, keys_str);
        }
 
        return TRUE;
@@ -2098,7 +2108,7 @@ fu_util_self_sign(FuUtilPrivate *priv, gchar **values, GError **error)
                                  error);
        if (sig == NULL)
                return FALSE;
-       g_print("%s\n", sig);
+       fu_console_print_literal(priv->console, sig);
        return TRUE;
 }
 
@@ -2108,7 +2118,7 @@ fu_util_device_added_cb(FwupdClient *client, FwupdDevice *device, gpointer user_
        FuUtilPrivate *priv = (FuUtilPrivate *)user_data;
        g_autofree gchar *tmp = fu_util_device_to_string(priv->client, device, 0);
        /* TRANSLATORS: this is when a device is hotplugged */
-       g_print("%s\n%s", _("Device added:"), tmp);
+       fu_console_print(priv->console, "%s\n%s", _("Device added:"), tmp);
 }
 
 static void
@@ -2117,7 +2127,7 @@ fu_util_device_removed_cb(FwupdClient *client, FwupdDevice *device, gpointer use
        FuUtilPrivate *priv = (FuUtilPrivate *)user_data;
        g_autofree gchar *tmp = fu_util_device_to_string(priv->client, device, 0);
        /* TRANSLATORS: this is when a device is hotplugged */
-       g_print("%s\n%s", _("Device removed:"), tmp);
+       fu_console_print(priv->console, "%s\n%s", _("Device removed:"), tmp);
 }
 
 static void
@@ -2126,14 +2136,15 @@ fu_util_device_changed_cb(FwupdClient *client, FwupdDevice *device, gpointer use
        FuUtilPrivate *priv = (FuUtilPrivate *)user_data;
        g_autofree gchar *tmp = fu_util_device_to_string(priv->client, device, 0);
        /* TRANSLATORS: this is when a device has been updated */
-       g_print("%s\n%s", _("Device changed:"), tmp);
+       fu_console_print(priv->console, "%s\n%s", _("Device changed:"), tmp);
 }
 
 static void
 fu_util_changed_cb(FwupdClient *client, gpointer user_data)
 {
+       FuUtilPrivate *priv = (FuUtilPrivate *)user_data;
        /* TRANSLATORS: this is when the daemon state changes */
-       g_print("%s\n", _("Changed"));
+       fu_console_print_literal(priv->console, _("Changed"));
 }
 
 static gboolean
@@ -2183,11 +2194,11 @@ fu_util_get_firmware_types(FuUtilPrivate *priv, gchar **values, GError **error)
        firmware_types = fu_context_get_firmware_gtype_ids(fu_engine_get_context(priv->engine));
        for (guint i = 0; i < firmware_types->len; i++) {
                const gchar *id = g_ptr_array_index(firmware_types, i);
-               g_print("%s\n", id);
+               fu_console_print_literal(priv->console, id);
        }
        if (firmware_types->len == 0) {
                /* TRANSLATORS: nothing found */
-               g_print("%s\n", _("No firmware IDs found"));
+               fu_console_print_literal(priv->console, _("No firmware IDs found"));
                return TRUE;
        }
 
@@ -2202,14 +2213,14 @@ fu_util_prompt_for_firmware_type(FuUtilPrivate *priv, GError **error)
        firmware_types = fu_context_get_firmware_gtype_ids(fu_engine_get_context(priv->engine));
 
        /* TRANSLATORS: get interactive prompt */
-       g_print("%s\n", _("Choose a firmware type:"));
+       fu_console_print_literal(priv->console, _("Choose a firmware type:"));
        /* TRANSLATORS: this is to abort the interactive prompt */
-       g_print("0.\t%s\n", _("Cancel"));
+       fu_console_print(priv->console, "0.\t%s", _("Cancel"));
        for (guint i = 0; i < firmware_types->len; i++) {
                const gchar *id = g_ptr_array_index(firmware_types, i);
-               g_print("%u.\t%s\n", i + 1, id);
+               fu_console_print(priv->console, "%u.\t%s", i + 1, id);
        }
-       idx = fu_util_prompt_for_number(firmware_types->len);
+       idx = fu_console_input_uint(priv->console, firmware_types->len);
        if (idx == 0) {
                g_set_error_literal(error,
                                    FWUPD_ERROR,
@@ -2289,7 +2300,7 @@ fu_util_firmware_parse(FuUtilPrivate *priv, gchar **values, GError **error)
        }
 
        str = fu_firmware_to_string(firmware);
-       g_print("%s", str);
+       fu_console_print_literal(priv->console, str);
        return TRUE;
 }
 
@@ -2350,7 +2361,7 @@ fu_util_firmware_export(FuUtilPrivate *priv, gchar **values, GError **error)
        str = fu_firmware_export_to_xml(firmware, flags, error);
        if (str == NULL)
                return FALSE;
-       g_print("%s", str);
+       fu_console_print_literal(priv->console, str);
        return TRUE;
 }
 
@@ -2406,7 +2417,7 @@ fu_util_firmware_extract(FuUtilPrivate *priv, gchar **values, GError **error)
        if (!fu_firmware_parse(firmware, blob, priv->flags, error))
                return FALSE;
        str = fu_firmware_to_string(firmware);
-       g_print("%s", str);
+       fu_console_print_literal(priv->console, str);
        images = fu_firmware_get_images(firmware);
        for (guint i = 0; i < images->len; i++) {
                FuFirmware *img = g_ptr_array_index(images, i);
@@ -2431,7 +2442,7 @@ fu_util_firmware_extract(FuUtilPrivate *priv, gchar **values, GError **error)
                        fn = g_strdup_printf("img-0x%x.fw", i);
                }
                /* TRANSLATORS: decompressing images from a container firmware */
-               g_print("%s : %s\n", _("Writing file:"), fn);
+               fu_console_print(priv->console, "%s : %s", _("Writing file:"), fn);
                if (!fu_bytes_set_contents(fn, blob_img, error))
                        return FALSE;
        }
@@ -2531,7 +2542,7 @@ fu_util_firmware_build(FuUtilPrivate *priv, gchar **values, GError **error)
        if (!fu_firmware_parse(firmware_dst, blob_dst, priv->flags, error))
                return FALSE;
        str = fu_firmware_to_string(firmware_dst);
-       g_print("%s", str);
+       fu_console_print_literal(priv->console, str);
 
        /* success */
        return TRUE;
@@ -2611,7 +2622,7 @@ fu_util_firmware_convert(FuUtilPrivate *priv, gchar **values, GError **error)
                return FALSE;
        }
        str_src = fu_firmware_to_string(firmware_src);
-       g_print("%s", str_src);
+       fu_console_print_literal(priv->console, str_src);
 
        /* copy images */
        firmware_dst = g_object_new(gtype_dst, NULL);
@@ -2642,7 +2653,7 @@ fu_util_firmware_convert(FuUtilPrivate *priv, gchar **values, GError **error)
        if (!fu_bytes_set_contents(values[1], blob_dst, error))
                return FALSE;
        str_dst = fu_firmware_to_string(firmware_dst);
-       g_print("%s", str_dst);
+       fu_console_print_literal(priv->console, str_dst);
 
        /* success */
        return TRUE;
@@ -2753,7 +2764,7 @@ fu_util_firmware_patch(FuUtilPrivate *priv, gchar **values, GError **error)
        if (!fu_bytes_set_contents(values[0], blob_dst, error))
                return FALSE;
        str = fu_firmware_to_string(firmware);
-       g_print("%s", str);
+       fu_console_print_literal(priv->console, str);
 
        /* success */
        return TRUE;
@@ -2801,7 +2812,7 @@ fu_util_verify_update(FuUtilPrivate *priv, gchar **values, GError **error)
 
        /* show checksums */
        str = fu_device_to_string(dev);
-       g_print("%s\n", str);
+       fu_console_print_literal(priv->console, str);
        return TRUE;
 }
 
@@ -2874,7 +2885,7 @@ fu_util_get_history(FuUtilPrivate *priv, gchar **values, GError **error)
                        continue;
                }
        }
-       fu_util_print_tree(priv->client, root);
+       fu_util_print_tree(priv->console, priv->client, root);
 
        return TRUE;
 }
@@ -2987,7 +2998,7 @@ fu_util_get_remotes(FuUtilPrivate *priv, gchar **values, GError **error)
                FwupdRemote *remote_tmp = g_ptr_array_index(remotes, i);
                g_node_append_data(root, remote_tmp);
        }
-       fu_util_print_tree(priv->client, root);
+       fu_util_print_tree(priv->console, priv->client, root);
 
        return TRUE;
 }
@@ -3051,17 +3062,18 @@ fu_util_security(FuUtilPrivate *priv, gchar **values, GError **error)
                str = fu_security_attrs_to_json_string(attrs, error);
                if (str == NULL)
                        return FALSE;
-               g_print("%s\n", str);
+               fu_console_print_literal(priv->console, str);
                return TRUE;
        }
 
-       g_print("%s \033[1m%s\033[0m\n",
-               /* TRANSLATORS: this is a string like 'HSI:2-U' */
-               _("Host Security ID:"),
-               fu_engine_get_host_security_id(priv->engine));
+       fu_console_print(priv->console,
+                        "%s \033[1m%s\033[0m",
+                        /* TRANSLATORS: this is a string like 'HSI:2-U' */
+                        _("Host Security ID:"),
+                        fu_engine_get_host_security_id(priv->engine));
 
        str = fu_util_security_attrs_to_string(items, flags);
-       g_print("%s\n", str);
+       fu_console_print_literal(priv->console, str);
 
        /* print the "when" */
        events = fu_engine_get_host_security_events(priv->engine, 10, error);
@@ -3071,7 +3083,7 @@ fu_util_security(FuUtilPrivate *priv, gchar **values, GError **error)
        if (events_array->len > 0) {
                g_autofree gchar *estr = fu_util_security_events_to_string(events_array, flags);
                if (estr != NULL)
-                       g_print("%s\n", estr);
+                       fu_console_print_literal(priv->console, estr);
        }
 
        /* print the "also" */
@@ -3081,7 +3093,7 @@ fu_util_security(FuUtilPrivate *priv, gchar **values, GError **error)
        if (devices->len > 0) {
                g_autofree gchar *estr = fu_util_security_issues_to_string(devices);
                if (estr != NULL)
-                       g_print("%s\n", estr);
+                       fu_console_print_literal(priv->console, estr);
        }
 
        /* success */
@@ -3102,20 +3114,23 @@ fu_util_prompt_for_volume(FuUtilPrivate *priv, GError **error)
                return NULL;
        if (volumes->len == 1) {
                volume = g_ptr_array_index(volumes, 0);
-               /* TRANSLATORS: Volume has been chosen by the user */
-               g_print("%s: %s\n", _("Selected volume"), fu_volume_get_id(volume));
+               fu_console_print(priv->console,
+                                "%s: %s",
+                                /* TRANSLATORS: Volume has been chosen by the user */
+                                _("Selected volume"),
+                                fu_volume_get_id(volume));
                return g_object_ref(volume);
        }
 
        /* TRANSLATORS: get interactive prompt */
-       g_print("%s\n", _("Choose a volume:"));
+       fu_console_print_literal(priv->console, _("Choose a volume:"));
        /* TRANSLATORS: this is to abort the interactive prompt */
-       g_print("0.\t%s\n", _("Cancel"));
+       fu_console_print(priv->console, "0.\t%s", _("Cancel"));
        for (guint i = 0; i < volumes->len; i++) {
                volume = g_ptr_array_index(volumes, i);
-               g_print("%u.\t%s\n", i + 1, fu_volume_get_id(volume));
+               fu_console_print(priv->console, "%u.\t%s", i + 1, fu_volume_get_id(volume));
        }
-       idx = fu_util_prompt_for_number(volumes->len);
+       idx = fu_console_input_uint(priv->console, volumes->len);
        if (idx == 0) {
                g_set_error_literal(error,
                                    FWUPD_ERROR,
@@ -3167,7 +3182,7 @@ fu_util_esp_list(FuUtilPrivate *priv, gchar **values, GError **error)
                return FALSE;
        for (guint i = 0; i < files->len; i++) {
                const gchar *fn = g_ptr_array_index(files, i);
-               g_print("%s\n", fn);
+               fu_console_print_literal(priv->console, fn);
        }
        return TRUE;
 }
@@ -3238,14 +3253,17 @@ fu_util_switch_branch(FuUtilPrivate *priv, gchar **values, GError **error)
 
                /* TRANSLATORS: get interactive prompt, where branch is the
                 * supplier of the firmware, e.g. "non-free" or "free" */
-               g_print("%s\n", _("Choose a branch:"));
+               fu_console_print_literal(priv->console, _("Choose a branch:"));
                /* TRANSLATORS: this is to abort the interactive prompt */
-               g_print("0.\t%s\n", _("Cancel"));
+               fu_console_print(priv->console, "0.\t%s", _("Cancel"));
                for (guint i = 0; i < branches->len; i++) {
                        const gchar *branch_tmp = g_ptr_array_index(branches, i);
-                       g_print("%u.\t%s\n", i + 1, fu_util_branch_for_display(branch_tmp));
+                       fu_console_print(priv->console,
+                                        "%u.\t%s",
+                                        i + 1,
+                                        fu_util_branch_for_display(branch_tmp));
                }
-               idx = fu_util_prompt_for_number(branches->len);
+               idx = fu_console_input_uint(priv->console, branches->len);
                if (idx == 0) {
                        g_set_error_literal(error,
                                            FWUPD_ERROR,
@@ -3285,7 +3303,7 @@ fu_util_switch_branch(FuUtilPrivate *priv, gchar **values, GError **error)
        }
 
        /* we're switching branch */
-       if (!fu_util_switch_branch_warning(FWUPD_DEVICE(dev), rel, FALSE, error))
+       if (!fu_util_switch_branch_warning(priv->console, FWUPD_DEVICE(dev), rel, FALSE, error))
                return FALSE;
 
        /* update the console if composite devices are also updated */
@@ -3306,7 +3324,7 @@ fu_util_switch_branch(FuUtilPrivate *priv, gchar **values, GError **error)
                return TRUE;
        }
 
-       return fu_util_prompt_complete(priv->completion_flags, TRUE, error);
+       return fu_util_prompt_complete(priv->console, priv->completion_flags, TRUE, error);
 }
 
 static gboolean
@@ -3340,7 +3358,7 @@ fu_util_set_bios_setting(FuUtilPrivate *priv, gchar **input, GError **error)
                            g_strdup_printf(_("Set BIOS setting '%s' using '%s'."),
                                            (const gchar *)key,
                                            (const gchar *)value);
-                       g_print("\n%s\n", msg);
+                       fu_console_print_literal(priv->console, msg);
                }
        }
        priv->completion_flags |= FWUPD_DEVICE_FLAG_NEEDS_REBOOT;
@@ -3350,7 +3368,7 @@ fu_util_set_bios_setting(FuUtilPrivate *priv, gchar **input, GError **error)
                return TRUE;
        }
 
-       return fu_util_prompt_complete(priv->completion_flags, TRUE, error);
+       return fu_util_prompt_complete(priv->console, priv->completion_flags, TRUE, error);
 }
 
 static gboolean
@@ -3371,13 +3389,13 @@ fu_util_get_bios_setting(FuUtilPrivate *priv, gchar **values, GError **error)
        attrs = fu_context_get_bios_settings(ctx);
        items = fu_bios_settings_get_all(attrs);
        if (priv->as_json)
-               return fu_util_get_bios_setting_as_json(values, items, error);
+               return fu_util_get_bios_setting_as_json(priv->console, values, items, error);
 
        for (guint i = 0; i < items->len; i++) {
                FwupdBiosSetting *attr = g_ptr_array_index(items, i);
                if (fu_util_bios_setting_matches_args(attr, values)) {
                        g_autofree gchar *tmp = fu_util_bios_setting_to_string(attr, 0);
-                       g_print("%s\n", tmp);
+                       fu_console_print_literal(priv->console, tmp);
                        found = TRUE;
                }
        }
@@ -3393,8 +3411,9 @@ fu_util_get_bios_setting(FuUtilPrivate *priv, gchar **values, GError **error)
                g_set_error(error,
                            FWUPD_ERROR,
                            FWUPD_ERROR_INVALID_ARGS,
+                           "%s: '%s'",
                            /* TRANSLATORS: error message */
-                           "Unable to find attribute '%s'",
+                           _("Unable to find attribute"),
                            values[0]);
                return FALSE;
        }
@@ -3422,9 +3441,9 @@ fu_util_version(FuUtilPrivate *priv, GError **error)
 
        /* dump to the screen in the most appropriate format */
        if (priv->as_json)
-               return fu_util_project_versions_as_json(metadata, error);
+               return fu_util_project_versions_as_json(priv->console, metadata, error);
        str = fu_util_project_versions_to_string(metadata);
-       g_print("%s", str);
+       fu_console_print_literal(priv->console, str);
        return TRUE;
 }
 
@@ -3442,17 +3461,17 @@ fu_util_setup_interactive(FuUtilPrivate *priv, GError **error)
                g_set_error_literal(error, G_IO_ERROR, G_IO_ERROR_NOT_SUPPORTED, "using --json");
                return FALSE;
        }
-       return fu_util_setup_interactive_console(error);
+       return fu_console_setup(priv->console, error);
 }
 
 static void
 fu_util_print_error(FuUtilPrivate *priv, const GError *error)
 {
        if (priv->as_json) {
-               fu_util_print_error_as_json(error);
+               fu_util_print_error_as_json(priv->console, error);
                return;
        }
-       g_printerr("%s\n", error->message);
+       fu_console_print_full(priv->console, FU_CONSOLE_PRINT_FLAG_STDERR, "%s\n", error->message);
 }
 
 int
@@ -3652,9 +3671,9 @@ main(int argc, char *argv[])
        /* create helper object */
        priv->main_ctx = g_main_context_new();
        priv->loop = g_main_loop_new(priv->main_ctx, FALSE);
-       priv->progressbar = fu_progressbar_new();
+       priv->console = fu_console_new();
        priv->post_requests = g_ptr_array_new_with_free_func((GDestroyNotify)g_object_unref);
-       fu_progressbar_set_main_context(priv->progressbar, priv->main_ctx);
+       fu_console_set_main_context(priv->console, priv->main_ctx);
        priv->request = fu_engine_request_new(FU_ENGINE_REQUEST_KIND_ACTIVE);
 
        /* used for monitoring and downloading */
@@ -4001,7 +4020,7 @@ main(int argc, char *argv[])
                        FWUPD_FEATURE_FLAG_COMMUNITY_TEXT | FWUPD_FEATURE_FLAG_SHOW_PROBLEMS |
                        FWUPD_FEATURE_FLAG_REQUESTS);
        }
-       fu_progressbar_set_interactive(priv->progressbar, priv->interactive);
+       fu_console_set_interactive(priv->console, priv->interactive);
 
        /* get a list of the commands */
        priv->context = g_option_context_new(NULL);
@@ -4019,23 +4038,24 @@ main(int argc, char *argv[])
        g_option_context_add_group(priv->context, fu_debug_get_option_group());
        ret = g_option_context_parse(priv->context, &argc, &argv, &error);
        if (!ret) {
-               /* TRANSLATORS: the user didn't read the man page */
-               g_print("%s: %s\n", _("Failed to parse arguments"), error->message);
+               fu_console_print(priv->console,
+                                "%s: %s",
+                                /* TRANSLATORS: the user didn't read the man page */
+                                _("Failed to parse arguments"),
+                                error->message);
                return EXIT_FAILURE;
        }
        fu_progress_set_profile(priv->progress, g_getenv("FWUPD_VERBOSE") != NULL);
 
        /* allow disabling SSL strict mode for broken corporate proxies */
        if (priv->disable_ssl_strict) {
-               g_autofree gchar *fmt = NULL;
-               /* TRANSLATORS: this is a prefix on the console */
-               fmt = fu_util_term_format(_("WARNING:"), FU_UTIL_TERM_COLOR_RED);
-               g_printerr("%s %s\n",
-                          fmt,
-                          /* TRANSLATORS: try to help */
-                          _("Ignoring SSL strict checks, "
-                            "to do this automatically in the future "
-                            "export DISABLE_SSL_STRICT in your environment"));
+               fu_console_print_full(priv->console,
+                                     FU_CONSOLE_PRINT_FLAG_WARNING,
+                                     "%s\n",
+                                     /* TRANSLATORS: try to help */
+                                     _("Ignoring SSL strict checks, "
+                                       "to do this automatically in the future "
+                                       "export DISABLE_SSL_STRICT in your environment"));
                (void)g_setenv("DISABLE_SSL_STRICT", "1", TRUE);
        }
 
@@ -4112,8 +4132,9 @@ main(int argc, char *argv[])
 #endif
                fu_util_print_error(priv, error);
                if (g_error_matches(error, FWUPD_ERROR, FWUPD_ERROR_INVALID_ARGS)) {
-                       /* TRANSLATORS: error message explaining command on how to get help */
-                       g_printerr("\n%s\n", _("Use fwupdtool --help for help"));
+                       fu_console_print_literal(priv->console,
+                                                /* TRANSLATORS: explain how to get help */
+                                                _("Use fwupdtool --help for help"));
                } else if (g_error_matches(error, FWUPD_ERROR, FWUPD_ERROR_NOTHING_TO_DO)) {
                        g_debug("%s\n", error->message);
                        return EXIT_NOTHING_TO_DO;
@@ -4121,8 +4142,12 @@ main(int argc, char *argv[])
 #ifdef HAVE_GETUID
                /* if not root, then notify users on the error path */
                if (priv->interactive && (getuid() != 0 || geteuid() != 0)) {
-                       /* TRANSLATORS: we're poking around as a power user */
-                       g_printerr("%s\n", _("NOTE: This program may only work correctly as root"));
+                       fu_console_print_full(priv->console,
+                                             FU_CONSOLE_PRINT_FLAG_STDERR |
+                                                 FU_CONSOLE_PRINT_FLAG_WARNING,
+                                             "%s\n",
+                                             /* TRANSLATORS: we're poking around as a power user */
+                                             _("This program may only work correctly as root"));
                }
 #endif
                return EXIT_FAILURE;
@@ -4132,7 +4157,7 @@ main(int argc, char *argv[])
        if (fu_progress_get_profile(priv->progress)) {
                g_autofree gchar *str = fu_progress_traceback(priv->progress);
                if (str != NULL)
-                       g_print("\n%s\n", str);
+                       fu_console_print_literal(priv->console, str);
        }
 
        /* success */
index e684633aeb4b3896d4a322320cdd7ac81568c473..539a6042feffd8500b8a21dea4ac12f755dd43ed 100644 (file)
@@ -62,7 +62,10 @@ fu_util_bios_setting_matches_args(FwupdBiosSetting *setting, gchar **values)
 }
 
 gboolean
-fu_util_get_bios_setting_as_json(gchar **values, GPtrArray *settings, GError **error)
+fu_util_get_bios_setting_as_json(FuConsole *console,
+                                gchar **values,
+                                GPtrArray *settings,
+                                GError **error)
 {
        g_autoptr(JsonBuilder) builder = json_builder_new();
        json_builder_begin_object(builder);
@@ -80,7 +83,7 @@ fu_util_get_bios_setting_as_json(gchar **values, GPtrArray *settings, GError **e
        }
        json_builder_end_array(builder);
        json_builder_end_object(builder);
-       return fu_util_print_builder(builder, error);
+       return fu_util_print_builder(console, builder, error);
 }
 
 gchar *
index 3f06eddf67c1924949b939a902b0452215863027..e7f09131cc371ea378bdd5ac2a291a90347b712e 100644 (file)
 
 #include "fwupd-bios-setting-private.h"
 
+#include "fu-console.h"
+
 gchar *
 fu_util_bios_setting_to_string(FwupdBiosSetting *setting, guint idt);
 gboolean
 fu_util_bios_setting_matches_args(FwupdBiosSetting *setting, gchar **values);
 gboolean
-fu_util_get_bios_setting_as_json(gchar **values, GPtrArray *settings, GError **error);
+fu_util_get_bios_setting_as_json(FuConsole *console,
+                                gchar **values,
+                                GPtrArray *settings,
+                                GError **error);
 GHashTable *
 fu_util_bios_settings_parse_argv(gchar **input, GError **error);
index 5ff65b44834a11e724bb78fc10ea00d9ab83add9..1bcc084c6a774f6c4ebabfc9673d40b14ecd0fa7 100644 (file)
 #include <curl/curl.h>
 #endif
 
-#ifdef _WIN32
-#include <wchar.h>
-#include <windows.h>
-#endif
-
+#include "fu-console.h"
 #include "fu-device-private.h"
 #include "fu-security-attr-common.h"
 #include "fu-util-common.h"
@@ -45,7 +41,7 @@ fu_util_get_systemd_unit(void)
 }
 
 gchar *
-fu_util_term_format(const gchar *text, FuUtilTermColor fg_color)
+fu_console_color_format(const gchar *text, FuConsoleColor fg_color)
 {
        if (g_getenv("NO_COLOR") != NULL)
                return g_strdup(text);
@@ -94,79 +90,15 @@ fu_util_using_correct_daemon(GError **error)
        return TRUE;
 }
 
-void
-fu_util_print_data(const gchar *title, const gchar *msg)
-{
-       gsize title_len;
-       g_auto(GStrv) lines = NULL;
-
-       if (msg == NULL)
-               return;
-       g_print("%s:", title);
-
-       /* pad */
-       title_len = fu_strwidth(title) + 1;
-       lines = g_strsplit(msg, "\n", -1);
-       for (guint j = 0; lines[j] != NULL; j++) {
-               for (gsize i = title_len; i < 25; i++)
-                       g_print(" ");
-               g_print("%s\n", lines[j]);
-               title_len = 0;
-       }
-}
-
-guint
-fu_util_prompt_for_number(guint maxnum)
-{
-       gint retval;
-       guint answer = 0;
-
-       do {
-               char buffer[64];
-
-               /* swallow the \n at end of line too */
-               if (!fgets(buffer, sizeof(buffer), stdin))
-                       break;
-               if (strlen(buffer) == sizeof(buffer) - 1)
-                       continue;
-
-               /* get a number */
-               retval = sscanf(buffer, "%u", &answer);
-
-               /* positive */
-               if (retval == 1 && answer <= maxnum)
-                       break;
-
-               /* TRANSLATORS: the user isn't reading the question */
-               g_print(_("Please enter a number from 0 to %u: "), maxnum);
-       } while (TRUE);
-       return answer;
-}
-
-gboolean
-fu_util_prompt_for_boolean(gboolean def)
-{
-       do {
-               char buffer[4];
-               if (!fgets(buffer, sizeof(buffer), stdin))
-                       continue;
-               if (strlen(buffer) == sizeof(buffer) - 1)
-                       continue;
-               if (g_strcmp0(buffer, "\n") == 0)
-                       return def;
-               buffer[0] = g_ascii_toupper(buffer[0]);
-               if (g_strcmp0(buffer, "Y\n") == 0)
-                       return TRUE;
-               if (g_strcmp0(buffer, "N\n") == 0)
-                       return FALSE;
-       } while (TRUE);
-       return FALSE;
-}
+typedef struct {
+       FwupdClient *client;
+       FuConsole *console;
+} FuUtilPrintTreeHelper;
 
 static gboolean
 fu_util_traverse_tree(GNode *n, gpointer data)
 {
-       FwupdClient *client = FWUPD_CLIENT(data);
+       FuUtilPrintTreeHelper *helper = (FuUtilPrintTreeHelper *)data;
        guint idx = g_node_depth(n) - 1;
        g_autofree gchar *tmp = NULL;
        g_auto(GStrv) split = NULL;
@@ -174,7 +106,7 @@ fu_util_traverse_tree(GNode *n, gpointer data)
        /* get split lines */
        if (FWUPD_IS_DEVICE(n->data)) {
                FwupdDevice *dev = FWUPD_DEVICE(n->data);
-               tmp = fu_util_device_to_string(client, dev, idx);
+               tmp = fu_util_device_to_string(helper->client, dev, idx);
        } else if (FWUPD_IS_REMOTE(n->data)) {
                FwupdRemote *remote = FWUPD_REMOTE(n->data);
                tmp = fu_util_remote_to_string(remote, idx);
@@ -186,9 +118,10 @@ fu_util_traverse_tree(GNode *n, gpointer data)
 
        /* root node */
        if (n->data == NULL && g_getenv("FWUPD_VERBOSE") == NULL) {
-               g_autofree gchar *str = g_strdup_printf("%s %s",
-                                                       fwupd_client_get_host_vendor(client),
-                                                       fwupd_client_get_host_product(client));
+               g_autofree gchar *str =
+                   g_strdup_printf("%s %s",
+                                   fwupd_client_get_host_vendor(helper->client),
+                                   fwupd_client_get_host_product(helper->client));
                g_print("%s\n│\n", str);
                return FALSE;
        }
@@ -227,22 +160,23 @@ fu_util_traverse_tree(GNode *n, gpointer data)
 
                /* empty line */
                if (split[i][0] == '\0') {
-                       g_print("%s\n", str->str);
+                       fu_console_print_literal(helper->console, str->str);
                        continue;
                }
 
                /* dump to the console */
                g_string_append(str, split[i] + (idx * 2));
-               g_print("%s\n", str->str);
+               fu_console_print_literal(helper->console, str->str);
        }
 
        return FALSE;
 }
 
 void
-fu_util_print_tree(FwupdClient *client, GNode *n)
+fu_util_print_tree(FuConsole *console, FwupdClient *client, GNode *n)
 {
-       g_node_traverse(n, G_PRE_ORDER, G_TRAVERSE_ALL, -1, fu_util_traverse_tree, client);
+       FuUtilPrintTreeHelper helper = {.client = client, .console = console};
+       g_node_traverse(n, G_PRE_ORDER, G_TRAVERSE_ALL, -1, fu_util_traverse_tree, &helper);
 }
 
 static gboolean
@@ -420,7 +354,8 @@ fu_util_get_release_description_with_fallback(FwupdRelease *rel)
 }
 
 gboolean
-fu_util_prompt_warning(FwupdDevice *device,
+fu_util_prompt_warning(FuConsole *console,
+                      FwupdDevice *device,
                       FwupdRelease *release,
                       const gchar *machine,
                       GError **error)
@@ -507,13 +442,10 @@ fu_util_prompt_warning(FwupdDevice *device,
                        }
                }
        }
-       fu_util_warning_box(title->str, str->str, 80);
+       fu_console_box(console, title->str, str->str, 80);
 
-       /* ask for confirmation */
-       g_print("\n%s [Y|n]: ",
-               /* TRANSLATORS: prompt to apply the update */
-               _("Perform operation?"));
-       if (!fu_util_prompt_for_boolean(TRUE)) {
+       /* TRANSLATORS: prompt to apply the update */
+       if (!fu_console_input_bool(console, TRUE, "%s", _("Perform operation?"))) {
                g_set_error_literal(error,
                                    FWUPD_ERROR,
                                    FWUPD_ERROR_NOTHING_TO_DO,
@@ -526,28 +458,31 @@ fu_util_prompt_warning(FwupdDevice *device,
 }
 
 gboolean
-fu_util_prompt_complete(FwupdDeviceFlags flags, gboolean prompt, GError **error)
+fu_util_prompt_complete(FuConsole *console, FwupdDeviceFlags flags, gboolean prompt, GError **error)
 {
        if (flags & FWUPD_DEVICE_FLAG_NEEDS_SHUTDOWN) {
                if (prompt) {
-                       g_print("\n%s %s [y|N]: ",
-                               /* TRANSLATORS: explain why we want to shutdown */
-                               _("An update requires the system to shutdown to complete."),
-                               /* TRANSLATORS: shutdown to apply the update */
-                               _("Shutdown now?"));
-                       if (!fu_util_prompt_for_boolean(FALSE))
+                       if (!fu_console_input_bool(console,
+                                                  FALSE,
+                                                  "%s %s",
+                                                  /* TRANSLATORS: explain why */
+                                                  _("An update requires the system to shutdown "
+                                                    "to complete."),
+                                                  /* TRANSLATORS: shutdown to apply the update */
+                                                  _("Shutdown now?")))
                                return TRUE;
                }
                return fu_util_update_shutdown(error);
        }
        if (flags & FWUPD_DEVICE_FLAG_NEEDS_REBOOT) {
                if (prompt) {
-                       g_print("\n%s %s [y|N]: ",
-                               /* TRANSLATORS: explain why we want to reboot */
-                               _("An update requires a reboot to complete."),
-                               /* TRANSLATORS: reboot to apply the update */
-                               _("Restart now?"));
-                       if (!fu_util_prompt_for_boolean(FALSE))
+                       if (!fu_console_input_bool(console,
+                                                  FALSE,
+                                                  "%s %s",
+                                                  /* TRANSLATORS: explain why we want to reboot */
+                                                  _("An update requires a reboot to complete."),
+                                                  /* TRANSLATORS: reboot to apply the update */
+                                                  _("Restart now?")))
                                return TRUE;
                }
                return fu_util_update_reboot(error);
@@ -849,116 +784,6 @@ fu_util_release_get_name(FwupdRelease *release)
        return g_strdup_printf(_("%s Update"), name);
 }
 
-static GPtrArray *
-fu_util_strsplit_words(const gchar *text, guint line_len)
-{
-       g_auto(GStrv) tokens = NULL;
-       g_autoptr(GPtrArray) lines = g_ptr_array_new_with_free_func(g_free);
-       g_autoptr(GString) curline = g_string_new(NULL);
-
-       /* sanity check */
-       if (text == NULL || text[0] == '\0')
-               return NULL;
-       if (line_len == 0)
-               return NULL;
-
-       /* tokenize the string */
-       tokens = g_strsplit(text, " ", -1);
-       for (guint i = 0; tokens[i] != NULL; i++) {
-               /* current line plus new token is okay */
-               if (curline->len + fu_strwidth(tokens[i]) < line_len) {
-                       g_string_append_printf(curline, "%s ", tokens[i]);
-                       continue;
-               }
-
-               /* too long, so remove space, add newline and dump */
-               if (curline->len > 0)
-                       g_string_truncate(curline, curline->len - 1);
-               g_ptr_array_add(lines, g_strdup(curline->str));
-               g_string_truncate(curline, 0);
-               g_string_append_printf(curline, "%s ", tokens[i]);
-       }
-
-       /* any incomplete line? */
-       if (curline->len > 0) {
-               g_string_truncate(curline, curline->len - 1);
-               g_ptr_array_add(lines, g_strdup(curline->str));
-       }
-       return g_steal_pointer(&lines);
-}
-
-static void
-fu_util_warning_box_line(const gchar *start,
-                        const gchar *text,
-                        const gchar *end,
-                        const gchar *padding,
-                        guint width)
-{
-       guint offset = 0;
-       if (start != NULL) {
-               offset += fu_strwidth(start);
-               g_print("%s", start);
-       }
-       if (text != NULL) {
-               offset += fu_strwidth(text);
-               g_print("%s", text);
-       }
-       if (end != NULL)
-               offset += fu_strwidth(end);
-       for (guint i = offset; i < width; i++)
-               g_print("%s", padding);
-       if (end != NULL)
-               g_print("%s\n", end);
-}
-
-void
-fu_util_warning_box(const gchar *title, const gchar *body, guint width)
-{
-       /* nothing to do */
-       if (title == NULL && body == NULL)
-               return;
-
-       /* header */
-       fu_util_warning_box_line("╔", NULL, "╗", "═", width);
-
-       /* optional title */
-       if (title != NULL) {
-               g_autoptr(GPtrArray) lines = fu_util_strsplit_words(title, width - 4);
-               for (guint j = 0; j < lines->len; j++) {
-                       const gchar *line = g_ptr_array_index(lines, j);
-                       fu_util_warning_box_line("║ ", line, " ║", " ", width);
-               }
-       }
-
-       /* join */
-       if (title != NULL && body != NULL)
-               fu_util_warning_box_line("╠", NULL, "╣", "═", width);
-
-       /* optional body */
-       if (body != NULL) {
-               gboolean has_nonempty = FALSE;
-               g_auto(GStrv) split = g_strsplit(body, "\n", -1);
-               for (guint i = 0; split[i] != NULL; i++) {
-                       g_autoptr(GPtrArray) lines = fu_util_strsplit_words(split[i], width - 4);
-                       if (lines == NULL) {
-                               if (has_nonempty) {
-                                       fu_util_warning_box_line("║ ", NULL, " ║", " ", width);
-                                       has_nonempty = FALSE;
-                               }
-                               continue;
-                       }
-                       for (guint j = 0; j < lines->len; j++) {
-                               const gchar *line = g_ptr_array_index(lines, j);
-                               fu_util_warning_box_line("║ ", line, " ║", " ", width);
-                       }
-                       has_nonempty = TRUE;
-               }
-       }
-
-       /* footer */
-       fu_util_warning_box_line("╚", NULL, "╝", "═", width);
-}
-
 gboolean
 fu_util_parse_filter_flags(const gchar *filter,
                           FwupdDeviceFlags *include,
@@ -1561,7 +1386,7 @@ fu_util_device_to_string(FwupdClient *client, FwupdDevice *dev, guint idt)
                        tmp = fwupd_device_get_update_message(dev);
                        if (tmp != NULL) {
                                g_autofree gchar *color =
-                                   fu_util_term_format(tmp, FU_UTIL_TERM_COLOR_BLUE);
+                                   fu_console_color_format(tmp, FU_CONSOLE_COLOR_BLUE);
                                fu_string_append(
                                    str,
                                    idt + 1,
@@ -1596,7 +1421,8 @@ fu_util_device_to_string(FwupdClient *client, FwupdDevice *dev, guint idt)
        if (fwupd_device_get_problems(dev) == FWUPD_DEVICE_PROBLEM_NONE) {
                tmp = fwupd_device_get_update_error(dev);
                if (tmp != NULL) {
-                       g_autofree gchar *color = fu_util_term_format(tmp, FU_UTIL_TERM_COLOR_RED);
+                       g_autofree gchar *color =
+                           fu_console_color_format(tmp, FU_CONSOLE_COLOR_RED);
                        /* TRANSLATORS: error message from last update attempt */
                        fu_string_append(str, idt + 1, _("Update Error"), color);
                }
@@ -1615,7 +1441,7 @@ fu_util_device_to_string(FwupdClient *client, FwupdDevice *dev, guint idt)
                        if (desc == NULL)
                                continue;
                        bullet = g_strdup_printf("• %s", desc);
-                       color = fu_util_term_format(bullet, FU_UTIL_TERM_COLOR_RED);
+                       color = fu_console_color_format(bullet, FU_CONSOLE_COLOR_RED);
                        fu_string_append(str, idt + 1, tmp, color);
                        tmp = NULL;
                }
@@ -1780,12 +1606,12 @@ fu_util_plugin_flag_to_cli_text(FwupdPluginFlags plugin_flag)
        case FWUPD_PLUGIN_FLAG_MODULAR:
        case FWUPD_PLUGIN_FLAG_MEASURE_SYSTEM_INTEGRITY:
        case FWUPD_PLUGIN_FLAG_SECURE_CONFIG:
-               return fu_util_term_format(fu_util_plugin_flag_to_string(plugin_flag),
-                                          FU_UTIL_TERM_COLOR_GREEN);
+               return fu_console_color_format(fu_util_plugin_flag_to_string(plugin_flag),
+                                              FU_CONSOLE_COLOR_GREEN);
        case FWUPD_PLUGIN_FLAG_DISABLED:
        case FWUPD_PLUGIN_FLAG_NO_HARDWARE:
-               return fu_util_term_format(fu_util_plugin_flag_to_string(plugin_flag),
-                                          FU_UTIL_TERM_COLOR_BLACK);
+               return fu_console_color_format(fu_util_plugin_flag_to_string(plugin_flag),
+                                              FU_CONSOLE_COLOR_BLACK);
        case FWUPD_PLUGIN_FLAG_LEGACY_BIOS:
        case FWUPD_PLUGIN_FLAG_CAPSULES_UNSUPPORTED:
        case FWUPD_PLUGIN_FLAG_UNLOCK_REQUIRED:
@@ -1793,8 +1619,8 @@ fu_util_plugin_flag_to_cli_text(FwupdPluginFlags plugin_flag)
        case FWUPD_PLUGIN_FLAG_EFIVAR_NOT_MOUNTED:
        case FWUPD_PLUGIN_FLAG_ESP_NOT_FOUND:
        case FWUPD_PLUGIN_FLAG_KERNEL_TOO_OLD:
-               return fu_util_term_format(fu_util_plugin_flag_to_string(plugin_flag),
-                                          FU_UTIL_TERM_COLOR_RED);
+               return fu_console_color_format(fu_util_plugin_flag_to_string(plugin_flag),
+                                              FU_CONSOLE_COLOR_RED);
        default:
                break;
        }
@@ -2334,16 +2160,16 @@ fu_security_attr_append_str(FwupdSecurityAttr *attr,
        for (guint i = fu_strwidth(name); i < 30; i++)
                g_string_append(str, " ");
        if (fwupd_security_attr_has_flag(attr, FWUPD_SECURITY_ATTR_FLAG_OBSOLETED)) {
-               g_autofree gchar *fmt = fu_util_term_format(fu_security_attr_get_result(attr),
-                                                           FU_UTIL_TERM_COLOR_YELLOW);
+               g_autofree gchar *fmt = fu_console_color_format(fu_security_attr_get_result(attr),
+                                                               FU_CONSOLE_COLOR_YELLOW);
                g_string_append(str, fmt);
        } else if (fwupd_security_attr_has_flag(attr, FWUPD_SECURITY_ATTR_FLAG_SUCCESS)) {
-               g_autofree gchar *fmt = fu_util_term_format(fu_security_attr_get_result(attr),
-                                                           FU_UTIL_TERM_COLOR_GREEN);
+               g_autofree gchar *fmt = fu_console_color_format(fu_security_attr_get_result(attr),
+                                                               FU_CONSOLE_COLOR_GREEN);
                g_string_append(str, fmt);
        } else {
-               g_autofree gchar *fmt =
-                   fu_util_term_format(fu_security_attr_get_result(attr), FU_UTIL_TERM_COLOR_RED);
+               g_autofree gchar *fmt = fu_console_color_format(fu_security_attr_get_result(attr),
+                                                               FU_CONSOLE_COLOR_RED);
                g_string_append(str, fmt);
        }
        if ((flags & FU_SECURITY_ATTR_TO_STRING_FLAG_SHOW_URLS) > 0 &&
@@ -2545,9 +2371,9 @@ fu_util_security_events_to_string(GPtrArray *events, FuSecurityAttrToStringFlags
                if (eventstr == NULL)
                        continue;
                if (fwupd_security_attr_has_flag(attr, FWUPD_SECURITY_ATTR_FLAG_SUCCESS)) {
-                       check = fu_util_term_format("✔", FU_UTIL_TERM_COLOR_GREEN);
+                       check = fu_console_color_format("✔", FU_CONSOLE_COLOR_GREEN);
                } else {
-                       check = fu_util_term_format("✘", FU_UTIL_TERM_COLOR_RED);
+                       check = fu_console_color_format("✘", FU_CONSOLE_COLOR_RED);
                }
                if (str->len == 0) {
                        /* TRANSLATORS: title for host security events */
@@ -2820,7 +2646,8 @@ fu_util_device_order_sort_cb(gconstpointer a, gconstpointer b)
 }
 
 gboolean
-fu_util_switch_branch_warning(FwupdDevice *dev,
+fu_util_switch_branch_warning(FuConsole *console,
+                             FwupdDevice *dev,
                              FwupdRelease *rel,
                              gboolean assume_yes,
                              GError **error)
@@ -2863,13 +2690,14 @@ fu_util_switch_branch_warning(FwupdDevice *dev,
        title = g_strdup_printf(_("Switch branch from %s to %s?"),
                                fu_util_branch_for_display(fwupd_device_get_branch(dev)),
                                fu_util_branch_for_display(fwupd_release_get_branch(rel)));
-       fu_util_warning_box(title, desc_full->str, 80);
+       fu_console_box(console, title, desc_full->str, 80);
        if (!assume_yes) {
-               /* ask for permission */
-               g_print("\n%s [y|N]: ",
-                       /* TRANSLATORS: should the branch be changed */
-                       _("Do you understand the consequences of changing the firmware branch?"));
-               if (!fu_util_prompt_for_boolean(FALSE)) {
+               if (!fu_console_input_bool(console,
+                                          FALSE,
+                                          "%s",
+                                          /* TRANSLATORS: should the branch be changed */
+                                          _("Do you understand the consequences "
+                                            "of changing the firmware branch?"))) {
                        g_set_error_literal(error,
                                            FWUPD_ERROR,
                                            FWUPD_ERROR_NOTHING_TO_DO,
@@ -2881,7 +2709,7 @@ fu_util_switch_branch_warning(FwupdDevice *dev,
 }
 
 gboolean
-fu_util_prompt_warning_fde(FwupdDevice *dev, GError **error)
+fu_util_prompt_warning_fde(FuConsole *console, FwupdDevice *dev, GError **error)
 {
        const gchar *url = "https://github.com/fwupd/fwupd/wiki/Full-Disk-Encryption-Detected";
        g_autoptr(GString) str = g_string_new(NULL);
@@ -2904,13 +2732,10 @@ fu_util_prompt_warning_fde(FwupdDevice *dev, GError **error)
                               _("See %s for more details."),
                               url);
        /* TRANSLATORS: title text, shown as a warning */
-       fu_util_warning_box(_("Full Disk Encryption Detected"), str->str, 80);
+       fu_console_box(console, _("Full Disk Encryption Detected"), str->str, 80);
 
-       /* ask for confirmation */
-       g_print("\n%s [Y|n]: ",
-               /* TRANSLATORS: prompt to apply the update */
-               _("Perform operation?"));
-       if (!fu_util_prompt_for_boolean(TRUE)) {
+       /* TRANSLATORS: prompt to apply the update */
+       if (!fu_console_input_bool(console, TRUE, "%s", _("Perform operation?"))) {
                g_set_error_literal(error,
                                    FWUPD_ERROR,
                                    FWUPD_ERROR_NOTHING_TO_DO,
@@ -2921,19 +2746,17 @@ fu_util_prompt_warning_fde(FwupdDevice *dev, GError **error)
 }
 
 void
-fu_util_show_unsupported_warn(void)
+fu_util_show_unsupported_warning(FuConsole *console)
 {
 #ifndef SUPPORTED_BUILD
-       g_autofree gchar *fmt = NULL;
-
        if (g_getenv("FWUPD_SUPPORTED") != NULL)
                return;
        /* TRANSLATORS: this is a prefix on the console */
-       fmt = fu_util_term_format(_("WARNING:"), FU_UTIL_TERM_COLOR_YELLOW);
-       g_printerr("%s %s\n",
-                  fmt,
-                  /* TRANSLATORS: unsupported build of the package */
-                  _("This package has not been validated, it may not work properly."));
+       fu_console_print_full(console,
+                             FU_CONSOLE_PRINT_FLAG_WARNING | FU_CONSOLE_PRINT_FLAG_STDERR,
+                             "%s\n",
+                             /* TRANSLATORS: unsupported build of the package */
+                             _("This package has not been validated, it may not work properly."));
 #endif
 }
 
@@ -2954,67 +2777,7 @@ fu_util_is_url(const gchar *perhaps_url)
 }
 
 gboolean
-fu_util_setup_interactive_console(GError **error)
-{
-#ifdef _WIN32
-       HANDLE hOut;
-       DWORD dwMode = 0;
-
-       /* enable VT sequences */
-       hOut = GetStdHandle(STD_OUTPUT_HANDLE);
-       if (hOut == INVALID_HANDLE_VALUE) {
-               g_set_error(error,
-                           G_IO_ERROR,
-                           G_IO_ERROR_NOT_SUPPORTED,
-                           "failed to get stdout [%u]",
-                           (guint)GetLastError());
-               return FALSE;
-       }
-       if (!GetConsoleMode(hOut, &dwMode)) {
-               g_set_error(error,
-                           G_IO_ERROR,
-                           G_IO_ERROR_NOT_SUPPORTED,
-                           "failed to get mode [%u]",
-                           (guint)GetLastError());
-               return FALSE;
-       }
-       dwMode |= ENABLE_VIRTUAL_TERMINAL_PROCESSING;
-       if (!SetConsoleMode(hOut, dwMode)) {
-               g_set_error(error,
-                           G_IO_ERROR,
-                           G_IO_ERROR_NOT_SUPPORTED,
-                           "failed to set mode [%u]",
-                           (guint)GetLastError());
-               return FALSE;
-       }
-       if (!SetConsoleOutputCP(CP_UTF8)) {
-               g_set_error(error,
-                           G_IO_ERROR,
-                           G_IO_ERROR_NOT_SUPPORTED,
-                           "failed to set output UTF-8 [%u]",
-                           (guint)GetLastError());
-               return FALSE;
-       }
-       if (!SetConsoleCP(CP_UTF8)) {
-               g_set_error(error,
-                           G_IO_ERROR,
-                           G_IO_ERROR_NOT_SUPPORTED,
-                           "failed to set UTF-8 [%u]",
-                           (guint)GetLastError());
-               return FALSE;
-       }
-#else
-       if (isatty(fileno(stdout)) == 0) {
-               g_set_error_literal(error, G_IO_ERROR, G_IO_ERROR_NOT_SUPPORTED, "not a TTY");
-               return FALSE;
-       }
-#endif
-       /* success */
-       return TRUE;
-}
-
-gboolean
-fu_util_print_builder(JsonBuilder *builder, GError **error)
+fu_util_print_builder(FuConsole *console, JsonBuilder *builder, GError **error)
 {
        g_autofree gchar *data = NULL;
        g_autoptr(JsonGenerator) json_generator = NULL;
@@ -3035,12 +2798,12 @@ fu_util_print_builder(JsonBuilder *builder, GError **error)
        }
 
        /* just print */
-       g_print("%s\n", data);
+       fu_console_print_literal(console, data);
        return TRUE;
 }
 
 void
-fu_util_print_error_as_json(const GError *error)
+fu_util_print_error_as_json(FuConsole *console, const GError *error)
 {
        g_autoptr(JsonBuilder) builder = json_builder_new();
        json_builder_begin_object(builder);
@@ -3054,7 +2817,7 @@ fu_util_print_error_as_json(const GError *error)
        json_builder_add_string_value(builder, error->message);
        json_builder_end_object(builder);
        json_builder_end_object(builder);
-       fu_util_print_builder(builder, NULL);
+       fu_util_print_builder(console, builder, NULL);
 }
 
 typedef enum {
@@ -3104,7 +2867,7 @@ fu_util_print_version_key_valid(const gchar *key)
 }
 
 gboolean
-fu_util_project_versions_as_json(GHashTable *metadata, GError **error)
+fu_util_project_versions_as_json(FuConsole *console, GHashTable *metadata, GError **error)
 {
        GHashTableIter iter;
        const gchar *key;
@@ -3138,7 +2901,7 @@ fu_util_project_versions_as_json(GHashTable *metadata, GError **error)
        }
        json_builder_end_array(builder);
        json_builder_end_object(builder);
-       return fu_util_print_builder(builder, error);
+       return fu_util_print_builder(console, builder, error);
 }
 
 gchar *
index 3d93ac853f816356bcefb12258eb65a8fc77b72b..77b25ee5079eb996fab3f9a68e9e03fd6eb38840 100644 (file)
@@ -13,6 +13,8 @@
 #include "fwupd-bios-setting-private.h"
 #include "fwupd-security-attr-private.h"
 
+#include "fu-console.h"
+
 /* this is only valid for tools */
 #define FWUPD_ERROR_INVALID_ARGS (FWUPD_ERROR_LAST + 1)
 
@@ -33,44 +35,26 @@ typedef enum {
        FU_SECURITY_ATTR_TO_STRING_FLAG_LAST
 } FuSecurityAttrToStringFlags;
 
-typedef enum {
-       FU_UTIL_TERM_COLOR_BLACK = 30,
-       FU_UTIL_TERM_COLOR_RED = 31,
-       FU_UTIL_TERM_COLOR_GREEN = 32,
-       FU_UTIL_TERM_COLOR_YELLOW = 33,
-       FU_UTIL_TERM_COLOR_BLUE = 34,
-       FU_UTIL_TERM_COLOR_MAGENTA = 35,
-       FU_UTIL_TERM_COLOR_CYAN = 36,
-       FU_UTIL_TERM_COLOR_WHITE = 37,
-} FuUtilTermColor;
-
 void
-fu_util_print_data(const gchar *title, const gchar *msg);
-gchar *
-fu_util_term_format(const gchar *text, FuUtilTermColor fg_color);
-guint
-fu_util_prompt_for_number(guint maxnum);
-gboolean
-fu_util_prompt_for_boolean(gboolean def);
-
-void
-fu_util_print_tree(FwupdClient *client, GNode *n);
+fu_util_print_tree(FuConsole *console, FwupdClient *client, GNode *n);
 gboolean
 fu_util_is_interesting_device(FwupdDevice *dev);
 gchar *
 fu_util_get_user_cache_path(const gchar *fn);
 
-void
-fu_util_warning_box(const gchar *title, const gchar *body, guint width);
 gboolean
-fu_util_prompt_warning(FwupdDevice *device,
+fu_util_prompt_warning(FuConsole *console,
+                      FwupdDevice *device,
                       FwupdRelease *release,
                       const gchar *machine,
                       GError **error);
 gboolean
-fu_util_prompt_warning_fde(FwupdDevice *dev, GError **error);
+fu_util_prompt_warning_fde(FuConsole *console, FwupdDevice *dev, GError **error);
 gboolean
-fu_util_prompt_complete(FwupdDeviceFlags flags, gboolean prompt, GError **error);
+fu_util_prompt_complete(FuConsole *console,
+                       FwupdDeviceFlags flags,
+                       gboolean prompt,
+                       GError **error);
 gboolean
 fu_util_update_reboot(GError **error);
 
@@ -145,21 +129,20 @@ gint
 fu_util_device_order_sort_cb(gconstpointer a, gconstpointer b);
 
 gboolean
-fu_util_switch_branch_warning(FwupdDevice *dev,
+fu_util_switch_branch_warning(FuConsole *console,
+                             FwupdDevice *dev,
                              FwupdRelease *rel,
                              gboolean assume_yes,
                              GError **error);
 void
-fu_util_show_unsupported_warn(void);
+fu_util_show_unsupported_warning(FuConsole *console);
 gboolean
 fu_util_is_url(const gchar *perhaps_url);
 gboolean
-fu_util_setup_interactive_console(GError **error);
-gboolean
-fu_util_print_builder(JsonBuilder *builder, GError **error);
+fu_util_print_builder(FuConsole *console, JsonBuilder *builder, GError **error);
 void
-fu_util_print_error_as_json(const GError *error);
+fu_util_print_error_as_json(FuConsole *console, const GError *error);
 gchar *
 fu_util_project_versions_to_string(GHashTable *metadata);
 gboolean
-fu_util_project_versions_as_json(GHashTable *metadata, GError **error);
+fu_util_project_versions_as_json(FuConsole *console, GHashTable *metadata, GError **error);
index 97967d54919911e7b30a71a665b664a0cb287b86..9023987e81bfb9a4256629d24d5334863b9e538a 100644 (file)
@@ -27,9 +27,9 @@
 #include "fwupd-release-private.h"
 #include "fwupd-remote-private.h"
 
+#include "fu-console.h"
 #include "fu-plugin-private.h"
 #include "fu-polkit-agent.h"
-#include "fu-progressbar.h"
 #include "fu-util-bios-setting.h"
 #include "fu-util-common.h"
 
@@ -56,7 +56,7 @@ struct FuUtilPrivate {
        FwupdInstallFlags flags;
        FwupdClientDownloadFlags download_flags;
        FwupdClient *client;
-       FuProgressbar *progressbar;
+       FuConsole *console;
        gboolean no_remote_check;
        gboolean no_metadata_check;
        gboolean no_reboot_check;
@@ -87,9 +87,9 @@ fu_util_client_notify_cb(GObject *object, GParamSpec *pspec, FuUtilPrivate *priv
 {
        if (priv->as_json)
                return;
-       fu_progressbar_update(priv->progressbar,
-                             fwupd_client_get_status(priv->client),
-                             fwupd_client_get_percentage(priv->client));
+       fu_console_set_progress(priv->console,
+                               fwupd_client_get_status(priv->client),
+                               fwupd_client_get_percentage(priv->client));
 }
 
 static void
@@ -105,9 +105,9 @@ fu_util_update_device_request_cb(FwupdClient *client, FwupdRequest *request, FuU
                g_autofree gchar *tmp = NULL;
 
                /* TRANSLATORS: the user needs to do something, e.g. remove the device */
-               fmt = fu_util_term_format(_("Action Required:"), FU_UTIL_TERM_COLOR_RED);
+               fmt = fu_console_color_format(_("Action Required:"), FU_CONSOLE_COLOR_RED);
                tmp = g_strdup_printf("%s %s", fmt, fwupd_request_get_message(request));
-               fu_progressbar_set_title(priv->progressbar, tmp);
+               fu_console_set_progress_title(priv->console, tmp);
        }
 
        /* save for later */
@@ -147,19 +147,19 @@ fu_util_update_device_changed_cb(FwupdClient *client, FwupdDevice *device, FuUti
                return;
        }
 
-       /* show message in progressbar */
+       /* show message in console */
        if (priv->current_operation == FU_UTIL_OPERATION_UPDATE) {
                /* TRANSLATORS: %1 is a device name */
                str = g_strdup_printf(_("Updating %s…"), fwupd_device_get_name(device));
-               fu_progressbar_set_title(priv->progressbar, str);
+               fu_console_set_progress_title(priv->console, str);
        } else if (priv->current_operation == FU_UTIL_OPERATION_DOWNGRADE) {
                /* TRANSLATORS: %1 is a device name */
                str = g_strdup_printf(_("Downgrading %s…"), fwupd_device_get_name(device));
-               fu_progressbar_set_title(priv->progressbar, str);
+               fu_console_set_progress_title(priv->console, str);
        } else if (priv->current_operation == FU_UTIL_OPERATION_INSTALL) {
                /* TRANSLATORS: %1 is a device name  */
                str = g_strdup_printf(_("Installing on %s…"), fwupd_device_get_name(device));
-               fu_progressbar_set_title(priv->progressbar, str);
+               fu_console_set_progress_title(priv->console, str);
        } else {
                g_warning("no FuUtilOperation set");
        }
@@ -212,8 +212,12 @@ fu_util_prompt_for_device(FuUtilPrivate *priv, GPtrArray *devices, GError **erro
        if (devices_filtered->len == 1) {
                dev = g_ptr_array_index(devices_filtered, 0);
                if (!priv->as_json) {
-                       /* TRANSLATORS: device has been chosen by the daemon for the user */
-                       g_print("%s: %s\n", _("Selected device"), fwupd_device_get_name(dev));
+                       fu_console_print(
+                           priv->console,
+                           "%s: %s",
+                           /* TRANSLATORS: device has been chosen by the daemon for the user */
+                           _("Selected device"),
+                           fwupd_device_get_name(dev));
                }
                return g_object_ref(dev);
        }
@@ -228,17 +232,18 @@ fu_util_prompt_for_device(FuUtilPrivate *priv, GPtrArray *devices, GError **erro
        }
 
        /* TRANSLATORS: get interactive prompt */
-       g_print("%s\n", _("Choose a device:"));
+       fu_console_print_literal(priv->console, _("Choose a device:"));
        /* TRANSLATORS: this is to abort the interactive prompt */
-       g_print("0.\t%s\n", _("Cancel"));
+       fu_console_print(priv->console, "0.\t%s", _("Cancel"));
        for (guint i = 0; i < devices_filtered->len; i++) {
                dev = g_ptr_array_index(devices_filtered, i);
-               g_print("%u.\t%s (%s)\n",
-                       i + 1,
-                       fwupd_device_get_id(dev),
-                       fwupd_device_get_name(dev));
+               fu_console_print(priv->console,
+                                "%u.\t%s (%s)",
+                                i + 1,
+                                fwupd_device_get_id(dev),
+                                fwupd_device_get_name(dev));
        }
-       idx = fu_util_prompt_for_number(devices_filtered->len);
+       idx = fu_console_input_uint(priv->console, devices_filtered->len);
        if (idx == 0) {
                g_set_error_literal(error,
                                    FWUPD_ERROR,
@@ -352,51 +357,59 @@ fu_util_perhaps_show_unreported(FuUtilPrivate *priv, GError **error)
        /* show the success and failures */
        if (!priv->assume_yes && !all_automatic) {
                /* delimit */
-               g_print("________________________________________________\n");
+               fu_console_line(priv->console, 48);
 
                /* failures */
                if (devices_failed->len > 0) {
-                       /* TRANSLATORS: a list of failed updates */
-                       g_print("\n%s\n\n", _("Devices that were not updated correctly:"));
+                       fu_console_print_literal(priv->console,
+                                                /* TRANSLATORS: a list of failed updates */
+                                                _("Devices that were not updated correctly:"));
                        for (guint i = 0; i < devices_failed->len; i++) {
                                FwupdDevice *dev = g_ptr_array_index(devices_failed, i);
                                FwupdRelease *rel = fwupd_device_get_release_default(dev);
-                               g_print(" • %s (%s → %s)\n",
-                                       fwupd_device_get_name(dev),
-                                       fwupd_device_get_version(dev),
-                                       fwupd_release_get_version(rel));
+                               fu_console_print(priv->console,
+                                                " • %s (%s → %s)",
+                                                fwupd_device_get_name(dev),
+                                                fwupd_device_get_version(dev),
+                                                fwupd_release_get_version(rel));
                        }
                }
 
                /* success */
                if (devices_success->len > 0) {
-                       /* TRANSLATORS: a list of successful updates */
-                       g_print("\n%s\n\n", _("Devices that have been updated successfully:"));
+                       fu_console_print_literal(priv->console,
+                                                /* TRANSLATORS: a list of successful updates */
+                                                _("Devices that have been updated successfully:"));
                        for (guint i = 0; i < devices_success->len; i++) {
                                FwupdDevice *dev = g_ptr_array_index(devices_success, i);
                                FwupdRelease *rel = fwupd_device_get_release_default(dev);
-                               g_print(" • %s (%s → %s)\n",
-                                       fwupd_device_get_name(dev),
-                                       fwupd_device_get_version(dev),
-                                       fwupd_release_get_version(rel));
+                               fu_console_print(priv->console,
+                                                " • %s (%s → %s)",
+                                                fwupd_device_get_name(dev),
+                                                fwupd_device_get_version(dev),
+                                                fwupd_release_get_version(rel));
                        }
                }
 
                /* ask for permission */
-               g_print("\n%s\n%s (%s) [Y|n]:\n",
-                       /* TRANSLATORS: explain why we want to upload */
-                       _("Uploading firmware reports helps hardware vendors"
-                         " to quickly identify failing and successful updates"
-                         " on real devices."),
-                       /* TRANSLATORS: ask the user to upload */
-                       _("Upload report now?"),
-                       /* TRANSLATORS: metadata is downloaded from the Internet */
-                       _("Requires internet connection"));
-               if (!fu_util_prompt_for_boolean(TRUE)) {
-                       g_print("\n%s [y|N]:\n",
-                               /* TRANSLATORS: offer to disable this nag */
-                               _("Do you want to disable this feature for future updates?"));
-                       if (fu_util_prompt_for_boolean(FALSE)) {
+               fu_console_print_literal(priv->console,
+                                        /* TRANSLATORS: explain why we want to upload */
+                                        _("Uploading firmware reports helps hardware vendors "
+                                          "to quickly identify failing and successful updates "
+                                          "on real devices."));
+               if (!fu_console_input_bool(priv->console,
+                                          TRUE,
+                                          "%s (%s)",
+                                          /* TRANSLATORS: ask the user to upload */
+                                          _("Upload report now?"),
+                                          /* TRANSLATORS: metadata is downloaded */
+                                          _("Requires internet connection"))) {
+                       if (fu_console_input_bool(priv->console,
+                                                 FALSE,
+                                                 "%s",
+                                                 /* TRANSLATORS: offer to disable this nag */
+                                                 _("Do you want to disable this feature "
+                                                   "for future updates?"))) {
                                for (guint i = 0; i < remotes->len; i++) {
                                        FwupdRemote *remote = g_ptr_array_index(remotes, i);
                                        const gchar *remote_id = fwupd_remote_get_id(remote);
@@ -425,10 +438,12 @@ fu_util_perhaps_show_unreported(FuUtilPrivate *priv, GError **error)
 
        /* offer to make automatic */
        if (!priv->assume_yes && !all_automatic) {
-               g_print("\n%s [y|N]:\n",
-                       /* TRANSLATORS: offer to stop asking the question */
-                       _("Do you want to upload reports automatically for future updates?"));
-               if (fu_util_prompt_for_boolean(FALSE)) {
+               if (fu_console_input_bool(priv->console,
+                                         FALSE,
+                                         "%s",
+                                         /* TRANSLATORS: offer to stop asking the question */
+                                         _("Do you want to upload reports automatically for "
+                                           "future updates?"))) {
                        for (guint i = 0; i < remotes->len; i++) {
                                FwupdRemote *remote = g_ptr_array_index(remotes, i);
                                const gchar *remote_id = fwupd_remote_get_id(remote);
@@ -466,13 +481,13 @@ fu_util_modify_remote_warning(FuUtilPrivate *priv, FwupdRemote *remote, GError *
                return FALSE;
 
        /* TRANSLATORS: a remote here is like a 'repo' or software source */
-       fu_util_warning_box(_("Enable new remote?"), warning_plain, 80);
+       fu_console_box(priv->console, _("Enable new remote?"), warning_plain, 80);
        if (!priv->assume_yes) {
-               /* ask for permission */
-               g_print("\n%s [Y|n]: ",
-                       /* TRANSLATORS: should the remote still be enabled */
-                       _("Agree and enable the remote?"));
-               if (!fu_util_prompt_for_boolean(TRUE)) {
+               if (!fu_console_input_bool(priv->console,
+                                          TRUE,
+                                          "%s",
+                                          /* TRANSLATORS: should the remote still be enabled */
+                                          _("Agree and enable the remote?"))) {
                        g_set_error_literal(error,
                                            FWUPD_ERROR,
                                            FWUPD_ERROR_NOTHING_TO_DO,
@@ -517,7 +532,7 @@ fu_util_get_releases_as_json(FuUtilPrivate *priv, GPtrArray *rels, GError **erro
        }
        json_builder_end_array(builder);
        json_builder_end_object(builder);
-       return fu_util_print_builder(builder, error);
+       return fu_util_print_builder(priv->console, builder, error);
 }
 
 static gboolean
@@ -553,7 +568,7 @@ fu_util_get_devices_as_json(FuUtilPrivate *priv, GPtrArray *devs, GError **error
        }
        json_builder_end_array(builder);
        json_builder_end_object(builder);
-       return fu_util_print_builder(builder, error);
+       return fu_util_print_builder(priv->console, builder, error);
 }
 
 static gboolean
@@ -575,11 +590,12 @@ fu_util_get_devices(FuUtilPrivate *priv, gchar **values, GError **error)
        if (devs->len > 0)
                fu_util_build_device_tree(priv, root, devs, NULL);
        if (g_node_n_children(root) == 0) {
-               /* TRANSLATORS: nothing attached that can be upgraded */
-               g_print("%s\n", _("No hardware detected with firmware update capability"));
+               fu_console_print_literal(priv->console,
+                                        /* TRANSLATORS: nothing attached that can be upgraded */
+                                        _("No hardware detected with firmware update capability"));
                return TRUE;
        }
-       fu_util_print_tree(priv->client, root);
+       fu_util_print_tree(priv->console, priv->client, root);
 
        /* nag? */
        if (!fu_util_perhaps_show_unreported(priv, error))
@@ -604,7 +620,7 @@ fu_util_get_plugins_as_json(FuUtilPrivate *priv, GPtrArray *plugins, GError **er
        }
        json_builder_end_array(builder);
        json_builder_end_object(builder);
-       return fu_util_print_builder(builder, error);
+       return fu_util_print_builder(priv->console, builder, error);
 }
 
 static gboolean
@@ -623,11 +639,11 @@ fu_util_get_plugins(FuUtilPrivate *priv, gchar **values, GError **error)
        for (guint i = 0; i < plugins->len; i++) {
                FuPlugin *plugin = g_ptr_array_index(plugins, i);
                g_autofree gchar *str = fu_util_plugin_to_string(FWUPD_PLUGIN(plugin), 0);
-               g_print("%s\n", str);
+               fu_console_print_literal(priv->console, str);
        }
        if (plugins->len == 0) {
                /* TRANSLATORS: nothing found */
-               g_print("%s\n", _("No plugins found"));
+               fu_console_print_literal(priv->console, _("No plugins found"));
        }
 
        /* success */
@@ -670,12 +686,12 @@ static void
 fu_util_display_current_message(FuUtilPrivate *priv)
 {
        /* TRANSLATORS: success message */
-       g_print("%s\n", _("Successfully installed firmware"));
+       fu_console_print_literal(priv->console, _("Successfully installed firmware"));
 
        /* print all POST requests */
        for (guint i = 0; i < priv->post_requests->len; i++) {
                FwupdRequest *request = g_ptr_array_index(priv->post_requests, i);
-               g_print("%s\n", fu_util_request_get_message(request));
+               fu_console_print_literal(priv->console, fu_util_request_get_message(request));
        }
 }
 
@@ -753,10 +769,11 @@ fu_util_device_test_component(FuUtilPrivate *priv,
        if (device == NULL) {
                if (!priv->as_json) {
                        g_autofree gchar *msg = NULL;
-                       /* TRANSLATORS: this is for the device tests */
-                       msg = fu_util_term_format(_("Did not find any devices with matching GUIDs"),
-                                                 FU_UTIL_TERM_COLOR_RED);
-                       g_print("%s: %s", name, msg);
+                       msg = fu_console_color_format(
+                           /* TRANSLATORS: this is for the device tests */
+                           _("Did not find any devices with matching GUIDs"),
+                           FU_CONSOLE_COLOR_RED);
+                       fu_console_print(priv->console, "%s: %s", name, msg);
                }
                json_builder_set_member_name(helper->builder, "error");
                json_builder_add_string_value(helper->builder, "no devices found");
@@ -783,8 +800,8 @@ fu_util_device_test_component(FuUtilPrivate *priv,
                                    _("The device version did not match: got %s, expected %s"),
                                    fu_device_get_version(device),
                                    version);
-                               msg = fu_util_term_format(str2, FU_UTIL_TERM_COLOR_RED);
-                               g_print("%s: %s", name, msg);
+                               msg = fu_console_color_format(str2, FU_CONSOLE_COLOR_RED);
+                               fu_console_print(priv->console, "%s: %s", name, msg);
                        }
                        json_builder_set_member_name(helper->builder, "error");
                        json_builder_add_string_value(helper->builder, str);
@@ -796,8 +813,8 @@ fu_util_device_test_component(FuUtilPrivate *priv,
        if (!priv->as_json) {
                g_autofree gchar *msg = NULL;
                /* TRANSLATORS: this is for the device tests */
-               msg = fu_util_term_format(_("OK!"), FU_UTIL_TERM_COLOR_GREEN);
-               g_print("%s: %s\n", helper->name, msg);
+               msg = fu_console_color_format(_("OK!"), FU_CONSOLE_COLOR_GREEN);
+               fu_console_print(priv->console, "%s: %s", helper->name, msg);
        }
        helper->nr_success++;
        return TRUE;
@@ -894,8 +911,8 @@ fu_util_device_test_step(FuUtilPrivate *priv,
                }
                if (!priv->as_json) {
                        g_autofree gchar *msg = NULL;
-                       msg = fu_util_term_format(error_local->message, FU_UTIL_TERM_COLOR_RED);
-                       g_print("%s: %s", helper->name, msg);
+                       msg = fu_console_color_format(error_local->message, FU_CONSOLE_COLOR_RED);
+                       fu_console_print(priv->console, "%s: %s", helper->name, msg);
                }
                json_builder_set_member_name(helper->builder, "error");
                json_builder_add_string_value(helper->builder, error_local->message);
@@ -1029,7 +1046,7 @@ fu_util_inhibit(FuUtilPrivate *priv, gchar **values, GError **error)
        /* TRANSLATORS: CTRL^C [holding control, and then pressing C] will exit the program */
        g_string_append(str, _("Use CTRL^C to cancel."));
        /* TRANSLATORS: this CLI tool is now preventing system updates */
-       fu_util_warning_box(_("System Update Inhibited"), str->str, 80);
+       fu_console_box(priv->console, _("System Update Inhibited"), str->str, 80);
        g_main_loop_run(priv->loop);
        return TRUE;
 }
@@ -1095,7 +1112,7 @@ fu_util_device_test_full(FuUtilPrivate *priv,
        /* dump to screen as JSON format */
        json_builder_end_object(builder);
        if (priv->as_json) {
-               if (!fu_util_print_builder(builder, error))
+               if (!fu_util_print_builder(priv->console, builder, error))
                        return FALSE;
        }
 
@@ -1217,7 +1234,7 @@ fu_util_local_install(FuUtilPrivate *priv, gchar **values, GError **error)
 
        /* detect bitlocker */
        if (dev != NULL && !priv->no_safety_check && !priv->assume_yes) {
-               if (!fu_util_prompt_warning_fde(dev, error))
+               if (!fu_util_prompt_warning_fde(priv->console, dev, error))
                        return FALSE;
        }
 
@@ -1238,7 +1255,7 @@ fu_util_local_install(FuUtilPrivate *priv, gchar **values, GError **error)
        }
 
        /* show reboot if needed */
-       return fu_util_prompt_complete(priv->completion_flags, TRUE, error);
+       return fu_util_prompt_complete(priv->console, priv->completion_flags, TRUE, error);
 }
 
 static gboolean
@@ -1256,7 +1273,7 @@ fu_util_get_details_as_json(FuUtilPrivate *priv, GPtrArray *devs, GError **error
        }
        json_builder_end_array(builder);
        json_builder_end_object(builder);
-       return fu_util_print_builder(builder, error);
+       return fu_util_print_builder(priv->console, builder, error);
 }
 
 static gboolean
@@ -1284,7 +1301,7 @@ fu_util_get_details(FuUtilPrivate *priv, gchar **values, GError **error)
                return fu_util_get_details_as_json(priv, array, error);
 
        fu_util_build_device_tree(priv, root, array, NULL);
-       fu_util_print_tree(priv->client, root);
+       fu_util_print_tree(priv->console, priv->client, root);
 
        return TRUE;
 }
@@ -1322,12 +1339,13 @@ fu_util_report_history_for_remote(FuUtilPrivate *priv,
 
        /* ask for permission */
        if (!priv->assume_yes && !fwupd_remote_get_automatic_reports(remote)) {
-               fu_util_print_data(_("Target"), fwupd_remote_get_report_uri(remote));
-               fu_util_print_data(_("Payload"), data);
+               fu_console_print_kv(priv->console,
+                                   _("Target"),
+                                   fwupd_remote_get_report_uri(remote));
+               fu_console_print_kv(priv->console, _("Payload"), data);
                if (sig != NULL)
-                       fu_util_print_data(_("Signature"), sig);
-               g_print("%s [Y|n]: ", _("Proceed with upload?"));
-               if (!fu_util_prompt_for_boolean(TRUE)) {
+                       fu_console_print_kv(priv->console, _("Signature"), sig);
+               if (!fu_console_input_bool(priv->console, TRUE, "%s", _("Proceed with upload?"))) {
                        g_set_error_literal(error,
                                            FWUPD_ERROR,
                                            FWUPD_ERROR_PERMISSION_DENIED,
@@ -1347,10 +1365,12 @@ fu_util_report_history_for_remote(FuUtilPrivate *priv,
 
        /* server wanted us to see a message */
        if (uri != NULL) {
-               g_print("%s %s\n",
-                       /* TRANSLATORS: the server sent the user a small message */
-                       _("Update failure is a known issue, visit this URL for more information:"),
-                       uri);
+               fu_console_print(
+                   priv->console,
+                   "%s %s",
+                   /* TRANSLATORS: the server sent the user a small message */
+                   _("Update failure is a known issue, visit this URL for more information:"),
+                   uri);
        }
 
        /* success */
@@ -1464,7 +1484,7 @@ fu_util_report_history(FuUtilPrivate *priv, gchar **values, GError **error)
                                        "Successfully uploaded %u reports",
                                        g_hash_table_size(report_map)),
                               g_hash_table_size(report_map));
-       g_print("%s\n", str->str);
+       fu_console_print_literal(priv->console, str->str);
        return TRUE;
 }
 
@@ -1540,7 +1560,7 @@ fu_util_get_history(FuUtilPrivate *priv, gchar **values, GError **error)
                }
        }
 
-       fu_util_print_tree(priv->client, root);
+       fu_util_print_tree(priv->console, priv->client, root);
 
        return TRUE;
 }
@@ -1658,8 +1678,9 @@ fu_util_verify_update(FuUtilPrivate *priv, gchar **values, GError **error)
                g_prefix_error(error, "failed to verify update %s: ", fu_device_get_name(dev));
                return FALSE;
        }
+
        /* TRANSLATORS: success message when user refreshes device checksums */
-       g_print("%s\n", _("Successfully updated device checksums"));
+       fu_console_print_literal(priv->console, _("Successfully updated device checksums"));
 
        return TRUE;
 }
@@ -1673,14 +1694,15 @@ fu_util_download_metadata_enable_lvfs(FuUtilPrivate *priv, GError **error)
        remote = fwupd_client_get_remote_by_id(priv->client, "lvfs", priv->cancellable, error);
        if (remote == NULL)
                return TRUE;
-       g_print("%s\n%s\n%s [Y|n]: ",
-               /* TRANSLATORS: explain why no metadata available */
-               _("No remotes are currently enabled so no metadata is available."),
-               /* TRANSLATORS: explain why no metadata available */
-               _("Metadata can be obtained from the Linux Vendor Firmware Service."),
-               /* TRANSLATORS: Turn on the remote */
-               _("Enable this remote?"));
-       if (!fu_util_prompt_for_boolean(TRUE))
+       fu_console_print(priv->console,
+                        /* TRANSLATORS: explain why no metadata available */
+                        _("No remotes are currently enabled so no metadata is available."));
+       fu_console_print(priv->console,
+                        /* TRANSLATORS: explain why no metadata available */
+                        _("Metadata can be obtained from the Linux Vendor Firmware Service."));
+
+       /* TRANSLATORS: Turn on the remote */
+       if (!fu_console_input_bool(priv->console, TRUE, "%s", _("Enable this remote?")))
                return TRUE;
        if (!fwupd_client_modify_remote(priv->client,
                                        fwupd_remote_get_id(remote),
@@ -1768,7 +1790,10 @@ fu_util_download_metadata(FuUtilPrivate *priv, GError **error)
                if (fwupd_remote_get_kind(remote) != FWUPD_REMOTE_KIND_DOWNLOAD)
                        continue;
                download_remote_enabled = TRUE;
-               g_print("%s %s\n", _("Updating"), fwupd_remote_get_id(remote));
+               fu_console_print(priv->console,
+                                "%s %s",
+                                _("Updating"),
+                                fwupd_remote_get_id(remote));
                if (!fwupd_client_refresh_remote(priv->client, remote, priv->cancellable, error))
                        return FALSE;
        }
@@ -1807,7 +1832,7 @@ fu_util_download_metadata(FuUtilPrivate *priv, GError **error)
                                        "%u local devices supported",
                                        devices_supported_cnt),
                               devices_supported_cnt);
-       g_print("%s\n", str->str);
+       fu_console_print_literal(priv->console, str->str);
        return TRUE;
 }
 
@@ -1834,7 +1859,7 @@ fu_util_refresh(FuUtilPrivate *priv, gchar **values, GError **error)
                return FALSE;
 
        /* TRANSLATORS: success message -- the user can do this by-hand too */
-       g_print("%s\n", _("Successfully refreshed metadata manually"));
+       fu_console_print_literal(priv->console, _("Successfully refreshed metadata manually"));
        return TRUE;
 }
 
@@ -1845,7 +1870,7 @@ fu_util_get_results_as_json(FuUtilPrivate *priv, FwupdDevice *res, GError **erro
        json_builder_begin_object(builder);
        fwupd_device_to_json_full(res, builder, FWUPD_DEVICE_FLAG_TRUSTED);
        json_builder_end_object(builder);
-       return fu_util_print_builder(builder, error);
+       return fu_util_print_builder(priv->console, builder, error);
 }
 
 static gboolean
@@ -1868,7 +1893,7 @@ fu_util_get_results(FuUtilPrivate *priv, gchar **values, GError **error)
        if (priv->as_json)
                return fu_util_get_results_as_json(priv, rel, error);
        tmp = fu_util_device_to_string(priv->client, rel, 0);
-       g_print("%s", tmp);
+       fu_console_print_literal(priv->console, tmp);
        return TRUE;
 }
 
@@ -1897,14 +1922,14 @@ fu_util_get_releases(FuUtilPrivate *priv, gchar **values, GError **error)
 
        if (rels->len == 0) {
                /* TRANSLATORS: no repositories to download from */
-               g_print("%s\n", _("No releases available"));
+               fu_console_print_literal(priv->console, _("No releases available"));
                return TRUE;
        }
        if (g_getenv("FWUPD_VERBOSE") != NULL) {
                for (guint i = 0; i < rels->len; i++) {
                        FwupdRelease *rel = g_ptr_array_index(rels, i);
                        g_autofree gchar *tmp = fwupd_release_to_string(rel);
-                       g_print("%s\n", tmp);
+                       fu_console_print_literal(priv->console, tmp);
                }
        } else {
                g_autoptr(GNode) root = g_node_new(NULL);
@@ -1912,7 +1937,7 @@ fu_util_get_releases(FuUtilPrivate *priv, gchar **values, GError **error)
                        FwupdRelease *rel = g_ptr_array_index(rels, i);
                        g_node_append_data(root, rel);
                }
-               fu_util_print_tree(priv->client, root);
+               fu_util_print_tree(priv->console, priv->client, root);
        }
 
        return TRUE;
@@ -1940,14 +1965,17 @@ fu_util_prompt_for_release(FuUtilPrivate *priv, GPtrArray *rels, GError **error)
        }
 
        /* TRANSLATORS: get interactive prompt */
-       g_print("%s\n", _("Choose a release:"));
+       fu_console_print_literal(priv->console, _("Choose a release:"));
        /* TRANSLATORS: this is to abort the interactive prompt */
-       g_print("0.\t%s\n", _("Cancel"));
+       fu_console_print(priv->console, "0.\t%s", _("Cancel"));
        for (guint i = 0; i < rels->len; i++) {
                FwupdRelease *rel_tmp = g_ptr_array_index(rels, i);
-               g_print("%u.\t%s\n", i + 1, fwupd_release_get_version(rel_tmp));
+               fu_console_print(priv->console,
+                                "%u.\t%s",
+                                i + 1,
+                                fwupd_release_get_version(rel_tmp));
        }
-       idx = fu_util_prompt_for_number(rels->len);
+       idx = fu_console_input_uint(priv->console, rels->len);
        if (idx == 0) {
                g_set_error_literal(error,
                                    FWUPD_ERROR,
@@ -1976,8 +2004,9 @@ fu_util_verify(FuUtilPrivate *priv, gchar **values, GError **error)
                g_prefix_error(error, "failed to verify %s: ", fu_device_get_name(dev));
                return FALSE;
        }
+
        /* TRANSLATORS: success message when user verified device checksums */
-       g_print("%s\n", _("Successfully verified device checksums"));
+       fu_console_print_literal(priv->console, _("Successfully verified device checksums"));
 
        return TRUE;
 }
@@ -2001,7 +2030,7 @@ fu_util_unlock(FuUtilPrivate *priv, gchar **values, GError **error)
        if (fwupd_device_has_flag(dev, FWUPD_DEVICE_FLAG_NEEDS_REBOOT))
                priv->completion_flags |= FWUPD_DEVICE_FLAG_NEEDS_REBOOT;
 
-       return fu_util_prompt_complete(priv->completion_flags, TRUE, error);
+       return fu_util_prompt_complete(priv->console, priv->completion_flags, TRUE, error);
 }
 
 static gboolean
@@ -2025,20 +2054,22 @@ fu_util_perhaps_refresh_remotes(FuUtilPrivate *priv, GError **error)
 
        /* ask for permission */
        if (!priv->assume_yes) {
-               /* TRANSLATORS: the metadata is very out of date; %u is a number > 1 */
-               g_print(ngettext("Firmware metadata has not been updated for %u"
-                                " day and may not be up to date.",
-                                "Firmware metadata has not been updated for %u"
-                                " days and may not be up to date.",
-                                (gint)age_limit_days),
-                       (guint)age_limit_days);
-               g_print("\n\n");
-               g_print("%s (%s) [y|N]: ",
-                       /* TRANSLATORS: ask the user if we can update the metadata */
-                       _("Update now?"),
-                       /* TRANSLATORS: metadata is downloaded from the Internet */
-                       _("Requires internet connection"));
-               if (!fu_util_prompt_for_boolean(FALSE))
+               fu_console_print(
+                   priv->console,
+                   /* TRANSLATORS: the metadata is very out of date; %u is a number > 1 */
+                   ngettext("Firmware metadata has not been updated for %u"
+                            " day and may not be up to date.",
+                            "Firmware metadata has not been updated for %u"
+                            " days and may not be up to date.",
+                            (gint)age_limit_days),
+                   (guint)age_limit_days);
+               if (!fu_console_input_bool(priv->console,
+                                          FALSE,
+                                          "%s (%s)",
+                                          /* TRANSLATORS: ask if we can update metadata */
+                                          _("Update now?"),
+                                          /* TRANSLATORS: metadata is downloaded */
+                                          _("Requires internet connection")))
                        return TRUE;
        }
 
@@ -2083,7 +2114,7 @@ fu_util_get_updates_as_json(FuUtilPrivate *priv, GPtrArray *devices, GError **er
        }
        json_builder_end_array(builder);
        json_builder_end_object(builder);
-       return fu_util_print_builder(builder, error);
+       return fu_util_print_builder(priv->console, builder, error);
 }
 
 static gboolean
@@ -2163,20 +2194,23 @@ fu_util_get_updates(FuUtilPrivate *priv, gchar **values, GError **error)
 
        /* devices that have no updates available for whatever reason */
        if (devices_no_support->len > 0) {
-               /* TRANSLATORS: message letting the user know no device upgrade
-                * available due to missing on LVFS */
-               g_printerr("%s\n", _("Devices with no available firmware updates: "));
+               fu_console_print_literal(priv->console,
+                                        /* TRANSLATORS: message letting the user know no device
+                                         * upgrade available due to missing on LVFS */
+                                        _("Devices with no available firmware updates: "));
                for (guint i = 0; i < devices_no_support->len; i++) {
                        FwupdDevice *dev = g_ptr_array_index(devices_no_support, i);
-                       g_printerr(" • %s\n", fwupd_device_get_name(dev));
+                       fu_console_print(priv->console, " • %s", fwupd_device_get_name(dev));
                }
        }
        if (devices_no_upgrades->len > 0) {
-               /* TRANSLATORS: message letting the user know no device upgrade available */
-               g_printerr("%s\n", _("Devices with the latest available firmware version:"));
+               fu_console_print_literal(
+                   priv->console,
+                   /* TRANSLATORS: message letting the user know no device upgrade available */
+                   _("Devices with the latest available firmware version:"));
                for (guint i = 0; i < devices_no_upgrades->len; i++) {
                        FwupdDevice *dev = g_ptr_array_index(devices_no_upgrades, i);
-                       g_printerr(" • %s\n", fwupd_device_get_name(dev));
+                       fu_console_print(priv->console, " • %s", fwupd_device_get_name(dev));
                }
        }
 
@@ -2203,7 +2237,7 @@ fu_util_get_updates(FuUtilPrivate *priv, gchar **values, GError **error)
                return FALSE;
        }
 
-       fu_util_print_tree(priv->client, root);
+       fu_util_print_tree(priv->console, priv->client, root);
 
        /* success */
        return TRUE;
@@ -2224,7 +2258,7 @@ fu_util_get_remotes_as_json(FuUtilPrivate *priv, GPtrArray *remotes, GError **er
        }
        json_builder_end_array(builder);
        json_builder_end_object(builder);
-       return fu_util_print_builder(builder, error);
+       return fu_util_print_builder(priv->console, builder, error);
 }
 
 static gboolean
@@ -2241,7 +2275,7 @@ fu_util_get_remotes(FuUtilPrivate *priv, gchar **values, GError **error)
 
        if (remotes->len == 0) {
                /* TRANSLATORS: no repositories to download from */
-               g_print("%s\n", _("No remotes available"));
+               fu_console_print_literal(priv->console, _("No remotes available"));
                return TRUE;
        }
 
@@ -2249,7 +2283,7 @@ fu_util_get_remotes(FuUtilPrivate *priv, gchar **values, GError **error)
                FwupdRemote *remote_tmp = g_ptr_array_index(remotes, i);
                g_node_append_data(root, remote_tmp);
        }
-       fu_util_print_tree(priv->client, root);
+       fu_util_print_tree(priv->console, priv->client, root);
 
        return TRUE;
 }
@@ -2324,15 +2358,16 @@ fu_util_prompt_warning_bkc(FuUtilPrivate *priv, FwupdDevice *dev, FwupdRelease *
            fwupd_release_get_version(rel),
            cmd);
 
-       /* TRANSLATORS: the best known configuration is a set of software that we know works well
-        * together. In the OEM and ODM industries it is often called a BKC */
-       fu_util_warning_box(_("Deviate from the best known configuration?"), str->str, 80);
+       fu_console_box(
+           priv->console,
+           /* TRANSLATORS: the best known configuration is a set of software that we know works
+            * well together. In the OEM and ODM industries it is often called a BKC */
+           _("Deviate from the best known configuration?"),
+           str->str,
+           80);
 
-       /* ask for confirmation */
-       g_print("\n%s [Y|n]: ",
-               /* TRANSLATORS: prompt to apply the update */
-               _("Perform operation?"));
-       if (!fu_util_prompt_for_boolean(TRUE)) {
+       /* TRANSLATORS: prompt to apply the update */
+       if (!fu_console_input_bool(priv->console, TRUE, "%s", _("Perform operation?"))) {
                g_set_error_literal(error,
                                    FWUPD_ERROR,
                                    FWUPD_ERROR_NOTHING_TO_DO,
@@ -2398,7 +2433,11 @@ fu_util_prompt_warning_composite(FuUtilPrivate *priv,
                                    g_strdup_printf("%s %s",
                                                    fwupd_client_get_host_product(priv->client),
                                                    fwupd_client_get_host_product(priv->client));
-                               if (!fu_util_prompt_warning(dev_tmp, rel_tmp, title, error))
+                               if (!fu_util_prompt_warning(priv->console,
+                                                           dev_tmp,
+                                                           rel_tmp,
+                                                           title,
+                                                           error))
                                        return FALSE;
                                break;
                        }
@@ -2431,9 +2470,9 @@ fu_util_update_device_with_release(FuUtilPrivate *priv,
        }
        if (!priv->no_safety_check && !priv->assume_yes) {
                const gchar *title = fwupd_client_get_host_product(priv->client);
-               if (!fu_util_prompt_warning(dev, rel, title, error))
+               if (!fu_util_prompt_warning(priv->console, dev, rel, title, error))
                        return FALSE;
-               if (!fu_util_prompt_warning_fde(dev, error))
+               if (!fu_util_prompt_warning_fde(priv->console, dev, error))
                        return FALSE;
                if (!fu_util_prompt_warning_composite(priv, dev, rel, error))
                        return FALSE;
@@ -2529,13 +2568,14 @@ fu_util_update(FuUtilPrivate *priv, gchar **values, GError **error)
                        continue;
                if (!fwupd_device_has_flag(dev, FWUPD_DEVICE_FLAG_SUPPORTED)) {
                        if (!no_updates_header) {
-                               g_printerr("%s\n",
-                                          /* TRANSLATORS: message letting the user know no device
-                                           * upgrade available due to missing on LVFS */
-                                          _("Devices with no available firmware updates: "));
+                               fu_console_print_literal(
+                                   priv->console,
+                                   /* TRANSLATORS: message letting the user know no
+                                    * device upgrade available due to missing on LVFS */
+                                   _("Devices with no available firmware updates: "));
                                no_updates_header = TRUE;
                        }
-                       g_printerr(" • %s\n", fwupd_device_get_name(dev));
+                       fu_console_print(priv->console, " • %s", fwupd_device_get_name(dev));
                        continue;
                }
 
@@ -2560,14 +2600,14 @@ fu_util_update(FuUtilPrivate *priv, gchar **values, GError **error)
                                                 &error_local);
                if (rels == NULL) {
                        if (!latest_header) {
-                               g_printerr(
-                                   "%s\n",
+                               fu_console_print_literal(
+                                   priv->console,
                                    /* TRANSLATORS: message letting the user know no device upgrade
                                     * available */
                                    _("Devices with the latest available firmware version:"));
                                latest_header = TRUE;
                        }
-                       g_printerr(" • %s\n", fwupd_device_get_name(dev));
+                       fu_console_print(priv->console, " • %s", fwupd_device_get_name(dev));
                        /* discard the actual reason from user, but leave for debugging */
                        g_debug("%s", error_local->message);
                        continue;
@@ -2598,7 +2638,7 @@ fu_util_update(FuUtilPrivate *priv, gchar **values, GError **error)
                return TRUE;
        }
 
-       return fu_util_prompt_complete(priv->completion_flags, TRUE, error);
+       return fu_util_prompt_complete(priv->console, priv->completion_flags, TRUE, error);
 }
 
 static gboolean
@@ -2626,7 +2666,7 @@ fu_util_remote_modify(FuUtilPrivate *priv, gchar **values, GError **error)
                return FALSE;
 
        /* TRANSLATORS: success message for a per-remote setting change */
-       g_print("%s\n", _("Successfully modified remote"));
+       fu_console_print_literal(priv->console, _("Successfully modified remote"));
        return TRUE;
 }
 
@@ -2657,18 +2697,19 @@ fu_util_remote_enable(FuUtilPrivate *priv, gchar **values, GError **error)
        /* ask for permission to refresh */
        if (priv->no_remote_check || fwupd_remote_get_kind(remote) != FWUPD_REMOTE_KIND_DOWNLOAD) {
                /* TRANSLATORS: success message */
-               g_print("%s\n", _("Successfully enabled remote"));
+               fu_console_print_literal(priv->console, _("Successfully enabled remote"));
                return TRUE;
        }
        if (!priv->assume_yes) {
-               g_print("%s (%s) [Y|n]: ",
-                       /* TRANSLATORS: ask the user if we can update the metadata */
-                       _("Do you want to refresh this remote now?"),
-                       /* TRANSLATORS: metadata is downloaded from the Internet */
-                       _("Requires internet connection"));
-               if (!fu_util_prompt_for_boolean(TRUE)) {
+               if (!fu_console_input_bool(priv->console,
+                                          TRUE,
+                                          "%s (%s)",
+                                          /* TRANSLATORS: ask if we can update the metadata */
+                                          _("Do you want to refresh this remote now?"),
+                                          /* TRANSLATORS: metadata is downloaded */
+                                          _("Requires internet connection"))) {
                        /* TRANSLATORS: success message */
-                       g_print("%s\n", _("Successfully enabled remote"));
+                       fu_console_print_literal(priv->console, _("Successfully enabled remote"));
                        return TRUE;
                }
        }
@@ -2676,7 +2717,7 @@ fu_util_remote_enable(FuUtilPrivate *priv, gchar **values, GError **error)
                return FALSE;
 
        /* TRANSLATORS: success message */
-       g_print("\n%s\n", _("Successfully enabled and refreshed remote"));
+       fu_console_print_literal(priv->console, _("Successfully enabled and refreshed remote"));
        return TRUE;
 }
 
@@ -2706,7 +2747,7 @@ fu_util_remote_disable(FuUtilPrivate *priv, gchar **values, GError **error)
                return FALSE;
 
        /* TRANSLATORS: success message */
-       g_print("%s\n", _("Successfully disabled remote"));
+       fu_console_print_literal(priv->console, _("Successfully disabled remote"));
        return TRUE;
 }
 
@@ -2767,7 +2808,7 @@ fu_util_downgrade(FuUtilPrivate *priv, gchar **values, GError **error)
                return TRUE;
        }
 
-       return fu_util_prompt_complete(priv->completion_flags, TRUE, error);
+       return fu_util_prompt_complete(priv->console, priv->completion_flags, TRUE, error);
 }
 
 static gboolean
@@ -2803,7 +2844,7 @@ fu_util_reinstall(FuUtilPrivate *priv, gchar **values, GError **error)
                return TRUE;
        }
 
-       return fu_util_prompt_complete(priv->completion_flags, TRUE, error);
+       return fu_util_prompt_complete(priv->console, priv->completion_flags, TRUE, error);
 }
 
 static gboolean
@@ -2860,7 +2901,7 @@ fu_util_install(FuUtilPrivate *priv, gchar **values, GError **error)
                return TRUE;
        }
 
-       return fu_util_prompt_complete(priv->completion_flags, TRUE, error);
+       return fu_util_prompt_complete(priv->console, priv->completion_flags, TRUE, error);
 }
 
 static gboolean
@@ -2914,14 +2955,17 @@ fu_util_switch_branch(FuUtilPrivate *priv, gchar **values, GError **error)
 
                /* TRANSLATORS: get interactive prompt, where branch is the
                 * supplier of the firmware, e.g. "non-free" or "free" */
-               g_print("%s\n", _("Choose a branch:"));
+               fu_console_print_literal(priv->console, _("Choose a branch:"));
                /* TRANSLATORS: this is to abort the interactive prompt */
-               g_print("0.\t%s\n", _("Cancel"));
+               fu_console_print(priv->console, "0.\t%s", _("Cancel"));
                for (guint i = 0; i < branches->len; i++) {
                        const gchar *branch_tmp = g_ptr_array_index(branches, i);
-                       g_print("%u.\t%s\n", i + 1, fu_util_branch_for_display(branch_tmp));
+                       fu_console_print(priv->console,
+                                        "%u.\t%s",
+                                        i + 1,
+                                        fu_util_branch_for_display(branch_tmp));
                }
-               idx = fu_util_prompt_for_number(branches->len);
+               idx = fu_console_input_uint(priv->console, branches->len);
                if (idx == 0) {
                        g_set_error_literal(error,
                                            FWUPD_ERROR,
@@ -2961,7 +3005,7 @@ fu_util_switch_branch(FuUtilPrivate *priv, gchar **values, GError **error)
        }
 
        /* we're switching branch */
-       if (!fu_util_switch_branch_warning(dev, rel, priv->assume_yes, error))
+       if (!fu_util_switch_branch_warning(priv->console, dev, rel, priv->assume_yes, error))
                return FALSE;
 
        /* update the console if composite devices are also updated */
@@ -2982,7 +3026,7 @@ fu_util_switch_branch(FuUtilPrivate *priv, gchar **values, GError **error)
                return TRUE;
        }
 
-       return fu_util_prompt_complete(priv->completion_flags, TRUE, error);
+       return fu_util_prompt_complete(priv->console, priv->completion_flags, TRUE, error);
 }
 
 static gboolean
@@ -3038,10 +3082,12 @@ fu_util_activate(FuUtilPrivate *priv, gchar **values, GError **error)
                        continue;
                if (!fu_device_has_flag(device, FWUPD_DEVICE_FLAG_NEEDS_ACTIVATION))
                        continue;
-               g_print("%s %s…\n",
-                       /* TRANSLATORS: shown when shutting down to switch to the new version */
-                       _("Activating firmware update for"),
-                       fwupd_device_get_name(device));
+               fu_console_print(
+                   priv->console,
+                   "%s %s…",
+                   /* TRANSLATORS: shown when shutting down to switch to the new version */
+                   _("Activating firmware update for"),
+                   fwupd_device_get_name(device));
                if (!fwupd_client_activate(priv->client,
                                           priv->cancellable,
                                           fwupd_device_get_id(device),
@@ -3051,7 +3097,7 @@ fu_util_activate(FuUtilPrivate *priv, gchar **values, GError **error)
 
        /* TRANSLATORS: success message -- where activation is making the new
         * firmware take effect, usually after updating offline */
-       g_print("%s\n", _("Successfully activated all devices"));
+       fu_console_print_literal(priv->console, _("Successfully activated all devices"));
        return TRUE;
 }
 
@@ -3097,7 +3143,7 @@ fu_util_get_checksums_as_json(FuUtilPrivate *priv, gchar **csums, GError **error
                json_builder_add_string_value(builder, csums[i]);
        json_builder_end_array(builder);
        json_builder_end_object(builder);
-       return fu_util_print_builder(builder, error);
+       return fu_util_print_builder(priv->console, builder, error);
 }
 
 static gboolean
@@ -3123,15 +3169,15 @@ fu_util_get_approved_firmware(FuUtilPrivate *priv, gchar **values, GError **erro
        if (g_strv_length(checksums) == 0) {
                /* TRANSLATORS: approved firmware has been checked by
                 * the domain administrator */
-               g_print("%s\n", _("There is no approved firmware."));
+               fu_console_print_literal(priv->console, _("There is no approved firmware."));
        } else {
-               g_print(
-                   "%s\n",
+               fu_console_print_literal(
+                   priv->console,
                    /* TRANSLATORS: approved firmware has been checked by
                     * the domain administrator */
                    ngettext("Approved firmware:", "Approved firmware:", g_strv_length(checksums)));
                for (guint i = 0; checksums[i] != NULL; i++)
-                       g_print(" * %s\n", checksums[i]);
+                       fu_console_print(priv->console, " * %s", checksums[i]);
        }
        return TRUE;
 }
@@ -3154,10 +3200,11 @@ fu_util_modify_config(FuUtilPrivate *priv, gchar **values, GError **error)
                                        error))
                return FALSE;
        if (!priv->assume_yes) {
-               g_print("%s [Y|n]: ",
-                       /* TRANSLATORS: configuration changes only take effect on restart */
-                       _("Restart the daemon to make the change effective?"));
-               if (!fu_util_prompt_for_boolean(FALSE))
+               if (!fu_console_input_bool(priv->console,
+                                          FALSE,
+                                          "%s",
+                                          /* TRANSLATORS: changes only take effect on restart */
+                                          _("Restart the daemon to make the change effective?")))
                        return TRUE;
        }
 #ifdef HAVE_SYSTEMD
@@ -3165,7 +3212,7 @@ fu_util_modify_config(FuUtilPrivate *priv, gchar **values, GError **error)
                return FALSE;
 #endif
        /* TRANSLATORS: success message -- a per-system setting value */
-       g_print("%s\n", _("Successfully modified configuration value"));
+       fu_console_print_literal(priv->console, _("Successfully modified configuration value"));
        return TRUE;
 }
 
@@ -3219,18 +3266,18 @@ fu_util_upload_security(FuUtilPrivate *priv, GPtrArray *attrs, GError **error)
        }
        if (!priv->assume_yes && !fwupd_remote_get_automatic_security_reports(remote)) {
                g_autofree gchar *tmp = NULL;
-               /* TRANSLATORS: ask the user to share, %s is something like:
-                * "Linux Vendor Firmware Service" */
-               tmp =
-                   g_strdup_printf("Upload these anonymous results to the %s to help other users?",
-                                   fwupd_remote_get_title(remote));
-
-               g_print("\n%s [y|N]: ", tmp);
-               if (!fu_util_prompt_for_boolean(FALSE)) {
-                       g_print("%s [Y|n]: ",
-                               /* TRANSLATORS: stop nagging the user */
-                               _("Ask again next time?"));
-                       if (!fu_util_prompt_for_boolean(TRUE)) {
+               if (!fu_console_input_bool(priv->console,
+                                          FALSE,
+                                          /* TRANSLATORS: ask the user to share, %s is something
+                                           * like: "Linux Vendor Firmware Service" */
+                                          _("Upload these anonymous results to the %s to help "
+                                            "other users?"),
+                                          fwupd_remote_get_title(remote))) {
+                       if (!fu_console_input_bool(priv->console,
+                                                  TRUE,
+                                                  "%s",
+                                                  /* TRANSLATORS: stop nagging the user */
+                                                  _("Ask again next time?"))) {
                                if (!fwupd_client_modify_remote(priv->client,
                                                                fwupd_remote_get_id(remote),
                                                                "SecurityReportURI",
@@ -3308,12 +3355,13 @@ fu_util_upload_security(FuUtilPrivate *priv, GPtrArray *attrs, GError **error)
 
        /* ask for permission */
        if (!priv->assume_yes && !fwupd_remote_get_automatic_security_reports(remote)) {
-               fu_util_print_data(_("Target"), fwupd_remote_get_security_report_uri(remote));
-               fu_util_print_data(_("Payload"), data);
+               fu_console_print_kv(priv->console,
+                                   _("Target"),
+                                   fwupd_remote_get_security_report_uri(remote));
+               fu_console_print_kv(priv->console, _("Payload"), data);
                if (sig != NULL)
-                       fu_util_print_data(_("Signature"), sig);
-               g_print("%s [Y|n]: ", _("Proceed with upload?"));
-               if (!fu_util_prompt_for_boolean(TRUE)) {
+                       fu_console_print_kv(priv->console, _("Signature"), sig);
+               if (!fu_console_input_bool(priv->console, TRUE, "%s", _("Proceed with upload?"))) {
                        g_set_error_literal(error,
                                            FWUPD_ERROR,
                                            FWUPD_ERROR_PERMISSION_DENIED,
@@ -3333,15 +3381,17 @@ fu_util_upload_security(FuUtilPrivate *priv, GPtrArray *attrs, GError **error)
        if (upload_response == NULL)
                return FALSE;
 
-       /* TRANSLATORS: success, so say thank you to the user */
-       g_print("%s\n", "Host Security ID attributes uploaded successfully, thanks!");
+       fu_console_print_literal(priv->console,
+                                /* TRANSLATORS: success, so say thank you to the user */
+                                _("Host Security ID attributes uploaded successfully, thanks!"));
 
        /* as this worked, ask if the user want to do this every time */
        if (!fwupd_remote_get_automatic_security_reports(remote)) {
-               g_print("%s [y|N]: ",
-                       /* TRANSLATORS: can we JFDI? */
-                       _("Automatically upload every time?"));
-               if (fu_util_prompt_for_boolean(FALSE)) {
+               if (fu_console_input_bool(priv->console,
+                                         FALSE,
+                                         "%s",
+                                         /* TRANSLATORS: can we JFDI? */
+                                         _("Automatically upload every time?"))) {
                        if (!fwupd_client_modify_remote(priv->client,
                                                        fwupd_remote_get_id(remote),
                                                        "AutomaticSecurityReports",
@@ -3413,7 +3463,7 @@ fu_util_security_as_json(FuUtilPrivate *priv,
        }
 
        json_builder_end_object(builder);
-       return fu_util_print_builder(builder, error);
+       return fu_util_print_builder(priv->console, builder, error);
 }
 
 static gboolean
@@ -3488,7 +3538,7 @@ fu_util_sync_bkc(FuUtilPrivate *priv, gchar **values, GError **error)
        }
 
        /* show reboot if needed */
-       return fu_util_prompt_complete(priv->completion_flags, TRUE, error);
+       return fu_util_prompt_complete(priv->console, priv->completion_flags, TRUE, error);
 }
 
 static gboolean
@@ -3522,13 +3572,10 @@ fu_util_security_modify_bios_setting(FuUtilPrivate *priv, FwupdSecurityAttr *att
                        _("You should ensure you are comfortable restoring the setting from "
                          "the system firmware setup, as this change may cause the system "
                          "to not boot into Linux or cause other system instability."));
-       fu_util_warning_box(title->str, body->str, 80);
+       fu_console_box(priv->console, title->str, body->str, 80);
 
-       /* ask for confirmation */
-       g_print("\n%s [y|N]: ",
-               /* TRANSLATORS: prompt to apply the update */
-               _("Perform operation?"));
-       if (!fu_util_prompt_for_boolean(FALSE))
+       /* TRANSLATORS: prompt to apply the update */
+       if (!fu_console_input_bool(priv->console, FALSE, "%s", _("Perform operation?")))
                return TRUE;
        g_hash_table_insert(bios_settings,
                            g_strdup(fwupd_security_attr_get_bios_setting_id(attr)),
@@ -3614,10 +3661,11 @@ fu_util_security(FuUtilPrivate *priv, gchar **values, GError **error)
        if (priv->as_json)
                return fu_util_security_as_json(priv, attrs, events, devices, error);
 
-       g_print("%s \033[1m%s\033[0m\n",
-               /* TRANSLATORS: this is a string like 'HSI:2-U' */
-               _("Host Security ID:"),
-               fwupd_client_get_host_security_id(priv->client));
+       fu_console_print(priv->console,
+                        "%s \033[1m%s\033[0m",
+                        /* TRANSLATORS: this is a string like 'HSI:2-U' */
+                        _("Host Security ID:"),
+                        fwupd_client_get_host_security_id(priv->client));
 
        /* show or hide different elements */
        if (priv->show_all) {
@@ -3625,20 +3673,20 @@ fu_util_security(FuUtilPrivate *priv, gchar **values, GError **error)
                flags |= FU_SECURITY_ATTR_TO_STRING_FLAG_SHOW_URLS;
        }
        str = fu_util_security_attrs_to_string(attrs, flags);
-       g_print("%s\n", str);
+       fu_console_print_literal(priv->console, str);
 
        /* events */
        if (events != NULL && events->len > 0) {
                g_autofree gchar *estr = fu_util_security_events_to_string(events, flags);
                if (estr != NULL)
-                       g_print("%s\n", estr);
+                       fu_console_print_literal(priv->console, estr);
        }
 
        /* known CVEs */
        if (devices != NULL && devices->len > 0) {
                g_autofree gchar *estr = fu_util_security_issues_to_string(devices);
                if (estr != NULL)
-                       g_print("%s", estr);
+                       fu_console_print_literal(priv->console, estr);
        }
 
        /* host emulation */
@@ -3672,7 +3720,7 @@ fu_util_security(FuUtilPrivate *priv, gchar **values, GError **error)
        /* reboot is required? */
        if (!priv->no_reboot_check &&
            (priv->completion_flags & FWUPD_DEVICE_FLAG_NEEDS_REBOOT) > 0) {
-               if (!fu_util_prompt_complete(priv->completion_flags, TRUE, error))
+               if (!fu_util_prompt_complete(priv->console, priv->completion_flags, TRUE, error))
                        return FALSE;
        }
 
@@ -3720,7 +3768,7 @@ fu_util_private_free(FuUtilPrivate *priv)
        g_main_loop_unref(priv->loop);
        g_main_context_unref(priv->main_ctx);
        g_object_unref(priv->cancellable);
-       g_object_unref(priv->progressbar);
+       g_object_unref(priv->console);
        g_option_context_free(priv->context);
        g_free(priv);
 }
@@ -3845,7 +3893,7 @@ fu_util_block_firmware(FuUtilPrivate *priv, gchar **values, GError **error)
        }
 
        /* TRANSLATORS: we will not offer this firmware to the user */
-       g_print("%s %s\n", _("Blocking firmware:"), csum);
+       fu_console_print(priv->console, "%s %s", _("Blocking firmware:"), csum);
 
        /* remove it from the new list */
        csums_new = g_new0(gchar *, g_strv_length(csums) + 2);
@@ -3900,7 +3948,7 @@ fu_util_unblock_firmware(FuUtilPrivate *priv, gchar **values, GError **error)
        }
 
        /* TRANSLATORS: we will now offer this firmware to the user */
-       g_print("%s %s\n", _("Unblocking firmware:"), csum);
+       fu_console_print(priv->console, "%s %s", _("Unblocking firmware:"), csum);
 
        /* remove it from the new list */
        csums_new = g_new0(gchar *, g_strv_length(csums));
@@ -3926,14 +3974,14 @@ fu_util_get_blocked_firmware(FuUtilPrivate *priv, gchar **values, GError **error
        /* empty list */
        if (g_strv_length(csums) == 0) {
                /* TRANSLATORS: nothing to show */
-               g_print("%s\n", _("There are no blocked firmware files"));
+               fu_console_print_literal(priv->console, _("There are no blocked firmware files"));
                return TRUE;
        }
 
        /* TRANSLATORS: there follows a list of hashes */
-       g_print("%s\n", _("Blocked firmware files:"));
+       fu_console_print_literal(priv->console, _("Blocked firmware files:"));
        for (guint i = 0; csums[i] != NULL; i++) {
-               g_print("%u.\t%s\n", i + 1, csums[i]);
+               fu_console_print(priv->console, "%u.\t%s", i + 1, csums[i]);
        }
 
        /* success */
@@ -3970,7 +4018,6 @@ fu_util_show_plugin_warnings(FuUtilPrivate *priv)
        for (guint i = 0; i < 64; i++) {
                FwupdPluginFlags flag = (guint64)1 << i;
                const gchar *tmp;
-               g_autofree gchar *fmt = NULL;
                g_autofree gchar *url = NULL;
                g_autoptr(GString) str = g_string_new(NULL);
                if ((flags & flag) == 0)
@@ -3978,17 +4025,15 @@ fu_util_show_plugin_warnings(FuUtilPrivate *priv)
                tmp = fu_util_plugin_flag_to_string(flag);
                if (tmp == NULL)
                        continue;
-               /* TRANSLATORS: this is a prefix on the console */
-               fmt = fu_util_term_format(_("WARNING:"), FU_UTIL_TERM_COLOR_RED);
-               g_string_append_printf(str, "%s %s\n", fmt, tmp);
-
+               g_string_append_printf(str, "%s\n", tmp);
                url = g_strdup_printf("https://github.com/fwupd/fwupd/wiki/PluginFlag:%s",
                                      fwupd_plugin_flag_to_string(flag));
-               g_string_append(str, "  ");
                /* TRANSLATORS: %s is a link to a website */
                g_string_append_printf(str, _("See %s for more information."), url);
-               g_string_append(str, "\n");
-               g_printerr("%s", str->str);
+               fu_console_print_full(priv->console,
+                                     FU_CONSOLE_PRINT_FLAG_WARNING,
+                                     "%s\n",
+                                     str->str);
        }
 }
 
@@ -4017,7 +4062,7 @@ fu_util_set_bios_setting(FuUtilPrivate *priv, gchar **input, GError **error)
                            g_strdup_printf(_("Set BIOS setting '%s' using '%s'."),
                                            (const gchar *)key,
                                            (const gchar *)value);
-                       g_print("\n%s\n", msg);
+                       fu_console_print_literal(priv->console, msg);
                }
        }
        priv->completion_flags |= FWUPD_DEVICE_FLAG_NEEDS_REBOOT;
@@ -4027,7 +4072,7 @@ fu_util_set_bios_setting(FuUtilPrivate *priv, gchar **input, GError **error)
                return TRUE;
        }
 
-       return fu_util_prompt_complete(priv->completion_flags, TRUE, error);
+       return fu_util_prompt_complete(priv->console, priv->completion_flags, TRUE, error);
 }
 
 static gboolean
@@ -4040,13 +4085,13 @@ fu_util_get_bios_setting(FuUtilPrivate *priv, gchar **values, GError **error)
        if (attrs == NULL)
                return FALSE;
        if (priv->as_json)
-               return fu_util_get_bios_setting_as_json(values, attrs, error);
+               return fu_util_get_bios_setting_as_json(priv->console, values, attrs, error);
 
        for (guint i = 0; i < attrs->len; i++) {
                FwupdBiosSetting *attr = g_ptr_array_index(attrs, i);
                if (fu_util_bios_setting_matches_args(attr, values)) {
                        g_autofree gchar *tmp = fu_util_bios_setting_to_string(attr, 0);
-                       g_print("\n%s\n", tmp);
+                       fu_console_print_literal(priv->console, tmp);
                        found = TRUE;
                }
        }
@@ -4157,9 +4202,9 @@ fu_util_version(FuUtilPrivate *priv, GError **error)
 
        /* dump to the screen in the most appropriate format */
        if (priv->as_json)
-               return fu_util_project_versions_as_json(metadata, error);
+               return fu_util_project_versions_as_json(priv->console, metadata, error);
        str = fu_util_project_versions_to_string(metadata);
-       g_print("%s", str);
+       fu_console_print_literal(priv->console, str);
        return TRUE;
 }
 
@@ -4170,7 +4215,7 @@ fu_util_setup_interactive(FuUtilPrivate *priv, GError **error)
                g_set_error_literal(error, G_IO_ERROR, G_IO_ERROR_NOT_SUPPORTED, "using --json");
                return FALSE;
        }
-       return fu_util_setup_interactive_console(error);
+       return fu_console_setup(priv->console, error);
 }
 
 static void
@@ -4180,7 +4225,7 @@ fu_util_cancelled_cb(GCancellable *cancellable, gpointer user_data)
        if (!g_main_loop_is_running(priv->loop))
                return;
        /* TRANSLATORS: this is from ctrl+c */
-       g_print("%s\n", _("Cancelled"));
+       fu_console_print_literal(priv->console, _("Cancelled"));
        g_main_loop_quit(priv->loop);
 }
 
@@ -4197,10 +4242,10 @@ static void
 fu_util_print_error(FuUtilPrivate *priv, const GError *error)
 {
        if (priv->as_json) {
-               fu_util_print_error_as_json(error);
+               fu_util_print_error_as_json(priv->console, error);
                return;
        }
-       g_printerr("%s\n", error->message);
+       fu_console_print_full(priv->console, FU_CONSOLE_PRINT_FLAG_STDERR, "%s\n", error->message);
 }
 
 int
@@ -4438,9 +4483,9 @@ main(int argc, char *argv[])
        /* create helper object */
        priv->main_ctx = g_main_context_new();
        priv->loop = g_main_loop_new(priv->main_ctx, FALSE);
-       priv->progressbar = fu_progressbar_new();
+       priv->console = fu_console_new();
        priv->post_requests = g_ptr_array_new_with_free_func((GDestroyNotify)g_object_unref);
-       fu_progressbar_set_main_context(priv->progressbar, priv->main_ctx);
+       fu_console_set_main_context(priv->console, priv->main_ctx);
 
        /* add commands */
        fu_util_cmd_array_add(cmd_array,
@@ -4768,7 +4813,7 @@ main(int argc, char *argv[])
        } else {
                is_interactive = TRUE;
        }
-       fu_progressbar_set_interactive(priv->progressbar, is_interactive);
+       fu_console_set_interactive(priv->console, is_interactive);
 
        /* get a list of the commands */
        priv->context = g_option_context_new(NULL);
@@ -4786,36 +4831,36 @@ main(int argc, char *argv[])
        g_option_context_add_main_entries(priv->context, options, NULL);
        ret = g_option_context_parse(priv->context, &argc, &argv, &error);
        if (!ret) {
-               /* TRANSLATORS: the user didn't read the man page */
-               g_print("%s: %s\n", _("Failed to parse arguments"), error->message);
+               fu_console_print(priv->console,
+                                "%s: %s",
+                                /* TRANSLATORS: the user didn't read the man page */
+                                _("Failed to parse arguments"),
+                                error->message);
                return EXIT_FAILURE;
        }
 
        /* allow disabling SSL strict mode for broken corporate proxies */
        if (priv->disable_ssl_strict) {
-               g_autofree gchar *fmt = NULL;
-               /* TRANSLATORS: this is a prefix on the console */
-               fmt = fu_util_term_format(_("WARNING:"), FU_UTIL_TERM_COLOR_RED);
-               g_printerr("%s %s\n",
-                          fmt,
-                          /* TRANSLATORS: try to help */
-                          _("Ignoring SSL strict checks, "
-                            "to do this automatically in the future "
-                            "export DISABLE_SSL_STRICT in your environment"));
+               fu_console_print_full(priv->console,
+                                     FU_CONSOLE_PRINT_FLAG_WARNING,
+                                     "%s\n",
+                                     /* TRANSLATORS: try to help */
+                                     _("Ignoring SSL strict checks, "
+                                       "to do this automatically in the future "
+                                       "export DISABLE_SSL_STRICT in your environment"));
                (void)g_setenv("DISABLE_SSL_STRICT", "1", TRUE);
        }
 
        /* this doesn't have to be precise (e.g. using the build-year) as we just
         * want to check the clock is not set to the default of 1970-01-01... */
        if (g_date_time_get_year(dt_now) < 2021) {
-               g_autofree gchar *fmt = NULL;
-               /* TRANSLATORS: this is a prefix on the console */
-               fmt = fu_util_term_format(_("WARNING:"), FU_UTIL_TERM_COLOR_RED);
-               g_printerr("%s %s\n",
-                          fmt,
-                          /* TRANSLATORS: try to help */
-                          _("The system clock has not been set "
-                            "correctly and downloading files may fail."));
+               fu_console_print_full(
+                   priv->console,
+                   FU_CONSOLE_PRINT_FLAG_WARNING,
+                   "%s\n",
+                   /* TRANSLATORS: try to help */
+                   _("The system clock has not been set correctly and downloading "
+                     "files may fail."));
        }
 
        /* parse filter flags */
@@ -4862,7 +4907,9 @@ main(int argc, char *argv[])
        if (is_interactive) {
                g_autoptr(GError) error_polkit = NULL;
                if (!fu_polkit_agent_open(&error_polkit)) {
-                       g_printerr("Failed to open polkit agent: %s\n", error_polkit->message);
+                       fu_console_print(priv->console,
+                                        "Failed to open polkit agent: %s",
+                                        error_polkit->message);
                }
        }
 #endif
@@ -4890,8 +4937,10 @@ main(int argc, char *argv[])
        /* show a warning if the daemon is tainted */
        if (!fwupd_client_connect(priv->client, priv->cancellable, &error)) {
 #ifdef _WIN32
-               /* TRANSLATORS: error message for Windows */
-               g_printerr(_("Failed to connect to Windows service, please ensure it's running."));
+               fu_console_print_literal(
+                   priv->console,
+                   /* TRANSLATORS: error message for Windows */
+                   _("Failed to connect to Windows service, please ensure it's running."));
                g_debug("%s", error->message);
 #else
                /* TRANSLATORS: could not contact the fwupd service over D-Bus */
@@ -4901,14 +4950,12 @@ main(int argc, char *argv[])
                return EXIT_FAILURE;
        }
        if (fwupd_client_get_tainted(priv->client)) {
-               g_autofree gchar *fmt = NULL;
-               /* TRANSLATORS: this is a prefix on the console */
-               fmt = fu_util_term_format(_("WARNING:"), FU_UTIL_TERM_COLOR_RED);
-               g_printerr("%s %s\n",
-                          fmt,
-                          /* TRANSLATORS: the user is SOL for support... */
-                          _("The daemon has loaded 3rd party code and "
-                            "is no longer supported by the upstream developers!"));
+               fu_console_print_full(priv->console,
+                                     FU_CONSOLE_PRINT_FLAG_WARNING,
+                                     "%s\n",
+                                     /* TRANSLATORS: the user is SOL for support... */
+                                     _("The daemon has loaded 3rd party code and "
+                                       "is no longer supported by the upstream developers!"));
        }
 
        /* just show versions and exit */
@@ -4924,7 +4971,7 @@ main(int argc, char *argv[])
        fu_util_show_plugin_warnings(priv);
 
        /* show any unsupported warnings */
-       fu_util_show_unsupported_warn();
+       fu_util_show_unsupported_warning(priv->console);
 
        /* we know the runtime daemon version now */
        fwupd_client_set_user_agent_for_package(priv->client, g_get_prgname(), PACKAGE_VERSION);
@@ -4986,10 +5033,10 @@ main(int argc, char *argv[])
                if (g_error_matches(error, FWUPD_ERROR, FWUPD_ERROR_INVALID_ARGS)) {
                        g_autofree gchar *cmd = g_strdup_printf("%s --help", g_get_prgname());
                        g_autoptr(GString) str = g_string_new("\n");
-                       /* TRANSLATORS: error message explaining command on how to get help,
+                       /* TRANSLATORS: explain how to get help,
                         * where $1 is something like 'fwupdmgr --help' */
                        g_string_append_printf(str, _("Use %s for help"), cmd);
-                       g_printerr("%s\n", str->str);
+                       fu_console_print_literal(priv->console, str->str);
                } else if (g_error_matches(error, FWUPD_ERROR, FWUPD_ERROR_NOTHING_TO_DO))
                        return EXIT_NOTHING_TO_DO;
                return EXIT_FAILURE;
index f3ca734eed26fad863dfd6cae918b34193e72ed7..e97ee25d3b8ad108dfc810f7f57d913ce45949bf 100644 (file)
@@ -80,7 +80,7 @@ endif
 fwupdutil = library(
   'fwupdutil',
   sources: [
-    'fu-progressbar.c',
+    'fu-console.c',
     'fu-security-attr-common.c',
     'fu-util-bios-setting.c',
     'fu-util-common.c',