Add support for a generic UI menu system. Signed-off-by: Geoff Levand --- rules.mk | 2 ui/common/menu.c | 284 +++++++++++++++++++++++++++++++++++++++++++++++++++++++ ui/common/menu.h | 164 +++++++++++++++++++++++++++++++ 3 files changed, 449 insertions(+), 1 deletion(-) --- a/rules.mk +++ b/rules.mk @@ -39,7 +39,7 @@ discover_objs = discover/udev.o discover discover/device-handler.o discover/paths.o # client objs -ui_common_objs = ui/common/discover-client.o +ui_common_objs = ui/common/discover-client.o ui/common/menu.o ncurses_objs = twin_objs = ui/twin/pb-twin.o --- /dev/null +++ b/ui/common/menu.c @@ -0,0 +1,284 @@ +/* + * Copyright (C) 2009 Sony Computer Entertainment Inc. + * Copyright 2009 Sony Corp. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; version 2 of the License. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#define _GNU_SOURCE + +#include + +#include "log/log.h" +#include "talloc/talloc.h" +#include "ui/common/menu.h" + +/** + * menu_item_init - Allocate and initialize a new menu_item instance. + * + * Returns a pointer the the initialized struct menu_item instance or NULL + * on error. The caller is responsible for calling talloc_free() for the + * returned instance. + */ + +struct menu_item *menu_item_init(struct menu *menu, enum menu_item_type type, + enum menu_item_state state, const char* text) +{ + struct menu_item *i = talloc_zero(menu, struct menu_item); + + if (i) { + i->i_sig = menu_item_sig; + i->type = type; + i->state = state; + i->text = text ? text : ""; + i->status = ""; + } + + return i; +} + +/** + * menu_item_delete - Delete a menu_item instance. + * + */ + +void menu_item_delete(struct menu_item *i) +{ + assert(i->i_sig == menu_item_sig); + i->i_sig = menu_removed_sig; + talloc_free(i); +} + +/** + * menu_item_set_text - Set menu_item text. + * @text: Reference taken, must be persistant. + */ + +void menu_item_set_text(struct menu_item *i, const char *text) +{ + i->text = text ? text : ""; +} + +/** + * menu_init - Allocate and initialize a new menu instance. + * + * Returns a pointer the the initialized struct menu instance or NULL on error. + * The caller is responsible for calling talloc_free() for the returned + * instance. + */ + +struct menu *menu_init(void* ctx) +{ + struct menu *menu = talloc_zero(ctx, struct menu); + + if (menu) { + menu->m_sig = menu_sig; + list_init(&menu->items); + } + + return menu; +} + +/** + * menu_delete - Delete a menu instance. + * + */ + +void menu_delete(struct menu *menu) +{ + assert(menu->m_sig == menu_sig); + menu->m_sig = menu_removed_sig; + talloc_free(menu); +} + +/** + * menu_draw - Display the menu. + * @menu: A menu instance returned from menu_init(). + */ + +void menu_draw(const struct menu *menu) +{ + struct menu_item *i; + int y; + + assert(menu->m_sig == menu_sig); + assert(menu->draw_item); + + if (menu->draw_start) + menu->draw_start(menu); + y = 0; + + list_for_each_entry(&menu->items, i, list) + if (i->state == state_visable || i->state == state_selected) { + assert(i->text); + menu->draw_item(menu, i, y++); + } + + if (menu->draw_end) + menu->draw_end(menu); +} + +static void menu_set_status(struct menu *menu, const char *text) +{ + if (menu->status) + menu_item_set_text(menu->status, text); +} + +/** + * menu_key_up - + * @menu: A menu instance returned from menu_init(). + * + * Calls menu_draw(). + */ + +void menu_key_up(struct menu *menu) +{ + struct menu_item *i; + struct menu_item *current = NULL; + struct menu_item *next = NULL; + + assert(menu->m_sig == menu_sig); + + list_for_each_entry(&menu->items, i, list) { + if (i->state == state_selected) { + current = i; + break; + } + if (i->type != type_text && i->state == state_visable) + next = i; + } + + assert(current); + + if (next) { + current->state = state_visable; + next->state = state_selected; + menu_set_status(menu, next->status); + menu_draw(menu); + } +} + +/** + * menu_key_down - + * @menu: A menu instance returned from menu_init(). + */ + +void menu_key_down(struct menu *menu) +{ + struct menu_item *i; + struct menu_item *current = NULL; + struct menu_item *next = NULL; + + assert(menu->m_sig == menu_sig); + + list_for_each_entry(&menu->items, i, list) + if (i->state == state_selected) { + current = i; + break; + } + + assert(current); + + list_continue_each_entry(&menu->items, i, list) + if (i->type != type_text && i->state == state_visable) { + next = i; + break; + } + + if (next) { + current->state = state_visable; + next->state = state_selected; + menu_set_status(menu, next->status); + menu_draw(menu); + } +} + +/** + * menu_set_item_text - Set menu_item text and redraw menu. + */ + +void menu_set_item_text(struct menu *menu, struct menu_item *i, + const char *text) +{ + menu_item_set_text(i, text); + menu_draw(menu); +} + +/** + * menu_item_kexec - Run kexec with the supplied boot options. + * @opt: Pointer to a @struct boot_option data structure. + */ + +static int menu_item_kexec(const struct menu_item_kexec *kexec) +{ + pb_log("%s: %s\n", __func__, kexec->opt->name); + return 0; +} + +/** + * menu_item_exec - Run exec with the supplied command. + * @cmd: A command string. + */ + +static int menu_item_exec(const char* cmd) +{ + pb_log("%s: %s\n", __func__, cmd); + return 0; +} + +/** + * menu_item_process - Execute the action associated with the item. + * @i: The item to execute. + */ + +static int menu_item_process(const struct menu_item *i) +{ + assert(i->i_sig == menu_item_sig); + + switch (i->type) { + case type_kexec: + pb_log("%s: kexec: %s\n", __func__, i->text); + return menu_item_kexec(&i->kexec); + case type_callback: + pb_log("%s: callback: %s\n", __func__, i->text); + return i->cb->fn(i->cb->arg); + case type_cmd: + pb_log("%s: cmd: %s\n", __func__, i->text); + return menu_item_exec(i->cmd); + case type_text: + default: + assert(0 && "bad type"); + break; + } + return -1; +} + +/** + * menu_key_execute - + * @menu: A menu instance returned from menu_init(). + */ + +int menu_key_execute(struct menu *menu) +{ + struct menu_item *i; + + assert(menu->m_sig == menu_sig); + + list_for_each_entry(&menu->items, i, list) + if (i->state == state_selected) + return menu_item_process(i); + + assert(0 && "no state_selected"); + return -1; +} --- /dev/null +++ b/ui/common/menu.h @@ -0,0 +1,164 @@ +/* + * 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_MENU_H) +#define _PB_MENU_H + +#include "list/list.h" +#include "pb-protocol/pb-protocol.h" + +#if defined(__cplusplus) +# define EXTERN_C extern "C" +#else +# define EXTERN_C +#endif + +enum menu_sig { + menu_sig = 333, + menu_item_sig = 444, + menu_removed_sig = -555, +}; + +/** + * enum menu_item_type - The type or action of the menu item. + * @type_text: A simple text item with no action, cannot be selected. + * @type_kexec: A selectable item that runs kexec. + * @type_callback: A selectable item that has an associated callback routine. + * @type_cmd: A selectable item that runs a command via exec(). + */ + +enum menu_item_type { + type_text = 0, + type_callback, + type_cmd, + type_kexec, +}; + +/** + * struct menu_item_callback - Data structure for @type_callback items. + * @fn: The user supplied callback routine. + * @arg: A user supplied argument passed to the callback routine. + */ + +struct menu_item_callback { + int (*fn)(void *arg); + void* arg; +}; + +/** + * struct menu_item_kexec - Data structure for @type_kexec items. + * @dev: A pointer to the device. + * @opt: A pointer to the boot option. + */ + +struct menu_item_kexec { + const struct device *dev; + const struct boot_option *opt; +}; + +/** + * enum menu_item_state + * @state_visable: The default state, visable. + * @state_hidden: Used to hide items to declutter the display. + * @state_selected: Only a single item can be selected at a time. + */ + +enum menu_item_state { + state_visable = 0, + state_hidden, + state_selected, +}; + +/** + * struct menu_item - Data structure defining a single menu item. + * @i_sig: Signature for callback type checking, should be menu_item_sig. + * @text: The text displayed for the menu item. + */ + +struct menu_item { + enum menu_sig i_sig; + struct list_item list; + enum menu_item_type type; + enum menu_item_state state; + const char* text; + const char* status; + union { + struct menu_item_kexec kexec; + const char* cmd; + const struct menu_item_callback* cb; + }; +}; + +/** + * struct menu - Data structure defining complete menu. + * @m_sig: Signature for callback type checking, should be menu_sig. + * @items: The list of menu_items to be displayed as the menu. + * @status: Pointer to a menu_item (in @items) to be used as the menu's status + * line. Set with menu_set_status_item(). + * @insert_start: Optional pointer to a menu_item (in @items) that defines a + * starting point for adding dynamic items. + * @insert_end: Optional pointer to a menu_item (in @items) that defines a + * end point for adding dynamic items. + * @ui_info: FIXME need this??? + * @draw_start: Optional user supplied callback to prepare for drawing. + * @draw_item: User callback to draw an item, called once for each item in the + * menu. + * @draw_end: Optional user supplied callback to finalise drawing. + */ + +struct menu { + enum menu_sig m_sig; + struct list items; + struct menu_item *status; + struct menu_item *insert_start; + struct menu_item *insert_end; + //void *ui_info; + void (*draw_start)(const struct menu *menu); + void (*draw_item)(const struct menu *menu, const struct menu_item *i, + int y); + void (*draw_end)(const struct menu *menu); +}; + +#define to_menu_item(_arg) \ + (assert(((struct menu_item *)(_arg))->i_sig == menu_item_sig), \ + ((struct menu_item *)(_arg))) + +EXTERN_C struct menu_item *menu_item_init(struct menu *menu, + enum menu_item_type type, enum menu_item_state state, const char* text); +EXTERN_C void menu_item_delete(struct menu_item *i); +EXTERN_C void menu_item_set_text(struct menu_item *i, const char *text); + +#define to_menu(_arg) \ + (assert(((struct menu *)(_arg))->m_sig == menu_sig), \ + ((struct menu *)(_arg))) + +EXTERN_C struct menu *menu_init(void* ctx); +EXTERN_C void menu_delete(struct menu *menu); +EXTERN_C void menu_draw(const struct menu *menu); +EXTERN_C void menu_key_up(struct menu *menu); +EXTERN_C void menu_key_down(struct menu *menu); +EXTERN_C int menu_key_execute(struct menu *menu); +EXTERN_C void menu_set_item_text(struct menu *menu, struct menu_item *i, + const char *text); // FIXME need this??? + +static inline void menu_set_status_item(struct menu *menu, struct menu_item *i) +{ + menu->status = i; +} + +#endif --