Embedded SDK
Embedded SDK
Loading...
Searching...
No Matches
ux_layout_pages.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#include "os_helpers.h"
19#include "os_math.h"
20#include "os_pic.h"
21#include "os_print.h"
22#include "os_utils.h"
23#include "ux.h"
24#include <string.h>
25#include "os.h"
26
27#ifdef HAVE_UX_FLOW
28#if defined(HAVE_INDEXED_STRINGS)
29
30#include "ux_layout_common.h"
31
32#ifdef HAVE_BAGL
33
34#if (defined HAVE_BOLOS || defined(BUILD_PNG))
35
36// clang-format off
37static const bagl_element_t ux_layout_pages_elements[] = {
38#if (BAGL_WIDTH==128 && BAGL_HEIGHT==64)
39// Default Layout for LNX/LNS+:
40// Coordinates for Layout:
41// N => X=6 Y=35
42// P => X=57 Y=26
43// NN => X=6 Y=27,43
44// PN => X=57,6 Y=17,44
45// NP => X=6,57 Y=26,33
46// P/N => X=41 Y=35
47// P/NN => X=41 Y=27,43
48// NNN => X=6 Y=19,35,51
49// PNN => X=57,6,6 Y=10,36,52
50// NNP => X=6,6,57 Y=19,34,41
51// NNNN => X=6 Y=14,28,42,56
52// PNNN => X=57,6,6,6 Y=5,29,43,56
53// NNNP => X=6,6,57 Y=14,27,41,45
54
55 // erase
56 {{BAGL_RECTANGLE , 0x00, 0, 0, 128, 64, 0, 0, BAGL_FILL, 0x000000, 0xFFFFFF, 0, 0}, .text=NULL},
57 // Left & Right arrows, when applicable:
58 {{BAGL_ICON , 0x01, 2, 28, 4, 7, 0, 0, 0 , 0xFFFFFF, 0x000000, 0, 0 }, .text=(const char*)&C_icon_left},
59 {{BAGL_ICON , 0x02, 122, 28, 4, 7, 0, 0, 0 , 0xFFFFFF, 0x000000, 0, 0 }, .text=(const char*)&C_icon_right},
60 // 14x14 Icon on the left side, vertically centered
61 {{BAGL_ICON , 0x0F, 16, 24, 16, 16, 0, 0, 0 , 0xFFFFFF, 0x000000, 0, 0 }, .text=NULL},
62 // Up to 4 lines of text (can be mixed with one centered 14x14 Icon):
63 {{BAGL_LABELINE , 0x10, 6, 15, 116, 32, 0, 0, 0 , 0xFFFFFF, 0x000000, BAGL_FONT_OPEN_SANS_REGULAR_11px|BAGL_FONT_ALIGNMENT_CENTER, 0 }, .text=NULL},
64 {{BAGL_LABELINE , 0x11, 6, 29, 116, 32, 0, 0, 0 , 0xFFFFFF, 0x000000, BAGL_FONT_OPEN_SANS_REGULAR_11px|BAGL_FONT_ALIGNMENT_CENTER, 0 }, .text=NULL},
65 {{BAGL_LABELINE , 0x12, 6, 43, 116, 32, 0, 0, 0 , 0xFFFFFF, 0x000000, BAGL_FONT_OPEN_SANS_REGULAR_11px|BAGL_FONT_ALIGNMENT_CENTER, 0 }, .text=NULL},
66 {{BAGL_LABELINE , 0x13, 6, 57, 116, 32, 0, 0, 0 , 0xFFFFFF, 0x000000, BAGL_FONT_OPEN_SANS_REGULAR_11px|BAGL_FONT_ALIGNMENT_CENTER, 0 }, .text=NULL},
67
68#elif (BAGL_WIDTH==128 && BAGL_HEIGHT==32)
69// Default Layout for LNS:
70// Coordinates for Layout:
71// N => X=6 Y=19
72// P => X=57 Y=9
73// NN => X=6 Y=12,26
74// PN => X=57,6 Y=2,27
75// NP => X=6,57 Y=11,16
76// P/N => X=41 Y=19
77// P/NN => X=41 Y=12,26
78
79 // erase
80 {{BAGL_RECTANGLE , 0x00, 0, 0, 128, 32, 0, 0, BAGL_FILL, 0x000000, 0xFFFFFF, 0, 0}, .text=NULL},
81 // Left & Right arrows, when applicable:
82 {{BAGL_ICON , 0x01, 2, 12, 4, 7, 0, 0, 0 , 0xFFFFFF, 0x000000, 0, 0 }, .text=(const char*)&C_icon_left},
83 {{BAGL_ICON , 0x02, 122, 12, 4, 7, 0, 0, 0 , 0xFFFFFF, 0x000000, 0, 0 }, .text=(const char*)&C_icon_right},
84 // 14x14 Icon on the left side, vertically centered
85 {{BAGL_ICON , 0x0F, 14, 8, 16, 16, 0, 0, 0 , 0xFFFFFF, 0x000000, 0, 0 }, .text=NULL},
86 // Up to 2 lines of text (can be mixed with one centered 14x14 Icon):
87 {{BAGL_LABELINE , 0x10, 6, 12, 116, 32, 0, 0, 0 , 0xFFFFFF, 0x000000, BAGL_FONT_OPEN_SANS_REGULAR_11px|BAGL_FONT_ALIGNMENT_CENTER, 0 }, .text=NULL},
88 {{BAGL_LABELINE , 0x11, 6, 26, 116, 32, 0, 0, 0 , 0xFFFFFF, 0x000000, BAGL_FONT_OPEN_SANS_REGULAR_11px|BAGL_FONT_ALIGNMENT_CENTER, 0 }, .text=NULL},
89#endif // (BAGL_WIDTH==128 && BAGL_HEIGHT==64)
90};
91// clang-format on
92
93// X Coordinate & width of text when there is an icon centered on the side:
94#define BAGL_ICON_TEXT_XCOORD 38
95#define BAGL_ICON_TEXT_WIDTH (BAGL_WIDTH - BAGL_ICON_TEXT_XCOORD)
96
97// =============================================================================
98// This function will parse text and build arrays of txt ptr & lengths for each
99// line, taking in account specific contents like string buffer, side icons etc.
100// It returns a ptr to the side icon if there is one on the page (on any line).
101const bagl_icon_details_t *update_lines_length(const char *text,
102 const char *lines[],
103 unsigned short lengths[])
104{
105 const bagl_icon_details_t *icon_details = NULL;
106
107 // Override text parameter by a string buffer addr, eventually:
108 // (this is necessary when a string buffer is displayed over several pages)
109 const char *string_buffer = get_string_buffer(G_ux.layout_paging.string_buffer_id);
110
111 // Parse all lines on this page to see if there is something special to do:
112 for (int i = 0; i < UX_LAYOUT_PAGES_LINE_COUNT; i++) {
113 const char *txt;
114 unsigned short length = G_ux.layout_paging.lengths[i];
115
116 // When using UX_LOC_PAGING macro, first line contain the title
117 if (!i && G_ux.layout_paging.paging_title) {
118 const char *title = get_ux_loc_string(G_ux.layout_paging.paging_title - 1);
119 if (title) {
120 SPRINTF(G_ux.string_buffer,
121 (G_ux.layout_paging.count > 1) ? "%s (%d/%d)" : "%s",
122 title,
123 G_ux.layout_paging.current + 1,
124 G_ux.layout_paging.count);
125 }
126 else {
127 SPRINTF(G_ux.string_buffer,
128 "%d/%d",
129 G_ux.layout_paging.current + 1,
130 G_ux.layout_paging.count);
131 }
132 // Store text pointer & length for this line:
133 lines[0] = G_ux.string_buffer;
134 lengths[0] = strlen(G_ux.string_buffer);
135 continue;
136 }
137
138 // Is there anything to display on this line?
139 if (length == 0) {
140 lines[i] = NULL;
141 lengths[i] = 0;
142 continue;
143 }
144 if (string_buffer) {
145 txt = string_buffer;
146 }
147 else if (text) {
148 txt = STRPIC(text);
149 }
150 else {
151 txt = G_ux.externalText;
152 }
153 // Take in account offset which is relative to txt ptr:
154 txt += G_ux.layout_paging.offsets[i];
155
156 // Check if this line starts with an escape sequence:
157 while (txt && txt[0] == '\e') {
158 // There is at least one more byte which is readable.
159 // (even if it is the end of the string, it contains '\0')
160 unsigned short extra_byte = (unsigned char) txt[1];
161 // Is it an icon?
162 if (extra_byte >= FIRST_GLYPHS_ICON && extra_byte <= LAST_GLYPHS_ICON) {
163 // Does this line contain a 'left side icon'?
164 // If there is only an icon, then it is displayed on a full line.
165 // If there are text after the icon, it is displayed on left side.
166 // (=> This is what userid 0x0F is about!!)
167 if (length > 2) {
168 // This is a side icon => update icon_details ptr:
169 icon_details = get_glyphs_icon(extra_byte);
170 // Skip the side icon related bytes => update txt ptr & length:
171 txt += 2;
172 length -= 2;
173 // Skip all spaces characters after the side icon related bytes:
174 while (*txt == ' ' && length) {
175 ++txt;
176 --length;
177 }
178 continue; // Check if there is something else to handle...
179 }
180 else {
181 // There is just an icon displayed on a full line.
182 break;
183 }
184 }
185 else {
186 if (extra_byte >= FIRST_STRING_BUFFER && extra_byte <= LAST_STRING_BUFFER) {
187 // WARNING: if the string buffer contain an escape sequence to a
188 // string buffer, then we'll have an infinite loop! => Avoid that...
189 if (string_buffer) {
190 // Don't recursively parse string buffers!
191 txt = NULL;
192 length = 0;
193 break;
194 }
195
196 // There is a string buffer, here => update txt to point on it:
197 txt = get_string_buffer(extra_byte);
198 // We have just switched from txt to string buffer => offset=0.
199 // (G_ux.layout_paging.offsets[i] is relative to original txt ptr)
200 // (G_ux.layout_paging.lengths[i] is related to string buffer)
201 // (G_ux.layout_paging.offsets[>i] are relative to string buffer)
202 // Use that string buffer for next lines on this page:
203 string_buffer = txt;
204 // Check for an escape sequence at the beginning of the string buffer:
205 continue;
206 }
207 else { // This is not an icon nor a screen buffer...ignore it!
208 txt = NULL;
209 length = 0;
210 break;
211 }
212 }
213 }
214 // Store text pointer & length for this line:
215 lines[i] = txt;
216 lengths[i] = length;
217 }
218
219 // Return a pointer to the side icon, if there is one on this page:
220 return icon_details;
221}
222
223// =============================================================================
224
225const bagl_element_t *ux_layout_pages_prepro_common(const bagl_element_t *element, const char *text)
226{
227 const char *lines[UX_LAYOUT_PAGES_LINE_COUNT];
228 unsigned short lengths[UX_LAYOUT_PAGES_LINE_COUNT];
229
230 // copy element before any mod
231 memmove(&G_ux.tmp_element, element, sizeof(bagl_element_t));
232
233 switch (element->component.userid) {
234 case 0x01:
235 // no step before AND no pages before
236 if (ux_flow_is_first() && G_ux.layout_paging.current == 0) {
237 return NULL;
238 }
239 break;
240
241 case 0x02:
242 if (ux_flow_is_last() && G_ux.layout_paging.current == G_ux.layout_paging.count - 1) {
243 return NULL;
244 }
245 break;
246
247 // Do we have to display 14x14 Icon on the left side, vertically centered?
248 case 0x0F:
249 case 0x1F: {
250 // Check all lines on this page to see if there is an icon on the side:
251 const bagl_icon_details_t *icon_side_details
252 = update_lines_length(text, lines, lengths);
253 if (icon_side_details != NULL) {
254 G_ux.tmp_element.text = (const void *) icon_side_details;
255 return &G_ux.tmp_element;
256 }
257 // There is no 14x14 icon to display on the side.
258 return NULL;
259 }
260 // Up to 4 lines of text (can be mixed with one centered 14x14 Icon):
261 case 0x10:
262 case 0x11:
263 case 0x12:
264 case 0x13:
265 case 0x20:
266 case 0x21:
267 case 0x22:
268 case 0x23: {
269 unsigned short lineidx = element->component.userid & 0xF;
270 // Check if there is something to display on this line:
271 if (lineidx >= UX_LAYOUT_PAGES_LINE_COUNT || G_ux.layout_paging.lengths[lineidx] == 0) {
272 return NULL;
273 }
274 // Check all lines on this page and update lines & lengths information:
275 // (if icon_side_details != NULL => there is an icon on left side)
276 const bagl_icon_details_t *icon_side_details
277 = update_lines_length(text, lines, lengths);
278 G_ux.tmp_element.component.font_id = G_ux.layout_paging.fond_ids[lineidx];
279 // When the icon is displayed on left side, text is left aligned:
280 if (icon_side_details != NULL) {
281 G_ux.tmp_element.component.font_id |= BAGL_FONT_ALIGNMENT_LEFT;
282 }
283 else {
284 G_ux.tmp_element.component.font_id |= BAGL_FONT_ALIGNMENT_CENTER;
285 }
286 // Let's eat a little bit more vertical space on LNX:
287#if (BAGL_HEIGHT == 64)
288 short y_margin = BAGL_HEIGHT - FONT_HEIGHT; // Start at 52
289#endif //(BAGL_HEIGHT==64)
290 unsigned short nb_lines = 0; // Number of lines displayed in this screen
291 short y_coord = 0; // Y coordinate for this element
292 short x_coord = G_ux.tmp_element.component.x;
293 unsigned short used_height = 0; // Number of vertical pixels used
294
295 // Find at which X,Y coordinates this line have to be displayed:
296 // (we need to scan all lines...)
297 for (int i = 0; i < UX_LAYOUT_PAGES_LINE_COUNT; i++) {
298 unsigned short length;
299 const char *txt;
300
301 // Skip that line if it doesn't contain anything:
302 if ((length = lengths[i]) == 0 || (txt = lines[i]) == NULL) {
303 continue;
304 }
305 // By default consider this line will contain text => use font height
306 unsigned short height = FONT_HEIGHT;
307
308 // If there is an icon displayed on the left side, adjust xcoord and
309 // available width of ALL lines:
310 if (icon_side_details != NULL) {
311 // The icon will be displayed when element->component.userid == 0x0F.
312 x_coord = BAGL_ICON_TEXT_XCOORD; // That's the X coord for PNN on LNS
313 G_ux.tmp_element.component.width = BAGL_ICON_TEXT_WIDTH;
314 }
315 // Does this line contain an icon?
316 // If yes it is the first characters in the string.
317 // If there is only the icon, then it is displayed on a full line.
318 // If there are text after the icon, it is displayed on left side.
319 // (This MUST HAVE BEEN handled before)
320 if (txt[0] == '\e') {
321 unsigned short extra_byte = (unsigned char) txt[1];
322 // Is it really an icon?
323 if (extra_byte >= FIRST_GLYPHS_ICON && extra_byte <= LAST_GLYPHS_ICON) {
324 // Be sure this icon have to be displayed here:
325 if (length == 2) {
326 // That line contain an icon => update height with correct value:
327 const bagl_icon_details_t *icon_details = get_glyphs_icon(extra_byte);
328 height = icon_details->height;
329 // If the icon is on current line, display it instead of text:
330 if (lineidx == i) {
331 G_ux.tmp_element.component.type = BAGL_ICON;
332 G_ux.tmp_element.component.icon_id = 0;
333 G_ux.tmp_element.text = (const char *) icon_details;
334 // Update x_coord, width & height using icon's width:
335 x_coord
336 += (G_ux.tmp_element.component.width - icon_details->width) / 2;
337 G_ux.tmp_element.component.height = icon_details->height;
338 G_ux.tmp_element.component.width = icon_details->width;
339 }
340 }
341 }
342 }
343 else if (lineidx == i) {
344 // Update G_ux.tmp_element.text value for the line we want to display:
345 SPRINTF(
346 G_ux.layout_paging.line_buffer,
347 "%.*s",
348 // avoid overflow
349 (unsigned int) (MIN(sizeof(G_ux.layout_paging.line_buffer) - 1, length)),
350 txt);
351 G_ux.tmp_element.text = G_ux.layout_paging.line_buffer;
352 // Don't forget to add Baseline for characters:
353 y_coord += FONT_BASELINE;
354 }
355 if (i < lineidx) {
356 y_coord += height;
357 }
358 used_height += height;
359 ++nb_lines;
360#if (BAGL_HEIGHT == 64)
361 // Vertical margin depending on the number of lines displayed:
362 y_margin /= 2; // y_margin values will be 26,13,6,3
363#endif //(BAGL_HEIGHT==64)
364 }
365 // Y will depend on the number of lines displayed.
366#if (BAGL_HEIGHT == 64)
367 y_coord += y_margin;
368 used_height += 2 * y_margin;
369#endif //(BAGL_HEIGHT==64)
370 // Vertical padding (vertical space between each element) is:
371 // vertical_padding = (BAGL_HEIGHT - used_height) / (nb_lines + 1);
372 // So we can compute Y coordinate which will be:
373 y_coord += (((lineidx + 1) * (BAGL_HEIGHT - used_height)) + (nb_lines + 1) / 2)
374 / (nb_lines + 1);
375 // NB: previous computations provides almost same values than original
376 // ones, but we can replace them with manually fixed values if necessary.
377 G_ux.tmp_element.component.x = x_coord;
378 G_ux.tmp_element.component.y = y_coord;
379 break;
380 }
381 }
382 return &G_ux.tmp_element;
383}
384
385// =============================================================================
386
387static bool is_loc_word_delim(unsigned char c)
388{
389 // return !((c >= 'a' && c <= 'z')
390 // || (c >= 'A' && c <= 'Z')
391 // || (c >= '0' && c <= '9'));
392 return c == ' ' || c == '\n' || c == '\b' || c == '\f' || c == '\e' || c == '-' || c == '_';
393}
394
395// return the number of pages to be displayed when current page to show is -1
396unsigned int ux_layout_pages_compute(const char *text_to_split,
397 unsigned int page_to_display,
398 ux_layout_paging_state_t *paging_state)
399{
400 bagl_font_id_e font_id;
401
402 // reset length and offset of lines for this page
403 memset(paging_state->offsets, 0, sizeof(paging_state->offsets));
404 memset(paging_state->lengths, 0, sizeof(paging_state->lengths));
405
406 // a page has been asked, but no page exists
407 if (page_to_display >= paging_state->count && page_to_display != (unsigned int) -1) {
408 return 0;
409 }
410
411#ifndef HAVE_FONTS
412 unsigned char paging_format;
413#endif // HAVE_FONTS
414 // Regular font by default at the beginning of the first page:
415 unsigned char use_bold_font;
416 if (G_ux.layout_paging.format & PAGING_FORMAT_NB) {
417 use_bold_font = 1;
418 }
419 else {
420 use_bold_font = 0;
421 }
422 // compute offset/length of text of each line for the current page
423 unsigned int page = 0;
424 unsigned int line = 0;
425 const char *start = (text_to_split ? STRPIC(text_to_split) : G_ux.externalText);
426 const char *start2 = start;
427 const char *end = start + strlen(start);
428 unsigned int string_buffer_mark_offset = (unsigned int) -1;
429 unsigned int string_buffer_mark_len = 0;
430 unsigned char string_buffer_id = 0; // By default, 0 => no string buffer
431 unsigned char icon_on_side = 0;
432 unsigned char extra_byte;
433 const char *page_start = start;
434 unsigned char page_use_bold_font = use_bold_font;
435 unsigned int string_buffer_start_page = (unsigned int) -1;
436 paging_state->string_buffer_id = string_buffer_id;
437 while (start < end) {
438 unsigned int len, icon_on_side_len;
439 unsigned int linew;
440 unsigned char c;
441 page_start_loop:
442 // When using UX_LOC_PAGING macro, first line contain the title on all pages
443 if (!line && G_ux.layout_paging.paging_title) {
444 paging_state->fond_ids[0] = BAGL_FONT_OPEN_SANS_REGULAR_11px; // By default
445 paging_state->lengths[0] = 1; // If 0 the line will be skipped
446 line = 1;
447 }
448 len = icon_on_side_len = 0;
449 linew = 0;
450 c = 0;
451
452 // Skip all spaces characters at the beginning of the line:
453 while (*start == ' ' && (start + 1) < end) {
454 ++start;
455 }
456 // If first character is \b, update bold state and remove it from the string
457 if (*start == '\b') {
458 use_bold_font ^= 1;
459 ++start;
460 }
461 const char *last_word_delim = start;
462 // Initialise font_id at the beginning of each line depending on bold state:
463 if (use_bold_font) {
464 font_id = BAGL_FONT_OPEN_SANS_EXTRABOLD_11px;
465#ifndef HAVE_FONTS
466 paging_format = PAGING_FORMAT_NB;
467#endif // HAVE_FONTS
468 }
469 else {
470 font_id = BAGL_FONT_OPEN_SANS_REGULAR_11px;
471#ifndef HAVE_FONTS
472 paging_format = PAGING_FORMAT_NN;
473#endif // HAVE_FONTS
474 }
475 // not reached end of content
476 while (start + len < end
477 // line is not full
478 && (icon_on_side == 0 || linew <= BAGL_ICON_TEXT_WIDTH)
479 && linew <= PIXEL_PER_LINE
480 // avoid display buffer overflow for each line
481 && len < (sizeof(G_ux.layout_paging.line_buffer) - 1)) {
482 // ========================
483 // Handle escape sequences:
484 // =========================
485 // Check if this line starts with the escape character \e:
486 if (start[icon_on_side_len] == '\e') {
487 extra_byte = start[icon_on_side_len + 1];
488 // ----------------------------
489 // Special case: string buffer:
490 // ----------------------------
491 // If this is a string buffer, then restart parsing the string buffer:
492 if (extra_byte >= FIRST_STRING_BUFFER && extra_byte <= LAST_STRING_BUFFER) {
493 // Don't allow recursive string buffer!
494 if (string_buffer_id || !get_string_buffer(extra_byte)) {
495 // Ignore this escape sequence
496 if (!icon_on_side_len) {
497 start += 2;
498 }
499 }
500 else {
501 // Store the string buffer mark offset/len from original string:
502 string_buffer_mark_offset = start - start2;
503 string_buffer_mark_len = icon_on_side_len;
504 string_buffer_start_page = page;
505 // If the string just contain \b + the string buffer, switch now!
506 if (string_buffer_mark_offset == 1 && start2[0] == '\b') {
507 string_buffer_mark_offset = 0;
508 }
509 string_buffer_id = extra_byte;
510 // Get String buffer addr:
511 start = get_string_buffer(extra_byte);
512 start2 = start;
513 end = start + strlen(start);
514 }
515 // Just continue parsing starting from the same line, but with new text buffer:
516 goto page_start_loop;
517 }
518 // Currently, escape character use just one additional byte.
519 // If next character is '\n' or '\f' or '\0' we'll remove it and store 2 bytes:
520 // (this mean we was dealing with a full line icon)
521 // -----------------------------
522 // Special case: full line icon:
523 // -----------------------------
524 if (start[icon_on_side_len + 2] == '\n' || start[icon_on_side_len + 2] == '\f'
525 || start[icon_on_side_len + 2] == '\0') {
526 c = start[icon_on_side_len + 2];
527 len = icon_on_side_len + 3;
528 break;
529 }
530 // -----------------------------
531 // Special case: left side icon:
532 // -----------------------------
533 // Otherwise, we continue adding following characters to that string.
534 // Check if line != 0 to restart scanning at line 0 with icon_on_side set!
535 if (!icon_on_side && line) {
536 line = 0;
537 icon_on_side = 1;
538 start = page_start;
539 end = start + strlen(start);
540 // If we switched to a string buffer on this page, reset start2:
541 if (string_buffer_start_page != (unsigned int) -1
542 && string_buffer_start_page == page) {
543 start2 = (text_to_split ? STRPIC(text_to_split) : G_ux.externalText);
544 string_buffer_start_page = (unsigned int) -1;
545 paging_state->string_buffer_id = string_buffer_id = 0;
546 string_buffer_mark_offset = (unsigned int) -1;
547 string_buffer_mark_len = 0;
548 }
549 use_bold_font = page_use_bold_font;
550 // reset length and offset of lines for this page
551 if (page_to_display == page) {
552 memset(paging_state->offsets, 0, sizeof(paging_state->offsets));
553 memset(paging_state->lengths, 0, sizeof(paging_state->lengths));
554 }
555 goto page_start_loop;
556 }
557 icon_on_side = 1;
558 len += 2;
559 // Skip all spaces characters after the icon on side bytes:
560 while (start[len] == ' ') {
561 ++len;
562 }
563 // Be able to check \e after some icon on the side bytes!
564 icon_on_side_len = len;
565 // ---------------------------------------------------------
566 // Special case: string buffer right after a left side icon:
567 // ---------------------------------------------------------
568 continue;
569 // =================================
570 // End of escape sequences handling:
571 // =================================
572 }
573 // compute new line length
574#ifdef HAVE_FONTS
575 linew = bagl_compute_line_width(font_id, 0, start, len + 1, BAGL_ENCODING_DEFAULT);
576#else // HAVE_FONTS
577 linew = se_compute_line_width_light(start, len + 1, paging_format);
578#endif // HAVE_FONTS
579 // if (start[len] )
580 if ((icon_on_side != 0 && linew > BAGL_ICON_TEXT_WIDTH) || linew > PIXEL_PER_LINE) {
581 // we got a full line
582 break;
583 }
584 c = start[len];
585 if (is_loc_word_delim(c)) {
586 last_word_delim = &start[len];
587 }
588 len++;
589 // New line, don't go further
590 if (c == '\n' || c == '\f') {
591 break;
592 }
593 }
594
595 // if not splitting line onto a word delimiter, then cut at the previous word_delim, adjust
596 // len accordingly (and a word delim has been found already)
597 if (start + len < end && last_word_delim != start && len) {
598 // if line split within a word
599 if ((!is_loc_word_delim(start[len - 1]) && !is_loc_word_delim(start[len]))) {
600 len = last_word_delim - start;
601 }
602 }
603
604 // Update boldness: parse text and toggle boldness if needed:
605 for (unsigned int i = 0; i < len; i++) {
606 if (start[i] == '\b') {
607 use_bold_font ^= 1;
608 }
609 }
610 // fill up the paging structure
611 if (page_to_display != (unsigned int) -1 && page_to_display == page
612 && page_to_display < paging_state->count) {
613 // If we just switched to a string buffer, keep the offset from original string:
614 if (string_buffer_mark_offset != (unsigned int) -1) {
615 paging_state->offsets[line] = string_buffer_mark_offset;
616 }
617 else {
618 paging_state->offsets[line] = start - start2;
619 }
620 // Store string buffer id if we are not in the initial page or if there
621 // is only the string buffer to display in that page
622 if (string_buffer_start_page != (unsigned int) -1
623 && (string_buffer_start_page < page
624 || (string_buffer_start_page == page && string_buffer_mark_offset == 0))) {
625 paging_state->string_buffer_id = string_buffer_id; // Contain 0xB1, 0xB2, etc
626 }
627 // Remove the '\n' or '\f' from the string (=> no need to handle it anymore!)
628 if (c == '\n' || c == '\f' || c == '\0') {
629 paging_state->lengths[line] = len - 1 + string_buffer_mark_len;
630 }
631 else {
632 paging_state->lengths[line] = len + string_buffer_mark_len;
633 }
634 string_buffer_mark_offset = (unsigned int) -1;
635 string_buffer_mark_len = 0;
636
637 paging_state->fond_ids[line] = font_id;
638
639 // won't compute all pages, we reached the one to display
640#if UX_LAYOUT_PAGES_LINE_COUNT > 1
641 if (line >= UX_LAYOUT_PAGES_LINE_COUNT - 1)
642#endif // UX_LAYOUT_PAGES_LINE_COUNT
643 {
644 return 1;
645 }
646 }
647
648 // prepare for next line
649 start += len;
650
651 // skip to next line/page
652 line++;
653
654 // Do we want to jump to next page?
655 if (c == '\f'
656 || (
657#if UX_LAYOUT_PAGES_LINE_COUNT > 1
658 line >= UX_LAYOUT_PAGES_LINE_COUNT &&
659#endif // UX_LAYOUT_PAGES_LINE_COUNT
660 start < end)) {
661 page++;
662 line = 0;
663 icon_on_side = 0;
664 page_start = start;
665 string_buffer_mark_offset = (unsigned int) -1;
666 string_buffer_mark_len = 0;
667 page_use_bold_font = use_bold_font;
668 }
669 }
670
671 // return total number of pages detected
672 return page + 1;
673}
674
675// =============================================================================
676
677void ux_layout_pages_display_init(const char *text)
678{
679 // Compute all UX_LOC_PAGE stuff into G_ux.layout_paging
680 G_ux.layout_paging.current = 0;
681 G_ux.layout_paging.count = 1;
682 G_ux.layout_paging.paging_title = 0;
683 G_ux.layout_paging.format = PAGING_FORMAT_NN;
684 // request offsets and lengths of lines for the current page
685 ux_layout_pages_compute(text, G_ux.layout_paging.current, &G_ux.layout_paging);
686}
687
688const bagl_element_t *ux_layout_pages_display_element(const bagl_element_t *element,
689 const char *text)
690{
691 return ux_layout_pages_prepro_common(element, text);
692}
693
694// =============================================================================
695
696// redisplay current page
697void ux_layout_pages_redisplay_common(unsigned int stack_slot,
698 const char *text,
699 button_push_callback_t button_callback,
701{
702 ux_stack_slot_t *slot = &G_ux.stack[stack_slot];
703
704 slot->element_arrays[0].element_array = ux_layout_pages_elements;
705 slot->element_arrays[0].element_array_count = ARRAYLEN(ux_layout_pages_elements);
706 slot->element_arrays_count = 1;
707
708 // request offsets and lengths of lines for the current page
709 ux_layout_pages_compute(text, G_ux.layout_paging.current, &G_ux.layout_paging);
710
712 slot->button_push_callback = button_callback;
713 ux_stack_display(stack_slot);
714}
715
716static const bagl_element_t *ux_layout_pages_prepro_by_addr(const bagl_element_t *element)
717{
718 // don't display if null
719 const void *params = ux_stack_get_current_step_params();
720 if (NULL == params) {
721 return NULL;
722 }
723 const char *text;
724
725#if defined(HAVE_INDEXED_STRINGS)
726 text = get_ux_loc_string(((const ux_loc_layout_params_t *) params)->index);
727#else // defined(HAVE_INDEXED_STRINGS)
728 text = ((const ux_layout_pages_params_t *) params)->text;
729#endif // defined(HAVE_INDEXED_STRINGS)
730 return ux_layout_pages_prepro_common(element, text);
731}
732
733unsigned int ux_layout_pages_button_callback_by_addr(unsigned int button_mask,
734 unsigned int button_mask_counter);
735unsigned int ux_loc_layout_paging_button_callback_by_addr(unsigned int button_mask,
736 unsigned int button_mask_counter);
737
738void ux_layout_pages_redisplay_by_addr(unsigned int stack_slot)
739{
740 const char *text;
741 const void *params = ux_stack_get_current_step_params();
742 if (NULL == params) {
743 return;
744 }
745#if defined(HAVE_INDEXED_STRINGS)
746 text = get_ux_loc_string(((const ux_loc_layout_params_t *) params)->index);
747#else // defined(HAVE_INDEXED_STRINGS)
748 text = ((const ux_layout_pages_params_t *) params)->text;
749#endif // defined(HAVE_INDEXED_STRINGS)
750 ux_layout_pages_redisplay_common(
751 stack_slot, text, ux_layout_pages_button_callback_by_addr, ux_layout_pages_prepro_by_addr);
752}
753
754static const bagl_element_t *ux_loc_layout_paging_prepro_by_addr(const bagl_element_t *element)
755{
756 // don't display if null
757 const void *params = ux_stack_get_current_step_params();
758 if (NULL == params) {
759 return NULL;
760 }
761 const char *text;
762
763 // As 0 mean it's not a UX_LOC_PAGING (due to all the memset layout_paging)
764 // Store index value + 1
765 G_ux.layout_paging.paging_title = ((const ux_loc_layout_params_t *) params)->index + 1;
766 text = get_ux_loc_string(G_ux.layout_paging.paging_title - 1 + 1);
767 return ux_layout_pages_prepro_common(element, text);
768}
769
770void ux_loc_layout_paging_redisplay_by_addr(unsigned int stack_slot)
771{
772 const char *text;
773 const void *params = ux_stack_get_current_step_params();
774 if (NULL == params) {
775 return;
776 }
777 // As 0 mean it's not a UX_LOC_PAGING (due to all the memset layout_paging)
778 // Store index value + 1
779 G_ux.layout_paging.paging_title = ((const ux_loc_layout_params_t *) params)->index + 1;
780 text = get_ux_loc_string(G_ux.layout_paging.paging_title - 1 + 1);
781 ux_layout_pages_redisplay_common(stack_slot,
782 text,
783 ux_loc_layout_paging_button_callback_by_addr,
784 ux_loc_layout_paging_prepro_by_addr);
785}
786
787// Function located in ux_layout_paging.c
788unsigned int ux_layout_paging_button_callback_common(unsigned int button_mask,
789 unsigned int button_mask_counter,
790 ux_layout_paging_redisplay_t redisplay);
791
792unsigned int ux_loc_layout_paging_button_callback_by_addr(unsigned int button_mask,
793 unsigned int button_mask_counter)
794{
795 return ux_layout_paging_button_callback_common(
796 button_mask, button_mask_counter, ux_loc_layout_paging_redisplay_by_addr);
797}
798
799unsigned int ux_layout_pages_button_callback_by_addr(unsigned int button_mask,
800 unsigned int button_mask_counter)
801{
802 return ux_layout_paging_button_callback_common(
803 button_mask, button_mask_counter, ux_layout_pages_redisplay_by_addr);
804}
805
806void ux_layout_pages_init(unsigned int stack_slot)
807{
808 const char *text;
809 const void *params = ux_stack_get_step_params(stack_slot);
810
811 G_ux.layout_paging.format = PAGING_FORMAT_NN; // By default
812 G_ux.layout_paging.paging_title = 0; // 0 mean it's not a UX_LOC_PAGING
813#if defined(HAVE_INDEXED_STRINGS)
814 text = get_ux_loc_string(((const ux_loc_layout_params_t *) params)->index);
815#else // defined(HAVE_INDEXED_STRINGS)
816 text = ((const ux_layout_pages_params_t *) params)->text;
817#endif // defined(HAVE_INDEXED_STRINGS)
818 ux_layout_pages_init_common(stack_slot, text, ux_layout_pages_redisplay_by_addr);
819}
820
821void ux_loc_layout_pages_init(unsigned int stack_slot)
822{
823 ux_layout_pages_init(stack_slot);
824}
825
826void ux_loc_layout_paging_init(unsigned int stack_slot)
827{
828 const char *text;
829 const void *params = ux_stack_get_step_params(stack_slot);
830
831 G_ux.layout_paging.format = PAGING_FORMAT_NN; // By default
832 // As 0 mean it's not a UX_LOC_PAGING (due to all the memset layout_paging)
833 // Store index value + 1
834 G_ux.layout_paging.paging_title = ((const ux_loc_layout_params_t *) params)->index + 1;
835 text = get_ux_loc_string(G_ux.layout_paging.paging_title - 1 + 1);
836 ux_layout_pages_init_common(stack_slot, text, ux_loc_layout_paging_redisplay_by_addr);
837}
838
839#endif // (defined HAVE_BOLOS || defined(BUILD_PNG))
840
841void ux_layout_pages_init_common(unsigned int stack_slot,
842 const char *text,
843 ux_layout_paging_redisplay_t redisplay)
844{
845 // At this very moment, we don't want to get rid of the format nor paging_title, so
846 // keep the one which has just been set (in case of direction backward or forward).
847 unsigned int backup_format = G_ux.layout_paging.format;
848 unsigned int backup_title = G_ux.layout_paging.paging_title;
849
850 // depending flow browsing direction, select the correct page to display
851 switch (ux_flow_direction()) {
853 ux_layout_paging_reset();
854 // ask the paging to start at the last page.
855 // This step must be performed after the 'ux_layout_paging_reset' call,
856 // thus we cannot mutualize the call with the one in the 'forward' case.
857 G_ux.layout_paging.current = (unsigned int) -1;
858 break;
860 // open the first page
861 ux_layout_paging_reset();
862 break;
864 // shall already be at the first page
865 break;
866 }
867
868 G_ux.layout_paging.format = backup_format;
869 G_ux.layout_paging.paging_title = backup_title;
870
871 // store params
872 ux_stack_init(stack_slot);
873
874 // compute number of chars to display from the params complete string
875 if ((text == NULL) && (G_ux.externalText == NULL)) {
876 text = ""; // empty string to avoid disrupting the ux flow.
877 }
878
879 // count total number of pages
880 G_ux.layout_paging.count = ux_layout_pages_compute(
881 text, (unsigned int) -1, &G_ux.layout_paging); // at least one page
882
883 // PRINTF("%d pages needed for =>%s<=\n", G_ux.layout_paging.count, text);
884 if (G_ux.layout_paging.count == 0) {
885 ux_layout_paging_reset();
886 }
887
888 // if (start != end) {
889 // ux_layout_paging_reset();
890 // }
891
892 // perform displaying the last page as requested (-1UL in prevstep hook does this)
893 if (G_ux.layout_paging.count && G_ux.layout_paging.current > (G_ux.layout_paging.count - 1)) {
894 G_ux.layout_paging.current = G_ux.layout_paging.count - 1;
895 }
896
897 redisplay(stack_slot);
898}
899
900#endif // HAVE_BAGL
901#endif // defined(HAVE_INDEXED_STRINGS)
902#endif // HAVE_UX_FLOW
#define BAGL_ENCODING_DEFAULT
Definition nbgl_fonts.h:96
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 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
@ FLOW_DIRECTION_BACKWARD
@ FLOW_DIRECTION_FORWARD
@ FLOW_DIRECTION_START
ux_flow_direction_t ux_flow_direction(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)
const void * ux_stack_get_current_step_params(void)
void ux_layout_pages_init(unsigned int stack_slot)
void ux_loc_layout_paging_init(unsigned int stack_slot)
void ux_loc_layout_pages_init(unsigned int stack_slot)