Embedded SDK
Embedded SDK
nbgl_obj_keypad.c
Go to the documentation of this file.
1 
8 #ifdef NBGL_KEYPAD
9 #ifdef HAVE_SE_TOUCH
10 
11 /*********************
12  * INCLUDES
13  *********************/
14 #include "nbgl_debug.h"
15 #include "nbgl_front.h"
16 #include "nbgl_draw.h"
17 #include "nbgl_obj.h"
18 #include "nbgl_fonts.h"
19 #include "nbgl_touch.h"
20 #include "glyphs.h"
21 #include "os_io_seproxyhal.h"
22 #include "lcx_rng.h"
23 
24 /*********************
25  * DEFINES
26  *********************/
27 #define KEY_WIDTH (SCREEN_WIDTH / 3)
28 #define DIGIT_OFFSET_Y (((KEYPAD_KEY_HEIGHT - 48) / 2) & 0xFFC)
29 
30 #define BACKSPACE_KEY_IDX 10
31 #define VALIDATE_KEY_IDX 11
32 
33 // to save RAM we use 5 uint8, and 4 bits per digit (MSBs for odd digits, LSBs for even ones)
34 #define GET_DIGIT_INDEX(_keypad, _digit) \
35  ((_digit & 1) ? (_keypad->digitIndexes[_digit >> 1] >> 4) \
36  : (_keypad->digitIndexes[_digit >> 1] & 0xF))
37 #define SET_DIGIT_INDEX(_keypad, _digit, _index) \
38  (_keypad->digitIndexes[_digit >> 1] |= (_digit & 1) ? (_index << 4) : _index)
39 
41 
42 /**********************
43  * TYPEDEFS
44  **********************/
45 
46 /**********************
47  * STATIC VARIABLES
48  **********************/
49 
50 /**********************
51  * VARIABLES
52  **********************/
53 
54 /**********************
55  * STATIC FUNCTIONS
56  **********************/
57 
58 static uint8_t getKeypadIndex(uint16_t x, uint16_t y)
59 {
60  uint8_t i = 0;
61  // get index of key pressed
62  if (y < KEYPAD_KEY_HEIGHT) {
63  // 1st line:
64  i = 1 + x / KEY_WIDTH;
65  }
66  else if (y < (2 * KEYPAD_KEY_HEIGHT)) {
67  // 2nd line:
68  i = 4 + x / KEY_WIDTH;
69  }
70  else if (y < (3 * KEYPAD_KEY_HEIGHT)) {
71  // 3rd line:
72  i = 7 + x / KEY_WIDTH;
73  }
74  else if (y < (4 * KEYPAD_KEY_HEIGHT)) {
75  // 4th line
76  if (x < KEY_WIDTH) {
78  }
79  else if (x < (2 * KEY_WIDTH)) {
80  i = 0;
81  }
82  else {
83  i = VALIDATE_KEY_IDX;
84  }
85  }
86  return i;
87 }
88 
89 static void keypadDrawGrid(nbgl_keypad_t *keypad)
90 {
91  nbgl_area_t rectArea;
92 
93  // clean full area
94  rectArea.backgroundColor = keypad->obj.area.backgroundColor;
95  rectArea.x0 = keypad->obj.area.x0;
96  rectArea.y0 = keypad->obj.area.y0;
97  rectArea.width = keypad->obj.area.width;
98  rectArea.height = keypad->obj.area.height;
99  nbgl_frontDrawRect(&rectArea);
100 
102  rectArea.backgroundColor = keypad->obj.area.backgroundColor;
103  rectArea.x0 = keypad->obj.area.x0;
104  rectArea.y0 = keypad->obj.area.y0;
105  rectArea.width = keypad->obj.area.width;
106  rectArea.height = 4;
107  nbgl_frontDrawHorizontalLine(&rectArea, 0x1, keypad->borderColor); // 1st line (top)
108  rectArea.y0 += KEYPAD_KEY_HEIGHT;
109  nbgl_frontDrawHorizontalLine(&rectArea, 0x1, keypad->borderColor); // 2nd line
110  rectArea.y0 += KEYPAD_KEY_HEIGHT;
111  nbgl_frontDrawHorizontalLine(&rectArea, 0x1, keypad->borderColor); // 3rd line
112  rectArea.y0 += KEYPAD_KEY_HEIGHT;
113  nbgl_frontDrawHorizontalLine(&rectArea, 0x1, keypad->borderColor); // 4th line
114 
116  rectArea.backgroundColor = keypad->borderColor;
117  rectArea.x0 = keypad->obj.area.x0;
118  rectArea.y0 = keypad->obj.area.y0;
119  rectArea.width = 1;
120  rectArea.height = KEYPAD_KEY_HEIGHT * 4;
121  nbgl_frontDrawRect(&rectArea); // 1st full line, on the left
122  rectArea.x0 += KEY_WIDTH;
123  nbgl_frontDrawRect(&rectArea); // 2nd line
124  rectArea.x0 += KEY_WIDTH;
125  nbgl_frontDrawRect(&rectArea); // 3rd line
126 }
127 
128 static void keypadDrawDigits(nbgl_keypad_t *keypad)
129 {
130  uint8_t i;
131  nbgl_area_t rectArea;
132  char key_value;
133 
134  rectArea.backgroundColor = keypad->obj.area.backgroundColor;
135  // only draw digits if not a partial refresh
136  if (!keypad->partial) {
137  rectArea.y0 = keypad->obj.area.y0 + DIGIT_OFFSET_Y;
138 
139  // First row of keys: 1 2 3
140  for (i = 0; i < 3; i++) {
141  key_value = GET_DIGIT_INDEX(keypad, (i + 1)) + 0x30;
142 
143  rectArea.x0 = keypad->obj.area.x0 + i * KEY_WIDTH;
144  rectArea.x0 += (KEY_WIDTH - nbgl_getCharWidth(LARGE_MEDIUM_FONT, &key_value)) / 2;
146  &rectArea, &key_value, 1, LARGE_MEDIUM_FONT, keypad->enableDigits ? BLACK : WHITE);
147  }
148  // Second row: 4 5 6
149  rectArea.y0 += KEYPAD_KEY_HEIGHT;
150  for (; i < 6; i++) {
151  key_value = GET_DIGIT_INDEX(keypad, (i + 1)) + 0x30;
152  rectArea.x0 = keypad->obj.area.x0 + (i - 3) * KEY_WIDTH;
153  rectArea.x0 += (KEY_WIDTH - nbgl_getCharWidth(LARGE_MEDIUM_FONT, &key_value)) / 2;
155  &rectArea, &key_value, 1, LARGE_MEDIUM_FONT, keypad->enableDigits ? BLACK : WHITE);
156  }
157  // Third row: 7 8 9
158  rectArea.y0 += KEYPAD_KEY_HEIGHT;
159  for (; i < 9; i++) {
160  key_value = GET_DIGIT_INDEX(keypad, (i + 1)) + 0x30;
161  rectArea.x0 = keypad->obj.area.x0 + (i - 6) * KEY_WIDTH;
162  rectArea.x0 += (KEY_WIDTH - nbgl_getCharWidth(LARGE_MEDIUM_FONT, &key_value)) / 2;
164  &rectArea, &key_value, 1, LARGE_MEDIUM_FONT, keypad->enableDigits ? BLACK : WHITE);
165  }
166  }
167  // 4th raw, Backspace, 0 and Validate
168  // draw backspace
169  rectArea.width = BACKSPACE_ICON.width;
170  rectArea.height = BACKSPACE_ICON.height;
171  rectArea.bpp = NBGL_BPP_1;
172  rectArea.x0 = keypad->obj.area.x0 + (KEY_WIDTH - rectArea.width) / 2;
173  rectArea.y0
174  = keypad->obj.area.y0 + KEYPAD_KEY_HEIGHT * 3 + (KEYPAD_KEY_HEIGHT - rectArea.height) / 2;
175  if (BACKSPACE_ICON.isFile) {
176  nbgl_frontDrawImageFile(&rectArea,
177  (uint8_t *) BACKSPACE_ICON.bitmap,
178  keypad->enableBackspace ? BLACK : WHITE,
179  ramBuffer);
180  }
181  else {
182  nbgl_frontDrawImage(&rectArea,
183  (uint8_t *) BACKSPACE_ICON.bitmap,
185  keypad->enableBackspace ? BLACK : WHITE);
186  }
187 
188  // only draw '0' if not a partial refresh
189  if (!keypad->partial) {
190  // draw 0
191  key_value = GET_DIGIT_INDEX(keypad, 0) + 0x30;
192  rectArea.x0 = keypad->obj.area.x0 + KEY_WIDTH;
193  rectArea.x0 += (KEY_WIDTH - nbgl_getCharWidth(LARGE_MEDIUM_FONT, &key_value)) / 2;
194  rectArea.y0 = keypad->obj.area.y0 + KEYPAD_KEY_HEIGHT * 3 + DIGIT_OFFSET_Y;
196  &rectArea, &key_value, 1, LARGE_MEDIUM_FONT, keypad->enableDigits ? BLACK : WHITE);
197  }
198 
199  // draw white background if validate not enabled
200  if (!keypad->enableValidate) {
201  rectArea.width = KEY_WIDTH - 1;
202  rectArea.height = KEYPAD_KEY_HEIGHT - 4;
203  rectArea.bpp = NBGL_BPP_1;
204  rectArea.x0 = keypad->obj.area.x0 + 2 * KEY_WIDTH + 1;
205  rectArea.y0 = keypad->obj.area.y0 + KEYPAD_KEY_HEIGHT * 3 + 4;
206  rectArea.backgroundColor = WHITE;
207  nbgl_frontDrawRect(&rectArea);
209  rectArea.backgroundColor = keypad->obj.area.backgroundColor;
210  rectArea.x0 = keypad->obj.area.x0 + 2 * KEY_WIDTH;
211  rectArea.y0 = keypad->obj.area.y0 + KEYPAD_KEY_HEIGHT * 3;
212  rectArea.width = KEY_WIDTH;
213  rectArea.height = 4;
214  nbgl_frontDrawHorizontalLine(&rectArea, 0x1, keypad->borderColor); // 1st line (top)
216  rectArea.backgroundColor = keypad->borderColor;
217  rectArea.x0 = keypad->obj.area.x0 + 2 * KEY_WIDTH;
218  rectArea.y0 = keypad->obj.area.y0 + KEYPAD_KEY_HEIGHT * 3;
219  rectArea.width = 1;
220  rectArea.height = KEYPAD_KEY_HEIGHT;
221  nbgl_frontDrawRect(&rectArea); // 1st full line, on the left
222  }
223  else {
224  const nbgl_icon_details_t *icon;
225 
226  if (keypad->softValidation) {
227  icon = &RIGHT_ARROW_ICON;
228  }
229  else {
230  icon = &VALIDATE_ICON;
231  }
232  // if enabled, draw icon in white on a black background
233  rectArea.backgroundColor = BLACK;
234  rectArea.x0 = keypad->obj.area.x0 + 2 * KEY_WIDTH;
235  rectArea.y0 = keypad->obj.area.y0 + KEYPAD_KEY_HEIGHT * 3;
236  rectArea.width = KEY_WIDTH;
237  rectArea.height = KEYPAD_KEY_HEIGHT;
238  nbgl_frontDrawRect(&rectArea);
239  rectArea.width = icon->width;
240  rectArea.height = icon->height;
241  rectArea.bpp = NBGL_BPP_1;
242  rectArea.x0 = keypad->obj.area.x0 + 2 * KEY_WIDTH + (KEY_WIDTH - rectArea.width) / 2;
243  rectArea.y0 = keypad->obj.area.y0 + KEYPAD_KEY_HEIGHT * 3
244  + (KEYPAD_KEY_HEIGHT - rectArea.height) / 2;
245  if (icon->isFile) {
246  nbgl_frontDrawImageFile(&rectArea, (uint8_t *) icon->bitmap, WHITE, ramBuffer);
247  }
248  else {
249  nbgl_frontDrawImage(&rectArea, (uint8_t *) icon->bitmap, NO_TRANSFORMATION, WHITE);
250  }
251  }
252 }
253 
254 static void keypadDraw(nbgl_keypad_t *keypad)
255 {
256  if (!keypad->partial) {
257  // At first, draw grid
258  keypadDrawGrid(keypad);
259  }
260 
261  // then draw key content
262  keypadDrawDigits(keypad);
263  keypad->partial = false;
264 }
265 
266 /**********************
267  * GLOBAL FUNCTIONS
268  **********************/
269 
278 {
279  uint8_t firstIndex, lastIndex;
280  nbgl_touchStatePosition_t *firstPosition, *lastPosition;
281  nbgl_keypad_t *keypad = (nbgl_keypad_t *) obj;
282 
283  LOG_DEBUG(MISC_LOGGER, "nbgl_keypadTouchCallback(): eventType = %d\n", eventType);
284  if ((eventType != TOUCHED) && (eventType != TOUCH_PRESSED)) {
285  return;
286  }
287  if (nbgl_touchGetTouchedPosition(obj, &firstPosition, &lastPosition) == false) {
288  return;
289  }
290 
291  // use positions relative to keypad position
292  firstIndex = getKeypadIndex(firstPosition->x - obj->area.x0, firstPosition->y - obj->area.y0);
293  if (firstIndex > VALIDATE_KEY_IDX) {
294  return;
295  }
296  lastIndex = getKeypadIndex(lastPosition->x - obj->area.x0, lastPosition->y - obj->area.y0);
297  if (lastIndex > VALIDATE_KEY_IDX) {
298  return;
299  }
300 
301  // if position of finger has moved during press to another "key", drop it
302  if (lastIndex != firstIndex) {
303  return;
304  }
305 
306  if ((firstIndex < 10) && (keypad->enableDigits)) {
307  // only call callback if event is TOUCHED, otherwise play tune on touch event (and not on
308  // release)
309  if (eventType == TOUCHED) {
310  keypad->callback(GET_DIGIT_INDEX(keypad, firstIndex) + 0x30);
311  }
312  else {
313  io_seproxyhal_play_tune(TUNE_TAP_CASUAL);
314  }
315  }
316  if ((firstIndex == BACKSPACE_KEY_IDX) && (keypad->enableBackspace)) { // backspace
317  // only call callback if event is TOUCHED, otherwise play tune on touch event (and not on
318  // release)
319  if (eventType == TOUCHED) {
320  keypad->callback(BACKSPACE_KEY);
321  }
322  else {
323  io_seproxyhal_play_tune(TUNE_TAP_CASUAL);
324  }
325  }
326  else if ((firstIndex == VALIDATE_KEY_IDX) && (keypad->enableValidate)) { // validate
327  // only call callback if event is TOUCHED
328  if (eventType == TOUCHED) {
329  keypad->callback(VALIDATE_KEY);
330  }
331  }
332 }
333 
345 {
346  // if in first line
347  if ((index >= '1') && (index <= '3')) {
348  *x = kpd->obj.area.x0 + (index - '1') * KEY_WIDTH;
349  *y = kpd->obj.area.y0;
350  }
351  else if ((index >= '4') && (index <= '6')) {
352  *x = kpd->obj.area.x0 + (index - '4') * KEY_WIDTH;
353  *y = kpd->obj.area.y0 + KEYPAD_KEY_HEIGHT;
354  }
355  else if ((index >= '7') && (index <= '9')) {
356  *x = kpd->obj.area.x0 + (index - '7') * KEY_WIDTH;
357  *y = kpd->obj.area.y0 + (2 * KEYPAD_KEY_HEIGHT);
358  }
359  else if (index == BACKSPACE_KEY) { // backspace
360  *x = kpd->obj.area.x0;
361  *y = kpd->obj.area.y0 + (3 * KEYPAD_KEY_HEIGHT);
362  }
363  else if (index == '0') {
364  *x = kpd->obj.area.x0 + KEY_WIDTH;
365  *y = kpd->obj.area.y0 + (3 * KEYPAD_KEY_HEIGHT);
366  }
367  else if (index == VALIDATE_KEY) { // validate
368  *x = kpd->obj.area.x0 + (2 * KEY_WIDTH);
369  *y = kpd->obj.area.y0 + (3 * KEYPAD_KEY_HEIGHT);
370  }
371  else {
372  return false;
373  }
374  return true;
375 }
376 
384 {
385  kpd->obj.touchMask = (1 << TOUCHED) | (1 << TOUCH_PRESSED);
386  kpd->obj.touchId = KEYPAD_ID;
387 
388  // if the object has not been already used, prepare indexes of digits
389  if (kpd->digitIndexes[0] == 0) {
390  uint32_t i;
391  if (kpd->shuffled) {
392  uint8_t shuffledDigits[10] = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9};
393 
394  // modern version of the Fisher-Yates shuffle
395  for (i = 0; i < 9; i++) {
396  // pick a random number k in [i:9] intervale
397  uint32_t j = cx_rng_u32_range(i, 10);
398  uint8_t tmp = shuffledDigits[j];
399 
400  // exchange shuffledDigits[i] and shuffledDigits[j]
401  shuffledDigits[j] = shuffledDigits[i];
402  shuffledDigits[i] = tmp;
403  }
404  for (i = 0; i < 10; i++) {
405  // apply the permuted value to digit i
406  SET_DIGIT_INDEX(kpd, i, shuffledDigits[i]);
407  }
408  }
409  else {
410  // no shuffling
411  for (i = 0; i < 10; i++) {
412  SET_DIGIT_INDEX(kpd, i, i);
413  }
414  }
415  }
416  keypadDraw(kpd);
417 }
418 
419 #endif // HAVE_SE_TOUCH
420 #endif // NBGL_KEYPAD
Random Number Generation.
debug traces management
#define LOG_DEBUG(__logger,...)
Definition: nbgl_debug.h:86
@ MISC_LOGGER
Definition: nbgl_debug.h:39
Middle Level API of the new BOLOS Graphical Library.
nbgl_font_id_e nbgl_drawText(const nbgl_area_t *area, const char *text, uint16_t textLen, nbgl_font_id_e fontId, color_t fontColor)
This function draws the given single-line text, with the given parameters.
Definition: nbgl_draw.c:538
uint8_t nbgl_getCharWidth(nbgl_font_id_e fontId, const char *text)
return the width in pixels of the given UTF-8 character
Definition: nbgl_fonts.c:364
Font screen low-Level driver API, to draw elementary forms.
void nbgl_frontDrawImage(const nbgl_area_t *area, const uint8_t *buffer, nbgl_transformation_t transformation, nbgl_color_map_t colorMap)
void nbgl_frontDrawImageFile(const nbgl_area_t *area, const uint8_t *buffer, nbgl_color_map_t colorMap, const uint8_t *uzlib_chunk_buffer)
void nbgl_frontDrawHorizontalLine(const nbgl_area_t *area, uint8_t mask, color_t lineColor)
void nbgl_frontDrawRect(const nbgl_area_t *area)
API to draw all basic graphic objects.
#define BACKSPACE_ICON
Definition: nbgl_obj.h:132
struct PACKED__ nbgl_keypad_s nbgl_keypad_t
struct to represent a keypad (KEYPAD type)
#define BACKSPACE_KEY
Definition: nbgl_obj.h:26
#define VALIDATE_ICON
Definition: nbgl_obj.h:135
#define VALIDATE_KEY
Definition: nbgl_obj.h:27
@ KEYPAD_ID
Definition: nbgl_obj.h:556
#define RIGHT_ARROW_ICON
Definition: nbgl_obj.h:140
#define KEYPAD_KEY_HEIGHT
Definition: nbgl_obj.h:55
struct PACKED__ nbgl_obj_s nbgl_obj_t
Common structure for all graphical objects.
#define GET_DIGIT_INDEX(_keypad, _digit)
#define KEY_WIDTH
void nbgl_objDrawKeypad(nbgl_keypad_t *kpd)
This function draws a keypad object.
#define BACKSPACE_KEY_IDX
#define SET_DIGIT_INDEX(_keypad, _digit, _index)
void nbgl_keypadTouchCallback(nbgl_obj_t *obj, nbgl_touchType_t eventType)
function to be called when the keypad object is touched
#define VALIDATE_KEY_IDX
#define DIGIT_OFFSET_Y
bool nbgl_keypadGetPosition(nbgl_keypad_t *kpd, char index, uint16_t *x, uint16_t *y)
This function gets the position (top-left corner) of the key at the given index. (to be used for Test...
uint8_t ramBuffer[GZLIB_UNCOMPRESSED_CHUNK]
Definition: nbgl_obj.c:125
bool nbgl_touchGetTouchedPosition(nbgl_obj_t *obj, nbgl_touchStatePosition_t **firstPos, nbgl_touchStatePosition_t **lastPos)
Definition: nbgl_touch.c:388
@ WHITE
Definition: nbgl_types.h:105
@ BLACK
Definition: nbgl_types.h:102
nbgl_touchType_t
The different types of Touchscreen events.
Definition: nbgl_types.h:220
@ TOUCHED
Definition: nbgl_types.h:221
@ TOUCH_PRESSED
Definition: nbgl_types.h:228
struct PACKED__ nbgl_icon_details_s nbgl_icon_details_t
Represents all information about an icon.
#define GZLIB_UNCOMPRESSED_CHUNK
size of gzlib uncompression buffer in bytes
Definition: nbgl_types.h:266
#define NO_TRANSFORMATION
Definition: nbgl_types.h:55
@ NBGL_BPP_1
1 bit per pixel
Definition: nbgl_types.h:245
struct PACKED__ nbgl_area_s nbgl_area_t
Represents a rectangle area of the screen.
The low level Touchscreen event, coming from driver.
Definition: nbgl_obj.h:206
int16_t y
vertical position of the touch (or for a RELEASED the last touched point)
Definition: nbgl_obj.h:213
int16_t x
horizontal position of the touch (or for a RELEASED the last touched point)
Definition: nbgl_obj.h:212
unsigned short uint16_t
Definition: usbd_conf.h:54
unsigned char uint8_t
Definition: usbd_conf.h:53