优化一版
This commit is contained in:
@ -275,8 +275,11 @@ menu_register_nodes(nodes, sizeof(nodes)/sizeof(nodes[0]));
|
||||
|
||||
#### 5.2.3 事件处理
|
||||
```c
|
||||
// 发布事件
|
||||
menu_post_event(MENU_EVENT_KEY_UP, 0);
|
||||
// 发布事件 - 带优先级
|
||||
menu_post_event(MENU_EVENT_KEY_UP, 0, MENU_EVENT_PRIORITY_NORMAL);
|
||||
|
||||
// 发布事件 - 简化方式(使用正常优先级)
|
||||
menu_post_event_normal(MENU_EVENT_KEY_UP, 0);
|
||||
|
||||
// 主循环
|
||||
while (1) {
|
||||
|
||||
@ -2,6 +2,8 @@
|
||||
#include "../src/core/menu_core.h"
|
||||
#include "../src/core/menu_event.h"
|
||||
#include "../src/core/menu_stack.h"
|
||||
#include "../src/core/menu_permission.h"
|
||||
#include "../src/core/menu_persistence.h"
|
||||
#include <stddef.h>
|
||||
|
||||
static MenuCoreCtx g_menu_ctx;
|
||||
@ -88,8 +90,12 @@ MenuErrCode menu_register_nodes(
|
||||
return MENU_ERR_OK;
|
||||
}
|
||||
|
||||
MenuErrCode menu_post_event(MenuEventType type, uint32_t param) {
|
||||
return menu_event_post(&g_menu_ctx.event_queue, type, param);
|
||||
MenuErrCode menu_post_event(MenuEventType type, uint32_t param, MenuEventPriority priority) {
|
||||
return menu_event_post(&g_menu_ctx.event_queue, type, param, priority);
|
||||
}
|
||||
|
||||
MenuErrCode menu_post_event_normal(MenuEventType type, uint32_t param) {
|
||||
return menu_event_post(&g_menu_ctx.event_queue, type, param, MENU_EVENT_PRIORITY_NORMAL);
|
||||
}
|
||||
|
||||
MenuErrCode menu_enter(void) {
|
||||
@ -134,7 +140,7 @@ MenuErrCode menu_enter(void) {
|
||||
}
|
||||
|
||||
MenuErrCode menu_back(void) {
|
||||
return menu_post_event(MENU_EVENT_KEY_ESC, 0);
|
||||
return menu_post_event_normal(MENU_EVENT_KEY_ESC, 0);
|
||||
}
|
||||
|
||||
MenuErrCode menu_node_bind_param(MenuNodeId node_id, uint16_t param_id) {
|
||||
@ -146,69 +152,40 @@ MenuErrCode menu_node_bind_param(MenuNodeId node_id, uint16_t param_id) {
|
||||
|
||||
// Permission management functions
|
||||
MenuErrCode menu_permission_register_role(uint8_t role_id, const char* name, MenuPermissionLevel level) {
|
||||
if (!name) return MENU_ERR_INVALID_PARAM;
|
||||
if (g_menu_ctx.permission.role_count >= 8) return MENU_ERR_NO_MEM;
|
||||
|
||||
for (uint8_t i = 0; i < g_menu_ctx.permission.role_count; i++) {
|
||||
if (g_menu_ctx.permission.roles[i].id == role_id) {
|
||||
return MENU_ERR_FAIL;
|
||||
}
|
||||
}
|
||||
|
||||
MenuRole* role = &g_menu_ctx.permission.roles[g_menu_ctx.permission.role_count];
|
||||
role->id = role_id;
|
||||
role->name = name;
|
||||
role->level = level;
|
||||
g_menu_ctx.permission.role_count++;
|
||||
|
||||
return MENU_ERR_OK;
|
||||
return menu_permission_register_role_impl(&g_menu_ctx, role_id, name, level);
|
||||
}
|
||||
|
||||
MenuErrCode menu_permission_set_current_role(uint8_t role_id) {
|
||||
g_menu_ctx.permission.current_role_id = role_id;
|
||||
return MENU_ERR_OK;
|
||||
return menu_permission_set_current_role_impl(&g_menu_ctx, role_id);
|
||||
}
|
||||
|
||||
MenuErrCode menu_permission_update_node_level(MenuNodeId node_id, MenuPermissionLevel level) {
|
||||
MenuNode* node = menu_core_get_node(&g_menu_ctx, node_id);
|
||||
if (!node) return MENU_ERR_NOT_FOUND;
|
||||
|
||||
node->permission_level = level;
|
||||
return MENU_ERR_OK;
|
||||
return menu_permission_update_node_level_impl(&g_menu_ctx, node_id, level);
|
||||
}
|
||||
|
||||
bool menu_permission_check_node_access(MenuNodeId node_id) {
|
||||
MenuNode* node = menu_core_get_node(&g_menu_ctx, node_id);
|
||||
if (!node) return false;
|
||||
|
||||
#if MENU_CONFIG_ENABLE_PERMISSION
|
||||
MenuRole* role_node = NULL;
|
||||
for (uint8_t i = 0; i < g_menu_ctx.permission.role_count; i++) {
|
||||
if (g_menu_ctx.permission.roles[i].id == g_menu_ctx.permission.current_role_id) {
|
||||
role_node = &g_menu_ctx.permission.roles[i];
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!role_node || role_node->level < node->permission_level) {
|
||||
return false;
|
||||
}
|
||||
#endif
|
||||
|
||||
return true;
|
||||
return menu_permission_check_node_access_impl(&g_menu_ctx, node_id);
|
||||
}
|
||||
|
||||
// Persistence functions
|
||||
MenuErrCode menu_persistence_save(void) {
|
||||
return menu_core_save_state(&g_menu_ctx);
|
||||
return menu_persistence_save_impl(&g_menu_ctx);
|
||||
}
|
||||
|
||||
MenuErrCode menu_persistence_load(void) {
|
||||
return menu_core_load_state(&g_menu_ctx);
|
||||
return menu_persistence_load_impl(&g_menu_ctx);
|
||||
}
|
||||
|
||||
MenuErrCode menu_persistence_clear(void) {
|
||||
return menu_persistence_clear_impl(&g_menu_ctx);
|
||||
}
|
||||
|
||||
bool menu_persistence_is_dirty(void) {
|
||||
return menu_persistence_is_dirty_impl(&g_menu_ctx);
|
||||
}
|
||||
|
||||
MenuErrCode menu_refresh(void) {
|
||||
MenuErrCode err = menu_post_event(MENU_EVENT_RENDER, 0);
|
||||
MenuErrCode err = menu_post_event_normal(MENU_EVENT_RENDER, 0);
|
||||
if (err != MENU_ERR_OK) {
|
||||
return err;
|
||||
}
|
||||
|
||||
@ -42,7 +42,8 @@ MenuErrCode menu_register_nodes(
|
||||
);
|
||||
|
||||
// Event Handling
|
||||
MenuErrCode menu_post_event(MenuEventType type, uint32_t param);
|
||||
MenuErrCode menu_post_event(MenuEventType type, uint32_t param, MenuEventPriority priority);
|
||||
MenuErrCode menu_post_event_normal(MenuEventType type, uint32_t param);
|
||||
|
||||
// Navigation
|
||||
MenuErrCode menu_enter(void); // Enter root
|
||||
@ -60,6 +61,8 @@ bool menu_permission_check_node_access(MenuNodeId node_id);
|
||||
// Persistence Management
|
||||
MenuErrCode menu_persistence_save(void);
|
||||
MenuErrCode menu_persistence_load(void);
|
||||
MenuErrCode menu_persistence_clear(void);
|
||||
bool menu_persistence_is_dirty(void);
|
||||
|
||||
// Dynamic menu updates
|
||||
MenuErrCode menu_refresh(void);
|
||||
|
||||
@ -81,6 +81,11 @@ void setup_menu(void) {
|
||||
// Child of Settings: Brightness
|
||||
MenuNodeId bri_node = menu_register_node(0, settings, "Brightness", on_enter_brightness, NULL);
|
||||
menu_node_bind_param(bri_node, 2); // Bind to Brightness param (ID 2)
|
||||
|
||||
// Set permission levels for nodes
|
||||
menu_permission_update_node_level(settings, 1); // User level required for Settings
|
||||
menu_permission_update_node_level(vol_node, 1); // User level required for Volume
|
||||
menu_permission_update_node_level(bri_node, 1); // User level required for Brightness
|
||||
}
|
||||
|
||||
// Stub for binding param (will implement in API)
|
||||
@ -96,24 +101,48 @@ int main(void) {
|
||||
menu_port_log("Menu System Demo Started");
|
||||
menu_enter(); // Enter root
|
||||
|
||||
// Simulation Loop
|
||||
int steps = 0;
|
||||
while (steps < 20) {
|
||||
menu_main_loop(0);
|
||||
|
||||
// Simulate inputs
|
||||
if (steps == 2) {
|
||||
menu_port_log("User Press: ENTER");
|
||||
menu_post_event(MENU_EVENT_KEY_ENTER, 0);
|
||||
}
|
||||
if (steps == 4) {
|
||||
menu_port_log("User Press: DOWN");
|
||||
menu_post_event(MENU_EVENT_KEY_DOWN, 0);
|
||||
}
|
||||
|
||||
steps++;
|
||||
// sleep
|
||||
// Demo: Permission management
|
||||
menu_port_log("=== Permission Management Demo ===");
|
||||
menu_port_log("Current role: Guest");
|
||||
menu_port_log("Trying to enter Settings...");
|
||||
menu_post_event_normal(MENU_EVENT_KEY_ENTER, 0);
|
||||
menu_main_loop(0);
|
||||
|
||||
menu_port_log("Switching to User role...");
|
||||
menu_permission_set_current_role(2); // Switch to User role
|
||||
menu_post_event_normal(MENU_EVENT_KEY_ENTER, 0);
|
||||
menu_main_loop(0);
|
||||
|
||||
// Navigate to Volume setting
|
||||
menu_post_event_normal(MENU_EVENT_KEY_DOWN, 0);
|
||||
menu_main_loop(0);
|
||||
menu_post_event_normal(MENU_EVENT_KEY_ENTER, 0);
|
||||
menu_main_loop(0);
|
||||
|
||||
// Demo: Persistence
|
||||
menu_port_log("=== Persistence Demo ===");
|
||||
menu_port_log("Saving current state...");
|
||||
menu_persistence_save();
|
||||
|
||||
menu_port_log("Exiting Volume menu...");
|
||||
menu_post_event_normal(MENU_EVENT_MENU_EXIT, 0);
|
||||
menu_main_loop(0);
|
||||
|
||||
menu_port_log("Loading saved state...");
|
||||
menu_persistence_load();
|
||||
menu_main_loop(0);
|
||||
|
||||
// Demo: Clear persistence
|
||||
menu_port_log("=== Clear Persistence Demo ===");
|
||||
menu_port_log("Clearing saved state...");
|
||||
menu_persistence_clear();
|
||||
|
||||
menu_port_log("Trying to load cleared state (should fail)...");
|
||||
MenuErrCode err = menu_persistence_load();
|
||||
if (err != MENU_ERR_OK) {
|
||||
menu_port_log("Load failed as expected (state was cleared)");
|
||||
}
|
||||
|
||||
menu_port_log("Menu System Demo Completed");
|
||||
return 0;
|
||||
}
|
||||
|
||||
@ -3,6 +3,8 @@
|
||||
#include "menu_stack.h"
|
||||
#include "menu_hash.h"
|
||||
#include "menu_param.h"
|
||||
#include "menu_permission.h"
|
||||
#include "menu_persistence.h"
|
||||
#include <string.h>
|
||||
|
||||
// Internal helper to get node pointer from ID
|
||||
@ -18,6 +20,7 @@ MenuErrCode menu_core_init(MenuCoreCtx* ctx) {
|
||||
menu_event_init(&ctx->event_queue);
|
||||
menu_stack_init(&ctx->stack);
|
||||
menu_hash_init(ctx);
|
||||
menu_permission_init_impl(ctx);
|
||||
|
||||
ctx->is_initialized = true;
|
||||
ctx->current_state = MENU_STATE_INIT;
|
||||
@ -153,13 +156,8 @@ MenuErrCode menu_core_unregister_node(MenuCoreCtx* ctx, MenuNodeId id) {
|
||||
ctx->node_count--;
|
||||
ctx->free_node_count++;
|
||||
|
||||
// Rebuild hash table since we can't easily remove from it
|
||||
menu_hash_init(ctx);
|
||||
for (int i = 0; i < MENU_CONFIG_MAX_NODES; i++) {
|
||||
if (ctx->nodes[i].flags.is_registered) {
|
||||
menu_hash_insert(ctx, ctx->nodes[i].id, i);
|
||||
}
|
||||
}
|
||||
// Remove node from hash table using optimized removal
|
||||
menu_hash_remove(ctx, id, slot);
|
||||
|
||||
return MENU_ERR_OK;
|
||||
}
|
||||
@ -172,14 +170,7 @@ MenuErrCode menu_core_enter_node(MenuCoreCtx* ctx, MenuNodeId node_id) {
|
||||
|
||||
// Permission check
|
||||
#if MENU_CONFIG_ENABLE_PERMISSION
|
||||
MenuRole* role_node = NULL;
|
||||
for (uint8_t i = 0; i < ctx->permission.role_count; i++) {
|
||||
if (ctx->permission.roles[i].id == ctx->permission.current_role_id) {
|
||||
role_node = &ctx->permission.roles[i];
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!role_node || role_node->level < node->permission_level) {
|
||||
if (!menu_permission_check_node_access_impl(ctx, node_id)) {
|
||||
return MENU_ERR_PERMISSION_DENIED;
|
||||
}
|
||||
#endif
|
||||
@ -366,51 +357,73 @@ MenuErrCode menu_core_handle_event(MenuCoreCtx* ctx, const MenuEvent* event) {
|
||||
}
|
||||
break;
|
||||
|
||||
case MENU_EVENT_RENDER:
|
||||
// Handle render event - this could trigger custom render callbacks or refresh the display
|
||||
// For now, we'll just update the last refresh tick
|
||||
ctx->last_refresh_tick = 0; // TODO: Use actual tick parameter from event
|
||||
break;
|
||||
|
||||
case MENU_EVENT_PARAM_CHANGED:
|
||||
// Handle parameter change event
|
||||
// This could be used to update the display or trigger other actions when a parameter changes
|
||||
break;
|
||||
|
||||
case MENU_EVENT_MENU_ENTER:
|
||||
// Handle menu enter event
|
||||
// This event is triggered when entering a menu node
|
||||
break;
|
||||
|
||||
case MENU_EVENT_MENU_EXIT:
|
||||
// Handle menu exit event
|
||||
// This event is triggered when exiting a menu node
|
||||
break;
|
||||
|
||||
case MENU_EVENT_STATE_CHANGED:
|
||||
// Handle state change event
|
||||
// This event is triggered when the menu state changes
|
||||
break;
|
||||
|
||||
case MENU_EVENT_ERROR:
|
||||
// Handle error event
|
||||
// This event is triggered when an error occurs
|
||||
ctx->current_state = MENU_STATE_ERROR;
|
||||
ctx->error_code = (MenuErrCode)event->param;
|
||||
break;
|
||||
|
||||
case MENU_EVENT_REFRESH:
|
||||
// Handle refresh event
|
||||
// This event is used to force a refresh of the menu display
|
||||
break;
|
||||
|
||||
default:
|
||||
// Handle custom events
|
||||
if (event->type >= MENU_EVENT_CUSTOM_BASE) {
|
||||
// Custom events can be handled by user-defined callbacks
|
||||
// For now, we'll just ignore them
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
return MENU_ERR_OK;
|
||||
}
|
||||
|
||||
// calculate_checksum is now implemented in menu_persistence.c
|
||||
// Keeping this for backward compatibility in case it's used elsewhere
|
||||
static uint8_t calculate_checksum(const MenuPersistenceData* data) {
|
||||
uint8_t checksum = 0;
|
||||
const uint8_t* bytes = (const uint8_t*)data;
|
||||
size_t len = sizeof(MenuPersistenceData) - sizeof(data->checksum);
|
||||
for (size_t i = 0; i < len; i++) {
|
||||
checksum ^= bytes[i];
|
||||
}
|
||||
return checksum;
|
||||
(void)data; // Suppress unused parameter warning
|
||||
return 0;
|
||||
}
|
||||
|
||||
MenuErrCode menu_core_save_state(MenuCoreCtx* ctx) {
|
||||
if (!ctx) return MENU_ERR_INVALID_PARAM;
|
||||
|
||||
MenuPersistenceData data = {
|
||||
.current_node_id = ctx->current_node_id,
|
||||
.current_state = ctx->current_state,
|
||||
.nav_path = ctx->nav_path,
|
||||
.stack = ctx->stack,
|
||||
.timestamp = 0,
|
||||
.checksum = 0
|
||||
};
|
||||
|
||||
data.checksum = calculate_checksum(&data);
|
||||
|
||||
// TODO: Implement actual storage write logic here
|
||||
// This would typically write to EEPROM, Flash, or other non-volatile storage
|
||||
// For now, we'll just mark as not dirty
|
||||
ctx->persistence.dirty = false;
|
||||
return MENU_ERR_OK;
|
||||
return menu_persistence_save_impl(ctx);
|
||||
}
|
||||
|
||||
MenuErrCode menu_core_load_state(MenuCoreCtx* ctx) {
|
||||
if (!ctx) return MENU_ERR_INVALID_PARAM;
|
||||
|
||||
// TODO: Implement actual storage read logic here
|
||||
// This would typically read from EEPROM, Flash, or other non-volatile storage
|
||||
// For now, we'll just return OK
|
||||
return MENU_ERR_OK;
|
||||
return menu_persistence_load_impl(ctx);
|
||||
}
|
||||
|
||||
void menu_core_loop(MenuCoreCtx* ctx, uint32_t tick) {
|
||||
|
||||
@ -25,6 +25,10 @@ MenuErrCode menu_core_handle_event(MenuCoreCtx* ctx, const MenuEvent* event);
|
||||
MenuErrCode menu_core_enter_node(MenuCoreCtx* ctx, MenuNodeId node_id);
|
||||
MenuErrCode menu_core_exit_node(MenuCoreCtx* ctx, MenuNodeId node_id);
|
||||
|
||||
// Persistence management
|
||||
MenuErrCode menu_core_save_state(MenuCoreCtx* ctx);
|
||||
MenuErrCode menu_core_load_state(MenuCoreCtx* ctx);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
@ -7,24 +7,56 @@ MenuErrCode menu_event_init(MenuEventQueue* queue) {
|
||||
return MENU_ERR_OK;
|
||||
}
|
||||
|
||||
MenuErrCode menu_event_post(MenuEventQueue* queue, MenuEventType type, uint32_t param) {
|
||||
MenuErrCode menu_event_post(MenuEventQueue* queue, MenuEventType type, uint32_t param, MenuEventPriority priority) {
|
||||
if (!queue) return MENU_ERR_INVALID_PARAM;
|
||||
|
||||
if (queue->count >= MENU_CONFIG_EVENT_QUEUE_LEN) {
|
||||
return MENU_ERR_QUEUE_FULL;
|
||||
}
|
||||
|
||||
MenuEvent* evt = &queue->events[queue->tail];
|
||||
evt->type = type;
|
||||
evt->param = param;
|
||||
// timestamp handling can be added here if we have system tick access,
|
||||
// for now left as 0 or handled by caller if passed in param (which is just 32bit)
|
||||
// or we can add a tick parameter to post_event.
|
||||
// For simplicity, we assume timestamp is not critical for now or set elsewhere.
|
||||
evt->timestamp = 0;
|
||||
// Create the new event
|
||||
MenuEvent new_event;
|
||||
new_event.type = type;
|
||||
new_event.param = param;
|
||||
new_event.timestamp = 0; // timestamp handling can be added later if needed
|
||||
new_event.priority = priority;
|
||||
|
||||
queue->tail = (queue->tail + 1) % MENU_CONFIG_EVENT_QUEUE_LEN;
|
||||
queue->count++;
|
||||
if (queue->count == 0) {
|
||||
// Queue is empty, add at tail
|
||||
queue->events[queue->tail] = new_event;
|
||||
queue->tail = (queue->tail + 1) % MENU_CONFIG_EVENT_QUEUE_LEN;
|
||||
queue->count++;
|
||||
} else {
|
||||
// Find insertion position based on priority
|
||||
uint8_t insert_pos = queue->tail;
|
||||
uint8_t i = queue->head;
|
||||
|
||||
// Traverse the queue to find the first event with lower or equal priority
|
||||
for (uint8_t j = 0; j < queue->count; j++) {
|
||||
if (queue->events[i].priority < priority) {
|
||||
insert_pos = i;
|
||||
break;
|
||||
}
|
||||
i = (i + 1) % MENU_CONFIG_EVENT_QUEUE_LEN;
|
||||
}
|
||||
|
||||
// Shift events to make room for the new event
|
||||
if (insert_pos != queue->tail) {
|
||||
uint8_t current = queue->tail;
|
||||
uint8_t prev = (current - 1 + MENU_CONFIG_EVENT_QUEUE_LEN) % MENU_CONFIG_EVENT_QUEUE_LEN;
|
||||
|
||||
while (current != insert_pos) {
|
||||
queue->events[current] = queue->events[prev];
|
||||
current = prev;
|
||||
prev = (prev - 1 + MENU_CONFIG_EVENT_QUEUE_LEN) % MENU_CONFIG_EVENT_QUEUE_LEN;
|
||||
}
|
||||
}
|
||||
|
||||
// Insert the new event
|
||||
queue->events[insert_pos] = new_event;
|
||||
queue->tail = (queue->tail + 1) % MENU_CONFIG_EVENT_QUEUE_LEN;
|
||||
queue->count++;
|
||||
}
|
||||
|
||||
return MENU_ERR_OK;
|
||||
}
|
||||
|
||||
@ -8,7 +8,7 @@ extern "C" {
|
||||
#endif
|
||||
|
||||
MenuErrCode menu_event_init(MenuEventQueue* queue);
|
||||
MenuErrCode menu_event_post(MenuEventQueue* queue, MenuEventType type, uint32_t param);
|
||||
MenuErrCode menu_event_post(MenuEventQueue* queue, MenuEventType type, uint32_t param, MenuEventPriority priority);
|
||||
MenuErrCode menu_event_get(MenuEventQueue* queue, MenuEvent* event);
|
||||
bool menu_event_is_empty(const MenuEventQueue* queue);
|
||||
|
||||
|
||||
@ -5,6 +5,12 @@
|
||||
MenuErrCode menu_hash_init(MenuCoreCtx* ctx) {
|
||||
if (!ctx) return MENU_ERR_INVALID_PARAM;
|
||||
memset(ctx->hash_table, 0, sizeof(ctx->hash_table));
|
||||
|
||||
// Initialize hash_next_id to 0 for all nodes
|
||||
for (int i = 0; i < MENU_CONFIG_MAX_NODES; i++) {
|
||||
ctx->nodes[i].hash_next_id = 0;
|
||||
}
|
||||
|
||||
return MENU_ERR_OK;
|
||||
}
|
||||
|
||||
@ -19,18 +25,10 @@ MenuErrCode menu_hash_insert(MenuCoreCtx* ctx, MenuNodeId id, uint16_t index) {
|
||||
} else {
|
||||
MenuNode* prev_node = &ctx->nodes[current - 1];
|
||||
while (prev_node->hash_next_id != 0) {
|
||||
bool found = false;
|
||||
for (int i = 0; i < MENU_CONFIG_MAX_NODES; i++) {
|
||||
if (ctx->nodes[i].flags.is_registered && ctx->nodes[i].id == prev_node->hash_next_id) {
|
||||
prev_node = &ctx->nodes[i];
|
||||
found = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!found) return MENU_ERR_FAIL;
|
||||
prev_node = &ctx->nodes[prev_node->hash_next_id - 1];
|
||||
}
|
||||
|
||||
prev_node->hash_next_id = id;
|
||||
prev_node->hash_next_id = index + 1;
|
||||
}
|
||||
|
||||
return MENU_ERR_OK;
|
||||
@ -49,21 +47,40 @@ MenuNode* menu_hash_find(MenuCoreCtx* ctx, MenuNodeId id) {
|
||||
return node;
|
||||
}
|
||||
|
||||
bool found = false;
|
||||
for (int i = 0; i < MENU_CONFIG_MAX_NODES; i++) {
|
||||
if (ctx->nodes[i].flags.is_registered && ctx->nodes[i].id == node->hash_next_id) {
|
||||
for (int j = 0; j < MENU_CONFIG_MAX_NODES; j++) {
|
||||
if (ctx->nodes[j].flags.is_registered && ctx->nodes[j].id == node->hash_next_id) {
|
||||
current = j + 1;
|
||||
found = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!found) break;
|
||||
current = node->hash_next_id;
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
MenuErrCode menu_hash_remove(MenuCoreCtx* ctx, MenuNodeId id, uint16_t index) {
|
||||
if (!ctx) return MENU_ERR_INVALID_PARAM;
|
||||
|
||||
uint16_t hash_index = id % MENU_CONFIG_HASH_TABLE_SIZE;
|
||||
uint16_t current = ctx->hash_table[hash_index];
|
||||
uint16_t prev = 0;
|
||||
|
||||
while (current != 0) {
|
||||
uint16_t node_index = current - 1;
|
||||
|
||||
if (node_index == index) {
|
||||
if (prev == 0) {
|
||||
// Remove head of the list
|
||||
ctx->hash_table[hash_index] = ctx->nodes[node_index].hash_next_id;
|
||||
} else {
|
||||
// Remove from middle/end
|
||||
ctx->nodes[prev - 1].hash_next_id = ctx->nodes[node_index].hash_next_id;
|
||||
}
|
||||
|
||||
// Clear the hash_next_id for this node
|
||||
ctx->nodes[node_index].hash_next_id = 0;
|
||||
|
||||
return MENU_ERR_OK;
|
||||
}
|
||||
|
||||
prev = current;
|
||||
current = ctx->nodes[node_index].hash_next_id;
|
||||
}
|
||||
|
||||
return MENU_ERR_NOT_FOUND;
|
||||
}
|
||||
@ -10,6 +10,7 @@ extern "C" {
|
||||
MenuErrCode menu_hash_init(MenuCoreCtx* ctx);
|
||||
MenuErrCode menu_hash_insert(MenuCoreCtx* ctx, MenuNodeId id, uint16_t index);
|
||||
MenuNode* menu_hash_find(MenuCoreCtx* ctx, MenuNodeId id);
|
||||
MenuErrCode menu_hash_remove(MenuCoreCtx* ctx, MenuNodeId id, uint16_t index);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
|
||||
157
src/core/menu_permission.c
Normal file
157
src/core/menu_permission.c
Normal file
@ -0,0 +1,157 @@
|
||||
#include "menu_permission.h"
|
||||
#include "menu_core.h"
|
||||
#include "menu_hash.h"
|
||||
#include <string.h>
|
||||
|
||||
MenuErrCode menu_permission_init_impl(MenuCoreCtx* ctx) {
|
||||
if (!ctx) return MENU_ERR_INVALID_PARAM;
|
||||
|
||||
memset(&ctx->permission, 0, sizeof(ctx->permission));
|
||||
ctx->permission.role_count = 0;
|
||||
ctx->permission.current_role_id = 0;
|
||||
|
||||
// Register default roles
|
||||
menu_permission_register_role_impl(ctx, 1, "Guest", 0);
|
||||
menu_permission_register_role_impl(ctx, 2, "User", 1);
|
||||
menu_permission_register_role_impl(ctx, 3, "Admin", 2);
|
||||
|
||||
// Set default role to Guest
|
||||
ctx->permission.current_role_id = 1;
|
||||
|
||||
return MENU_ERR_OK;
|
||||
}
|
||||
|
||||
MenuErrCode menu_permission_register_role_impl(MenuCoreCtx* ctx, uint8_t role_id, const char* name, MenuPermissionLevel level) {
|
||||
if (!ctx || !name) return MENU_ERR_INVALID_PARAM;
|
||||
if (ctx->permission.role_count >= 8) return MENU_ERR_NO_MEM;
|
||||
|
||||
// Check if role already exists
|
||||
for (uint8_t i = 0; i < ctx->permission.role_count; i++) {
|
||||
if (ctx->permission.roles[i].id == role_id) {
|
||||
return MENU_ERR_FAIL;
|
||||
}
|
||||
}
|
||||
|
||||
MenuRole* role = &ctx->permission.roles[ctx->permission.role_count];
|
||||
role->id = role_id;
|
||||
role->name = name;
|
||||
role->level = level;
|
||||
ctx->permission.role_count++;
|
||||
|
||||
return MENU_ERR_OK;
|
||||
}
|
||||
|
||||
MenuErrCode menu_permission_unregister_role_impl(MenuCoreCtx* ctx, uint8_t role_id) {
|
||||
if (!ctx) return MENU_ERR_INVALID_PARAM;
|
||||
|
||||
for (uint8_t i = 0; i < ctx->permission.role_count; i++) {
|
||||
if (ctx->permission.roles[i].id == role_id) {
|
||||
// Shift roles up
|
||||
for (uint8_t j = i; j < ctx->permission.role_count - 1; j++) {
|
||||
ctx->permission.roles[j] = ctx->permission.roles[j + 1];
|
||||
}
|
||||
|
||||
ctx->permission.role_count--;
|
||||
|
||||
// If we removed the current role, set to default
|
||||
if (ctx->permission.current_role_id == role_id) {
|
||||
ctx->permission.current_role_id = 1; // Guest role
|
||||
}
|
||||
|
||||
return MENU_ERR_OK;
|
||||
}
|
||||
}
|
||||
|
||||
return MENU_ERR_NOT_FOUND;
|
||||
}
|
||||
|
||||
MenuErrCode menu_permission_set_current_role_impl(MenuCoreCtx* ctx, uint8_t role_id) {
|
||||
if (!ctx) return MENU_ERR_INVALID_PARAM;
|
||||
|
||||
// Check if role exists
|
||||
if (!menu_permission_role_exists_impl(ctx, role_id)) {
|
||||
return MENU_ERR_NOT_FOUND;
|
||||
}
|
||||
|
||||
ctx->permission.current_role_id = role_id;
|
||||
return MENU_ERR_OK;
|
||||
}
|
||||
|
||||
uint8_t menu_permission_get_current_role_impl(MenuCoreCtx* ctx) {
|
||||
if (!ctx) return 0;
|
||||
return ctx->permission.current_role_id;
|
||||
}
|
||||
|
||||
MenuErrCode menu_permission_update_node_level_impl(MenuCoreCtx* ctx, MenuNodeId node_id, MenuPermissionLevel level) {
|
||||
if (!ctx) return MENU_ERR_INVALID_PARAM;
|
||||
|
||||
MenuNode* node = menu_core_get_node(ctx, node_id);
|
||||
if (!node) return MENU_ERR_NOT_FOUND;
|
||||
|
||||
node->permission_level = level;
|
||||
return MENU_ERR_OK;
|
||||
}
|
||||
|
||||
bool menu_permission_check_node_access_impl(MenuCoreCtx* ctx, MenuNodeId node_id) {
|
||||
if (!ctx) return false;
|
||||
|
||||
MenuNode* node = menu_core_get_node(ctx, node_id);
|
||||
if (!node) return false;
|
||||
|
||||
#if MENU_CONFIG_ENABLE_PERMISSION
|
||||
MenuRole* role_node = NULL;
|
||||
for (uint8_t i = 0; i < ctx->permission.role_count; i++) {
|
||||
if (ctx->permission.roles[i].id == ctx->permission.current_role_id) {
|
||||
role_node = &ctx->permission.roles[i];
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!role_node || role_node->level < node->permission_level) {
|
||||
return false;
|
||||
}
|
||||
#endif
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
MenuErrCode menu_permission_check_and_enter_node_impl(MenuCoreCtx* ctx, MenuNodeId node_id) {
|
||||
if (!ctx) return MENU_ERR_INVALID_PARAM;
|
||||
|
||||
// Check permission first
|
||||
if (!menu_permission_check_node_access_impl(ctx, node_id)) {
|
||||
return MENU_ERR_PERMISSION_DENIED;
|
||||
}
|
||||
|
||||
// If permission granted, enter the node
|
||||
return menu_core_enter_node(ctx, node_id);
|
||||
}
|
||||
|
||||
MenuRole* menu_permission_get_role_impl(MenuCoreCtx* ctx, uint8_t role_id) {
|
||||
if (!ctx) return NULL;
|
||||
|
||||
for (uint8_t i = 0; i < ctx->permission.role_count; i++) {
|
||||
if (ctx->permission.roles[i].id == role_id) {
|
||||
return &ctx->permission.roles[i];
|
||||
}
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
uint8_t menu_permission_get_role_count_impl(MenuCoreCtx* ctx) {
|
||||
if (!ctx) return 0;
|
||||
return ctx->permission.role_count;
|
||||
}
|
||||
|
||||
bool menu_permission_role_exists_impl(MenuCoreCtx* ctx, uint8_t role_id) {
|
||||
if (!ctx) return false;
|
||||
|
||||
for (uint8_t i = 0; i < ctx->permission.role_count; i++) {
|
||||
if (ctx->permission.roles[i].id == role_id) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
31
src/core/menu_permission.h
Normal file
31
src/core/menu_permission.h
Normal file
@ -0,0 +1,31 @@
|
||||
#ifndef MENU_PERMISSION_H
|
||||
#define MENU_PERMISSION_H
|
||||
|
||||
#include <stdint.h>
|
||||
#include <stdbool.h>
|
||||
#include "menu_types.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
// Permission management functions
|
||||
MenuErrCode menu_permission_init_impl(MenuCoreCtx* ctx);
|
||||
MenuErrCode menu_permission_register_role_impl(MenuCoreCtx* ctx, uint8_t role_id, const char* name, MenuPermissionLevel level);
|
||||
MenuErrCode menu_permission_unregister_role_impl(MenuCoreCtx* ctx, uint8_t role_id);
|
||||
MenuErrCode menu_permission_set_current_role_impl(MenuCoreCtx* ctx, uint8_t role_id);
|
||||
uint8_t menu_permission_get_current_role_impl(MenuCoreCtx* ctx);
|
||||
MenuErrCode menu_permission_update_node_level_impl(MenuCoreCtx* ctx, MenuNodeId node_id, MenuPermissionLevel level);
|
||||
bool menu_permission_check_node_access_impl(MenuCoreCtx* ctx, MenuNodeId node_id);
|
||||
MenuErrCode menu_permission_check_and_enter_node_impl(MenuCoreCtx* ctx, MenuNodeId node_id);
|
||||
|
||||
// Role query functions
|
||||
MenuRole* menu_permission_get_role_impl(MenuCoreCtx* ctx, uint8_t role_id);
|
||||
uint8_t menu_permission_get_role_count_impl(MenuCoreCtx* ctx);
|
||||
bool menu_permission_role_exists_impl(MenuCoreCtx* ctx, uint8_t role_id);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif // MENU_PERMISSION_H
|
||||
109
src/core/menu_persistence.c
Normal file
109
src/core/menu_persistence.c
Normal file
@ -0,0 +1,109 @@
|
||||
#include "menu_persistence.h"
|
||||
#include "menu_types.h"
|
||||
#include "menu_core.h"
|
||||
#include <string.h>
|
||||
|
||||
// Mock storage buffer for persistence (simulating non-volatile storage)
|
||||
static MenuPersistenceData g_persistence_storage = {0};
|
||||
static bool g_storage_initialized = false;
|
||||
|
||||
// Calculate checksum for integrity verification
|
||||
static uint16_t calculate_checksum(const MenuPersistenceData* data) {
|
||||
if (!data) return 0;
|
||||
|
||||
const uint8_t* bytes = (const uint8_t*)data;
|
||||
uint16_t checksum = 0;
|
||||
uint16_t len = sizeof(MenuPersistenceData) - sizeof(data->checksum);
|
||||
|
||||
for (uint16_t i = 0; i < len; i++) {
|
||||
checksum ^= bytes[i];
|
||||
}
|
||||
|
||||
return checksum;
|
||||
}
|
||||
|
||||
MenuErrCode menu_persistence_init_impl(MenuCoreCtx* ctx) {
|
||||
if (!ctx) return MENU_ERR_INVALID_PARAM;
|
||||
|
||||
ctx->persistence.dirty = false;
|
||||
|
||||
// Initialize storage if not already initialized
|
||||
if (!g_storage_initialized) {
|
||||
memset(&g_persistence_storage, 0, sizeof(g_persistence_storage));
|
||||
g_storage_initialized = true;
|
||||
}
|
||||
|
||||
return MENU_ERR_OK;
|
||||
}
|
||||
|
||||
MenuErrCode menu_persistence_save_impl(MenuCoreCtx* ctx) {
|
||||
if (!ctx) return MENU_ERR_INVALID_PARAM;
|
||||
|
||||
MenuPersistenceData data = {
|
||||
.current_node_id = ctx->current_node_id,
|
||||
.current_state = ctx->current_state,
|
||||
.nav_path = ctx->nav_path,
|
||||
.stack = ctx->stack,
|
||||
.timestamp = 0, // In a real system, this would be a actual timestamp
|
||||
.checksum = 0
|
||||
};
|
||||
|
||||
// Calculate checksum for integrity verification
|
||||
data.checksum = calculate_checksum(&data);
|
||||
|
||||
// Write to mock storage
|
||||
memcpy(&g_persistence_storage, &data, sizeof(MenuPersistenceData));
|
||||
|
||||
// Mark as not dirty
|
||||
ctx->persistence.dirty = false;
|
||||
|
||||
return MENU_ERR_OK;
|
||||
}
|
||||
|
||||
MenuErrCode menu_persistence_load_impl(MenuCoreCtx* ctx) {
|
||||
if (!ctx) return MENU_ERR_INVALID_PARAM;
|
||||
|
||||
MenuPersistenceData data;
|
||||
|
||||
// Read from mock storage
|
||||
memcpy(&data, &g_persistence_storage, sizeof(MenuPersistenceData));
|
||||
|
||||
// Verify checksum for integrity
|
||||
uint16_t expected_checksum = calculate_checksum(&data);
|
||||
if (data.checksum != expected_checksum) {
|
||||
return MENU_ERR_CORRUPTED_DATA;
|
||||
}
|
||||
|
||||
// Verify that the current_node_id exists in the node table
|
||||
MenuNode* node = menu_core_get_node(ctx, data.current_node_id);
|
||||
if (!node) {
|
||||
return MENU_ERR_INVALID_NODE;
|
||||
}
|
||||
|
||||
// Load the state into the context
|
||||
ctx->current_node_id = data.current_node_id;
|
||||
ctx->current_state = data.current_state;
|
||||
ctx->nav_path = data.nav_path;
|
||||
ctx->stack = data.stack;
|
||||
|
||||
ctx->persistence.dirty = false;
|
||||
|
||||
return MENU_ERR_OK;
|
||||
}
|
||||
|
||||
MenuErrCode menu_persistence_clear_impl(MenuCoreCtx* ctx) {
|
||||
if (!ctx) return MENU_ERR_INVALID_PARAM;
|
||||
|
||||
// Clear mock storage
|
||||
memset(&g_persistence_storage, 0, sizeof(g_persistence_storage));
|
||||
g_persistence_storage.checksum = calculate_checksum(&g_persistence_storage);
|
||||
|
||||
ctx->persistence.dirty = true;
|
||||
|
||||
return MENU_ERR_OK;
|
||||
}
|
||||
|
||||
bool menu_persistence_is_dirty_impl(MenuCoreCtx* ctx) {
|
||||
if (!ctx) return false;
|
||||
return ctx->persistence.dirty;
|
||||
}
|
||||
36
src/core/menu_persistence.h
Normal file
36
src/core/menu_persistence.h
Normal file
@ -0,0 +1,36 @@
|
||||
/*
|
||||
* menu_persistence.h
|
||||
*
|
||||
* Created on: 2024-07-22
|
||||
* Author: Jfen
|
||||
*/
|
||||
|
||||
#ifndef MENU_PERSISTENCE_H
|
||||
#define MENU_PERSISTENCE_H
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
#include "menu_types.h"
|
||||
|
||||
// Persistence management functions
|
||||
MenuErrCode menu_persistence_init_impl(MenuCoreCtx* ctx);
|
||||
MenuErrCode menu_persistence_save_impl(MenuCoreCtx* ctx);
|
||||
MenuErrCode menu_persistence_load_impl(MenuCoreCtx* ctx);
|
||||
MenuErrCode menu_persistence_clear_impl(MenuCoreCtx* ctx);
|
||||
bool menu_persistence_is_dirty_impl(MenuCoreCtx* ctx);
|
||||
|
||||
// Storage write/read callbacks for platform-specific implementation
|
||||
typedef MenuErrCode (*MenuPersistenceWriteFunc)(const void* data, size_t size);
|
||||
typedef MenuErrCode (*MenuPersistenceReadFunc)(void* data, size_t size);
|
||||
|
||||
MenuErrCode menu_persistence_register_storage_impl(MenuCoreCtx* ctx,
|
||||
MenuPersistenceWriteFunc write_func,
|
||||
MenuPersistenceReadFunc read_func);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif // MENU_PERSISTENCE_H
|
||||
@ -37,6 +37,8 @@ typedef enum {
|
||||
MENU_ERR_STACK_OVERFLOW,
|
||||
MENU_ERR_STACK_UNDERFLOW,
|
||||
MENU_ERR_NOT_INIT,
|
||||
MENU_ERR_CORRUPTED_DATA,
|
||||
MENU_ERR_INVALID_NODE,
|
||||
} MenuErrCode;
|
||||
|
||||
// Menu States
|
||||
@ -57,7 +59,15 @@ typedef enum {
|
||||
MENU_EVENT_KEY_ESC,
|
||||
MENU_EVENT_TIMEOUT,
|
||||
MENU_EVENT_RENDER, // Optimization: Render event
|
||||
MENU_EVENT_CUSTOM_BASE = 10,
|
||||
|
||||
// Extended event types for more complex interactions
|
||||
MENU_EVENT_PARAM_CHANGED, // Parameter value changed
|
||||
MENU_EVENT_MENU_ENTER, // Entered a menu node
|
||||
MENU_EVENT_MENU_EXIT, // Exited a menu node
|
||||
MENU_EVENT_STATE_CHANGED, // Menu state changed
|
||||
MENU_EVENT_ERROR, // Error occurred
|
||||
MENU_EVENT_REFRESH, // Force refresh the menu
|
||||
MENU_EVENT_CUSTOM_BASE = 20, // Custom events start from here
|
||||
} MenuEventType;
|
||||
|
||||
// Callback function types
|
||||
@ -96,11 +106,20 @@ typedef struct {
|
||||
uint8_t top;
|
||||
} MenuStack;
|
||||
|
||||
// Event priority levels
|
||||
typedef enum {
|
||||
MENU_EVENT_PRIORITY_LOW = 0,
|
||||
MENU_EVENT_PRIORITY_NORMAL,
|
||||
MENU_EVENT_PRIORITY_HIGH,
|
||||
MENU_EVENT_PRIORITY_URGENT,
|
||||
} MenuEventPriority;
|
||||
|
||||
// Event structure
|
||||
typedef struct {
|
||||
MenuEventType type;
|
||||
uint32_t param;
|
||||
uint32_t timestamp;
|
||||
MenuEventPriority priority;
|
||||
} MenuEvent;
|
||||
|
||||
// Event Queue
|
||||
|
||||
Reference in New Issue
Block a user