Embedded SDK
Embedded SDK
Loading...
Searching...
No Matches
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_seph_cmd.h"
22#include "os_io_seph_ux.h"
23#include "lcx_rng.h"
24
25/*********************
26 * DEFINES
27 *********************/
28#define KEY_WIDTH (SCREEN_WIDTH / 3)
29#if defined(TARGET_STAX)
30#define DIGIT_OFFSET_Y (((KEYPAD_KEY_HEIGHT - 48) / 2) & 0xFFC)
31#elif defined(TARGET_FLEX)
32#define DIGIT_OFFSET_Y (((KEYPAD_KEY_HEIGHT - 48) / 2) & 0xFFC)
33#elif defined(TARGET_APEX)
34#define DIGIT_OFFSET_Y (((KEYPAD_KEY_HEIGHT - 28) / 2) & 0xFFC)
35#endif // TARGETS
36
37#define BACKSPACE_KEY_IDX 10
38#define VALIDATE_KEY_IDX 11
39
40// to save RAM we use 5 uint8, and 4 bits per digit (MSBs for odd digits, LSBs for even ones)
41#define GET_DIGIT_INDEX(_keypad, _digit) \
42 ((_digit & 1) ? (_keypad->digitIndexes[_digit >> 1] >> 4) \
43 : (_keypad->digitIndexes[_digit >> 1] & 0xF))
44#define SET_DIGIT_INDEX(_keypad, _digit, _index) \
45 (_keypad->digitIndexes[_digit >> 1] |= (_digit & 1) ? (_index << 4) : _index)
46
48
49/**********************
50 * TYPEDEFS
51 **********************/
52
53/**********************
54 * STATIC VARIABLES
55 **********************/
56
57/**********************
58 * VARIABLES
59 **********************/
60
61/**********************
62 * STATIC FUNCTIONS
63 **********************/
64
65static uint8_t getKeypadIndex(uint16_t x, uint16_t y)
66{
67 uint8_t i = 0;
68 // get index of key pressed
69 if (y < KEYPAD_KEY_HEIGHT) {
70 // 1st line:
71 i = 1 + x / KEY_WIDTH;
72 }
73 else if (y < (2 * KEYPAD_KEY_HEIGHT)) {
74 // 2nd line:
75 i = 4 + x / KEY_WIDTH;
76 }
77 else if (y < (3 * KEYPAD_KEY_HEIGHT)) {
78 // 3rd line:
79 i = 7 + x / KEY_WIDTH;
80 }
81 else if (y < (4 * KEYPAD_KEY_HEIGHT)) {
82 // 4th line
83 if (x < KEY_WIDTH) {
85 }
86 else if (x < (2 * KEY_WIDTH)) {
87 i = 0;
88 }
89 else {
91 }
92 }
93 return i;
94}
95
96static void keypadDrawGrid(nbgl_keypad_t *keypad)
97{
98 nbgl_area_t rectArea;
99 uint8_t i;
100
101 // clean full area
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 = keypad->obj.area.height;
107 nbgl_frontDrawRect(&rectArea);
108
110 rectArea.backgroundColor = keypad->obj.area.backgroundColor;
111 rectArea.x0 = keypad->obj.area.x0;
112 rectArea.y0 = keypad->obj.area.y0;
113 rectArea.height = 1;
114 rectArea.width = keypad->obj.area.width;
115 for (i = 0; i < 4; i++) {
116#ifdef TARGET_APEX
117 // on Apex, draw 3 segments per line, for "intersections"
118 rectArea.width = KEY_WIDTH - 2;
119 nbgl_frontDrawLine(&rectArea, 2, LIGHT_GRAY);
120 rectArea.x0 += KEY_WIDTH + 1;
121 nbgl_frontDrawLine(&rectArea, 2, LIGHT_GRAY);
122 rectArea.x0 += KEY_WIDTH + 1;
123 nbgl_frontDrawLine(&rectArea, 2, LIGHT_GRAY);
124 rectArea.x0 = keypad->obj.area.x0;
125#else // TARGET_APEX
126 nbgl_frontDrawLine(&rectArea, 2, LIGHT_GRAY);
127#endif // TARGET_APEX
128 rectArea.y0 += KEYPAD_KEY_HEIGHT;
129 }
130
132 rectArea.x0 = keypad->obj.area.x0;
133 rectArea.y0 = keypad->obj.area.y0;
134 rectArea.width = 1;
135 rectArea.height = KEYPAD_KEY_HEIGHT * 4;
136#ifdef HAVE_SIDE_SCREEN
137 nbgl_frontDrawLine(&rectArea, 0, LIGHT_GRAY); // 1st full line, on the left
138#endif // HAVE_SIDE_SCREEN
139 rectArea.x0 += KEY_WIDTH;
140#ifdef TARGET_APEX
141 // on Apex, the first "column" is only 99px large
142 rectArea.x0--;
143#endif // TARGET_APEX
144 nbgl_frontDrawLine(&rectArea, 0, LIGHT_GRAY); // 2nd line
145 rectArea.x0 = keypad->obj.area.x0 + 2 * KEY_WIDTH;
146 nbgl_frontDrawLine(&rectArea, 0, LIGHT_GRAY); // 3rd line
147}
148
149static void keypadDrawDigits(nbgl_keypad_t *keypad)
150{
151 uint8_t i;
152 nbgl_area_t rectArea;
153 char key_value;
154
155 rectArea.backgroundColor = keypad->obj.area.backgroundColor;
156 // only draw digits if not a partial refresh
157 if (keypad->digitsChanged) {
158 rectArea.y0 = keypad->obj.area.y0 + DIGIT_OFFSET_Y;
159
160 // First row of keys: 1 2 3
161 for (i = 0; i < 3; i++) {
162 key_value = GET_DIGIT_INDEX(keypad, (i + 1)) + 0x30;
163
164 rectArea.x0 = keypad->obj.area.x0 + i * KEY_WIDTH;
165 rectArea.x0 += (KEY_WIDTH - nbgl_getCharWidth(LARGE_MEDIUM_FONT, &key_value)) / 2;
167 &rectArea, &key_value, 1, LARGE_MEDIUM_FONT, keypad->enableDigits ? BLACK : WHITE);
168 }
169 // Second row: 4 5 6
170 rectArea.y0 += KEYPAD_KEY_HEIGHT;
171 for (; i < 6; i++) {
172 key_value = GET_DIGIT_INDEX(keypad, (i + 1)) + 0x30;
173 rectArea.x0 = keypad->obj.area.x0 + (i - 3) * KEY_WIDTH;
174 rectArea.x0 += (KEY_WIDTH - nbgl_getCharWidth(LARGE_MEDIUM_FONT, &key_value)) / 2;
176 &rectArea, &key_value, 1, LARGE_MEDIUM_FONT, keypad->enableDigits ? BLACK : WHITE);
177 }
178 // Third row: 7 8 9
179 rectArea.y0 += KEYPAD_KEY_HEIGHT;
180 for (; i < 9; i++) {
181 key_value = GET_DIGIT_INDEX(keypad, (i + 1)) + 0x30;
182 rectArea.x0 = keypad->obj.area.x0 + (i - 6) * KEY_WIDTH;
183 rectArea.x0 += (KEY_WIDTH - nbgl_getCharWidth(LARGE_MEDIUM_FONT, &key_value)) / 2;
185 &rectArea, &key_value, 1, LARGE_MEDIUM_FONT, keypad->enableDigits ? BLACK : WHITE);
186 }
187 }
188 // 4th raw, Backspace, 0 and Validate
189 // draw backspace
190 rectArea.width = BACKSPACE_ICON.width;
191 rectArea.height = BACKSPACE_ICON.height;
192 rectArea.bpp = NBGL_BPP_1;
193 rectArea.x0 = keypad->obj.area.x0 + (KEY_WIDTH - rectArea.width) / 2;
194 rectArea.y0
195 = keypad->obj.area.y0 + KEYPAD_KEY_HEIGHT * 3 + (KEYPAD_KEY_HEIGHT - rectArea.height) / 2;
196 if (BACKSPACE_ICON.isFile) {
197 nbgl_frontDrawImageFile(&rectArea,
198 (uint8_t *) BACKSPACE_ICON.bitmap,
199 keypad->enableBackspace ? BLACK : WHITE,
200 ramBuffer);
201 }
202 else {
203 nbgl_frontDrawImage(&rectArea,
204 (uint8_t *) BACKSPACE_ICON.bitmap,
206 keypad->enableBackspace ? BLACK : WHITE);
207 }
208
209 // only draw '0' if not a partial refresh
210 if (keypad->digitsChanged) {
211 // draw 0
212 key_value = GET_DIGIT_INDEX(keypad, 0) + 0x30;
213 rectArea.x0 = keypad->obj.area.x0 + KEY_WIDTH;
214 rectArea.x0 += (KEY_WIDTH - nbgl_getCharWidth(LARGE_MEDIUM_FONT, &key_value)) / 2;
215 rectArea.y0 = keypad->obj.area.y0 + KEYPAD_KEY_HEIGHT * 3 + DIGIT_OFFSET_Y;
217 &rectArea, &key_value, 1, LARGE_MEDIUM_FONT, keypad->enableDigits ? BLACK : WHITE);
218 }
219
220 // draw white background if validate not enabled
221 if (!keypad->enableValidate && keypad->validateChanged) {
222 uint8_t dotStartIdx = 0;
223
224 rectArea.width = KEY_WIDTH;
225 rectArea.height = KEYPAD_KEY_HEIGHT;
226 rectArea.bpp = NBGL_BPP_1;
227 rectArea.x0 = keypad->obj.area.x0 + 2 * KEY_WIDTH;
228 rectArea.y0 = keypad->obj.area.y0 + KEYPAD_KEY_HEIGHT * 3;
229 rectArea.backgroundColor = WHITE;
230 nbgl_frontDrawRect(&rectArea);
232 rectArea.backgroundColor = keypad->obj.area.backgroundColor;
233 rectArea.x0 = keypad->obj.area.x0 + 2 * KEY_WIDTH;
234 rectArea.y0 = keypad->obj.area.y0 + KEYPAD_KEY_HEIGHT * 3;
235 rectArea.width = KEY_WIDTH;
236 rectArea.height = 1;
237 nbgl_frontDrawLine(&rectArea, 0, LIGHT_GRAY);
239 rectArea.x0 = keypad->obj.area.x0 + 2 * KEY_WIDTH;
240 rectArea.y0 = keypad->obj.area.y0 + KEYPAD_KEY_HEIGHT * 3;
241 rectArea.width = 1;
242 rectArea.height = KEYPAD_KEY_HEIGHT;
243#ifdef TARGET_APEX
244 // draw the small erased vertical ... on top-left corner
245 rectArea.y0 -= 4;
246 rectArea.height += 4;
247 dotStartIdx = 1;
248#endif // TARGET_APEX
249 nbgl_frontDrawLine(&rectArea, dotStartIdx, LIGHT_GRAY);
250 }
251 else if (keypad->enableValidate && (keypad->digitsChanged || keypad->validateChanged)) {
252 const nbgl_icon_details_t *icon;
253
254 if (keypad->softValidation) {
255 icon = &RIGHT_ARROW_ICON;
256 }
257 else {
258 icon = &VALIDATE_ICON;
259 }
260 // if enabled, draw icon in white on a black background
261 rectArea.backgroundColor = BLACK;
262 rectArea.x0 = keypad->obj.area.x0 + 2 * KEY_WIDTH;
263 rectArea.y0 = keypad->obj.area.y0 + KEYPAD_KEY_HEIGHT * 3;
264 rectArea.width = KEY_WIDTH;
265 rectArea.height = KEYPAD_KEY_HEIGHT;
266 nbgl_frontDrawRect(&rectArea);
267 rectArea.width = icon->width;
268 rectArea.height = icon->height;
269 rectArea.bpp = NBGL_BPP_1;
270 rectArea.x0 = keypad->obj.area.x0 + 2 * KEY_WIDTH + (KEY_WIDTH - rectArea.width) / 2;
271 rectArea.y0 = keypad->obj.area.y0 + KEYPAD_KEY_HEIGHT * 3
272 + (KEYPAD_KEY_HEIGHT - rectArea.height) / 2;
273 if (icon->isFile) {
274 nbgl_frontDrawImageFile(&rectArea, (uint8_t *) icon->bitmap, WHITE, ramBuffer);
275 }
276 else {
277 nbgl_frontDrawImage(&rectArea, (uint8_t *) icon->bitmap, NO_TRANSFORMATION, WHITE);
278 }
279#ifdef TARGET_APEX
280 // draw the small erased vertical ... on top-left corner
281 ramBuffer[0] = 0x4F;
282 rectArea.backgroundColor = WHITE;
283 rectArea.x0 = keypad->obj.area.x0 + 2 * KEY_WIDTH;
284 rectArea.y0 = keypad->obj.area.y0 + KEYPAD_KEY_HEIGHT * 3 - 4;
285 rectArea.width = 1;
286 rectArea.height = 8;
287 nbgl_frontDrawImage(&rectArea, (uint8_t *) ramBuffer, NO_TRANSFORMATION, BLACK);
288#endif // TARGET_APEX
289 }
290}
291
292static void keypadDraw(nbgl_keypad_t *keypad)
293{
294 if (keypad->digitsChanged) {
295 // At first, draw grid
296 keypadDrawGrid(keypad);
297 }
298
299 // then draw key content
300 keypadDrawDigits(keypad);
301 keypad->digitsChanged = false;
302}
303
304/**********************
305 * GLOBAL FUNCTIONS
306 **********************/
307
316{
317 uint8_t firstIndex, lastIndex;
318 nbgl_touchStatePosition_t *firstPosition, *lastPosition;
319 nbgl_keypad_t *keypad = (nbgl_keypad_t *) obj;
320
321 LOG_DEBUG(MISC_LOGGER, "nbgl_keypadTouchCallback(): eventType = %d\n", eventType);
322 if ((eventType != TOUCHED) && (eventType != TOUCH_PRESSED)) {
323 return;
324 }
325 if (nbgl_touchGetTouchedPosition(obj, &firstPosition, &lastPosition) == false) {
326 return;
327 }
328
329 // use positions relative to keypad position
330 firstIndex = getKeypadIndex(firstPosition->x - obj->area.x0, firstPosition->y - obj->area.y0);
331 if (firstIndex > VALIDATE_KEY_IDX) {
332 return;
333 }
334 lastIndex = getKeypadIndex(lastPosition->x - obj->area.x0, lastPosition->y - obj->area.y0);
335 if (lastIndex > VALIDATE_KEY_IDX) {
336 return;
337 }
338
339 // if position of finger has moved during press to another "key", drop it
340 if (lastIndex != firstIndex) {
341 return;
342 }
343
344 if ((firstIndex < 10) && (keypad->enableDigits)) {
345 // only call callback if event is TOUCHED, otherwise play tune on touch event (and not on
346 // release)
347 if (eventType == TOUCHED) {
348 keypad->callback(GET_DIGIT_INDEX(keypad, firstIndex) + 0x30);
349 }
350 else {
351 os_io_seph_cmd_piezo_play_tune(TUNE_TAP_CASUAL);
352 }
353 }
354 if ((firstIndex == BACKSPACE_KEY_IDX) && (keypad->enableBackspace)) { // backspace
355 // only call callback if event is TOUCHED, otherwise play tune on touch event (and not on
356 // release)
357 if (eventType == TOUCHED) {
358 keypad->callback(BACKSPACE_KEY);
359 }
360 else {
361 os_io_seph_cmd_piezo_play_tune(TUNE_TAP_CASUAL);
362 }
363 }
364 else if ((firstIndex == VALIDATE_KEY_IDX) && (keypad->enableValidate)) { // validate
365 // only call callback if event is TOUCHED
366 if (eventType == TOUCHED) {
367 keypad->callback(VALIDATE_KEY);
368 }
369 }
370}
371
382bool nbgl_keypadGetPosition(nbgl_keypad_t *kpd, char index, uint16_t *x, uint16_t *y)
383{
384 // if in first line
385 if ((index >= '1') && (index <= '3')) {
386 *x = kpd->obj.area.x0 + (index - '1') * KEY_WIDTH;
387 *y = kpd->obj.area.y0;
388 }
389 else if ((index >= '4') && (index <= '6')) {
390 *x = kpd->obj.area.x0 + (index - '4') * KEY_WIDTH;
391 *y = kpd->obj.area.y0 + KEYPAD_KEY_HEIGHT;
392 }
393 else if ((index >= '7') && (index <= '9')) {
394 *x = kpd->obj.area.x0 + (index - '7') * KEY_WIDTH;
395 *y = kpd->obj.area.y0 + (2 * KEYPAD_KEY_HEIGHT);
396 }
397 else if (index == BACKSPACE_KEY) { // backspace
398 *x = kpd->obj.area.x0;
399 *y = kpd->obj.area.y0 + (3 * KEYPAD_KEY_HEIGHT);
400 }
401 else if (index == '0') {
402 *x = kpd->obj.area.x0 + KEY_WIDTH;
403 *y = kpd->obj.area.y0 + (3 * KEYPAD_KEY_HEIGHT);
404 }
405 else if (index == VALIDATE_KEY) { // validate
406 *x = kpd->obj.area.x0 + (2 * KEY_WIDTH);
407 *y = kpd->obj.area.y0 + (3 * KEYPAD_KEY_HEIGHT);
408 }
409 else {
410 return false;
411 }
412 return true;
413}
414
422{
423 kpd->obj.touchMask = (1 << TOUCHED) | (1 << TOUCH_PRESSED);
424 kpd->obj.touchId = KEYPAD_ID;
425
426 // if the object has not been already used, prepare indexes of digits
427 if (kpd->digitIndexes[0] == 0) {
428 uint32_t i;
429 if (kpd->shuffled) {
430 uint8_t shuffledDigits[10] = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9};
431
432 // modern version of the Fisher-Yates shuffle
433 for (i = 0; i < 9; i++) {
434 // pick a random number k in [i:9] intervale
435 uint32_t j = cx_rng_u32_range(i, 10);
436 uint8_t tmp = shuffledDigits[j];
437
438 // exchange shuffledDigits[i] and shuffledDigits[j]
439 shuffledDigits[j] = shuffledDigits[i];
440 shuffledDigits[i] = tmp;
441 }
442 for (i = 0; i < 10; i++) {
443 // apply the permuted value to digit i
444 SET_DIGIT_INDEX(kpd, i, shuffledDigits[i]);
445 }
446 }
447 else {
448 // no shuffling
449 for (i = 0; i < 10; i++) {
450 SET_DIGIT_INDEX(kpd, i, i);
451 }
452 }
453 }
454 keypadDraw(kpd);
455}
456
457#endif // HAVE_SE_TOUCH
458#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:584
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:366
Font screen low-Level driver API, to draw elementary forms.
void nbgl_frontDrawLine(const nbgl_area_t *area, uint8_t dotStartIdx, color_t lineColor)
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_frontDrawRect(const nbgl_area_t *area)
API to draw all basic graphic objects.
struct PACKED__ nbgl_keypad_s nbgl_keypad_t
struct to represent a keypad (KEYPAD type)
#define BACKSPACE_KEY
Definition nbgl_obj.h:26
@ KEYPAD_ID
Definition nbgl_obj.h:686
#define VALIDATE_KEY
Definition nbgl_obj.h:27
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
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:146
bool nbgl_touchGetTouchedPosition(nbgl_obj_t *obj, nbgl_touchStatePosition_t **firstPos, nbgl_touchStatePosition_t **lastPos)
Definition nbgl_touch.c:389
@ WHITE
Definition nbgl_types.h:144
@ LIGHT_GRAY
Definition nbgl_types.h:143
@ BLACK
Definition nbgl_types.h:141
nbgl_touchType_t
The different types of Touchscreen events.
Definition nbgl_types.h:259
@ TOUCHED
Definition nbgl_types.h:260
@ TOUCH_PRESSED
Definition nbgl_types.h:267
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:305
#define NO_TRANSFORMATION
Definition nbgl_types.h:91
@ NBGL_BPP_1
1 bit per pixel
Definition nbgl_types.h:284
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:331
int16_t y
vertical position of the touch (or for a RELEASED the last touched point)
Definition nbgl_obj.h:338
int16_t x
horizontal position of the touch (or for a RELEASED the last touched point)
Definition nbgl_obj.h:337