mail archive of the barebox mailing list
 help / color / mirror / Atom feed
* [PATCH] Add Menu Framework
@ 2010-08-19  3:53 Jean-Christophe PLAGNIOL-VILLARD
  2010-08-19  8:29 ` Sascha Hauer
                   ` (2 more replies)
  0 siblings, 3 replies; 9+ messages in thread
From: Jean-Christophe PLAGNIOL-VILLARD @ 2010-08-19  3:53 UTC (permalink / raw)
  To: barebox

Introduce a menu framework that allow us to create list menu to simplify
barebox and make it more user-frendly

This kind of menu is very usefull when you do not have a keyboard or a
serial console attached to your board to allow you to interract with
barebox

For the develloper part,
The framework introduce two API

1) C
that allow you to create menu, submenu, entry and complex menu action

2) Command
that allow you as the C API to create menu, submenu, entry and complex
menu action but this time the actions will be store in a function and
then be evaluated and excecuted at runtime.

Signed-off-by: Jean-Christophe PLAGNIOL-VILLARD <plagnioj@jcrosoft.com>
---
 commands/Kconfig  |   10 ++
 commands/Makefile |    1 +
 commands/menu.c   |  463 +++++++++++++++++++++++++++++++++++++++++++++++++++++
 common/Kconfig    |    7 +
 common/Makefile   |    1 +
 common/menu.c     |  303 ++++++++++++++++++++++++++++++++++
 include/menu.h    |   87 ++++++++++
 include/readkey.h |    5 +
 8 files changed, 877 insertions(+), 0 deletions(-)
 create mode 100644 commands/menu.c
 create mode 100644 common/menu.c
 create mode 100644 include/menu.h

diff --git a/commands/Kconfig b/commands/Kconfig
index 1ffc826..57c9b75 100644
--- a/commands/Kconfig
+++ b/commands/Kconfig
@@ -50,6 +50,16 @@ config CMD_FALSE
 	default y
 	prompt "false"
 
+config CMD_MENU
+	tristate
+	depends on MENU
+	prompt "menu"
+
+config CMD_MENU_MANAGEMENT
+	tristate
+	depends on CMD_MENU
+	prompt "menu scripts management"
+
 endmenu
 
 menu "file commands                 "
diff --git a/commands/Makefile b/commands/Makefile
index b99f042..154a778 100644
--- a/commands/Makefile
+++ b/commands/Makefile
@@ -50,3 +50,4 @@ obj-$(CONFIG_CMD_GPIO)		+= gpio.o
 obj-$(CONFIG_CMD_UNLZO)		+= unlzo.o
 obj-$(CONFIG_CMD_I2C)		+= i2c.o
 obj-$(CONFIG_CMD_UBI)		+= ubi.o
+obj-$(CONFIG_CMD_MENU)		+= menu.o
diff --git a/commands/menu.c b/commands/menu.c
new file mode 100644
index 0000000..4f33877
--- /dev/null
+++ b/commands/menu.c
@@ -0,0 +1,463 @@
+/*
+ * (C) Copyright 2009-2010 Jean-Christophe PLAGNIOL-VILLARD <plagnioj@jcrosoft.com>
+ *
+ * See file CREDITS for list of people who contributed to this
+ * project.
+ *
+ * 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
+ */
+
+#include <common.h>
+#include <command.h>
+#include <readkey.h>
+#include <menu.h>
+#include <getopt.h>
+#include <errno.h>
+
+typedef enum {
+#if defined(CONFIG_CMD_MENU_MANAGEMENT)
+	action_add,
+	action_remove,
+	action_select,
+#endif
+	action_list,
+	action_show,
+} menu_action;
+
+struct cmd_menu {
+	char		*menu;
+	menu_action	action;
+#if defined(CONFIG_CMD_MENU_MANAGEMENT)
+	int		entry;
+	int		re_entrant;
+	char		*description;
+	char		*command;
+	int		num;
+#endif
+};
+
+#if defined(CONFIG_CMD_MENU_MANAGEMENT)
+#define OPTS		"m:earlc:d:RsSn:"
+#define	is_entry(x)	((x)->entry)
+#else
+#define OPTS		"m:ls"
+#define	is_entry(x)	(0)
+#endif
+
+#if defined(CONFIG_CMD_MENU_MANAGEMENT)
+/*
+ * menu -e -a -m <menu> -c <command> [-R] -d <description>
+ */
+static int do_menu_entry_add(struct cmd_menu *cm)
+{
+	struct menu_entry *me;
+	struct menu *m;
+	int len;
+	int ret = -ENOMEM;
+
+	if (!cm->menu || !cm->command || !cm->description)
+		return -1;
+
+	m = menu_get_by_name(cm->menu);
+
+	if (!m) {
+		eprintf("Menu '%s' not found\n", cm->menu);
+		return -1;
+	}
+
+	me = menu_entry_alloc();
+
+	if (!me)
+		goto free;
+
+	me->action = menu_action_run;
+
+	len = strlen(cm->command) + 1;
+
+	me->priv = calloc(len, sizeof(char));
+
+	if (!me->priv)
+		goto free;
+
+	strncpy(me->priv, cm->command, len);
+
+	len = strlen(cm->description) + 1;
+
+	me->display = calloc(len, sizeof(char));;
+
+	if (!m->display)
+		goto free;
+
+	strncpy(me->display, cm->description, len);
+
+	ret = menu_add_entry(m, me);
+
+	if (ret)
+		goto free;
+
+	me->non_re_ent = !cm->re_entrant;
+
+	return 0;
+
+free:
+	eputs("Entry add fail\n");
+
+	free(me->priv);
+
+	menu_entry_free(me);
+
+	return ret;
+}
+
+/*
+ * menu -e -r -m <name> -n <num>
+ */
+static int do_menu_entry_remove(struct cmd_menu *cm)
+{
+	struct menu *m;
+	struct menu_entry *me;
+
+	if (!cm->menu || cm->num < 0)
+		return -1;
+
+	m = menu_get_by_name(cm->menu);
+
+	if (!m) {
+		eprintf("Menu '%s' not found\n", cm->menu);
+		return -1;
+	}
+
+	me = menu_entry_get_by_num(m, cm->num);
+
+	if (!me) {
+		eprintf("Entry '%s' not found\n", cm->num);
+		return -1;
+	}
+
+	menu_remove_entry(m, me);
+
+	menu_entry_free(me);
+
+	return 0;
+}
+
+/*
+ * menu -a -m <name> -d <description>
+ */
+static int do_menu_add(struct cmd_menu *cm)
+{
+	struct menu *m;
+	int len = 0;
+	int ret = -ENOMEM;
+
+	if (!cm->menu || !cm->description)
+		return -1;
+
+	m = menu_alloc();
+
+	if (!m)
+		goto free;
+
+	len = strlen(cm->menu) + 1;
+
+	m->name = calloc(len, sizeof(char));;
+	if (!m->name)
+		goto free;
+
+	strncpy(m->name, cm->menu, len);
+
+	len = strlen(cm->description) + 1;
+
+	m->display = calloc(len, sizeof(char));;
+
+	if (!m->display)
+		goto free;
+
+	strncpy(m->display, cm->description, len);
+
+	ret = menu_add(m);
+
+	if (ret)
+		goto free;
+
+	return 0;
+
+free:
+	eprintf("Menu '%s' add fail\n", cm->menu);
+
+	menu_free(m);
+
+	return ret;
+}
+/*
+ * menu -r -m <name>
+ */
+static int do_menu_remove(struct cmd_menu *cm)
+{
+	struct menu *m;
+
+	m = menu_get_by_name(cm->menu);
+
+	if (!m) {
+		eprintf("Menu '%s' not found\n", cm->menu);
+		return -1;
+	}
+
+	menu_remove(m);
+
+	menu_free(m);
+
+	return 0;
+}
+
+/*
+ * menu -m <menu> -S -n <entry num starting at 1>
+ */
+static int do_menu_select(struct cmd_menu *cm)
+{
+	struct menu *m;
+
+	if (cm->num < 0)
+		return -1;
+
+	m = menu_get_by_name(cm->menu);
+
+	if (!m) {
+		eprintf("Menu '%s' not found\n", cm->menu);
+		return -1;
+	}
+
+	if (!menu_set_selected(m, cm->num)) {
+		eprintf("Entry '%d' not found\n", cm->num);
+		return -1;
+	}
+
+	return 0;
+}
+#endif
+
+/*
+ * menu -s -m <menu>
+ */
+static int do_menu_show(struct cmd_menu *cm)
+{
+	struct menu *m;
+
+	if (cm->menu)
+		m = menu_get_by_name(cm->menu);
+	else
+		m = menu_get_by_name("boot");
+
+	return menu_show(m);
+}
+
+static void print_entries(struct menu *m)
+{
+	struct list_head *pos;
+	struct menu_entry *me;
+
+	list_for_each(pos, &(m->entries.list)) {
+		me = list_entry(pos, struct menu_entry, list);
+		printf("%d: %s\n", me->num, me->display);
+	}
+}
+
+/*
+ * menu -l
+ * menu -e -l [menu]
+ */
+static int do_menu_list(struct cmd_menu *cm)
+{
+	struct list_head *pos;
+	struct menu* m = NULL;
+	struct menu* menus = menu_get_menus();
+
+	if (is_entry(cm)) {
+		if (cm->menu)
+			m = menu_get_by_name(cm->menu);
+
+		if (m) {
+			print_entries(m);
+			return 0;
+		}
+	}
+
+	list_for_each(pos, &menus->list) {
+		m = list_entry(pos, struct menu, list);
+		printf("%s: %s\n", m->name, m->display? m->display : m->name);
+		if (is_entry(cm))
+			print_entries(m);
+	}
+
+	return 0;
+}
+
+#if defined(CONFIG_CMD_MENU_MANAGEMENT)
+static int do_menu_entry(struct cmd_menu *cm)
+{
+	switch(cm->action) {
+	case action_list:
+		return do_menu_list(cm);
+	case action_remove:
+		return do_menu_entry_remove(cm);
+	case action_add:
+		return do_menu_entry_add(cm);
+	case action_select:
+	case action_show:
+		break;
+	}
+	return -1;
+}
+#else
+static int do_menu_entry(struct cmd_menu *cm)
+{
+	return -1;
+}
+#endif
+
+static int do_menu(struct command *cmdtp, int argc, char *argv[])
+{
+	struct cmd_menu cm;
+	int opt;
+
+	memset(&cm, 0, sizeof(struct cmd_menu));
+#if defined(CONFIG_CMD_MENU_MANAGEMENT)
+	cm.num = -1;
+#endif
+
+	cm.action = action_show;
+
+	while((opt = getopt(argc, argv, OPTS)) > 0) {
+		switch(opt) {
+		case 'm':
+			cm.menu = optarg;
+			break;
+			break;
+		case 'l':
+			cm.action = action_list;
+			break;
+		case 's':
+			cm.action = action_show;
+			break;
+#if defined(CONFIG_CMD_MENU_MANAGEMENT)
+		case 'e':
+			cm.entry = 1;
+		case 'a':
+			cm.action = action_add;
+			break;
+		case 'r':
+			cm.action = action_remove;
+			break;
+		case 'c':
+			cm.command = optarg;
+			break;
+		case 'd':
+			cm.description = optarg;
+			break;
+		case 'R':
+			cm.re_entrant = 1;
+			break;
+		case 'S':
+			cm.action = action_select;
+			break;
+		case 'n':
+			cm.num = simple_strtoul(optarg, NULL, 10);
+			break;
+#endif
+		default:
+			return 1;
+		}
+	}
+
+	if (is_entry(&cm))
+		return do_menu_entry(&cm);
+
+	switch(cm.action) {
+	case action_list:
+		return do_menu_list(&cm);
+#if defined(CONFIG_CMD_MENU_MANAGEMENT)
+	case action_remove:
+		return do_menu_remove(&cm);
+	case action_add:
+		return do_menu_add(&cm);
+	case action_select:
+		return do_menu_select(&cm);
+#endif
+	case action_show:
+		return do_menu_show(&cm);
+	}
+
+	return 0;
+}
+	
+static const __maybe_unused char cmd_menu_help[] =
+"Usage: menu [OPTION]... \n"
+"Manage Menu\n"
+"  -m  menu\n"
+"  -l  list\n"
+"  -s  show\n"
+#if defined(CONFIG_CMD_MENU_MANAGEMENT)
+"Advanced\n"
+"  -e  menu entry\n"
+"  -a  add\n"
+"  -r  remove\n"
+"  -S  select\n"
+#endif
+"\n"
+"How to\n"
+"\n"
+"Show menu\n"
+"  menu -s -m <menu>\n"
+"\n"
+"List menu\n"
+"  menu -l\n"
+"\n"
+#if defined(CONFIG_CMD_MENU_MANAGEMENT)
+"Add a menu\n"
+"  menu -a -m <name> -d <description>\n"
+"\n"
+"Remove a menu\n"
+"  menu -r -m <name>\n"
+"\n"
+"Add an entry\n"
+"  (-R for do no exit the menu after executing the command)\n"
+"  menu -e -a -m <menu> -c <command> [-R] -d <description>\n"
+"\n"
+"Remove an entry\n"
+"  menu -e -r -m <name> -n <num>\n"
+"\n"
+"Select an entry\n"
+"  menu -m <menu> -S -n <entry num starting at 1>\n"
+"\n"
+"List menu\n"
+"  menu -e -l [menu]\n"
+"\n"
+"Menu example\n"
+"menu -a -m boot -d \"Boot Menu\"\n"
+"menu -e -a -m boot -c boot -d \"Boot\"\n"
+"menu -e -a -m boot -c reset -d \"Reset\"\n"
+"menu -s -m boot\n"
+#else
+"Menu example\n"
+"menu -s -m boot\n"
+#endif
+;
+
+BAREBOX_CMD_START(menu)
+	.cmd		= do_menu,
+	.usage		= "Menu Management",
+	BAREBOX_CMD_HELP(cmd_menu_help)
+BAREBOX_CMD_END
diff --git a/common/Kconfig b/common/Kconfig
index a58f242..c07c638 100644
--- a/common/Kconfig
+++ b/common/Kconfig
@@ -244,6 +244,13 @@ config AUTO_COMPLETE
 	depends on CMDLINE_EDITING
 	prompt "Enable auto completion"
 
