Embedded SDK
Embedded SDK
nbgl_screen.c
Go to the documentation of this file.
1 
6 /*********************
7  * INCLUDES
8  *********************/
9 #include "app_config.h"
10 #include "nbgl_front.h"
11 #include "nbgl_screen.h"
12 #include "nbgl_debug.h"
13 #include "nbgl_touch.h"
14 #include "os_pic.h"
15 #include "os_io.h"
16 #include "os_task.h"
17 
18 /*********************
19  * DEFINES
20  *********************/
21 
39 #define SCREEN_STACK_SIZE 4
40 
41 /**********************
42  * TYPEDEFS
43  **********************/
44 
45 /**********************
46  * VARIABLES
47  **********************/
48 static nbgl_screen_t screenStack[SCREEN_STACK_SIZE];
49 // number of screens in the stack
50 static uint8_t nbScreensOnStack = 0;
51 // this is a pointer of the current top of stack screen
52 static nbgl_screen_t *topOfStack;
53 
54 /**********************
55  * STATIC PROTOTYPES
56  **********************/
57 
58 /**********************
59  * GLOBAL FUNCTIONS
60  **********************/
61 
67 {
68  if (nbScreensOnStack == 0) {
69  LOG_WARN(SCREEN_LOGGER, "nbgl_screenRedraw(): no screen to redraw\n");
70  return;
71  }
72  LOG_DEBUG(SCREEN_LOGGER, "nbgl_screenRedraw(): nbScreensOnStack = %d\n", nbScreensOnStack);
73 #ifdef TARGET_STAX
74  // by default, exclude only left border from touch
75  // if any sub-object is a keyboard, this will be modified when drawing it
76  touch_exclude_borders(LEFT_BORDER);
77 #endif // TARGET_STAX
78 
80  nbgl_objDraw((nbgl_obj_t *) topOfStack);
81 }
82 
89 {
90  if (screenIndex < nbScreensOnStack) {
91  return (nbgl_obj_t *) &screenStack[screenIndex];
92  }
93  else {
94  return NULL;
95  }
96 }
97 
103 {
104  if (nbScreensOnStack > 0) {
105  return (nbgl_obj_t *) topOfStack;
106  }
107  else {
108  return NULL;
109  }
110 }
111 
117 {
118  if ((nbScreensOnStack == 1) && (screenStack[0].container.nbChildren == 0)) {
119  return 0;
120  }
121  return nbScreensOnStack;
122 }
123 
129 {
130  uint8_t nbUxScreens = 0;
131  nbgl_screen_t *screen = (nbgl_screen_t *) topOfStack;
132 
133  if (nbgl_screenGetCurrentStackSize() == 0) {
134  return 0;
135  }
136  while (screen != NULL) {
137  if (screen->isUxScreen) {
138  nbUxScreens++;
139  }
140  screen = screen->previous;
141  }
142  return nbUxScreens;
143 }
144 
161 static int nbgl_screenSetAt(uint8_t screenIndex,
162  nbgl_obj_t ***children,
163  uint8_t nbChildren,
164  const nbgl_screenTickerConfiguration_t *ticker,
165 #ifdef HAVE_SE_TOUCH
166  nbgl_touchCallback_t callback)
167 {
168 #else // HAVE_SE_TOUCH
169  nbgl_buttonCallback_t callback)
170 {
171 #endif // HAVE_SE_TOUCH
172  if (screenIndex >= SCREEN_STACK_SIZE) {
173  LOG_WARN(SCREEN_LOGGER, "nbgl_screenSetAt(): forbidden screenIndex (%d)\n", screenIndex);
174  return -1;
175  }
176  *children = nbgl_containerPoolGet(nbChildren, screenIndex);
177  screenStack[screenIndex].container.obj.type = SCREEN;
178 #ifdef HAVE_SE_TOUCH
179  screenStack[screenIndex].container.obj.area.backgroundColor = WHITE;
180 #else // HAVE_SE_TOUCH
181  screenStack[screenIndex].container.obj.area.backgroundColor = BLACK;
182 #endif // HAVE_SE_TOUCH
183  screenStack[screenIndex].container.obj.area.height = SCREEN_HEIGHT;
184  screenStack[screenIndex].container.obj.area.width = SCREEN_WIDTH;
185  screenStack[screenIndex].container.obj.area.x0 = 0;
186  screenStack[screenIndex].container.obj.area.y0 = 0;
187  screenStack[screenIndex].container.obj.rel_x0 = 0;
188  screenStack[screenIndex].container.obj.rel_y0 = 0;
189  screenStack[screenIndex].index = screenIndex;
190  screenStack[screenIndex].container.layout = VERTICAL;
191  screenStack[screenIndex].container.children = *children;
192  screenStack[screenIndex].container.nbChildren = nbChildren;
193 #ifdef HAVE_SE_TOUCH
194  screenStack[screenIndex].touchCallback = callback;
195 #else // HAVE_SE_TOUCH
196  screenStack[screenIndex].buttonCallback = callback;
197 #endif // HAVE_SE_TOUCH
198  if (ticker != NULL) {
199  screenStack[screenIndex].ticker.tickerCallback
200  = (nbgl_tickerCallback_t) PIC(ticker->tickerCallback);
201  screenStack[screenIndex].ticker.tickerIntervale = ticker->tickerIntervale;
202  screenStack[screenIndex].ticker.tickerValue = ticker->tickerValue;
203  }
204  else {
205  screenStack[screenIndex].ticker.tickerCallback = NULL;
206  }
207  screenStack[screenIndex].isUxScreen = (os_sched_current_task() == TASK_BOLOS_UX);
208  return 0;
209 }
210 
225 int nbgl_screenSet(nbgl_obj_t ***elements,
226  uint8_t nbElements,
227  const nbgl_screenTickerConfiguration_t *ticker,
228 #ifdef HAVE_SE_TOUCH
229  nbgl_touchCallback_t callback)
230 {
231 #else // HAVE_SE_TOUCH
232  nbgl_buttonCallback_t callback)
233 {
234 #endif // HAVE_SE_TOUCH
235  // if no screen, consider it as a first fake push
236  if (nbScreensOnStack == 0) {
237  nbScreensOnStack++;
238  topOfStack = &screenStack[0];
239  }
240  // release used objects and containers
243  // always use the first layer (background) for user application
244  return nbgl_screenSetAt(0, elements, nbElements, ticker, callback);
245 }
246 
257 int nbgl_screenUpdateNbElements(uint8_t screenIndex, uint8_t nbElements)
258 {
259  screenStack[screenIndex].container.nbChildren = nbElements;
260  return 0;
261 }
262 
274 {
275  screenStack[screenIndex].container.obj.area.backgroundColor = color;
276  return 0;
277 }
278 
290 {
291  if (ticker != NULL) {
292  screenStack[screenIndex].ticker.tickerCallback
293  = (nbgl_tickerCallback_t) PIC(ticker->tickerCallback);
294  screenStack[screenIndex].ticker.tickerIntervale = ticker->tickerIntervale;
295  screenStack[screenIndex].ticker.tickerValue = ticker->tickerValue;
296  }
297  else {
298  screenStack[screenIndex].ticker.tickerCallback = NULL;
299  }
300  return 0;
301 }
302 
313 {
314  return screenStack[screenIndex].container.children;
315 }
316 
330 int nbgl_screenPush(nbgl_obj_t ***elements,
331  uint8_t nbElements,
332  const nbgl_screenTickerConfiguration_t *ticker,
333 #ifdef HAVE_SE_TOUCH
334  nbgl_touchCallback_t callback)
335 {
336 #else // HAVE_SE_TOUCH
337  nbgl_buttonCallback_t callback)
338 {
339 #endif // HAVE_SE_TOUCH
340  uint8_t screenIndex;
341  if (nbScreensOnStack >= SCREEN_STACK_SIZE) {
343  "nbgl_screenPush(): already in highest index in the stack(%d)\n",
344  nbScreensOnStack - 1);
345  return -1;
346  }
347  // if no screen, consider it as a first fake push
348  if (nbScreensOnStack == 0) {
349  screenIndex = 1; // push at position 1 because 0 is reserved for background
350  topOfStack = &screenStack[screenIndex];
351  topOfStack->next = NULL;
352  // link top of stack to background (even if empty)
353  topOfStack->previous = &screenStack[0];
354  screenStack[0].next = topOfStack;
355  screenStack[0].container.nbChildren = 0;
356  // count empty background as an active screen
357  nbScreensOnStack++;
358  }
359  else {
360  // find a non used screen in the array
361  for (screenIndex = 1; screenIndex < SCREEN_STACK_SIZE; screenIndex++) {
362  if (screenStack[screenIndex].previous == NULL) {
363  // if no previous, means unused, so take it
364  // update previous topOfStack
365  topOfStack->next = &screenStack[screenIndex];
366  screenStack[screenIndex].previous = topOfStack;
367 #ifdef HAVE_SE_TOUCH
368  nbgl_touchStatePosition_t touchStatePosition = {.state = RELEASED, .x = 0, .y = 0};
369  // make a fake touch release for the current top-of-stack to avoid issue
370  // (for example in long-touch press)
371  nbgl_touchHandler(&touchStatePosition, 0);
372 #endif // HAVE_SE_TOUCH
373  // new top of stack
374  topOfStack = &screenStack[screenIndex];
375  topOfStack->next = NULL;
376  break;
377  }
378  }
379  if (screenIndex == SCREEN_STACK_SIZE) {
380  // should never happen
381  LOG_WARN(SCREEN_LOGGER, "nbgl_screenPush(): corruption in stack\n");
382  }
383  }
384  if (nbgl_screenSetAt(screenIndex, elements, nbElements, ticker, callback) >= 0) {
385  nbScreensOnStack++;
386  LOG_DEBUG(SCREEN_LOGGER, "nbgl_screenPush(): screen %d is now top of stack\n", screenIndex);
387  return screenIndex;
388  }
389  else {
390  return -1;
391  }
392 }
393 
403 int nbgl_screenPop(uint8_t screenIndex)
404 {
405  if (nbScreensOnStack == 0) {
406  LOG_WARN(SCREEN_LOGGER, "nbgl_screenPop(): already in lowest index in the stack\n");
407  return -1;
408  }
409  LOG_DEBUG(SCREEN_LOGGER, "nbgl_screenPop(): at index %d\n", screenIndex);
410  nbScreensOnStack--;
411  // move top of stack if needed
412  if (&screenStack[screenIndex] == topOfStack) {
413  if (nbScreensOnStack == 0) {
414  topOfStack = NULL;
415  }
416  else {
417  topOfStack = topOfStack->previous;
418  }
419  }
420  else {
421  // connect previous to next
422  if (screenStack[screenIndex].previous != NULL) {
423  screenStack[screenIndex].previous->next = screenStack[screenIndex].next;
424  }
425  if (screenStack[screenIndex].next != NULL) {
426  screenStack[screenIndex].next->previous = screenStack[screenIndex].previous;
427  }
428  }
429  // free slot
430  screenStack[screenIndex].previous = NULL;
431  screenStack[screenIndex].next = NULL;
432  screenStack[screenIndex].container.nbChildren = 0;
433  screenStack[screenIndex].container.children = NULL;
434  // release used objects and containers
435  nbgl_objPoolRelease(screenIndex);
436  nbgl_containerPoolRelease(screenIndex);
437 
438  // special case when we pop the only modal and no real background is under it
439  if ((nbScreensOnStack == 1) && (screenStack[0].container.nbChildren == 0)) {
440  nbScreensOnStack = 0;
441  topOfStack = NULL;
442  }
443  return 0;
444 }
445 
453 {
454  uint8_t screenIndex;
455  for (screenIndex = 0; screenIndex < SCREEN_STACK_SIZE; screenIndex++) {
456  if ((screenStack[screenIndex].previous != NULL)
457  || (screenStack[screenIndex].next != NULL)) {
458  // release used objects and containers
459  nbgl_objPoolRelease(screenIndex);
460  nbgl_containerPoolRelease(screenIndex);
461  }
462  screenStack[screenIndex].container.children = NULL;
463  screenStack[screenIndex].container.nbChildren = 0;
464  }
465  nbScreensOnStack = 0;
466  topOfStack = NULL;
467  return 1;
468 }
469 
475 void nbgl_screenHandler(uint32_t intervaleMs)
476 {
477  // ensure a screen exists
478  if (nbScreensOnStack == 0) {
479  return;
480  }
481  // call ticker callback of top of stack if active and not expired yet (for a non periodic)
482  if ((topOfStack->ticker.tickerCallback != NULL) && (topOfStack->ticker.tickerValue != 0)) {
483  topOfStack->ticker.tickerValue -= MIN(topOfStack->ticker.tickerValue, intervaleMs);
484  if (topOfStack->ticker.tickerValue == 0) {
485  // rearm if intervale is not null, and call the registered function
486  topOfStack->ticker.tickerValue = topOfStack->ticker.tickerIntervale;
487  topOfStack->ticker.tickerCallback();
488  }
489  }
490 }
491 
499 static bool objIsIn(nbgl_obj_t *refObj, nbgl_obj_t *obj)
500 {
501  uint8_t i;
502 
503  if (obj == NULL) {
504  return false;
505  }
506  if ((nbgl_obj_t *) refObj == obj) {
507  LOG_DEBUG(SCREEN_LOGGER, "nbgl_screenContainsObj(): yes\n");
508  return true;
509  }
510 
511  if ((refObj->type == SCREEN) || (refObj->type == CONTAINER)) {
512  nbgl_container_t *container = (nbgl_container_t *) refObj;
513  // draw the children, if any
514  if (container->children != NULL) {
515  for (i = 0; i < container->nbChildren; i++) {
516  nbgl_obj_t *current = container->children[i];
517  if (current != NULL) {
518  if (objIsIn(current, obj) == true) {
519  return true;
520  }
521  }
522  }
523  }
524  }
525  return false;
526 }
527 
535 static nbgl_obj_t *objIsOfType(nbgl_obj_t *refObj, nbgl_obj_type_t type)
536 {
537  uint8_t i;
538 
539  if (refObj->type == type) {
540  LOG_DEBUG(SCREEN_LOGGER, "objIsOfType(): yes\n");
541  return refObj;
542  }
543 
544  if ((refObj->type == SCREEN) || (refObj->type == CONTAINER)) {
545  nbgl_container_t *container = (nbgl_container_t *) refObj;
546  // draw the children, if any
547  if (container->children != NULL) {
548  for (i = 0; i < container->nbChildren; i++) {
549  nbgl_obj_t *current = container->children[i];
550  if (current != NULL) {
551  nbgl_obj_t *found = objIsOfType(current, type);
552  if (found) {
553  return found;
554  }
555  }
556  }
557  }
558  }
559  return NULL;
560 }
561 
569 {
570  if (nbScreensOnStack == 0) {
571  return false;
572  }
573  return objIsIn((nbgl_obj_t *) topOfStack, obj);
574 }
575 
584 {
585  if (nbScreensOnStack == 0) {
586  return NULL;
587  }
588 
589  return objIsOfType((nbgl_obj_t *) screen, type);
590 }
debug traces management
#define LOG_WARN(__logger,...)
Definition: nbgl_debug.h:87
#define LOG_DEBUG(__logger,...)
Definition: nbgl_debug.h:86
@ SCREEN_LOGGER
Definition: nbgl_debug.h:32
Font screen low-Level driver API, to draw elementary forms.
void(* nbgl_touchCallback_t)(void *obj, nbgl_touchType_t eventType)
prototype of function to be called when a touch event is received by an object
Definition: nbgl_obj.h:221
void nbgl_objDraw(nbgl_obj_t *obj)
This function draws or redraws the given object and its children (recursive version)
Definition: nbgl_obj.c:1521
nbgl_obj_t ** nbgl_containerPoolGet(uint8_t nbObjs, uint8_t layer)
Gets a new container from the pool, with the given number of obj pointers.
void nbgl_containerPoolRelease(uint8_t layer)
Release the objects pointers from the pool for the given layer.
void(* nbgl_buttonCallback_t)(void *obj, nbgl_buttonEvent_t buttonEvent)
prototype of function to be called when a button event is received by an object (TODO: change to scre...
Definition: nbgl_obj.h:200
struct PACKED__ nbgl_container_s nbgl_container_t
struct to represent a container (CONTAINER type)
void nbgl_objPoolRelease(uint8_t layer)
Release the objects from the pool for the given layer.
struct PACKED__ nbgl_obj_s nbgl_obj_t
Common structure for all graphical objects.
#define SCREEN_STACK_SIZE
Max number of stackable screens.
Definition: nbgl_screen.c:39
nbgl_obj_t * nbgl_screenGetTop(void)
Returns the screen on top layer, as a generic object.
Definition: nbgl_screen.c:102
int nbgl_screenSet(nbgl_obj_t ***elements, uint8_t nbElements, const nbgl_screenTickerConfiguration_t *ticker, nbgl_touchCallback_t callback)
Configures the lowest layer screen. To be used by applications A nbgl_screenRedraw() can be called af...
Definition: nbgl_screen.c:225
int nbgl_screenPush(nbgl_obj_t ***elements, uint8_t nbElements, const nbgl_screenTickerConfiguration_t *ticker, nbgl_touchCallback_t callback)
Pushes a screen on top of the stack, with the given number of elements, if possible....
Definition: nbgl_screen.c:330
int nbgl_screenUpdateNbElements(uint8_t screenIndex, uint8_t nbElements)
Updates the number of children on given layer. can only be smaller than the number given in nbgl_scre...
Definition: nbgl_screen.c:257
int nbgl_screenUpdateTicker(uint8_t screenIndex, const nbgl_screenTickerConfiguration_t *ticker)
Updates the ticker configuration of the screen at the given screenIndex, always set at WHITE in.
Definition: nbgl_screen.c:289
bool nbgl_screenContainsObj(nbgl_obj_t *obj)
return true if the given obj can be found in refObj or any of its children
Definition: nbgl_screen.c:568
int nbgl_screenReset(void)
Releases all screens and objects and resets the screen stack. It is supposed to be called before runn...
Definition: nbgl_screen.c:452
nbgl_obj_t ** nbgl_screenGetElements(uint8_t screenIndex)
Returns the array of elements (children) of the screen at the given index (return value of nbgl_scree...
Definition: nbgl_screen.c:312
uint8_t nbgl_screenGetUxStackSize(void)
Returns the number of used UX screens on stack.
Definition: nbgl_screen.c:128
int nbgl_screenPop(uint8_t screenIndex)
Release the screen at the given index in screen array (index returned by nbgl_screenPush())....
Definition: nbgl_screen.c:403
uint8_t nbgl_screenGetCurrentStackSize(void)
Returns the number of used screens on stack.
Definition: nbgl_screen.c:116
nbgl_obj_t * nbgl_screenContainsObjType(nbgl_screen_t *screen, nbgl_obj_type_t type)
return an object of the given type in the given screen
Definition: nbgl_screen.c:583
nbgl_obj_t * nbgl_screenGetAt(uint8_t screenIndex)
Returns the screen on the given layer index, as a generic object.
Definition: nbgl_screen.c:88
int nbgl_screenUpdateBackgroundColor(uint8_t screenIndex, color_t color)
Updates the background color of the screen at the given screenIndex, always set at WHITE in.
Definition: nbgl_screen.c:273
void nbgl_screenRedraw(void)
This function redraws the whole screen on top of stack and its children.
Definition: nbgl_screen.c:66
void nbgl_screenHandler(uint32_t intervaleMs)
Function to be called periodically by system to enable using ticker.
Definition: nbgl_screen.c:475
API to manage screens.
struct PACKED__ nbgl_screen_s nbgl_screen_t
struct to represent a screen (SCREEN type)
void(* nbgl_tickerCallback_t)(void)
prototype of function to be called when a timer on screen is fired
Definition: nbgl_screen.h:32
struct PACKED__ nbgl_screenTickerConfiguration_s nbgl_screenTickerConfiguration_t
struct to configure a screen layer
void nbgl_screen_reinit(void)
void nbgl_touchHandler(nbgl_touchStatePosition_t *touchEvent, uint32_t currentTimeMs)
Function to be called periodically to check touchscreen state and coordinates.
Definition: nbgl_touch.c:265
color_t
Definition: nbgl_types.h:101
@ WHITE
Definition: nbgl_types.h:105
@ BLACK
Definition: nbgl_types.h:102
#define SCREEN_WIDTH
Definition: nbgl_types.h:32
@ VERTICAL
from top to bottom
Definition: nbgl_types.h:170
#define SCREEN_HEIGHT
Definition: nbgl_types.h:45
#define MIN(x, y)
Definition: nbgl_types.h:79
nbgl_obj_type_t
All types of graphical objects.
Definition: nbgl_types.h:115
@ CONTAINER
Empty container.
Definition: nbgl_types.h:117
@ SCREEN
Main screen.
Definition: nbgl_types.h:116
@ RELEASED
the finger has been released from the screen
Definition: nbgl_types.h:201
The low level Touchscreen event, coming from driver.
Definition: nbgl_obj.h:206
nbgl_touchState_t state
state of the touch event, e.g PRESSED or RELEASED
Definition: nbgl_obj.h:207
unsigned char uint8_t
Definition: usbd_conf.h:53