Important changes to repositories hosted on mbed.com
Mbed hosted mercurial repositories are deprecated and are due to be permanently deleted in July 2026.
To keep a copy of this software download the repository Zip archive or clone locally using Mercurial.
It is also possible to export all your personal repositories from the account settings page.
adi_console_menu.c
- Committer:
- mbradley
- Date:
- 2020-03-27
- Revision:
- 2:6a97882f6144
- Parent:
- 1:dcc17e5a913f
- Child:
- 4:ced4fa6875ed
File content as of revision 2:6a97882f6144:
/*!
*****************************************************************************
@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 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);
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);
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);
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();
}