+config MENU
+	bool
+	prompt "Menu Framework"
+	help
+	   a menu framework that allow us to create list menu to simplify
+	   barebox and make it more user-frendly
+
 config DYNAMIC_CRC_TABLE
 	bool
 	depends on CRC32
diff --git a/common/Makefile b/common/Makefile
index 14f8643..4b8cce0 100644
--- a/common/Makefile
+++ b/common/Makefile
@@ -15,6 +15,7 @@ obj-y += env.o
 obj-y += startup.o
 obj-y += misc.o
 obj-y += memsize.o
+obj-$(CONFIG_MENU) += menu.o
 obj-$(CONFIG_MODULES) += module.o
 extra-$(CONFIG_MODULES) += module.lds
 
diff --git a/common/menu.c b/common/menu.c
new file mode 100644
index 0000000..70553db
--- /dev/null
+++ b/common/menu.c
@@ -0,0 +1,303 @@
+/*
+ * (C) Copyright 2009-2010 Jean-Christophe PLAGNIOL-VILLARD <plagnioj@jcrosoft.com>
+ *
+ * See file CREDITS for list of people who contributed to this
+ * project.
+ *
+ * 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
+ */
+
+#include <common.h>
+#include <command.h>
+#include <environment.h>
+#include <init.h>
+#include <menu.h>
+#include <malloc.h>
+#include <xfuncs.h>
+#include <errno.h>
+#include <readkey.h>
+
+static struct menu menus;
+
+struct menu* menu_get_menus(void)
+{
+	return &menus;
+}
+
+void menu_free(struct menu *m)
+{
+	struct list_head *pos;
+	struct menu_entry *me;
+
+	if (!m)
+		return;
+	free(m->name);
+	free(m->display);
+
+	list_for_each(pos, &m->entries.list) {
+		me = list_entry(pos, struct menu_entry, list);
+		menu_entry_free(me);
+		list_del(pos);
+	}
+
+	free(m);
+}
+
+int menu_add(struct menu *m)
+{
+	if (!m || !m->name)
+		return -1;
+
+	list_add_tail(&m->list, &menus.list);
+	m->nb_entries = 0;
+
+	INIT_LIST_HEAD(&m->entries.list);
+
+	return 0;
+}
+
+void menu_remove(struct menu *m)
+{
+	if (!m)
+		return;
+
+	list_del(&m->list);
+}
+
+int menu_add_entry(struct menu *m, struct menu_entry *me)
+{
+	int len;
+
+	if (!m || !me || !me->display)
+		return -1;
+
+	len = strlen(me->display);
+
+	m->width = max(len, m->width);
+
+	m->nb_entries++;
+	me->num = m->nb_entries;
+	list_add_tail(&me->list, &m->entries.list);
+
+	return 0;
+}
+
+void menu_remove_entry(struct menu *m, struct menu_entry *me)
+{
+	if (!m || !me)
+		return;
+
+	m->nb_entries--;
+	list_del(&me->list);
+
+	return;
+}
+
+struct menu* menu_get_by_name(char *name)
+{
+	struct list_head *pos;
+	struct menu* m;
+
+	if (!name)
+		return NULL;
+
+	list_for_each(pos, &menus.list) {
+		m = list_entry(pos, struct menu, list);
+		if(strcmp(m->name, name) == 0)
+			return m;
+	}
+
+	return NULL;
+}
+
+struct menu_entry* menu_entry_get_by_num(struct menu* m, int num)
+{
+	struct list_head *pos;
+	struct menu_entry* me;
+
+	if (!m || num < 1 || num > m->nb_entries)
+		return NULL;
+
+	list_for_each(pos, &m->entries.list) {
+		me = list_entry(pos, struct menu_entry, list);
+		if(me->num == num)
+			return me;
+	}
+
+	return NULL;
+}
+
+void menu_entry_free(struct menu_entry *me)
+{
+	if (!me)
+		return;
+
+	free(me->display);
+	free(me);
+}
+
+static void print_menu_entry(struct menu *m, struct menu_entry *me, int reverse)
+{
+	gotoXY(me->num + 1, 3);
+	if (reverse)
+		printf_reverse("%d: %-*s", me->num, m->width, me->display);
+	else
+		printf("%d: %-*s", me->num, m->width, me->display);
+}
+
+int menu_set_selected_entry(struct menu *m, struct menu_entry* me)
+{
+	struct list_head *pos;
+	struct menu_entry* tmp;
+
+	if (!m || !me)
+		return -1;
+
+	list_for_each(pos, &m->entries.list) {
+		tmp = list_entry(pos, struct menu_entry, list);
+		if(me == tmp) {
+			m->selected = me;
+			return 0;
+		}
+	}
+
+	return -1;
+}
+
+int menu_set_selected(struct menu *m, int num)
+{
+	struct menu_entry *me;
+
+	me = menu_entry_get_by_num(m, num);
+
+	if (!me)
+		return -1;
+
+	m->selected = me;
+
+	return 0;
+}
+
+static void print_menu(struct menu *m)
+{
+	struct list_head *pos;
+	struct menu_entry *me;
+
+	clear();
+	gotoXY(1, 2);
+	if(m->display) {
+		puts(m->display);
+	} else {
+		puts("Menu : ");
+		puts(m->name);
+	}
+
+	list_for_each(pos, &m->entries.list) {
+		me = list_entry(pos, struct menu_entry, list);
+		if(m->selected != me)
+			print_menu_entry(m, me, 0);
+	}
+
+	if (!m->selected) {
+		m->selected = list_first_entry(&m->entries.list,
+						struct menu_entry, list);
+	}
+
+	print_menu_entry(m, m->selected, 1);
+}
+
+int menu_show(struct menu *m)
+{
+	int ch;
+	int escape = 0;
+
+	if(!m || list_empty(&m->entries.list))
+		return -1;
+
+	print_menu(m);
+
+	do {
+		ch = getc();
+		switch(ch) {
+		case 0x1b:
+			escape = 1;
+			break;
+		case '[':
+			if (escape)
+				break;
+		case 'A': /* up */
+			escape = 0;
+			print_menu_entry(m, m->selected, 0);
+			m->selected = list_entry(m->selected->list.prev, struct menu_entry,
+						 list);
+			if (&(m->selected->list) == &(m->entries.list)) {
+				m->selected = list_entry(m->selected->list.prev, struct menu_entry,
+							 list);
+			}
+			print_menu_entry(m, m->selected, 1);
+			break;
+		case 'B': /* down */
+			escape = 0;
+			print_menu_entry(m, m->selected, 0);
+			m->selected = list_entry(m->selected->list.next, struct menu_entry,
+						 list);
+			if (&(m->selected->list) == &(m->entries.list)) {
+				m->selected = list_entry(m->selected->list.next, struct menu_entry,
+							 list);
+			}
+			print_menu_entry(m, m->selected, 1);
+			break;
+		case '\n':
+		case '\r':
+			clear();
+			gotoXY(1,1);
+			m->selected->action(m, m->selected);
+			if (m->selected->non_re_ent)
+				return m->selected->num;
+			else
+				print_menu(m);
+		default:
+			break;
+		}
+	} while(1);
+
+	return 0;
+}
+
+void menu_action_exit(struct menu *m, struct menu_entry *me) {}
+
+void menu_action_run(struct menu *m, struct menu_entry *me)
+{
+	int ret;
+	const char *s = getenv((const char*)me->priv);
+
+	/* can be a command as boot */
+	if (!s)
+		s = me->priv;
+
+	ret = run_command (s, 0);
+
+	if (ret < 0)
+		udelay(1000000);
+}
+
+static int menu_init(void)
+{
+	INIT_LIST_HEAD(&menus.list);
+
+	return 0;
+}
+postcore_initcall(menu_init);
diff --git a/include/menu.h b/include/menu.h
new file mode 100644
index 0000000..29f18f2
--- /dev/null
+++ b/include/menu.h
@@ -0,0 +1,87 @@
+/*
+ * (C) Copyright 2009-2010 Jean-Christophe PLAGNIOL-VILLARD <plagnioj@jcrosoft.com>
+ *
+ * See file CREDITS for list of people who contributed to this
+ * project.
+ *
+ * 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
+ */
+
+#ifndef __MENU_H__
+#define __MENU_H__
+
+#include <linux/list.h>
+#include <malloc.h>
+
+struct menu;
+
+struct menu_entry {
+	int num;
+	char *display;
+	void (*action)(struct menu *m, struct menu_entry *me);
+	int non_re_ent;
+
+	struct list_head list;
+	void *priv;
+};
+
+struct menu {
+	char *name;
+	char *display;
+
+	struct list_head list;
+	struct menu_entry entries;
+	int nb_entries;
+	int width;
+	struct menu_entry *selected;
+	void *priv;
+};
+
+/*
+ * menu functions
+ */
+static inline struct menu* menu_alloc(void)
+{
+	return calloc(1, sizeof(struct menu));
+}
+void menu_free(struct menu *m);
+int menu_add(struct menu* m);
+void menu_remove(struct menu *m);
+struct menu* menu_get_by_name(char *name);
+int menu_show(struct menu *m);
+int menu_set_selected_entry(struct menu *m, struct menu_entry* me);
+int menu_set_selected(struct menu *m, int num);
+struct menu* menu_get_menus(void);
+
+/*
+ * menu entry functions
+ */
+static inline struct menu_entry* menu_entry_alloc(void)
+{
+	return calloc(1, sizeof(struct menu_entry));
+}
+void menu_entry_free(struct menu_entry *me);
+int menu_add_entry(struct menu *m, struct menu_entry* me);
+void menu_remove_entry(struct menu *m, struct menu_entry *me);
+struct menu_entry* menu_entry_get_by_num(struct menu* m, int num);
+
+/*
+ * menu entry action functions
+ */
+void menu_action_run(struct menu *m, struct menu_entry *me);
+void menu_action_exit(struct menu *m, struct menu_entry *me);
+
+#endif /* __MENU_H__ */
diff --git a/include/readkey.h b/include/readkey.h
index 919af64..aabb835 100644
--- a/include/readkey.h
+++ b/include/readkey.h
@@ -22,6 +22,11 @@
 
 #define ANSI_CLEAR_SCREEN "\e[2J\e[;H"
 
