Low-level display management
Introduction
This article only concerns about 1% of users who wish to get a deeper understanding and/or design more advanced flows. Feel free to skip it.
BAGLs and Ledger OS
The Ledger OS SDKs contain a toolkit for building GUIs called the BOLOS Application Graphics Library (BAGL). BAGL defines a few useful types, most notably bagl_element_t
. This type defines a single display element, such as a rectangle or a line of text, etc. Therefore, a GUI consists of an entire array of such elements. As the device hardware, including the screen, is managed by the MCU, the BAGL elements need to be transported to the MCU over SEPROXYHAL in order to display them. This is done using a Display Status.
A Display Status may be used to send a single BAGL element to the MCU. However, due to the design of the E/Cs/S protocol, in order to send a sequence of BAGL elements, the application must:
- asynchronously send Display Statuses and wait for Display Processed Events before sending the next one
- handle the other Events that are received from the MCU in the mean time.
As this is something that must be done by every Ledger OS application, a set of utilities have been defined in the SDKs to facilitate this process. These utilities also simplify the process of handling user input events, such as button presses.
Asynchronous Display and Interaction Helpers
To facilitate the process of implementing an asynchronous display manager loop, a set of macros have been defined in the Ledger OS SDKs. All these macros have the prefix UX_
, and use a global variable of type ux_state_t
called ux
to maintain the user interface state.
-
UX_INIT(...)
must be called when initializing the application, prior to sending the first Display Status. -
UX_DISPLAY(...)
takes an array of BAGL elements to be displayed and renders them, asynchronously. It is called when a new screen is displayed over the current one. This macro only sends the first element of the given array using a Display Status. Therefore, further Commands and Statuses are prohibited until the Display Processed Event is sent by the MCU. -
UX_REDISPLAY()
restarts the process of drawing the current screen. It behaves likeUX_DISPLAY(...)
, but takes no arguments. -
UX_DISPLAYED()
returns 0 when the array requested to be displayed byUX_DISPLAY(...)
orUX_REDISPLAY()
has not been displayed entirely, and it returns a non-zero value when the array has been displayed entirely. -
UX_DISPLAYED_EVENT(...)
is called to handle Display Processed Events (generally in theio_event(...)
function). It displays the next element in the array given as a parameter toUX_DISPLAY(...)
. This macro sends a Display Status if an element in the given array remains to be displayed. Therefore, further Commands and Statuses are prohibited until the Display Processed Event is sent by the MCU. -
UX_BUTTON_PUSH_EVENT(...)
facilitates handling Button Push Events, by setting the button released flag and calling the button handler implicitly passed toUX_DISPLAY(...)
.
See the sample apps for examples of how to use these macros.
After a Display Status has been sent, the application must wait, asynchronously, for the Display Processed Event before being able to continue to display more elements of the GUI.
Ledger OS UX
The Ledger OS UX is the implementation of the device-wide user interface; it is a component of the dashboard application. Applications delegate certain jobs to the BOLOS UX in order to retain consistency across all apps for certain UI components, as well as to allow the operating system to override the application’s UI when necessary (for example, when locking the screen). The application interfaces with the Ledger OS UX using os_ux(...)
, which is a system call. However, applications don’t need to call this function as it is automatically called by the display interaction helpers (the UX_
macros).
Applications should delegate Events like Button Push Events to the Ledger OS UX (in this case, using UX_BUTTON_PUSH_EVENT(...)
) instead of handling them directly, in case the Ledger OS UX needs to override the application’s UI. If the event is consumed by the Ledger OS UX (for example, a pressed button while the user is unlocking the screen) then the event is not passed on to the application.