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
100 // clean full area
101 rectArea.backgroundColor = keypad->obj.area.backgroundColor;
102 rectArea.x0 = keypad->obj.area.x0;
103 rectArea.y0 = keypad->obj.area.y0;
104 rectArea.width = keypad->obj.area.width;
105 rectArea.height = keypad->obj.area.height;
106 nbgl_frontDrawRect(&rectArea);
107
109 rectArea.backgroundColor = keypad->obj.area.backgroundColor;
110 rectArea.x0 = keypad->obj.area.x0;
111 rectArea.y0 = keypad->obj.area.y0;
112 rectArea.width = keypad->obj.area.width;
113 rectArea.height = 1;
114 nbgl_frontDrawLine(&rectArea, 1, LIGHT_GRAY); // 1st line (top)
115 rectArea.y0 += KEYPAD_KEY_HEIGHT;
116 nbgl_frontDrawLine(&rectArea, 1, LIGHT_GRAY); // 2nd line
117 rectArea.y0 += KEYPAD_KEY_HEIGHT;
118 nbgl_frontDrawLine(&rectArea, 1, LIGHT_GRAY); // 3rd line
119 rectArea.y0 += KEYPAD_KEY_HEIGHT;
120 nbgl_frontDrawLine(&rectArea, 1, LIGHT_GRAY); // 4th line
121
123 rectArea.x0 = keypad->obj.area.x0;
124 rectArea.y0 = keypad->obj.area.y0;
125 rectArea.width = 1;
126 rectArea.height = KEYPAD_KEY_HEIGHT * 4;
127#ifdef HAVE_SIDE_SCREEN
128 nbgl_frontDrawLine(&rectArea, 0, LIGHT_GRAY); // 1st full line, on the left
129#endif // HAVE_SIDE_SCREEN
130 rectArea.x0 += KEY_WIDTH;
131 nbgl_frontDrawLine(&rectArea, 0, LIGHT_GRAY); // 2nd line
132 rectArea.x0 += KEY_WIDTH;
133 nbgl_frontDrawLine(&rectArea, 0, LIGHT_GRAY); // 3rd line
134}
135
136static void keypadDrawDigits(nbgl_keypad_t *keypad)
137{
138 uint8_t i;
139 nbgl_area_t rectArea;
140 char key_value;
141
142 rectArea.backgroundColor = keypad->obj.area.backgroundColor;
143 // only draw digits if not a partial refresh
144 if (keypad->digitsChanged) {
145 rectArea.y0 = keypad->obj.area.y0 + DIGIT_OFFSET_Y;
146
147 // First row of keys: 1 2 3
148 for (i = 0; i < 3; i++) {
149 key_value = GET_DIGIT_INDEX(keypad, (i + 1)) + 0x30;
150
151 rectArea.x0 = keypad->obj.area.x0 + i * KEY_WIDTH;
152 rectArea.x0 += (KEY_WIDTH - nbgl_getCharWidth(LARGE_MEDIUM_FONT, &key_value)) / 2;
154 &rectArea, &key_value, 1, LARGE_MEDIUM_FONT, keypad->enableDigits ? BLACK : WHITE);
155 }
156 // Second row: 4 5 6
157 rectArea.y0 += KEYPAD_KEY_HEIGHT;
158 for (; i < 6; i++) {
159 key_value = GET_DIGIT_INDEX(keypad, (i + 1)) + 0x30;
160 rectArea.x0 = keypad->obj.area.x0 + (i - 3) * KEY_WIDTH;
161 rectArea.x0 += (KEY_WIDTH - nbgl_getCharWidth(LARGE_MEDIUM_FONT, &key_value)) / 2;
163 &rectArea, &key_value, 1, LARGE_MEDIUM_FONT, keypad->enableDigits ? BLACK : WHITE);
164 }
165 // Third row: 7 8 9
166 rectArea.y0 += KEYPAD_KEY_HEIGHT;
167 for (; i < 9; i++) {
168 key_value = GET_DIGIT_INDEX(keypad, (i + 1)) + 0x30;
169 rectArea.x0 = keypad->obj.area.x0 + (i - 6) * KEY_WIDTH;
170 rectArea.x0 += (KEY_WIDTH - nbgl_getCharWidth(LARGE_MEDIUM_FONT, &key_value)) / 2;
172 &rectArea, &key_value, 1, LARGE_MEDIUM_FONT, keypad->enableDigits ? BLACK : WHITE);
173 }
174 }
175 // 4th raw, Backspace, 0 and Validate
176 // draw backspace
177 rectArea.width = BACKSPACE_ICON.width;
178 rectArea.height = BACKSPACE_ICON.height;
179 rectArea.bpp = NBGL_BPP_1;
180 rectArea.x0 = keypad->obj.area.x0 + (KEY_WIDTH - rectArea.width) / 2;
181 rectArea.y0
182 = keypad->obj.area.y0 + KEYPAD_KEY_HEIGHT * 3 + (KEYPAD_KEY_HEIGHT - rectArea.height) / 2;
183 if (BACKSPACE_ICON.isFile) {
184 nbgl_frontDrawImageFile(&rectArea,
185 (uint8_t *) BACKSPACE_ICON.bitmap,
186 keypad->enableBackspace ? BLACK : WHITE,
187 ramBuffer);
188 }
189 else {
190 nbgl_frontDrawImage(&rectArea,
191 (uint8_t *) BACKSPACE_ICON.bitmap,
193 keypad->enableBackspace ? BLACK : WHITE);
194 }
195
196 // only draw '0' if not a partial refresh
197 if (keypad->digitsChanged) {
198 // draw 0
199 key_value = GET_DIGIT_INDEX(keypad, 0) + 0x30;
200 rectArea.x0 = keypad->obj.area.x0 + KEY_WIDTH;
201 rectArea.x0 += (KEY_WIDTH - nbgl_getCharWidth(LARGE_MEDIUM_FONT, &key_value)) / 2;
202 rectArea.y0 = keypad->obj.area.y0 + KEYPAD_KEY_HEIGHT * 3 + DIGIT_OFFSET_Y;
204 &rectArea, &key_value, 1, LARGE_MEDIUM_FONT, keypad->enableDigits ? BLACK : WHITE);
205 }
206
207 // draw white background if validate not enabled
208 if (!keypad->enableValidate && keypad->validateChanged) {
209 uint8_t dotStartIdx = 0;
210
211 rectArea.width = KEY_WIDTH;
212 rectArea.height = KEYPAD_KEY_HEIGHT;
213 rectArea.bpp = NBGL_BPP_1;
214 rectArea.x0 = keypad->obj.area.x0 + 2 * KEY_WIDTH;
215 rectArea.y0 = keypad->obj.area.y0 + KEYPAD_KEY_HEIGHT * 3;
216 rectArea.backgroundColor = WHITE;
217 nbgl_frontDrawRect(&rectArea);
219 rectArea.backgroundColor = keypad->obj.area.backgroundColor;
220 rectArea.x0 = keypad->obj.area.x0 + 2 * KEY_WIDTH;
221 rectArea.y0 = keypad->obj.area.y0 + KEYPAD_KEY_HEIGHT * 3;
222 rectArea.width = KEY_WIDTH;
223 rectArea.height = 1;
224 nbgl_frontDrawLine(&rectArea, 0, LIGHT_GRAY);
226 rectArea.x0 = keypad->obj.area.x0 + 2 * KEY_WIDTH;
227 rectArea.y0 = keypad->obj.area.y0 + KEYPAD_KEY_HEIGHT * 3;
228 rectArea.width = 1;
229 rectArea.height = KEYPAD_KEY_HEIGHT;
230#ifdef TARGET_APEX
231 // draw the small erased vertical ... on top-left corner
232 rectArea.y0 -= 4;
233 rectArea.height += 4;
234 dotStartIdx = 1;
235#endif // TARGET_APEX
236 nbgl_frontDrawLine(&rectArea, dotStartIdx, LIGHT_GRAY);
237 }
238 else if (keypad->validateChanged) {
239 const nbgl_icon_details_t *icon;
240
241 if (keypad->softValidation) {
242 icon = &RIGHT_ARROW_ICON;
243 }
244 else {
245 icon = &VALIDATE_ICON;
246 }
247 // if enabled, draw icon in white on a black background
248 rectArea.backgroundColor = BLACK;
249 rectArea.x0 = keypad->obj.area.x0 + 2 * KEY_WIDTH;
250 rectArea.y0 = keypad->obj.area.y0 + KEYPAD_KEY_HEIGHT * 3;
251 rectArea.width = KEY_WIDTH;
252 rectArea.height = KEYPAD_KEY_HEIGHT;
253 nbgl_frontDrawRect(&rectArea);
254 rectArea.width = icon->width;
255 rectArea.height = icon->height;
256 rectArea.bpp = NBGL_BPP_1;
257 rectArea.x0 = keypad->obj.area.x0 + 2 * KEY_WIDTH + (KEY_WIDTH - rectArea.width) / 2;
258 rectArea.y0 = keypad->obj.area.y0 + KEYPAD_KEY_HEIGHT * 3
259 + (KEYPAD_KEY_HEIGHT - rectArea.height) / 2;
260 if (icon->isFile) {
261 nbgl_frontDrawImageFile(&rectArea, (uint8_t *) icon->bitmap, WHITE, ramBuffer);
262 }
263 else {
264 nbgl_frontDrawImage(&rectArea, (uint8_t *) icon->bitmap, NO_TRANSFORMATION, WHITE);
265 }
266#ifdef TARGET_APEX
267 // draw the small erased vertical ... on top-left corner
268 ramBuffer[0] = 0x4F;
269 rectArea.backgroundColor = WHITE;
270 rectArea.x0 = keypad->obj.area.x0 + 2 * KEY_WIDTH;
271 rectArea.y0 = keypad->obj.area.y0 + KEYPAD_KEY_HEIGHT * 3 - 4;
272 rectArea.width = 1;
273 rectArea.height = 8;
274 nbgl_frontDrawImage(&rectArea, (uint8_t *) ramBuffer, NO_TRANSFORMATION, BLACK);
275#endif // TARGET_APEX
276 }
277}
278
279static void keypadDraw(nbgl_keypad_t *keypad)
280{
281 if (keypad->digitsChanged) {
282 // At first, draw grid
283 keypadDrawGrid(keypad);
284 }
285
286 // then draw key content
287 keypadDrawDigits(keypad);
288 keypad->digitsChanged = false;
289}
290
291/**********************
292 * GLOBAL FUNCTIONS
293 **********************/
294
303{
304 uint8_t firstIndex, lastIndex;
305 nbgl_touchStatePosition_t *firstPosition, *lastPosition;
306 nbgl_keypad_t *keypad = (nbgl_keypad_t *) obj;
307
308 LOG_DEBUG(MISC_LOGGER, "nbgl_keypadTouchCallback(): eventType = %d\n", eventType);
309 if ((eventType != TOUCHED) && (eventType != TOUCH_PRESSED)) {
310 return;
311 }
312 if (nbgl_touchGetTouchedPosition(obj, &firstPosition, &lastPosition) == false) {
313 return;
314 }
315
316 // use positions relative to keypad position
317 firstIndex = getKeypadIndex(firstPosition->x - obj->area.x0, firstPosition->y - obj->area.y0);
318 if (firstIndex > VALIDATE_KEY_IDX) {
319 return;
320 }
321 lastIndex = getKeypadIndex(lastPosition->x - obj->area.x0, lastPosition->y - obj->area.y0);
322 if (lastIndex > VALIDATE_KEY_IDX) {
323 return;
324 }
325
326 // if position of finger has moved during press to another "key", drop it
327 if (lastIndex != firstIndex) {
328 return;
329 }
330
331 if ((firstIndex < 10) && (keypad->enableDigits)) {
332 // only call callback if event is TOUCHED, otherwise play tune on touch event (and not on
333 // release)
334 if (eventType == TOUCHED) {
335 keypad->callback(GET_DIGIT_INDEX(keypad, firstIndex) + 0x30);
336 }
337 else {
338 os_io_seph_cmd_piezo_play_tune(TUNE_TAP_CASUAL);
339 }
340 }
341 if ((firstIndex == BACKSPACE_KEY_IDX) && (keypad->enableBackspace)) { // backspace
342 // only call callback if event is TOUCHED, otherwise play tune on touch event (and not on
343 // release)
344 if (eventType == TOUCHED) {
345 keypad->callback(BACKSPACE_KEY);
346 }
347 else {
348 os_io_seph_cmd_piezo_play_tune(TUNE_TAP_CASUAL);
349 }
350 }
351 else if ((firstIndex == VALIDATE_KEY_IDX) && (keypad->enableValidate)) { // validate
352 // only call callback if event is TOUCHED
353 if (eventType == TOUCHED) {
354 keypad->callback(VALIDATE_KEY);
355 }
356 }
357}
358
369bool nbgl_keypadGetPosition(nbgl_keypad_t *kpd, char index, uint16_t *x, uint16_t *y)
370{
371 // if in first line
372 if ((index >= '1') && (index <= '3')) {
373 *x = kpd->obj.area.x0 + (index - '1') * KEY_WIDTH;
374 *y = kpd->obj.area.y0;
375 }
376 else if ((index >= '4') && (index <= '6')) {
377 *x = kpd->obj.area.x0 + (index - '4') * KEY_WIDTH;
378 *y = kpd->obj.area.y0 + KEYPAD_KEY_HEIGHT;
379 }
380 else if ((index >= '7') && (index <= '9')) {
381 *x = kpd->obj.area.x0 + (index - '7') * KEY_WIDTH;
382 *y = kpd->obj.area.y0 + (2 * KEYPAD_KEY_HEIGHT);
383 }
384 else if (index == BACKSPACE_KEY) { // backspace
385 *x = kpd->obj.area.x0;
386 *y = kpd->obj.area.y0 + (3 * KEYPAD_KEY_HEIGHT);
387 }
388 else if (index == '0') {
389 *x = kpd->obj.area.x0 + KEY_WIDTH;
390 *y = kpd->obj.area.y0 + (3 * KEYPAD_KEY_HEIGHT);
391 }
392 else if (index == VALIDATE_KEY) { // validate
393 *x = kpd->obj.area.x0 + (2 * KEY_WIDTH);
394 *y = kpd->obj.area.y0 + (3 * KEYPAD_KEY_HEIGHT);
395 }
396 else {
397 return false;
398 }
399 return true;
400}
401
409{
410 kpd->obj.touchMask = (1 << TOUCHED) | (1 << TOUCH_PRESSED);
411 kpd->obj.touchId = KEYPAD_ID;
412
413 // if the object has not been already used, prepare indexes of digits
414 if (kpd->digitIndexes[0] == 0) {
415 uint32_t i;
416 if (kpd->shuffled) {
417 uint8_t shuffledDigits[10] = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9};
418
419 // modern version of the Fisher-Yates shuffle
420 for (i = 0; i < 9; i++) {
421 // pick a random number k in [i:9] intervale
422 uint32_t j = cx_rng_u32_range(i, 10);
423 uint8_t tmp = shuffledDigits[j];
424
425 // exchange shuffledDigits[i] and shuffledDigits[j]
426 shuffledDigits[j] = shuffledDigits[i];
427 shuffledDigits[i] = tmp;
428 }
429 for (i = 0; i < 10; i++) {
430 // apply the permuted value to digit i
431 SET_DIGIT_INDEX(kpd, i, shuffledDigits[i]);
432 }
433 }
434 else {
435 // no shuffling
436 for (i = 0; i < 10; i++) {
437 SET_DIGIT_INDEX(kpd, i, i);
438 }
439 }
440 }
441 keypadDraw(kpd);
442}
443
444#endif // HAVE_SE_TOUCH
445#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:580
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