+#define printf_reverse(fmt,args...)	printf("\e[7m" fmt "\e[m",##args)
+#define puts_reverse(fmt)		puts("\e[7m" fmt "\e[m")
+#define gotoXY(row, col)		printf("\e[%d;%dH", row, col)
+#define clear()				puts("\e[2J")
+
 int read_key(void);
 
 #endif /* READKEY_H */
-- 
1.7.1


_______________________________________________
barebox mailing list
barebox@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/barebox

^ permalink raw reply	[flat|nested] 9+ messages in thread

* Re: [PATCH] Add Menu Framework
  2010-08-19  3:53 [PATCH] Add Menu Framework Jean-Christophe PLAGNIOL-VILLARD
@ 2010-08-19  8:29 ` Sascha Hauer
  2010-08-20  3:21   ` Jean-Christophe PLAGNIOL-VILLARD
  2010-08-20  8:22 ` [PATCH 1/2 v2] " Jean-Christophe PLAGNIOL-VILLARD
  2010-08-20  8:22 ` [PATCH 2/2] Menu/cmd: add sub menu entry command support Jean-Christophe PLAGNIOL-VILLARD
  2 siblings, 1 reply; 9+ messages in thread
From: Sascha Hauer @ 2010-08-19  8:29 UTC (permalink / raw)
  To: Jean-Christophe PLAGNIOL-VILLARD; +Cc: barebox

On Thu, Aug 19, 2010 at 05:53:22AM +0200, Jean-Christophe PLAGNIOL-VILLARD wrote:
> Introduce a menu framework that allow us to create list menu to simplify
> barebox and make it more user-frendly
> 
> This kind of menu is very usefull when you do not have a keyboard or a
> serial console attached to your board to allow you to interract with
> barebox

\o/ Cool! I really like it.

This is simple and still very flexible.

Some things which came up during testing:

- I get a data abort when trying to remove a menu without removing the
  entries first. example:

barebox@Phytec phyCORE-i.MX31:/ menu -a -m boot -d "Boot Menu"
barebox@Phytec phyCORE-i.MX31:/ menu -e -a -m boot -c boot -d "Boot"
barebox@Phytec phyCORE-i.MX31:/ menu -r -m boot
data abort
pc : [<87f05058>]    lr : [<87f05098>]
sp : 87affe20  ip : 87f20794  fp : 00000004
r10: 00000002  r9 : 00000000  r8 : 00200200
r7 : 00100100  r6 : 87b0daf8  r5 : 87b0db18  r4 : 001000f0
r3 : 87b0db18  r2 : 87b0db18  r1 : 003b7fff  r0 : 001000f0
Flags: nzCv  IRQs off  FIQs off  Mode SVC_32
Resetting CPU ...

- do_menu could use a if (!argc) return COMMAND_ERROR_USAGE;

- It would be nice to have an option to directly create a submenu on the
  command line (though this can be added later). So instead of doing

menu -e -a -m boot -c "menu -s -m network" -R -d "Network settings ->"

we could have a

menu -e -a -m boot -u network -R -d "Network settings ->"

We could than automatically add a 'back' entry if a menu is a submenu.

- It shouldn't be possible to add the same menu twice. example:

barebox@Phytec phyCORE-i.MX31:/ menu -a -m boot -d "Boot Menu"
barebox@Phytec phyCORE-i.MX31:/ menu -a -m boot -d "Boot Menu"
barebox@Phytec phyCORE-i.MX31:/ menu -l
boot: Boot Menu
boot: Boot Menu
barebox@Phytec phyCORE-i.MX31:/

- Removing entries does not work as expected. example:

menu -a -m boot -d "Boot Menu"
menu -e -a -m boot -c boot -d "Boot"
menu -e -a -m boot -c reset -d "Reset"
menu -e -a -m boot -c "exit" -d "Command line"
menu -e -r -m boot -n 1
menu -s -m boot

As expected the first entry is missing, but instead an empty line is
printed. When I then try to add an entry after this the menu is corrupt.

- commands should always return positive error codes. A good practice is
  to pass -E* values up to do_menu, use strerror() to print the error
  code and return 1 afterwards.

> +		switch(opt) {
> +		case 'm':
> +			cm.menu = optarg;
> +			break;
> +			break;
> +		case 'l':
> +			cm.action = action_list;
> +			break;
> +		case 's':
> +			cm.action = action_show;
> +			break;
> +#if defined(CONFIG_CMD_MENU_MANAGEMENT)
> +		case 'e':
> +			cm.entry = 1;
> +		case 'a':

There is a 'break' missing here.

> +			cm.action = action_add;
> +			break;
> +		case 'r':
> +			cm.action = action_remove;
> +			break;
> +		case 'c':
> +			cm.command = optarg;
> +			break;

Thank you for this work. I really appreciate it ;)

