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  // if enabled, draw icon in white on a black background
225  rectArea.backgroundColor = BLACK;
226  rectArea.x0 = keypad->obj.area.x0 + 2 * KEY_WIDTH;
227  rectArea.y0 = keypad->obj.area.y0 + KEYPAD_KEY_HEIGHT * 3;
228  rectArea.width = KEY_WIDTH;
229  rectArea.height = KEYPAD_KEY_HEIGHT;
230  nbgl_frontDrawRect(&rectArea);
231  rectArea.width = VALIDATE_ICON.width;
232  rectArea.height = VALIDATE_ICON.height;
233  rectArea.bpp = NBGL_BPP_1;
234  rectArea.x0 = keypad->obj.area.x0 + 2 * KEY_WIDTH + (KEY_WIDTH - rectArea.width) / 2;
235  rectArea.y0 = keypad->obj.area.y0 + KEYPAD_KEY_HEIGHT * 3
236  + (KEYPAD_KEY_HEIGHT - rectArea.height) / 2;
237  if (VALIDATE_ICON.isFile) {
239  }
240  else {
242  &rectArea, (uint8_t *) VALIDATE_ICON.bitmap, NO_TRANSFORMATION, WHITE);
243  }
244  }
245 }
246 
247 static void keypadDraw(nbgl_keypad_t *keypad)
248 {
249  if (!keypad->partial) {
250  // At first, draw grid
251  keypadDrawGrid(keypad);
252  }
253 
254  // then draw key content
255  keypadDrawDigits(keypad);
256  keypad->partial = false;
257 }
258 
259 /**********************
260  * GLOBAL FUNCTIONS
261  **********************/
262 
271 {
272  uint8_t firstIndex, lastIndex;
273  nbgl_touchStatePosition_t *firstPosition, *lastPosition;
274  nbgl_keypad_t *keypad = (nbgl_keypad_t *) obj;
275 
276  LOG_DEBUG(MISC_LOGGER, "nbgl_keypadTouchCallback(): eventType = %d\n", eventType);
277  if ((eventType != TOUCHED) && (eventType != TOUCH_PRESSED)) {
278  return;
279  }
280  if (nbgl_touchGetTouchedPosition(obj, &firstPosition, &lastPosition) == false) {
281  return;
282  }
283 
284  // use positions relative to keypad position
285  firstIndex = getKeypadIndex(firstPosition->x - obj->area.x0, firstPosition->y - obj->area.y0);
286  if (firstIndex > VALIDATE_KEY_IDX) {
287  return;
288  }
289  lastIndex = getKeypadIndex(lastPosition->x - obj->area.x0, lastPosition->y - obj->area.y0);
290  if (lastIndex > VALIDATE_KEY_IDX) {
291  return;
292  }
293 
294  // if position of finger has moved during press to another "key", drop it
295  if (lastIndex != firstIndex) {
296  return;
297  }
298 
299  if ((firstIndex < 10) && (keypad->enableDigits)) {
300  // only call callback if event is TOUCHED, otherwise play tune on touch event (and not on
301  // release)
302  if (eventType == TOUCHED) {
303  keypad->callback(GET_DIGIT_INDEX(keypad, firstIndex) + 0x30);
304  }
305  else {
306  io_seproxyhal_play_tune(TUNE_TAP_CASUAL);
307  }
308  }
309  if ((firstIndex == BACKSPACE_KEY_IDX) && (keypad->enableBackspace)) { // backspace
310  // only call callback if event is TOUCHED, otherwise play tune on touch event (and not on
311  // release)
312  if (eventType == TOUCHED) {
313  keypad->callback(BACKSPACE_KEY);
314  }
315  else {
316  io_seproxyhal_play_tune(TUNE_TAP_CASUAL);
317  }
318  }
319  else if ((firstIndex == VALIDATE_KEY_IDX) && (keypad->enableValidate)) { // validate
320  // only call callback if event is TOUCHED
321  if (eventType == TOUCHED) {
322  keypad->callback(VALIDATE_KEY);
323  }
324  }
325 }
326 
338 {
339  // if in first line
340  if ((index >= '1') && (index <= '3')) {
341  *x = kpd->obj.area.x0 + (index - '1') * KEY_WIDTH;
342  *y = kpd->obj.area.y0;
343  }
344  else if ((index >= '4') && (index <= '6')) {
345  *x = kpd->obj.area.x0 + (index - '4') * KEY_WIDTH;
346  *y = kpd->obj.area.y0 + KEYPAD_KEY_HEIGHT;
347  }
348  else if ((index >= '7') && (index <= '9')) {
349  *x = kpd->obj.area.x0 + (index - '7') * KEY_WIDTH;
350  *y = kpd->obj.area.y0 + (2 * KEYPAD_KEY_HEIGHT);
351  }
352  else if (index == BACKSPACE_KEY) { // backspace
353  *x = kpd->obj.area.x0;
354  *y = kpd->obj.area.y0 + (3 * KEYPAD_KEY_HEIGHT);
355  }
356  else if (index == '0') {
357  *x = kpd->obj.area.x0 + KEY_WIDTH;
358  *y = kpd->obj.area.y0 + (3 * KEYPAD_KEY_HEIGHT);
359  }
360  else if (index == VALIDATE_KEY) { // validate
361  *x = kpd->obj.area.x0 + (2 * KEY_WIDTH);
362  *y = kpd->obj.area.y0 + (3 * KEYPAD_KEY_HEIGHT);
363  }
364  else {
365  return false;
366  }
367  return true;
368 }
369 
377 {
378  kpd->obj.touchMask = (1 << TOUCHED) | (1 << TOUCH_PRESSED);
379  kpd->obj.touchId = KEYPAD_ID;
380 
381  // if the object has not been already used, prepare indexes of digits
382  if (kpd->digitIndexes[0] == 0) {
383  uint32_t i;
384  if (kpd->shuffled) {
385  uint8_t shuffledDigits[10] = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9};
386 
387  // modern version of the Fisher-Yates shuffle
388  for (i = 0; i < 9; i++) {
389  // pick a random number k in [i:9] intervale
390  uint32_t j = cx_rng_u32_range(i, 10);
391  uint8_t tmp = shuffledDigits[j];
392 
393  // exchange shuffledDigits[i] and shuffledDigits[j]
394  shuffledDigits[j] = shuffledDigits[i];
395  shuffledDigits[i] = tmp;
396  }
397  for (i = 0; i < 10; i++) {
398  // apply the permuted value to digit i
399  SET_DIGIT_INDEX(kpd, i, shuffledDigits[i]);
400  }
401  }
402  else {
403  // no shuffling
404  for (i = 0; i < 10; i++) {
405  SET_DIGIT_INDEX(kpd, i, i);
406  }
407  }
408  }
409  keypadDraw(kpd);
410 }
411 
412 #endif // HAVE_SE_TOUCH
413 #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:372
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:555
#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:124
bool nbgl_touchGetTouchedPosition(nbgl_obj_t *obj, nbgl_touchStatePosition_t **firstPos, nbgl_touchStatePosition_t **lastPos)
Definition: nbgl_touch.c:364
@ 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
#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