petitboot: Update to current repository revision
authorGeoff Levand <geoffrey.levand@am.sony.com>
Mon, 24 Aug 2009 20:24:28 +0000 (20:24 +0000)
committerGeoff Levand <geoffrey.levand@am.sony.com>
Mon, 24 Aug 2009 20:24:28 +0000 (20:24 +0000)
Signed-off-by: Geoff Levand <geoffrey.levand@am.sony.com>
SVN-Revision: 17380

utils/petitboot/Makefile
utils/petitboot/patches/010-petitboot-fixups.diff
utils/petitboot/patches/020-petitboot-fix-pb-twin.diff

index 6f544dbc1a375f29168cf361b5eda4d0af6cf4a1..d4cdeae44e0723fce7890283c9c1100b69641dcf 100644 (file)
@@ -8,7 +8,7 @@
 include $(TOPDIR)/rules.mk
 
 PKG_NAME:=petitboot
-PKG_VERSION:=53aa807ae41e48fd71653c2d00083a44a8bca14c
+PKG_VERSION:=93b2c2e0f0ca46d2a823b33cdfa44d082e9e8d10
 PKG_RELEASE:=1
 
 PKG_SOURCE:=$(PKG_NAME)-$(PKG_VERSION).tar.gz
index f82dd29ef50fac53684bedbfd0a77aeeceb026e0..5cddeb6b032b11a8eb6d1ff5e6fbe2689e577b06 100644 (file)
@@ -1,49 +1,10 @@
34 files changed, 1687 insertions(+), 235 deletions(-)
9 files changed, 90 insertions(+), 33 deletions(-)
 