Sascha

-- 
Pengutronix e.K.                           |                             |
Industrial Linux Solutions                 | http://www.pengutronix.de/  |
Peiner Str. 6-8, 31137 Hildesheim, Germany | Phone: +49-5121-206917-0    |
Amtsgericht Hildesheim, HRA 2686           | Fax:   +49-5121-206917-5555 |

_______________________________________________
barebox mailing list
barebox@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/barebox

^ permalink raw reply	[flat|nested] 9+ messages in thread

* Re: [PATCH] Add Menu Framework
  2010-08-19  8:29 ` Sascha Hauer
@ 2010-08-20  3:21   ` Jean-Christophe PLAGNIOL-VILLARD
  2010-08-20  6:38     ` Sascha Hauer
  0 siblings, 1 reply; 9+ messages in thread
From: Jean-Christophe PLAGNIOL-VILLARD @ 2010-08-20  3:21 UTC (permalink / raw)
  To: Sascha Hauer; +Cc: barebox

On 10:29 Thu 19 Aug     , Sascha Hauer wrote:
> On Thu, Aug 19, 2010 at 05:53:22AM +0200, Jean-Christophe PLAGNIOL-VILLARD wrote:
> > Introduce a menu framework that allow us to create list menu to simplify
> > barebox and make it more user-frendly
> > 
> > This kind of menu is very usefull when you do not have a keyboard or a
> > serial console attached to your board to allow you to interract with
> > barebox
> 
> \o/ Cool! I really like it.
> 
> This is simple and still very flexible.
> 
> Some things which came up during testing:
> 
> - I get a data abort when trying to remove a menu without removing the
>   entries first. example:
> 
> barebox@Phytec phyCORE-i.MX31:/ menu -a -m boot -d "Boot Menu"
> barebox@Phytec phyCORE-i.MX31:/ menu -e -a -m boot -c boot -d "Boot"
> barebox@Phytec phyCORE-i.MX31:/ menu -r -m boot
> data abort
> pc : [<87f05058>]    lr : [<87f05098>]
> sp : 87affe20  ip : 87f20794  fp : 00000004
> r10: 00000002  r9 : 00000000  r8 : 00200200
> r7 : 00100100  r6 : 87b0daf8  r5 : 87b0db18  r4 : 001000f0
> r3 : 87b0db18  r2 : 87b0db18  r1 : 003b7fff  r0 : 001000f0
> Flags: nzCv  IRQs off  FIQs off  Mode SVC_32
> Resetting CPU ...
> 
FIx
> - do_menu could use a if (!argc) return COMMAND_ERROR_USAGE;
> 
too
> - It would be nice to have an option to directly create a submenu on the
>   command line (though this can be added later). So instead of doing
> 
> menu -e -a -m boot -c "menu -s -m network" -R -d "Network settings ->"
> 
> we could have a
> 
> menu -e -a -m boot -u network -R -d "Network settings ->"
> 
> We could than automatically add a 'back' entry if a menu is a submenu.
I disagree here about the automatic adding of a 'back" entry
as you may want to put at the top or the bottom or where ever you want and you
may not write the menu in English or the text "Back"
so I will prefer to let it done manually as this

menu -a -m boot -d "Boot Menu"
menu -a -m network -d "Network settings"
menu -e -a -m network -c "echo ok" -R -d "test"
menu -e -a -m network -u boot -d "Back"
menu -e -a -m boot -u network -d "Network settings ->"
menu -e -a -m boot -c reset -R -d "Exit"

> 
> - It shouldn't be possible to add the same menu twice. example:
> 
> barebox@Phytec phyCORE-i.MX31:/ menu -a -m boot -d "Boot Menu"
> barebox@Phytec phyCORE-i.MX31:/ menu -a -m boot -d "Boot Menu"
> barebox@Phytec phyCORE-i.MX31:/ menu -l
> boot: Boot Menu
> boot: Boot Menu
> barebox@Phytec phyCORE-i.MX31:/
fix
> 
> - Removing entries does not work as expected. example:
> 
> menu -a -m boot -d "Boot Menu"
> menu -e -a -m boot -c boot -d "Boot"
> menu -e -a -m boot -c reset -d "Reset"
> menu -e -a -m boot -c "exit" -d "Command line"
> menu -e -r -m boot -n 1
> menu -s -m boot
fix
> 
> - commands should always return positive error codes. A good practice is
>   to pass -E* values up to do_menu, use strerror() to print the error
>   code and return 1 afterwards.
I agree but but of the time there is no -E* related to this Framework

so do u want to create them?
> 
> > +		switch(opt) {
> > +		case 'm':
> > +			cm.menu = optarg;
> > +			break;
> > +			break;
> > +		case 'l':
> > +			cm.action = action_list;
> > +			break;
> > +		case 's':
> > +			cm.action = action_show;
> > +			break;
> > +#if defined(CONFIG_CMD_MENU_MANAGEMENT)
> > +		case 'e':
> > +			cm.entry = 1;
> > +		case 'a':
> 
> There is a 'break' missing here.
> 
fix
> > +			cm.action = action_add;
> > +			break;
> > +		case 'r':
> > +			cm.action = action_remove;
> > +			break;
> > +		case 'c':
> > +			cm.command = optarg;
> > +			break;
> 
> Thank you for this work. I really appreciate it ;)
Your welcome
My goal was to make the bootloader more easy to use for end user and device without keyboard

and keep 2 API
commands as it's easy to manage at run time

and C for very complex menu

I plan to have this also via FrameBuffer and with a background image
so maybe barebox could replace grub & co aneday :)

Best Regards,
J.

_______________________________________________
barebox mailing list
barebox@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/barebox

^ permalink raw reply	[flat|nested] 9+ messages in thread

* Re: [PATCH] Add Menu Framework
  2010-08-20  3:21   ` Jean-Christophe PLAGNIOL-VILLARD
@ 2010-08-20  6:38     ` Sascha Hauer
  2010-08-20  6:51       ` Jean-Christophe PLAGNIOL-VILLARD
  0 siblings, 1 reply; 9+ messages in thread
From: Sascha Hauer @ 2010-08-20  6:38 UTC (permalink / raw)
  To: Jean-Christophe PLAGNIOL-VILLARD; +Cc: barebox

On Fri, Aug 20, 2010 at 05:21:01AM +0200, Jean-Christophe PLAGNIOL-VILLARD wrote:
> > menu -e -a -m boot -c "menu -s -m network" -R -d "Network settings ->"
> > 
> > we could have a
> > 
> > menu -e -a -m boot -u network -R -d "Network settings ->"
> > 
> > We could than automatically add a 'back' entry if a menu is a submenu.
> I disagree here about the automatic adding of a 'back" entry
> as you may want to put at the top or the bottom or where ever you want and you
> may not write the menu in English or the text "Back"
> so I will prefer to let it done manually as this
> 
> menu -a -m boot -d "Boot Menu"
> menu -a -m network -d "Network settings"
> menu -e -a -m network -c "echo ok" -R -d "test"
> menu -e -a -m network -u boot -d "Back"
> menu -e -a -m boot -u network -d "Network settings ->"
> menu -e -a -m boot -c reset -R -d "Exit"

That's better, agreed.

> 
> > 
> > - It shouldn't be possible to add the same menu twice. example:
> > 
> > barebox@Phytec phyCORE-i.MX31:/ menu -a -m boot -d "Boot Menu"
> > barebox@Phytec phyCORE-i.MX31:/ menu -a -m boot -d "Boot Menu"
> > barebox@Phytec phyCORE-i.MX31:/ menu -l
> > boot: Boot Menu
> > boot: Boot Menu
> > barebox@Phytec phyCORE-i.MX31:/
> fix
> > 
> > - Removing entries does not work as expected. example:
> > 
> > menu -a -m boot -d "Boot Menu"
> > menu -e -a -m boot -c boot -d "Boot"
> > menu -e -a -m boot -c reset -d "Reset"
> > menu -e -a -m boot -c "exit" -d "Command line"
> > menu -e -r -m boot -n 1
> > menu -s -m boot
> fix
> > 
> > - commands should always return positive error codes. A good practice is
> >   to pass -E* values up to do_menu, use strerror() to print the error
> >   code and return 1 afterwards.
> I agree but but of the time there is no -E* related to this Framework
> 
> so do u want to create them?

No. The use of strerror might not be appropriate here, but all functions
used should return some error value instead of -1, with -EINVAL as
fallback when no better value can be found.

> > 
> > There is a 'break' missing here.
> > 
> fix
> > > +			cm.action = action_add;
> > > +			break;
> > > +		case 'r':
> > > +			cm.action = action_remove;
> > > +			break;
> > > +		case 'c':
> > > +			cm.command = optarg;
> > > +			break;
> > 
> > Thank you for this work. I really appreciate it ;)
> Your welcome
> My goal was to make the bootloader more easy to use for end user and
> device without keyboard

Do you know openmoko? They also have a menu in U-Boot, but it's more or
less hardcoded to their exact needs.

> 
> and keep 2 API
> commands as it's easy to manage at run time
> 
> and C for very complex menu
> 
> I plan to have this also via FrameBuffer and with a background image
> so maybe barebox could replace grub & co aneday :)

A long way to go ;)

Sascha

-- 
Pengutronix e.K.                           |                             |
Industrial Linux Solutions                 | http://www.pengutronix.de/  |
Peiner Str. 6-8, 31137 Hildesheim, Germany | Phone: +49-5121-206917-0    |
Amtsgericht Hildesheim, HRA 2686           | Fax:   +49-5121-206917-5555 |

_______________________________________________
barebox mailing list
barebox@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/barebox

