From cf1803dd77c4d8e2a99b32b229a958aa1a56420a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=86=AF=E4=BD=B3?= <13101321+jfen5577@user.noreply.gitee.com> Date: Tue, 23 Dec 2025 09:45:36 +0800 Subject: [PATCH] =?UTF-8?q?=E4=BC=98=E5=8C=96=E4=B8=80=E7=89=88?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- README.md | 7 +- api/menu_api.c | 71 ++++++---------- api/menu_api.h | 5 +- examples/demo.c | 63 +++++++++++---- src/core/menu_core.c | 97 ++++++++++++---------- src/core/menu_core.h | 4 + src/core/menu_event.c | 54 ++++++++++--- src/core/menu_event.h | 2 +- src/core/menu_hash.c | 65 +++++++++------ src/core/menu_hash.h | 1 + src/core/menu_permission.c | 157 ++++++++++++++++++++++++++++++++++++ src/core/menu_permission.h | 31 +++++++ src/core/menu_persistence.c | 109 +++++++++++++++++++++++++ src/core/menu_persistence.h | 36 +++++++++ src/core/menu_types.h | 21 ++++- 15 files changed, 577 insertions(+), 146 deletions(-) create mode 100644 src/core/menu_permission.c create mode 100644 src/core/menu_permission.h create mode 100644 src/core/menu_persistence.c create mode 100644 src/core/menu_persistence.h diff --git a/README.md b/README.md index 6d137f4..da13e83 100644 --- a/README.md +++ b/README.md @@ -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) { diff --git a/api/menu_api.c b/api/menu_api.c index 4e22cb8..f354827 100644 --- a/api/menu_api.c +++ b/api/menu_api.c @@ -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 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; } diff --git a/api/menu_api.h b/api/menu_api.h index 514c90e..a154b28 100644 --- a/api/menu_api.h +++ b/api/menu_api.h @@ -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); diff --git a/examples/demo.c b/examples/demo.c index 12d9896..e3243b4 100644 --- a/examples/demo.c +++ b/examples/demo.c @@ -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; } diff --git a/src/core/menu_core.c b/src/core/menu_core.c index a0e7096..afe7c47 100644 --- a/src/core/menu_core.c +++ b/src/core/menu_core.c @@ -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 // 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) { diff --git a/src/core/menu_core.h b/src/core/menu_core.h index 4bdde41..0888a7e 100644 --- a/src/core/menu_core.h +++ b/src/core/menu_core.h @@ -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 diff --git a/src/core/menu_event.c b/src/core/menu_event.c index 3661b04..4447cc7 100644 --- a/src/core/menu_event.c +++ b/src/core/menu_event.c @@ -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; } diff --git a/src/core/menu_event.h b/src/core/menu_event.h index 0b6c689..4f66066 100644 --- a/src/core/menu_event.h +++ b/src/core/menu_event.h @@ -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); diff --git a/src/core/menu_hash.c b/src/core/menu_hash.c index 26d31bb..3f194e8 100644 --- a/src/core/menu_hash.c +++ b/src/core/menu_hash.c @@ -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; } \ No newline at end of file diff --git a/src/core/menu_hash.h b/src/core/menu_hash.h index 83be97b..a0e62af 100644 --- a/src/core/menu_hash.h +++ b/src/core/menu_hash.h @@ -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 } diff --git a/src/core/menu_permission.c b/src/core/menu_permission.c new file mode 100644 index 0000000..e4e7788 --- /dev/null +++ b/src/core/menu_permission.c @@ -0,0 +1,157 @@ +#include "menu_permission.h" +#include "menu_core.h" +#include "menu_hash.h" +#include + +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; +} diff --git a/src/core/menu_permission.h b/src/core/menu_permission.h new file mode 100644 index 0000000..3b32cc2 --- /dev/null +++ b/src/core/menu_permission.h @@ -0,0 +1,31 @@ +#ifndef MENU_PERMISSION_H +#define MENU_PERMISSION_H + +#include +#include +#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 diff --git a/src/core/menu_persistence.c b/src/core/menu_persistence.c new file mode 100644 index 0000000..d820917 --- /dev/null +++ b/src/core/menu_persistence.c @@ -0,0 +1,109 @@ +#include "menu_persistence.h" +#include "menu_types.h" +#include "menu_core.h" +#include + +// 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; +} diff --git a/src/core/menu_persistence.h b/src/core/menu_persistence.h new file mode 100644 index 0000000..1167191 --- /dev/null +++ b/src/core/menu_persistence.h @@ -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 diff --git a/src/core/menu_types.h b/src/core/menu_types.h index 35c225a..0c82db1 100644 --- a/src/core/menu_types.h +++ b/src/core/menu_types.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