ra8-firmware 0.1.0
Bare-metal firmware for the Renesas RA8 family (RA8D2 / RA8P1)
Loading...
Searching...
No Matches
ra8_app.c
Go to the documentation of this file.
1
10
11#include "ra8_app.h"
12
13#include "ra8_attributes.h"
14#include "ra8_check.h"
15
17static const char* s_tag = "ra8_app";
18
19[[nodiscard]] ra8_err_t
21{
22 RA8_CHECK_NULL_PTR(reg, s_tag, "reg must not be nullptr");
23 RA8_CHECK_NULL_PTR(storage, s_tag, "storage must not be nullptr");
24 if (cap == 0U) {
26 }
27 reg->apps = storage;
28 reg->cap = cap;
29 reg->count = 0U;
30 reg->active = (int16_t)k_ra8_app_none;
31 return k_ra8_ok;
32}
33
34[[nodiscard]] ra8_err_t ra8_app_find(const ra8_app_registry_t* reg, uint16_t id, int16_t* out_idx)
35{
36 RA8_CHECK_NULL_PTR(reg, s_tag, "reg must not be nullptr");
37 RA8_CHECK_NULL_PTR(out_idx, s_tag, "out_idx must not be nullptr");
38 *out_idx = (int16_t)k_ra8_app_none;
39 for (uint16_t i = 0U; i < reg->count; ++i) {
40 if ((reg->apps[i] != nullptr) && (reg->apps[i]->id == id)) {
41 *out_idx = (int16_t)i;
42 return k_ra8_ok;
43 }
44 }
45 return k_ra8_ok;
46}
47
49{
50 RA8_CHECK_NULL_PTR(reg, s_tag, "reg must not be nullptr");
51 RA8_CHECK_NULL_PTR(app, s_tag, "app must not be nullptr");
52 RA8_CHECK_NULL_PTR(app->vt, s_tag, "app->vt must not be nullptr");
53 /* A duplicate id is a conflict regardless of capacity (check it first so a
54 * full registry still reports the more precise error). */
55 int16_t existing = (int16_t)k_ra8_app_none;
56 const ra8_err_t ferr = ra8_app_find(reg, app->id, &existing);
57 if (ferr != k_ra8_ok) {
58 return ferr;
59 }
60 if (existing != (int16_t)k_ra8_app_none) {
61 return k_ra8_err_conflict;
62 }
63 if (reg->count >= reg->cap) {
64 return k_ra8_err_no_mem;
65 }
66 if (app->vt->init != nullptr) {
67 const ra8_err_t ierr = app->vt->init(app);
68 if (ierr != k_ra8_ok) {
69 return ierr; /* leave unregistered */
70 }
71 }
72 app->initialized = true;
73 reg->apps[reg->count] = app;
74 reg->count = (uint16_t)(reg->count + 1U);
75 return k_ra8_ok;
76}
77
78[[nodiscard]] ra8_err_t ra8_app_launch(ra8_app_registry_t* reg, uint16_t id)
79{
80 RA8_CHECK_NULL_PTR(reg, s_tag, "reg must not be nullptr");
81 int16_t target = (int16_t)k_ra8_app_none;
82 const ra8_err_t ferr = ra8_app_find(reg, id, &target);
83 if (ferr != k_ra8_ok) {
84 return ferr;
85 }
86 if (target == (int16_t)k_ra8_app_none) {
88 }
89 if (target == reg->active) {
90 return k_ra8_ok; /* already focused: idempotent */
91 }
92 if (reg->active != (int16_t)k_ra8_app_none) {
93 ra8_app_t* cur = reg->apps[reg->active];
94 if ((cur != nullptr) && (cur->vt->on_leave != nullptr)) {
95 cur->vt->on_leave(cur);
96 }
97 }
98 reg->active = target;
99 ra8_app_t* next = reg->apps[target];
100 /*
101 * `target` is a live registry index returned by ra8_app_find() above (the
102 * function already returned k_ra8_err_not_found for k_ra8_app_none), and
103 * ra8_app_register() never stores a nullptr slot, so reg->apps[target] is
104 * provably non-null here. The (next != nullptr) clause is a defensive guard
105 * that cannot be driven false on any public-API path, so it cannot be given
106 * independent MC/DC influence; only (next->vt->on_enter != nullptr) varies.
107 */
108 /* mcdc-deactivated: next=reg->apps[target] with target a validated ra8_app_find index; (next!=nullptr) is always true here. */
109 if ((next != nullptr) && (next->vt->on_enter != nullptr)) {
110 next->vt->on_enter(next);
111 }
112 return k_ra8_ok;
113}
114
115[[nodiscard]] ra8_err_t ra8_app_active(const ra8_app_registry_t* reg, ra8_app_t** out_app)
116{
117 RA8_CHECK_NULL_PTR(reg, s_tag, "reg must not be nullptr");
118 RA8_CHECK_NULL_PTR(out_app, s_tag, "out_app must not be nullptr");
119 *out_app = (reg->active != (int16_t)k_ra8_app_none) ? reg->apps[reg->active] : nullptr;
120 return k_ra8_ok;
121}
122
123[[nodiscard]] ra8_err_t
125{
126 RA8_CHECK_NULL_PTR(reg, s_tag, "reg must not be nullptr");
127 RA8_CHECK_NULL_PTR(ev, s_tag, "ev must not be nullptr");
128 RA8_CHECK_NULL_PTR(out_handled, s_tag, "out_handled must not be nullptr");
129 *out_handled = false;
130 if (reg->active == (int16_t)k_ra8_app_none) {
131 return k_ra8_ok;
132 }
133 ra8_app_t* a = reg->apps[reg->active];
134 if ((a != nullptr) && (a->vt->on_input != nullptr)) {
135 *out_handled = a->vt->on_input(a, ev);
136 }
137 return k_ra8_ok;
138}
139
141{
142 RA8_CHECK_NULL_PTR(reg, s_tag, "reg must not be nullptr");
143 if (reg->active == (int16_t)k_ra8_app_none) {
144 return k_ra8_ok;
145 }
146 ra8_app_t* a = reg->apps[reg->active];
147 if ((a != nullptr) && (a->vt->tick != nullptr)) {
148 a->vt->tick(a);
149 }
150 return k_ra8_ok;
151}
152
154{
155 RA8_CHECK_NULL_PTR(reg, s_tag, "reg must not be nullptr");
156 if (reg->active == (int16_t)k_ra8_app_none) {
157 return k_ra8_ok;
158 }
159 ra8_app_t* a = reg->apps[reg->active];
160 if ((a != nullptr) && (a->vt->render != nullptr)) {
161 a->vt->render(a);
162 }
163 return k_ra8_ok;
164}
165
166[[nodiscard]] ra8_err_t ra8_app_count(const ra8_app_registry_t* reg, uint16_t* out_count)
167{
168 RA8_CHECK_NULL_PTR(reg, s_tag, "reg must not be nullptr");
169 RA8_CHECK_NULL_PTR(out_count, s_tag, "out_count must not be nullptr");
170 *out_count = reg->count;
171 return k_ra8_ok;
172}
173
174[[nodiscard]] ra8_err_t ra8_app_at(const ra8_app_registry_t* reg, uint16_t idx, ra8_app_t** out_app)
175{
176 RA8_CHECK_NULL_PTR(reg, s_tag, "reg must not be nullptr");
177 RA8_CHECK_NULL_PTR(out_app, s_tag, "out_app must not be nullptr");
178 if (idx >= reg->count) {
180 }
181 *out_app = reg->apps[idx];
182 return k_ra8_ok;
183}
184
185[[nodiscard]] ra8_err_t
186ra8_app_nav_init(ra8_app_nav_t* nav, ra8_app_registry_t* reg, uint16_t* storage, uint16_t cap)
187{
188 RA8_CHECK_NULL_PTR(nav, s_tag, "nav must not be nullptr");
189 RA8_CHECK_NULL_PTR(reg, s_tag, "reg must not be nullptr");
190 RA8_CHECK_NULL_PTR(storage, s_tag, "storage must not be nullptr");
191 if (cap == 0U) {
193 }
194 nav->reg = reg;
195 nav->stack = storage;
196 nav->cap = cap;
197 nav->depth = 0U;
198 return k_ra8_ok;
199}
200
201[[nodiscard]] ra8_err_t ra8_app_nav_go(ra8_app_nav_t* nav, uint16_t id)
202{
203 RA8_CHECK_NULL_PTR(nav, s_tag, "nav must not be nullptr");
204 RA8_CHECK_NULL_PTR(nav->reg, s_tag, "nav->reg must not be nullptr");
205 ra8_app_t* cur = nullptr;
206 const ra8_err_t aerr = ra8_app_active(nav->reg, &cur);
207 if (aerr != k_ra8_ok) {
208 return aerr;
209 }
210 /* Only a switch *away from* a different app records a trail entry. The first
211 * launch (no prior focus) and an idempotent re-tap push nothing. The two
212 * single-condition guards below avoid a compound decision (each is plain
213 * branch coverage, not MC/DC). */
214 bool will_push = false;
215 uint16_t prev_id = 0U;
216 if (cur != nullptr) {
217 if (cur->id != id) {
218 will_push = true;
219 prev_id = cur->id;
220 }
221 }
222 if (will_push) {
223 if (nav->depth >= nav->cap) {
224 return k_ra8_err_no_mem;
225 }
226 }
227 const ra8_err_t lerr = ra8_app_launch(nav->reg, id);
228 if (lerr != k_ra8_ok) {
229 return lerr;
230 }
231 if (will_push) {
232 nav->stack[nav->depth] = prev_id;
233 nav->depth = (uint16_t)(nav->depth + 1U);
234 }
235 return k_ra8_ok;
236}
237
238[[nodiscard]] ra8_err_t ra8_app_nav_go_index(ra8_app_nav_t* nav, uint16_t idx)
239{
240 RA8_CHECK_NULL_PTR(nav, s_tag, "nav must not be nullptr");
241 RA8_CHECK_NULL_PTR(nav->reg, s_tag, "nav->reg must not be nullptr");
242 ra8_app_t* app = nullptr;
243 const ra8_err_t aerr = ra8_app_at(nav->reg, idx, &app);
244 if (aerr != k_ra8_ok) {
245 return aerr; /* out_of_range for idx >= count */
246 }
247 RA8_CHECK_NULL_PTR(app, s_tag, "registry slot at idx must not be nullptr");
248 return ra8_app_nav_go(nav, app->id);
249}
250
251[[nodiscard]] ra8_err_t ra8_app_nav_back(ra8_app_nav_t* nav, bool* out_popped)
252{
253 RA8_CHECK_NULL_PTR(nav, s_tag, "nav must not be nullptr");
254 RA8_CHECK_NULL_PTR(nav->reg, s_tag, "nav->reg must not be nullptr");
255 RA8_CHECK_NULL_PTR(out_popped, s_tag, "out_popped must not be nullptr");
256 *out_popped = false;
257 if (nav->depth == 0U) {
258 return k_ra8_ok; /* at the root: nothing to go back to */
259 }
260 const uint16_t prev_id = nav->stack[nav->depth - 1U];
261 const ra8_err_t lerr = ra8_app_launch(nav->reg, prev_id);
262 if (lerr != k_ra8_ok) {
263 return lerr;
264 }
265 nav->depth = (uint16_t)(nav->depth - 1U);
266 *out_popped = true;
267 return k_ra8_ok;
268}
269
270[[nodiscard]] ra8_err_t ra8_app_nav_depth(const ra8_app_nav_t* nav, uint16_t* out_depth)
271{
272 RA8_CHECK_NULL_PTR(nav, s_tag, "nav must not be nullptr");
273 RA8_CHECK_NULL_PTR(out_depth, s_tag, "out_depth must not be nullptr");
274 *out_depth = nav->depth;
275 return k_ra8_ok;
276}
277
278[[nodiscard]] ra8_err_t
279ra8_app_state(const ra8_app_registry_t* reg, uint16_t id, ra8_app_state_t* out_state)
280{
281 RA8_CHECK_NULL_PTR(reg, s_tag, "reg must not be nullptr");
282 RA8_CHECK_NULL_PTR(out_state, s_tag, "out_state must not be nullptr");
283 int16_t idx = (int16_t)k_ra8_app_none;
284 const ra8_err_t ferr = ra8_app_find(reg, id, &idx);
285 if (ferr != k_ra8_ok) {
286 return ferr;
287 }
288 if (idx == (int16_t)k_ra8_app_none) {
289 *out_state = k_ra8_app_state_unmounted;
290 return k_ra8_ok;
291 }
292 *out_state = (idx == reg->active) ? k_ra8_app_state_foreground : k_ra8_app_state_background;
293 return k_ra8_ok;
294}
295
319{
320 for (uint16_t i = idx; (uint16_t)(i + 1U) < reg->count; ++i) {
321 reg->apps[i] = reg->apps[i + 1U];
322 }
323 reg->count = (uint16_t)(reg->count - 1U);
324 if (reg->active > (int16_t)idx) {
325 reg->active = (int16_t)(reg->active - 1);
326 }
327}
328
329[[nodiscard]] ra8_err_t ra8_app_uninstall(ra8_app_registry_t* reg, uint16_t id)
330{
331 RA8_CHECK_NULL_PTR(reg, s_tag, "reg must not be nullptr");
332 int16_t idx = (int16_t)k_ra8_app_none;
333 const ra8_err_t ferr = ra8_app_find(reg, id, &idx);
334 if (ferr != k_ra8_ok) {
335 return ferr;
336 }
337 if (idx == (int16_t)k_ra8_app_none) {
338 return k_ra8_err_not_found;
339 }
340 ra8_app_t* app = reg->apps[idx];
341 RA8_CHECK_NULL_PTR(app, s_tag, "registry slot must not be nullptr");
342 if (!app->removable) {
343 return k_ra8_err_not_supported; /* core app: uninstall refused */
344 }
345 if (idx == reg->active) {
346 return k_ra8_err_busy; /* the focused app cannot be unmounted */
347 }
348 if (app->vt->deinit != nullptr) {
349 app->vt->deinit(app);
350 }
351 app->initialized = false;
352 internal_app_remove_at(reg, (uint16_t)idx);
353 return k_ra8_ok;
354}
ra8_err_t ra8_app_nav_init(ra8_app_nav_t *nav, ra8_app_registry_t *reg, uint16_t *storage, uint16_t cap)
Bind a navigation back-stack to a registry + caller-owned storage.
Definition ra8_app.c:186
ra8_err_t ra8_app_launch(ra8_app_registry_t *reg, uint16_t id)
Launch (focus) the app with id, running the focus lifecycle.
Definition ra8_app.c:78
ra8_err_t ra8_app_tick(ra8_app_registry_t *reg)
Run the focused app's per-frame tick (no-op if none / NULL).
Definition ra8_app.c:140
ra8_err_t ra8_app_nav_back(ra8_app_nav_t *nav, bool *out_popped)
Return to the most recently pushed app (pop the back-stack).
Definition ra8_app.c:251
ra8_err_t ra8_app_register(ra8_app_registry_t *reg, ra8_app_t *app)
Register an app (calls its init once) and add it to the registry.
Definition ra8_app.c:48
ra8_err_t ra8_app_nav_go_index(ra8_app_nav_t *nav, uint16_t idx)
Focus the app at registry index idx (launcher select-by-position).
Definition ra8_app.c:238
ra8_err_t ra8_app_nav_go(ra8_app_nav_t *nav, uint16_t id)
Focus app id, pushing the outgoing app onto the back-stack.
Definition ra8_app.c:201
ra8_err_t ra8_app_count(const ra8_app_registry_t *reg, uint16_t *out_count)
Number of registered apps (for the launcher to list).
Definition ra8_app.c:166
ra8_err_t ra8_app_state(const ra8_app_registry_t *reg, uint16_t id, ra8_app_state_t *out_state)
Report an app's lifecycle state, derived from the registry.
Definition ra8_app.c:279
ra8_err_t ra8_app_find(const ra8_app_registry_t *reg, uint16_t id, int16_t *out_idx)
Find a registered app's index by id.
Definition ra8_app.c:34
ra8_err_t ra8_app_at(const ra8_app_registry_t *reg, uint16_t idx, ra8_app_t **out_app)
Get the app at registry index idx (launcher enumeration).
Definition ra8_app.c:174
ra8_err_t ra8_app_uninstall(ra8_app_registry_t *reg, uint16_t id)
Uninstall (unmount) a removable, non-focused app from the registry.
Definition ra8_app.c:329
ra8_err_t ra8_app_route_input(ra8_app_registry_t *reg, const ra8_widget_event_t *ev, bool *out_handled)
Route an input event to the focused app.
Definition ra8_app.c:124
static void internal_app_remove_at(ra8_app_registry_t *reg, uint16_t idx)
Remove the registry slot at idx and compact the table down one place.
Definition ra8_app.c:318
ra8_err_t ra8_app_nav_depth(const ra8_app_nav_t *nav, uint16_t *out_depth)
Current back-stack depth (apps the user can still go back through).
Definition ra8_app.c:270
ra8_err_t ra8_app_active(const ra8_app_registry_t *reg, ra8_app_t **out_app)
Get the currently focused app.
Definition ra8_app.c:115
ra8_err_t ra8_app_render(ra8_app_registry_t *reg)
Run the focused app's render (on-target; no-op if none / NULL).
Definition ra8_app.c:153
ra8_err_t ra8_app_registry_init(ra8_app_registry_t *reg, ra8_app_t **storage, uint16_t cap)
Bind a registry to caller-owned pointer storage (empty, no focus).
Definition ra8_app.c:20
static const char * s_tag
Logging / check tag.
Definition ra8_app.c:17
Zero-heap app framework: lifecycle + static registry + launcher (#146).
@ k_ra8_app_none
No active app / not found.
Definition ra8_app.h:84
ra8_app_state_t
Lifecycle state of one app within the framework's state machine.
Definition ra8_app.h:115
@ k_ra8_app_state_background
Mounted (init ran), not focused.
Definition ra8_app.h:117
@ k_ra8_app_state_foreground
Mounted and focused (the active app).
Definition ra8_app.h:118
@ k_ra8_app_state_unmounted
Not registered (initial / after uninstall).
Definition ra8_app.h:116
Annotation-attribute framework macros for ra8-firmware.
#define RA8_INTERNAL
Marker that a function is intended to be static (file-local).
Validation and Error-Checking Macros for ra8-firmware.
#define RA8_CHECK_NULL_PTR(ptr, tag, message)
Reject nullptr pointer, returning k_ra8_err_null_ptr.
Definition ra8_check.h:243
@ k_ra8_err_not_supported
Requested feature not compiled in, not wired, or not supported by this MCU variant.
Definition ra8_err.h:180
@ k_ra8_err_no_mem
Static buffer exhausted (no dynamic memory on this project).
Definition ra8_err.h:142
@ k_ra8_err_busy
Resource busy – blocking operation cannot proceed.
Definition ra8_err.h:195
@ k_ra8_err_out_of_range
Sensor or peripheral output out of valid range.
Definition ra8_err.h:337
@ k_ra8_err_invalid_arg
Invalid function argument.
Definition ra8_err.h:152
@ k_ra8_ok
Success – operation completed with all postconditions satisfied.
Definition ra8_err.h:119
@ k_ra8_err_not_found
Requested item not found (lookup / search missed).
Definition ra8_err.h:173
@ k_ra8_err_conflict
Conflict with concurrent access detected.
Definition ra8_err.h:441
ra8_err_codes_t ra8_err_t
Canonical error-return type used by every ra8-firmware API.
Definition ra8_err.h:546
A bounded back-stack of app ids layered over a registry (zero-heap).
Definition ra8_app.h:485
uint16_t cap
Capacity of stack.
Definition ra8_app.h:488
ra8_app_registry_t * reg
Registry the navigation drives (non-NULL).
Definition ra8_app.h:486
uint16_t * stack
Caller storage: back-stack of app ids.
Definition ra8_app.h:487
uint16_t depth
Number of ids currently on the back-stack.
Definition ra8_app.h:489
Fixed table of registered apps + the focused index.
Definition ra8_app.h:161
ra8_app_t ** apps
Caller storage (array of app pointers).
Definition ra8_app.h:162
int16_t active
Focused app index, or k_ra8_app_none.
Definition ra8_app.h:165
uint16_t count
Registered app count.
Definition ra8_app.h:164
uint16_t cap
Capacity of apps.
Definition ra8_app.h:163
One app instance + its metadata (caller-owned, static).
uint16_t id
Unique app id (launch key).
Definition ra8_app.h:151
One input event delivered to ra8_widget_dispatch.
Definition ra8_widget.h:77