^ permalink raw reply	[flat|nested] 9+ messages in thread

* Re: [PATCH] Add Menu Framework
  2010-08-20  6:38     ` Sascha Hauer
@ 2010-08-20  6:51       ` Jean-Christophe PLAGNIOL-VILLARD
  2010-08-20  7:09         ` Robert Schwebel
  0 siblings, 1 reply; 9+ messages in thread
From: Jean-Christophe PLAGNIOL-VILLARD @ 2010-08-20  6:51 UTC (permalink / raw)
  To: Sascha Hauer; +Cc: barebox

> > > 
> > > - commands should always return positive error codes. A good practice is
> > >   to pass -E* values up to do_menu, use strerror() to print the error
> > >   code and return 1 afterwards.
> > I agree but but of the time there is no -E* related to this Framework
> > 
> > so do u want to create them?
> 
> No. The use of strerror might not be appropriate here, but all functions
> used should return some error value instead of -1, with -EINVAL as
> fallback when no better value can be found.
ok
> 
> > > 
> > > There is a 'break' missing here.
> > > 
> > fix
> > > > +			cm.action = action_add;
> > > > +			break;
> > > > +		case 'r':
> > > > +			cm.action = action_remove;
> > > > +			break;
> > > > +		case 'c':
> > > > +			cm.command = optarg;
> > > > +			break;
> > > 
> > > Thank you for this work. I really appreciate it ;)
> > Your welcome
> > My goal was to make the bootloader more easy to use for end user and
> > device without keyboard
> 
> Do you know openmoko? They also have a menu in U-Boot, but it's more or
> less hardcoded to their exact needs.
yeah I known, Denk force me to take a look at it when I send my patch to u-boot
ML but as you said everything is hardcoded and very limited
and for the record my code is NOT based on there work :)
so as I said him I do not like there stuff and think this one is as you said
flexible and simple

> > 
> > I plan to have this also via FrameBuffer and with a background image
> > so maybe barebox could replace grub & co aneday :)
> 
> A long way to go ;)
but a good target :-)

Best Regards,
J.

_______________________________________________
barebox mailing list
barebox@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/barebox

^ permalink raw reply	[flat|nested] 9+ messages in thread

* Re: [PATCH] Add Menu Framework
  2010-08-20  6:51       ` Jean-Christophe PLAGNIOL-VILLARD
@ 2010-08-20  7:09         ` Robert Schwebel
  2010-08-20  8:18           ` Jean-Christophe PLAGNIOL-VILLARD
  0 siblings, 1 reply; 9+ messages in thread
From: Robert Schwebel @ 2010-08-20  7:09 UTC (permalink / raw)
  To: barebox

On Fri, Aug 20, 2010 at 08:51:39AM +0200, Jean-Christophe PLAGNIOL-VILLARD wrote:
> > > I plan to have this also via FrameBuffer and with a background image
> > > so maybe barebox could replace grub & co aneday :)
> >
> > A long way to go ;)
>
> but a good target :-)

Note that Jürgen has already pushed initial x86 support (ontop of BIOS)
into Barebox. What's mainly needed to be useful for people is:

- framebuffer console
- support for fetchin the kernel from filesystems

rsc
-- 
Pengutronix e.K.                           |                             |
Industrial Linux Solutions                 | http://www.pengutronix.de/  |
Peiner Str. 6-8, 31137 Hildesheim, Germany | Phone: +49-5121-206917-0    |
Amtsgericht Hildesheim, HRA 2686           | Fax:   +49-5121-206917-5555 |

_______________________________________________
barebox mailing list
barebox@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/barebox

^ permalink raw reply	[flat|nested] 9+ messages in thread

* Re: [PATCH] Add Menu Framework
  2010-08-20  7:09         ` Robert Schwebel
@ 2010-08-20  8:18           ` Jean-Christophe PLAGNIOL-VILLARD
  0 siblings, 0 replies; 9+ messages in thread
From: Jean-Christophe PLAGNIOL-VILLARD @ 2010-08-20  8:18 UTC (permalink / raw)
  To: Robert Schwebel; +Cc: barebox

On 09:09 Fri 20 Aug     , Robert Schwebel wrote:
> On Fri, Aug 20, 2010 at 08:51:39AM +0200, Jean-Christophe PLAGNIOL-VILLARD wrote:
> > > > I plan to have this also via FrameBuffer and with a background image
> > > > so maybe barebox could replace grub & co aneday :)
> > >
> > > A long way to go ;)
> >
> > but a good target :-)
> 
> Note that Jürgen has already pushed initial x86 support (ontop of BIOS)
> into Barebox. What's mainly needed to be useful for people is:
> 
> - framebuffer console
> - support for fetchin the kernel from filesystems
so we are not so far as for some project I need also fs support

Best Regards,
J.

_______________________________________________
barebox mailing list
barebox@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/barebox

^ permalink raw reply	[flat|nested] 9+ messages in thread

