Embedded SDK
Embedded SDK
Loading...
Searching...
No Matches
ux_layout_paging.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 "os_helpers.h"
20#include "os_math.h"
21#include "os_pic.h"
22#include "os_print.h"
23#include "os_utils.h"
24#include "ux.h"
25#include <string.h>
26#include "os.h"
27
28#ifdef HAVE_UX_FLOW
29
30#include "ux_layout_common.h"
31
32#ifdef HAVE_BAGL
33
34/*********************************************************************************
35 * 4 text lines
36 */
37#define LINE_FONT BAGL_FONT_OPEN_SANS_REGULAR_11px
38
39#if (BAGL_WIDTH == 128 && BAGL_HEIGHT == 64)
40// clang-format off
41static const bagl_element_t ux_layout_paging_elements[] = {
42 // erase
43 {{BAGL_RECTANGLE , 0x00, 0, 0, 128, 64, 0, 0, BAGL_FILL, 0x000000, 0xFFFFFF, 0, 0}, .text=NULL},
44
45 {{BAGL_ICON , 0x01, 2, 28, 4, 7, 0, 0, 0 , 0xFFFFFF, 0x000000, 0, 0 }, .text=(const char*)&C_icon_left},
46 {{BAGL_ICON , 0x02, 122, 28, 4, 7, 0, 0, 0 , 0xFFFFFF, 0x000000, 0, 0 }, .text=(const char*)&C_icon_right},
47
48 {{BAGL_LABELINE , 0x10, 0, 15, 128, 12, 0, 0, 0 , 0xFFFFFF, 0x000000, BAGL_FONT_OPEN_SANS_EXTRABOLD_11px|BAGL_FONT_ALIGNMENT_CENTER, 0 }, .text=NULL},
49 {{BAGL_LABELINE , 0x11, (128-PIXEL_PER_LINE)/2, 29, PIXEL_PER_LINE, 12, 0, 0, 0 , 0xFFFFFF, 0x000000, LINE_FONT|BAGL_FONT_ALIGNMENT_CENTER, 0 }, .text=NULL},
50 {{BAGL_LABELINE , 0x12, (128-PIXEL_PER_LINE)/2, 43, PIXEL_PER_LINE, 12, 0, 0, 0 , 0xFFFFFF, 0x000000, LINE_FONT|BAGL_FONT_ALIGNMENT_CENTER, 0 }, .text=NULL},
51 {{BAGL_LABELINE , 0x13, (128-PIXEL_PER_LINE)/2, 57, PIXEL_PER_LINE, 12, 0, 0, 0 , 0xFFFFFF, 0x000000, LINE_FONT|BAGL_FONT_ALIGNMENT_CENTER, 0 }, .text=NULL},
52};
53// clang-format on
54#endif // (BAGL_WIDTH==128 && BAGL_HEIGHT==64)
55
56const bagl_element_t *ux_layout_paging_prepro_common(const bagl_element_t *element,
57 const char *title,
58 const char *text)
59{
60 // copy element before any mod
61 memmove(&G_ux.tmp_element, element, sizeof(bagl_element_t));
62
63 switch (element->component.userid) {
64 case 0x01:
65 // no step before AND no pages before
66 if (ux_flow_is_first() && G_ux.layout_paging.current == 0) {
67 return NULL;
68 }
69 break;
70
71 case 0x02:
72 if (ux_flow_is_last() && G_ux.layout_paging.current == G_ux.layout_paging.count - 1) {
73 return NULL;
74 }
75 break;
76
77 case 0x10:
78 // We set the boldness of the text.
79 // display
80 if (title) {
81 SPRINTF(G_ux.string_buffer,
82 (G_ux.layout_paging.count > 1) ? "%s (%d/%d)" : "%s",
83 STRPIC(title),
84 G_ux.layout_paging.current + 1,
85 G_ux.layout_paging.count);
86 }
87 else {
88 SPRINTF(G_ux.string_buffer,
89 "%d/%d",
90 G_ux.layout_paging.current + 1,
91 G_ux.layout_paging.count);
92 }
93
94 G_ux.tmp_element.component.font_id
95 = ((G_ux.layout_paging.format & PAGING_FORMAT_BN) == PAGING_FORMAT_BN)
96 ? (BAGL_FONT_OPEN_SANS_EXTRABOLD_11px | BAGL_FONT_ALIGNMENT_CENTER)
97 : (BAGL_FONT_OPEN_SANS_REGULAR_11px | BAGL_FONT_ALIGNMENT_CENTER);
98 G_ux.tmp_element.text = G_ux.string_buffer;
99 break;
100
101 case 0x11:
102 case 0x12:
103 case 0x13: {
104 unsigned int lineidx = (element->component.userid & 0xF) - 1;
105 if (lineidx < UX_LAYOUT_PAGING_LINE_COUNT && G_ux.layout_paging.lengths[lineidx]) {
106 SPRINTF(G_ux.string_buffer,
107 "%.*s",
108 // avoid overflow
109 MIN(sizeof(G_ux.string_buffer) - 1, G_ux.layout_paging.lengths[lineidx]),
110 (text ? STRPIC(text) : G_ux.externalText)
111 + G_ux.layout_paging.offsets[lineidx]);
112 G_ux.tmp_element.text = G_ux.string_buffer;
113
114 G_ux.tmp_element.component.font_id
115 = ((G_ux.layout_paging.format & PAGING_FORMAT_NB) == PAGING_FORMAT_NB)
116 ? (BAGL_FONT_OPEN_SANS_EXTRABOLD_11px | BAGL_FONT_ALIGNMENT_CENTER)
117 : (BAGL_FONT_OPEN_SANS_REGULAR_11px | BAGL_FONT_ALIGNMENT_CENTER);
118 }
119 break;
120 }
121 }
122 return &G_ux.tmp_element;
123}
124
125static const bagl_element_t *ux_layout_paging_prepro_by_addr(const bagl_element_t *element)
126{
127 // don't display if null
128 const void *params = ux_stack_get_current_step_params();
129 if (NULL == params) {
130 return NULL;
131 }
132 const char *title;
133 const char *text;
134
135#if defined(HAVE_INDEXED_STRINGS)
136 UX_LOC_STRINGS_INDEX index = ((const ux_loc_layout_params_t *) params)->index;
137 title = get_ux_loc_string(index);
138 text = get_ux_loc_string(index + 1);
139#else // defined(HAVE_INDEXED_STRINGS)
140 title = ((const ux_layout_paging_params_t *) params)->title;
141 text = ((const ux_layout_paging_params_t *) params)->text;
142#endif // defined(HAVE_INDEXED_STRINGS)
143 return ux_layout_paging_prepro_common(element, title, text);
144}
145
146// redisplay current page
147void ux_layout_paging_redisplay_common(unsigned int stack_slot,
148 const char *text,
149 button_push_callback_t button_callback,
151{
152 bagl_font_id_e font_id;
153 ux_stack_slot_t *slot = &G_ux.stack[stack_slot];
154#if (BAGL_WIDTH == 128 && BAGL_HEIGHT == 64)
155 slot->element_arrays[0].element_array = ux_layout_paging_elements;
156 slot->element_arrays[0].element_array_count = ARRAYLEN(ux_layout_paging_elements);
157 slot->element_arrays_count = 1;
158#else
159 ux_layout_bb_init_common(stack_slot);
160#endif // (BAGL_WIDTH==128 && BAGL_HEIGHT==64)
161
162 // Use the correct font, to be able to compute correctly text width:
163 if (G_ux.layout_paging.format & PAGING_FORMAT_NB) {
164 font_id = BAGL_FONT_OPEN_SANS_EXTRABOLD_11px;
165 }
166 else {
167 font_id = BAGL_FONT_OPEN_SANS_REGULAR_11px;
168 }
169 // request offsets and lengths of lines for the current page
170 ux_layout_paging_compute(text, G_ux.layout_paging.current, &G_ux.layout_paging, font_id);
171
173 slot->button_push_callback = button_callback;
174 ux_stack_display(stack_slot);
175}
176
177STATIC_IF_NOT_INDEXED unsigned int ux_layout_paging_button_callback_by_addr(
178 unsigned int button_mask,
179 unsigned int button_mask_counter);
180
181void ux_layout_paging_redisplay_by_addr(unsigned int stack_slot)
182{
183 const char *text;
184 const void *params = ux_stack_get_current_step_params();
185 if (NULL == params) {
186 return;
187 }
188#if defined(HAVE_INDEXED_STRINGS)
189 text = get_ux_loc_string(((const ux_loc_layout_params_t *) params)->index + 1);
190#else // defined(HAVE_INDEXED_STRINGS)
191 text = ((const ux_layout_paging_params_t *) params)->text;
192#endif // defined(HAVE_INDEXED_STRINGS)
193 ux_layout_paging_redisplay_common(stack_slot,
194 text,
195 ux_layout_paging_button_callback_by_addr,
196 ux_layout_paging_prepro_by_addr);
197}
198
199static void ux_layout_paging_next(ux_layout_paging_redisplay_t redisplay)
200{
201 if (G_ux.layout_paging.current == G_ux.layout_paging.count - 1) {
202 ux_flow_next();
203 }
204 else {
205 // display next page, count the number of char to fit in the next page
206 G_ux.layout_paging.current++;
207 redisplay(G_ux.stack_count - 1);
208 }
209}
210
211static void ux_layout_paging_prev(ux_layout_paging_redisplay_t redisplay)
212{
213 if (G_ux.layout_paging.current == 0) {
214 ux_flow_prev();
215 }
216 else {
217 // display previous page, count the number of char to fit in the previous page
218 G_ux.layout_paging.current--;
219 redisplay(G_ux.stack_count - 1);
220 }
221}
222
223STATIC_IF_NOT_INDEXED unsigned int ux_layout_paging_button_callback_common(
224 unsigned int button_mask,
225 unsigned int button_mask_counter,
226 ux_layout_paging_redisplay_t redisplay)
227{
228 UNUSED(button_mask_counter);
229 switch (button_mask) {
231 ux_layout_paging_prev(redisplay);
232 break;
234 ux_layout_paging_next(redisplay);
235 break;
237 if (G_ux.layout_paging.count == 0
238 || G_ux.layout_paging.count - 1 == G_ux.layout_paging.current) {
240 }
241 break;
242 }
243 return 0;
244}
245
246STATIC_IF_NOT_INDEXED unsigned int ux_layout_paging_button_callback_by_addr(
247 unsigned int button_mask,
248 unsigned int button_mask_counter)
249{
250 return ux_layout_paging_button_callback_common(
251 button_mask, button_mask_counter, ux_layout_paging_redisplay_by_addr);
252}
253
254void ux_layout_paging_init_common(unsigned int stack_slot,
255 const char *text,
256 ux_layout_paging_redisplay_t redisplay)
257{
258 bagl_font_id_e font_id;
259
260 // At this very moment, we don't want to get rid of the format, but keep
261 // the one which has just been set (in case of direction backward or forward).
262 unsigned int backup_format = G_ux.layout_paging.format;
263
264 // depending flow browsing direction, select the correct page to display
265 switch (ux_flow_direction()) {
267 ux_layout_paging_reset();
268 // ask the paging to start at the last page.
269 // This step must be performed after the 'ux_layout_paging_reset' call,
270 // thus we cannot mutualize the call with the one in the 'forward' case.
271 G_ux.layout_paging.current = -1UL;
272 break;
274 // open the first page
275 ux_layout_paging_reset();
276 break;
278 // shall already be at the first page
279 break;
280 }
281
282 G_ux.layout_paging.format = backup_format;
283
284 // store params
285 ux_stack_init(stack_slot);
286
287 // compute number of chars to display from the params complete string
288 if ((text == NULL) && (G_ux.externalText == NULL)) {
289 text = ""; // empty string to avoid disrupting the ux flow.
290 }
291
292 // Use the correct font, to be able to compute correctly text width:
293 if (G_ux.layout_paging.format & PAGING_FORMAT_NB) {
294 font_id = BAGL_FONT_OPEN_SANS_EXTRABOLD_11px;
295 }
296 else {
297 font_id = BAGL_FONT_OPEN_SANS_REGULAR_11px;
298 }
299
300 // count total number of pages
301 G_ux.layout_paging.count
302 = ux_layout_paging_compute(text, -1UL, &G_ux.layout_paging, font_id); // at least one page
303
304 if (G_ux.layout_paging.count == 0) {
305 ux_layout_paging_reset();
306 }
307
308 // if (start != end) {
309 // ux_layout_paging_reset();
310 // }
311
312 // perform displaying the last page as requested (-1UL in prevstep hook does this)
313 if (G_ux.layout_paging.count && G_ux.layout_paging.current > G_ux.layout_paging.count - 1UL) {
314 G_ux.layout_paging.current = G_ux.layout_paging.count - 1;
315 }
316
317 redisplay(stack_slot);
318}
319
320void ux_layout_paging_init(unsigned int stack_slot)
321{
322 const ux_layout_paging_params_t *params
323 = (const ux_layout_paging_params_t *) ux_stack_get_step_params(stack_slot);
324 ux_layout_paging_init_common(stack_slot, params->text, ux_layout_paging_redisplay_by_addr);
325}
326
327void ux_layout_xx_paging_init(unsigned int stack_slot, unsigned int format)
328{
329 G_ux.layout_paging.format = format;
330 ux_layout_paging_init(stack_slot);
331}
332
333void ux_layout_nn_paging_init(unsigned int stack_slot)
334{
335 ux_layout_xx_paging_init(stack_slot, PAGING_FORMAT_NN);
336}
337
338void ux_layout_nb_paging_init(unsigned int stack_slot)
339{
340 ux_layout_xx_paging_init(stack_slot, PAGING_FORMAT_NB);
341}
342
343void ux_layout_bn_paging_init(unsigned int stack_slot)
344{
345 ux_layout_xx_paging_init(stack_slot, PAGING_FORMAT_BN);
346}
347
348void ux_layout_bb_paging_init(unsigned int stack_slot)
349{
350 ux_layout_xx_paging_init(stack_slot, PAGING_FORMAT_BB);
351}
352
353// function callable externally which reset the paging (to be called before init when willing to
354// redisplay the first page)
355void ux_layout_paging_reset(void)
356{
357 memset(&G_ux.layout_paging, 0, sizeof(G_ux.layout_paging));
358}
359
360#endif // HAVE_BAGL
361
362#endif // HAVE_UX_FLOW
const char * get_ux_loc_string(uint32_t index)
#define MIN(x, y)
Definition nbgl_types.h:98
const char * text
Definition ux_bagl.h:68
bagl_component_t component
Definition ux_bagl.h:58
struct ux_stack_slot_s::@48 element_arrays[UX_STACK_SLOT_ARRAY_COUNT]
unsigned char element_arrays_count
Definition ux_bagl.h:279
button_push_callback_t button_push_callback
Definition ux_bagl.h:292
bagl_element_callback_t screen_before_element_display_callback
Definition ux_bagl.h:291
const bagl_element_t * element_array
Definition ux_bagl.h:283
unsigned char element_array_count
Definition ux_bagl.h:284
#define STATIC_IF_NOT_INDEXED
Definition ux_bagl.h:74
#define BUTTON_RIGHT
Definition ux_bagl.h:145
#define BUTTON_EVT_RELEASED
Definition ux_bagl.h:148
#define G_ux
Definition ux_bagl.h:345
void ux_stack_display(unsigned int stack_slot)
Definition ux_stack.c:331
void ux_stack_init(unsigned int stack_slot)
Definition ux_stack.c:171
unsigned int(* button_push_callback_t)(unsigned int button_mask, unsigned int button_mask_counter)
Definition ux_bagl.h:142
const bagl_element_t *(* bagl_element_callback_t)(const bagl_element_t *element)
Definition ux_bagl.h:54
#define BUTTON_LEFT
Definition ux_bagl.h:144
@ FLOW_DIRECTION_BACKWARD
@ FLOW_DIRECTION_FORWARD
@ FLOW_DIRECTION_START
ux_flow_direction_t ux_flow_direction(void)
void ux_flow_next(void)
void ux_flow_prev(void)
unsigned int ux_flow_is_first(void)
const void * ux_stack_get_step_params(unsigned int stack_slot)
unsigned int ux_flow_is_last(void)
#define STRPIC(x)
void ux_flow_validate(void)
const void * ux_stack_get_current_step_params(void)
BOLOS_UX_LOC_STRINGS UX_LOC_STRINGS_INDEX