ADI console menu library
Diff: adi_console_menu.c
- Revision:
- 1:dcc17e5a913f
- Parent:
- 0:47b30a147723
- Child:
- 2:6a97882f6144
diff -r 47b30a147723 -r dcc17e5a913f adi_console_menu.c --- a/adi_console_menu.c Fri Feb 28 20:04:52 2020 +0000 +++ b/adi_console_menu.c Sat Feb 29 04:11:15 2020 +0000 @@ -0,0 +1,317 @@ +/*! + ***************************************************************************** + @file: adi_console_menu.c + + @brief: A simple console menu manager handler + + @details: A way to define using arrays of structs a set of menus that can + be displayed to a user, easily, with all user interaction handled + by the library, leaving only the implementation of the menu actions + to be done by the library user. + ----------------------------------------------------------------------------- + Copyright (c) 2019, 2020 Analog Devices, Inc. + All rights reserved. + + This software is proprietary to Analog Devices, Inc. and its licensors. + By using this software you agree to the terms of the associated + Analog Devices Software License Agreement. + +*****************************************************************************/ + +#include <stdio.h> +#include <stdlib.h> +#include <ctype.h> +#include <string.h> +#include <stdbool.h> +#include <assert.h> + +#include "adi_console_menu.h" + + +#define DIV_STRING "\t==================================================" + +/*! + * @brief displays the text of a console menu + * + * @details + */ +static void adi_display_console_menu(const console_menu * menu) +{ + adi_clear_console(); + + // call headerItem to allow display of other content + if (menu->headerItem != NULL) { + menu->headerItem(); + printf(DIV_STRING EOL); + } + + /* + * Display the menu title and menuItems + * The shortcutKey is used to display '[A]' before the dispayText + */ + printf("\t%s" EOL "\t", menu->title); + // show an underline to distinguish title from item + for (uint8_t i = 0; i < strlen(menu->title); i++) { + putchar('-'); + } + // Extend underline past end of string, and then new line + printf("--" EOL); + + // If the shortcutKey is not unique, first found is used + for (uint8_t i = 0; i < menu->itemCount; i ++) { + if (menu->items[i].shortcutKey == '\00') { + // No shortcut key defined, but display item text if available + printf("\t%s" EOL, menu->items[i].text); + } else { + printf("\t[%c] %s" EOL, toupper(menu->items[i].shortcutKey), + menu->items[i].text); + } + } + if (menu->enableEscapeKey) { + printf(EOL "\t[ESC] Exit Menu" EOL); + } + + printf(EOL "\tPlease make a selection." EOL); + + // call footerItem to allow display of other content + if (menu->footerItem != NULL) { + printf(DIV_STRING EOL); + menu->footerItem(); + } +} + + +/*! + * @brief Display a consoleMenu and handle User interaction + * + * @details This displays the menuItems defined by the console menu, and + * handles all user interaction for the menu. + */ +int32_t adi_do_console_menu(const console_menu * menu) +{ + int32_t itemSelected = MENU_ESCAPED; + bool enableKeyScan = true; + + adi_display_console_menu(menu); + + /* + * Loop waiting for valid user input. menuItem index is returned if + * user presses a valid menu option. + */ + do { + char keyPressed = toupper(getchar()); + + if (menu->enableEscapeKey) { + if (keyPressed == ESCAPE_KEY_CODE) { + itemSelected = MENU_ESCAPED; + enableKeyScan = false; + break; + } + } + + for (uint8_t i = 0; i < menu->itemCount; i ++) { + if (toupper(menu->items[i].shortcutKey) == keyPressed) { + itemSelected = i; + + // If the menuAction function pointer is not NULL, call the action + if (menu->items[i].action != NULL) { + switch (menu->items[i].action(menu->items[i].id)) { + case MENU_DONE: { + enableKeyScan = false; + break; + } + case MENU_CONTINUE: + default: { + enableKeyScan = true; + adi_display_console_menu(menu); + break; + } + } + } + break; + } + } + } while (enableKeyScan); + + return (itemSelected); +} + + +/*! + * @brief Clears the standard input buffer (typically + * needed after scanf function) + * + * @details + */ +static void flushf(void) +{ + // flush the standard input (i.e. clear the input buffer) + while((getchar()) != '\n'); +} + + +/*! + * @brief Reads a decimal string from the user + * + * @param input_len max number of character to accept from the user + * + * @return The integer value entered + * + * @details Allows a user to type in number, echoing back to the user, + * up to input_len chars + * + * @note Only positive integer numbers are supported currently + */ +int32_t adi_get_decimal_int(uint8_t input_len) +{ + char buf[20] = {0}; + uint8_t buf_index = 0; + char ch; + bool loop = true; + + assert(input_len < 19); + + do { + ch = getchar(); + if (isdigit(ch) && buf_index < (input_len)) { + // echo and store it as buf not full + buf[buf_index++] = ch; + putchar(ch); + } + if ((ch == '\x7F') && (buf_index > 0)) { + //backspace and at least 1 char in buffer + buf[buf_index--] = '\x00'; + putchar(ch); + } + if ((ch == '\x0D') || (ch == '\x0A')) { + // return key pressed, all done, null terminate string + buf[buf_index] = '\x00'; + loop = false; + } + } while(loop); + + flushf(); + return atoi(buf); +} + +/*! + * @brief Reads a hexadecimal number from the user + * + * @param input_len max number of character to accept from the user + * + * @return The integer value entered + * +* @details Allows a user to type in a hexnumber, echoing back to the user, + * up to input_len chars + */ +uint32_t adi_get_hex_integer(uint8_t input_len) +{ + char buf[9] = {0}; + uint8_t buf_index = 0; + char ch; + bool loop = true; + + assert(input_len < 8); + + do { + ch = getchar(); + if (isxdigit(ch) && buf_index < (input_len)) { + // echo and store it as buf not full + buf[buf_index++] = ch; + putchar(ch); + } + if ((ch == '\x7F') && (buf_index > 0)) { + //backspace and at least 1 char in buffer + buf[buf_index--] = '\x00'; + putchar(ch); + } + if ((ch == '\x0D') || (ch == '\x0A')) { + // return key pressed, all done, null terminate string + buf[buf_index] = '\x00'; + loop = false; + } + } while(loop); + + flushf(); + return strtol(buf, NULL, 16); +} + + +/*! + * @brief Reads a floating string from the user + * + * @param input_len max number of character to accept from the user + * + * @return The float value entered + * + * @details Allows a user to type in number, echoing back to the user, + * up to input_len chars + * + * @note Only positive floating point numbers are supported currently + */ +float adi_get_decimal_float(uint8_t input_len) +{ + char buf[20] = { 0 }; + uint8_t buf_index = 0; + char ch; + bool loop = true; + + assert(input_len < 19); + + do { + ch = getchar(); + if ((isdigit(ch) || (ch == '.')) && buf_index < (input_len)) { + // echo and store it as buf not full + buf[buf_index++] = ch; + putchar(ch); + } + if ((ch == '\x7F') && (buf_index > 0)) { + //backspace and at least 1 char in buffer + buf[buf_index--] = '\x00'; + putchar(ch); + } + if ((ch == '\x0D') || (ch == '\x0A')) { + // return key pressed, all done, null terminate string + buf[buf_index] = '\x00'; + loop = false; + } + } while (loop); + + flushf(); + return atof(buf); +} + + +/*! + * @brief Clears the console terminal + * + * @details Clears the console terminal using VT100 escape code, or can be changed to + * output blank lines if serial link doesn't support VT100. + */ +void adi_clear_console(void) +{ + /* + * clear console and move cursor to home location, followed by move to home location. + * Dedicated call to move home is because sometimes first move home doesn't work + * \r\n required to flush the uart buffer. + */ + printf("\x1B[2J\x1B[H\r\n"); + + /* + * if VT100 is not supported, this can be enabled instead, but menu display may not work well + */ +// for (uint8_t = 0; i < 100; i++) +// printf("\r\n\r"); +} + + +/*! + * @brief waits for any key to be pressed, and displays a prompt to the user + * + * @details + */ +void adi_press_any_key_to_continue(void) +{ + printf("\r\nPress any key to continue...\r\n"); + getchar(); +}