* [PATCH 1/2 v2] Add Menu Framework
  2010-08-19  3:53 [PATCH] Add Menu Framework Jean-Christophe PLAGNIOL-VILLARD
  2010-08-19  8:29 ` Sascha Hauer
@ 2010-08-20  8:22 ` Jean-Christophe PLAGNIOL-VILLARD
  2010-08-20  8:22 ` [PATCH 2/2] Menu/cmd: add sub menu entry command support Jean-Christophe PLAGNIOL-VILLARD
  2 siblings, 0 replies; 9+ messages in thread
From: Jean-Christophe PLAGNIOL-VILLARD @ 2010-08-20  8:22 UTC (permalink / raw)
  To: barebox

Introduce a menu framework that allow us to create list menu to simplify
barebox and make it more user-frendly

This kind of menu is very usefull when you do not have a keyboard or a
serial console attached to your board to allow you to interract with
barebox

For the develloper part,
The framework introduce two API

1) C
that allow you to create menu, submenu, entry and complex menu action

2) Command
that allow you as the C API to create menu, submenu, entry and complex
menu action but this time the actions will be store in a function and
then be evaluated and excecuted at runtime.

Signed-off-by: Jean-Christophe PLAGNIOL-VILLARD <plagnioj@jcrosoft.com>
---
V2:
	bug fix

Best Regards,
J.
 commands/Kconfig  |   10 +
 commands/Makefile |    1 +
 commands/menu.c   |  481 +++++++++++++++++++++++++++++++++++++++++++++++++++++
 common/Kconfig    |    7 +
 common/Makefile   |    1 +
 common/menu.c     |  315 +++++++++++++++++++++++++++++++++++
 include/menu.h    |   87 ++++++++++
 include/readkey.h |    5 +
 8 files changed, 907 insertions(+), 0 deletions(-)
 create mode 100644 commands/menu.c
 create mode 100644 common/menu.c
 create mode 100644 include/menu.h

diff --git a/commands/Kconfig b/commands/Kconfig
index 1ffc826..57c9b75 100644
--- a/commands/Kconfig
+++ b/commands/Kconfig
@@ -50,6 +50,16 @@ config CMD_FALSE
 	default y
 	prompt "false"
 
+config CMD_MENU
+	tristate
+	depends on MENU
+	prompt "menu"
+
+config CMD_MENU_MANAGEMENT
+	tristate
+	depends on CMD_MENU
+	prompt "menu scripts management"
+
 endmenu
 
 menu "file commands                 "
diff --git a/commands/Makefile b/commands/Makefile
index b99f042..154a778 100644
--- a/commands/Makefile
+++ b/commands/Makefile
@@ -50,3 +50,4 @@ obj-$(CONFIG_CMD_GPIO)		+= gpio.o
 obj-$(CONFIG_CMD_UNLZO)		+= unlzo.o
 obj-$(CONFIG_CMD_I2C)		+= i2c.o
 obj-$(CONFIG_CMD_UBI)		+= ubi.o
+obj-$(CONFIG_CMD_MENU)		+= menu.o
diff --git a/commands/menu.c b/commands/menu.c
new file mode 100644
index 0000000..c1bd0e2
--- /dev/null
+++ b/commands/menu.c
@@ -0,0 +1,481 @@
+/*
+ * (C) Copyright 2009-2010 Jean-Christophe PLAGNIOL-VILLARD <plagnioj@jcrosoft.com>
+ *
+ * See file CREDITS for list of people who contributed to this
+ * project.
+ *
+ * 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
+ */
+
+#include <common.h>
+#include <command.h>
+#include <readkey.h>
+#include <menu.h>
+#include <getopt.h>
+#include <errno.h>
+
+typedef enum {
+#if defined(CONFIG_CMD_MENU_MANAGEMENT)
+	action_add,
+	action_remove,
+	action_select,
+#endif
+	action_list,
+	action_show,
+} menu_action;
+
+struct cmd_menu {
+	char		*menu;
+	menu_action	action;
+#if defined(CONFIG_CMD_MENU_MANAGEMENT)
+	int		entry;
+	int		re_entrant;
+	char		*description;
+	char		*command;
+	int		num;
+#endif
+};
+
+#if defined(CONFIG_CMD_MENU_MANAGEMENT)
+#define OPTS		"m:earlc:d:RsSn:"
+#define	is_entry(x)	((x)->entry)
+#else
+#define OPTS		"m:ls"
+#define	is_entry(x)	(0)
+#endif
+
+#if defined(CONFIG_CMD_MENU_MANAGEMENT)
+/*
+ * menu -e -a -m <menu> -c <command> [-R] -d <description>
+ */
+static int do_menu_entry_add(struct cmd_menu *cm)
+{
+	struct menu_entry *me;
+	struct menu *m;
+	int len;
+	int ret = -ENOMEM;
+
+	if (!cm->menu || !cm->command || !cm->description)
+		return -EINVAL;
+
+	m = menu_get_by_name(cm->menu);
+
+	if (!m) {
+		eprintf("Menu '%s' not found\n", cm->menu);
+		return -EINVAL;
+	}
+
+	me = menu_entry_alloc();
+
+	if (!me)
+		goto free;
+
+	me->action = menu_action_run;
+
+	len = strlen(cm->command) + 1;
+
+	me->priv = calloc(len, sizeof(char));
+
+	if (!me->priv)
+		goto free;
+
+	strncpy(me->priv, cm->command, len);
+
+	len = strlen(cm->description) + 1;
+
+	me->display = calloc(len, sizeof(char));;
+
+	if (!m->display)
+		goto free;
+
+	strncpy(me->display, cm->description, len);
+
+	ret = menu_add_entry(m, me);
+
+	if (ret)
+		goto free;
+
+	me->non_re_ent = !cm->re_entrant;
+
+	return 0;
+
+free:
+	eputs("Entry add fail\n");
+
+	free(me->priv);
+
+	menu_entry_free(me);
+
+	return ret;
+}
+
+/*
+ * menu -e -r -m <name> -n <num>
+ */
+static int do_menu_entry_remove(struct cmd_menu *cm)
+{
+	struct menu *m;
+	struct menu_entry *me;
+
+	if (!cm->menu || cm->num < 0)
+		return -EINVAL;
+
+	m = menu_get_by_name(cm->menu);
+
+	if (!m) {
+		eprintf("Menu '%s' not found\n", cm->menu);
+		return -EINVAL;
+	}
+
+	me = menu_entry_get_by_num(m, cm->num);
+
+	if (!me) {
+		eprintf("Entry '%s' not found\n", cm->num);
+		return -EINVAL;
+	}
+
+	menu_remove_entry(m, me);
+
+	menu_entry_free(me);
+
+	return 0;
+}
+
+/*
+ * menu -a -m <name> -d <description>
+ */
+static int do_menu_add(struct cmd_menu *cm)
+{
+	struct menu *m;
+	int len = 0;
+	int ret = -ENOMEM;
+
+	if (!cm->menu || !cm->description)
+		return -EINVAL;
+
+	m = menu_alloc();
+
+	if (!m)
+		goto free;
+
+	len = strlen(cm->menu) + 1;
+
+	m->name = calloc(len, sizeof(char));;
+	if (!m->name)
+		goto free;
+
+	strncpy(m->name, cm->menu, len);
+
+	len = strlen(cm->description) + 1;
+
+	m->display = calloc(len, sizeof(char));;
+
+	if (!m->display)
+		goto free;
+
+	strncpy(m->display, cm->description, len);
+
+	ret = menu_add(m);
+
+	if (ret)
+		goto free;
+
+	return 0;
+
+free:
+	eprintf("Menu '%s' add fail", cm->menu);
+	if (ret == -EEXIST)
+		eputs(" already exist");
+	eputs("\n");
+
+	menu_free(m);
+
+	return ret;
+}
+/*
+ * menu -r -m <name>
+ */
+static int do_menu_remove(struct cmd_menu *cm)
+{
+	struct menu *m;
+
+	m = menu_get_by_name(cm->menu);
+
+	if (!m) {
+		eprintf("Menu '%s' not found\n", cm->menu);
+		return -EINVAL;
+	}
+
+	menu_remove(m);
+
+	menu_free(m);
+
+	return 0;
+}
+
+/*
+ * menu -m <menu> -S -n <entry num starting at 1>
+ */
+static int do_menu_select(struct cmd_menu *cm)
+{
+	struct menu *m;
+
+	if (cm->num < 0)
+		return -EINVAL;
+
+	m = menu_get_by_name(cm->menu);
+
+	if (!m) {
+		eprintf("Menu '%s' not found\n", cm->menu);
+		return -EINVAL;
+	}
+
+	if (!menu_set_selected(m, cm->num)) {
+		eprintf("Entry '%d' not found\n", cm->num);
+		return -EINVAL;
+	}
+
+	return 0;
+}
+#endif
+
+/*
+ * menu -s -m <menu>
+ */
+static int do_menu_show(struct cmd_menu *cm)
+{
+	struct menu *m;
+
+	if (cm->menu)
+		m = menu_get_by_name(cm->menu);
+	else
+		m = menu_get_by_name("boot");
+
+	return menu_show(m);
+}
+
+static void print_entries(struct menu *m)
+{
+	struct list_head *pos;
+	struct menu_entry *me;
+
+	list_for_each(pos, &(m->entries.list)) {
+		me = list_entry(pos, struct menu_entry, list);
+		printf("%d: %s\n", me->num, me->display);
+	}
+}
+
+/*
+ * menu -l
+ * menu -e -l [menu]
+ */
+static int do_menu_list(struct cmd_menu *cm)
+{
+	struct list_head *pos;
+	struct menu* m = NULL;
+	struct menu* menus = menu_get_menus();
+
+	if (is_entry(cm)) {
+		if (cm->menu)
+			m = menu_get_by_name(cm->menu);
+
+		if (m) {
+			print_entries(m);
+			return 0;
+		}
+	}
+
+	list_for_each(pos, &menus->list) {
+		m = list_entry(pos, struct menu, list);
+		printf("%s: %s\n", m->name, m->display? m->display : m->name);
+		if (is_entry(cm))
+			print_entries(m);
+	}
+
+	return 0;
+}
+
+#if defined(CONFIG_CMD_MENU_MANAGEMENT)
+static int do_menu_entry(struct cmd_menu *cm)
+{
+	switch(cm->action) {
+	case action_list:
+		return do_menu_list(cm);
+	case action_remove:
+		return do_menu_entry_remove(cm);
+	case action_add:
+		return do_menu_entry_add(cm);
+	case action_select:
+	case action_show:
+		break;
+	}
+	return -EINVAL;
+}
+#else
+static int do_menu_entry(struct cmd_menu *cm)
+{
+	return -EINVAL;
+}
+#endif
+
+static int do_menu(struct command *cmdtp, int argc, char *argv[])
+{
+	struct cmd_menu cm;
+	int opt;
+	int ret = -EINVAL;
+
+	if (!argc)
+		return COMMAND_ERROR_USAGE;
+
+	memset(&cm, 0, sizeof(struct cmd_menu));
+#if defined(CONFIG_CMD_MENU_MANAGEMENT)
+	cm.num = -EINVAL;
+#endif
+
+	cm.action = action_show;
+
+	while((opt = getopt(argc, argv, OPTS)) > 0) {
+		switch(opt) {
+		case 'm':
+			cm.menu = optarg;
+			break;
+		case 'l':
+			cm.action = action_list;
+			break;
+		case 's':
+			cm.action = action_show;
+			break;
+#if defined(CONFIG_CMD_MENU_MANAGEMENT)
+		case 'e':
+			cm.entry = 1;
+			break;
+		case 'a':
+			cm.action = action_add;
+			break;
+		case 'r':
+			cm.action = action_remove;
+			break;
+		case 'c':
+			cm.command = optarg;
+			break;
+		case 'd':
+			cm.description = optarg;
+			break;
+		case 'R':
+			cm.re_entrant = 1;
+			break;
+		case 'S':
+			cm.action = action_select;
+			break;
+		case 'n':
+			cm.num = simple_strtoul(optarg, NULL, 10);
+			break;
+#endif
+		default:
+			return 1;
+		}
+	}
+
+	if (is_entry(&cm)) {
+		ret = do_menu_entry(&cm);
+		goto end;
+	}
+
+	switch(cm.action) {
+	case action_list:
+		ret = do_menu_list(&cm);
+		break;
+#if defined(CONFIG_CMD_MENU_MANAGEMENT)
+	case action_remove:
+		ret = do_menu_remove(&cm);
+		break;
+	case action_add:
+		ret = do_menu_add(&cm);
+		break;
+	case action_select:
+		ret = do_menu_select(&cm);
+		break;
+#endif
+	case action_show:
+		ret = do_menu_show(&cm);
+		break;
+	}
+
+end:
+	if (ret)
+		return 0;
+
+	return 1;
+}
+
+static const __maybe_unused char cmd_menu_help[] =
+"Usage: menu [OPTION]... \n"
+"Manage Menu\n"
+"  -m  menu\n"
+"  -l  list\n"
+"  -s  show\n"
+#if defined(CONFIG_CMD_MENU_MANAGEMENT)
+"Advanced\n"
+"  -e  menu entry\n"
+"  -a  add\n"
+"  -r  remove\n"
+"  -S  select\n"
+#endif
+"\n"
+"How to\n"
+"\n"
+"Show menu\n"
+"  menu -s -m <menu>\n"
+"\n"
+"List menu\n"
+"  menu -l\n"
+"\n"
+#if defined(CONFIG_CMD_MENU_MANAGEMENT)
+"Add a menu\n"
+"  menu -a -m <name> -d <description>\n"
+"\n"
+"Remove a menu\n"
+"  menu -r -m <name>\n"
+"\n"
+"Add an entry\n"
+"  (-R for do no exit the menu after executing the command)\n"
+"  menu -e -a -m <menu> -c <command> [-R] -d <description>\n"
+"\n"
+"Remove an entry\n"
+"  menu -e -r -m <name> -n <num>\n"
+"\n"
+"Select an entry\n"
+"  menu -m <menu> -S -n <entry num starting at 1>\n"
+"\n"
+"List menu\n"
+"  menu -e -l [menu]\n"
+"\n"
+"Menu example\n"
+"menu -a -m boot -d \"Boot Menu\"\n"
+"menu -e -a -m boot -c boot -d \"Boot\"\n"
+"menu -e -a -m boot -c reset -d \"Reset\"\n"
+"menu -s -m boot\n"
+#else
+"Menu example\n"
+"menu -s -m boot\n"
+#endif
+;
+
+BAREBOX_CMD_START(menu)
+	.cmd		= do_menu,
+	.usage		= "Menu Management",
+	BAREBOX_CMD_HELP(cmd_menu_help)
+BAREBOX_CMD_END
diff --git a/common/Kconfig b/common/Kconfig
index a58f242..c07c638 100644
--- a/common/Kconfig
+++ b/common/Kconfig
@@ -244,6 +244,13 @@ config AUTO_COMPLETE
 	depends on CMDLINE_EDITING
 	prompt "Enable auto completion"
 
+config MENU
+	bool
+	prompt "Menu Framework"
+	help
+	   a menu framework that allow us to create list menu to simplify
+	   barebox and make it more user-frendly
+
 config DYNAMIC_CRC_TABLE
 	bool
 	depends on CRC32
diff --git a/common/Makefile b/common/Makefile
index 14f8643..4b8cce0 100644
--- a/common/Makefile
+++ b/common/Makefile
@@ -15,6 +15,7 @@ obj-y += env.o
 obj-y += startup.o
 obj-y += misc.o
 obj-y += memsize.o
+obj-$(CONFIG_MENU) += menu.o
 obj-$(CONFIG_MODULES) += module.o
 extra-$(CONFIG_MODULES) += module.lds
 
diff --git a/common/menu.c b/common/menu.c
new file mode 100644
index 0000000..e03e4d1
--- /dev/null
+++ b/common/menu.c
@@ -0,0 +1,315 @@
+/*
+ * (C) Copyright 2009-2010 Jean-Christophe PLAGNIOL-VILLARD <plagnioj@jcrosoft.com>
+ *
+ * See file CREDITS for list of people who contributed to this
+ * project.
+ *
+ * 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
+ */
+
+#include <common.h>
+#include <command.h>
+#include <environment.h>
+#include <init.h>
+#include <menu.h>
+#include <malloc.h>
+#include <xfuncs.h>
+#include <errno.h>
+#include <readkey.h>
+
+static struct menu menus;
+
+struct menu* menu_get_menus(void)
+{
+	return &menus;
+}
+
+void menu_free(struct menu *m)
+{
+	struct list_head *pos;
+	struct menu_entry *me;
+
+	if (!m)
+		return;
+	free(m->name);
+	free(m->display);
+
+	pos = &m->entries.list;
+
+	if (pos->prev != pos->next && pos->prev != 0)
+		list_for_each(pos, &m->entries.list) {
+			me = list_entry(pos, struct menu_entry, list);
+			menu_entry_free(me);
+		}
+
+	free(m);
+}
+
+int menu_add(struct menu *m)
+{
+	if (!m || !m->name)
+		return -EINVAL;
+
+	if (menu_get_by_name(m->name))
+		return -EEXIST;
+
+	list_add_tail(&m->list, &menus.list);
+
+	m->nb_entries = 0;
+
+	INIT_LIST_HEAD(&m->entries.list);
+
+	return 0;
+}
+
+void menu_remove(struct menu *m)
+{
+	if (!m)
+		return;
+
+	list_del(&m->list);
+}
+
+int menu_add_entry(struct menu *m, struct menu_entry *me)
+{
+	int len;
+
+	if (!m || !me || !me->display)
+		return -EINVAL;
+
+	len = strlen(me->display);
+
+	m->width = max(len, m->width);
+
+	m->nb_entries++;
+	me->num = m->nb_entries;
+	list_add_tail(&me->list, &m->entries.list);
+
+	return 0;
+}
+
+void menu_remove_entry(struct menu *m, struct menu_entry *me)
+{
+	struct list_head *pos;
+	int i = 1;
+
+	if (!m || !me)
+		return;
+
+	m->nb_entries--;
+	list_del(&me->list);
+
+	list_for_each(pos, &m->entries.list) {
+		me = list_entry(pos, struct menu_entry, list);
+		me->num = i++;
+	}
+}
+
+struct menu* menu_get_by_name(char *name)
+{
+	struct list_head *pos;
+	struct menu* m;
+
+	if (!name)
+		return NULL;
+
+	list_for_each(pos, &menus.list) {
+		m = list_entry(pos, struct menu, list);
+		if(strcmp(m->name, name) == 0)
+			return m;
+	}
+
+	return NULL;
+}
+
+struct menu_entry* menu_entry_get_by_num(struct menu* m, int num)
+{
+	struct list_head *pos;
+	struct menu_entry* me;
+
+	if (!m || num < 1 || num > m->nb_entries)
+		return NULL;
+
+	list_for_each(pos, &m->entries.list) {
+		me = list_entry(pos, struct menu_entry, list);
+		if(me->num == num)
+			return me;
+	}
+
+	return NULL;
+}
+
+void menu_entry_free(struct menu_entry *me)
+{
+	if (!me)
+		return;
+
+	free(me->display);
+	free(me);
+}
+
+static void print_menu_entry(struct menu *m, struct menu_entry *me, int reverse)
+{
+	gotoXY(me->num + 1, 3);
+	if (reverse)
+		printf_reverse("%d: %-*s", me->num, m->width, me->display);
+	else
+		printf("%d: %-*s", me->num, m->width, me->display);
+}
+
+int menu_set_selected_entry(struct menu *m, struct menu_entry* me)
+{
+	struct list_head *pos;
+	struct menu_entry* tmp;
+
+	if (!m || !me)
+		return -EINVAL;
+
+	list_for_each(pos, &m->entries.list) {
+		tmp = list_entry(pos, struct menu_entry, list);
+		if(me == tmp) {
+			m->selected = me;
+			return 0;
+		}
+	}
+
+	return -EINVAL;
+}
+
+int menu_set_selected(struct menu *m, int num)
+{
+	struct menu_entry *me;
+
+	me = menu_entry_get_by_num(m, num);
+
+	if (!me)
+		return -EINVAL;
+
+	m->selected = me;
+
+	return 0;
+}
+
+static void print_menu(struct menu *m)
+{
+	struct list_head *pos;
+	struct menu_entry *me;
+
+	clear();
+	gotoXY(1, 2);
+	if(m->display) {
+		puts(m->display);
+	} else {
+		puts("Menu : ");
+		puts(m->name);
+	}
+
+	list_for_each(pos, &m->entries.list) {
+		me = list_entry(pos, struct menu_entry, list);
+		if(m->selected != me)
+			print_menu_entry(m, me, 0);
+	}
+
+	if (!m->selected) {
+		m->selected = list_first_entry(&m->entries.list,
+						struct menu_entry, list);
+	}
+
+	print_menu_entry(m, m->selected, 1);
+}
+
+int menu_show(struct menu *m)
+{
+	int ch;
+	int escape = 0;
+
+	if(!m || list_empty(&m->entries.list))
+		return -EINVAL;
+
+	print_menu(m);
+
+	do {
+		ch = getc();
+		switch(ch) {
+		case 0x1b:
+			escape = 1;
+			break;
+		case '[':
+			if (escape)
+				break;
+		case 'A': /* up */
+			escape = 0;
+			print_menu_entry(m, m->selected, 0);
+			m->selected = list_entry(m->selected->list.prev, struct menu_entry,
+						 list);
+			if (&(m->selected->list) == &(m->entries.list)) {
+				m->selected = list_entry(m->selected->list.prev, struct menu_entry,
+							 list);
+			}
+			print_menu_entry(m, m->selected, 1);
+			break;
+		case 'B': /* down */
+			escape = 0;
+			print_menu_entry(m, m->selected, 0);
+			m->selected = list_entry(m->selected->list.next, struct menu_entry,
+						 list);
+			if (&(m->selected->list) == &(m->entries.list)) {
+				m->selected = list_entry(m->selected->list.next, struct menu_entry,
+							 list);
+			}
+			print_menu_entry(m, m->selected, 1);
+			break;
+		case '\n':
+		case '\r':
+			clear();
+			gotoXY(1,1);
+			m->selected->action(m, m->selected);
+			if (m->selected->non_re_ent)
+				return m->selected->num;
+			else
+				print_menu(m);
+		default:
+			break;
+		}
+	} while(1);
+
+	return 0;
+}
+
+void menu_action_exit(struct menu *m, struct menu_entry *me) {}
+
+void menu_action_run(struct menu *m, struct menu_entry *me)
+{
+	int ret;
+	const char *s = getenv((const char*)me->priv);
+
+	/* can be a command as boot */
+	if (!s)
+		s = me->priv;
+
+	ret = run_command (s, 0);
+
+	if (ret < 0)
+		udelay(1000000);
+}
+
+static int menu_init(void)
+{
+	INIT_LIST_HEAD(&menus.list);
+
+	return 0;
+}
+postcore_initcall(menu_init);
diff --git a/include/menu.h b/include/menu.h
new file mode 100644
index 0000000..29f18f2
--- /dev/null
+++ b/include/menu.h
@@ -0,0 +1,87 @@
+/*
+ * (C) Copyright 2009-2010 Jean-Christophe PLAGNIOL-VILLARD <plagnioj@jcrosoft.com>
+ *
+ * See file CREDITS for list of people who contributed to this
+ * project.
+ *
+ * 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
+ */
+
+#ifndef __MENU_H__
+#define __MENU_H__
+
+#include <linux/list.h>
+#include <malloc.h>
+
+struct menu;
+
+struct menu_entry {
+	int num;
+	char *display;
+	void (*action)(struct menu *m, struct menu_entry *me);
+	int non_re_ent;
+
+	struct list_head list;
+	void *priv;
+};
+
+struct menu {
+	char *name;
+	char *display;
+
+	struct list_head list;
+	struct menu_entry entries;
+	int nb_entries;
+	int width;
+	struct menu_entry *selected;
+	void *priv;
+};
+
+/*
+ * menu functions
+ */
+static inline struct menu* menu_alloc(void)
+{
+	return calloc(1, sizeof(struct menu));
+}
+void menu_free(struct menu *m);
+int menu_add(struct menu* m);
+void menu_remove(struct menu *m);
+struct menu* menu_get_by_name(char *name);
+int menu_show(struct menu *m);
+int menu_set_selected_entry(struct menu *m, struct menu_entry* me);
+int menu_set_selected(struct menu *m, int num);
+struct menu* menu_get_menus(void);
+
+/*
+ * menu entry functions
+ */
+static inline struct menu_entry* menu_entry_alloc(void)
+{
+	return calloc(1, sizeof(struct menu_entry));
+}
+void menu_entry_free(struct menu_entry *me);
+int menu_add_entry(struct menu *m, struct menu_entry* me);
+void menu_remove_entry(struct menu *m, struct menu_entry *me);
+struct menu_entry* menu_entry_get_by_num(struct menu* m, int num);
+
+/*
+ * menu entry action functions
+ */
+void menu_action_run(struct menu *m, struct menu_entry *me);
+void menu_action_exit(struct menu *m, struct menu_entry *me);
+
+#endif /* __MENU_H__ */
diff --git a/include/readkey.h b/include/readkey.h
index 919af64..aabb835 100644
--- a/include/readkey.h
+++ b/include/readkey.h
@@ -22,6 +22,11 @@
 
 #define ANSI_CLEAR_SCREEN "\e[2J\e[;H"
 
+#define printf_reverse(fmt,args...)	printf("\e[7m" fmt "\e[m",##args)
+#define puts_reverse(fmt)		puts("\e[7m" fmt "\e[m")
+#define gotoXY(row, col)		printf("\e[%d;%dH", row, col)
+#define clear()				puts("\e[2J")
+
 int read_key(void);
 
 #endif /* READKEY_H */
-- 
1.7.1


_______________________________________________
barebox mailing list
barebox@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/barebox

^ permalink raw reply	[flat|nested] 9+ messages in thread

* [PATCH 2/2] Menu/cmd: add sub menu entry command support
  2010-08-19  3:53 [PATCH] Add Menu Framework Jean-Christophe PLAGNIOL-VILLARD
  2010-08-19  8:29 ` Sascha Hauer
  2010-08-20  8:22 ` [PATCH 1/2 v2] " Jean-Christophe PLAGNIOL-VILLARD
@ 2010-08-20  8:22 ` Jean-Christophe PLAGNIOL-VILLARD
  2 siblings, 0 replies; 9+ messages in thread
