Embedded SDK
Embedded SDK
Loading...
Searching...
No Matches
ux_stack.c
Go to the documentation of this file.
1
2/*******************************************************************************
3 * Ledger Nano S - Secure firmware
4 * (c) 2022 Ledger
5 *
6 * Licensed under the Apache License, Version 2.0 (the "License");
7 * you may not use this file except in compliance with the License.
8 * You may obtain a copy of the License at
9 *
10 * http://www.apache.org/licenses/LICENSE-2.0
11 *
12 * Unless required by applicable law or agreed to in writing, software
13 * distributed under the License is distributed on an "AS IS" BASIS,
14 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15 * See the License for the specific language governing permissions and
16 * limitations under the License.
17 ********************************************************************************/
18
19#include "ux.h"
20#include "os_pin.h"
21#include "os_seed.h"
22#include "os_screen.h"
23#include "os_types.h"
24#include "os_utils.h"
25#include "os_io_seph_ux.h"
26#include <string.h>
27
28// return true (stack slot +1) if an element
29unsigned int ux_stack_is_element_array_present(const bagl_element_t *element_array)
30{
31 unsigned int i, j;
32
33 // looks for a element array though the ux stack
34 for (i = 0; i < ARRAYLEN(G_ux.stack) && i < G_ux.stack_count; i++) {
35 for (j = 0; j < UX_STACK_SLOT_ARRAY_COUNT && j < G_ux.stack[i].element_arrays_count; j++) {
36 if (G_ux.stack[i].element_arrays[j].element_array == element_array) {
37 return i + 1;
38 }
39 }
40 }
41
42 return 0;
43}
44
45unsigned int ux_stack_push(void)
46{
47 // only push if an available slot exists
48 if (G_ux.stack_count < ARRAYLEN(G_ux.stack)) {
49 memset(&G_ux.stack[G_ux.stack_count], 0, sizeof(G_ux.stack[0]));
50#ifdef HAVE_UX_FLOW
51 memset(&G_ux.flow_stack[G_ux.stack_count], 0, sizeof(G_ux.flow_stack[0]));
52#endif // HAVE_UX_FLOW
53 G_ux.stack_count++;
54 }
55 // return the stack top index
56 return G_ux.stack_count - 1;
57}
58
59unsigned int ux_stack_pop(void)
60{
61 unsigned int exit_code = BOLOS_UX_OK;
62 // only pop if more than two stack entry (0 and 1,top is an index not a count)
63 if (G_ux.stack_count > 0) {
64 G_ux.stack_count--;
65 if (G_ux.stack_count < UX_STACK_SLOT_COUNT) {
66 exit_code = G_ux.stack[G_ux.stack_count].exit_code_after_elements_displayed;
67 // wipe popped slot
68 memset(&G_ux.stack[G_ux.stack_count], 0, sizeof(G_ux.stack[0]));
69#ifdef HAVE_UX_FLOW
70 memset(&G_ux.flow_stack[G_ux.stack_count], 0, sizeof(G_ux.flow_stack[0]));
71#endif // HAVE_UX_FLOW
72 }
73 }
74
75 // prepare output code when popping the last stack screen
76 if (G_ux.stack_count == 0) {
77 G_ux.exit_code = exit_code;
78 }
79 // ask for a complete redraw (optimisation due to blink must be avoided as we're returning from
80 // a modal, and within the bolos ux screen stack)
81 else {
82 // prepare to redraw the slot when asked
83 G_ux.stack[G_ux.stack_count - 1].element_index = 0;
84 }
85 // return the stack top index (or -1 if no top)
86 return G_ux.stack_count - 1;
87}
88
90{
91#ifdef HAVE_UX_FLOW
92 // an ux step has been relayout on the screen, don't flicker by redisplaying here
93 if (ux_flow_relayout()) {
94 return;
95 }
96#endif // HAVE_UX_FLOW
97
98 // check if any screen is schedule for displaying
99 if (G_ux.stack_count > 0 && G_ux.stack_count <= ARRAYLEN(G_ux.stack)) {
100 G_ux.stack[G_ux.stack_count - 1].element_index = 0;
101 ux_stack_display(G_ux.stack_count - 1);
102 }
103 // else return redraw for the app
104 else if (G_ux.stack_count == 0) {
105 if (G_ux.exit_code == BOLOS_UX_OK) {
106 G_ux.exit_code = BOLOS_UX_REDRAW;
107 }
108 }
109}
110
111void ux_stack_insert(unsigned int stack_slot)
112{
113 if (stack_slot >= ARRAYLEN(G_ux.stack)) {
114 return; // arbitrary, this is feil that's it
115 }
116 // not a full stack
117 if (G_ux.stack_count < ARRAYLEN(G_ux.stack)) {
118 // if not inserting as top of stack, then perform move
119 if (stack_slot != ARRAYLEN(G_ux.stack) - 1) {
120 memmove(&G_ux.stack[stack_slot + 1],
121 &G_ux.stack[stack_slot],
122 (ARRAYLEN(G_ux.stack) - (stack_slot + 1)) * sizeof(G_ux.stack[0]));
123#ifdef HAVE_UX_FLOW
124 memmove(&G_ux.flow_stack[stack_slot + 1],
125 &G_ux.flow_stack[stack_slot],
126 (ARRAYLEN(G_ux.flow_stack) - (stack_slot + 1)) * sizeof(G_ux.flow_stack[0]));
127#endif // HAVE_UX_FLOW
128 }
129 memset(&G_ux.stack[stack_slot], 0, sizeof(ux_stack_slot_t));
130#ifdef HAVE_UX_FLOW
131 memset(&G_ux.flow_stack[stack_slot], 0, sizeof(ux_flow_state_t));
132#endif // HAVE_UX_FLOW
133 // add the slot
134 G_ux.stack_count++;
135 // slot not wiped
136 }
137 // feil, that's it
138}
139
140void ux_stack_remove(unsigned int stack_slot)
141{
142 if (stack_slot > ARRAYLEN(G_ux.stack) - 1) {
143 stack_slot = ARRAYLEN(G_ux.stack) - 1;
144 }
145
146 // removing something not in stack
147 if (stack_slot >= G_ux.stack_count) {
148 return;
149 }
150
151 // before: | screenz | removed screen | other screenz |
152 // after: | screenz | other screenz |
153
154 if (stack_slot != ARRAYLEN(G_ux.stack) - 1) {
155 memmove(&G_ux.stack[stack_slot],
156 &G_ux.stack[stack_slot + 1],
157 (ARRAYLEN(G_ux.stack) - (stack_slot + 1)) * sizeof(G_ux.stack[0]));
158#ifdef HAVE_UX_FLOW
159 // also move the flow attached to the popped screen
160 memmove(&G_ux.flow_stack[stack_slot],
161 &G_ux.flow_stack[stack_slot + 1],
162 (ARRAYLEN(G_ux.flow_stack) - (stack_slot + 1)) * sizeof(G_ux.flow_stack[0]));
163#endif // HAVE_UX_FLOW
164 }
165
166 // wipe last slot
167 ux_stack_pop();
168}
169
170// common code for all screens
171void ux_stack_init(unsigned int stack_slot)
172{
173 /*
174 // mismatch, the stack slot is not accessible !!
175 if (G_ux.stack_count<stack_slot+1) {
176 reset();
177 }
178 */
179
180 if (stack_slot < UX_STACK_SLOT_COUNT) {
181#ifdef HAVE_UX_STACK_INIT_KEEP_TICKER
182 callback_int_t ticker_callback = G_ux.stack[stack_slot].ticker_callback;
183 unsigned int ticker_value = G_ux.stack[stack_slot].ticker_value;
184 unsigned int ticker_interval = G_ux.stack[stack_slot].ticker_interval;
185#endif // HAVE_UX_STACK_INIT_KEEP_TICKER
186
187 // wipe the slot to be displayed just in case
188 memset(&G_ux.stack[stack_slot], 0, sizeof(G_ux.stack[0]));
189
190#ifdef HAVE_UX_STACK_INIT_KEEP_TICKER
191 G_ux.stack[stack_slot].ticker_callback = ticker_callback;
192 G_ux.stack[stack_slot].ticker_value = ticker_value;
193 G_ux.stack[stack_slot].ticker_interval = ticker_interval;
194#endif // HAVE_UX_STACK_INIT_KEEP_TICKER
195
196 // init current screen state
197 G_ux.stack[stack_slot].exit_code_after_elements_displayed = BOLOS_UX_CONTINUE;
198 }
199}
200
201// check to process keyboard callback before screen generic callback
203{
204 const bagl_element_t *el;
205 if (G_ux.stack_count) {
206 if (G_ux.stack[G_ux.stack_count - 1].screen_before_element_display_callback) {
207 el = G_ux.stack[G_ux.stack_count - 1].screen_before_element_display_callback(element);
208 if (!el) {
209 return 0;
210 }
211 // legacy forvery old ux on blue and nano s
212 if ((unsigned int) el != 1) {
213 element = el;
214 }
215 }
216 }
217 // consider good to be displayed by default
218 return element;
219}
220
222{
223 unsigned int elem_idx;
224 unsigned int total_element_count;
225 const bagl_element_t *element;
226 unsigned int i;
227
228 total_element_count = 0;
229 // can't display UX of the app, when PIN is not validated
230#ifndef HAVE_BOLOS
231 if ((os_perso_isonboarded() != BOLOS_UX_OK || os_global_pin_is_validated() == BOLOS_UX_OK))
232#endif // HAVE_BOLOS
233 {
234 for (i = 0; i < UX_STACK_SLOT_ARRAY_COUNT && i < slot->element_arrays_count; i++) {
235 // compute elem_idx in the current array (element_index refers to the total number of
236 // element to display)
237 elem_idx = slot->element_index - total_element_count;
238 total_element_count += slot->element_arrays[i].element_array_count;
239 // check if we're sending from this array or not
240 while (elem_idx < slot->element_arrays[i].element_array_count) {
241 const bagl_element_t *el;
242 // pre inc before callback to allow callback to change the next element to be drawn
243 slot->element_index++;
244
245 element = &slot->element_arrays[i].element_array[elem_idx];
247 if (!el) {
248 // skip display if requested to
249 if (G_ux.exit_code != BOLOS_UX_CONTINUE) {
250 return;
251 }
252 }
253 else {
254 // legacy support
255 if ((unsigned int) el != 1) {
256 element = el;
257 }
258 io_seph_ux_display_bagl_element(element);
259 }
260 elem_idx++;
261 }
262 }
263
264 // TODO draw the status bar
265
266 if (slot->element_index == total_element_count) {
267 // if screen has special stuff todo on exit
268 screen_update();
269 // screen marked as displayed
270 slot->element_index++;
271
272 // check if a displayed callback is requiring redraw
273 if (slot->displayed_callback) {
274 // if screen displayed callback requested one more round, then set CONTINUE exit
275 // code
276 if (!slot->displayed_callback(0)) {
277 slot->element_index = 0;
278 G_ux.exit_code = BOLOS_UX_CONTINUE;
279 return; // break;
280 }
281 }
283 }
284 }
285}
286
287// common code for all screens
288void ux_stack_display(unsigned int stack_slot)
289{
290 // don't display any elements of a previous screen replacement
291 if (G_ux.stack_count > 0 && stack_slot + 1 == G_ux.stack_count) {
292 // at worse a redisplay of the current screen has been requested, ensure to redraw it
293 // correctly
294 G_ux.stack[stack_slot].element_index = 0;
295 ux_stack_display_elements(&G_ux.stack[stack_slot]); // on balenos, no need to wait for the
296 // display processed event
297 }
298 // asking to redraw below top screen (likely the app below the ux)
299 else if (stack_slot == -1UL || G_ux.stack_count == 0) {
300 if (G_ux.exit_code == BOLOS_UX_OK) {
301 G_ux.exit_code = BOLOS_UX_REDRAW;
302 }
303 }
304 // else don't draw (in stack insertion)
305}
struct ux_stack_slot_s::@48 element_arrays[UX_STACK_SLOT_ARRAY_COUNT]
unsigned char element_arrays_count
Definition ux_bagl.h:276
bolos_task_status_t exit_code_after_elements_displayed
Definition ux_bagl.h:275
unsigned short element_index
Definition ux_bagl.h:277
const bagl_element_t * element_array
Definition ux_bagl.h:280
unsigned char element_array_count
Definition ux_bagl.h:281
#define UX_STACK_SLOT_COUNT
Definition ux_bagl.h:166
#define G_ux
Definition ux_bagl.h:338
#define UX_STACK_SLOT_ARRAY_COUNT
Definition ux_bagl.h:170
unsigned int(* callback_int_t)(unsigned int)
Definition ux_bagl.h:173
unsigned int ux_flow_relayout(void)
unsigned int ux_stack_push(void)
Definition ux_stack.c:45
unsigned int ux_stack_is_element_array_present(const bagl_element_t *element_array)
Definition ux_stack.c:29
const bagl_element_t * ux_stack_display_element_callback(const bagl_element_t *element)
Definition ux_stack.c:202
void ux_stack_display(unsigned int stack_slot)
Definition ux_stack.c:288
void ux_stack_remove(unsigned int stack_slot)
Definition ux_stack.c:140
void ux_stack_insert(unsigned int stack_slot)
Definition ux_stack.c:111
void ux_stack_init(unsigned int stack_slot)
Definition ux_stack.c:171
void ux_stack_display_elements(ux_stack_slot_t *slot)
Definition ux_stack.c:221
void ux_stack_redisplay(void)
Definition ux_stack.c:89
unsigned int ux_stack_pop(void)
Definition ux_stack.c:59