feat!: command processing moved to task
This commit is contained in:
1 parent
8dc7385a90
commit
4116a94c5c
5 files changed
+866
-188
No files matched your search
@@ -22,7 +22,15 @@ static const char *TAG = "zh_menu";
|
||||
return err; \
|
||||
}
|
||||
|
||||
#define ZH_MENU_MAX_DEPTH 8 /*!< Maximum depth of nested submenus. Limits the navigation stack size */
|
||||
#define ZH_ERROR_CHECK_CONT(cond, cleanup, msg, ...) \
|
||||
if (!(cond)) \
|
||||
{ \
|
||||
ZH_LOGE(msg, ESP_FAIL, ##__VA_ARGS__); \
|
||||
cleanup; \
|
||||
}
|
||||
|
||||
#define ZH_MENU_MAX_DEPTH 8
|
||||
#define ZH_MENU_ARG_MAX_SIZE 128
|
||||
|
||||
/**
|
||||
* @brief Internal representation of the menu handle.
|
||||
@@ -48,21 +56,105 @@ struct _zh_menu_handle_t
|
||||
uint8_t current_index; /*!< Index of the highlighted item on the current level */
|
||||
bool is_activated; /*!< Menu activity flag */
|
||||
bool is_selected; /*!< Selection state flag of the current item */
|
||||
portMUX_TYPE lock; /*!< Spinlock protecting fields accessed from ISR and task context */
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief Internal queue item for passing navigation commands.
|
||||
*
|
||||
* Stores the command, associated handle, argument size and a copy of
|
||||
* the user argument data. Used to transfer commands from ISR/task context
|
||||
* to the background processing task.
|
||||
*/
|
||||
typedef struct
|
||||
{
|
||||
zh_menu_navigate_t command; /*!< Navigation command to process */
|
||||
zh_menu_handle_t *handle; /*!< Menu handle associated with the command */
|
||||
uint32_t arg_size; /*!< Size of the argument data in bytes */
|
||||
uint8_t arg[ZH_MENU_ARG_MAX_SIZE]; /*!< Argument data buffer passed to user callbacks */
|
||||
} zh_menu_queue_t;
|
||||
|
||||
TaskHandle_t zh_menu = NULL;
|
||||
static QueueHandle_t _queue_handle = NULL;
|
||||
|
||||
static zh_menu_stats_t _stats = {0};
|
||||
volatile static uint8_t _menu_counter = 0;
|
||||
|
||||
/**
|
||||
* @brief Validate the initialization configuration.
|
||||
*
|
||||
* Checks task priority, stack size, queue size and root node parameters.
|
||||
* Copies user callbacks and initial menu state into the handle.
|
||||
*
|
||||
* @param config Pointer to the initialization configuration (must not be NULL)
|
||||
* @param handle Pointer to the menu handle being initialized (must not be NULL)
|
||||
*
|
||||
* @return ESP_OK on success
|
||||
* @return ESP_ERR_INVALID_ARG if any configuration parameter is out of range or root is invalid
|
||||
*/
|
||||
static esp_err_t _zh_menu_validate_config(const zh_menu_init_config_t *config, zh_menu_handle_t *handle);
|
||||
|
||||
/**
|
||||
* @brief Create the internal command queue if it does not exist.
|
||||
*
|
||||
* The queue is created only once, on the first initialization.
|
||||
* Subsequent calls return success without recreating the queue.
|
||||
*
|
||||
* @param config Pointer to the initialization configuration (must not be NULL)
|
||||
*
|
||||
* @return ESP_OK on success
|
||||
* @return ESP_FAIL if queue creation fails
|
||||
*/
|
||||
static esp_err_t _zh_menu_resources_init(const zh_menu_init_config_t *config);
|
||||
|
||||
/**
|
||||
* @brief Create the processing task if it does not exist.
|
||||
*
|
||||
* The task is created only once, on the first initialization.
|
||||
* Subsequent calls return success without recreating the task.
|
||||
*
|
||||
* @param config Pointer to the initialization configuration (must not be NULL)
|
||||
*
|
||||
* @return ESP_OK on success
|
||||
* @return ESP_FAIL if task creation fails
|
||||
*/
|
||||
static esp_err_t _zh_menu_task_init(const zh_menu_init_config_t *config);
|
||||
|
||||
/**
|
||||
* @brief Delete the processing task and queue on rollback.
|
||||
*
|
||||
* Called when initialization fails after task and queue have been created.
|
||||
* Cleans up resources to avoid leaks.
|
||||
*/
|
||||
static void _zh_menu_rollback_task_and_resources(void);
|
||||
|
||||
/**
|
||||
* @brief Background task that processes menu navigation commands from the queue.
|
||||
*
|
||||
* Receives commands and executes state transitions, invoking user callbacks
|
||||
* for position changes, menu entry, exit and item selection.
|
||||
*
|
||||
* @param pvParameter Task parameter (unused).
|
||||
*/
|
||||
static void _zh_menu_isr_processing_task(void *pvParameter);
|
||||
|
||||
esp_err_t zh_menu_init(const zh_menu_init_config_t *config, zh_menu_handle_t **handle)
|
||||
{
|
||||
ZH_LOGI("Menu initialization started.");
|
||||
ZH_ERROR_CHECK(config != NULL && config->root != NULL && handle != NULL && config->root->submenu != NULL && config->root->item_numbers != 0, ESP_ERR_INVALID_ARG, NULL, "Menu initialization failed. Invalid argument.");
|
||||
ZH_ERROR_CHECK(config != NULL && handle != NULL, ESP_ERR_INVALID_ARG, NULL, "Menu initialization failed. Invalid argument.");
|
||||
ZH_ERROR_CHECK(*handle == NULL, ESP_ERR_INVALID_STATE, NULL, "Menu initialization failed. Menu is already initialized.");
|
||||
*handle = heap_caps_calloc(1, sizeof(zh_menu_handle_t), MALLOC_CAP_8BIT);
|
||||
ZH_ERROR_CHECK(*handle != NULL, ESP_ERR_NO_MEM, NULL, "Menu initialization failed. Failed to allocate menu handle.");
|
||||
(*handle)->change_cb = config->change_cb;
|
||||
(*handle)->enter_cb = config->enter_cb;
|
||||
(*handle)->exit_cb = config->exit_cb;
|
||||
(*handle)->current_menu = config->root;
|
||||
(*handle)->root = config->root;
|
||||
(*handle)->loop = config->loop;
|
||||
ZH_ERROR_CHECK(_zh_menu_validate_config(config, *handle) == ESP_OK, ESP_FAIL, heap_caps_free(*handle); *handle = NULL, "Menu initialization failed. Initial configuration check failed.");
|
||||
ZH_ERROR_CHECK(_zh_menu_resources_init(config) == ESP_OK, ESP_FAIL, heap_caps_free(*handle); *handle = NULL, "Menu initialization failed. Resources initialization failed.");
|
||||
ZH_ERROR_CHECK(_zh_menu_task_init(config) == ESP_OK, ESP_FAIL, _zh_menu_rollback_task_and_resources();
|
||||
heap_caps_free(*handle); *handle = NULL, "Menu initialization failed. Processing task initialization failed.");
|
||||
++_menu_counter;
|
||||
portMUX_INITIALIZE(&(*handle)->lock);
|
||||
if (_stats.min_stack_size == 0)
|
||||
{
|
||||
_stats.min_stack_size = config->stack_size;
|
||||
}
|
||||
ZH_LOGI("Menu initialization completed successfully.");
|
||||
return ESP_OK;
|
||||
}
|
||||
@@ -71,172 +163,41 @@ esp_err_t zh_menu_deinit(zh_menu_handle_t **handle)
|
||||
{
|
||||
ZH_LOGI("Menu deinitialization started.");
|
||||
ZH_ERROR_CHECK(handle != NULL && *handle != NULL, ESP_ERR_INVALID_ARG, NULL, "Menu deinitialization failed. Invalid argument.");
|
||||
if (--_menu_counter == 0)
|
||||
{
|
||||
if (zh_menu != NULL)
|
||||
{
|
||||
vTaskDelete(zh_menu);
|
||||
zh_menu = NULL;
|
||||
}
|
||||
if (_queue_handle != NULL)
|
||||
{
|
||||
vQueueDelete(_queue_handle);
|
||||
_queue_handle = NULL;
|
||||
}
|
||||
}
|
||||
heap_caps_free(*handle);
|
||||
*handle = NULL;
|
||||
ZH_LOGI("Menu deinitialization completed successfully.");
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
esp_err_t zh_menu_set(zh_menu_handle_t **handle, zh_menu_navigate_t command, void *arg)
|
||||
esp_err_t zh_menu_set(zh_menu_handle_t **handle, zh_menu_navigate_t command, const void *arg, uint8_t arg_size)
|
||||
{
|
||||
ZH_LOGI("Menu set position started.");
|
||||
ZH_ERROR_CHECK(handle != NULL && *handle != NULL && command >= ZH_MENU_ENTER && command < ZH_MENU_MAX, ESP_ERR_INVALID_ARG, NULL, "Menu set position failed. Invalid argument.");
|
||||
switch (command)
|
||||
ZH_ERROR_CHECK(arg_size <= ZH_MENU_ARG_MAX_SIZE, ESP_ERR_INVALID_ARG, NULL, "Menu set position failed. Invalid argument.");
|
||||
ZH_ERROR_CHECK(arg_size == 0 || arg != NULL, ESP_ERR_INVALID_ARG, NULL, "Menu set position failed. Invalid argument.");
|
||||
taskENTER_CRITICAL(&(*handle)->lock);
|
||||
zh_menu_queue_t menu_queue = {.handle = *handle,
|
||||
.command = command,
|
||||
.arg_size = arg_size};
|
||||
if (arg_size > 0)
|
||||
{
|
||||
case ZH_MENU_ENTER:
|
||||
if ((*handle)->is_activated == false)
|
||||
{
|
||||
(*handle)->is_activated = true;
|
||||
(*handle)->is_selected = false;
|
||||
(*handle)->current_menu = (*handle)->root;
|
||||
(*handle)->current_index = 0;
|
||||
(*handle)->menu_depth = 0;
|
||||
memset((*handle)->menu_stack, 0, sizeof((*handle)->menu_stack));
|
||||
if ((*handle)->enter_cb != NULL)
|
||||
{
|
||||
(*handle)->enter_cb(arg);
|
||||
}
|
||||
if ((*handle)->change_cb != NULL)
|
||||
{
|
||||
(*handle)->change_cb((*handle)->current_menu, (*handle)->current_index);
|
||||
}
|
||||
}
|
||||
break;
|
||||
case ZH_MENU_SHOW:
|
||||
if ((*handle)->is_activated == true)
|
||||
{
|
||||
if ((*handle)->change_cb != NULL)
|
||||
{
|
||||
(*handle)->change_cb((*handle)->current_menu, (*handle)->current_index);
|
||||
}
|
||||
}
|
||||
break;
|
||||
case ZH_MENU_UP:
|
||||
if ((*handle)->current_menu == NULL || (*handle)->current_menu->item_numbers == 0)
|
||||
{
|
||||
break;
|
||||
}
|
||||
if ((*handle)->is_activated == true && (*handle)->is_selected == false)
|
||||
{
|
||||
if ((*handle)->current_index > 0)
|
||||
{
|
||||
--(*handle)->current_index;
|
||||
}
|
||||
else if ((*handle)->loop == true)
|
||||
{
|
||||
(*handle)->current_index = (*handle)->current_menu->item_numbers - 1;
|
||||
}
|
||||
if ((*handle)->change_cb != NULL)
|
||||
{
|
||||
(*handle)->change_cb((*handle)->current_menu, (*handle)->current_index);
|
||||
}
|
||||
}
|
||||
break;
|
||||
case ZH_MENU_DOWN:
|
||||
if ((*handle)->current_menu == NULL || (*handle)->current_menu->item_numbers == 0)
|
||||
{
|
||||
break;
|
||||
}
|
||||
if ((*handle)->is_activated == true && (*handle)->is_selected == false)
|
||||
{
|
||||
if ((*handle)->current_index < (*handle)->current_menu->item_numbers - 1)
|
||||
{
|
||||
++(*handle)->current_index;
|
||||
}
|
||||
else if ((*handle)->loop == true)
|
||||
{
|
||||
(*handle)->current_index = 0;
|
||||
}
|
||||
if ((*handle)->change_cb != NULL)
|
||||
{
|
||||
(*handle)->change_cb((*handle)->current_menu, (*handle)->current_index);
|
||||
}
|
||||
}
|
||||
break;
|
||||
case ZH_MENU_SELECT:
|
||||
if ((*handle)->is_activated == true)
|
||||
{
|
||||
if ((*handle)->current_menu->submenu == NULL || (*handle)->current_index >= (*handle)->current_menu->item_numbers)
|
||||
{
|
||||
break;
|
||||
}
|
||||
if ((*handle)->is_selected == true)
|
||||
{
|
||||
if ((*handle)->current_menu->submenu[(*handle)->current_index].item_cb != NULL)
|
||||
{
|
||||
(*handle)->current_menu->submenu[(*handle)->current_index].item_cb(arg);
|
||||
}
|
||||
}
|
||||
else if ((*handle)->current_menu->submenu[(*handle)->current_index].selectable == true)
|
||||
{
|
||||
(*handle)->is_selected = true;
|
||||
if ((*handle)->current_menu->submenu[(*handle)->current_index].item_cb != NULL)
|
||||
{
|
||||
(*handle)->current_menu->submenu[(*handle)->current_index].item_cb(arg);
|
||||
}
|
||||
}
|
||||
else if ((*handle)->current_menu->submenu[(*handle)->current_index].item_cb != NULL)
|
||||
{
|
||||
(*handle)->current_menu->submenu[(*handle)->current_index].item_cb(arg);
|
||||
}
|
||||
else if ((*handle)->current_menu->submenu[(*handle)->current_index].submenu != NULL && (*handle)->current_menu->submenu[(*handle)->current_index].item_numbers > 0)
|
||||
{
|
||||
if ((*handle)->menu_depth >= ZH_MENU_MAX_DEPTH)
|
||||
{
|
||||
break;
|
||||
}
|
||||
(*handle)->menu_stack[(*handle)->menu_depth] = (*handle)->current_menu;
|
||||
++(*handle)->menu_depth;
|
||||
(*handle)->current_menu = &(*handle)->current_menu->submenu[(*handle)->current_index];
|
||||
(*handle)->current_index = 0;
|
||||
if ((*handle)->change_cb != NULL)
|
||||
{
|
||||
(*handle)->change_cb((*handle)->current_menu, (*handle)->current_index);
|
||||
}
|
||||
}
|
||||
}
|
||||
break;
|
||||
case ZH_MENU_BACK:
|
||||
if ((*handle)->is_activated == true)
|
||||
{
|
||||
if ((*handle)->is_selected == true)
|
||||
{
|
||||
(*handle)->is_selected = false;
|
||||
if ((*handle)->change_cb != NULL)
|
||||
{
|
||||
(*handle)->change_cb((*handle)->current_menu, (*handle)->current_index);
|
||||
}
|
||||
break;
|
||||
}
|
||||
if ((*handle)->menu_depth > 0)
|
||||
{
|
||||
--(*handle)->menu_depth;
|
||||
(*handle)->current_menu = (*handle)->menu_stack[(*handle)->menu_depth];
|
||||
(*handle)->menu_stack[(*handle)->menu_depth] = NULL;
|
||||
(*handle)->current_index = 0;
|
||||
if ((*handle)->change_cb != NULL)
|
||||
{
|
||||
(*handle)->change_cb((*handle)->current_menu, (*handle)->current_index);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
(*handle)->is_activated = false;
|
||||
(*handle)->is_selected = false;
|
||||
(*handle)->current_menu = (*handle)->root;
|
||||
(*handle)->current_index = 0;
|
||||
(*handle)->menu_depth = 0;
|
||||
memset((*handle)->menu_stack, 0, sizeof((*handle)->menu_stack));
|
||||
if ((*handle)->exit_cb != NULL)
|
||||
{
|
||||
(*handle)->exit_cb(arg);
|
||||
}
|
||||
}
|
||||
}
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
memcpy(menu_queue.arg, arg, arg_size);
|
||||
}
|
||||
taskEXIT_CRITICAL(&(*handle)->lock);
|
||||
ZH_ERROR_CHECK_CONT(xQueueSend(_queue_handle, &menu_queue, pdMS_TO_TICKS(10)) == pdTRUE, ++_stats.queue_overflow_error, "Menu isr processing failed. Queue overflow.");
|
||||
ZH_LOGI("Menu set position completed successfully.");
|
||||
return ESP_OK;
|
||||
}
|
||||
@@ -245,8 +206,275 @@ esp_err_t zh_menu_get(zh_menu_handle_t **handle, zh_menu_status_t *status)
|
||||
{
|
||||
ZH_LOGI("Menu get status started.");
|
||||
ZH_ERROR_CHECK(handle != NULL && *handle != NULL && status != NULL, ESP_ERR_INVALID_ARG, NULL, "Menu get status failed. Invalid argument.");
|
||||
taskENTER_CRITICAL(&(*handle)->lock);
|
||||
status->is_active = (*handle)->is_activated;
|
||||
status->is_selected = (*handle)->is_selected;
|
||||
taskEXIT_CRITICAL(&(*handle)->lock);
|
||||
ZH_LOGI("Menu get status completed successfully.");
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
const zh_menu_stats_t *zh_menu_get_stats(void)
|
||||
{
|
||||
return &_stats;
|
||||
}
|
||||
|
||||
void zh_menu_reset_stats(void)
|
||||
{
|
||||
ZH_LOGI("Error statistic reset started.");
|
||||
_stats.queue_overflow_error = 0;
|
||||
_stats.min_stack_size = 0;
|
||||
ZH_LOGI("Error statistic reset successfully.");
|
||||
}
|
||||
|
||||
static esp_err_t _zh_menu_validate_config(const zh_menu_init_config_t *config, zh_menu_handle_t *handle)
|
||||
{
|
||||
ZH_ERROR_CHECK(config->task_priority >= 1 && config->stack_size >= configMINIMAL_STACK_SIZE, ESP_ERR_INVALID_ARG, NULL, "Invalid task settings.");
|
||||
ZH_ERROR_CHECK(config->queue_size >= 1, ESP_ERR_INVALID_ARG, NULL, "Invalid queue size.");
|
||||
ZH_ERROR_CHECK(config->root != NULL && config->root->submenu != NULL && config->root->item_numbers > 0, ESP_ERR_INVALID_ARG, NULL, "Invalid argument.");
|
||||
handle->change_cb = config->change_cb;
|
||||
handle->enter_cb = config->enter_cb;
|
||||
handle->exit_cb = config->exit_cb;
|
||||
handle->current_menu = config->root;
|
||||
handle->root = config->root;
|
||||
handle->loop = config->loop;
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
static esp_err_t _zh_menu_resources_init(const zh_menu_init_config_t *config)
|
||||
{
|
||||
if (_menu_counter == 0)
|
||||
{
|
||||
_queue_handle = xQueueCreate(config->queue_size, sizeof(zh_menu_queue_t));
|
||||
ZH_ERROR_CHECK(_queue_handle != NULL, ESP_FAIL, NULL, "Failed to create queue.");
|
||||
}
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
static esp_err_t _zh_menu_task_init(const zh_menu_init_config_t *config)
|
||||
{
|
||||
if (_menu_counter == 0)
|
||||
{
|
||||
ZH_ERROR_CHECK(xTaskCreatePinnedToCore(&_zh_menu_isr_processing_task, "zh_menu_isr_processing", config->stack_size, NULL, config->task_priority, &zh_menu, tskNO_AFFINITY) == pdPASS,
|
||||
ESP_FAIL, NULL, "Failed to create isr processing task.");
|
||||
}
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
static void _zh_menu_rollback_task_and_resources(void)
|
||||
{
|
||||
if (_menu_counter == 0)
|
||||
{
|
||||
if (zh_menu != NULL)
|
||||
{
|
||||
vTaskDelete(zh_menu);
|
||||
zh_menu = NULL;
|
||||
}
|
||||
if (_queue_handle != NULL)
|
||||
{
|
||||
vQueueDelete(_queue_handle);
|
||||
_queue_handle = NULL;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void _zh_menu_isr_processing_task(void *pvParameter)
|
||||
{
|
||||
(void)pvParameter;
|
||||
zh_menu_queue_t menu_queue = {0};
|
||||
while (xQueueReceive(_queue_handle, &menu_queue, portMAX_DELAY) == pdTRUE)
|
||||
{
|
||||
zh_menu_handle_t *handle = menu_queue.handle;
|
||||
void (*enter_cb)(void *) = NULL;
|
||||
void (*exit_cb)(void *) = NULL;
|
||||
void (*item_cb)(void *) = NULL;
|
||||
void (*change_cb)(zh_menu_node_t *, uint8_t) = NULL;
|
||||
zh_menu_node_t *change_node = NULL;
|
||||
uint8_t change_index = 0;
|
||||
void *cb_arg = (menu_queue.arg_size > 0) ? (void *)menu_queue.arg : NULL;
|
||||
taskENTER_CRITICAL(&handle->lock);
|
||||
switch (menu_queue.command)
|
||||
{
|
||||
case ZH_MENU_ENTER:
|
||||
if (handle->is_activated == false)
|
||||
{
|
||||
handle->is_activated = true;
|
||||
handle->is_selected = false;
|
||||
handle->current_menu = handle->root;
|
||||
handle->current_index = 0;
|
||||
handle->menu_depth = 0;
|
||||
memset(handle->menu_stack, 0, sizeof(handle->menu_stack));
|
||||
enter_cb = handle->enter_cb;
|
||||
if (handle->change_cb != NULL)
|
||||
{
|
||||
change_cb = handle->change_cb;
|
||||
change_node = handle->current_menu;
|
||||
change_index = handle->current_index;
|
||||
}
|
||||
}
|
||||
break;
|
||||
case ZH_MENU_SHOW:
|
||||
if (handle->is_activated == true && handle->change_cb != NULL)
|
||||
{
|
||||
change_cb = handle->change_cb;
|
||||
change_node = handle->current_menu;
|
||||
change_index = handle->current_index;
|
||||
}
|
||||
break;
|
||||
case ZH_MENU_UP:
|
||||
if (handle->current_menu == NULL || handle->current_menu->item_numbers == 0)
|
||||
{
|
||||
break;
|
||||
}
|
||||
if (handle->is_activated == true && handle->is_selected == false)
|
||||
{
|
||||
if (handle->current_index > 0)
|
||||
{
|
||||
--handle->current_index;
|
||||
}
|
||||
else if (handle->loop == true)
|
||||
{
|
||||
handle->current_index = handle->current_menu->item_numbers - 1;
|
||||
}
|
||||
if (handle->change_cb != NULL)
|
||||
{
|
||||
change_cb = handle->change_cb;
|
||||
change_node = handle->current_menu;
|
||||
change_index = handle->current_index;
|
||||
}
|
||||
}
|
||||
break;
|
||||
case ZH_MENU_DOWN:
|
||||
if (handle->current_menu == NULL || handle->current_menu->item_numbers == 0)
|
||||
{
|
||||
break;
|
||||
}
|
||||
if (handle->is_activated == true && handle->is_selected == false)
|
||||
{
|
||||
if (handle->current_index < handle->current_menu->item_numbers - 1)
|
||||
{
|
||||
++handle->current_index;
|
||||
}
|
||||
else if (handle->loop == true)
|
||||
{
|
||||
handle->current_index = 0;
|
||||
}
|
||||
if (handle->change_cb != NULL)
|
||||
{
|
||||
change_cb = handle->change_cb;
|
||||
change_node = handle->current_menu;
|
||||
change_index = handle->current_index;
|
||||
}
|
||||
}
|
||||
break;
|
||||
case ZH_MENU_SELECT:
|
||||
if (handle->is_activated != true)
|
||||
{
|
||||
break;
|
||||
}
|
||||
if (handle->current_menu == NULL || handle->current_menu->submenu == NULL || handle->current_index >= handle->current_menu->item_numbers)
|
||||
{
|
||||
break;
|
||||
}
|
||||
{
|
||||
zh_menu_node_t *item = &handle->current_menu->submenu[handle->current_index];
|
||||
if (handle->is_selected == true)
|
||||
{
|
||||
if (item->item_cb != NULL)
|
||||
{
|
||||
item_cb = item->item_cb;
|
||||
}
|
||||
}
|
||||
else if (item->selectable == true)
|
||||
{
|
||||
handle->is_selected = true;
|
||||
if (item->item_cb != NULL)
|
||||
{
|
||||
item_cb = item->item_cb;
|
||||
}
|
||||
}
|
||||
else if (item->item_cb != NULL)
|
||||
{
|
||||
item_cb = item->item_cb;
|
||||
}
|
||||
else if (item->submenu != NULL && item->item_numbers > 0)
|
||||
{
|
||||
if (handle->menu_depth < ZH_MENU_MAX_DEPTH)
|
||||
{
|
||||
handle->menu_stack[handle->menu_depth] = handle->current_menu;
|
||||
++handle->menu_depth;
|
||||
handle->current_menu = item;
|
||||
handle->current_index = 0;
|
||||
if (handle->change_cb != NULL)
|
||||
{
|
||||
change_cb = handle->change_cb;
|
||||
change_node = handle->current_menu;
|
||||
change_index = handle->current_index;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
break;
|
||||
case ZH_MENU_BACK:
|
||||
if (handle->is_activated != true)
|
||||
{
|
||||
break;
|
||||
}
|
||||
if (handle->is_selected == true)
|
||||
{
|
||||
handle->is_selected = false;
|
||||
if (handle->change_cb != NULL)
|
||||
{
|
||||
change_cb = handle->change_cb;
|
||||
change_node = handle->current_menu;
|
||||
change_index = handle->current_index;
|
||||
}
|
||||
}
|
||||
else if (handle->menu_depth > 0)
|
||||
{
|
||||
--handle->menu_depth;
|
||||
handle->current_menu = handle->menu_stack[handle->menu_depth];
|
||||
handle->menu_stack[handle->menu_depth] = NULL;
|
||||
handle->current_index = 0;
|
||||
if (handle->change_cb != NULL)
|
||||
{
|
||||
change_cb = handle->change_cb;
|
||||
change_node = handle->current_menu;
|
||||
change_index = handle->current_index;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
handle->is_activated = false;
|
||||
handle->is_selected = false;
|
||||
handle->current_menu = handle->root;
|
||||
handle->current_index = 0;
|
||||
handle->menu_depth = 0;
|
||||
memset(handle->menu_stack, 0, sizeof(handle->menu_stack));
|
||||
exit_cb = handle->exit_cb;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
taskEXIT_CRITICAL(&handle->lock);
|
||||
if (enter_cb != NULL)
|
||||
{
|
||||
enter_cb(cb_arg);
|
||||
}
|
||||
if (change_cb != NULL)
|
||||
{
|
||||
change_cb(change_node, change_index);
|
||||
}
|
||||
if (item_cb != NULL)
|
||||
{
|
||||
item_cb(cb_arg);
|
||||
}
|
||||
if (exit_cb != NULL)
|
||||
{
|
||||
exit_cb(cb_arg);
|
||||
}
|
||||
_stats.min_stack_size = (uint32_t)uxTaskGetStackHighWaterMark(NULL);
|
||||
}
|
||||
vTaskDelete(NULL);
|
||||
}
|
||||
Reference in new issue
Block a user