From: Jean-Christophe PLAGNIOL-VILLARD @ 2010-08-20  8:22 UTC (permalink / raw)
  To: barebox

this will simplify the creation of navigation link

as this

menu -a -m boot -d "Boot Menu"
menu -a -m network -d "Network settings"
menu -e -a -m network -u boot -d "Back"
menu -e -a -m boot -u network -d "Network settings ->"
menu -e -a -m boot -c reset -R -d "Reset"
menu -e -a -m boot -c clear -d "Exit"
menu -s -m boot

in C

struct menu m_boot = {
	.name = "boot",
	.display = "Boot Menu",
};

struct menu m_network = {
	.name = "network",
	.display = "Network settings",
};

struct menu_entry e_to_network = {
	.display = "Network settings ->",
	.action = menu_action_show,
	.priv = &m_network,
};

struct menu_entry e_to_boot = {
	.display = "Back",
	.action = menu_action_show,
	.priv = &m_boot,
};

struct menu_entry e_reset = {
	.display = "Reset",
	.action = menu_action_run,
	.priv = "reset",
};

struct menu_entry e_exit = {
	.display = "Exit",
	.action = menu_action_run,
	.non_re_ent = 1,
	.priv = "clear",
};

void menu_test(void)
{
	menu_add(&m_boot);
	menu_add(&m_network);
	menu_add_entry(&m_boot, &e_to_network);
	menu_add_entry(&m_boot, &e_reset);
	menu_add_entry(&m_boot, &e_exit);
	menu_add_entry(&m_network, &e_to_boot);
	menu_show(&m_boot);
}



