/* * (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 #include #include #include static LIST_HEAD(menus); struct menu* menu_get_menus(void) { if (list_empty(&menus)) return NULL; return list_entry(&menus, struct menu, list); } EXPORT_SYMBOL(menu_get_menus); void menu_free(struct menu *m) { struct menu_entry *me, *tmp; if (!m) return; free(m->name); free(m->display); free(m->auto_display); free(m->display_buffer); list_for_each_entry_safe(me, tmp, &m->entries, list) menu_entry_free(me); free(m); } EXPORT_SYMBOL(menu_free); 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); return 0; } EXPORT_SYMBOL(menu_add); void menu_remove(struct menu *m) { if (!m) return; list_del(&m->list); } EXPORT_SYMBOL(menu_remove); 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); return 0; } EXPORT_SYMBOL(menu_add_entry); void menu_remove_entry(struct menu *m, struct menu_entry *me) { int i = 1; if (!m || !me) return; m->nb_entries--; list_del(&me->list); list_for_each_entry(me, &m->entries, list) me->num = i++; } EXPORT_SYMBOL(menu_remove_entry); struct menu* menu_get_by_name(char *name) { struct menu* m; if (!name) return NULL; list_for_each_entry(m, &menus, list) { if(strcmp(m->name, name) == 0) return m; } return NULL; } EXPORT_SYMBOL(menu_get_by_name); struct menu_entry* menu_entry_get_by_num(struct menu* m, int num) { struct menu_entry* me; if (!m || num < 1 || num > m->nb_entries) return NULL; list_for_each_entry(me, &m->entries, list) { if(me->num == num) return me; } return NULL; } EXPORT_SYMBOL(menu_entry_get_by_num); void menu_entry_free(struct menu_entry *me) { if (!me) return; me->free(me); } EXPORT_SYMBOL(menu_entry_free); static void print_menu_entry(struct menu *m, struct menu_entry *me, int selected) { gotoXY(me->num + 1, 3); if (me->type == MENU_ENTRY_BOX) { if (me->box_state) puts("[*]"); else puts("[ ]"); } else { puts(" "); } process_escape_sequence(me->display, m->display_buffer, m->display_buffer_size); printf(" %d: ", me->num); if (selected) puts("\e[7m"); puts(m->display_buffer); if (selected) puts("\e[m"); } int menu_set_selected_entry(struct menu *m, struct menu_entry* me) { struct menu_entry* tmp; if (!m || !me) return -EINVAL; list_for_each_entry(tmp, &m->entries, list) { if(me == tmp) { m->selected = me; return 0; } } return -EINVAL; } EXPORT_SYMBOL(menu_set_selected_entry); 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; } EXPORT_SYMBOL(menu_set_selected); int menu_set_auto_select(struct menu *m, int delay) { if (!m) return -EINVAL; m->auto_select = delay; return 0; } EXPORT_SYMBOL(menu_set_auto_select); static void print_menu(struct menu *m) { struct menu_entry *me; clear(); gotoXY(1, 2); if(m->display) { process_escape_sequence(m->display, m->display_buffer, m->display_buffer_size); puts(m->display_buffer); } else { puts("Menu : "); puts(m->name); } list_for_each_entry(me, &m->entries, list) { if(m->selected != me) print_menu_entry(m, me, 0); } if (!m->selected) { m->selected = list_first_entry(&m->entries, struct menu_entry, list); } print_menu_entry(m, m->selected, 1); } static int menu_alloc_display_buffer(struct menu *m) { int min_size; if (m->display) min_size = max((int)strlen(m->display), m->width); else min_size = m->width; if (m->display_buffer) { if (m->display_buffer_size >= min_size) return 0; m->display_buffer = realloc(m->display_buffer, min_size * sizeof(char)); } else { m->display_buffer = calloc(min_size, sizeof(char)); } if (!m->display_buffer) { perror("display_buffer"); return -ENOMEM; } m->display_buffer_size = min_size; return 0; } int menu_show(struct menu *m) { int ch; int escape = 0; int countdown; int auto_display_len = 16; uint64_t start, second; int ret; if(!m || list_empty(&m->entries)) return -EINVAL; ret = menu_alloc_display_buffer(m); if (ret) return ret; print_menu(m); countdown = m->auto_select; if (m->auto_select >= 0) { gotoXY(m->nb_entries + 2, 3); if (!m->auto_display) { printf("Auto Select in"); } else { auto_display_len = strlen(m->auto_display); printf(m->auto_display); } printf(" %2d", countdown--); } start = get_time_ns(); second = start; while (m->auto_select > 0 && !is_timeout(start, m->auto_select * SECOND)) { if (tstc()) { m->auto_select = -1; break; } if (is_timeout(second, SECOND)) { printf("\b\b%2d", countdown--); second += SECOND; } } gotoXY(m->nb_entries + 2, 3); printf("%*c", auto_display_len + 4, ' '); gotoXY(m->selected->num + 1, 3); do { if (m->auto_select >= 0) ch = KEY_RETURN; else ch = getc(); m->auto_select = -1; 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)) { 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)) { m->selected = list_entry(m->selected->list.next, struct menu_entry, list); } print_menu_entry(m, m->selected, 1); break; case ' ': if (m->selected->type != MENU_ENTRY_BOX) break; m->selected->box_state = !m->selected->box_state; if (m->selected->action) m->selected->action(m, m->selected); print_menu_entry(m, m->selected, 1); break; case KEY_RETURN: 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; } EXPORT_SYMBOL(menu_show); void menu_action_exit(struct menu *m, struct menu_entry *me) {} EXPORT_SYMBOL(menu_action_exit); struct submenu { char *submenu; struct menu_entry entry; }; static void menu_action_show(struct menu *m, struct menu_entry *me) { struct submenu *s = container_of(me, struct submenu, entry); struct menu *sm; if (me->type == MENU_ENTRY_BOX && !me->box_state) return; sm = menu_get_by_name(s->submenu); if (sm) menu_show(sm); else eprintf("no such menu: %s\n", s->submenu); } static void submenu_free(struct menu_entry *me) { struct submenu *s = container_of(me, struct submenu, entry); free(s->entry.display); free(s->submenu); free(s); } struct menu_entry *menu_add_submenu(struct menu *parent, char *submenu, char *display) { struct submenu *s = calloc(1, sizeof(*s)); int ret; if (!s) return ERR_PTR(-ENOMEM); s->submenu = strdup(submenu); s->entry.action = menu_action_show; s->entry.free = submenu_free; s->entry.display = strdup(display); if (!s->entry.display || !s->submenu) { ret = -ENOMEM; goto err_free; } ret = menu_add_entry(parent, &s->entry); if (ret) goto err_free; return &s->entry; err_free: submenu_free(&s->entry); return ERR_PTR(ret); } EXPORT_SYMBOL(menu_add_submenu); struct action_entry { char *command; struct menu_entry entry; }; static void menu_action_command(struct menu *m, struct menu_entry *me) { struct action_entry *e = container_of(me, struct action_entry, entry); int ret; const char *s = getenv(e->command); /* can be a command as boot */ if (!s) s = e->command; ret = run_command (s, 0); if (ret < 0) udelay(1000000); } static void menu_command_free(struct menu_entry *me) { struct action_entry *e = container_of(me, struct action_entry, entry); free(e->entry.display); free(e->command); free(e); } struct menu_entry *menu_add_command_entry(struct menu *m, char *display, char *command, menu_entry_type type) { struct action_entry *e = calloc(1, sizeof(*e)); int ret; if (!e) return ERR_PTR(-ENOMEM); e->command = strdup(command); e->entry.action = menu_action_command; e->entry.free = menu_command_free; e->entry.type = type; e->entry.display = strdup(display); if (!e->entry.display || !e->command) { ret = -ENOMEM; goto err_free; } ret = menu_add_entry(m, &e->entry); if (ret) goto err_free; return &e->entry; err_free: menu_command_free(&e->entry); return ERR_PTR(ret); } EXPORT_SYMBOL(menu_add_command_entry);