- 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)
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
}
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)
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",
.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
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.
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);
+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 */
+ /* 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 */
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;
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);
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);
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
# 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);
+
+
+ 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;
+}