-diff --git a/Makefile.in b/Makefile.in
-index 01771db..23135aa 100644
---- a/Makefile.in
-+++ b/Makefile.in
-@@ -21,7 +21,12 @@ ENABLE_PS3 = @ENABLE_PS3@
- # other programs
- INSTALL = @INSTALL@
-+INSTALL_DATA = @INSTALL_DATA@
-+INSTALL_PROGRAM = @INSTALL_PROGRAM@
-+INSTALL_SCRIPT = @INSTALL_SCRIPT@
-+INSTALL_STRIP_PROGRAM = @INSTALL_STRIP_PROGRAM@
- SHELL = @SHELL@
-+STRIP = @STRIP@
- # paths
- prefix = @prefix@
-@@ -34,5 +39,6 @@ localstatedir = @localstatedir@
- builddir = @builddir@
- srcdir = @srcdir@
- top_srcdir = @top_srcdir@
-+mandir = @mandir@
- include $(top_srcdir)/rules.mk
-diff --git a/configure.ac b/configure.ac
-index cb43f8c..fa40f34 100644
---- a/configure.ac
-+++ b/configure.ac
-@@ -35,9 +35,7 @@ AC_ARG_ENABLE([ps3],
-       [],
-       [enable_ps3=check])
--AS_IF([test "x$enable_ps3" != xno],
--      [AC_SUBST([ENABLE_PS3], ["y"])],
--      [AC_SUBST([ENABLE_PS3], ["n"])])
-+AS_IF([test "x$enable_ps3" != xno], [AC_SUBST([ENABLE_PS3], ["y"])], [])
- AC_ARG_WITH([twin],
-       [AS_HELP_STRING([--with-twin],
 diff --git a/discover/kboot-parser.c b/discover/kboot-parser.c
-index e688c22..7c7cb5d 100644
+index 23d48a4..7c7cb5d 100644
 --- a/discover/kboot-parser.c
 +++ b/discover/kboot-parser.c
-@@ -133,10 +133,11 @@ static int kboot_parse(struct discover_context *dc)
+@@ -133,7 +133,7 @@ static int kboot_parse(struct discover_context *dc)
        conf = talloc_zero(dc, struct conf_context);
  
        if (!conf)
@@ -52,67 +13,6 @@ index e688c22..7c7cb5d 100644
  
        conf->dc = dc;
        conf->global_options = kboot_global_options,
-+      conf_init_global_options(conf);
-       conf->conf_files = kboot_conf_files,
-       conf->process_pair = kboot_process_pair;
-       conf->parser_info = (void *)kboot_ignored_names,
-diff --git a/discover/parser-conf.c b/discover/parser-conf.c
-index 14f847d..88e96b7 100644
---- a/discover/parser-conf.c
-+++ b/discover/parser-conf.c
-@@ -121,6 +121,18 @@ int conf_param_in_list(const char *const *list, const char *param)
- }
- /**
-+ * conf_init_global_options - Zero the global option table.
-+ */
-+
-+void conf_init_global_options(struct conf_context *conf)
-+{
-+      int i;
-+
-+      for (i = 0; conf->global_options[i].name; i++)
-+              conf->global_options[i].value = NULL;
-+}
-+
-+/**
-  * conf_set_global_option - Set a value in the global option table.
-  *
-  * Check if an option (name=value) is a global option. If so, store it in
-@@ -136,7 +148,7 @@ int conf_set_global_option(struct conf_context *conf, const char *name,
-               if (streq(name, conf->global_options[i].name)) {
-                       conf->global_options[i].value
-                               = talloc_strdup(conf, value);
--                      pb_log("%s: %s:%s\n", __func__, name, value);
-+                      pb_log("%s: @%s@%s@\n", __func__, name, value);
-                       return 1;
-               }
-       }
-@@ -158,8 +170,11 @@ const char *conf_get_global_option(struct conf_context *conf,
-       int i;
-       for (i = 0; conf->global_options[i].name ;i++)
--              if (streq(name, conf->global_options[i].name))
-+              if (streq(name, conf->global_options[i].name)) {
-+                      pb_log("%s: @%s@%s@\n", __func__, name,
-+                              conf->global_options[i].value);
-                       return conf->global_options[i].value;
-+              }
-       assert(0 && "unknown global name");
-       return NULL;
-diff --git a/discover/parser-conf.h b/discover/parser-conf.h
-index 09015d1..3325faf 100644
---- a/discover/parser-conf.h
-+++ b/discover/parser-conf.h
-@@ -41,6 +41,7 @@ struct conf_context {
- int conf_parse(struct conf_context *conf);
- char *conf_get_param_pair(char *str, char **name_out, char **value_out,
-               char terminator);
-+void conf_init_global_options(struct conf_context *conf);
- const char *conf_get_global_option(struct conf_context *conf,
-       const char *name);
- int conf_set_global_option(struct conf_context *conf, const char *name,
 diff --git a/discover/parser.c b/discover/parser.c
 index 2b4ddd2..8f2735c 100644
 --- a/discover/parser.c
@@ -138,306 +38,11 @@ index 2b4ddd2..8f2735c 100644
  }
  
  static int compare_parsers(const void *a, const void *b)
-diff --git a/discover/paths.c b/discover/paths.c
-index 8e2a361..fe7a876 100644
---- a/discover/paths.c
-+++ b/discover/paths.c
-@@ -81,14 +81,6 @@ char *parse_device_path(void *alloc_ctx,
-       if (is_prefix(dev_str, "/dev/"))
-               dev_str += strlen("/dev/");
--      /* PS3 hack: if we're reading from a ps3dx device, and we refer to
--       * a sdx device, remap to ps3dx */
--      if (cur_dev && is_prefix(cur_dev, "/dev/ps3d")
--                      && is_prefix(dev_str, "sd")) {
--              snprintf(tmp, 255, "ps3d%s", dev_str + 2);
--              dev_str = tmp;
--      }
--
-       return join_paths(alloc_ctx, "/dev", dev_str);
- }
-diff --git a/discover/pb-discover.c b/discover/pb-discover.c
-index d7ea0ca..bd515e3 100644
---- a/discover/pb-discover.c
-+++ b/discover/pb-discover.c
-@@ -1,5 +1,10 @@
-+#if defined(HAVE_CONFIG_H)
-+#include "config.h"
-+#endif
-+
- #include <assert.h>
-+#include <getopt.h>
- #include <stdlib.h>
- #include <signal.h>
-@@ -11,6 +16,79 @@
- #include "discover-server.h"
- #include "device-handler.h"
-+static void print_version(void)
-+{
-+      printf("pb-discover (" PACKAGE_NAME ") " PACKAGE_VERSION "\n");
-+}
-+
-+static void print_usage(void)
-+{
-+      print_version();
-+      printf(
-+"Usage: pb-discover [-h, --help] [-l, --log log-file] [-V, --version]\n");
-+}
-+
-+/**
-+ * enum opt_value - Tri-state options variables.
-+ */
-+
-+enum opt_value {opt_undef = 0, opt_yes, opt_no};
-+
-+/**
-+ * struct opts - Values from command line options.
-+ */
-+
-+struct opts {
-+      enum opt_value show_help;
-+      const char *log_file;
-+      enum opt_value show_version;
-+};
-+
-+/**
-+ * opts_parse - Parse the command line options.
-+ */
-+
-+static int opts_parse(struct opts *opts, int argc, char *argv[])
-+{
-+      static const struct option long_options[] = {
-+              {"help",           no_argument,       NULL, 'h'},
-+              {"log",            required_argument, NULL, 'l'},
-+              {"version",        no_argument,       NULL, 'V'},
-+              { NULL, 0, NULL, 0},
-+      };
-+      static const char short_options[] = "hl:V";
-+      static const struct opts default_values = {
-+              .log_file = "pb-discover.log",
-+      };
-+
-+      *opts = default_values;
-+
-+      while (1) {
-+              int c = getopt_long(argc, argv, short_options, long_options,
-+                      NULL);
-+
-+              if (c == EOF)
-+                      break;
-+
-+              switch (c) {
-+              case 'h':
-+                      opts->show_help = opt_yes;
-+                      break;
-+              case 'l':
-+                      opts->log_file = optarg;
-+                      break;
-+              case 'V':
-+                      opts->show_version = opt_yes;
-+                      break;
-+              default:
-+                      opts->show_help = opt_yes;
-+                      return -1;
-+              }
-+      }
-+
-+      return optind != argc;
-+}
-+
- static int running;
- static void sigint_handler(int __attribute__((unused)) signum)
-@@ -18,15 +96,31 @@ static void sigint_handler(int __attribute__((unused)) signum)
-       running = 0;
- }
--int main(void)
-+int main(int argc, char *argv[])
- {
-       struct device_handler *handler;
-       struct discover_server *server;
-+      struct opts opts;
-       struct udev *udev;
-       struct user_event *uev;
-       FILE *log;
--      log = fopen("pb-discover.log", "a");
-+      if (opts_parse(&opts, argc, argv)) {
-+              print_usage();
-+              return EXIT_FAILURE;
-+      }
-+
-+      if (opts.show_help == opt_yes) {
-+              print_usage();
-+              return EXIT_SUCCESS;
-+      }
-+
-+      if (opts.show_version == opt_yes) {
-+              print_version();
-+              return EXIT_SUCCESS;
-+      }
-+
-+      log = fopen(opts.log_file, "a");
-       assert(log);
-       pb_log_set_stream(log);
 diff --git a/discover/yaboot-parser.c b/discover/yaboot-parser.c
-index d9f2aff..6101cd8 100644
+index 1000505..6101cd8 100644
 --- a/discover/yaboot-parser.c
 +++ b/discover/yaboot-parser.c
-@@ -15,7 +15,6 @@ struct yaboot_state {
-       struct boot_option *opt;
-       const char *desc_image;
-       char *desc_initrd;
--      int found_suse;
-       int globals_done;
-       const char *const *known_names;
- };
-@@ -56,6 +55,19 @@ static void yaboot_process_pair(struct conf_context *conf, const char *name,
-               char *value)
- {
-       struct yaboot_state *state = conf->parser_info;
-+      struct fixed_pair {
-+              const char *image;
-+              const char *initrd;
-+      };
-+      static const struct fixed_pair suse_fp32 = {
-+              .image = "/suseboot/vmlinux32",
-+              .initrd = "/suseboot/initrd32",
-+      };
-+      static const struct fixed_pair suse_fp64 = {
-+              .image = "/suseboot/vmlinux64",
-+              .initrd = "/suseboot/initrd64",
-+      };
-+      const struct fixed_pair *suse_fp;
-       /* fixup for bare values */
-@@ -73,32 +85,70 @@ static void yaboot_process_pair(struct conf_context *conf, const char *name,
-       /* image */
-       if (streq(name, "image")) {
-+              const char *g_boot = conf_get_global_option(conf, "boot");
-+              const char *g_part = conf_get_global_option(conf, "partition");
-+
-+              /* First finish any previous image. */
-+
-               if (state->opt->boot_image_file)
-                       yaboot_finish(conf);
--              state->opt->boot_image_file = resolve_path(state->opt, value,
--                      conf->dc->device_path);
--              state->desc_image = talloc_strdup(state->opt, value);
-+              /* Then start the new image. */
-+
-+              if (g_boot && g_part) {
-+                      char* dev = talloc_asprintf(NULL, "%s%s", g_boot,
-+                              g_part);
-+
-+                      state->opt->boot_image_file = resolve_path(state->opt,
-+                              value, dev);
-+                      state->desc_image = talloc_asprintf(state->opt,
-+                              "%s%s", dev, value);
-+                      talloc_free(dev);
-+              } else if (g_boot) {
-+                      state->opt->boot_image_file = resolve_path(state->opt,
-+                              value, g_boot);
-+                      state->desc_image = talloc_asprintf(state->opt,
-+                              "%s%s", g_boot, value);
-+              } else {
-+                      state->opt->boot_image_file = resolve_path(state->opt,
-+                              value, conf->dc->device_path);
-+                      state->desc_image = talloc_strdup(state->opt, value);
-+              }
-+
-               return;
-       }
--      if (streq(name, "image[32bit]") || streq(name, "image[64bit]")) {
--              state->found_suse = 1;
-+      /* Special processing for SUSE install CD. */
-+
-+      if (streq(name, "image[32bit]"))
-+              suse_fp = &suse_fp32;
-+      else if (streq(name, "image[64bit]"))
-+              suse_fp = &suse_fp64;
-+      else
-+              suse_fp = NULL;
-+
-+      if (suse_fp) {
-+              /* First finish any previous image. */
-               if (state->opt->boot_image_file)
-                       yaboot_finish(conf);
-+              /* Then start the new image. */
-+
-               if (*value == '/') {
-                       state->opt->boot_image_file = resolve_path(state->opt,
-                               value, conf->dc->device_path);
-                       state->desc_image = talloc_strdup(state->opt, value);
-               } else {
--                      char *s;
--                      asprintf(&s, "/suseboot/%s", value);
-                       state->opt->boot_image_file = resolve_path(state->opt,
--                              s, conf->dc->device_path);
--                      state->desc_image = talloc_strdup(state->opt, s);
--                      free(s);
-+                              suse_fp->image, conf->dc->device_path);
-+                      state->desc_image = talloc_strdup(state->opt,
-+                              suse_fp->image);
-+
-+                      state->opt->initrd_file = resolve_path(state->opt,
-+                              suse_fp->initrd, conf->dc->device_path);
-+                      state->desc_initrd = talloc_asprintf(state, "initrd=%s",
-+                              suse_fp->initrd);
-               }
-               return;
-@@ -112,19 +162,28 @@ static void yaboot_process_pair(struct conf_context *conf, const char *name,
-       /* initrd */
-       if (streq(name, "initrd")) {
--              if (!state->found_suse || (*value == '/')) {
-+              const char *g_boot = conf_get_global_option(conf, "boot");
-+              const char *g_part = conf_get_global_option(conf, "partition");
-+
-+              if (g_boot && g_part) {
-+                      char* dev = talloc_asprintf(NULL, "%s%s", g_boot,
-+                              g_part);
-+
-                       state->opt->initrd_file = resolve_path(state->opt,
--                              value, conf->dc->device_path);
--                      state->desc_initrd = talloc_asprintf(state, "initrd=%s",
--                              value);
-+                              value, dev);
-+                      state->desc_initrd = talloc_asprintf(state,
-+                              "initrd=%s%s", dev, value);
-+                      talloc_free(dev);
-+              } else if (g_boot) {
-+                      state->opt->initrd_file = resolve_path(state->opt,
-+                              value, g_boot);
-+                      state->desc_initrd = talloc_asprintf(state,
-+                              "initrd=%s%s", g_boot, value);
-               } else {
--                      char *s;
--                      asprintf(&s, "/suseboot/%s", value);
-                       state->opt->initrd_file = resolve_path(state->opt,
--                              s, conf->dc->device_path);
-+                              value, conf->dc->device_path);
-                       state->desc_initrd = talloc_asprintf(state, "initrd=%s",
--                              s);
--                      free(s);
-+                              value);
-               }
-               return;
-       }
-@@ -236,10 +295,11 @@ static int yaboot_parse(struct discover_context *dc)
+@@ -295,7 +295,7 @@ static int yaboot_parse(struct discover_context *dc)
        conf = talloc_zero(dc, struct conf_context);
  
        if (!conf)
@@ -446,23 +51,11 @@ index d9f2aff..6101cd8 100644
  
        conf->dc = dc;
        conf->global_options = yaboot_global_options,
-+      conf_init_global_options(conf);
-       conf->conf_files = yaboot_conf_files,
-       conf->process_pair = yaboot_process_pair;
-       conf->finish = yaboot_finish;
 diff --git a/lib/system/system.c b/lib/system/system.c
-index 380dded..7371445 100644
+index 65bd6bf..7371445 100644
 --- a/lib/system/system.c
 +++ b/lib/system/system.c
-@@ -3,6 +3,7 @@
- #include "config.h"
- #endif
-+#include <assert.h>
- #include <errno.h>
- #include <stdlib.h>
- #include <string.h>
-@@ -19,6 +20,7 @@ const struct pb_system_apps pb_system_apps = {
+@@ -20,6 +20,7 @@ const struct pb_system_apps pb_system_apps = {
        .cp = "/bin/cp",
        .kexec = "/sbin/kexec",
        .mount = "/bin/mount",
@@ -470,46 +63,6 @@ index 380dded..7371445 100644
        .sftp = "/usr/bin/sftp",
        .tftp = "/usr/bin/tftp",
        .umount = "/bin/umount",
-@@ -104,13 +106,13 @@ int pb_rmdir_recursive(const char *base, const char *dir)
- int pb_run_cmd(const char *const *cmd_argv)
- {
--      int status;
--      pid_t pid;
- #if defined(DEBUG)
-       enum {do_debug = 1};
- #else
-       enum {do_debug = 0};
- #endif
-+      int status;
-+      pid_t pid;
-       if (do_debug) {
-               const char *const *p = cmd_argv;
-@@ -125,12 +127,23 @@ int pb_run_cmd(const char *const *cmd_argv)
-               pb_log("%s: %s\n", __func__, cmd_argv[0]);
-       pid = fork();
-+
-       if (pid == -1) {
-               pb_log("%s: fork failed: %s\n", __func__, strerror(errno));
-               return -1;
-       }
-       if (pid == 0) {
-+              int log = fileno(pb_log_get_stream());
-+
-+              /* Redirect child output to log. */
-+
-+              status = dup2(log, STDOUT_FILENO);
-+              assert(status != -1);
-+
-+              status = dup2(log, STDERR_FILENO);
-+              assert(status != -1);
-+
-               execvp(cmd_argv[0], (char *const *)cmd_argv);
-               pb_log("%s: exec failed: %s\n", __func__, strerror(errno));
-               exit(EXIT_FAILURE);
 diff --git a/lib/system/system.h b/lib/system/system.h
 index 47c7c02..1918309 100644
 --- a/lib/system/system.h
@@ -522,499 +75,24 @@ index 47c7c02..1918309 100644
        const char *sftp;
        const char *tftp;
        const char *umount;
-diff --git a/man/pb-cui.8 b/man/pb-cui.8
-new file mode 100644
-index 0000000..e671c20
---- /dev/null
-+++ b/man/pb-cui.8
-@@ -0,0 +1,67 @@
-+.\" Copyright (C) 2009 Sony Computer Entertainment Inc.
-+.\" Copyright 2009 Sony Corp.
-+.\"
-+.\" This program is free software; you can redistribute it and/or modify
-+.\" it under the terms of the GNU General Public License as published by
-+.\" the Free Software Foundation; version 2 of the License.
-+.\"
-+.\" This program is distributed in the hope that it will be useful,
-+.\" but WITHOUT ANY WARRANTY; without even the implied warranty of
-+.\" MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-+.\" GNU General Public License for more details.
-+.\"
-+.\" You should have received a copy of the GNU General Public License
-+.\" along with this program; if not, write to the Free Software
-+.\" Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
-+.\"
-+.\" Maintainer's Notes:
-+.\"  * For syntax help see the man pages for 'mdoc' and 'mdoc.samples'.
-+.\"  * To check syntax use this:
-+.\"    'groff -C -mtty-char -Tutf8 -man pb-cui.8'.
-+.\"  * To check format use this: 'less pb-cui.8'.
-+.\"
-+.Dd ""
-+.Dt pb-cui 8
-+.Os
-+.\"
-+.Sh NAME
-+.\" ====
-+.Nm pb-cui
-+.Nd Petitboot ncurses bootloader UI
-+.\"
-+.Sh SYNOPSIS
-+.\" ========
-+.Nm
-+.Op Fl h, -help
-+.Op Fl l, -log Ar log-file
-+.Op Fl V, -version
-+.\"
-+.Sh DESCRIPTION
-+.\" ===========
-+pb-cui is an ncurses based interface to the Petitboot bootloader.
-+.Pp
-+Petitboot is a Linux kexec based bootloader that supports loading Linux
-+kernel and initrd images from any device that can be mounted by Linux.
-+It can also load images from the network using the
-+HTTP, HTTPS, NFS, SFTP, and TFTP
-+protocols.
-+.\"
-+.Sh OPTIONS
-+.\" =======
-+.Bl -tag -width indent
-+.\"
-+.It Fl h, -help
-+Print a help message.
-+.\"
-+.It Fl l, -log Ar log-file
-+Log messages to the file
-+.Ar log-file .
-+The default log is a file pb-cui.log in the directory where pb-cui
-+is started.  New messages are appended to an existing log file.
-+.\"
-+.It Fl V, -version
-+Display the program version number.
-+.El
-+.Sh SEE ALSO
-+.\" ========
-+.Xr petitboot 8 , Xr pb-discover 8 , Xr pb-event 8
-diff --git a/man/pb-discover.8 b/man/pb-discover.8
-new file mode 100644
-index 0000000..9123e0f
---- /dev/null
-+++ b/man/pb-discover.8
-@@ -0,0 +1,44 @@
-+.\" Copyright (C) 2009 Sony Computer Entertainment Inc.
-+.\" Copyright 2009 Sony Corp.
-+.\"
-+.\" This program is free software; you can redistribute it and/or modify
-+.\" it under the terms of the GNU General Public License as published by
-+.\" the Free Software Foundation; version 2 of the License.
-+.\"
-+.\" This program is distributed in the hope that it will be useful,
-+.\" but WITHOUT ANY WARRANTY; without even the implied warranty of
-+.\" MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-+.\" GNU General Public License for more details.
-+.\"
-+.\" You should have received a copy of the GNU General Public License
-+.\" along with this program; if not, write to the Free Software
-+.\" Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
-+.\"
-+.\" Maintainer's Notes:
-+.\"  * For syntax help see the man pages for 'mdoc' and 'mdoc.samples'.
-+.\"  * To check syntax use this:
-+.\"    'groff -C -mtty-char -Tutf8 -man pb-discover.8'.
-+.\"  * To check format use this: 'less pb-discover.8'.
-+.\"
-+.Dd ""
-+.Dt pb-discover 8
-+.Os
-+.\"
-+.Sh NAME
-+.\" ====
-+.Nm pb-discover
-+.Nd The dynamic device discovery daemon of the Petitboot bootloader
-+.\"
-+.Sh SYNOPSIS
-+.\" ========
-+.Nm
-+.\"
-+.Sh DESCRIPTION
-+.\" ===========
-+pb-discover maintains a dynamic list of boot options available to
-+the system.  On startup, the petitboot user interface clients connect to
-+pb-discover daemon and receive boot option information.
-+.\"
-+.Sh SEE ALSO
-+.\" ========
-+.Xr petitboot 8 , Xr pb-cui 8 , Xr pb-event 8
-diff --git a/man/pb-event.8 b/man/pb-event.8
-new file mode 100644
-index 0000000..dc123e1
---- /dev/null
-+++ b/man/pb-event.8
-@@ -0,0 +1,43 @@
-+.\" Copyright (C) 2009 Sony Computer Entertainment Inc.
-+.\" Copyright 2009 Sony Corp.
-+.\"
-+.\" This program is free software; you can redistribute it and/or modify
-+.\" it under the terms of the GNU General Public License as published by
-+.\" the Free Software Foundation; version 2 of the License.
-+.\"
-+.\" This program is distributed in the hope that it will be useful,
-+.\" but WITHOUT ANY WARRANTY; without even the implied warranty of
-+.\" MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-+.\" GNU General Public License for more details.
-+.\"
-+.\" You should have received a copy of the GNU General Public License
-+.\" along with this program; if not, write to the Free Software
-+.\" Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
-+.\"
-+.\" Maintainer's Notes:
-+.\"  * For syntax help see the man pages for 'mdoc' and 'mdoc.samples'.
-+.\"  * To check syntax use this:
-+.\"    'groff -C -mtty-char -Tutf8 -man pb-event.8'.
-+.\"  * To check format use this: 'less pb-event.8'.
-+.\"
-+.Dd ""
-+.Dt pb-event 8
-+.Os
-+.\"
-+.Sh NAME
-+.\" ====
-+.Nm pb-event
-+.Nd Event helper for the Petitboot bootloader
-+.\"
-+.Sh SYNOPSIS
-+.\" ========
-+.Nm
-+.\"
-+.Sh DESCRIPTION
-+.\" ===========
-+The pb-event utility is used to send user mode events to pb-discover, the
-+petitboot device discovery daemon.
-+.\"
-+.Sh SEE ALSO
-+.\" ========
-+.Xr petitboot 8 , Xr pb-cui 8 , Xr pb-discover 8
-diff --git a/man/petitboot.8 b/man/petitboot.8
-new file mode 100644
-index 0000000..9dc2222
---- /dev/null
-+++ b/man/petitboot.8
-@@ -0,0 +1,47 @@
-+.\" Copyright (C) 2009 Sony Computer Entertainment Inc.
-+.\" Copyright 2009 Sony Corp.
-+.\"
-+.\" This program is free software; you can redistribute it and/or modify
-+.\" it under the terms of the GNU General Public License as published by
-+.\" the Free Software Foundation; version 2 of the License.
-+.\"
-+.\" This program is distributed in the hope that it will be useful,
-+.\" but WITHOUT ANY WARRANTY; without even the implied warranty of
-+.\" MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-+.\" GNU General Public License for more details.
-+.\"
-+.\" You should have received a copy of the GNU General Public License
-+.\" along with this program; if not, write to the Free Software
-+.\" Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
-+.\"
-+.\" Maintainer's Notes:
-+.\"  * For syntax help see the man pages for 'mdoc' and 'mdoc.samples'.
-+.\"  * To check syntax use this:
-+.\"    'groff -C -mtty-char -Tutf8 -man petitboot.8'.
-+.\"  * To check format use this: 'less petitboot.8'.
-+.\"
-+.Dd ""
-+.Dt petitboot 8
-+.Os
-+.\"
-+.Sh NAME
-+.\" ====
-+.Nm petitboot
-+.Nd The Petitboot bootloader
-+.\"
-+.Sh DESCRIPTION
-+.\" ===========
-+Petitboot is a platform independent bootloader based on Linux kexec.
-+Petitboot can load Linux kernel and initrd images from any device that
-+can be mounted by Linux, and can also load images from the network
-+using the
-+HTTP, HTTPS, NFS, SFTP, and TFTP
-+protocols.
-+.Pp
-+Petitboot looks for bootloader configuration files on mountable devices
-+in the system, and can also be configured to use boot information
-+from a DHCP server.
-+.\"
-+.Sh SEE ALSO
-+.\" ========
-+.Xr pb-cui 8 , Xr pb-discover 8 , Xr pb-event 8
-diff --git a/rules.mk b/rules.mk
-index e743338..3b1dcfe 100644
---- a/rules.mk
-+++ b/rules.mk
-@@ -19,8 +19,8 @@ parser_test = test/parser-test
- # install targets and components
- daemons = $(pb_discover)
- parsers = event kboot yaboot
--uis = $(pb_cui) $(pb_test)
--tests = $(parser_test)
-+uis = $(pb_cui)
-+tests = $(parser_test) $(pb_test)
- utils = $(pb_event)
- ifeq ($(PBTWIN),y)
-@@ -29,6 +29,7 @@ endif
- # other to install
- artwork = background.jpg cdrom.png hdd.png usbpen.png tux.png cursor.gz
-+man8 = pb-cui.8 pb-discover.8 pb-event.8 petitboot.8
- rules = utils/99-petitboot.rules
- udhcpc = utils/udhcpc
-@@ -48,10 +49,11 @@ discover_objs = discover/event.o discover/user-event.o discover/udev.o \
-       discover/parser-utils.o
- # client objs
--ui_common_objs = ui/common/discover-client.o ui/common/loader.o \
--      ui/common/ui-system.o ui/common/url.o
--ncurses_objs = ui/ncurses/nc-scr.o ui/ncurses/nc-menu.o \
--       ui/ncurses/nc-ked.o ui/ncurses/nc-cui.o
-+ui_common_objs = ui/common/discover-client.o ui/common/joystick.o \
-+      ui/common/loader.o ui/common/ui-system.o ui/common/timer.o \
-+      ui/common/url.o
-+ncurses_objs = ui/ncurses/nc-scr.o ui/ncurses/nc-menu.o ui/ncurses/nc-ked.o \
-+      ui/ncurses/nc-cui.o
- twin_objs = ui/twin/pb-twin.o
- # Makefiles
-@@ -68,6 +70,7 @@ client_objs = $(lib_objs) $(ui_common_objs)
- all: $(uis) $(daemons) $(utils)
- # ncurses cui
-+pb_cui_objs-y$(ENABLE_PS3) += ui/ncurses/pb-cui.o
- pb_cui_objs-$(ENABLE_PS3) += ui/ncurses/ps3-cui.o ui/common/ps3.o
- pb_cui_ldflags-$(ENABLE_PS3) += -lps3-utils
-@@ -120,13 +123,16 @@ parser-test: $(parser_test)
- install: all $(rules) $(udhcpc)
-       $(INSTALL) -d $(DESTDIR)$(sbindir)/
--      $(INSTALL) $(daemons) $(uis) $(utils) $(DESTDIR)$(sbindir)/
-+      $(INSTALL_PROGRAM) $(daemons) $(uis) $(utils) $(DESTDIR)$(sbindir)/
-       $(INSTALL) -d $(DESTDIR)$(pkgdatadir)/artwork/
--      $(INSTALL) $(addprefix $(top_srcdir)/ui/twin/artwork/,$(artwork)) \
-+      $(INSTALL_DATA) $(addprefix $(top_srcdir)/ui/twin/artwork/,$(artwork)) \
-               $(DESTDIR)$(pkgdatadir)/artwork/
-       $(INSTALL) -d $(DESTDIR)$(pkgdatadir)/utils
--      $(INSTALL) -m 644 $(top_srcdir)/$(rules) $(DESTDIR)$(pkgdatadir)/utils
--      $(INSTALL) -m 644 $(top_srcdir)/$(udhcpc) $(DESTDIR)$(pkgdatadir)/utils
-+      $(INSTALL_DATA) $(top_srcdir)/$(rules) $(DESTDIR)$(pkgdatadir)/utils
-+      $(INSTALL_DATA) $(top_srcdir)/$(udhcpc) $(DESTDIR)$(pkgdatadir)/utils
-+      $(INSTALL) -d $(DESTDIR)$(mandir)/man8/
-+      $(INSTALL_DATA) $(addprefix $(top_srcdir)/man/, $(man8)) \
-+              $(DESTDIR)$(mandir)/man8/
- dist: $(PACKAGE)-$(VERSION).tar.gz
-diff --git a/ui/common/joystick.c b/ui/common/joystick.c
-new file mode 100644
-index 0000000..94c85fe
---- /dev/null
-+++ b/ui/common/joystick.c
-@@ -0,0 +1,106 @@
-+/*
-+ *  Copyright (C) 2009 Sony Computer Entertainment Inc.
-+ *  Copyright 2009 Sony Corp.
-+ *
-+ *  This program is free software; you can redistribute it and/or modify
-+ *  it under the terms of the GNU General Public License as published by
-+ *  the Free Software Foundation; version 2 of the License.
-+ *
-+ *  This program is distributed in the hope that it will be useful,
-+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
-+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-+ *  GNU General Public License for more details.
-+ *
-+ *  You should have received a copy of the GNU General Public License
-+ *  along with this program; if not, write to the Free Software
-+ *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
-+ */
-+
-+#if defined(HAVE_CONFIG_H)
-+#include "config.h"
-+#endif
-+
-+#define _GNU_SOURCE
-+#include <assert.h>
-+#include <errno.h>
-+#include <fcntl.h>
-+#include <string.h>
-+#include <sys/stat.h>
-+#include <sys/types.h>
-+
-+#include "log/log.h"
-+#include "talloc/talloc.h"
-+#include "joystick.h"
-+
-+/**
-+ * pjs_process_event - Read joystick event and map to UI key code.
-+ *
-+ * Returns a map routine UI key code or zero.
-+ */
-+
-+int pjs_process_event(const struct pjs *pjs)
-+{
-+      int result;
-+      struct js_event e;
-+
-+      assert(pjs->fd);
-+
-+      result = read(pjs->fd, &e, sizeof(e));
-+
-+      if (result != sizeof(e)) {
-+              pb_log("%s: read failed: %s\n", __func__, strerror(errno));
-+              return 0;
-+      }
-+
-+      return pjs->map(&e);
-+}
-+
-+/**
-+ * pjs_destructor - The talloc destructor for a joystick handler.
-+ */
-+
-+static int pjs_destructor(void *arg)
-+{
-+      struct pjs *pjs = pjs_from_arg(arg);
-+
-+      close(pjs->fd);
-+      pjs->fd = 0;
-+
-+      return 0;
-+}
-+
-+/**
-+ * pjs_init - Initialize the joystick event handler.
-+ */
-+
-+struct pjs *pjs_init(void *ctx, int (*map)(const struct js_event *))
-+{
-+      static const char dev_name[] = "/dev/input/js0";
-+      struct pjs *pjs;
-+
-+      pjs = talloc_zero(ctx, struct pjs);
-+
-+      if (!pjs)
-+              return NULL;
-+
-+      pjs->map = map;
-+      pjs->fd = open(dev_name, O_RDONLY | O_NONBLOCK);
-+
-+      if (pjs->fd < 0) {
-+              pb_log("%s: open %s failed: %s\n", __func__, dev_name,
-+                      strerror(errno));
-+              goto out_err;
-+      }
-+
-+      talloc_set_destructor(pjs, pjs_destructor);
-+
-+      pb_log("%s: using %s\n", __func__, dev_name);
-+
-+      return pjs;
-+
-+out_err:
-+      close(pjs->fd);
-+      pjs->fd = 0;
-+      talloc_free(pjs);
-+      return NULL;
-+}
-diff --git a/ui/common/joystick.h b/ui/common/joystick.h
-new file mode 100644
-index 0000000..cf3fc45
---- /dev/null
-+++ b/ui/common/joystick.h
-@@ -0,0 +1,47 @@
-+/*
-+ *  Copyright (C) 2009 Sony Computer Entertainment Inc.
-+ *  Copyright 2009 Sony Corp.
-+ *
-+ *  This program is free software; you can redistribute it and/or modify
-+ *  it under the terms of the GNU General Public License as published by
-+ *  the Free Software Foundation; version 2 of the License.
-+ *
-+ *  This program is distributed in the hope that it will be useful,
-+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
-+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-+ *  GNU General Public License for more details.
-+ *
-+ *  You should have received a copy of the GNU General Public License
-+ *  along with this program; if not, write to the Free Software
-+ *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
-+ */
-+
-+#if !defined(_PB_JOYSTICK_H)
-+#define _PB_JOYSTICK_H
-+
-+#include <linux/joystick.h>
-+
-+/**
-+ * struct pjs - Petitboot joystick event handler.
-+ * @map: Routine to map from a Linux struct js_event to a ui key code.
-+ */
-+
-+struct pjs {
-+      int fd;
-+      int (*map)(const struct js_event *e);
-+};
-+
-+struct pjs *pjs_init(void *ctx, int (*map)(const struct js_event *));
-+int pjs_process_event(const struct pjs *pjs);
-+
-+static inline struct pjs *pjs_from_arg(void *arg)
-+{
-+      return arg;
-+}
-+
-+static inline int pjs_get_fd(const struct pjs *pjs)
-+{
-+      return pjs->fd;
-+}
-+
-+#endif
+diff --git a/ui/common/discover-client.c b/ui/common/discover-client.c
+index e8ce4dd..5b42b6c 100644
+--- a/ui/common/discover-client.c
++++ b/ui/common/discover-client.c
+@@ -46,7 +46,7 @@ struct discover_client* discover_client_init(
+       client->ops.cb_arg = cb_arg;
+       client->fd = socket(AF_UNIX, SOCK_STREAM, 0);
+-      if (!client->fd < 0) {
++      if (client->fd < 0) {
+               pb_log("%s: socket: %s\n", __func__, strerror(errno));
+               goto out_err;
+       }
 diff --git a/ui/common/loader.c b/ui/common/loader.c
-index babca28..5c69533 100644
+index 0fe62a0..5c69533 100644
 --- a/ui/common/loader.c
 +++ b/ui/common/loader.c
-@@ -227,7 +227,7 @@ enum wget_flags {
- static char *pb_load_wget(void *ctx, struct pb_url *url, enum wget_flags flags)
- {
-       int result;
--      const char *argv[6];
-+      const char *argv[7];
-       const char **p;
-       char *local;
-@@ -238,12 +238,15 @@ static char *pb_load_wget(void *ctx, struct pb_url *url, enum wget_flags flags)
-       p = argv;
-       *p++ = pb_system_apps.wget;                     /* 1 */
--      *p++ = "-O";                                    /* 2 */
--      *p++ = local;                                   /* 3 */
--      *p++ = url->full;                               /* 4 */
-+#if !defined(DEBUG)
-+      *p++ = "--quiet";                               /* 2 */
-+#endif
-+      *p++ = "-O";                                    /* 3 */
-+      *p++ = local;                                   /* 4 */
-+      *p++ = url->full;                               /* 5 */
-       if (flags & wget_no_check_certificate)
--              *p++ = "--no-check-certificate";        /* 5 */
--      *p++ = NULL;                                    /* 6 */
-+              *p++ = "--no-check-certificate";        /* 6 */
-+      *p++ = NULL;                                    /* 7 */
-       result = pb_run_cmd(argv);
-@@ -260,16 +263,22 @@ fail:
+@@ -263,16 +263,22 @@ fail:
  /**
   * pb_load_file - Loads a remote file and returns the local file path.
   * @ctx: The talloc context to associate with the returned string.
@@ -1038,7 +116,7 @@ index babca28..5c69533 100644
        if (!url)
                return NULL;
  
-@@ -277,19 +286,28 @@ char *pb_load_file(void *ctx, const char *remote)
+@@ -280,19 +286,28 @@ char *pb_load_file(void *ctx, const char *remote)
        case pb_url_ftp:
        case pb_url_http:
                local = pb_load_wget(ctx, url, 0);
@@ -1081,402 +159,78 @@ index b06bb43..42d4d4b 100644
 +char *pb_load_file(void *ctx, const char *remote, unsigned int *tempfile);
  
  #endif
-diff --git a/ui/common/ps3.c b/ui/common/ps3.c
-index 5c83289..cb1c8d1 100644
---- a/ui/common/ps3.c
-+++ b/ui/common/ps3.c
-@@ -49,6 +49,11 @@ static const struct os_area_db_id id_flags =
-       .owner = OS_AREA_DB_OWNER_PETITBOOT, /* 3 */
-       .key = 3,
- };
-+static const struct os_area_db_id id_timeout =
-+{
-+      .owner = OS_AREA_DB_OWNER_PETITBOOT, /* 3 */
-+      .key = 4,
-+};
+diff --git a/ui/common/ui-system.c b/ui/common/ui-system.c
+index bd6dd31..0140f0e 100644
+--- a/ui/common/ui-system.c
++++ b/ui/common/ui-system.c
+@@ -33,13 +33,13 @@
+ #include "ui-system.h"
  
- struct ps3_flash_ctx {
-       FILE *dev;
-@@ -59,6 +64,8 @@ struct ps3_flash_ctx {
+ /**
+- * run_kexec_local - Final kexec helper.
++ * kexec_load - kexec load helper.
+  * @l_image: The local image file for kexec to execute.
+  * @l_initrd: Optional local initrd file for kexec --initrd, can be NULL.
+  * @args: Optional command line args for kexec --append, can be NULL.
+  */
  
- static void ps3_flash_close(struct ps3_flash_ctx *fc)
+-static int run_kexec_local(const char *l_image, const char *l_initrd,
++static int kexec_load(const char *l_image, const char *l_initrd,
+       const char *args)
  {
-+      assert(fc->dev);
-+
-       fclose(fc->dev);
-       fc->dev = NULL;
- }
-@@ -104,19 +111,19 @@ int ps3_flash_get_values(struct ps3_flash_values *values)
-       struct ps3_flash_ctx fc;
-       uint64_t tmp;
--      memset(values, 0, sizeof(*values));
--
-       result = ps3_flash_open(&fc, "r");
-       if (result)
--              return -1;
-+              goto done;
+       int result;
+@@ -49,34 +49,64 @@ static int run_kexec_local(const char *l_image, const char *l_initrd,
+       char *s_args = NULL;
  
-       result = os_area_db_read(&fc.db, &fc.header, fc.dev);
+       p = argv;
+-      *p++ = pb_system_apps.kexec;            /* 1 */
++      *p++ = pb_system_apps.kexec;    /* 1 */
++      *p++ = "-l";                    /* 2 */
  
-+      ps3_flash_close(&fc);
-+
-       if (result) {
-               pb_log("%s: os_area_db_read failed: %s\n", __func__,
-                       strerror(errno));
--              goto fail;
-+              goto done;
+       if (l_initrd) {
+               s_initrd = talloc_asprintf(NULL, "--initrd=%s", l_initrd);
+               assert(s_initrd);
+-              *p++ = s_initrd;                 /* 2 */
++              *p++ = s_initrd;         /* 3 */
        }
  
-       sum = result = os_area_db_get(&fc.db, &id_default_item, &tmp);
-@@ -124,21 +131,25 @@ int ps3_flash_get_values(struct ps3_flash_values *values)
-       if (!result)
-               values->default_item = (uint32_t)tmp;
--      sum += result = os_area_db_get(&fc.db, &id_video_mode, &tmp);
-+      result = os_area_db_get(&fc.db, &id_timeout, &tmp);
-       if (!result)
--              values->video_mode = (uint16_t)tmp;
-+              values->timeout = (uint8_t)tmp;
-+      sum += result = os_area_db_get(&fc.db, &id_video_mode, &tmp);
--      pb_log("%s: default_item: %u\n", __func__, values->default_item);
--      pb_log("%s: video_mode:   %u\n", __func__, values->video_mode);
-+      if (!result)
-+              values->video_mode = (uint16_t)tmp;
--      ps3_flash_close(&fc);
--      return !!sum;
-+done:
-+      pb_log("%s: default_item: %x\n", __func__,
-+              (unsigned int)values->default_item);
-+      pb_log("%s: timeout: %u\n", __func__,
-+              (unsigned int)values->timeout);
-+      pb_log("%s: video_mode:   %u\n", __func__,
-+              (unsigned int)values->video_mode);
--fail:
--      ps3_flash_close(&fc);
--      return -1;
-+      return (result || sum) ? -1 : 0;
- }
- /**
-@@ -177,6 +188,8 @@ int ps3_flash_set_values(const struct ps3_flash_values *values)
-               }
+       if (args) {
+               s_args = talloc_asprintf(NULL, "--append=%s", args);
+               assert(s_args);
+-              *p++ = s_args;                   /* 3 */
++              *p++ = s_args;          /* 4 */
        }
  
-+      /* timeout is currently read-only, set with ps3-bl-option */
-+
-       result = os_area_db_set_32(&fc.db, &id_default_item,
-               values->default_item);
-       result += os_area_db_set_16(&fc.db, &id_video_mode,
-diff --git a/ui/common/ps3.h b/ui/common/ps3.h
-index 8a7fe1c..5ba8afe 100644
---- a/ui/common/ps3.h
-+++ b/ui/common/ps3.h
-@@ -33,9 +33,14 @@ enum ps3_flash_flags {
-       ps3_flag_telnet = 1,
- };
-+enum ps3_timeouts {
-+      ps3_timeout_forever = 255,
-+};
-+
- /**
-  * struct ps3_flash_values - Values from PS3 flash memory.
-  * @default_item: The default menu item.
-+ * @timeout: The timeout in seconds.
-  * @video_mode: The default video_mode.
-  * @flags: Logical OR of enum ps3_flash_flags.
-  */
-@@ -44,6 +49,13 @@ struct ps3_flash_values {
-       uint32_t default_item;
-       uint16_t video_mode;
-       /* uint16_t flags; */
-+      uint8_t timeout;
-+};
-+
-+static const struct ps3_flash_values ps3_flash_defaults = {
-+      .default_item = 0,
-+      .video_mode = 1,
-+      .timeout = ps3_timeout_forever,
- };
+-      /* First try by telling kexec to run shutdown */
++      *p++ = l_image;                 /* 5 */
++      *p++ = NULL;                    /* 6 */
  
- int ps3_flash_get_values(struct ps3_flash_values *values);
-diff --git a/ui/common/timer.c b/ui/common/timer.c
-new file mode 100644
-index 0000000..954a18a
---- /dev/null
-+++ b/ui/common/timer.c
-@@ -0,0 +1,139 @@
-+/*
-+ *  Copyright (C) 2009 Sony Computer Entertainment Inc.
-+ *  Copyright 2009 Sony Corp.
-+ *
-+ *  This program is free software; you can redistribute it and/or modify
-+ *  it under the terms of the GNU General Public License as published by
-+ *  the Free Software Foundation; version 2 of the License.
-+ *
-+ *  This program is distributed in the hope that it will be useful,
-+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
-+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-+ *  GNU General Public License for more details.
-+ *
-+ *  You should have received a copy of the GNU General Public License
-+ *  along with this program; if not, write to the Free Software
-+ *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
-+ */
-+
-+#if defined(HAVE_CONFIG_H)
-+#include "config.h"
-+#endif
-+
-+#define _GNU_SOURCE
-+#include <assert.h>
-+#include <limits.h>
-+#include <unistd.h>
+-      *(p + 0) = l_image;
+-      *(p + 1) = NULL;
++      result = pb_run_cmd(argv);
 +
-+#include "log/log.h"
-+#include "timer.h"
++      if (result)
++              pb_log("%s: failed: (%d)\n", __func__, result);
 +
-+/**
-+ * ui_timer_init - Initialize the timer for use.
-+ * @seconds: The final timeout value in seconds.
-+ */
++      talloc_free(s_initrd);
++      talloc_free(s_args);
 +
-+void ui_timer_init(struct ui_timer *timer, unsigned int seconds)
-+{
-+      pb_log("%s: %u\n", __func__, seconds);
-+      assert(!timer->disabled);
-+      timer->timeout = seconds;
++      return result;
 +}
 +
 +/**
-+ * ui_timer_next - Calculate the next timer interval.
++ * kexec_reboot - Helper to boot the new kernel.
++ *
++ * Must only be called after a successful call to kexec_load().
 + */
 +
-+static unsigned int ui_timer_next(unsigned int seconds)
++static int kexec_reboot(void)
 +{
-+      unsigned int next;
-+
-+      if (seconds == 0) {
-+              next = 0;
-+              goto done;
-+      }
++      int result;
++      const char *argv[4];
++      const char **p;
 +
-+      if (seconds <= 10) {
-+              next = 1;
-+              goto done;
-+      }
-+
-+      if (seconds <= 60) {
-+              next = seconds % 5;
-+              next = next ? next : 5;
-+              goto done;
-+      }
-+
-+      next = seconds % 10;
-+      next = next ? next : 10;
-+
-+done:
-+      pb_log("%s: %u = %u\n", __func__, seconds, next);
-+      return next;
-+}
-+
-+/**
-+ * ui_timer_kick - Kickstart the next timer interval.
-+ */
-+
-+void ui_timer_kick(struct ui_timer *timer)
-+{
-+      unsigned int next;
-+
-+      if(timer->disabled)
-+              return;
-+
-+      if (timer->update_display)
-+              timer->update_display(timer, timer->timeout);
-+
-+      next = ui_timer_next(timer->timeout);
-+      timer->timeout -= next;
-+
-+      if (next) {
-+              alarm(next);
-+              return;
-+      }
-+
-+      pb_log("%s: timed out\n", __func__);
-+
-+      ui_timer_disable(timer);
-+      timer->handle_timeout(timer);
-+}
-+
-+/**
-+ * ui_timer_disable - Stop and disable the timer.
-+ */
-+
-+void ui_timer_disable(struct ui_timer *timer)
-+{
-+      if (timer->disabled)
-+              return;
-+
-+      pb_log("%s\n", __func__);
-+      timer->disabled = 1;
-+      timer->timeout = UINT_MAX;
-+      alarm(0);
-+}
-+
-+/**
-+ * ui_timer_sigalrm
-+ *
-+ * Called at SIGALRM.
-+ */
-+
-+void ui_timer_sigalrm(struct ui_timer *timer)
-+{
-+      timer->signaled = 1;
-+}
-+
-+/**
-+ * ui_timer_process_sig - Process a timer signal
-+ */
-+
-+void ui_timer_process_sig(struct ui_timer *timer)
-+{
-+      while (timer->signaled) {
-+              timer->signaled = 0;
-+              ui_timer_kick(timer);
-+      }
-+}
-diff --git a/ui/common/timer.h b/ui/common/timer.h
-new file mode 100644
-index 0000000..781b442
---- /dev/null
-+++ b/ui/common/timer.h
-@@ -0,0 +1,42 @@
-+/*
-+ *  Copyright (C) 2009 Sony Computer Entertainment Inc.
-+ *  Copyright 2009 Sony Corp.
-+ *
-+ *  This program is free software; you can redistribute it and/or modify
-+ *  it under the terms of the GNU General Public License as published by
-+ *  the Free Software Foundation; version 2 of the License.
-+ *
-+ *  This program is distributed in the hope that it will be useful,
-+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
-+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-+ *  GNU General Public License for more details.
-+ *
-+ *  You should have received a copy of the GNU General Public License
-+ *  along with this program; if not, write to the Free Software
-+ *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
-+ */
-+
-+#if !defined(_PB_UI_TIMER_H)
-+#define _PB_UI_TIMER_H
-+
-+#include <signal.h>
-+
-+/**
-+ * struct ui_timer - UI timeout.
-+ */
-+
-+struct ui_timer {
-+      unsigned int timeout;
-+      unsigned int disabled;
-+      sig_atomic_t signaled;
-+      void (*update_display)(struct ui_timer *timer, unsigned int timeout);
-+      void (*handle_timeout)(struct ui_timer *timer);
-+};
-+
-+void ui_timer_init(struct ui_timer *timer, unsigned int seconds);
-+void ui_timer_kick(struct ui_timer *timer);
-+void ui_timer_disable(struct ui_timer *timer);
-+void ui_timer_sigalrm(struct ui_timer *timer);
-+void ui_timer_process_sig(struct ui_timer *timer);
-+
-+#endif
-diff --git a/ui/common/ui-system.c b/ui/common/ui-system.c
-index 3f54191..0140f0e 100644
---- a/ui/common/ui-system.c
-+++ b/ui/common/ui-system.c
-@@ -33,46 +33,80 @@
- #include "ui-system.h"
- /**
-- * run_kexec_local - Final kexec helper.
-+ * kexec_load - kexec load helper.
-  * @l_image: The local image file for kexec to execute.
-  * @l_initrd: Optional local initrd file for kexec --initrd, can be NULL.
-  * @args: Optional command line args for kexec --append, can be NULL.
-  */
--static int run_kexec_local(const char *l_image, const char *l_initrd,
-+static int kexec_load(const char *l_image, const char *l_initrd,
-       const char *args)
- {
-       int result;
--      const char *argv[8];
-+      const char *argv[6];
-       const char **p;
-+      char *s_initrd = NULL;
-+      char *s_args = NULL;
-       p = argv;
--      *p++ = pb_system_apps.kexec;            /* 1 */
-+      *p++ = pb_system_apps.kexec;    /* 1 */
-+      *p++ = "-l";                    /* 2 */
-       if (l_initrd) {
--              *p++ = "--initrd";              /* 2 */
--              *p++ = l_initrd;                /* 3 */
-+              s_initrd = talloc_asprintf(NULL, "--initrd=%s", l_initrd);
-+              assert(s_initrd);
-+              *p++ = s_initrd;         /* 3 */
-       }
-       if (args) {
--              *p++ = "--append";              /* 4 */
--              *p++ = args;                    /* 5 */
-+              s_args = talloc_asprintf(NULL, "--append=%s", args);
-+              assert(s_args);
-+              *p++ = s_args;          /* 4 */
-       }
--      /* First try by telling kexec to run shutdown */
-+      *p++ = l_image;                 /* 5 */
-+      *p++ = NULL;                    /* 6 */
--      *(p + 0) = l_image;
--      *(p + 1) = NULL;
-+      result = pb_run_cmd(argv);
-+
-+      if (result)
-+              pb_log("%s: failed: (%d)\n", __func__, result);
-+
-+      talloc_free(s_initrd);
-+      talloc_free(s_args);
-+
-+      return result;
-+}
-+
-+/**
-+ * kexec_reboot - Helper to boot the new kernel.
-+ *
-+ * Must only be called after a successful call to kexec_load().
-+ */
-+
-+static int kexec_reboot(void)
-+{
-+      int result;
-+      const char *argv[4];
-+      const char **p;
-+
-+      /* First try running shutdown.  Init scripts should run 'exec -e' */
++      /* First try running shutdown.  Init scripts should run 'exec -e' */
 +
 +      p = argv;
 +      *p++ = pb_system_apps.shutdown; /* 1 */
@@ -1491,9 +245,9 @@ index 3f54191..0140f0e 100644
 +      /* On error, force a kexec with the -e option */
  
        if (result) {
--              *(p + 0) = "-f";                /* 6 */
--              *(p + 1) = l_image;             /* 7 */
--              *(p + 2) = NULL;                /* 8 */
+-              *(p + 0) = "-f";                /* 4 */
+-              *(p + 1) = l_image;             /* 5 */
+-              *(p + 2) = NULL;                /* 6 */
 +              p = argv;
 +              *p++ = pb_system_apps.kexec;    /* 1 */
 +              *p++ = "-e";                    /* 2 */
@@ -1501,22 +255,20 @@ index 3f54191..0140f0e 100644
  
                result = pb_run_cmd(argv);
        }
-@@ -85,38 +119,51 @@ static int run_kexec_local(const char *l_image, const char *l_initrd,
+@@ -84,9 +114,6 @@ static int run_kexec_local(const char *l_image, const char *l_initrd,
+       if (result)
+               pb_log("%s: failed: (%d)\n", __func__, result);
  
- /**
-  * pb_run_kexec - Run kexec with the supplied boot options.
-- *
-- * For the convenience of the user, tries to load both files before
-- * returning error.
-  */
+-      talloc_free(s_initrd);
+-      talloc_free(s_args);
+-
+       return result;
+ }
  
- int pb_run_kexec(const struct pb_kexec_data *kd)
- {
+@@ -99,31 +126,44 @@ int pb_run_kexec(const struct pb_kexec_data *kd)
        int result;
--      char *l_image;
--      char *l_initrd;
-+      char *l_image = NULL;
-+      char *l_initrd = NULL;
+       char *l_image = NULL;
+       char *l_initrd = NULL;
 +      unsigned int clean_image = 0;
 +      unsigned int clean_initrd = 0;
  
@@ -1524,35 +276,31 @@ index 3f54191..0140f0e 100644
        pb_log("%s: initrd: '%s'\n", __func__, kd->initrd);
        pb_log("%s: args:   '%s'\n", __func__, kd->args);
  
--      if (kd->image)
--              l_image = pb_load_file(NULL, kd->image);
--      else {
--              l_image = NULL;
--              pb_log("%s: error null image\n", __func__);
 +      result = -1;
 +
-+      if (kd->image) {
+       if (kd->image) {
+-              l_image = pb_load_file(NULL, kd->image);
 +              l_image = pb_load_file(NULL, kd->image, &clean_image);
-+              if (!l_image)
+               if (!l_image)
+-                      return -1;
 +                      goto no_load;
-+      }
-+
-+      if (kd->initrd) {
+       }
+       if (kd->initrd) {
+-              l_initrd = pb_load_file(NULL, kd->initrd);
 +              l_initrd = pb_load_file(NULL, kd->initrd, &clean_initrd);
-+              if (!l_initrd)
+               if (!l_initrd)
+-                      return -1;
 +                      goto no_load;
        }
  
--      l_initrd = kd->initrd ? pb_load_file(NULL, kd->initrd) : NULL;
-+      if (!l_image && !l_initrd)
+       if (!l_image && !l_initrd)
+-              return -1;
 +              goto no_load;
 +
 +      result = kexec_load(l_image, l_initrd, kd->args);
  
--      if (!l_image || (kd->initrd && !l_initrd))
--              result = -1;
--      else
--              result = run_kexec_local(l_image, l_initrd, kd->args);
+-      result = run_kexec_local(l_image, l_initrd, kd->args);
 +no_load:
 +      if (clean_image)
 +              unlink(l_image);
@@ -1568,1508 +316,3 @@ index 3f54191..0140f0e 100644
        return result;
  }
  
-diff --git a/ui/ncurses/nc-cui.c b/ui/ncurses/nc-cui.c
-index 2a4c971..aed5ff7 100644
---- a/ui/ncurses/nc-cui.c
-+++ b/ui/ncurses/nc-cui.c
-@@ -30,7 +30,6 @@
- #include "ui/common/discover-client.h"
- #include "nc-cui.h"
--
- static struct cui_opt_data *cod_from_item(struct pmenu_item *item)
- {
-       return item->data;
-@@ -61,6 +60,35 @@ void cui_resize(struct cui *cui)
- }
- /**
-+ * cui_make_item_name - Format the menu item name srting.
-+ *
-+ * Returns a talloc string.
-+ */
-+
-+static char *cui_make_item_name(struct pmenu_item *i, struct cui_opt_data *cod)
-+{
-+      char *name;
-+
-+      assert(cod->name);
-+      assert(cod->kd);
-+
-+      name = talloc_asprintf(i, "%s:", cod->name);
-+
-+      if (cod->kd->image)
-+              name = talloc_asprintf_append(name, " %s", cod->kd->image);
-+
-+      if (cod->kd->initrd)
-+              name = talloc_asprintf_append(name, " initrd=%s",
-+                      cod->kd->initrd);
-+
-+      if (cod->kd->args)
-+              name = talloc_asprintf_append(name, " %s", cod->kd->args);
-+
-+      DBGS("@%s@\n", name);
-+      return name;
-+}
-+
-+/**
-  * cui_on_exit - A generic main menu ESC callback.
-  */
-@@ -109,8 +137,8 @@ static int cui_run_kexec(struct pmenu_item *item)
-       assert(cui->current == &cui->main->scr);
-       assert(cui->on_kexec);
--      pb_log("%s: %s\n", __func__, cod->dev->name, cod->opt->name);
--      nc_scr_status_printf(cui->current, "Booting %s...", cod->opt->name);
-+      pb_log("%s: %s\n", __func__, cod->name);
-+      nc_scr_status_printf(cui->current, "Booting %s...", cod->name);
-       def_prog_mode();
-@@ -141,9 +169,10 @@ static void cui_ked_on_exit(struct ked *ked, enum ked_result ked_result,
- {
-       struct cui *cui = cui_from_arg(ked->scr.ui_ctx);
--      if (ked_result == ked_update || ked_result == ked_boot) {
-+      if (ked_result == ked_update) {
-               struct pmenu_item *i = pmenu_find_selected(cui->main);
-               struct cui_opt_data *cod = cod_from_item(i);
-+              char *name;
-               assert(kd);
-@@ -151,7 +180,13 @@ static void cui_ked_on_exit(struct ked *ked, enum ked_result ked_result,
-               talloc_free(cod->kd);
-               cod->kd = kd;
--              pb_log("%s: updating opt '%s'\n", __func__, cod->opt->name);
-+              name = cui_make_item_name(i, cod);
-+              pmenu_item_replace(i, name);
-+
-+              /* FIXME: need to make item visible somehow */
-+              set_current_item(cui->main->ncm, i->nci);
-+
-+              pb_log("%s: updating opt '%s'\n", __func__, cod->name);
-               pb_log(" image  '%s'\n", cod->kd->image);
-               pb_log(" initrd '%s'\n", cod->kd->initrd);
-               pb_log(" args   '%s'\n", cod->kd->args);
-@@ -160,20 +195,15 @@ static void cui_ked_on_exit(struct ked *ked, enum ked_result ked_result,
-       cui_set_current(cui, &cui->main->scr);
-       talloc_free(ked);
--
--      if (ked_result == ked_boot) {
--              struct pmenu_item *i = pmenu_find_selected(cui->main);
--              i->on_execute(i);
--      }
- }
- int cui_ked_run(struct pmenu_item *item)
- {
-       struct cui *cui = cui_from_item(item);
-+      struct cui_opt_data *cod = cod_from_item(item);
-       struct ked *ked;
--      ked = ked_init(cui, cod_from_item(item)->kd, cui_ked_on_exit);
--
-+      ked = ked_init(cui, cod->kd, cui_ked_on_exit);
-       cui_set_current(cui, &ked->scr);
-       return 0;
-@@ -200,14 +230,43 @@ struct nc_scr *cui_set_current(struct cui *cui, struct nc_scr *scr)
-       return old;
- }
-+/**
-+ * cui_process_key - Process input on stdin.
-+ */
-+
- static int cui_process_key(void *arg)
- {
-       struct cui *cui = cui_from_arg(arg);
-       assert(cui->current);
-+
-+      ui_timer_disable(&cui->timer);
-       cui->current->process_key(cui->current);
-+
-+      return 0;
-+}
-+
-+/**
-+ * cui_process_js - Process joystick events.
-+ */
-+
-+static int cui_process_js(void *arg)
-+{
-+      struct cui *cui = cui_from_arg(arg);
-+      int c;
-+
-+      c = pjs_process_event(cui->pjs);
-+
-+      if (c) {
-+              ungetch(c);
-+              cui_process_key(arg);
-+      }
-+
-       return 0;
- }
-+/**
-+ * cui_client_process_socket - Process a socket event from the discover server.
-+ */
- static int cui_client_process_socket(void *arg)
- {
-@@ -218,6 +277,24 @@ static int cui_client_process_socket(void *arg)
- }
- /**
-+ * cui_handle_timeout - Handle the timeout.
-+ */
-+
-+static void cui_handle_timeout(struct ui_timer *timer)
-+{
-+      struct cui *cui = cui_from_timer(timer);
-+      struct pmenu_item *i = pmenu_find_selected(cui->main);
-+
-+#if defined(DEBUG)
-+      {
-+              struct cui_opt_data *cod = cod_from_item(i);
-+              assert(cod && (cod->opt_hash == cui->default_item));
-+      }
-+#endif
-+      i->on_execute(i);
-+}
-+
-+/**
-  * cui_handle_resize - Handle the term resize.
-  */
-@@ -243,6 +320,46 @@ static void cui_handle_resize(struct cui *cui)
- }
- /**
-+ * cui_on_open - Open new item callback.
-+ */
-+
-+void cui_on_open(struct pmenu *menu)
-+{
-+      unsigned int insert_pt;
-+      struct pmenu_item *i;
-+      struct cui_opt_data *cod;
-+
-+      menu->scr.unpost(&menu->scr);
-+
-+      /* This disconnects items array from menu. */
-+
-+      set_menu_items(menu->ncm, NULL);
-+
-+      /* Insert new items at insert_pt. */
-+
-+      insert_pt = pmenu_grow(menu, 1);
-+      i = pmenu_item_alloc(menu);
-+
-+      i->on_edit = cui_ked_run;
-+      i->on_execute = cui_run_kexec;
-+      i->data = cod = talloc_zero(i, struct cui_opt_data);
-+
-+      cod->name = talloc_asprintf(i, "User item %u:", insert_pt);
-+      cod->kd = talloc_zero(i, struct pb_kexec_data);
-+
-+      pmenu_item_setup(menu, i, insert_pt, talloc_strdup(i, cod->name));
-+
-+      /* Re-attach the items array. */
-+
-+      set_menu_items(menu->ncm, menu->items);
-+
-+      menu->scr.post(&menu->scr);
-+      set_current_item(menu->ncm, i->nci);
-+
-+      i->on_edit(i);
-+}
-+
-+/**
-  * cui_device_add - Client device_add callback.
-  *
-  * Creates menu_items for all the device boot_options and inserts those
-@@ -284,23 +401,11 @@ static int cui_device_add(struct device *dev, void *arg)
-               struct pmenu_item *i;
-               struct cui_opt_data *cod;
-               char *name;
--              char *description;
-               /* Save the item in opt->ui_info for cui_device_remove() */
-               opt->ui_info = i = pmenu_item_alloc(cui->main);
--              name = talloc_asprintf(i, "%s: %s", opt->name,
--                      opt->description);
--
--              description = talloc_asprintf(i,
--                      " kexec: image='%s' initrd='%s' args='%s'",
--                      opt->boot_image_file,
--                      opt->initrd_file,
--                      opt->boot_args);
--
--              pmenu_item_setup(cui->main, i, insert_pt, name, description);
--
-               i->on_edit = cui_ked_run;
-               i->on_execute = cui_run_kexec;
-               i->data = cod = talloc(i, struct cui_opt_data);
-@@ -308,23 +413,29 @@ static int cui_device_add(struct device *dev, void *arg)
-               cod->dev = dev;
-               cod->opt = opt;
-               cod->opt_hash = pb_opt_hash(dev, opt);
-+              cod->name = opt->name;
-               cod->kd = talloc(i, struct pb_kexec_data);
-               cod->kd->image = talloc_strdup(cod->kd, opt->boot_image_file);
-               cod->kd->initrd = talloc_strdup(cod->kd, opt->initrd_file);
-               cod->kd->args = talloc_strdup(cod->kd, opt->boot_args);
--              insert_pt++;
--
--              /* If this is the default_item select it. */
-+              name = cui_make_item_name(i, cod);
-+              pmenu_item_setup(cui->main, i, insert_pt, name);
--              if (cod->opt_hash == cui->default_item)
--                      selected = i->nci;
-+              insert_pt++;
--              pb_log("%s: adding opt '%s'\n", __func__, cod->opt->name);
-+              pb_log("%s: adding opt '%s'\n", __func__, cod->name);
-               pb_log("   image  '%s'\n", cod->kd->image);
-               pb_log("   initrd '%s'\n", cod->kd->initrd);
-               pb_log("   args   '%s'\n", cod->kd->args);
-+
-+              /* If this is the default_item select it and start timer. */
-+
-+              if (cod->opt_hash == cui->default_item) {
-+                      selected = i->nci;
-+                      ui_timer_kick(&cui->timer);
-+              }
-       }
-       /* Re-attach the items array. */
-@@ -340,6 +451,9 @@ static int cui_device_add(struct device *dev, void *arg)
-                       item_count(cui->main->ncm) + 1);
-       }
-+      /* FIXME: need to make item visible somehow */
-+      menu_driver(cui->main->ncm, REQ_SCR_UPAGE);
-+      menu_driver(cui->main->ncm, REQ_SCR_DPAGE);
-       set_current_item(cui->main->ncm, selected);
-       if (cui->current == &cui->main->scr)
-@@ -375,9 +489,15 @@ static void cui_device_remove(struct device *dev, void *arg)
-       list_for_each_entry(&dev->boot_options, opt, list) {
-               struct pmenu_item *i = pmenu_item_from_arg(opt->ui_info);
-+              struct cui_opt_data *cod = cod_from_item(i);
--              assert(pb_protocol_device_cmp(dev, cod_from_item(i)->dev));
-+              assert(pb_protocol_device_cmp(dev, cod->dev));
-               pmenu_remove(cui->main, i);
-+
-+              /* If this is the default_item disable timer. */
-+
-+              if (cod->opt_hash == cui->default_item)
-+                      ui_timer_disable(&cui->timer);
-       }
-       /* Re-attach the items array. */
-@@ -397,10 +517,6 @@ static void cui_device_remove(struct device *dev, void *arg)
-               cui->current->post(cui->current);
- }
--/**
-- * cui_client_process_socket - Process a socket event from the discover server.
-- */
--
- static struct discover_client_ops cui_client_ops = {
-       .device_add = cui_device_add,
-       .device_remove = cui_device_remove,
-@@ -417,7 +533,8 @@ static struct discover_client_ops cui_client_ops = {
-  */
- struct cui *cui_init(void* platform_info,
--      int (*on_kexec)(struct cui *, struct cui_opt_data *))
-+      int (*on_kexec)(struct cui *, struct cui_opt_data *),
-+      int (*js_map)(const struct js_event *e))
- {
-       struct cui *cui;
-       struct discover_client *client;
-@@ -434,6 +551,7 @@ struct cui *cui_init(void* platform_info,
-       cui->c_sig = pb_cui_sig;
-       cui->platform_info = platform_info;
-       cui->on_kexec = on_kexec;
-+      cui->timer.handle_timeout = cui_handle_timeout;
-       /* Loop here for scripts that just started the server. */
-@@ -462,6 +580,15 @@ struct cui *cui_init(void* platform_info,
-       waiter_register(STDIN_FILENO, WAIT_IN, cui_process_key, cui);
-+      if (js_map) {
-+
-+              cui->pjs = pjs_init(cui, js_map);
-+
-+              if (cui->pjs)
-+                      waiter_register(pjs_get_fd(cui->pjs), WAIT_IN,
-+                              cui_process_js, cui);
-+      }
-+
-       return cui;
- fail_client_init:
-@@ -501,6 +628,8 @@ int cui_run(struct cui *cui, struct pmenu *main, unsigned int default_item)
-               if (cui->abort)
-                       break;
-+              ui_timer_process_sig(&cui->timer);
-+
-               while (cui->resize) {
-                       cui->resize = 0;
-                       cui_handle_resize(cui);
-diff --git a/ui/ncurses/nc-cui.h b/ui/ncurses/nc-cui.h
-index 668776e..94fef6b 100644
---- a/ui/ncurses/nc-cui.h
-+++ b/ui/ncurses/nc-cui.h
-@@ -21,15 +21,19 @@
- #include <signal.h>
-+#include "ui/common/joystick.h"
-+#include "ui/common/timer.h"
- #include "nc-menu.h"
- #include "nc-ked.h"
--
- struct cui_opt_data {
-+      const char *name;
-+      struct pb_kexec_data *kd;
-+
-+      /* optional data */
-       const struct device *dev;
-       const struct boot_option *opt;
-       uint32_t opt_hash;
--      struct pb_kexec_data *kd;
- };
- /**
-@@ -49,13 +53,16 @@ struct cui {
-       sig_atomic_t resize;
-       struct nc_scr *current;
-       struct pmenu *main;
-+      struct ui_timer timer;
-+      struct pjs *pjs;
-       void *platform_info;
-       unsigned int default_item;
-       int (*on_kexec)(struct cui *cui, struct cui_opt_data *cod);
- };
- struct cui *cui_init(void* platform_info,
--      int (*on_kexec)(struct cui *, struct cui_opt_data *));
-+      int (*on_kexec)(struct cui *, struct cui_opt_data *),
-+      int (*js_map)(const struct js_event *e));
- struct nc_scr *cui_set_current(struct cui *cui, struct nc_scr *scr);
- int cui_run(struct cui *cui, struct pmenu *main, unsigned int default_item);
- int cui_ked_run(struct pmenu_item *item);
-@@ -65,6 +72,7 @@ int cui_ked_run(struct pmenu_item *item);
- void cui_abort(struct cui *cui);
- void cui_resize(struct cui *cui);
- void cui_on_exit(struct pmenu *menu);
-+void cui_on_open(struct pmenu *menu);
- int cui_run_cmd(struct pmenu_item *item);
- static inline struct cui *cui_from_arg(void *arg)
-@@ -85,4 +93,15 @@ static inline struct cui *cui_from_item(struct pmenu_item *item)
-       return cui_from_pmenu(item->pmenu);
- }
-+static inline struct cui *cui_from_timer(struct ui_timer *timer)
-+{
-+      struct cui *cui;
-+
-+      cui = (struct cui *)((char *)timer
-+              - (size_t)&((struct cui *)0)->timer);
-+      assert(cui->c_sig == pb_cui_sig);
-+
-+      return cui;
-+}
-+
- #endif
-diff --git a/ui/ncurses/nc-ked.c b/ui/ncurses/nc-ked.c
-index 3bdbd6c..806d389 100644
---- a/ui/ncurses/nc-ked.c
-+++ b/ui/ncurses/nc-ked.c
-@@ -48,12 +48,15 @@ static struct ked *ked_from_arg(void *arg)
-  * @req: An ncurses request or char to send to form_driver().
-  */
--static void ked_move_cursor(struct ked *ked, int req)
-+static int ked_move_cursor(struct ked *ked, int req)
- {
-+      int result;
-+
-       wchgat(ked->scr.sub_ncw, 1, ked_attr_field_selected, 0, 0);
--      form_driver(ked->ncf, req);
-+      result = form_driver(ked->ncf, req);
-       wchgat(ked->scr.sub_ncw, 1, ked->attr_cursor, 0, 0);
-       wrefresh(ked->scr.main_ncw);
-+      return result;
- }
- /**
-@@ -93,12 +96,15 @@ static void ked_insert_mode_tog(struct ked *ked)
-  * @req: An ncurses request to send to form_driver().
-  */
--static void ked_move_field(struct ked *ked, int req)
-+static int ked_move_field(struct ked *ked, int req)
- {
-+      int result;
-+
-       set_field_back(current_field(ked->ncf), ked_attr_field_normal);
--      form_driver(ked->ncf, req);
-+      result = form_driver(ked->ncf, req);
-       set_field_back(current_field(ked->ncf), ked_attr_field_selected);
-       ked_move_cursor(ked, REQ_END_FIELD);
-+      return result;
- }
- static int ked_post(struct nc_scr *scr)
-@@ -143,9 +149,10 @@ static char *ked_chomp(char *s)
-       for (; s < s_end; s++)
-               if (*s != ' ' && *s != '\t')
-                       break;
--      start = s;
--      for (++s; s < s_end; s++)
-+      start = end = s;
-+
-+      for (; s < s_end; s++)
-               if (*s != ' ' && *s != '\t')
-                       end = s;
-       *(end + 1) = 0;
-@@ -183,41 +190,33 @@ static struct pb_kexec_data *ked_prepare_data(struct ked *ked)
- static void ked_process_key(struct nc_scr *scr)
- {
-       struct ked *ked = ked_from_scr(scr);
-+      struct pb_kexec_data *kd;
-       while (1) {
-               int c = getch();
-+              if (c == ERR)
-+                      return;
-+
-+              /* DBGS("%d (%o)\n", c, c); */
-+
-               switch (c) {
-               default:
-                       ked_move_cursor(ked, c);
--              break;
--              case ERR:
--                      return;
-+                      break;
-               /* hot keys */
--              case 2: { /* CTRL-B */
--                      struct pb_kexec_data *kd;
--
--                      form_driver(ked->ncf, REQ_VALIDATION);
--                      kd = ked_prepare_data(ked);
--                      ked->on_exit(ked, ked_boot, kd);
--                      nc_flush_keys();
--                      return;
--              }
-               case 27: /* ESC */
-                       ked->on_exit(ked, ked_cancel, NULL);
-                       nc_flush_keys();
-                       return;
-               case '\n':
--              case '\r': {
--                      struct pb_kexec_data *kd;
--
-+              case '\r':
-                       form_driver(ked->ncf, REQ_VALIDATION);
-                       kd = ked_prepare_data(ked);
-                       ked->on_exit(ked, ked_update, kd);
-                       nc_flush_keys();
-                       return;
--              }
-               /* insert mode */
-               case KEY_IC:
-@@ -252,8 +251,8 @@ static void ked_process_key(struct nc_scr *scr)
-                       ked_move_cursor(ked, REQ_RIGHT_CHAR);
-                       break;
-               case KEY_BACKSPACE:
--                      ked_move_cursor(ked, REQ_LEFT_CHAR);
--                      ked_move_cursor(ked, REQ_DEL_CHAR);
-+                      if (ked_move_cursor(ked, REQ_LEFT_CHAR) == E_OK)
-+                              ked_move_cursor(ked, REQ_DEL_CHAR);
-                       break;
-               case KEY_DC:
-                       ked_move_cursor(ked, REQ_DEL_CHAR);
-@@ -325,7 +324,7 @@ struct ked *ked_init(void *ui_ctx, const struct pb_kexec_data *kd,
-       ked->scr.frame.title = talloc_strdup(ked, "Petitboot Option Editor");
-       ked->scr.frame.help = talloc_strdup(ked,
--              "ESC=cancel, Enter=accept, Ctrl-b=boot");
-+              "ESC=cancel, Enter=accept");
-       ked->on_exit = on_exit;
-diff --git a/ui/ncurses/nc-ked.h b/ui/ncurses/nc-ked.h
-index 36ed4f1..62fddd6 100644
---- a/ui/ncurses/nc-ked.h
-+++ b/ui/ncurses/nc-ked.h
-@@ -20,6 +20,7 @@
- #define _PB_NC_KED_H
- #include <assert.h>
-+#include <linux/input.h> /* This must be included before ncurses.h */
- #include <form.h>
- #include "pb-protocol/pb-protocol.h"
-@@ -40,13 +41,11 @@ enum ked_attr_cursor {
-  * enum ked_result - Result code for ked:on_exit().
-  * @ked_cancel: The user canceled the operation.
-  * @ked_update: The args were updated.
-- * @ked_boot: The user requested a boot of this item.
-  */
- enum ked_result {
-       ked_cancel,
-       ked_update,
--      ked_boot,
- };
- /**
-diff --git a/ui/ncurses/nc-menu.c b/ui/ncurses/nc-menu.c
-index 0a76b11..f96eb82 100644
---- a/ui/ncurses/nc-menu.c
-+++ b/ui/ncurses/nc-menu.c
-@@ -91,17 +91,17 @@ struct pmenu_item *pmenu_item_alloc(struct pmenu *menu)
- }
- struct pmenu_item *pmenu_item_setup(struct pmenu *menu, struct pmenu_item *i,
--      unsigned int index, const char *name,
--      const char *description)
-+      unsigned int index, const char *name)
- {
-       assert(i);
-+      assert(name);
-       if (!i)
-               return NULL;
-       i->i_sig = pb_item_sig;
-       i->pmenu = menu;
--      i->nci = new_item(name, description);
-+      i->nci = new_item(name, NULL);
-       if (!i->nci) {
-               talloc_free(i);
-@@ -115,6 +115,67 @@ struct pmenu_item *pmenu_item_setup(struct pmenu *menu, struct pmenu_item *i,
-       return i;
- }
-+static int pmenu_item_get_index(const struct pmenu_item *item)
-+{
-+      unsigned int i;
-+
-+      for (i = 0; i < item->pmenu->item_count; i++)
-+              if (item->pmenu->items[i] == item->nci)
-+                      return i;
-+
-+      pb_log("%s: not found: %p %s\n", __func__, item,
-+              (item ? item->nci->name.str : "(null)"));
-+      return -1;
-+}
-+
-+/**
-+ * pmenu_item_replace - Replace the menu item with a new one.
-+ *
-+ * Use this routine to change a menu item's text.
-+ */
-+
-+int pmenu_item_replace(struct pmenu_item *i, const char *name)
-+{
-+      struct pmenu *menu;
-+      ITEM *nci;
-+      int index;
-+
-+      assert(name);
-+      assert(i->nci);
-+
-+      menu = i->pmenu;
-+      index = pmenu_item_get_index(i);
-+
-+      if (index < 0) {
-+              assert(0 && "get_index failed");
-+              return -1;
-+      }
-+
-+      nci = new_item(name, NULL);
-+
-+      if (!nci) {
-+              assert(0 && "new_item failed");
-+              return -1;
-+      }
-+
-+      set_item_userptr(nci, i);
-+
-+      menu->scr.unpost(&menu->scr);
-+      set_menu_items(menu->ncm, NULL);
-+
-+      // FIXME: need to assure item name is a talloc string.
-+      /* talloc_free((char *)item_name(i->nci)); */
-+
-+      free_item(i->nci);
-+      menu->items[index] = nci;
-+      i->nci = nci;
-+
-+      set_menu_items(menu->ncm, menu->items);
-+      menu->scr.post(&menu->scr);
-+
-+      return 0;
-+}
-+
- /**
-  * pmenu_move_cursor - Move the cursor.
-  * @req: An ncurses request or char to send to menu_driver().
-@@ -143,7 +204,7 @@ static void pmenu_process_key(struct nc_scr *scr)
-               if (c == ERR)
-                       return;
--              /* DBGS("%d (%o)\n", c, c); */
-+              if (1) DBGS("%d (%o)\n", c, c);
-               if (menu->hot_key)
-                       c = menu->hot_key(menu, item, c);
-@@ -174,13 +235,16 @@ static void pmenu_process_key(struct nc_scr *scr)
-               case '\t':
-                       pmenu_move_cursor(menu, REQ_DOWN_ITEM);
-                       break;
--
-               case KEY_LEFT:
--              case 'E':
-               case 'e':
-                       if (item->on_edit)
-                               item->on_edit(item);
-                       break;
-+              case 'o':
-+                      DBGS("on_open: %p\n", menu->on_open);
-+                      if (menu->on_open)
-+                              menu->on_open(menu);
-+                      break;
-               case '\n':
-               case '\r':
-                       if (item->on_execute)
-@@ -228,19 +292,6 @@ unsigned int pmenu_grow(struct pmenu *menu, unsigned int count)
-       return tmp;
- }
--static int pmenu_item_get_index(const struct pmenu_item *item)
--{
--      unsigned int i;
--
--      for (i = 0; i < item->pmenu->item_count; i++)
--              if (item->pmenu->items[i] == item->nci)
--                      return i;
--
--      pb_log("%s: not found: %p %s\n", __func__, item,
--              (item ? item->nci->name.str : "(null)"));
--      return -1;
--}
--
- /**
-  * pmenu_remove - Remove an item from the item array.
-  *
-@@ -262,6 +313,9 @@ int pmenu_remove(struct pmenu *menu, struct pmenu_item *item)
-       if (index < 0)
-               return -1;
-+      free_item(item->nci);
-+      talloc_free(item);
-+
-       /* Note that items array has a null terminator. */
-       menu->insert_pt--;
-@@ -349,7 +403,7 @@ void pmenu_delete(struct pmenu *menu)
-       menu->scr.sig = pb_removed_sig;
-       for (i = item_count(menu->ncm); i; i--)
--              free_item(menu->items[i]);
-+              free_item(menu->items[i - 1]);
-       free_menu(menu->ncm);
-       delwin(menu->scr.sub_ncw);
-diff --git a/ui/ncurses/nc-menu.h b/ui/ncurses/nc-menu.h
-index b487df9..4abec6f 100644
---- a/ui/ncurses/nc-menu.h
-+++ b/ui/ncurses/nc-menu.h
-@@ -20,6 +20,7 @@
- #define _PB_NC_MENU_H
- #include <assert.h>
-+#include <linux/input.h> /* This must be included before ncurses.h */
- #include <menu.h>
- #include "log/log.h"
-@@ -45,7 +46,8 @@ struct pmenu_item {
- struct pmenu_item *pmenu_item_alloc(struct pmenu *menu);
- struct pmenu_item *pmenu_item_setup(struct pmenu *menu, struct pmenu_item *i,
--      unsigned int index, const char *name, const char *description);
-+      unsigned int index, const char *name);
-+int pmenu_item_replace(struct pmenu_item *i, const char *name);
- void pmenu_item_delete(struct pmenu_item *item);
- static inline struct pmenu_item *pmenu_item_from_arg(void *arg)
-@@ -57,10 +59,9 @@ static inline struct pmenu_item *pmenu_item_from_arg(void *arg)
- }
- static inline struct pmenu_item *pmenu_item_init(struct pmenu *menu,
--      unsigned int index, const char *name, const char *description)
-+      unsigned int index, const char *name)
- {
--      return pmenu_item_setup(menu, pmenu_item_alloc(menu), index, name,
--              description);
-+      return pmenu_item_setup(menu, pmenu_item_alloc(menu), index, name);
- }
- /**
-@@ -77,6 +78,7 @@ struct pmenu {
-       unsigned int insert_pt;
-       int (*hot_key)(struct pmenu *menu, struct pmenu_item *item, int c);
-       void (*on_exit)(struct pmenu *menu);
-+      void (*on_open)(struct pmenu *menu);
- };
- struct pmenu *pmenu_init(void *ui_ctx, unsigned int item_count,
-diff --git a/ui/ncurses/nc-scr.h b/ui/ncurses/nc-scr.h
-index c08fcd4..2374c20 100644
---- a/ui/ncurses/nc-scr.h
-+++ b/ui/ncurses/nc-scr.h
-@@ -19,6 +19,7 @@
- #if !defined(_PB_NC_SCR_H)
- #define _PB_NC_SCR_H
-+#include <linux/input.h> /* This must be included before ncurses.h */
- #include <ncurses.h>
- #define DBG(fmt, args...) pb_log("DBG: " fmt, ## args)
-diff --git a/ui/ncurses/pb-cui.c b/ui/ncurses/pb-cui.c
-new file mode 100644
-index 0000000..972490a
---- /dev/null
-+++ b/ui/ncurses/pb-cui.c
-@@ -0,0 +1,289 @@
-+/*
-+ * Petitboot generic ncurses bootloader UI
-+ *
-+ *  Copyright (C) 2009 Sony Computer Entertainment Inc.
-+ *  Copyright 2009 Sony Corp.
-+ *
-+ *  This program is free software; you can redistribute it and/or modify
-+ *  it under the terms of the GNU General Public License as published by
-+ *  the Free Software Foundation; version 2 of the License.
-+ *
-+ *  This program is distributed in the hope that it will be useful,
-+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
-+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-+ *  GNU General Public License for more details.
-+ *
-+ *  You should have received a copy of the GNU General Public License
-+ *  along with this program; if not, write to the Free Software
-+ *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
-+ */
-+
-+#if defined(HAVE_CONFIG_H)
-+#include "config.h"
-+#endif
-+
-+#define _GNU_SOURCE
-+#include <errno.h>
-+#include <getopt.h>
-+#include <signal.h>
-+#include <stdlib.h>
-+#include <string.h>
-+#include <sys/time.h>
-+
-+#include "log/log.h"
-+#include "talloc/talloc.h"
-+#include "waiter/waiter.h"
-+#include "ui/common/discover-client.h"
-+#include "nc-cui.h"
-+
-+static void print_version(void)
-+{
-+      printf("pb-cui (" PACKAGE_NAME ") " PACKAGE_VERSION "\n");
-+}
-+
-+static void print_usage(void)
-+{
-+      print_version();
-+      printf(
-+"Usage: pb-cui [-h, --help] [-l, --log log-file] [-V, --version]\n");
-+}
-+
-+/**
-+ * enum opt_value - Tri-state options variables.
-+ */
-+
-+enum opt_value {opt_undef = 0, opt_yes, opt_no};
-+
-+/**
-+ * struct opts - Values from command line options.
-+ */
-+
-+struct opts {
-+      enum opt_value show_help;
-+      const char *log_file;
-+      enum opt_value show_version;
-+};
-+
-+/**
-+ * opts_parse - Parse the command line options.
-+ */
-+
-+static int opts_parse(struct opts *opts, int argc, char *argv[])
-+{
-+      static const struct option long_options[] = {
-+              {"help",    no_argument,       NULL, 'h'},
-+              {"log",     required_argument, NULL, 'l'},
-+              {"version", no_argument,       NULL, 'V'},
-+              { NULL,     0,                 NULL, 0},
-+      };
-+      static const char short_options[] = "hl:V";
-+      static const struct opts default_values = {
-+              .log_file = "pb-cui.log",
-+      };
-+
-+      *opts = default_values;
-+
-+      while (1) {
-+              int c = getopt_long(argc, argv, short_options, long_options,
-+                      NULL);
-+
-+              if (c == EOF)
-+                      break;
-+
-+              switch (c) {
-+              case 'h':
-+                      opts->show_help = opt_yes;
-+                      break;
-+              case 'l':
-+                      opts->log_file = optarg;
-+                      break;
-+              case 'V':
-+                      opts->show_version = opt_yes;
-+                      break;
-+              default:
-+                      opts->show_help = opt_yes;
-+                      return -1;
-+              }
-+      }
-+
-+      return 0;
-+}
-+
-+/**
-+ * struct pb_cui - Main cui program instance.
-+ * @mm: Main menu.
-+ * @svm: Set video mode menu.
-+ */
-+
-+struct pb_cui {
-+      struct pmenu *mm;
-+      struct cui *cui;
-+};
-+
-+static struct pb_cui *pb_from_cui(struct cui *cui)
-+{
-+      struct pb_cui *pb;
-+
-+      assert(cui->c_sig == pb_cui_sig);
-+      pb = cui->platform_info;
-+      assert(pb->cui->c_sig == pb_cui_sig);
-+      return pb;
-+}
-+
-+/**
-+ * pb_kexec_cb - The kexec callback.
-+ */
-+
-+static int pb_kexec_cb(struct cui *cui, struct cui_opt_data *cod)
-+{
-+      struct pb_cui *pb = pb_from_cui(cui);
-+
-+      pb_log("%s: %s\n", __func__, cod->name);
-+
-+      assert(pb->cui->current == &pb->cui->main->scr);
-+
-+      return pb_run_kexec(cod->kd);
-+}
-+
-+/**
-+ * pb_mm_init - Setup the main menu instance.
-+ */
-+
-+static struct pmenu *pb_mm_init(struct pb_cui *pb_cui)
-+{
-+      int result;
-+      struct pmenu *m;
-+      struct pmenu_item *i;
-+
-+      m = pmenu_init(pb_cui->cui, 1, cui_on_exit);
-+
-+      if (!m) {
-+              pb_log("%s: failed\n", __func__);
-+              return NULL;
-+      }
-+
-+      m->on_open = cui_on_open;
-+
-+      m->scr.frame.title = talloc_strdup(m, "Petitboot");
-+      m->scr.frame.help = talloc_strdup(m,
-+              "ESC=exit, Enter=accept, e=edit, o=open");
-+      m->scr.frame.status = talloc_strdup(m, "Welcome to Petitboot");
-+
-+      i = pmenu_item_init(m, 0, "Exit to Shell");
-+      i->on_execute = pmenu_exit_cb;
-+
-+      result = pmenu_setup(m);
-+
-+      if (result) {
-+              pb_log("%s:%d: pmenu_setup failed: %s\n", __func__, __LINE__,
-+                      strerror(errno));
-+              goto fail_setup;
-+      }
-+
-+      menu_opts_off(m->ncm, O_SHOWDESC);
-+      set_menu_mark(m->ncm, " *");
-+      set_current_item(m->ncm, i->nci);
-+
-+      return m;
-+
-+fail_setup:
-+      talloc_free(m);
-+      return NULL;
-+}
-+
-+static struct pb_cui pb;
-+
-+static void sig_handler(int signum)
-+{
-+      DBGS("%d\n", signum);
-+
-+      switch (signum) {
-+      case SIGALRM:
-+              if (pb.cui)
-+                      ui_timer_sigalrm(&pb.cui->timer);
-+              break;
-+      case SIGWINCH:
-+              if (pb.cui)
-+                      cui_resize(pb.cui);
-+              break;
-+      default:
-+              assert(0 && "unknown sig");
-+              /* fall through */
-+      case SIGINT:
-+      case SIGHUP:
-+      case SIGTERM:
-+              if (pb.cui)
-+                      cui_abort(pb.cui);
-+              break;
-+      }
-+}
-+
-+/**
-+ * main - cui bootloader main routine.
-+ */
-+
-+int main(int argc, char *argv[])
-+{
-+      static struct sigaction sa;
-+      static struct opts opts;
-+      int result;
-+      int cui_result;
-+      FILE *log;
-+
-+      result = opts_parse(&opts, argc, argv);
-+
-+      if (result) {
-+              print_usage();
-+              return EXIT_FAILURE;
-+      }
-+
-+      if (opts.show_help == opt_yes) {
-+              print_usage();
-+              return EXIT_SUCCESS;
-+      }
-+
-+      if (opts.show_version == opt_yes) {
-+              print_version();
-+              return EXIT_SUCCESS;
-+      }
-+
-+      log = fopen(opts.log_file, "a");
-+      assert(log);
-+      pb_log_set_stream(log);
-+
-+#if defined(DEBUG)
-+      pb_log_always_flush(1);
-+#endif
-+
-+      pb_log("--- pb-cui ---\n");
-+
-+      sa.sa_handler = sig_handler;
-+      result = sigaction(SIGALRM, &sa, NULL);
-+      result += sigaction(SIGHUP, &sa, NULL);
-+      result += sigaction(SIGINT, &sa, NULL);
-+      result += sigaction(SIGTERM, &sa, NULL);
-+      result += sigaction(SIGWINCH, &sa, NULL);
-+
-+      if (result) {
-+              pb_log("%s sigaction failed.\n", __func__);
-+              return EXIT_FAILURE;
-+      }
-+
-+      pb.cui = cui_init(&pb, pb_kexec_cb, NULL);
-+
-+      if (!pb.cui)
-+              return EXIT_FAILURE;
-+
-+      pb.mm = pb_mm_init(&pb);
-+      ui_timer_disable(&pb.cui->timer);
-+
-+      cui_result = cui_run(pb.cui, pb.mm, 0);
-+
-+      pmenu_delete(pb.mm);
-+
-+      talloc_free(pb.cui);
-+
-+      pb_log("--- end ---\n");
-+
-+      return cui_result ? EXIT_FAILURE : EXIT_SUCCESS;
-+}
-diff --git a/ui/ncurses/ps3-cui.c b/ui/ncurses/ps3-cui.c
-index f166c88..d9a66fa 100644
---- a/ui/ncurses/ps3-cui.c
-+++ b/ui/ncurses/ps3-cui.c
-@@ -21,9 +21,7 @@
- /*
-  * TODO
-  * removable media event
-- * resize after video mode change
-  * ncurses mouse support
-- * timeout
-  */
- #if defined(HAVE_CONFIG_H)
-@@ -36,6 +34,7 @@
- #include <signal.h>
- #include <stdlib.h>
- #include <string.h>
-+#include <sys/time.h>
- #include "log/log.h"
- #include "talloc/talloc.h"
-@@ -53,7 +52,8 @@ static void print_usage(void)
- {
-       print_version();
-       printf(
--"Usage: pb-cui [-h, --help] [-l, --log log-file] [-V, --version]\n");
-+"Usage: pb-cui [-h, --help] [-l, --log log-file] [-r, --reset-defaults]\n"
-+"              [-t, --timeout] [-V, --version]\n");
- }
- /**
-@@ -69,6 +69,8 @@ enum opt_value {opt_undef = 0, opt_yes, opt_no};
- struct opts {
-       enum opt_value show_help;
-       const char *log_file;
-+      enum opt_value reset_defaults;
-+      enum opt_value use_timeout;
-       enum opt_value show_version;
- };
-@@ -79,12 +81,14 @@ struct opts {
- static int opts_parse(struct opts *opts, int argc, char *argv[])
- {
-       static const struct option long_options[] = {
--              {"help",    no_argument,       NULL, 'h'},
--              {"log",     required_argument, NULL, 'l'},
--              {"version", no_argument,       NULL, 'V'},
--              { NULL,     0,                 NULL, 0},
-+              {"help",           no_argument,       NULL, 'h'},
-+              {"log",            required_argument, NULL, 'l'},
-+              {"reset-defaults", no_argument,       NULL, 'r'},
-+              {"timeout",        no_argument,       NULL, 't'},
-+              {"version",        no_argument,       NULL, 'V'},
-+              { NULL, 0, NULL, 0},
-       };
--      static const char short_options[] = "hl:V";
-+      static const char short_options[] = "hl:trV";
-       static const struct opts default_values = {
-               .log_file = "pb-cui.log",
-       };
-@@ -105,6 +109,12 @@ static int opts_parse(struct opts *opts, int argc, char *argv[])
-               case 'l':
-                       opts->log_file = optarg;
-                       break;
-+              case 't':
-+                      opts->use_timeout = opt_yes;
-+                      break;
-+              case 'r':
-+                      opts->reset_defaults = opt_yes;
-+                      break;
-               case 'V':
-                       opts->show_version = opt_yes;
-                       break;
-@@ -114,7 +124,7 @@ static int opts_parse(struct opts *opts, int argc, char *argv[])
-               }
-       }
--      return 0;
-+      return optind != argc;
- }
- /**
-@@ -147,6 +157,83 @@ static struct ps3_cui *ps3_from_item(struct pmenu_item *item)
- }
- /**
-+ * ps3_sixaxis_map - Map a Linux joystick event to an ncurses key code.
-+ *
-+ */
-+
-+static int ps3_sixaxis_map(const struct js_event *e)
-+{
-+#if 0
-+      static const int axis_map[] = {
-+              0,              /*   0  Left thumb X    */
-+              0,              /*   1  Left thumb Y    */
-+              0,              /*   2  Right thumb X   */
-+              0,              /*   3  Right thumb Y   */
-+              0,              /*   4  nothing         */
-+              0,              /*   5  nothing         */
-+              0,              /*   6  nothing         */
-+              0,              /*   7  nothing         */
-+              0,              /*   8  Dpad Up         */
-+              0,              /*   9  Dpad Right      */
-+              0,              /*  10  Dpad Down       */
-+              0,              /*  11  Dpad Left       */
-+              0,              /*  12  L2              */
-+              0,              /*  13  R2              */
-+              0,              /*  14  L1              */
-+              0,              /*  15  R1              */
-+              0,              /*  16  Triangle        */
-+              0,              /*  17  Circle          */
-+              0,              /*  18  Cross           */
-+              0,              /*  19  Square          */
-+              0,              /*  20  nothing         */
-+              0,              /*  21  nothing         */
-+              0,              /*  22  nothing         */
-+              0,              /*  23  nothing         */
-+              0,              /*  24  nothing         */
-+              0,              /*  25  nothing         */
-+              0,              /*  26  nothing         */
-+              0,              /*  27  nothing         */
-+      };
-+#endif
-+      static const int button_map[] = {
-+              0,              /*   0  Select          */
-+              0,              /*   1  L3              */
-+              0,              /*   2  R3              */
-+              0,              /*   3  Start           */
-+              KEY_UP,         /*   4  Dpad Up         */
-+              0,              /*   5  Dpad Right      */
-+              KEY_DOWN,       /*   6  Dpad Down       */
-+              0,              /*   7  Dpad Left       */
-+              KEY_UP,         /*   8  L2              */
-+              KEY_DOWN,       /*   9  R2              */
-+              KEY_HOME,       /*  10  L1              */
-+              KEY_END,        /*  11  R1              */
-+              0,              /*  12  Triangle        */
-+              0,              /*  13  Circle          */
-+              13,             /*  14  Cross           */
-+              0,              /*  15  Square          */
-+              0,              /*  16  PS Button       */
-+              0,              /*  17  nothing         */
-+              0,              /*  18  nothing         */
-+      };
-+
-+      if (!e->value)
-+              return 0;
-+
-+      if (e->type == JS_EVENT_BUTTON
-+              && e->number < sizeof(button_map) / sizeof(button_map[0]))
-+              return button_map[e->number];
-+
-+#if 0
-+      if (e->type == JS_EVENT_AXIS
-+              && e->number < sizeof(axis_map) / sizeof(axis_map[0]))
-+              return axis_map[e->number];
-+#endif
-+
-+      return 0;
-+}
-+
-+/**
-  * ps3_set_mode - Set video mode helper.
-  *
-  * Runs ps3_set_video_mode().
-@@ -183,22 +270,51 @@ static int ps3_svm_cb(struct pmenu_item *item)
-  * ps3_kexec_cb - The kexec callback.
-  *
-  * Writes config data to PS3 flash then calls pb_run_kexec().
-+ * Adds a video mode arg to the kernel command line if needed.
-  */
- static int ps3_kexec_cb(struct cui *cui, struct cui_opt_data *cod)
- {
-       struct ps3_cui *ps3 = ps3_from_cui(cui);
-+      int result;
-+      int altered_args;
-+      char *orig_args;
--      pb_log("%s: %s:%s\n", __func__, cod->dev->name, cod->opt->name);
-+      pb_log("%s: %s\n", __func__, cod->name);
-       assert(ps3->cui->current == &ps3->cui->main->scr);
--      if (cui->default_item != cod->opt_hash || ps3->dirty_values) {
-+      /* Save values to flash if needed */
-+
-+      if ((cod->opt_hash && cod->opt_hash != cui->default_item)
-+              || ps3->dirty_values) {
-               ps3->values.default_item = cod->opt_hash;
-               ps3_flash_set_values(&ps3->values);
-       }
--      return pb_run_kexec(cod->kd);
-+      /* Add a default kernel video mode. */
-+
-+      if (!cod->kd->args) {
-+              altered_args = 1;
-+              orig_args = NULL;
-+              cod->kd->args = talloc_asprintf(NULL, "video=ps3fb:mode:%u",
-+                      (unsigned int)ps3->values.video_mode);
-+      } else if (!strstr(cod->kd->args, "video=")) {
-+              altered_args = 1;
-+              orig_args = cod->kd->args;
-+              cod->kd->args = talloc_asprintf(NULL, "%s video=ps3fb:mode:%u",
-+                      orig_args, (unsigned int)ps3->values.video_mode);
-+      } else
-+              altered_args = 0;
-+
-+      result = pb_run_kexec(cod->kd);
-+
-+      if (altered_args) {
-+              talloc_free(cod->kd->args);
-+              cod->kd->args = orig_args;
-+      }
-+
-+      return result;
- }
- /**
-@@ -283,6 +399,21 @@ static int ps3_hot_key(struct pmenu __attribute__((unused)) *menu,
- }
- /**
-+ * ps3_timer_update - Timer callback.
-+ */
-+
-+static void ps3_timer_update(struct ui_timer *timer, unsigned int timeout)
-+{
-+      struct ps3_cui *ps3 = ps3_from_cui(cui_from_timer(timer));
-+
-+      //FIXME: make scr:timer.
-+      // nc_scr_timer_update(&ps3.mm->scr, timeout);
-+
-+      nc_scr_status_printf(&ps3->mm->scr,
-+              "Welcome to Petitboot (timeout %u sec)", timeout);
-+}
-+
-+/**
-  * ps3_mm_init - Setup the main menu instance.
-  */
-@@ -291,8 +422,7 @@ static struct pmenu *ps3_mm_init(struct ps3_cui *ps3_cui)
-       int result;
-       struct pmenu *m;
-       struct pmenu_item *i;
--      static const char *const bgo[] =
--              {"/usr/sbin/ps3-boot-game-os-NOT", NULL};
-+      static const char *const bgo[] = {"/usr/sbin/ps3-boot-game-os", NULL};
-       m = pmenu_init(ps3_cui->cui, 3, cui_on_exit);
-@@ -302,22 +432,26 @@ static struct pmenu *ps3_mm_init(struct ps3_cui *ps3_cui)
-       }
-       m->hot_key = ps3_hot_key;
-+      m->on_open = cui_on_open;
-+
-+#if defined(DEBUG)
-+      m->scr.frame.title = talloc_strdup(m,
-+              "Petitboot PS3 (" PACKAGE_VERSION ")");
-+#else
-       m->scr.frame.title = talloc_strdup(m, "Petitboot PS3");
-+#endif
-       m->scr.frame.help = talloc_strdup(m,
--              "ESC=exit, Enter=accept, E,e=edit");
-+              "ESC=exit, Enter=accept, e=edit, o=open");
-       m->scr.frame.status = talloc_strdup(m, "Welcome to Petitboot");
--      i = pmenu_item_init(m, 0, "Boot GameOS",
--              "Reboot the PS3 into the GameOS");
-+      i = pmenu_item_init(m, 0, "Boot GameOS");
-       i->on_execute = cui_run_cmd;
-       i->data = (void *)bgo;
--      i = pmenu_item_init(m, 1, "Set Video Mode",
--              "Display a video mode selection menu");
-+      i = pmenu_item_init(m, 1, "Set Video Mode");
-       i->on_execute = ps3_mm_to_svm_cb;
--      i = pmenu_item_init(m, 2, "Exit to Shell",
--              "Exit petitboot and return to a shell prompt");
-+      i = pmenu_item_init(m, 2, "Exit to Shell");
-       i->on_execute = pmenu_exit_cb;
-       result = pmenu_setup(m);
-@@ -360,53 +494,51 @@ static struct pmenu *ps3_svm_init(struct ps3_cui *ps3_cui)
-       m->scr.frame.title = talloc_strdup(m, "Select PS3 Video Mode");
-       m->scr.frame.help = talloc_strdup(m, "ESC=exit, Enter=accept");
--      i = pmenu_item_init(m, 0, "auto detect",
--              "Auto detect the best HDMI video mode");
-+      i = pmenu_item_init(m, 0, "auto detect");
-       i->on_execute = ps3_svm_cb;
-       i->data = (void *)0;
--      i = pmenu_item_init(m, 1, "480i    (576 x 384)", NULL);
-+      i = pmenu_item_init(m, 1, "480i    (576 x 384)");
-       i->on_execute = ps3_svm_cb;
-       i->data = (void *)1;
--      i = pmenu_item_init(m, 2, "480p    (576 x 384)", NULL);
-+      i = pmenu_item_init(m, 2, "480p    (576 x 384)");
-       i->on_execute = ps3_svm_cb;
-       i->data = (void *)2;
--      i = pmenu_item_init(m, 3, "576i    (576 x 460)", NULL);
-+      i = pmenu_item_init(m, 3, "576i    (576 x 460)");
-       i->on_execute = ps3_svm_cb;
-       i->data = (void *)6;
--      i = pmenu_item_init(m, 4, "576p    (576 x 460)", NULL);
-+      i = pmenu_item_init(m, 4, "576p    (576 x 460)");
-       i->on_execute = ps3_svm_cb;
-       i->data = (void *)7;
--      i = pmenu_item_init(m, 5, "720p   (1124 x 644)", NULL);
-+      i = pmenu_item_init(m, 5, "720p   (1124 x 644)");
-       i->on_execute = ps3_svm_cb;
-       i->data = (void *)3;
--      i = pmenu_item_init(m, 6, "1080i  (1688 x 964)", NULL);
-+      i = pmenu_item_init(m, 6, "1080i  (1688 x 964)");
-       i->on_execute = ps3_svm_cb;
-       i->data = (void *)4;
--      i = pmenu_item_init(m, 7, "1080p  (1688 x 964)", NULL);
-+      i = pmenu_item_init(m, 7, "1080p  (1688 x 964)");
-       i->on_execute = ps3_svm_cb;
-       i->data = (void *)5;
--      i = pmenu_item_init(m, 8, "wxga   (1280 x 768)", NULL);
-+      i = pmenu_item_init(m, 8, "wxga   (1280 x 768)");
-       i->on_execute = ps3_svm_cb;
-       i->data = (void *)11;
--      i = pmenu_item_init(m, 9, "sxga   (1280 x 1024)", NULL);
-+      i = pmenu_item_init(m, 9, "sxga   (1280 x 1024)");
-       i->on_execute = ps3_svm_cb;
-       i->data = (void *)12;
--      i = pmenu_item_init(m, 10, "wuxga  (1920 x 1200)", NULL);
-+      i = pmenu_item_init(m, 10, "wuxga  (1920 x 1200)");
-       i->on_execute = ps3_svm_cb;
-       i->data = (void *)13;
--      i = pmenu_item_init(m, 11, "Return",
--              "Return to the main menu");
-+      i = pmenu_item_init(m, 11, "Return");
-       i->on_execute = ps3_svm_to_mm_cb;
-       result = pmenu_setup(m);
-@@ -434,6 +566,10 @@ static void sig_handler(int signum)
-       DBGS("%d\n", signum);
-       switch (signum) {
-+      case SIGALRM:
-+              if (ps3.cui)
-+                      ui_timer_sigalrm(&ps3.cui->timer);
-+              break;
-       case SIGWINCH:
-               if (ps3.cui)
-                       cui_resize(ps3.cui);
-@@ -491,8 +627,9 @@ int main(int argc, char *argv[])
-       pb_log("--- pb-cui ---\n");
-       sa.sa_handler = sig_handler;
--      result = sigaction(SIGINT, &sa, NULL);
-+      result = sigaction(SIGALRM, &sa, NULL);
-       result += sigaction(SIGHUP, &sa, NULL);
-+      result += sigaction(SIGINT, &sa, NULL);
-       result += sigaction(SIGTERM, &sa, NULL);
-       result += sigaction(SIGWINCH, &sa, NULL);
-@@ -501,7 +638,10 @@ int main(int argc, char *argv[])
-               return EXIT_FAILURE;
-       }
--      ps3.dirty_values = ps3_flash_get_values(&ps3.values);
-+      ps3.values = ps3_flash_defaults;
-+
-+      if (opts.reset_defaults != opt_yes)
-+              ps3.dirty_values = ps3_flash_get_values(&ps3.values);
-       result = ps3_get_video_mode(&mode);
-@@ -515,7 +655,7 @@ int main(int argc, char *argv[])
-       if (!result && (ps3.values.video_mode != (uint16_t)mode))
-               ps3_set_video_mode(ps3.values.video_mode);
--      ps3.cui = cui_init(&ps3, ps3_kexec_cb);
-+      ps3.cui = cui_init(&ps3, ps3_kexec_cb, ps3_sixaxis_map);
-       if (!ps3.cui)
-               return EXIT_FAILURE;
-@@ -523,6 +663,14 @@ int main(int argc, char *argv[])
-       ps3.mm = ps3_mm_init(&ps3);
-       ps3.svm = ps3_svm_init(&ps3);
-+      if (opts.use_timeout != opt_yes
-+              || ps3.values.timeout == ps3_timeout_forever)
-+              ui_timer_disable(&ps3.cui->timer);
-+      else {
-+              ps3.cui->timer.update_display = ps3_timer_update;
-+              ui_timer_init(&ps3.cui->timer, ps3.values.timeout);
-+      }
-+
-       cui_result = cui_run(ps3.cui, ps3.mm, ps3.values.default_item);
-       pmenu_delete(ps3.mm);
index e8589c5c1cc866c89792a5977bb6c9b09bfe454a..ed8f93d3a10d501ec3bc23c265b32a144719ae7d 100644 (file)
@@ -6,9 +6,9 @@ Update the PS3 twin GUI program to work with petitboot-multi-ui.
 
 Signed-off-by: Geoff Levand <geoffrey.levand@am.sony.com>
 ---
- rules.mk           |    2 +-
- ui/twin/ps3-twin.c |   49 +++++++++++++++++++++++++++++++++++++++++++++++++
- 2 files changed, 50 insertions(+), 1 deletion(-)
+ rules.mk           |   13 
+ ui/twin/ps3-twin.c | 1434 +++++++++++++++++++++++++++++++++++++++++++++++++++++
+ 2 files changed, 1443 insertions(+), 4 deletions(-)
 
 --- a/rules.mk
 +++ b/rules.mk
@@ -21,39 +21,1373 @@ Signed-off-by: Geoff Levand <geoffrey.levand@am.sony.com>
  
  # Makefiles
  makefiles = Makefile $(top_srcdir)/rules.mk
+@@ -89,11 +89,16 @@ $(pb_test): $(pb_test_objs)
+       $(LINK.o) -o $@ $^
+ # twin gui
+-pb_twin_objs = $(client_objs) $(twin_objs) ui/twin/ps3-twin.o
++pb_twin_objs-y$(ENABLE_PS3) += ui/twin/pb-twin.o
++pb_twin_objs-$(ENABLE_PS3) += ui/twin/ps3-twin.o ui/common/ps3.o
++pb_twin_ldflags-$(ENABLE_PS3) += -lps3-utils
++
++pb_twin_objs = $(client_objs) $(twin_objs)  $(pb_twin_objs-y)
+ $(pb_twin_objs): $(makefiles)
+-$(pb_twin): LDFLAGS+=$(twin_LDFLAGS) $(LIBTWIN)
+-$(pb_twin): CFLAGS+=$(twin_CFLAGS)
++$(pb_twin): LDFLAGS += $(pb_twin_ldflags-y) $(twin_LDFLAGS) $(LIBTWIN)
++$(pb_twin): CFLAGS += $(twin_CFLAGS) \
++      -DPB_ARTWORK_PATH='"$(pkgdatadir)/artwork/"'
+ $(pb_twin): $(pb_twin_objs)
+       $(LINK.o) -o $@ $^
 --- /dev/null
 +++ b/ui/twin/ps3-twin.c
-@@ -0,0 +1,49 @@
+@@ -0,0 +1,1434 @@
 +/*
 + * Petitboot twin bootloader for the PS3 game console
 + *
++ *  Copyright (C) 2009 Sony Computer Entertainment Inc.
++ *  Copyright 2009 Sony Corp.
++ *
++ *  This program is free software; you can redistribute it and/or modify
++ *  it under the terms of the GNU General Public License as published by
++ *  the Free Software Foundation; version 2 of the License.
++ *
++ *  This program is distributed in the hope that it will be useful,
++ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
++ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
++ *  GNU General Public License for more details.
++ *
++ *  You should have received a copy of the GNU General Public License
++ *  along with this program; if not, write to the Free Software
++ *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 + */
 +
-+#define _GNU_SOURCE
++#if defined(HAVE_CONFIG_H)
++#include "config.h"
++#endif
 +
++#define _GNU_SOURCE
 +#include <assert.h>
++#include <errno.h>
++#include <getopt.h>
 +#include <signal.h>
-+
++#include <stdlib.h>
++#include <string.h>
++#include <sys/time.h>
 +#include <libtwin/twin.h>
 +#include <libtwin/twin_fbdev.h>
-+static twin_fbdev_t *pboot_fbdev;
++#include <libtwin/twin_jpeg.h>
++#include <libtwin/twin_linux_mouse.h>
++#include <libtwin/twin_linux_js.h>
++#include <libtwin/twin_png.h>
++#if defined(TWIN_X11)
++# include <libtwin/twin_x11.h>
++#endif
++#include <linux/input.h>
 +
 +#include "log/log.h"
++#include "talloc/talloc.h"
++#include "waiter/waiter.h"
++#include "ui/common/discover-client.h"
++#include "ui/common/ps3.h"
++#include "ui/common/timer.h"
++
++//------------------------------------------------------------------------------
++// twin-ui.c/h
++
++#define DBG(fmt, args...) pb_log("DBG: " fmt, ## args)
++#define DBGS(fmt, args...) \
++      pb_log("DBG:%s:%d: " fmt, __func__, __LINE__, ## args)
++
++/* control to keyboard mappings for the sixaxis controller */
++uint8_t sixaxis_map[] = {
++      0,              /*   0  Select          */
++      0,              /*   1  L3              */
++      0,              /*   2  R3              */
++      0,              /*   3  Start           */
++      KEY_UP,         /*   4  Dpad Up         */
++      KEY_RIGHT,      /*   5  Dpad Right      */
++      KEY_DOWN,       /*   6  Dpad Down       */
++      KEY_LEFT,       /*   7  Dpad Left       */
++      0,              /*   8  L2              */
++      0,              /*   9  R2              */
++      0,              /*  10  L1              */
++      0,              /*  11  R1              */
++      0,              /*  12  Triangle        */
++      KEY_ENTER,      /*  13  Circle          */
++      0,              /*  14  Cross           */
++      KEY_DELETE,     /*  15  Square          */
++      0,              /*  16  PS Button       */
++      0,              /*  17  nothing      */
++      0,              /*  18  nothing      */
++};
++
++#define PBOOT_LEFT_PANE_SIZE          160
++#define PBOOT_LEFT_PANE_COLOR         0x80000000
++#define PBOOT_LEFT_LINE_COLOR         0xff000000
++
++#define PBOOT_LEFT_FOCUS_WIDTH                80
++#define PBOOT_LEFT_FOCUS_HEIGHT               80
++#define PBOOT_LEFT_FOCUS_XOFF         40
++#define PBOOT_LEFT_FOCUS_YOFF         40
++#define PBOOT_LEFT_FOCUS_XRAD         (6 * TWIN_FIXED_ONE)
++#define PBOOT_LEFT_FOCUS_YRAD         (6 * TWIN_FIXED_ONE)
++
++#define PBOOT_RIGHT_FOCUS_XOFF                20
++#define PBOOT_RIGHT_FOCUS_YOFF                60
++#define PBOOT_RIGHT_FOCUS_HEIGHT      80
++#define PBOOT_RIGHT_FOCUS_XRAD                (6 * TWIN_FIXED_ONE)
++#define PBOOT_RIGHT_FOCUS_YRAD                (6 * TWIN_FIXED_ONE)
++
++#define PBOOT_LEFT_ICON_WIDTH         64
++#define PBOOT_LEFT_ICON_HEIGHT                64
++#define PBOOT_LEFT_ICON_XOFF          50
++#define PBOOT_LEFT_ICON_YOFF          50
++#define PBOOT_LEFT_ICON_STRIDE                100
++
++#define PBOOT_RIGHT_OPTION_LMARGIN    30
++#define PBOOT_RIGHT_OPTION_RMARGIN    30
++#define PBOOT_RIGHT_OPTION_TMARGIN    70
++#define PBOOT_RIGHT_OPTION_HEIGHT     64
++#define PBOOT_RIGHT_OPTION_STRIDE     100
++#define PBOOT_RIGHT_TITLE_TEXT_SIZE   (30 * TWIN_FIXED_ONE)
++#define PBOOT_RIGHT_SUBTITLE_TEXT_SIZE        (18 * TWIN_FIXED_ONE)
++#define PBOOT_RIGHT_TITLE_XOFFSET     80
++#define PBOOT_RIGHT_TITLE_YOFFSET     30
++#define PBOOT_RIGHT_SUBTITLE_XOFFSET  100
++#define PBOOT_RIGHT_SUBTITLE_YOFFSET  50
++#define PBOOT_RIGHT_BADGE_XOFFSET     2
++#define PBOOT_RIGHT_BADGE_YOFFSET     0
++
++
++#define PBOOT_RIGHT_TITLE_COLOR               0xff000000
++#define PBOOT_RIGHT_SUBTITLE_COLOR    0xff400000
++
++#define PBOOT_FOCUS_COLOR             0x10404040
++
++#define PBOOT_STATUS_PANE_COLOR               0x60606060
++#define PBOOT_STATUS_PANE_HEIGHT      20
++#define PBOOT_STATUS_PANE_XYMARGIN    20
++#define PBOOT_STATUS_TEXT_MARGIN      10
++#define PBOOT_STATUS_TEXT_SIZE                (16 * TWIN_FIXED_ONE)
++#define PBOOT_STATUS_TEXT_COLOR               0xff000000
++
++#define PBOOT_MAX_OPTION 100
++#define PBOOT_MAX_DEV 10
++
++struct pbt_option
++{
++      char            *title;
++      char            *subtitle;
++      twin_pixmap_t   *badge;
++      twin_pixmap_t   *cache;
++      twin_rect_t     box;
++      void            *data;
++};
++
++struct pbt_device
++{
++      char                    *id;
++      twin_pixmap_t           *badge;
++      twin_rect_t             box;
++      int                     option_count;
++      struct pbt_option               options[PBOOT_MAX_OPTION];
++};
++
++enum pbt_sig {
++      pbt_scr_sig = 111,
++      pbt_menu_sig = 222,
++      pbt_pane_sig = 333,
++      pb_removed_sig = -555,
++};
++
++struct pbt_cursor {
++      twin_pixmap_t *pixmap;
++      int hx;
++      int hy;
++};
++
++struct pbt_scr {
++      enum pbt_sig sig;
++      struct pbt_cursor cursor;
++      twin_screen_t *tscreen;
++#if defined(USE_TWIN_X11)
++      twin_x11_t *x11;
++#else
++      twin_fbdev_t *fbdev;
++#endif
++};
++
++struct pbt_frame {
++      twin_label_t *title;
++      twin_label_t *help;
++      twin_label_t *status;
++};
++
++/**
++ * struct pbt_pane - A twin menu pane.
++ */
++
++struct pbt_pane {
++      enum pbt_sig sig;
++      twin_window_t *window;
++      twin_rect_t focus_box;
++      int focus_start;
++      int focus_target;
++      int focus_curindex;
++      int mouse_target;
++      int has_focus;
++};
++
++/**
++ * struct pbt_menu - A twin menu.
++ * @sig: Sanity check signature.
++ * @scr: The screen this menu is associated with.
++ * @dp: The device pane instance.
++ * @op: The option pane instance.
++ */
++
++struct pbt_menu {
++      enum pbt_sig sig;
++      struct pbt_scr *scr;
++      struct pbt_pane *dp;
++      struct pbt_pane *op;
++};
++
++//==============================================================================
++// helper
++//==============================================================================
++
++static struct pbt_scr *pbt_scr_from_tscreen(twin_screen_t *tscreen);
++
++static struct pbt_menu *pbt_menu_from_twindow(twin_window_t *twindow)
++{
++      struct pbt_menu *menu = twindow->client_data;
++
++      assert(menu);
++      assert(menu->sig == pbt_menu_sig);
++      return menu;
++}
++
++/*
++static struct pbt_menu *pbt_menu_from_arg(void *arg)
++{
++      struct pbt_menu *menu = arg;
++
++      assert(menu);
++      assert(menu->sig == pbt_menu_sig);
++      return menu;
++}
++*/
++
++static struct pbt_pane *pbt_pane_from_arg(void *arg)
++{
++      struct pbt_pane *pane = arg;
++
++      assert(pane);
++      assert(pane->sig == pbt_pane_sig);
++      return pane;
++}
++
++static twin_bool_t pbt_rect_intersect(twin_rect_t r1, twin_rect_t r2)
++{
++      // FIXME: move this to twin!!!
++      return !(r1.left > r2.right ||
++               r1.right < r2.left ||
++               r1.top > r2.bottom ||
++               r1.bottom < r2.top);
++}
++
++static twin_pixmap_t * pbt_load_background(twin_screen_t *tscreen)
++{
++      static const char *bgd = PB_ARTWORK_PATH "/background.jpg";
++      twin_pixmap_t *rawpix;
++      twin_pixmap_t *scaledpix;
++
++      rawpix = twin_jpeg_to_pixmap(bgd, TWIN_ARGB32);
++
++      if (!rawpix) {
++              pb_log("%s: loading image %s failed\n", __func__, bgd);
++              return twin_make_pattern();
++      }
++
++      if (tscreen->height == rawpix->height &&
++              tscreen->width == rawpix->width)
++              return rawpix;
++
++      /* Scale as needed. */
++
++      twin_fixed_t sx, sy;
++      twin_operand_t srcop;
++
++      scaledpix = twin_pixmap_create(TWIN_ARGB32,
++                              tscreen->width,
++                              tscreen->height);
++      if (!scaledpix) {
++              pb_log("%s: scale %s failed\n", __func__, bgd);
++              twin_pixmap_destroy(rawpix);
++              return twin_make_pattern();
++      }
++      sx = twin_fixed_div(twin_int_to_fixed(rawpix->width),
++                      twin_int_to_fixed(tscreen->width));
++      sy = twin_fixed_div(twin_int_to_fixed(rawpix->height),
++                      twin_int_to_fixed(tscreen->height));
++
++      twin_matrix_scale(&rawpix->transform, sx, sy);
++      srcop.source_kind = TWIN_PIXMAP;
++      srcop.u.pixmap = rawpix;
++      twin_composite(scaledpix, 0, 0, &srcop, 0, 0,
++              NULL, 0, 0, TWIN_SOURCE,
++              tscreen->width, tscreen->height);
++
++      twin_pixmap_destroy(rawpix);
++      return scaledpix;
++}
++
++//==============================================================================
++// option
++//==============================================================================
++
++static void pbt_option_execute(struct pbt_menu *menu)
++{
++#if 0
++      pboot_device_t *dev = pboot_devices[pboot_dev_sel];
++      pboot_option_t *opt = &dev->options[menu->op->focus_curindex];
++
++      pb_log("Selected device %s\n", opt->title);
++      pboot_message("booting %s...", opt->title);
++
++      /* Give user feedback, make sure errors and panics will be seen */
++      pboot_exec_option(opt->data);
++#endif
++}
++
++//==============================================================================
++// device
++//==============================================================================
++
++//==============================================================================
++// scr
++//==============================================================================
++
++static twin_bool_t pbt_scr_event(twin_screen_t *tscreen, twin_event_t *event)
++{
++      struct pbt_scr *scr = pbt_scr_from_tscreen(tscreen);
++
++      switch(event->kind) {
++      case TwinEventEnter:
++      case TwinEventMotion:
++      case TwinEventLeave:
++      case TwinEventButtonDown:
++      case TwinEventButtonUp:
++              if (scr->cursor.pixmap)
++                      twin_screen_set_cursor(tscreen, scr->cursor.pixmap,
++                              scr->cursor.hx, scr->cursor.hy);
++              break;
++      case TwinEventJoyButton:
++              /* map joystick events into key events */
++              if (event->u.js.control >= sizeof(sixaxis_map))
++                      break;
++
++              event->u.key.key = sixaxis_map[event->u.js.control];
++              if (event->u.js.value == 0) {
++                      event->kind = TwinEventKeyUp;
++                      break;
++              } else {
++                      event->kind = TwinEventKeyDown;
++              }
++              /* fall through.. */
++      case TwinEventKeyDown:
++              switch(event->u.key.key) {
++              case KEY_0:
++                      return TWIN_TRUE;
++              case KEY_BACKSPACE:
++              case KEY_DELETE:
++                      return TWIN_FALSE;
++              }
++      case TwinEventKeyUp:
++              twin_screen_set_cursor(tscreen, NULL, 0, 0);
++              break;
++      default:
++              break;
++      }
++      return TWIN_FALSE;
++}
++
++//==============================================================================
++// pane
++//==============================================================================
++
++static int pbt_pane_has_focus(const struct pbt_pane *pane)
++{
++      return pane->has_focus;
++}
++
++static twin_time_t pbt_pane_timeout(twin_time_t now, void *closure)
++{
++      const int accel[11] = { 7, 4, 2, 1, 1, 1, 1, 1, 2, 2, 3 };
++      struct pbt_pane *pane = pbt_pane_from_arg(closure);
++      int dir = 1, dist, pos;
++
++      dist = abs(pane->focus_target - pane->focus_start);
++      dir = dist > 5 ? 5 : dist;
++      pos = pane->focus_target - (int)pane->focus_box.top;
++      if (pos == 0) {
++              return -1;
++      }
++      if (pos < 0) {
++              dir = -dir;
++              pos = -pos;
++      }
++      twin_window_damage(pane->window,
++                         pane->focus_box.left,
++                         pane->focus_box.top,
++                         pane->focus_box.right,
++                         pane->focus_box.bottom);
++
++      pane->focus_box.top += dir;
++      pane->focus_box.bottom += dir;
++
++      twin_window_damage(pane->window,
++                         pane->focus_box.left,
++                         pane->focus_box.top,
++                         pane->focus_box.right,
++                         pane->focus_box.bottom);
++
++      twin_window_queue_paint(pane->window);
++
++      return accel[(pos * 10) / dist];
++}
++
++//==============================================================================
++// menu
++//==============================================================================
++
++/**
++ * pbt_menu_set_focus - Set the menu's pane of focus.
++ * @menu: The menu to operate on.
++ * @pane: The pane that will have the focus.
++ */
++
++static void pbt_menu_set_focus(struct pbt_menu *menu, struct pbt_pane *pane)
++{
++      assert(!pane->has_focus);
++
++      if (pane == menu->dp) {
++              menu->dp->has_focus = 1;
++              menu->op->has_focus = 0;
++      } else if (pane == menu->op) {
++              menu->dp->has_focus = 0;
++              menu->op->has_focus = 1;
++//            pbt_menu_set_option_focus(menu, 0);
++      } else
++              assert(0 && "bad logic");
++}
++
++static void pbt_menu_pane_select(struct pbt_menu *menu, struct pbt_pane *pane)
++{
++      if(pbt_pane_has_focus(pane))
++              return;
++
++      twin_screen_set_active(menu->scr->tscreen, pane->window->pixmap);
++
++      twin_window_damage(menu->dp->window,
++                         menu->dp->focus_box.left,
++                         menu->dp->focus_box.top,
++                         menu->dp->focus_box.right,
++                         menu->dp->focus_box.bottom);
++      twin_window_damage(menu->op->window,
++                         menu->op->focus_box.left,
++                         menu->op->focus_box.top,
++                         menu->op->focus_box.right,
++                         menu->op->focus_box.bottom);
++
++      twin_window_queue_paint(menu->dp->window);
++      twin_window_queue_paint(menu->op->window);
++
++      pbt_menu_set_focus(menu, pane);
++}
++
++
++static struct pbt_pane *pbt_device_pane_create(struct pbt_menu *menu);
++static struct pbt_pane *pbt_option_pane_create(struct pbt_menu *menu);
++
++static struct pbt_menu *pbt_menu_create(void *ctx, struct pbt_scr *scr)
++{
++      struct pbt_menu *menu = talloc_zero(ctx, struct pbt_menu);
++
++      if (!menu)
++              return NULL;
++
++      assert(scr && scr->sig == pbt_scr_sig);
++
++      menu->sig = pbt_menu_sig;
++      menu->scr = scr;
++
++      menu->dp = pbt_device_pane_create(menu);
++
++      if (!menu->dp)
++              goto fail_dp;
++
++      menu->op = pbt_option_pane_create(menu);
++
++      if (!menu->op)
++              goto fail_op;
++
++      return menu;
++
++fail_op:
++      //clean dp
++fail_dp:
++      talloc_free(menu);
++      return NULL;
++}
++
++//==============================================================================
++// device_pane
++//==============================================================================
++
++static void pbt_device_pane_draw(twin_window_t *window)
++{
++      struct pbt_pane *dp = pbt_menu_from_twindow(window)->dp;
++      twin_pixmap_t   *px = window->pixmap;
++      twin_path_t     *path;
++      twin_fixed_t    x, y, w, h;
++      int             i;
++
++      /* Fill background */
++      twin_fill(px, PBOOT_LEFT_PANE_COLOR, TWIN_SOURCE, 0, 0, px->width,
++              px->height);
++
++      /* Create a path for use later */
++      path = twin_path_create();
++      assert(path);
++
++      /* Draw right line if needed */
++      if (px->clip.right > (PBOOT_LEFT_PANE_SIZE - 4)) {
++              x = twin_int_to_fixed(PBOOT_LEFT_PANE_SIZE - 4);
++              y = twin_int_to_fixed(px->height);
++              twin_path_rectangle(path, x, 0, 0x40000, y);
++              twin_paint_path(px, PBOOT_LEFT_LINE_COLOR, path);
++              twin_path_empty(path);
++      }
++
++      /* Draw focus box */
++      if (dp->focus_curindex >= 0 &&
++          pbt_rect_intersect(dp->focus_box, px->clip)) {
++              x = twin_int_to_fixed(dp->focus_box.left + 2);
++              y = twin_int_to_fixed(dp->focus_box.top + 2);
++              w = twin_int_to_fixed(dp->focus_box.right -
++                                    dp->focus_box.left - 4);
++              h = twin_int_to_fixed(dp->focus_box.bottom -
++                                    dp->focus_box.top - 4);
++              twin_path_rounded_rectangle(path, x, y, w, h,
++                                          PBOOT_LEFT_FOCUS_XRAD,
++                                          PBOOT_LEFT_FOCUS_YRAD);
++              if (pbt_pane_has_focus(dp))
++                      twin_paint_path(px, PBOOT_FOCUS_COLOR, path);
++              else
++                      twin_paint_stroke(px, PBOOT_FOCUS_COLOR, path,
++                                        4 * TWIN_FIXED_ONE);
++      }
++
++#if 0
++      /* Draw icons */
++      for (i = 0; i < pboot_dev_count; i++) {
++              pboot_device_t  *dev = pboot_devices[i];
++              twin_operand_t  src;
++
++              if (!twin_rect_intersect(dev->box, px->clip))
++                      continue;
++
++              src.source_kind = TWIN_PIXMAP;
++              src.u.pixmap = dev->badge;
++
++              twin_composite(px, dev->box.left, dev->box.top,
++                             &src, 0, 0, NULL, 0, 0, TWIN_OVER,
++                             dev->box.right - dev->box.left,
++                             dev->box.bottom - dev->box.top);
++      }
++#endif
++
++      /* Destroy path */
++      twin_path_destroy(path);
++}
++
++
++static void pbt_device_pane_set_focus(struct pbt_menu *menu, int index)
++{
++#if 0
++      if (index >= pboot_dev_count)
++              return;
++#endif
++
++      menu->dp->focus_start = menu->dp->focus_box.top;
++
++      if (index < 0)
++              menu->dp->focus_target = 0 - PBOOT_LEFT_FOCUS_HEIGHT;
++      else
++              menu->dp->focus_target = PBOOT_LEFT_FOCUS_YOFF +
++                      PBOOT_LEFT_ICON_STRIDE * index;
++
++      menu->dp->focus_curindex = index;
++
++      twin_set_timeout(pbt_pane_timeout, 0, menu->dp);
++}
++
++static void pbt_device_pane_mousetrack(struct pbt_menu *menu, twin_coord_t x,
++      twin_coord_t y)
++{
++      int candidate = -1;
++      twin_coord_t icon_top;
++
++      if (x < PBOOT_LEFT_ICON_XOFF ||
++          x > (PBOOT_LEFT_ICON_XOFF + PBOOT_LEFT_ICON_WIDTH))
++              goto miss;
++
++      if (y < PBOOT_LEFT_ICON_YOFF)
++              goto miss;
++
++      candidate = (y - PBOOT_LEFT_ICON_YOFF) / PBOOT_LEFT_ICON_STRIDE;
++
++#if 0
++      if (candidate >= pboot_dev_count) {
++              candidate = -1;
++              goto miss;
++      }
++#endif
++      if (candidate == menu->dp->mouse_target)
++              return;
++
++      icon_top = PBOOT_LEFT_ICON_YOFF + candidate * PBOOT_LEFT_ICON_STRIDE;
++
++      if (y > (icon_top + PBOOT_LEFT_ICON_HEIGHT)) {
++              candidate = -1;
++              goto miss;
++      }
++
++      /* The mouse hit an icon that wasn't the same
++       * as the previous one, trigger a focus change.
++       */
++
++      pbt_device_pane_set_focus(menu, candidate);
++
++ miss:
++      menu->dp->mouse_target = candidate;
++}
++
++static twin_bool_t pbt_device_pane_event(twin_window_t *window,
++      twin_event_t *event)
++{
++      struct pbt_menu *menu = pbt_menu_from_twindow(window);
++
++      /* filter out all mouse events */
++      switch(event->kind) {
++      case TwinEventEnter:
++      case TwinEventMotion:
++      case TwinEventLeave:
++              pbt_menu_pane_select(menu, menu->dp);
++              pbt_device_pane_mousetrack(menu, event->u.pointer.x,
++                      event->u.pointer.y);
++              return TWIN_TRUE;
++      case TwinEventButtonDown:
++      case TwinEventButtonUp:
++              return TWIN_TRUE;
++      case TwinEventKeyDown:
++              switch(event->u.key.key) {
++              case KEY_UP:
++                      if (menu->dp->focus_curindex > 0)
++                              pbt_device_pane_set_focus(menu,
++                                      menu->dp->focus_curindex - 1);
++                      return TWIN_TRUE;
++              case KEY_DOWN:
++                      pbt_device_pane_set_focus(menu, menu->dp->focus_curindex + 1);
++                      return TWIN_TRUE;
++              case KEY_RIGHT:
++                      pbt_menu_pane_select(menu, menu->op);
++                      return TWIN_TRUE;
++              default:
++                      break;
++              }
++              break;
++      default:
++              break;
++      }
++      return TWIN_FALSE;
++}
++
++static struct pbt_pane *pbt_device_pane_create(struct pbt_menu *menu)
++{
++      struct pbt_pane *pane = talloc_zero(menu, struct pbt_pane);
++
++      if (!pane)
++              return NULL;
++
++      pane->sig = pbt_pane_sig;
++      pane->window = twin_window_create(menu->scr->tscreen, TWIN_ARGB32,
++              TwinWindowPlain, 0, 0, PBOOT_LEFT_PANE_SIZE,
++              menu->scr->tscreen->height);
++
++      if (!pane->window)
++              goto fail_window;
++
++      pane->window->draw = pbt_device_pane_draw;
++      pane->window->event = pbt_device_pane_event;
++      pane->window->client_data = menu;
++
++      pane->focus_curindex = -1;
++      pane->focus_box.left = PBOOT_LEFT_FOCUS_XOFF;
++      pane->focus_box.top = -2 * PBOOT_LEFT_FOCUS_HEIGHT;
++      pane->focus_box.right = pane->focus_box.left + PBOOT_LEFT_FOCUS_WIDTH;
++      pane->focus_box.bottom = pane->focus_box.top + PBOOT_LEFT_FOCUS_HEIGHT;
++
++      pane->mouse_target = -1;
++
++      twin_window_show(pane->window);
++      twin_window_queue_paint(pane->window);
++
++      return pane;
++
++fail_window:
++      talloc_free(pane);
++      return NULL;
++}
++
++//==============================================================================
++// option_pane
++//==============================================================================
++
++static void pbt_option_pane_set_focus(struct pbt_menu *menu, int index)
++{
++#if 0
++      pboot_device_t  *dev;
++
++      if (pboot_dev_sel < 0 || pboot_dev_sel >= pboot_dev_count)
++              return;
++      dev = pboot_devices[pboot_dev_sel];
++      if (index < 0 || index >= dev->option_count)
++              return;
++#endif
++
++      menu->op->focus_start = menu->op->focus_box.top;
++      menu->op->focus_target = PBOOT_RIGHT_FOCUS_YOFF +
++              PBOOT_RIGHT_OPTION_STRIDE * index;
++      menu->op->focus_curindex = index;
++
++      twin_set_timeout(pbt_pane_timeout, 0, menu->op);
++}
++
++static void pbt_option_pane_draw(twin_window_t *window)
++{
++      struct pbt_pane *op = pbt_menu_from_twindow(window)->op;
++      twin_pixmap_t   *px = window->pixmap;
++//    pboot_device_t  *dev;
++      twin_path_t     *path;
++      twin_fixed_t    x, y, w, h;
++      int             i;
++
++      /* Fill background */
++      twin_fill(px, 0x00000000, TWIN_SOURCE, 0, 0, px->width, px->height);
++
++      /* Nothing to draw, return */
++//    if (pboot_dev_sel < 0)
++//            return;
++
++      /* Create a path for use later */
++      path = twin_path_create();
++      assert(path);
++
++      /* Draw focus box */
++      if (op->focus_curindex >= 0 &&
++          pbt_rect_intersect(op->focus_box, px->clip)) {
++              x = twin_int_to_fixed(op->focus_box.left + 2);
++              y = twin_int_to_fixed(op->focus_box.top + 2);
++              w = twin_int_to_fixed(op->focus_box.right -
++                                    op->focus_box.left - 4);
++              h = twin_int_to_fixed(op->focus_box.bottom -
++                                    op->focus_box.top - 4);
++              twin_path_rounded_rectangle(path, x, y, w, h,
++                                          PBOOT_RIGHT_FOCUS_XRAD,
++                                          PBOOT_RIGHT_FOCUS_YRAD);
++              if (pbt_pane_has_focus(op))
++                      twin_paint_path(px, PBOOT_FOCUS_COLOR, path);
++              else
++                      twin_paint_stroke(px, PBOOT_FOCUS_COLOR, path,
++                                        4 * TWIN_FIXED_ONE);
++      }
++
++      /* Get device and iterate through options */
++/*
++      dev = pboot_devices[pboot_dev_sel];
++      for (i = 0; i < dev->option_count; i++) {
++              pboot_option_t  *opt = &dev->options[i];
++              twin_operand_t  src;
++
++              if (opt->title == NULL)
++                      continue;
++              if (!pbt_rect_intersect(opt->box, px->clip))
++                      continue;
++              if (opt->cache == NULL)
++                      pboot_draw_option_cache(dev, opt, i);
++
++              src.source_kind = TWIN_PIXMAP;
++              src.u.pixmap = opt->cache;
++
++              twin_composite(px, opt->box.left, opt->box.top,
++                             &src, 0, 0, NULL, 0, 0, TWIN_OVER,
++                             opt->box.right - opt->box.left,
++                             opt->box.bottom - opt->box.top);
++      }
++*/
++      /* Destroy path */
++      twin_path_destroy(path);
++}
++
++static void pbt_option_pane_mousetrack(struct pbt_menu *menu, twin_coord_t x,
++      twin_coord_t y)
++{
++      int candidate = -1;
++
++      if (y < PBOOT_RIGHT_OPTION_TMARGIN)
++              goto miss;
++
++#if 0
++      pboot_device_t  *dev;
++      pboot_option_t  *opt;
++
++      if (pboot_dev_sel < 0 || pboot_dev_sel >= pboot_dev_count)
++              return;
++
++      dev = pboot_devices[pboot_dev_sel];
++
++      candidate = (y - PBOOT_RIGHT_OPTION_TMARGIN) /
++              PBOOT_RIGHT_OPTION_STRIDE;
++
++      if (candidate >= dev->option_count) {
++              candidate = -1;
++              goto miss;
++      }
++
++      if (candidate == op->mouse_target)
++              return;
++
++      opt = &dev->options[candidate];
++
++      if (x < opt->box.left || x > opt->box.right ||
++          y < opt->box.top || y > opt->box.bottom) {
++              candidate = -1;
++              goto miss;
++      }
++#endif
++
++      pbt_option_pane_set_focus(menu, candidate);
++
++miss:
++      menu->op->mouse_target = candidate;
++}
++
++static twin_bool_t pbt_option_pane_event(twin_window_t *window,
++      twin_event_t *event)
++{
++      struct pbt_menu *menu = pbt_menu_from_twindow(window);
++
++      /* filter out all mouse events */
++      switch(event->kind) {
++      case TwinEventEnter:
++      case TwinEventMotion:
++      case TwinEventLeave:
++              pbt_menu_pane_select(menu, menu->op);
++              pbt_option_pane_mousetrack(menu, event->u.pointer.x,
++                      event->u.pointer.y);
++              return TWIN_TRUE;
++      case TwinEventButtonDown:
++              pbt_menu_pane_select(menu, menu->op);
++              pbt_option_pane_mousetrack(menu, event->u.pointer.x,
++                      event->u.pointer.y);
++              pbt_option_execute(menu);
++              return TWIN_TRUE;
++      case TwinEventButtonUp:
++              return TWIN_TRUE;
++      case TwinEventKeyDown:
++              switch(event->u.key.key) {
++              case KEY_UP:
++                      //pbt_menu_set_option_focus(menu, menu->op->focus_curindex - 1);
++                      return TWIN_TRUE;
++              case KEY_DOWN:
++                      //pbt_menu_set_option_focus(menu, menu->op->focus_curindex + 1);
++                      return TWIN_TRUE;
++              case KEY_LEFT:
++                      pbt_menu_pane_select(menu, menu->dp);
++                      return TWIN_TRUE;
++              case KEY_ENTER:
++                      pbt_option_execute(menu);
++              default:
++                      break;
++              }
++              break;
++      default:
++              break;
++      }
++      return TWIN_FALSE;
++}
++
++static struct pbt_pane *pbt_option_pane_create(struct pbt_menu *menu)
++{
++      struct pbt_pane *pane = talloc_zero(menu, struct pbt_pane);
++
++      if (!pane)
++              return NULL;
++
++      pane->sig = pbt_pane_sig;
++      pane->window = twin_window_create(menu->scr->tscreen, TWIN_ARGB32,
++              TwinWindowPlain, PBOOT_LEFT_PANE_SIZE, 0,
++              menu->scr->tscreen->width - PBOOT_LEFT_PANE_SIZE,
++              menu->scr->tscreen->height);
++
++      if (!pane->window)
++              goto fail_window;
++
++      pane->window->draw = pbt_option_pane_draw;
++      pane->window->event = pbt_option_pane_event;
++      pane->window->client_data = menu;
++
++      pane->focus_curindex = -1;
++      pane->focus_box.left = PBOOT_RIGHT_FOCUS_XOFF;
++      pane->focus_box.top = -2 * PBOOT_RIGHT_FOCUS_HEIGHT;
++      pane->focus_box.right = pane->window->pixmap->width
++              - 2 * PBOOT_RIGHT_FOCUS_XOFF;
++      pane->focus_box.bottom = pane->focus_box.top + PBOOT_RIGHT_FOCUS_HEIGHT;
++
++      pane->mouse_target = -1;
++
++      twin_window_show(pane->window);
++      twin_window_queue_paint(pane->window);
++
++      return pane;
++
++fail_window:
++      talloc_free(pane);
++      return NULL;
++}
++
++//==============================================================================
++// junk
++//==============================================================================
++
++#if 0
++
++static pboot_device_t *pboot_devices[PBOOT_MAX_DEV];
++static int            pboot_dev_count;
++static int            pboot_dev_sel = -1;
++
++
++
++
++static int pboot_vmode_change = -1;
++
++static void pboot_message(const char *fmt, ...)
++{
++      va_list ap;
++      char *msg;
++
++      va_start(ap, fmt);
++      vasprintf(&msg, fmt, ap);
++      va_end(ap);
++
++      pb_log(msg);
++}
++
++static void pboot_draw_option_cache(pboot_device_t *dev, pboot_option_t *opt,
++                                  int index)
++{
++      twin_pixmap_t   *px;
++      twin_path_t     *path;
++      twin_fixed_t    tx, ty;
++
++      /* Create pixmap */
++      px = twin_pixmap_create(TWIN_ARGB32, opt->box.right - opt->box.left,
++                               opt->box.bottom - opt->box.top);
++      assert(px);
++      opt->cache = px;
++
++      /* Fill background */
++      twin_fill(px, 0x00000000, TWIN_SOURCE, 0, 0, px->width, px->height);
++
++      /* Allocate a path for drawing */
++      path = twin_path_create();
++      assert(path);
++
++#if 0
++      /* TEST - Bounding rectangle */
++      twin_path_rectangle(path, 0, 0,
++                          twin_int_to_fixed(px->width),
++                          twin_int_to_fixed(px->height));
++      twin_paint_path(px, PBOOT_RIGHT_TITLE_COLOR, path);
++      twin_path_empty(path);
++      twin_fill(px, 0x00000000, TWIN_SOURCE, 2, 2,
++                px->width - 3, px->height - 3);
++#endif
++
++      /* Draw texts */
++      twin_path_set_font_size(path, PBOOT_RIGHT_TITLE_TEXT_SIZE);
++      twin_path_set_font_style(path, TWIN_TEXT_UNHINTED);
++      tx = twin_int_to_fixed(PBOOT_RIGHT_TITLE_XOFFSET);
++      ty = twin_int_to_fixed(PBOOT_RIGHT_TITLE_YOFFSET);
++      twin_path_move (path, tx, ty);
++      twin_path_utf8 (path, opt->title);
++      twin_paint_path (px, PBOOT_RIGHT_TITLE_COLOR, path);
++      twin_path_empty (path);
++
++      if (opt->subtitle) {
++              twin_path_set_font_size(path, PBOOT_RIGHT_SUBTITLE_TEXT_SIZE);
++              twin_path_set_font_style(path, TWIN_TEXT_UNHINTED);
++              tx = twin_int_to_fixed(PBOOT_RIGHT_SUBTITLE_XOFFSET);
++              ty = twin_int_to_fixed(PBOOT_RIGHT_SUBTITLE_YOFFSET);
++              twin_path_move (path, tx, ty);
++              twin_path_utf8 (path, opt->subtitle);
++              twin_paint_path (px, PBOOT_RIGHT_SUBTITLE_COLOR, path);
++              twin_path_empty (path);
++      }
++
++      if (opt->badge) {
++              twin_operand_t  src;
++
++              src.source_kind = TWIN_PIXMAP;
++              src.u.pixmap = opt->badge;
++
++              twin_composite(px, PBOOT_RIGHT_BADGE_XOFFSET,
++                             PBOOT_RIGHT_BADGE_YOFFSET,
++                             &src, 0, 0, NULL, 0, 0, TWIN_OVER,
++                             opt->badge->width, opt->badge->height);
++      }
++
++
++      /* Destroy path */
++      twin_path_destroy(path);
++}
++
++
++
++static int pboot_add_option(int devindex, const char *title,
++                   const char *subtitle, twin_pixmap_t *badge, void *data)
++{
++      pboot_device_t  *dev;
++      pboot_option_t  *opt;
++      twin_coord_t    width;
++      int             index;
++      struct pbt_menu *menu = NULL;
++
++      if (devindex < 0 || devindex >= pboot_dev_count)
++              return -1;
++      dev = pboot_devices[devindex];
++
++      if (dev->option_count >= PBOOT_MAX_OPTION)
++              return -1;
++      index = dev->option_count++;
++      opt = &dev->options[index];
++
++      opt->title = malloc(strlen(title) + 1);
++      strcpy(opt->title, title);
++
++      if (subtitle) {
++              opt->subtitle = malloc(strlen(subtitle) + 1);
++              strcpy(opt->subtitle, subtitle);
++      } else
++              opt->subtitle = NULL;
++
++      opt->badge = badge;
++      opt->cache = NULL;
++
++      width = menu->op->window->pixmap->width -
++              (PBOOT_RIGHT_OPTION_LMARGIN + PBOOT_RIGHT_OPTION_RMARGIN);
++
++      opt->box.left = PBOOT_RIGHT_OPTION_LMARGIN;
++      opt->box.right = opt->box.left + width;
++      opt->box.top = PBOOT_RIGHT_OPTION_TMARGIN +
++              index * PBOOT_RIGHT_OPTION_STRIDE;
++      opt->box.bottom = opt->box.top + PBOOT_RIGHT_OPTION_HEIGHT;
++
++      opt->data = data;
++      return index;
++}
++
++static void pboot_set_device_select(struct pbt_menu *menu, int sel, int force)
++{
++      pb_log("%s: %d -> %d\n", __FUNCTION__, pboot_dev_sel, sel);
++      if (!force && sel == pboot_dev_sel)
++              return;
++      if (sel >= pboot_dev_count)
++              return;
++      pboot_dev_sel = sel;
++      if (force) {
++              menu->dp->focus_curindex = sel;
++              if (sel < 0)
++                      menu->dp->focus_target = 0 - PBOOT_LEFT_FOCUS_HEIGHT;
++              else
++                      menu->dp->focus_target = PBOOT_LEFT_FOCUS_YOFF +
++                              PBOOT_LEFT_ICON_STRIDE * sel;
++              menu->op->focus_box.bottom = menu->dp->focus_target;
++              menu->op->focus_box.bottom = menu->op->focus_box.top +
++                      PBOOT_RIGHT_FOCUS_HEIGHT;
++              twin_window_damage(menu->dp->window,
++                                 0, 0,
++                                 menu->dp->window->pixmap->width,
++                                 menu->dp->window->pixmap->height);
++              twin_window_queue_paint(menu->dp->window);
++      }
++      menu->op->focus_curindex = -1;
++      menu->op->mouse_target = -1;
++      menu->op->focus_box.top = -2*PBOOT_RIGHT_FOCUS_HEIGHT;
++      menu->op->focus_box.bottom = menu->op->focus_box.top +
++              PBOOT_RIGHT_FOCUS_HEIGHT;
++      twin_window_damage(menu->op->window, 0, 0,
++                         menu->op->window->pixmap->width,
++                         menu->op->window->pixmap->height);
++      twin_window_queue_paint(menu->op->window);
++}
++
++static void pboot_quit(void)
++{
++      kill(0, SIGINT);
++}
++
++
++static int pboot_add_device(const char *dev_id, twin_pixmap_t *pixmap)
++{
++      int             index;
++      pboot_device_t  *dev;
++
++      struct pbt_menu *menu = NULL;
++
++      if (pboot_dev_count >= PBOOT_MAX_DEV)
++              return -1;
++
++      index = pboot_dev_count++;
++
++      dev = malloc(sizeof(*dev));
++      memset(dev, 0, sizeof(*dev));
++      dev->id = malloc(strlen(dev_id) + 1);
++      strcpy(dev->id, dev_id);
++      dev->badge = pixmap;
++      dev->box.left = PBOOT_LEFT_ICON_XOFF;
++      dev->box.right = dev->box.left + PBOOT_LEFT_ICON_WIDTH;
++      dev->box.top = PBOOT_LEFT_ICON_YOFF +
++              PBOOT_LEFT_ICON_STRIDE * index;
++      dev->box.bottom = dev->box.top + PBOOT_LEFT_ICON_HEIGHT;
++
++      pboot_devices[index] = dev;
++
++      twin_window_damage(menu->dp->window,
++                         dev->box.left, dev->box.top,
++                         dev->box.right, dev->box.bottom);
++      twin_window_queue_paint(menu->dp->window);
++
++      return index;
++}
++
++static int pboot_remove_device(const char *dev_id)
++{
++      pboot_device_t  *dev = NULL;
++      int             i, newsel = pboot_dev_sel;
++
++      struct pbt_menu *menu = NULL;
++
++      /* find the matching device */
++      for (i = 0; i < pboot_dev_count; i++) {
++              if (!strcmp(pboot_devices[i]->id, dev_id)) {
++                      dev = pboot_devices[i];
++                      break;
++              }
++      }
++
++      if (!dev)
++              return TWIN_FALSE;
++
++      memmove(pboot_devices + i, pboot_devices + i + 1,
++                      sizeof(*pboot_devices) * (pboot_dev_count + i - 1));
++      pboot_devices[--pboot_dev_count] = NULL;
++
++      /* select the newly-focussed device */
++      if (pboot_dev_sel > i)
++              newsel = pboot_dev_sel - 1;
++      else if (pboot_dev_sel == i && i >= pboot_dev_count)
++                      newsel = pboot_dev_count - 1;
++      pboot_set_device_select(menu, newsel, 1);
++
++      /* todo: free device & options */
++
++      return TWIN_TRUE;
++}
++#endif
++
++//------------------------------------------------------------------------------
++
++static void print_version(void)
++{
++      printf("pb-twin (" PACKAGE_NAME ") " PACKAGE_VERSION "\n");
++}
++
++static void print_usage(void)
++{
++      print_version();
++      printf(
++"Usage: pb-twin [-h, --help] [-l, --log log-file] [-r, --reset-defaults]\n"
++"               [-t, --timeout] [-V, --version]\n");
++}
++
++/**
++ * enum opt_value - Tri-state options variables.
++ */
++
++enum opt_value {opt_undef = 0, opt_yes, opt_no};
++
++/**
++ * struct opts - Values from command line options.
++ */
++
++struct opts {
++      enum opt_value show_help;
++      const char *log_file;
++      enum opt_value reset_defaults;
++      enum opt_value use_timeout;
++      enum opt_value show_version;
++};
++
++/**
++ * opts_parse - Parse the command line options.
++ */
++
++static int opts_parse(struct opts *opts, int argc, char *argv[])
++{
++      static const struct option long_options[] = {
++              {"help",           no_argument,       NULL, 'h'},
++              {"log",            required_argument, NULL, 'l'},
++              {"reset-defaults", no_argument,       NULL, 'r'},
++              {"timeout",        no_argument,       NULL, 't'},
++              {"version",        no_argument,       NULL, 'V'},
++              { NULL, 0, NULL, 0},
++      };
++      static const char short_options[] = "hl:trV";
++      static const struct opts default_values = {
++              .log_file = "pb-twin.log",
++      };
++
++      *opts = default_values;
++
++      while (1) {
++              int c = getopt_long(argc, argv, short_options, long_options,
++                      NULL);
++
++              if (c == EOF)
++                      break;
++
++              switch (c) {
++              case 'h':
++                      opts->show_help = opt_yes;
++                      break;
++              case 'l':
++                      opts->log_file = optarg;
++                      break;
++              case 't':
++                      opts->use_timeout = opt_yes;
++                      break;
++              case 'r':
++                      opts->reset_defaults = opt_yes;
++                      break;
++              case 'V':
++                      opts->show_version = opt_yes;
++                      break;
++              default:
++                      opts->show_help = opt_yes;
++                      return -1;
++              }
++      }
++
++      return optind != argc;
++}
++
++/**
++ * struct ps3_gui - Main gui program instance.
++ */
++
++
++struct ps3_gui {
++      struct ui_timer timer;
++      struct ps3_flash_values values;
++      int dirty_values;
++
++      struct pbt_scr scr;
++      struct pbt_frame frame;
++      struct pbt_menu *menu;
++};
++
++static struct ps3_gui ps3;
++
++static struct pbt_scr *pbt_scr_from_tscreen(twin_screen_t *tscreen)
++{
++      assert(ps3.scr.sig == pbt_scr_sig);
++      assert(ps3.scr.tscreen == tscreen);
++      return &ps3.scr;
++}
++
++static void sig_handler(int signum)
++{
++      DBGS("%d\n", signum);
++
++      switch (signum) {
++      case SIGALRM:
++              ui_timer_sigalrm(&ps3.timer);
++              break;
++      case SIGWINCH:
++//            if (ps3.gui)
++//                    gui_resize(ps3.gui);
++              break;
++      default:
++              assert(0 && "unknown sig");
++              /* fall through */
++      case SIGINT:
++      case SIGHUP:
++      case SIGTERM:
++              exit(EXIT_FAILURE);
++//            if (ps3.gui)
++//                    gui_abort(ps3.gui);
++              break;
++      }
++}
 +
 +/**
 + * main - twin bootloader main routine.
-+ *
-+ * Returns:
-+ *   0  = Returned as success by ps3-utils programs.
-+ *   1  = Error, expecting a restart.
-+ *   22 = Exit to shell.
 + */
 +
-+int main(void)
++int main(int argc, char *argv[])
 +{
++      static struct sigaction sa;
++      static struct opts opts;
++      int result;
++      int ui_result;
++      unsigned int mode;
 +      FILE *log;
 +
-+      log = fopen("pb-twin.log", "a");
++      result = opts_parse(&opts, argc, argv);
++
++      if (result) {
++              print_usage();
++              return EXIT_FAILURE;
++      }
++
++      if (opts.show_help == opt_yes) {
++              print_usage();
++              return EXIT_SUCCESS;
++      }
++
++      if (opts.show_version == opt_yes) {
++              print_version();
++              return EXIT_SUCCESS;
++      }
++
++      log = fopen(opts.log_file, "a");
 +      assert(log);
 +      pb_log_set_stream(log);
 +
@@ -63,13 +1397,84 @@ Signed-off-by: Geoff Levand <geoffrey.levand@am.sony.com>
 +
 +      pb_log("--- pb-twin ---\n");
 +
-+      pboot_fbdev = twin_fbdev_create(-1, SIGUSR1);
-+      if (pboot_fbdev == NULL) {
++      sa.sa_handler = sig_handler;
++      result = sigaction(SIGALRM, &sa, NULL);
++      result += sigaction(SIGHUP, &sa, NULL);
++      result += sigaction(SIGINT, &sa, NULL);
++      result += sigaction(SIGTERM, &sa, NULL);
++      result += sigaction(SIGWINCH, &sa, NULL);
++
++      if (result) {
++              pb_log("%s sigaction failed.\n", __func__);
++              return EXIT_FAILURE;
++      }
++
++      ps3.values = ps3_flash_defaults;
++
++      if (opts.reset_defaults != opt_yes)
++              ps3.dirty_values = ps3_flash_get_values(&ps3.values);
++
++      twin_feature_init(); // need it???
++
++      /* Setup screen. */
++
++      ps3.scr.sig = pbt_scr_sig;
++
++#if defined(USE_TWIN_X11)
++      ps3.scr.x11 = twin_x11_create(XOpenDisplay(0), 1024, 768);
++
++      if (!ps3.scr.x11) {
++              perror("failed to create x11 screen !\n");
++              return EXIT_FAILURE;
++      }
++
++      ps3.scr.tscreen = ps3.scr.x11->screen;
++#else
++      result = ps3_get_video_mode(&mode);
++
++      /* Current becomes default if ps3_flash_get_values() failed. */
++
++      if (ps3.dirty_values && !result)
++              ps3.values.video_mode = mode;
++
++      /* Set mode if not at default. */
++
++      if (!result && (ps3.values.video_mode != (uint16_t)mode))
++              ps3_set_video_mode(ps3.values.video_mode);
++
++      ps3.scr.fbdev = twin_fbdev_create(-1, SIGUSR1);
++
++      if (!ps3.scr.fbdev) {
 +              perror("failed to create fbdev screen !\n");
-+              return 1;
++              return EXIT_FAILURE;
 +      }
 +
++      ps3.scr.tscreen = ps3.scr.fbdev->screen;
++#endif
++
++      ps3.scr.tscreen->event_filter = pbt_scr_event;
++
++      twin_screen_set_background(ps3.scr.tscreen,
++              pbt_load_background(ps3.scr.tscreen));
++
++      /* setup menu */
++
++      ps3.menu = pbt_menu_create(NULL, &ps3.scr);
++
++      pbt_device_pane_set_focus(ps3.menu, 0);
++      twin_screen_set_active(ps3.scr.tscreen, ps3.menu->dp->window->pixmap);
++
++      /* Console switch */
++
++      if (ps3.scr.fbdev)
++              twin_fbdev_activate(ps3.scr.fbdev);
++
++      /* run twin */
++
++//    twin_toplevel_show(ps3.toplevel);
++      twin_dispatch();
++
 +      pb_log("--- end ---\n");
 +
-+      return 22;
++      return ui_result ? EXIT_FAILURE : EXIT_SUCCESS;
 +}