Signed-off-by: Jean-Christophe PLAGNIOL-VILLARD <plagnioj@jcrosoft.com>
---
 commands/menu.c |   40 +++++++++++++++++++++++++++++++---------
 common/menu.c   |    7 +++++++
 include/menu.h  |    1 +
 3 files changed, 39 insertions(+), 9 deletions(-)

diff --git a/commands/menu.c b/commands/menu.c
index c1bd0e2..237de9f 100644
--- a/commands/menu.c
+++ b/commands/menu.c
@@ -45,12 +45,13 @@ struct cmd_menu {
 	int		re_entrant;
 	char		*description;
 	char		*command;
+	char		*submenu;
 	int		num;
 #endif
 };
 
 #if defined(CONFIG_CMD_MENU_MANAGEMENT)
-#define OPTS		"m:earlc:d:RsSn:"
+#define OPTS		"m:earlc:d:RsSn:u:"
 #define	is_entry(x)	((x)->entry)
 #else
 #define OPTS		"m:ls"
@@ -60,15 +61,16 @@ struct cmd_menu {
 #if defined(CONFIG_CMD_MENU_MANAGEMENT)
 /*
  * menu -e -a -m <menu> -c <command> [-R] -d <description>
+ * menu -e -a -m <menu> -u submenu -d <description>
  */
 static int do_menu_entry_add(struct cmd_menu *cm)
 {
 	struct menu_entry *me;
-	struct menu *m;
+	struct menu *m, *sm;
 	int len;
 	int ret = -ENOMEM;
 
-	if (!cm->menu || !cm->command || !cm->description)
+	if (!cm->menu || (!cm->command && !cm->submenu) || !cm->description)
 		return -EINVAL;
 
 	m = menu_get_by_name(cm->menu);
@@ -83,16 +85,29 @@ static int do_menu_entry_add(struct cmd_menu *cm)
 	if (!me)
 		goto free;
 
-	me->action = menu_action_run;
+	if (cm->submenu) {
+		me->action = menu_action_show;
 
-	len = strlen(cm->command) + 1;
+		sm = menu_get_by_name(cm->submenu);
 
-	me->priv = calloc(len, sizeof(char));
+		if (!sm) {
+			eprintf("SubMenu '%s' not found\n", cm->menu);
+			goto free;
+		}
 
-	if (!me->priv)
-		goto free;
+		me->priv = sm;
+	} else {
+		me->action = menu_action_run;
+
+		len = strlen(cm->command) + 1;
+
+		me->priv = calloc(len, sizeof(char));
 
-	strncpy(me->priv, cm->command, len);
+		if (!me->priv)
+			goto free;
+
+		strncpy(me->priv, cm->command, len);
+	}
 
 	len = strlen(cm->description) + 1;
 
@@ -371,6 +386,9 @@ static int do_menu(struct command *cmdtp, int argc, char *argv[])
 		case 'c':
 			cm.command = optarg;
 			break;
+		case 'u':
+			cm.submenu = optarg;
+			break;
 		case 'd':
 			cm.description = optarg;
 			break;
@@ -453,6 +471,10 @@ static const __maybe_unused char cmd_menu_help[] =
 "Add an entry\n"
 "  (-R for do no exit the menu after executing the command)\n"
 "  menu -e -a -m <menu> -c <command> [-R] -d <description>\n"
+
+"Add a submenu entry\n"
+"  (-R is not needed)\n"
+"  menu -e -a -m <menu> -u <menu> -d <description>\n"
 "\n"
 "Remove an entry\n"
 "  menu -e -r -m <name> -n <num>\n"
diff --git a/common/menu.c b/common/menu.c
index e03e4d1..b201644 100644
--- a/common/menu.c
+++ b/common/menu.c
@@ -306,6 +306,13 @@ void menu_action_run(struct menu *m, struct menu_entry *me)
 		udelay(1000000);
 }
 
+void menu_action_show(struct menu *m, struct menu_entry *me)
+{
+	struct menu *sm = me->priv;
+
+	menu_show(sm);
+}
+
 static int menu_init(void)
 {
 	INIT_LIST_HEAD(&menus.list);
diff --git a/include/menu.h b/include/menu.h
index 29f18f2..4405ced 100644
--- a/include/menu.h
+++ b/include/menu.h
@@ -82,6 +82,7 @@ struct menu_entry* menu_entry_get_by_num(struct menu* m, int num);
  * menu entry action functions
  */
 void menu_action_run(struct menu *m, struct menu_entry *me);
+void menu_action_show(struct menu *m, struct menu_entry *me);
 void menu_action_exit(struct menu *m, struct menu_entry *me);
 
 #endif /* __MENU_H__ */
-- 
1.7.1


_______________________________________________
barebox mailing list
barebox@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/barebox

^ permalink raw reply	[flat|nested] 9+ messages in thread

end of thread, other threads:[~2010-08-20  8:22 UTC | newest]

Thread overview: 9+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2010-08-19  3:53 [PATCH] Add Menu Framework Jean-Christophe PLAGNIOL-VILLARD
2010-08-19  8:29 ` Sascha Hauer
2010-08-20  3:21   ` Jean-Christophe PLAGNIOL-VILLARD
2010-08-20  6:38     ` Sascha Hauer
2010-08-20  6:51       ` Jean-Christophe PLAGNIOL-VILLARD
2010-08-20  7:09         ` Robert Schwebel
2010-08-20  8:18           ` Jean-Christophe PLAGNIOL-VILLARD
2010-08-20  8:22 ` [PATCH 1/2 v2] " Jean-Christophe PLAGNIOL-VILLARD
2010-08-20  8:22 ` [PATCH 2/2] Menu/cmd: add sub menu entry command support Jean-Christophe PLAGNIOL-VILLARD

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox