ADI console menu library
adi_console_menu.c
- Committer:
- mahphalke
- Date:
- 2020-02-29
- Revision:
- 1:dcc17e5a913f
- Parent:
- 0:47b30a147723
- Child:
- 2:6a97882f6144
File content as of revision 1:dcc17e5a913f:
/*! ***************************************************************************** @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(); }