From b712b26632d4af1e1c9454a3b330ed7b401e165b Mon Sep 17 00:00:00 2001 From: Jean-Christophe PLAGNIOL-VILLARD Date: Fri, 20 Aug 2010 10:22:47 +0200 Subject: Add Menu Framework 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 Signed-off-by: Sascha Hauer --- common/Kconfig | 7 ++ common/Makefile | 1 + common/menu.c | 315 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 323 insertions(+) create mode 100644 common/menu.c (limited to 'common') diff --git a/common/Kconfig b/common/Kconfig index 2ddf90c767..6556c62fd4 100644 --- a/common/Kconfig +++ b/common/Kconfig @@ -257,6 +257,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 14f8643f15..4b8cce0d01 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 0000000000..e03e4d1d28 --- /dev/null +++ b/common/menu.c @@ -0,0 +1,315 @@ +/* + * (C) Copyright 2009-2010 Jean-Christophe PLAGNIOL-VILLARD + * + * 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 +#include +#include +#include +#include +#include +#include +#include +#include + +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); -- cgit v1.2.3