From 725baba65044bf7a390f10b51127f96f0c05519c Mon Sep 17 00:00:00 2001 From: geoff Date: Mon, 24 Aug 2009 20:24:28 +0000 Subject: [PATCH] petitboot: Update to current repository revision Signed-off-by: Geoff Levand git-svn-id: svn://svn.openwrt.org/openwrt/packages@17380 3c298f89-4303-0410-b956-a3cf2f4a3e73 --- utils/petitboot/Makefile | 2 +- .../patches/010-petitboot-fixups.diff | 2879 +---------------- .../patches/020-petitboot-fix-pb-twin.diff | 1443 ++++++++- 3 files changed, 1486 insertions(+), 2838 deletions(-) diff --git a/utils/petitboot/Makefile b/utils/petitboot/Makefile index 6f544dbc1..d4cdeae44 100644 --- a/utils/petitboot/Makefile +++ b/utils/petitboot/Makefile @@ -8,7 +8,7 @@ include $(TOPDIR)/rules.mk PKG_NAME:=petitboot -PKG_VERSION:=53aa807ae41e48fd71653c2d00083a44a8bca14c +PKG_VERSION:=93b2c2e0f0ca46d2a823b33cdfa44d082e9e8d10 PKG_RELEASE:=1 PKG_SOURCE:=$(PKG_NAME)-$(PKG_VERSION).tar.gz diff --git a/utils/petitboot/patches/010-petitboot-fixups.diff b/utils/petitboot/patches/010-petitboot-fixups.diff index f82dd29ef..5cddeb6b0 100644 --- a/utils/petitboot/patches/010-petitboot-fixups.diff +++ b/utils/petitboot/patches/010-petitboot-fixups.diff @@ -1,49 +1,10 @@ - 34 files changed, 1687 insertions(+), 235 deletions(-) + 9 files changed, 90 insertions(+), 33 deletions(-) -diff --git a/Makefile.in b/Makefile.in -index 01771db..23135aa 100644 ---- a/Makefile.in -+++ b/Makefile.in -@@ -21,7 +21,12 @@ ENABLE_PS3 = @ENABLE_PS3@ - - # other programs - INSTALL = @INSTALL@ -+INSTALL_DATA = @INSTALL_DATA@ -+INSTALL_PROGRAM = @INSTALL_PROGRAM@ -+INSTALL_SCRIPT = @INSTALL_SCRIPT@ -+INSTALL_STRIP_PROGRAM = @INSTALL_STRIP_PROGRAM@ - SHELL = @SHELL@ -+STRIP = @STRIP@ - - # paths - prefix = @prefix@ -@@ -34,5 +39,6 @@ localstatedir = @localstatedir@ - builddir = @builddir@ - srcdir = @srcdir@ - top_srcdir = @top_srcdir@ -+mandir = @mandir@ - - include $(top_srcdir)/rules.mk -diff --git a/configure.ac b/configure.ac -index cb43f8c..fa40f34 100644 ---- a/configure.ac -+++ b/configure.ac -@@ -35,9 +35,7 @@ AC_ARG_ENABLE([ps3], - [], - [enable_ps3=check]) - --AS_IF([test "x$enable_ps3" != xno], -- [AC_SUBST([ENABLE_PS3], ["y"])], -- [AC_SUBST([ENABLE_PS3], ["n"])]) -+AS_IF([test "x$enable_ps3" != xno], [AC_SUBST([ENABLE_PS3], ["y"])], []) - - AC_ARG_WITH([twin], - [AS_HELP_STRING([--with-twin], diff --git a/discover/kboot-parser.c b/discover/kboot-parser.c -index e688c22..7c7cb5d 100644 +index 23d48a4..7c7cb5d 100644 --- a/discover/kboot-parser.c +++ b/discover/kboot-parser.c -@@ -133,10 +133,11 @@ static int kboot_parse(struct discover_context *dc) +@@ -133,7 +133,7 @@ static int kboot_parse(struct discover_context *dc) conf = talloc_zero(dc, struct conf_context); if (!conf) @@ -52,67 +13,6 @@ index e688c22..7c7cb5d 100644 conf->dc = dc; conf->global_options = kboot_global_options, -+ conf_init_global_options(conf); - conf->conf_files = kboot_conf_files, - conf->process_pair = kboot_process_pair; - conf->parser_info = (void *)kboot_ignored_names, -diff --git a/discover/parser-conf.c b/discover/parser-conf.c -index 14f847d..88e96b7 100644 ---- a/discover/parser-conf.c -+++ b/discover/parser-conf.c -@@ -121,6 +121,18 @@ int conf_param_in_list(const char *const *list, const char *param) - } - - /** -+ * conf_init_global_options - Zero the global option table. -+ */ -+ -+void conf_init_global_options(struct conf_context *conf) -+{ -+ int i; -+ -+ for (i = 0; conf->global_options[i].name; i++) -+ conf->global_options[i].value = NULL; -+} -+ -+/** - * conf_set_global_option - Set a value in the global option table. - * - * Check if an option (name=value) is a global option. If so, store it in -@@ -136,7 +148,7 @@ int conf_set_global_option(struct conf_context *conf, const char *name, - if (streq(name, conf->global_options[i].name)) { - conf->global_options[i].value - = talloc_strdup(conf, value); -- pb_log("%s: %s:%s\n", __func__, name, value); -+ pb_log("%s: @%s@%s@\n", __func__, name, value); - return 1; - } - } -@@ -158,8 +170,11 @@ const char *conf_get_global_option(struct conf_context *conf, - int i; - - for (i = 0; conf->global_options[i].name ;i++) -- if (streq(name, conf->global_options[i].name)) -+ if (streq(name, conf->global_options[i].name)) { -+ pb_log("%s: @%s@%s@\n", __func__, name, -+ conf->global_options[i].value); - return conf->global_options[i].value; -+ } - - assert(0 && "unknown global name"); - return NULL; -diff --git a/discover/parser-conf.h b/discover/parser-conf.h -index 09015d1..3325faf 100644 ---- a/discover/parser-conf.h -+++ b/discover/parser-conf.h -@@ -41,6 +41,7 @@ struct conf_context { - int conf_parse(struct conf_context *conf); - char *conf_get_param_pair(char *str, char **name_out, char **value_out, - char terminator); -+void conf_init_global_options(struct conf_context *conf); - const char *conf_get_global_option(struct conf_context *conf, - const char *name); - int conf_set_global_option(struct conf_context *conf, const char *name, diff --git a/discover/parser.c b/discover/parser.c index 2b4ddd2..8f2735c 100644 --- a/discover/parser.c @@ -138,306 +38,11 @@ index 2b4ddd2..8f2735c 100644 } static int compare_parsers(const void *a, const void *b) -diff --git a/discover/paths.c b/discover/paths.c -index 8e2a361..fe7a876 100644 ---- a/discover/paths.c -+++ b/discover/paths.c -@@ -81,14 +81,6 @@ char *parse_device_path(void *alloc_ctx, - if (is_prefix(dev_str, "/dev/")) - dev_str += strlen("/dev/"); - -- /* PS3 hack: if we're reading from a ps3dx device, and we refer to -- * a sdx device, remap to ps3dx */ -- if (cur_dev && is_prefix(cur_dev, "/dev/ps3d") -- && is_prefix(dev_str, "sd")) { -- snprintf(tmp, 255, "ps3d%s", dev_str + 2); -- dev_str = tmp; -- } -- - return join_paths(alloc_ctx, "/dev", dev_str); - } - -diff --git a/discover/pb-discover.c b/discover/pb-discover.c -index d7ea0ca..bd515e3 100644 ---- a/discover/pb-discover.c -+++ b/discover/pb-discover.c -@@ -1,5 +1,10 @@ - -+#if defined(HAVE_CONFIG_H) -+#include "config.h" -+#endif -+ - #include -+#include - #include - #include - -@@ -11,6 +16,79 @@ - #include "discover-server.h" - #include "device-handler.h" - -+static void print_version(void) -+{ -+ printf("pb-discover (" PACKAGE_NAME ") " PACKAGE_VERSION "\n"); -+} -+ -+static void print_usage(void) -+{ -+ print_version(); -+ printf( -+"Usage: pb-discover [-h, --help] [-l, --log log-file] [-V, --version]\n"); -+} -+ -+/** -+ * enum opt_value - Tri-state options variables. -+ */ -+ -+enum opt_value {opt_undef = 0, opt_yes, opt_no}; -+ -+/** -+ * struct opts - Values from command line options. -+ */ -+ -+struct opts { -+ enum opt_value show_help; -+ const char *log_file; -+ enum opt_value show_version; -+}; -+ -+/** -+ * opts_parse - Parse the command line options. -+ */ -+ -+static int opts_parse(struct opts *opts, int argc, char *argv[]) -+{ -+ static const struct option long_options[] = { -+ {"help", no_argument, NULL, 'h'}, -+ {"log", required_argument, NULL, 'l'}, -+ {"version", no_argument, NULL, 'V'}, -+ { NULL, 0, NULL, 0}, -+ }; -+ static const char short_options[] = "hl:V"; -+ static const struct opts default_values = { -+ .log_file = "pb-discover.log", -+ }; -+ -+ *opts = default_values; -+ -+ while (1) { -+ int c = getopt_long(argc, argv, short_options, long_options, -+ NULL); -+ -+ if (c == EOF) -+ break; -+ -+ switch (c) { -+ case 'h': -+ opts->show_help = opt_yes; -+ break; -+ case 'l': -+ opts->log_file = optarg; -+ break; -+ case 'V': -+ opts->show_version = opt_yes; -+ break; -+ default: -+ opts->show_help = opt_yes; -+ return -1; -+ } -+ } -+ -+ return optind != argc; -+} -+ - static int running; - - static void sigint_handler(int __attribute__((unused)) signum) -@@ -18,15 +96,31 @@ static void sigint_handler(int __attribute__((unused)) signum) - running = 0; - } - --int main(void) -+int main(int argc, char *argv[]) - { - struct device_handler *handler; - struct discover_server *server; -+ struct opts opts; - struct udev *udev; - struct user_event *uev; - FILE *log; - -- log = fopen("pb-discover.log", "a"); -+ if (opts_parse(&opts, argc, argv)) { -+ print_usage(); -+ return EXIT_FAILURE; -+ } -+ -+ if (opts.show_help == opt_yes) { -+ print_usage(); -+ return EXIT_SUCCESS; -+ } -+ -+ if (opts.show_version == opt_yes) { -+ print_version(); -+ return EXIT_SUCCESS; -+ } -+ -+ log = fopen(opts.log_file, "a"); - assert(log); - pb_log_set_stream(log); - diff --git a/discover/yaboot-parser.c b/discover/yaboot-parser.c -index d9f2aff..6101cd8 100644 +index 1000505..6101cd8 100644 --- a/discover/yaboot-parser.c +++ b/discover/yaboot-parser.c -@@ -15,7 +15,6 @@ struct yaboot_state { - struct boot_option *opt; - const char *desc_image; - char *desc_initrd; -- int found_suse; - int globals_done; - const char *const *known_names; - }; -@@ -56,6 +55,19 @@ static void yaboot_process_pair(struct conf_context *conf, const char *name, - char *value) - { - struct yaboot_state *state = conf->parser_info; -+ struct fixed_pair { -+ const char *image; -+ const char *initrd; -+ }; -+ static const struct fixed_pair suse_fp32 = { -+ .image = "/suseboot/vmlinux32", -+ .initrd = "/suseboot/initrd32", -+ }; -+ static const struct fixed_pair suse_fp64 = { -+ .image = "/suseboot/vmlinux64", -+ .initrd = "/suseboot/initrd64", -+ }; -+ const struct fixed_pair *suse_fp; - - /* fixup for bare values */ - -@@ -73,32 +85,70 @@ static void yaboot_process_pair(struct conf_context *conf, const char *name, - /* image */ - - if (streq(name, "image")) { -+ const char *g_boot = conf_get_global_option(conf, "boot"); -+ const char *g_part = conf_get_global_option(conf, "partition"); -+ -+ /* First finish any previous image. */ -+ - if (state->opt->boot_image_file) - yaboot_finish(conf); - -- state->opt->boot_image_file = resolve_path(state->opt, value, -- conf->dc->device_path); -- state->desc_image = talloc_strdup(state->opt, value); -+ /* Then start the new image. */ -+ -+ if (g_boot && g_part) { -+ char* dev = talloc_asprintf(NULL, "%s%s", g_boot, -+ g_part); -+ -+ state->opt->boot_image_file = resolve_path(state->opt, -+ value, dev); -+ state->desc_image = talloc_asprintf(state->opt, -+ "%s%s", dev, value); -+ talloc_free(dev); -+ } else if (g_boot) { -+ state->opt->boot_image_file = resolve_path(state->opt, -+ value, g_boot); -+ state->desc_image = talloc_asprintf(state->opt, -+ "%s%s", g_boot, value); -+ } else { -+ state->opt->boot_image_file = resolve_path(state->opt, -+ value, conf->dc->device_path); -+ state->desc_image = talloc_strdup(state->opt, value); -+ } -+ - return; - } - -- if (streq(name, "image[32bit]") || streq(name, "image[64bit]")) { -- state->found_suse = 1; -+ /* Special processing for SUSE install CD. */ -+ -+ if (streq(name, "image[32bit]")) -+ suse_fp = &suse_fp32; -+ else if (streq(name, "image[64bit]")) -+ suse_fp = &suse_fp64; -+ else -+ suse_fp = NULL; -+ -+ if (suse_fp) { -+ /* First finish any previous image. */ - - if (state->opt->boot_image_file) - yaboot_finish(conf); - -+ /* Then start the new image. */ -+ - if (*value == '/') { - state->opt->boot_image_file = resolve_path(state->opt, - value, conf->dc->device_path); - state->desc_image = talloc_strdup(state->opt, value); - } else { -- char *s; -- asprintf(&s, "/suseboot/%s", value); - state->opt->boot_image_file = resolve_path(state->opt, -- s, conf->dc->device_path); -- state->desc_image = talloc_strdup(state->opt, s); -- free(s); -+ suse_fp->image, conf->dc->device_path); -+ state->desc_image = talloc_strdup(state->opt, -+ suse_fp->image); -+ -+ state->opt->initrd_file = resolve_path(state->opt, -+ suse_fp->initrd, conf->dc->device_path); -+ state->desc_initrd = talloc_asprintf(state, "initrd=%s", -+ suse_fp->initrd); - } - - return; -@@ -112,19 +162,28 @@ static void yaboot_process_pair(struct conf_context *conf, const char *name, - /* initrd */ - - if (streq(name, "initrd")) { -- if (!state->found_suse || (*value == '/')) { -+ const char *g_boot = conf_get_global_option(conf, "boot"); -+ const char *g_part = conf_get_global_option(conf, "partition"); -+ -+ if (g_boot && g_part) { -+ char* dev = talloc_asprintf(NULL, "%s%s", g_boot, -+ g_part); -+ - state->opt->initrd_file = resolve_path(state->opt, -- value, conf->dc->device_path); -- state->desc_initrd = talloc_asprintf(state, "initrd=%s", -- value); -+ value, dev); -+ state->desc_initrd = talloc_asprintf(state, -+ "initrd=%s%s", dev, value); -+ talloc_free(dev); -+ } else if (g_boot) { -+ state->opt->initrd_file = resolve_path(state->opt, -+ value, g_boot); -+ state->desc_initrd = talloc_asprintf(state, -+ "initrd=%s%s", g_boot, value); - } else { -- char *s; -- asprintf(&s, "/suseboot/%s", value); - state->opt->initrd_file = resolve_path(state->opt, -- s, conf->dc->device_path); -+ value, conf->dc->device_path); - state->desc_initrd = talloc_asprintf(state, "initrd=%s", -- s); -- free(s); -+ value); - } - return; - } -@@ -236,10 +295,11 @@ static int yaboot_parse(struct discover_context *dc) +@@ -295,7 +295,7 @@ static int yaboot_parse(struct discover_context *dc) conf = talloc_zero(dc, struct conf_context); if (!conf) @@ -446,23 +51,11 @@ index d9f2aff..6101cd8 100644 conf->dc = dc; conf->global_options = yaboot_global_options, -+ conf_init_global_options(conf); - conf->conf_files = yaboot_conf_files, - conf->process_pair = yaboot_process_pair; - conf->finish = yaboot_finish; diff --git a/lib/system/system.c b/lib/system/system.c -index 380dded..7371445 100644 +index 65bd6bf..7371445 100644 --- a/lib/system/system.c +++ b/lib/system/system.c -@@ -3,6 +3,7 @@ - #include "config.h" - #endif - -+#include - #include - #include - #include -@@ -19,6 +20,7 @@ const struct pb_system_apps pb_system_apps = { +@@ -20,6 +20,7 @@ const struct pb_system_apps pb_system_apps = { .cp = "/bin/cp", .kexec = "/sbin/kexec", .mount = "/bin/mount", @@ -470,46 +63,6 @@ index 380dded..7371445 100644 .sftp = "/usr/bin/sftp", .tftp = "/usr/bin/tftp", .umount = "/bin/umount", -@@ -104,13 +106,13 @@ int pb_rmdir_recursive(const char *base, const char *dir) - - int pb_run_cmd(const char *const *cmd_argv) - { -- int status; -- pid_t pid; - #if defined(DEBUG) - enum {do_debug = 1}; - #else - enum {do_debug = 0}; - #endif -+ int status; -+ pid_t pid; - - if (do_debug) { - const char *const *p = cmd_argv; -@@ -125,12 +127,23 @@ int pb_run_cmd(const char *const *cmd_argv) - pb_log("%s: %s\n", __func__, cmd_argv[0]); - - pid = fork(); -+ - if (pid == -1) { - pb_log("%s: fork failed: %s\n", __func__, strerror(errno)); - return -1; - } - - if (pid == 0) { -+ int log = fileno(pb_log_get_stream()); -+ -+ /* Redirect child output to log. */ -+ -+ status = dup2(log, STDOUT_FILENO); -+ assert(status != -1); -+ -+ status = dup2(log, STDERR_FILENO); -+ assert(status != -1); -+ - execvp(cmd_argv[0], (char *const *)cmd_argv); - pb_log("%s: exec failed: %s\n", __func__, strerror(errno)); - exit(EXIT_FAILURE); diff --git a/lib/system/system.h b/lib/system/system.h index 47c7c02..1918309 100644 --- a/lib/system/system.h @@ -522,499 +75,24 @@ index 47c7c02..1918309 100644 const char *sftp; const char *tftp; const char *umount; -diff --git a/man/pb-cui.8 b/man/pb-cui.8 -new file mode 100644 -index 0000000..e671c20 ---- /dev/null -+++ b/man/pb-cui.8 -@@ -0,0 +1,67 @@ -+.\" Copyright (C) 2009 Sony Computer Entertainment Inc. -+.\" Copyright 2009 Sony Corp. -+.\" -+.\" This program is free software; you can redistribute it and/or modify -+.\" it under the terms of the GNU General Public License as published by -+.\" the Free Software Foundation; version 2 of the License. -+.\" -+.\" This program is distributed in the hope that it will be useful, -+.\" but WITHOUT ANY WARRANTY; without even the implied warranty of -+.\" MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -+.\" GNU General Public License for more details. -+.\" -+.\" You should have received a copy of the GNU General Public License -+.\" along with this program; if not, write to the Free Software -+.\" Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA -+.\" -+.\" Maintainer's Notes: -+.\" * For syntax help see the man pages for 'mdoc' and 'mdoc.samples'. -+.\" * To check syntax use this: -+.\" 'groff -C -mtty-char -Tutf8 -man pb-cui.8'. -+.\" * To check format use this: 'less pb-cui.8'. -+.\" -+.Dd "" -+.Dt pb-cui 8 -+.Os -+.\" -+.Sh NAME -+.\" ==== -+.Nm pb-cui -+.Nd Petitboot ncurses bootloader UI -+.\" -+.Sh SYNOPSIS -+.\" ======== -+.Nm -+.Op Fl h, -help -+.Op Fl l, -log Ar log-file -+.Op Fl V, -version -+.\" -+.Sh DESCRIPTION -+.\" =========== -+pb-cui is an ncurses based interface to the Petitboot bootloader. -+.Pp -+Petitboot is a Linux kexec based bootloader that supports loading Linux -+kernel and initrd images from any device that can be mounted by Linux. -+It can also load images from the network using the -+HTTP, HTTPS, NFS, SFTP, and TFTP -+protocols. -+.\" -+.Sh OPTIONS -+.\" ======= -+.Bl -tag -width indent -+.\" -+.It Fl h, -help -+Print a help message. -+.\" -+.It Fl l, -log Ar log-file -+Log messages to the file -+.Ar log-file . -+The default log is a file pb-cui.log in the directory where pb-cui -+is started. New messages are appended to an existing log file. -+.\" -+.It Fl V, -version -+Display the program version number. -+.El -+.Sh SEE ALSO -+.\" ======== -+.Xr petitboot 8 , Xr pb-discover 8 , Xr pb-event 8 -diff --git a/man/pb-discover.8 b/man/pb-discover.8 -new file mode 100644 -index 0000000..9123e0f ---- /dev/null -+++ b/man/pb-discover.8 -@@ -0,0 +1,44 @@ -+.\" Copyright (C) 2009 Sony Computer Entertainment Inc. -+.\" Copyright 2009 Sony Corp. -+.\" -+.\" This program is free software; you can redistribute it and/or modify -+.\" it under the terms of the GNU General Public License as published by -+.\" the Free Software Foundation; version 2 of the License. -+.\" -+.\" This program is distributed in the hope that it will be useful, -+.\" but WITHOUT ANY WARRANTY; without even the implied warranty of -+.\" MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -+.\" GNU General Public License for more details. -+.\" -+.\" You should have received a copy of the GNU General Public License -+.\" along with this program; if not, write to the Free Software -+.\" Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA -+.\" -+.\" Maintainer's Notes: -+.\" * For syntax help see the man pages for 'mdoc' and 'mdoc.samples'. -+.\" * To check syntax use this: -+.\" 'groff -C -mtty-char -Tutf8 -man pb-discover.8'. -+.\" * To check format use this: 'less pb-discover.8'. -+.\" -+.Dd "" -+.Dt pb-discover 8 -+.Os -+.\" -+.Sh NAME -+.\" ==== -+.Nm pb-discover -+.Nd The dynamic device discovery daemon of the Petitboot bootloader -+.\" -+.Sh SYNOPSIS -+.\" ======== -+.Nm -+.\" -+.Sh DESCRIPTION -+.\" =========== -+pb-discover maintains a dynamic list of boot options available to -+the system. On startup, the petitboot user interface clients connect to -+pb-discover daemon and receive boot option information. -+.\" -+.Sh SEE ALSO -+.\" ======== -+.Xr petitboot 8 , Xr pb-cui 8 , Xr pb-event 8 -diff --git a/man/pb-event.8 b/man/pb-event.8 -new file mode 100644 -index 0000000..dc123e1 ---- /dev/null -+++ b/man/pb-event.8 -@@ -0,0 +1,43 @@ -+.\" Copyright (C) 2009 Sony Computer Entertainment Inc. -+.\" Copyright 2009 Sony Corp. -+.\" -+.\" This program is free software; you can redistribute it and/or modify -+.\" it under the terms of the GNU General Public License as published by -+.\" the Free Software Foundation; version 2 of the License. -+.\" -+.\" This program is distributed in the hope that it will be useful, -+.\" but WITHOUT ANY WARRANTY; without even the implied warranty of -+.\" MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -+.\" GNU General Public License for more details. -+.\" -+.\" You should have received a copy of the GNU General Public License -+.\" along with this program; if not, write to the Free Software -+.\" Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA -+.\" -+.\" Maintainer's Notes: -+.\" * For syntax help see the man pages for 'mdoc' and 'mdoc.samples'. -+.\" * To check syntax use this: -+.\" 'groff -C -mtty-char -Tutf8 -man pb-event.8'. -+.\" * To check format use this: 'less pb-event.8'. -+.\" -+.Dd "" -+.Dt pb-event 8 -+.Os -+.\" -+.Sh NAME -+.\" ==== -+.Nm pb-event -+.Nd Event helper for the Petitboot bootloader -+.\" -+.Sh SYNOPSIS -+.\" ======== -+.Nm -+.\" -+.Sh DESCRIPTION -+.\" =========== -+The pb-event utility is used to send user mode events to pb-discover, the -+petitboot device discovery daemon. -+.\" -+.Sh SEE ALSO -+.\" ======== -+.Xr petitboot 8 , Xr pb-cui 8 , Xr pb-discover 8 -diff --git a/man/petitboot.8 b/man/petitboot.8 -new file mode 100644 -index 0000000..9dc2222 ---- /dev/null -+++ b/man/petitboot.8 -@@ -0,0 +1,47 @@ -+.\" Copyright (C) 2009 Sony Computer Entertainment Inc. -+.\" Copyright 2009 Sony Corp. -+.\" -+.\" This program is free software; you can redistribute it and/or modify -+.\" it under the terms of the GNU General Public License as published by -+.\" the Free Software Foundation; version 2 of the License. -+.\" -+.\" This program is distributed in the hope that it will be useful, -+.\" but WITHOUT ANY WARRANTY; without even the implied warranty of -+.\" MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -+.\" GNU General Public License for more details. -+.\" -+.\" You should have received a copy of the GNU General Public License -+.\" along with this program; if not, write to the Free Software -+.\" Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA -+.\" -+.\" Maintainer's Notes: -+.\" * For syntax help see the man pages for 'mdoc' and 'mdoc.samples'. -+.\" * To check syntax use this: -+.\" 'groff -C -mtty-char -Tutf8 -man petitboot.8'. -+.\" * To check format use this: 'less petitboot.8'. -+.\" -+.Dd "" -+.Dt petitboot 8 -+.Os -+.\" -+.Sh NAME -+.\" ==== -+.Nm petitboot -+.Nd The Petitboot bootloader -+.\" -+.Sh DESCRIPTION -+.\" =========== -+Petitboot is a platform independent bootloader based on Linux kexec. -+Petitboot can load Linux kernel and initrd images from any device that -+can be mounted by Linux, and can also load images from the network -+using the -+HTTP, HTTPS, NFS, SFTP, and TFTP -+protocols. -+.Pp -+Petitboot looks for bootloader configuration files on mountable devices -+in the system, and can also be configured to use boot information -+from a DHCP server. -+.\" -+.Sh SEE ALSO -+.\" ======== -+.Xr pb-cui 8 , Xr pb-discover 8 , Xr pb-event 8 -diff --git a/rules.mk b/rules.mk -index e743338..3b1dcfe 100644 ---- a/rules.mk -+++ b/rules.mk -@@ -19,8 +19,8 @@ parser_test = test/parser-test - # install targets and components - daemons = $(pb_discover) - parsers = event kboot yaboot --uis = $(pb_cui) $(pb_test) --tests = $(parser_test) -+uis = $(pb_cui) -+tests = $(parser_test) $(pb_test) - utils = $(pb_event) +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; - 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 -+#include -+#include -+#include -+#include -+#include -+ -+#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 -+ -+/** -+ * 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 + client->fd = socket(AF_UNIX, SOCK_STREAM, 0); +- if (!client->fd < 0) { ++ if (client->fd < 0) { + pb_log("%s: socket: %s\n", __func__, strerror(errno)); + goto out_err; + } diff --git a/ui/common/loader.c b/ui/common/loader.c -index babca28..5c69533 100644 +index 0fe62a0..5c69533 100644 --- a/ui/common/loader.c +++ b/ui/common/loader.c -@@ -227,7 +227,7 @@ enum wget_flags { - static char *pb_load_wget(void *ctx, struct pb_url *url, enum wget_flags flags) - { - int result; -- const char *argv[6]; -+ const char *argv[7]; - const char **p; - char *local; - -@@ -238,12 +238,15 @@ static char *pb_load_wget(void *ctx, struct pb_url *url, enum wget_flags flags) - - p = argv; - *p++ = pb_system_apps.wget; /* 1 */ -- *p++ = "-O"; /* 2 */ -- *p++ = local; /* 3 */ -- *p++ = url->full; /* 4 */ -+#if !defined(DEBUG) -+ *p++ = "--quiet"; /* 2 */ -+#endif -+ *p++ = "-O"; /* 3 */ -+ *p++ = local; /* 4 */ -+ *p++ = url->full; /* 5 */ - if (flags & wget_no_check_certificate) -- *p++ = "--no-check-certificate"; /* 5 */ -- *p++ = NULL; /* 6 */ -+ *p++ = "--no-check-certificate"; /* 6 */ -+ *p++ = NULL; /* 7 */ - - result = pb_run_cmd(argv); - -@@ -260,16 +263,22 @@ fail: +@@ -263,16 +263,22 @@ fail: /** * pb_load_file - Loads a remote file and returns the local file path. * @ctx: The talloc context to associate with the returned string. @@ -1038,7 +116,7 @@ index babca28..5c69533 100644 if (!url) return NULL; -@@ -277,19 +286,28 @@ char *pb_load_file(void *ctx, const char *remote) +@@ -280,19 +286,28 @@ char *pb_load_file(void *ctx, const char *remote) case pb_url_ftp: case pb_url_http: local = pb_load_wget(ctx, url, 0); @@ -1081,330 +159,11 @@ index b06bb43..42d4d4b 100644 +char *pb_load_file(void *ctx, const char *remote, unsigned int *tempfile); #endif -diff --git a/ui/common/ps3.c b/ui/common/ps3.c -index 5c83289..cb1c8d1 100644 ---- a/ui/common/ps3.c -+++ b/ui/common/ps3.c -@@ -49,6 +49,11 @@ static const struct os_area_db_id id_flags = - .owner = OS_AREA_DB_OWNER_PETITBOOT, /* 3 */ - .key = 3, - }; -+static const struct os_area_db_id id_timeout = -+{ -+ .owner = OS_AREA_DB_OWNER_PETITBOOT, /* 3 */ -+ .key = 4, -+}; - - struct ps3_flash_ctx { - FILE *dev; -@@ -59,6 +64,8 @@ struct ps3_flash_ctx { - - static void ps3_flash_close(struct ps3_flash_ctx *fc) - { -+ 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; - - result = os_area_db_read(&fc.db, &fc.header, fc.dev); - -+ ps3_flash_close(&fc); -+ - if (result) { - pb_log("%s: os_area_db_read failed: %s\n", __func__, - strerror(errno)); -- goto fail; -+ goto done; - } - - 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) - } - } - -+ /* 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, - }; - - 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 -+#include -+#include -+ -+#include "log/log.h" -+#include "timer.h" -+ -+/** -+ * ui_timer_init - Initialize the timer for use. -+ * @seconds: The final timeout value in seconds. -+ */ -+ -+void ui_timer_init(struct ui_timer *timer, unsigned int seconds) -+{ -+ pb_log("%s: %u\n", __func__, seconds); -+ assert(!timer->disabled); -+ timer->timeout = seconds; -+} -+ -+/** -+ * ui_timer_next - Calculate the next timer interval. -+ */ -+ -+static unsigned int ui_timer_next(unsigned int seconds) -+{ -+ unsigned int next; -+ -+ if (seconds == 0) { -+ next = 0; -+ goto done; -+ } -+ -+ 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 -+ -+/** -+ * 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 +index bd6dd31..0140f0e 100644 --- a/ui/common/ui-system.c +++ b/ui/common/ui-system.c -@@ -33,46 +33,80 @@ +@@ -33,13 +33,13 @@ #include "ui-system.h" /** @@ -1420,11 +179,8 @@ index 3f54191..0140f0e 100644 const char *args) { int result; -- const char *argv[8]; -+ const char *argv[6]; - const char **p; -+ char *s_initrd = NULL; -+ char *s_args = NULL; +@@ -49,34 +49,64 @@ static int run_kexec_local(const char *l_image, const char *l_initrd, + char *s_args = NULL; p = argv; - *p++ = pb_system_apps.kexec; /* 1 */ @@ -1432,18 +188,16 @@ index 3f54191..0140f0e 100644 + *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); + s_initrd = talloc_asprintf(NULL, "--initrd=%s", l_initrd); + assert(s_initrd); +- *p++ = s_initrd; /* 2 */ + *p++ = s_initrd; /* 3 */ } if (args) { -- *p++ = "--append"; /* 4 */ -- *p++ = args; /* 5 */ -+ s_args = talloc_asprintf(NULL, "--append=%s", args); -+ assert(s_args); + s_args = talloc_asprintf(NULL, "--append=%s", args); + assert(s_args); +- *p++ = s_args; /* 3 */ + *p++ = s_args; /* 4 */ } @@ -1491,9 +245,9 @@ index 3f54191..0140f0e 100644 + /* On error, force a kexec with the -e option */ if (result) { -- *(p + 0) = "-f"; /* 6 */ -- *(p + 1) = l_image; /* 7 */ -- *(p + 2) = NULL; /* 8 */ +- *(p + 0) = "-f"; /* 4 */ +- *(p + 1) = l_image; /* 5 */ +- *(p + 2) = NULL; /* 6 */ + p = argv; + *p++ = pb_system_apps.kexec; /* 1 */ + *p++ = "-e"; /* 2 */ @@ -1501,22 +255,20 @@ index 3f54191..0140f0e 100644 result = pb_run_cmd(argv); } -@@ -85,38 +119,51 @@ static int run_kexec_local(const char *l_image, const char *l_initrd, +@@ -84,9 +114,6 @@ static int run_kexec_local(const char *l_image, const char *l_initrd, + if (result) + pb_log("%s: failed: (%d)\n", __func__, result); - /** - * pb_run_kexec - Run kexec with the supplied boot options. -- * -- * For the convenience of the user, tries to load both files before -- * returning error. - */ +- talloc_free(s_initrd); +- talloc_free(s_args); +- + return result; + } - int pb_run_kexec(const struct pb_kexec_data *kd) - { +@@ -99,31 +126,44 @@ int pb_run_kexec(const struct pb_kexec_data *kd) int result; -- char *l_image; -- char *l_initrd; -+ char *l_image = NULL; -+ char *l_initrd = NULL; + char *l_image = NULL; + char *l_initrd = NULL; + unsigned int clean_image = 0; + unsigned int clean_initrd = 0; @@ -1524,35 +276,31 @@ index 3f54191..0140f0e 100644 pb_log("%s: initrd: '%s'\n", __func__, kd->initrd); pb_log("%s: args: '%s'\n", __func__, kd->args); -- if (kd->image) -- l_image = pb_load_file(NULL, kd->image); -- else { -- l_image = NULL; -- pb_log("%s: error null image\n", __func__); + result = -1; + -+ if (kd->image) { + if (kd->image) { +- l_image = pb_load_file(NULL, kd->image); + l_image = pb_load_file(NULL, kd->image, &clean_image); -+ if (!l_image) -+ goto no_load; -+ } -+ -+ if (kd->initrd) { -+ l_initrd = pb_load_file(NULL, kd->initrd, &clean_initrd); -+ if (!l_initrd) + if (!l_image) +- return -1; + goto no_load; } -- l_initrd = kd->initrd ? pb_load_file(NULL, kd->initrd) : NULL; -+ if (!l_image && !l_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) +- return -1; ++ goto no_load; + } + + if (!l_image && !l_initrd) +- return -1; + goto no_load; + + result = kexec_load(l_image, l_initrd, kd->args); -- if (!l_image || (kd->initrd && !l_initrd)) -- result = -1; -- else -- result = run_kexec_local(l_image, l_initrd, kd->args); +- result = run_kexec_local(l_image, l_initrd, kd->args); +no_load: + if (clean_image) + unlink(l_image); @@ -1568,1508 +316,3 @@ index 3f54191..0140f0e 100644 return result; } -diff --git a/ui/ncurses/nc-cui.c b/ui/ncurses/nc-cui.c -index 2a4c971..aed5ff7 100644 ---- a/ui/ncurses/nc-cui.c -+++ b/ui/ncurses/nc-cui.c -@@ -30,7 +30,6 @@ - #include "ui/common/discover-client.h" - #include "nc-cui.h" - -- - static struct cui_opt_data *cod_from_item(struct pmenu_item *item) - { - return item->data; -@@ -61,6 +60,35 @@ void cui_resize(struct cui *cui) - } - - /** -+ * cui_make_item_name - Format the menu item name srting. -+ * -+ * Returns a talloc string. -+ */ -+ -+static char *cui_make_item_name(struct pmenu_item *i, struct cui_opt_data *cod) -+{ -+ char *name; -+ -+ assert(cod->name); -+ assert(cod->kd); -+ -+ name = talloc_asprintf(i, "%s:", cod->name); -+ -+ if (cod->kd->image) -+ name = talloc_asprintf_append(name, " %s", cod->kd->image); -+ -+ if (cod->kd->initrd) -+ name = talloc_asprintf_append(name, " initrd=%s", -+ cod->kd->initrd); -+ -+ if (cod->kd->args) -+ name = talloc_asprintf_append(name, " %s", cod->kd->args); -+ -+ DBGS("@%s@\n", name); -+ return name; -+} -+ -+/** - * cui_on_exit - A generic main menu ESC callback. - */ - -@@ -109,8 +137,8 @@ static int cui_run_kexec(struct pmenu_item *item) - assert(cui->current == &cui->main->scr); - assert(cui->on_kexec); - -- pb_log("%s: %s\n", __func__, cod->dev->name, cod->opt->name); -- nc_scr_status_printf(cui->current, "Booting %s...", cod->opt->name); -+ pb_log("%s: %s\n", __func__, cod->name); -+ nc_scr_status_printf(cui->current, "Booting %s...", cod->name); - - def_prog_mode(); - -@@ -141,9 +169,10 @@ static void cui_ked_on_exit(struct ked *ked, enum ked_result ked_result, - { - struct cui *cui = cui_from_arg(ked->scr.ui_ctx); - -- if (ked_result == ked_update || ked_result == ked_boot) { -+ if (ked_result == ked_update) { - struct pmenu_item *i = pmenu_find_selected(cui->main); - struct cui_opt_data *cod = cod_from_item(i); -+ char *name; - - assert(kd); - -@@ -151,7 +180,13 @@ static void cui_ked_on_exit(struct ked *ked, enum ked_result ked_result, - talloc_free(cod->kd); - cod->kd = kd; - -- pb_log("%s: updating opt '%s'\n", __func__, cod->opt->name); -+ name = cui_make_item_name(i, cod); -+ pmenu_item_replace(i, name); -+ -+ /* FIXME: need to make item visible somehow */ -+ set_current_item(cui->main->ncm, i->nci); -+ -+ pb_log("%s: updating opt '%s'\n", __func__, cod->name); - pb_log(" image '%s'\n", cod->kd->image); - pb_log(" initrd '%s'\n", cod->kd->initrd); - pb_log(" args '%s'\n", cod->kd->args); -@@ -160,20 +195,15 @@ static void cui_ked_on_exit(struct ked *ked, enum ked_result ked_result, - cui_set_current(cui, &cui->main->scr); - - talloc_free(ked); -- -- if (ked_result == ked_boot) { -- struct pmenu_item *i = pmenu_find_selected(cui->main); -- i->on_execute(i); -- } - } - - int cui_ked_run(struct pmenu_item *item) - { - struct cui *cui = cui_from_item(item); -+ struct cui_opt_data *cod = cod_from_item(item); - struct ked *ked; - -- ked = ked_init(cui, cod_from_item(item)->kd, cui_ked_on_exit); -- -+ ked = ked_init(cui, cod->kd, cui_ked_on_exit); - cui_set_current(cui, &ked->scr); - - return 0; -@@ -200,14 +230,43 @@ struct nc_scr *cui_set_current(struct cui *cui, struct nc_scr *scr) - return old; - } - -+/** -+ * cui_process_key - Process input on stdin. -+ */ -+ - static int cui_process_key(void *arg) - { - struct cui *cui = cui_from_arg(arg); - - assert(cui->current); -+ -+ ui_timer_disable(&cui->timer); - cui->current->process_key(cui->current); -+ -+ return 0; -+} -+ -+/** -+ * cui_process_js - Process joystick events. -+ */ -+ -+static int cui_process_js(void *arg) -+{ -+ struct cui *cui = cui_from_arg(arg); -+ int c; -+ -+ c = pjs_process_event(cui->pjs); -+ -+ if (c) { -+ ungetch(c); -+ cui_process_key(arg); -+ } -+ - return 0; - } -+/** -+ * cui_client_process_socket - Process a socket event from the discover server. -+ */ - - static int cui_client_process_socket(void *arg) - { -@@ -218,6 +277,24 @@ static int cui_client_process_socket(void *arg) - } - - /** -+ * cui_handle_timeout - Handle the timeout. -+ */ -+ -+static void cui_handle_timeout(struct ui_timer *timer) -+{ -+ struct cui *cui = cui_from_timer(timer); -+ struct pmenu_item *i = pmenu_find_selected(cui->main); -+ -+#if defined(DEBUG) -+ { -+ struct cui_opt_data *cod = cod_from_item(i); -+ assert(cod && (cod->opt_hash == cui->default_item)); -+ } -+#endif -+ i->on_execute(i); -+} -+ -+/** - * cui_handle_resize - Handle the term resize. - */ - -@@ -243,6 +320,46 @@ static void cui_handle_resize(struct cui *cui) - } - - /** -+ * cui_on_open - Open new item callback. -+ */ -+ -+void cui_on_open(struct pmenu *menu) -+{ -+ unsigned int insert_pt; -+ struct pmenu_item *i; -+ struct cui_opt_data *cod; -+ -+ menu->scr.unpost(&menu->scr); -+ -+ /* This disconnects items array from menu. */ -+ -+ set_menu_items(menu->ncm, NULL); -+ -+ /* Insert new items at insert_pt. */ -+ -+ insert_pt = pmenu_grow(menu, 1); -+ i = pmenu_item_alloc(menu); -+ -+ i->on_edit = cui_ked_run; -+ i->on_execute = cui_run_kexec; -+ i->data = cod = talloc_zero(i, struct cui_opt_data); -+ -+ cod->name = talloc_asprintf(i, "User item %u:", insert_pt); -+ cod->kd = talloc_zero(i, struct pb_kexec_data); -+ -+ pmenu_item_setup(menu, i, insert_pt, talloc_strdup(i, cod->name)); -+ -+ /* Re-attach the items array. */ -+ -+ set_menu_items(menu->ncm, menu->items); -+ -+ menu->scr.post(&menu->scr); -+ set_current_item(menu->ncm, i->nci); -+ -+ i->on_edit(i); -+} -+ -+/** - * cui_device_add - Client device_add callback. - * - * Creates menu_items for all the device boot_options and inserts those -@@ -284,23 +401,11 @@ static int cui_device_add(struct device *dev, void *arg) - struct pmenu_item *i; - struct cui_opt_data *cod; - char *name; -- char *description; - - /* Save the item in opt->ui_info for cui_device_remove() */ - - opt->ui_info = i = pmenu_item_alloc(cui->main); - -- name = talloc_asprintf(i, "%s: %s", opt->name, -- opt->description); -- -- description = talloc_asprintf(i, -- " kexec: image='%s' initrd='%s' args='%s'", -- opt->boot_image_file, -- opt->initrd_file, -- opt->boot_args); -- -- pmenu_item_setup(cui->main, i, insert_pt, name, description); -- - i->on_edit = cui_ked_run; - i->on_execute = cui_run_kexec; - i->data = cod = talloc(i, struct cui_opt_data); -@@ -308,23 +413,29 @@ static int cui_device_add(struct device *dev, void *arg) - cod->dev = dev; - cod->opt = opt; - cod->opt_hash = pb_opt_hash(dev, opt); -+ cod->name = opt->name; - cod->kd = talloc(i, struct pb_kexec_data); - - cod->kd->image = talloc_strdup(cod->kd, opt->boot_image_file); - cod->kd->initrd = talloc_strdup(cod->kd, opt->initrd_file); - cod->kd->args = talloc_strdup(cod->kd, opt->boot_args); - -- insert_pt++; -- -- /* If this is the default_item select it. */ -+ name = cui_make_item_name(i, cod); -+ pmenu_item_setup(cui->main, i, insert_pt, name); - -- if (cod->opt_hash == cui->default_item) -- selected = i->nci; -+ insert_pt++; - -- pb_log("%s: adding opt '%s'\n", __func__, cod->opt->name); -+ pb_log("%s: adding opt '%s'\n", __func__, cod->name); - pb_log(" image '%s'\n", cod->kd->image); - pb_log(" initrd '%s'\n", cod->kd->initrd); - pb_log(" args '%s'\n", cod->kd->args); -+ -+ /* If this is the default_item select it and start timer. */ -+ -+ if (cod->opt_hash == cui->default_item) { -+ selected = i->nci; -+ ui_timer_kick(&cui->timer); -+ } - } - - /* Re-attach the items array. */ -@@ -340,6 +451,9 @@ static int cui_device_add(struct device *dev, void *arg) - item_count(cui->main->ncm) + 1); - } - -+ /* FIXME: need to make item visible somehow */ -+ menu_driver(cui->main->ncm, REQ_SCR_UPAGE); -+ menu_driver(cui->main->ncm, REQ_SCR_DPAGE); - set_current_item(cui->main->ncm, selected); - - if (cui->current == &cui->main->scr) -@@ -375,9 +489,15 @@ static void cui_device_remove(struct device *dev, void *arg) - - list_for_each_entry(&dev->boot_options, opt, list) { - struct pmenu_item *i = pmenu_item_from_arg(opt->ui_info); -+ struct cui_opt_data *cod = cod_from_item(i); - -- assert(pb_protocol_device_cmp(dev, cod_from_item(i)->dev)); -+ assert(pb_protocol_device_cmp(dev, cod->dev)); - pmenu_remove(cui->main, i); -+ -+ /* If this is the default_item disable timer. */ -+ -+ if (cod->opt_hash == cui->default_item) -+ ui_timer_disable(&cui->timer); - } - - /* Re-attach the items array. */ -@@ -397,10 +517,6 @@ static void cui_device_remove(struct device *dev, void *arg) - cui->current->post(cui->current); - } - --/** -- * cui_client_process_socket - Process a socket event from the discover server. -- */ -- - static struct discover_client_ops cui_client_ops = { - .device_add = cui_device_add, - .device_remove = cui_device_remove, -@@ -417,7 +533,8 @@ static struct discover_client_ops cui_client_ops = { - */ - - struct cui *cui_init(void* platform_info, -- int (*on_kexec)(struct cui *, struct cui_opt_data *)) -+ int (*on_kexec)(struct cui *, struct cui_opt_data *), -+ int (*js_map)(const struct js_event *e)) - { - struct cui *cui; - struct discover_client *client; -@@ -434,6 +551,7 @@ struct cui *cui_init(void* platform_info, - cui->c_sig = pb_cui_sig; - cui->platform_info = platform_info; - cui->on_kexec = on_kexec; -+ cui->timer.handle_timeout = cui_handle_timeout; - - /* Loop here for scripts that just started the server. */ - -@@ -462,6 +580,15 @@ struct cui *cui_init(void* platform_info, - - waiter_register(STDIN_FILENO, WAIT_IN, cui_process_key, cui); - -+ if (js_map) { -+ -+ cui->pjs = pjs_init(cui, js_map); -+ -+ if (cui->pjs) -+ waiter_register(pjs_get_fd(cui->pjs), WAIT_IN, -+ cui_process_js, cui); -+ } -+ - return cui; - - fail_client_init: -@@ -501,6 +628,8 @@ int cui_run(struct cui *cui, struct pmenu *main, unsigned int default_item) - if (cui->abort) - break; - -+ ui_timer_process_sig(&cui->timer); -+ - while (cui->resize) { - cui->resize = 0; - cui_handle_resize(cui); -diff --git a/ui/ncurses/nc-cui.h b/ui/ncurses/nc-cui.h -index 668776e..94fef6b 100644 ---- a/ui/ncurses/nc-cui.h -+++ b/ui/ncurses/nc-cui.h -@@ -21,15 +21,19 @@ - - #include - -+#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 -+#include /* This must be included before ncurses.h */ - #include - - #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 -+#include /* This must be included before ncurses.h */ - #include - - #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 /* This must be included before ncurses.h */ - #include - - #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 -+#include -+#include -+#include -+#include -+#include -+ -+#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 - #include - #include -+#include - - #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); diff --git a/utils/petitboot/patches/020-petitboot-fix-pb-twin.diff b/utils/petitboot/patches/020-petitboot-fix-pb-twin.diff index e8589c5c1..ed8f93d3a 100644 --- a/utils/petitboot/patches/020-petitboot-fix-pb-twin.diff +++ b/utils/petitboot/patches/020-petitboot-fix-pb-twin.diff @@ -6,9 +6,9 @@ Update the PS3 twin GUI program to work with petitboot-multi-ui. Signed-off-by: Geoff Levand --- - rules.mk | 2 +- - ui/twin/ps3-twin.c | 49 +++++++++++++++++++++++++++++++++++++++++++++++++ - 2 files changed, 50 insertions(+), 1 deletion(-) + rules.mk | 13 + ui/twin/ps3-twin.c | 1434 +++++++++++++++++++++++++++++++++++++++++++++++++++++ + 2 files changed, 1443 insertions(+), 4 deletions(-) --- a/rules.mk +++ b/rules.mk @@ -21,39 +21,1373 @@ Signed-off-by: Geoff Levand # 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 + */ + ++#if defined(HAVE_CONFIG_H) ++#include "config.h" ++#endif ++ +#define _GNU_SOURCE -+ +#include ++#include ++#include +#include -+ ++#include ++#include ++#include +#include +#include -+static twin_fbdev_t *pboot_fbdev; ++#include ++#include ++#include ++#include ++#if defined(TWIN_X11) ++# include ++#endif ++#include + +#include "log/log.h" ++#include "talloc/talloc.h" ++#include "waiter/waiter.h" ++#include "ui/common/discover-client.h" ++#include "ui/common/ps3.h" ++#include "ui/common/timer.h" ++ ++//------------------------------------------------------------------------------ ++// twin-ui.c/h ++ ++#define DBG(fmt, args...) pb_log("DBG: " fmt, ## args) ++#define DBGS(fmt, args...) \ ++ pb_log("DBG:%s:%d: " fmt, __func__, __LINE__, ## args) ++ ++/* control to keyboard mappings for the sixaxis controller */ ++uint8_t sixaxis_map[] = { ++ 0, /* 0 Select */ ++ 0, /* 1 L3 */ ++ 0, /* 2 R3 */ ++ 0, /* 3 Start */ ++ KEY_UP, /* 4 Dpad Up */ ++ KEY_RIGHT, /* 5 Dpad Right */ ++ KEY_DOWN, /* 6 Dpad Down */ ++ KEY_LEFT, /* 7 Dpad Left */ ++ 0, /* 8 L2 */ ++ 0, /* 9 R2 */ ++ 0, /* 10 L1 */ ++ 0, /* 11 R1 */ ++ 0, /* 12 Triangle */ ++ KEY_ENTER, /* 13 Circle */ ++ 0, /* 14 Cross */ ++ KEY_DELETE, /* 15 Square */ ++ 0, /* 16 PS Button */ ++ 0, /* 17 nothing */ ++ 0, /* 18 nothing */ ++}; ++ ++#define PBOOT_LEFT_PANE_SIZE 160 ++#define PBOOT_LEFT_PANE_COLOR 0x80000000 ++#define PBOOT_LEFT_LINE_COLOR 0xff000000 ++ ++#define PBOOT_LEFT_FOCUS_WIDTH 80 ++#define PBOOT_LEFT_FOCUS_HEIGHT 80 ++#define PBOOT_LEFT_FOCUS_XOFF 40 ++#define PBOOT_LEFT_FOCUS_YOFF 40 ++#define PBOOT_LEFT_FOCUS_XRAD (6 * TWIN_FIXED_ONE) ++#define PBOOT_LEFT_FOCUS_YRAD (6 * TWIN_FIXED_ONE) ++ ++#define PBOOT_RIGHT_FOCUS_XOFF 20 ++#define PBOOT_RIGHT_FOCUS_YOFF 60 ++#define PBOOT_RIGHT_FOCUS_HEIGHT 80 ++#define PBOOT_RIGHT_FOCUS_XRAD (6 * TWIN_FIXED_ONE) ++#define PBOOT_RIGHT_FOCUS_YRAD (6 * TWIN_FIXED_ONE) ++ ++#define PBOOT_LEFT_ICON_WIDTH 64 ++#define PBOOT_LEFT_ICON_HEIGHT 64 ++#define PBOOT_LEFT_ICON_XOFF 50 ++#define PBOOT_LEFT_ICON_YOFF 50 ++#define PBOOT_LEFT_ICON_STRIDE 100 ++ ++#define PBOOT_RIGHT_OPTION_LMARGIN 30 ++#define PBOOT_RIGHT_OPTION_RMARGIN 30 ++#define PBOOT_RIGHT_OPTION_TMARGIN 70 ++#define PBOOT_RIGHT_OPTION_HEIGHT 64 ++#define PBOOT_RIGHT_OPTION_STRIDE 100 ++#define PBOOT_RIGHT_TITLE_TEXT_SIZE (30 * TWIN_FIXED_ONE) ++#define PBOOT_RIGHT_SUBTITLE_TEXT_SIZE (18 * TWIN_FIXED_ONE) ++#define PBOOT_RIGHT_TITLE_XOFFSET 80 ++#define PBOOT_RIGHT_TITLE_YOFFSET 30 ++#define PBOOT_RIGHT_SUBTITLE_XOFFSET 100 ++#define PBOOT_RIGHT_SUBTITLE_YOFFSET 50 ++#define PBOOT_RIGHT_BADGE_XOFFSET 2 ++#define PBOOT_RIGHT_BADGE_YOFFSET 0 ++ ++ ++#define PBOOT_RIGHT_TITLE_COLOR 0xff000000 ++#define PBOOT_RIGHT_SUBTITLE_COLOR 0xff400000 ++ ++#define PBOOT_FOCUS_COLOR 0x10404040 ++ ++#define PBOOT_STATUS_PANE_COLOR 0x60606060 ++#define PBOOT_STATUS_PANE_HEIGHT 20 ++#define PBOOT_STATUS_PANE_XYMARGIN 20 ++#define PBOOT_STATUS_TEXT_MARGIN 10 ++#define PBOOT_STATUS_TEXT_SIZE (16 * TWIN_FIXED_ONE) ++#define PBOOT_STATUS_TEXT_COLOR 0xff000000 ++ ++#define PBOOT_MAX_OPTION 100 ++#define PBOOT_MAX_DEV 10 ++ ++struct pbt_option ++{ ++ char *title; ++ char *subtitle; ++ twin_pixmap_t *badge; ++ twin_pixmap_t *cache; ++ twin_rect_t box; ++ void *data; ++}; ++ ++struct pbt_device ++{ ++ char *id; ++ twin_pixmap_t *badge; ++ twin_rect_t box; ++ int option_count; ++ struct pbt_option options[PBOOT_MAX_OPTION]; ++}; ++ ++enum pbt_sig { ++ pbt_scr_sig = 111, ++ pbt_menu_sig = 222, ++ pbt_pane_sig = 333, ++ pb_removed_sig = -555, ++}; ++ ++struct pbt_cursor { ++ twin_pixmap_t *pixmap; ++ int hx; ++ int hy; ++}; ++ ++struct pbt_scr { ++ enum pbt_sig sig; ++ struct pbt_cursor cursor; ++ twin_screen_t *tscreen; ++#if defined(USE_TWIN_X11) ++ twin_x11_t *x11; ++#else ++ twin_fbdev_t *fbdev; ++#endif ++}; ++ ++struct pbt_frame { ++ twin_label_t *title; ++ twin_label_t *help; ++ twin_label_t *status; ++}; ++ ++/** ++ * struct pbt_pane - A twin menu pane. ++ */ ++ ++struct pbt_pane { ++ enum pbt_sig sig; ++ twin_window_t *window; ++ twin_rect_t focus_box; ++ int focus_start; ++ int focus_target; ++ int focus_curindex; ++ int mouse_target; ++ int has_focus; ++}; ++ ++/** ++ * struct pbt_menu - A twin menu. ++ * @sig: Sanity check signature. ++ * @scr: The screen this menu is associated with. ++ * @dp: The device pane instance. ++ * @op: The option pane instance. ++ */ ++ ++struct pbt_menu { ++ enum pbt_sig sig; ++ struct pbt_scr *scr; ++ struct pbt_pane *dp; ++ struct pbt_pane *op; ++}; ++ ++//============================================================================== ++// helper ++//============================================================================== ++ ++static struct pbt_scr *pbt_scr_from_tscreen(twin_screen_t *tscreen); ++ ++static struct pbt_menu *pbt_menu_from_twindow(twin_window_t *twindow) ++{ ++ struct pbt_menu *menu = twindow->client_data; ++ ++ assert(menu); ++ assert(menu->sig == pbt_menu_sig); ++ return menu; ++} ++ ++/* ++static struct pbt_menu *pbt_menu_from_arg(void *arg) ++{ ++ struct pbt_menu *menu = arg; ++ ++ assert(menu); ++ assert(menu->sig == pbt_menu_sig); ++ return menu; ++} ++*/ ++ ++static struct pbt_pane *pbt_pane_from_arg(void *arg) ++{ ++ struct pbt_pane *pane = arg; ++ ++ assert(pane); ++ assert(pane->sig == pbt_pane_sig); ++ return pane; ++} ++ ++static twin_bool_t pbt_rect_intersect(twin_rect_t r1, twin_rect_t r2) ++{ ++ // FIXME: move this to twin!!! ++ return !(r1.left > r2.right || ++ r1.right < r2.left || ++ r1.top > r2.bottom || ++ r1.bottom < r2.top); ++} ++ ++static twin_pixmap_t * pbt_load_background(twin_screen_t *tscreen) ++{ ++ static const char *bgd = PB_ARTWORK_PATH "/background.jpg"; ++ twin_pixmap_t *rawpix; ++ twin_pixmap_t *scaledpix; ++ ++ rawpix = twin_jpeg_to_pixmap(bgd, TWIN_ARGB32); ++ ++ if (!rawpix) { ++ pb_log("%s: loading image %s failed\n", __func__, bgd); ++ return twin_make_pattern(); ++ } ++ ++ if (tscreen->height == rawpix->height && ++ tscreen->width == rawpix->width) ++ return rawpix; ++ ++ /* Scale as needed. */ ++ ++ twin_fixed_t sx, sy; ++ twin_operand_t srcop; ++ ++ scaledpix = twin_pixmap_create(TWIN_ARGB32, ++ tscreen->width, ++ tscreen->height); ++ if (!scaledpix) { ++ pb_log("%s: scale %s failed\n", __func__, bgd); ++ twin_pixmap_destroy(rawpix); ++ return twin_make_pattern(); ++ } ++ sx = twin_fixed_div(twin_int_to_fixed(rawpix->width), ++ twin_int_to_fixed(tscreen->width)); ++ sy = twin_fixed_div(twin_int_to_fixed(rawpix->height), ++ twin_int_to_fixed(tscreen->height)); ++ ++ twin_matrix_scale(&rawpix->transform, sx, sy); ++ srcop.source_kind = TWIN_PIXMAP; ++ srcop.u.pixmap = rawpix; ++ twin_composite(scaledpix, 0, 0, &srcop, 0, 0, ++ NULL, 0, 0, TWIN_SOURCE, ++ tscreen->width, tscreen->height); ++ ++ twin_pixmap_destroy(rawpix); ++ return scaledpix; ++} ++ ++//============================================================================== ++// option ++//============================================================================== ++ ++static void pbt_option_execute(struct pbt_menu *menu) ++{ ++#if 0 ++ pboot_device_t *dev = pboot_devices[pboot_dev_sel]; ++ pboot_option_t *opt = &dev->options[menu->op->focus_curindex]; ++ ++ pb_log("Selected device %s\n", opt->title); ++ pboot_message("booting %s...", opt->title); ++ ++ /* Give user feedback, make sure errors and panics will be seen */ ++ pboot_exec_option(opt->data); ++#endif ++} ++ ++//============================================================================== ++// device ++//============================================================================== ++ ++//============================================================================== ++// scr ++//============================================================================== ++ ++static twin_bool_t pbt_scr_event(twin_screen_t *tscreen, twin_event_t *event) ++{ ++ struct pbt_scr *scr = pbt_scr_from_tscreen(tscreen); ++ ++ switch(event->kind) { ++ case TwinEventEnter: ++ case TwinEventMotion: ++ case TwinEventLeave: ++ case TwinEventButtonDown: ++ case TwinEventButtonUp: ++ if (scr->cursor.pixmap) ++ twin_screen_set_cursor(tscreen, scr->cursor.pixmap, ++ scr->cursor.hx, scr->cursor.hy); ++ break; ++ case TwinEventJoyButton: ++ /* map joystick events into key events */ ++ if (event->u.js.control >= sizeof(sixaxis_map)) ++ break; ++ ++ event->u.key.key = sixaxis_map[event->u.js.control]; ++ if (event->u.js.value == 0) { ++ event->kind = TwinEventKeyUp; ++ break; ++ } else { ++ event->kind = TwinEventKeyDown; ++ } ++ /* fall through.. */ ++ case TwinEventKeyDown: ++ switch(event->u.key.key) { ++ case KEY_0: ++ return TWIN_TRUE; ++ case KEY_BACKSPACE: ++ case KEY_DELETE: ++ return TWIN_FALSE; ++ } ++ case TwinEventKeyUp: ++ twin_screen_set_cursor(tscreen, NULL, 0, 0); ++ break; ++ default: ++ break; ++ } ++ return TWIN_FALSE; ++} ++ ++//============================================================================== ++// pane ++//============================================================================== ++ ++static int pbt_pane_has_focus(const struct pbt_pane *pane) ++{ ++ return pane->has_focus; ++} ++ ++static twin_time_t pbt_pane_timeout(twin_time_t now, void *closure) ++{ ++ const int accel[11] = { 7, 4, 2, 1, 1, 1, 1, 1, 2, 2, 3 }; ++ struct pbt_pane *pane = pbt_pane_from_arg(closure); ++ int dir = 1, dist, pos; ++ ++ dist = abs(pane->focus_target - pane->focus_start); ++ dir = dist > 5 ? 5 : dist; ++ pos = pane->focus_target - (int)pane->focus_box.top; ++ if (pos == 0) { ++ return -1; ++ } ++ if (pos < 0) { ++ dir = -dir; ++ pos = -pos; ++ } ++ twin_window_damage(pane->window, ++ pane->focus_box.left, ++ pane->focus_box.top, ++ pane->focus_box.right, ++ pane->focus_box.bottom); ++ ++ pane->focus_box.top += dir; ++ pane->focus_box.bottom += dir; ++ ++ twin_window_damage(pane->window, ++ pane->focus_box.left, ++ pane->focus_box.top, ++ pane->focus_box.right, ++ pane->focus_box.bottom); ++ ++ twin_window_queue_paint(pane->window); ++ ++ return accel[(pos * 10) / dist]; ++} ++ ++//============================================================================== ++// menu ++//============================================================================== ++ ++/** ++ * pbt_menu_set_focus - Set the menu's pane of focus. ++ * @menu: The menu to operate on. ++ * @pane: The pane that will have the focus. ++ */ ++ ++static void pbt_menu_set_focus(struct pbt_menu *menu, struct pbt_pane *pane) ++{ ++ assert(!pane->has_focus); ++ ++ if (pane == menu->dp) { ++ menu->dp->has_focus = 1; ++ menu->op->has_focus = 0; ++ } else if (pane == menu->op) { ++ menu->dp->has_focus = 0; ++ menu->op->has_focus = 1; ++// pbt_menu_set_option_focus(menu, 0); ++ } else ++ assert(0 && "bad logic"); ++} ++ ++static void pbt_menu_pane_select(struct pbt_menu *menu, struct pbt_pane *pane) ++{ ++ if(pbt_pane_has_focus(pane)) ++ return; ++ ++ twin_screen_set_active(menu->scr->tscreen, pane->window->pixmap); ++ ++ twin_window_damage(menu->dp->window, ++ menu->dp->focus_box.left, ++ menu->dp->focus_box.top, ++ menu->dp->focus_box.right, ++ menu->dp->focus_box.bottom); ++ twin_window_damage(menu->op->window, ++ menu->op->focus_box.left, ++ menu->op->focus_box.top, ++ menu->op->focus_box.right, ++ menu->op->focus_box.bottom); ++ ++ twin_window_queue_paint(menu->dp->window); ++ twin_window_queue_paint(menu->op->window); ++ ++ pbt_menu_set_focus(menu, pane); ++} ++ ++ ++static struct pbt_pane *pbt_device_pane_create(struct pbt_menu *menu); ++static struct pbt_pane *pbt_option_pane_create(struct pbt_menu *menu); ++ ++static struct pbt_menu *pbt_menu_create(void *ctx, struct pbt_scr *scr) ++{ ++ struct pbt_menu *menu = talloc_zero(ctx, struct pbt_menu); ++ ++ if (!menu) ++ return NULL; ++ ++ assert(scr && scr->sig == pbt_scr_sig); ++ ++ menu->sig = pbt_menu_sig; ++ menu->scr = scr; ++ ++ menu->dp = pbt_device_pane_create(menu); ++ ++ if (!menu->dp) ++ goto fail_dp; ++ ++ menu->op = pbt_option_pane_create(menu); ++ ++ if (!menu->op) ++ goto fail_op; ++ ++ return menu; ++ ++fail_op: ++ //clean dp ++fail_dp: ++ talloc_free(menu); ++ return NULL; ++} ++ ++//============================================================================== ++// device_pane ++//============================================================================== ++ ++static void pbt_device_pane_draw(twin_window_t *window) ++{ ++ struct pbt_pane *dp = pbt_menu_from_twindow(window)->dp; ++ twin_pixmap_t *px = window->pixmap; ++ twin_path_t *path; ++ twin_fixed_t x, y, w, h; ++ int i; ++ ++ /* Fill background */ ++ twin_fill(px, PBOOT_LEFT_PANE_COLOR, TWIN_SOURCE, 0, 0, px->width, ++ px->height); ++ ++ /* Create a path for use later */ ++ path = twin_path_create(); ++ assert(path); ++ ++ /* Draw right line if needed */ ++ if (px->clip.right > (PBOOT_LEFT_PANE_SIZE - 4)) { ++ x = twin_int_to_fixed(PBOOT_LEFT_PANE_SIZE - 4); ++ y = twin_int_to_fixed(px->height); ++ twin_path_rectangle(path, x, 0, 0x40000, y); ++ twin_paint_path(px, PBOOT_LEFT_LINE_COLOR, path); ++ twin_path_empty(path); ++ } ++ ++ /* Draw focus box */ ++ if (dp->focus_curindex >= 0 && ++ pbt_rect_intersect(dp->focus_box, px->clip)) { ++ x = twin_int_to_fixed(dp->focus_box.left + 2); ++ y = twin_int_to_fixed(dp->focus_box.top + 2); ++ w = twin_int_to_fixed(dp->focus_box.right - ++ dp->focus_box.left - 4); ++ h = twin_int_to_fixed(dp->focus_box.bottom - ++ dp->focus_box.top - 4); ++ twin_path_rounded_rectangle(path, x, y, w, h, ++ PBOOT_LEFT_FOCUS_XRAD, ++ PBOOT_LEFT_FOCUS_YRAD); ++ if (pbt_pane_has_focus(dp)) ++ twin_paint_path(px, PBOOT_FOCUS_COLOR, path); ++ else ++ twin_paint_stroke(px, PBOOT_FOCUS_COLOR, path, ++ 4 * TWIN_FIXED_ONE); ++ } ++ ++#if 0 ++ /* Draw icons */ ++ for (i = 0; i < pboot_dev_count; i++) { ++ pboot_device_t *dev = pboot_devices[i]; ++ twin_operand_t src; ++ ++ if (!twin_rect_intersect(dev->box, px->clip)) ++ continue; ++ ++ src.source_kind = TWIN_PIXMAP; ++ src.u.pixmap = dev->badge; ++ ++ twin_composite(px, dev->box.left, dev->box.top, ++ &src, 0, 0, NULL, 0, 0, TWIN_OVER, ++ dev->box.right - dev->box.left, ++ dev->box.bottom - dev->box.top); ++ } ++#endif ++ ++ /* Destroy path */ ++ twin_path_destroy(path); ++} ++ ++ ++static void pbt_device_pane_set_focus(struct pbt_menu *menu, int index) ++{ ++#if 0 ++ if (index >= pboot_dev_count) ++ return; ++#endif ++ ++ menu->dp->focus_start = menu->dp->focus_box.top; ++ ++ if (index < 0) ++ menu->dp->focus_target = 0 - PBOOT_LEFT_FOCUS_HEIGHT; ++ else ++ menu->dp->focus_target = PBOOT_LEFT_FOCUS_YOFF + ++ PBOOT_LEFT_ICON_STRIDE * index; ++ ++ menu->dp->focus_curindex = index; ++ ++ twin_set_timeout(pbt_pane_timeout, 0, menu->dp); ++} ++ ++static void pbt_device_pane_mousetrack(struct pbt_menu *menu, twin_coord_t x, ++ twin_coord_t y) ++{ ++ int candidate = -1; ++ twin_coord_t icon_top; ++ ++ if (x < PBOOT_LEFT_ICON_XOFF || ++ x > (PBOOT_LEFT_ICON_XOFF + PBOOT_LEFT_ICON_WIDTH)) ++ goto miss; ++ ++ if (y < PBOOT_LEFT_ICON_YOFF) ++ goto miss; ++ ++ candidate = (y - PBOOT_LEFT_ICON_YOFF) / PBOOT_LEFT_ICON_STRIDE; ++ ++#if 0 ++ if (candidate >= pboot_dev_count) { ++ candidate = -1; ++ goto miss; ++ } ++#endif ++ if (candidate == menu->dp->mouse_target) ++ return; ++ ++ icon_top = PBOOT_LEFT_ICON_YOFF + candidate * PBOOT_LEFT_ICON_STRIDE; ++ ++ if (y > (icon_top + PBOOT_LEFT_ICON_HEIGHT)) { ++ candidate = -1; ++ goto miss; ++ } ++ ++ /* The mouse hit an icon that wasn't the same ++ * as the previous one, trigger a focus change. ++ */ ++ ++ pbt_device_pane_set_focus(menu, candidate); ++ ++ miss: ++ menu->dp->mouse_target = candidate; ++} ++ ++static twin_bool_t pbt_device_pane_event(twin_window_t *window, ++ twin_event_t *event) ++{ ++ struct pbt_menu *menu = pbt_menu_from_twindow(window); ++ ++ /* filter out all mouse events */ ++ switch(event->kind) { ++ case TwinEventEnter: ++ case TwinEventMotion: ++ case TwinEventLeave: ++ pbt_menu_pane_select(menu, menu->dp); ++ pbt_device_pane_mousetrack(menu, event->u.pointer.x, ++ event->u.pointer.y); ++ return TWIN_TRUE; ++ case TwinEventButtonDown: ++ case TwinEventButtonUp: ++ return TWIN_TRUE; ++ case TwinEventKeyDown: ++ switch(event->u.key.key) { ++ case KEY_UP: ++ if (menu->dp->focus_curindex > 0) ++ pbt_device_pane_set_focus(menu, ++ menu->dp->focus_curindex - 1); ++ return TWIN_TRUE; ++ case KEY_DOWN: ++ pbt_device_pane_set_focus(menu, menu->dp->focus_curindex + 1); ++ return TWIN_TRUE; ++ case KEY_RIGHT: ++ pbt_menu_pane_select(menu, menu->op); ++ return TWIN_TRUE; ++ default: ++ break; ++ } ++ break; ++ default: ++ break; ++ } ++ return TWIN_FALSE; ++} ++ ++static struct pbt_pane *pbt_device_pane_create(struct pbt_menu *menu) ++{ ++ struct pbt_pane *pane = talloc_zero(menu, struct pbt_pane); ++ ++ if (!pane) ++ return NULL; ++ ++ pane->sig = pbt_pane_sig; ++ pane->window = twin_window_create(menu->scr->tscreen, TWIN_ARGB32, ++ TwinWindowPlain, 0, 0, PBOOT_LEFT_PANE_SIZE, ++ menu->scr->tscreen->height); ++ ++ if (!pane->window) ++ goto fail_window; ++ ++ pane->window->draw = pbt_device_pane_draw; ++ pane->window->event = pbt_device_pane_event; ++ pane->window->client_data = menu; ++ ++ pane->focus_curindex = -1; ++ pane->focus_box.left = PBOOT_LEFT_FOCUS_XOFF; ++ pane->focus_box.top = -2 * PBOOT_LEFT_FOCUS_HEIGHT; ++ pane->focus_box.right = pane->focus_box.left + PBOOT_LEFT_FOCUS_WIDTH; ++ pane->focus_box.bottom = pane->focus_box.top + PBOOT_LEFT_FOCUS_HEIGHT; ++ ++ pane->mouse_target = -1; ++ ++ twin_window_show(pane->window); ++ twin_window_queue_paint(pane->window); ++ ++ return pane; ++ ++fail_window: ++ talloc_free(pane); ++ return NULL; ++} ++ ++//============================================================================== ++// option_pane ++//============================================================================== ++ ++static void pbt_option_pane_set_focus(struct pbt_menu *menu, int index) ++{ ++#if 0 ++ pboot_device_t *dev; ++ ++ if (pboot_dev_sel < 0 || pboot_dev_sel >= pboot_dev_count) ++ return; ++ dev = pboot_devices[pboot_dev_sel]; ++ if (index < 0 || index >= dev->option_count) ++ return; ++#endif ++ ++ menu->op->focus_start = menu->op->focus_box.top; ++ menu->op->focus_target = PBOOT_RIGHT_FOCUS_YOFF + ++ PBOOT_RIGHT_OPTION_STRIDE * index; ++ menu->op->focus_curindex = index; ++ ++ twin_set_timeout(pbt_pane_timeout, 0, menu->op); ++} ++ ++static void pbt_option_pane_draw(twin_window_t *window) ++{ ++ struct pbt_pane *op = pbt_menu_from_twindow(window)->op; ++ twin_pixmap_t *px = window->pixmap; ++// pboot_device_t *dev; ++ twin_path_t *path; ++ twin_fixed_t x, y, w, h; ++ int i; ++ ++ /* Fill background */ ++ twin_fill(px, 0x00000000, TWIN_SOURCE, 0, 0, px->width, px->height); ++ ++ /* Nothing to draw, return */ ++// if (pboot_dev_sel < 0) ++// return; ++ ++ /* Create a path for use later */ ++ path = twin_path_create(); ++ assert(path); ++ ++ /* Draw focus box */ ++ if (op->focus_curindex >= 0 && ++ pbt_rect_intersect(op->focus_box, px->clip)) { ++ x = twin_int_to_fixed(op->focus_box.left + 2); ++ y = twin_int_to_fixed(op->focus_box.top + 2); ++ w = twin_int_to_fixed(op->focus_box.right - ++ op->focus_box.left - 4); ++ h = twin_int_to_fixed(op->focus_box.bottom - ++ op->focus_box.top - 4); ++ twin_path_rounded_rectangle(path, x, y, w, h, ++ PBOOT_RIGHT_FOCUS_XRAD, ++ PBOOT_RIGHT_FOCUS_YRAD); ++ if (pbt_pane_has_focus(op)) ++ twin_paint_path(px, PBOOT_FOCUS_COLOR, path); ++ else ++ twin_paint_stroke(px, PBOOT_FOCUS_COLOR, path, ++ 4 * TWIN_FIXED_ONE); ++ } ++ ++ /* Get device and iterate through options */ ++/* ++ dev = pboot_devices[pboot_dev_sel]; ++ for (i = 0; i < dev->option_count; i++) { ++ pboot_option_t *opt = &dev->options[i]; ++ twin_operand_t src; ++ ++ if (opt->title == NULL) ++ continue; ++ if (!pbt_rect_intersect(opt->box, px->clip)) ++ continue; ++ if (opt->cache == NULL) ++ pboot_draw_option_cache(dev, opt, i); ++ ++ src.source_kind = TWIN_PIXMAP; ++ src.u.pixmap = opt->cache; ++ ++ twin_composite(px, opt->box.left, opt->box.top, ++ &src, 0, 0, NULL, 0, 0, TWIN_OVER, ++ opt->box.right - opt->box.left, ++ opt->box.bottom - opt->box.top); ++ } ++*/ ++ /* Destroy path */ ++ twin_path_destroy(path); ++} ++ ++static void pbt_option_pane_mousetrack(struct pbt_menu *menu, twin_coord_t x, ++ twin_coord_t y) ++{ ++ int candidate = -1; ++ ++ if (y < PBOOT_RIGHT_OPTION_TMARGIN) ++ goto miss; ++ ++#if 0 ++ pboot_device_t *dev; ++ pboot_option_t *opt; ++ ++ if (pboot_dev_sel < 0 || pboot_dev_sel >= pboot_dev_count) ++ return; ++ ++ dev = pboot_devices[pboot_dev_sel]; ++ ++ candidate = (y - PBOOT_RIGHT_OPTION_TMARGIN) / ++ PBOOT_RIGHT_OPTION_STRIDE; ++ ++ if (candidate >= dev->option_count) { ++ candidate = -1; ++ goto miss; ++ } ++ ++ if (candidate == op->mouse_target) ++ return; ++ ++ opt = &dev->options[candidate]; ++ ++ if (x < opt->box.left || x > opt->box.right || ++ y < opt->box.top || y > opt->box.bottom) { ++ candidate = -1; ++ goto miss; ++ } ++#endif ++ ++ pbt_option_pane_set_focus(menu, candidate); ++ ++miss: ++ menu->op->mouse_target = candidate; ++} ++ ++static twin_bool_t pbt_option_pane_event(twin_window_t *window, ++ twin_event_t *event) ++{ ++ struct pbt_menu *menu = pbt_menu_from_twindow(window); ++ ++ /* filter out all mouse events */ ++ switch(event->kind) { ++ case TwinEventEnter: ++ case TwinEventMotion: ++ case TwinEventLeave: ++ pbt_menu_pane_select(menu, menu->op); ++ pbt_option_pane_mousetrack(menu, event->u.pointer.x, ++ event->u.pointer.y); ++ return TWIN_TRUE; ++ case TwinEventButtonDown: ++ pbt_menu_pane_select(menu, menu->op); ++ pbt_option_pane_mousetrack(menu, event->u.pointer.x, ++ event->u.pointer.y); ++ pbt_option_execute(menu); ++ return TWIN_TRUE; ++ case TwinEventButtonUp: ++ return TWIN_TRUE; ++ case TwinEventKeyDown: ++ switch(event->u.key.key) { ++ case KEY_UP: ++ //pbt_menu_set_option_focus(menu, menu->op->focus_curindex - 1); ++ return TWIN_TRUE; ++ case KEY_DOWN: ++ //pbt_menu_set_option_focus(menu, menu->op->focus_curindex + 1); ++ return TWIN_TRUE; ++ case KEY_LEFT: ++ pbt_menu_pane_select(menu, menu->dp); ++ return TWIN_TRUE; ++ case KEY_ENTER: ++ pbt_option_execute(menu); ++ default: ++ break; ++ } ++ break; ++ default: ++ break; ++ } ++ return TWIN_FALSE; ++} ++ ++static struct pbt_pane *pbt_option_pane_create(struct pbt_menu *menu) ++{ ++ struct pbt_pane *pane = talloc_zero(menu, struct pbt_pane); ++ ++ if (!pane) ++ return NULL; ++ ++ pane->sig = pbt_pane_sig; ++ pane->window = twin_window_create(menu->scr->tscreen, TWIN_ARGB32, ++ TwinWindowPlain, PBOOT_LEFT_PANE_SIZE, 0, ++ menu->scr->tscreen->width - PBOOT_LEFT_PANE_SIZE, ++ menu->scr->tscreen->height); ++ ++ if (!pane->window) ++ goto fail_window; ++ ++ pane->window->draw = pbt_option_pane_draw; ++ pane->window->event = pbt_option_pane_event; ++ pane->window->client_data = menu; ++ ++ pane->focus_curindex = -1; ++ pane->focus_box.left = PBOOT_RIGHT_FOCUS_XOFF; ++ pane->focus_box.top = -2 * PBOOT_RIGHT_FOCUS_HEIGHT; ++ pane->focus_box.right = pane->window->pixmap->width ++ - 2 * PBOOT_RIGHT_FOCUS_XOFF; ++ pane->focus_box.bottom = pane->focus_box.top + PBOOT_RIGHT_FOCUS_HEIGHT; ++ ++ pane->mouse_target = -1; ++ ++ twin_window_show(pane->window); ++ twin_window_queue_paint(pane->window); ++ ++ return pane; ++ ++fail_window: ++ talloc_free(pane); ++ return NULL; ++} ++ ++//============================================================================== ++// junk ++//============================================================================== ++ ++#if 0 ++ ++static pboot_device_t *pboot_devices[PBOOT_MAX_DEV]; ++static int pboot_dev_count; ++static int pboot_dev_sel = -1; ++ ++ ++ ++ ++static int pboot_vmode_change = -1; ++ ++static void pboot_message(const char *fmt, ...) ++{ ++ va_list ap; ++ char *msg; ++ ++ va_start(ap, fmt); ++ vasprintf(&msg, fmt, ap); ++ va_end(ap); ++ ++ pb_log(msg); ++} ++ ++static void pboot_draw_option_cache(pboot_device_t *dev, pboot_option_t *opt, ++ int index) ++{ ++ twin_pixmap_t *px; ++ twin_path_t *path; ++ twin_fixed_t tx, ty; ++ ++ /* Create pixmap */ ++ px = twin_pixmap_create(TWIN_ARGB32, opt->box.right - opt->box.left, ++ opt->box.bottom - opt->box.top); ++ assert(px); ++ opt->cache = px; ++ ++ /* Fill background */ ++ twin_fill(px, 0x00000000, TWIN_SOURCE, 0, 0, px->width, px->height); ++ ++ /* Allocate a path for drawing */ ++ path = twin_path_create(); ++ assert(path); ++ ++#if 0 ++ /* TEST - Bounding rectangle */ ++ twin_path_rectangle(path, 0, 0, ++ twin_int_to_fixed(px->width), ++ twin_int_to_fixed(px->height)); ++ twin_paint_path(px, PBOOT_RIGHT_TITLE_COLOR, path); ++ twin_path_empty(path); ++ twin_fill(px, 0x00000000, TWIN_SOURCE, 2, 2, ++ px->width - 3, px->height - 3); ++#endif ++ ++ /* Draw texts */ ++ twin_path_set_font_size(path, PBOOT_RIGHT_TITLE_TEXT_SIZE); ++ twin_path_set_font_style(path, TWIN_TEXT_UNHINTED); ++ tx = twin_int_to_fixed(PBOOT_RIGHT_TITLE_XOFFSET); ++ ty = twin_int_to_fixed(PBOOT_RIGHT_TITLE_YOFFSET); ++ twin_path_move (path, tx, ty); ++ twin_path_utf8 (path, opt->title); ++ twin_paint_path (px, PBOOT_RIGHT_TITLE_COLOR, path); ++ twin_path_empty (path); ++ ++ if (opt->subtitle) { ++ twin_path_set_font_size(path, PBOOT_RIGHT_SUBTITLE_TEXT_SIZE); ++ twin_path_set_font_style(path, TWIN_TEXT_UNHINTED); ++ tx = twin_int_to_fixed(PBOOT_RIGHT_SUBTITLE_XOFFSET); ++ ty = twin_int_to_fixed(PBOOT_RIGHT_SUBTITLE_YOFFSET); ++ twin_path_move (path, tx, ty); ++ twin_path_utf8 (path, opt->subtitle); ++ twin_paint_path (px, PBOOT_RIGHT_SUBTITLE_COLOR, path); ++ twin_path_empty (path); ++ } ++ ++ if (opt->badge) { ++ twin_operand_t src; ++ ++ src.source_kind = TWIN_PIXMAP; ++ src.u.pixmap = opt->badge; ++ ++ twin_composite(px, PBOOT_RIGHT_BADGE_XOFFSET, ++ PBOOT_RIGHT_BADGE_YOFFSET, ++ &src, 0, 0, NULL, 0, 0, TWIN_OVER, ++ opt->badge->width, opt->badge->height); ++ } ++ ++ ++ /* Destroy path */ ++ twin_path_destroy(path); ++} ++ ++ ++ ++static int pboot_add_option(int devindex, const char *title, ++ const char *subtitle, twin_pixmap_t *badge, void *data) ++{ ++ pboot_device_t *dev; ++ pboot_option_t *opt; ++ twin_coord_t width; ++ int index; ++ struct pbt_menu *menu = NULL; ++ ++ if (devindex < 0 || devindex >= pboot_dev_count) ++ return -1; ++ dev = pboot_devices[devindex]; ++ ++ if (dev->option_count >= PBOOT_MAX_OPTION) ++ return -1; ++ index = dev->option_count++; ++ opt = &dev->options[index]; ++ ++ opt->title = malloc(strlen(title) + 1); ++ strcpy(opt->title, title); ++ ++ if (subtitle) { ++ opt->subtitle = malloc(strlen(subtitle) + 1); ++ strcpy(opt->subtitle, subtitle); ++ } else ++ opt->subtitle = NULL; ++ ++ opt->badge = badge; ++ opt->cache = NULL; ++ ++ width = menu->op->window->pixmap->width - ++ (PBOOT_RIGHT_OPTION_LMARGIN + PBOOT_RIGHT_OPTION_RMARGIN); ++ ++ opt->box.left = PBOOT_RIGHT_OPTION_LMARGIN; ++ opt->box.right = opt->box.left + width; ++ opt->box.top = PBOOT_RIGHT_OPTION_TMARGIN + ++ index * PBOOT_RIGHT_OPTION_STRIDE; ++ opt->box.bottom = opt->box.top + PBOOT_RIGHT_OPTION_HEIGHT; ++ ++ opt->data = data; ++ return index; ++} ++ ++static void pboot_set_device_select(struct pbt_menu *menu, int sel, int force) ++{ ++ pb_log("%s: %d -> %d\n", __FUNCTION__, pboot_dev_sel, sel); ++ if (!force && sel == pboot_dev_sel) ++ return; ++ if (sel >= pboot_dev_count) ++ return; ++ pboot_dev_sel = sel; ++ if (force) { ++ menu->dp->focus_curindex = sel; ++ if (sel < 0) ++ menu->dp->focus_target = 0 - PBOOT_LEFT_FOCUS_HEIGHT; ++ else ++ menu->dp->focus_target = PBOOT_LEFT_FOCUS_YOFF + ++ PBOOT_LEFT_ICON_STRIDE * sel; ++ menu->op->focus_box.bottom = menu->dp->focus_target; ++ menu->op->focus_box.bottom = menu->op->focus_box.top + ++ PBOOT_RIGHT_FOCUS_HEIGHT; ++ twin_window_damage(menu->dp->window, ++ 0, 0, ++ menu->dp->window->pixmap->width, ++ menu->dp->window->pixmap->height); ++ twin_window_queue_paint(menu->dp->window); ++ } ++ menu->op->focus_curindex = -1; ++ menu->op->mouse_target = -1; ++ menu->op->focus_box.top = -2*PBOOT_RIGHT_FOCUS_HEIGHT; ++ menu->op->focus_box.bottom = menu->op->focus_box.top + ++ PBOOT_RIGHT_FOCUS_HEIGHT; ++ twin_window_damage(menu->op->window, 0, 0, ++ menu->op->window->pixmap->width, ++ menu->op->window->pixmap->height); ++ twin_window_queue_paint(menu->op->window); ++} ++ ++static void pboot_quit(void) ++{ ++ kill(0, SIGINT); ++} ++ ++ ++static int pboot_add_device(const char *dev_id, twin_pixmap_t *pixmap) ++{ ++ int index; ++ pboot_device_t *dev; ++ ++ struct pbt_menu *menu = NULL; ++ ++ if (pboot_dev_count >= PBOOT_MAX_DEV) ++ return -1; ++ ++ index = pboot_dev_count++; ++ ++ dev = malloc(sizeof(*dev)); ++ memset(dev, 0, sizeof(*dev)); ++ dev->id = malloc(strlen(dev_id) + 1); ++ strcpy(dev->id, dev_id); ++ dev->badge = pixmap; ++ dev->box.left = PBOOT_LEFT_ICON_XOFF; ++ dev->box.right = dev->box.left + PBOOT_LEFT_ICON_WIDTH; ++ dev->box.top = PBOOT_LEFT_ICON_YOFF + ++ PBOOT_LEFT_ICON_STRIDE * index; ++ dev->box.bottom = dev->box.top + PBOOT_LEFT_ICON_HEIGHT; ++ ++ pboot_devices[index] = dev; ++ ++ twin_window_damage(menu->dp->window, ++ dev->box.left, dev->box.top, ++ dev->box.right, dev->box.bottom); ++ twin_window_queue_paint(menu->dp->window); ++ ++ return index; ++} ++ ++static int pboot_remove_device(const char *dev_id) ++{ ++ pboot_device_t *dev = NULL; ++ int i, newsel = pboot_dev_sel; ++ ++ struct pbt_menu *menu = NULL; ++ ++ /* find the matching device */ ++ for (i = 0; i < pboot_dev_count; i++) { ++ if (!strcmp(pboot_devices[i]->id, dev_id)) { ++ dev = pboot_devices[i]; ++ break; ++ } ++ } ++ ++ if (!dev) ++ return TWIN_FALSE; ++ ++ memmove(pboot_devices + i, pboot_devices + i + 1, ++ sizeof(*pboot_devices) * (pboot_dev_count + i - 1)); ++ pboot_devices[--pboot_dev_count] = NULL; ++ ++ /* select the newly-focussed device */ ++ if (pboot_dev_sel > i) ++ newsel = pboot_dev_sel - 1; ++ else if (pboot_dev_sel == i && i >= pboot_dev_count) ++ newsel = pboot_dev_count - 1; ++ pboot_set_device_select(menu, newsel, 1); ++ ++ /* todo: free device & options */ ++ ++ return TWIN_TRUE; ++} ++#endif ++ ++//------------------------------------------------------------------------------ ++ ++static void print_version(void) ++{ ++ printf("pb-twin (" PACKAGE_NAME ") " PACKAGE_VERSION "\n"); ++} ++ ++static void print_usage(void) ++{ ++ print_version(); ++ printf( ++"Usage: pb-twin [-h, --help] [-l, --log log-file] [-r, --reset-defaults]\n" ++" [-t, --timeout] [-V, --version]\n"); ++} ++ ++/** ++ * enum opt_value - Tri-state options variables. ++ */ ++ ++enum opt_value {opt_undef = 0, opt_yes, opt_no}; ++ ++/** ++ * struct opts - Values from command line options. ++ */ ++ ++struct opts { ++ enum opt_value show_help; ++ const char *log_file; ++ enum opt_value reset_defaults; ++ enum opt_value use_timeout; ++ enum opt_value show_version; ++}; ++ ++/** ++ * opts_parse - Parse the command line options. ++ */ ++ ++static int opts_parse(struct opts *opts, int argc, char *argv[]) ++{ ++ static const struct option long_options[] = { ++ {"help", no_argument, NULL, 'h'}, ++ {"log", required_argument, NULL, 'l'}, ++ {"reset-defaults", no_argument, NULL, 'r'}, ++ {"timeout", no_argument, NULL, 't'}, ++ {"version", no_argument, NULL, 'V'}, ++ { NULL, 0, NULL, 0}, ++ }; ++ static const char short_options[] = "hl:trV"; ++ static const struct opts default_values = { ++ .log_file = "pb-twin.log", ++ }; ++ ++ *opts = default_values; ++ ++ while (1) { ++ int c = getopt_long(argc, argv, short_options, long_options, ++ NULL); ++ ++ if (c == EOF) ++ break; ++ ++ switch (c) { ++ case 'h': ++ opts->show_help = opt_yes; ++ break; ++ case 'l': ++ opts->log_file = optarg; ++ break; ++ case 't': ++ opts->use_timeout = opt_yes; ++ break; ++ case 'r': ++ opts->reset_defaults = opt_yes; ++ break; ++ case 'V': ++ opts->show_version = opt_yes; ++ break; ++ default: ++ opts->show_help = opt_yes; ++ return -1; ++ } ++ } ++ ++ return optind != argc; ++} ++ ++/** ++ * struct ps3_gui - Main gui program instance. ++ */ ++ ++ ++struct ps3_gui { ++ struct ui_timer timer; ++ struct ps3_flash_values values; ++ int dirty_values; ++ ++ struct pbt_scr scr; ++ struct pbt_frame frame; ++ struct pbt_menu *menu; ++}; ++ ++static struct ps3_gui ps3; ++ ++static struct pbt_scr *pbt_scr_from_tscreen(twin_screen_t *tscreen) ++{ ++ assert(ps3.scr.sig == pbt_scr_sig); ++ assert(ps3.scr.tscreen == tscreen); ++ return &ps3.scr; ++} ++ ++static void sig_handler(int signum) ++{ ++ DBGS("%d\n", signum); ++ ++ switch (signum) { ++ case SIGALRM: ++ ui_timer_sigalrm(&ps3.timer); ++ break; ++ case SIGWINCH: ++// if (ps3.gui) ++// gui_resize(ps3.gui); ++ break; ++ default: ++ assert(0 && "unknown sig"); ++ /* fall through */ ++ case SIGINT: ++ case SIGHUP: ++ case SIGTERM: ++ exit(EXIT_FAILURE); ++// if (ps3.gui) ++// gui_abort(ps3.gui); ++ break; ++ } ++} + +/** + * main - twin bootloader main routine. -+ * -+ * Returns: -+ * 0 = Returned as success by ps3-utils programs. -+ * 1 = Error, expecting a restart. -+ * 22 = Exit to shell. + */ + -+int main(void) ++int main(int argc, char *argv[]) +{ ++ static struct sigaction sa; ++ static struct opts opts; ++ int result; ++ int ui_result; ++ unsigned int mode; + FILE *log; + -+ log = fopen("pb-twin.log", "a"); ++ result = opts_parse(&opts, argc, argv); ++ ++ if (result) { ++ print_usage(); ++ return EXIT_FAILURE; ++ } ++ ++ if (opts.show_help == opt_yes) { ++ print_usage(); ++ return EXIT_SUCCESS; ++ } ++ ++ if (opts.show_version == opt_yes) { ++ print_version(); ++ return EXIT_SUCCESS; ++ } ++ ++ log = fopen(opts.log_file, "a"); + assert(log); + pb_log_set_stream(log); + @@ -63,13 +1397,84 @@ Signed-off-by: Geoff Levand + + pb_log("--- pb-twin ---\n"); + -+ pboot_fbdev = twin_fbdev_create(-1, SIGUSR1); -+ if (pboot_fbdev == NULL) { -+ perror("failed to create fbdev screen !\n"); -+ return 1; ++ 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 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; +}