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#endif // TARGETS
34
35#define BACKSPACE_KEY_IDX 10
36#define VALIDATE_KEY_IDX 11
37
38// to save RAM we use 5 uint8, and 4 bits per digit (MSBs for odd digits, LSBs for even ones)
39#define GET_DIGIT_INDEX(_keypad, _digit) \
40 ((_digit & 1) ? (_keypad->digitIndexes[_digit >> 1] >> 4) \
41 : (_keypad->digitIndexes[_digit >> 1] & 0xF))
42#define SET_DIGIT_INDEX(_keypad, _digit, _index) \
43 (_keypad->digitIndexes[_digit >> 1] |= (_digit & 1) ? (_index << 4) : _index)
44
46
47/**********************
48 * TYPEDEFS
49 **********************/
50
51/**********************
52 * STATIC VARIABLES
53 **********************/
54
55/**********************
56 * VARIABLES
57 **********************/
58
59/**********************
60 * STATIC FUNCTIONS
61 **********************/
62
63static uint8_t getKeypadIndex(uint16_t x, uint16_t y)
64{
65 uint8_t i = 0;
66 // get index of key pressed
67 if (y < KEYPAD_KEY_HEIGHT) {
68 // 1st line:
69 i = 1 + x / KEY_WIDTH;
70 }
71 else if (y < (2 * KEYPAD_KEY_HEIGHT)) {
72 // 2nd line:
73 i = 4 + x / KEY_WIDTH;
74 }
75 else if (y < (3 * KEYPAD_KEY_HEIGHT)) {
76 // 3rd line:
77 i = 7 + x / KEY_WIDTH;
78 }
79 else if (y < (4 * KEYPAD_KEY_HEIGHT)) {
80 // 4th line
81 if (x < KEY_WIDTH) {
83 }
84 else if (x < (2 * KEY_WIDTH)) {
85 i = 0;
86 }
87 else {
89 }
90 }
91 return i;
92}
93
94static void keypadDrawGrid(nbgl_keypad_t *keypad)
95{
96 nbgl_area_t rectArea;
97
98 // clean full area
99 rectArea.backgroundColor = keypad->obj.area.backgroundColor;
100 rectArea.x0 = keypad->obj.area.x0;
101 rectArea.y0 = keypad->obj.area.y0;
102 rectArea.width = keypad->obj.area.width;
103 rectArea.height = keypad->obj.area.height;
104 nbgl_frontDrawRect(&rectArea);
105
107 rectArea.backgroundColor = keypad->obj.area.backgroundColor;
108 rectArea.x0 = keypad->obj.area.x0;
109 rectArea.y0 = keypad->obj.area.y0;
110 rectArea.width = keypad->obj.area.width;
111 rectArea.height = VERTICAL_ALIGNMENT;
112 nbgl_frontDrawHorizontalLine(&rectArea, 0x1, keypad->borderColor); // 1st line (top)
113 rectArea.y0 += KEYPAD_KEY_HEIGHT;
114 nbgl_frontDrawHorizontalLine(&rectArea, 0x1, keypad->borderColor); // 2nd line
115 rectArea.y0 += KEYPAD_KEY_HEIGHT;
116 nbgl_frontDrawHorizontalLine(&rectArea, 0x1, keypad->borderColor); // 3rd line
117 rectArea.y0 += KEYPAD_KEY_HEIGHT;
118 nbgl_frontDrawHorizontalLine(&rectArea, 0x1, keypad->borderColor); // 4th line
119
121 rectArea.backgroundColor = keypad->borderColor;
122 rectArea.x0 = keypad->obj.area.x0;
123 rectArea.y0 = keypad->obj.area.y0;
124 rectArea.width = 1;
125 rectArea.height = KEYPAD_KEY_HEIGHT * 4;
126 nbgl_frontDrawRect(&rectArea); // 1st full line, on the left
127 rectArea.x0 += KEY_WIDTH;
128 nbgl_frontDrawRect(&rectArea); // 2nd line
129 rectArea.x0 += KEY_WIDTH;
130 nbgl_frontDrawRect(&rectArea); // 3rd line
131}
132
133static void keypadDrawDigits(nbgl_keypad_t *keypad)
134{
135 uint8_t i;
136 nbgl_area_t rectArea;
137 char key_value;
138
139 rectArea.backgroundColor = keypad->obj.area.backgroundColor;
140 // only draw digits if not a partial refresh
141 if (!keypad->partial) {
142 rectArea.y0 = keypad->obj.area.y0 + DIGIT_OFFSET_Y;
143
144 // First row of keys: 1 2 3
145 for (i = 0; i < 3; i++) {
146 key_value = GET_DIGIT_INDEX(keypad, (i + 1)) + 0x30;
147
148 rectArea.x0 = keypad->obj.area.x0 + i * KEY_WIDTH;
149 rectArea.x0 += (KEY_WIDTH - nbgl_getCharWidth(LARGE_MEDIUM_FONT, &key_value)) / 2;
151 &rectArea, &key_value, 1, LARGE_MEDIUM_FONT, keypad->enableDigits ? BLACK : WHITE);
152 }
153 // Second row: 4 5 6
154 rectArea.y0 += KEYPAD_KEY_HEIGHT;
155 for (; i < 6; i++) {
156 key_value = GET_DIGIT_INDEX(keypad, (i + 1)) + 0x30;
157 rectArea.x0 = keypad->obj.area.x0 + (i - 3) * KEY_WIDTH;
158 rectArea.x0 += (KEY_WIDTH - nbgl_getCharWidth(LARGE_MEDIUM_FONT, &key_value)) / 2;
160 &rectArea, &key_value, 1, LARGE_MEDIUM_FONT, keypad->enableDigits ? BLACK : WHITE);
161 }
162 // Third row: 7 8 9
163 rectArea.y0 += KEYPAD_KEY_HEIGHT;
164 for (; i < 9; i++) {
165 key_value = GET_DIGIT_INDEX(keypad, (i + 1)) + 0x30;
166 rectArea.x0 = keypad->obj.area.x0 + (i - 6) * KEY_WIDTH;
167 rectArea.x0 += (KEY_WIDTH - nbgl_getCharWidth(LARGE_MEDIUM_FONT, &key_value)) / 2;
169 &rectArea, &key_value, 1, LARGE_MEDIUM_FONT, keypad->enableDigits ? BLACK : WHITE);
170 }
171 }
172 // 4th raw, Backspace, 0 and Validate
173 // draw backspace
174 rectArea.width = BACKSPACE_ICON.width;
175 rectArea.height = BACKSPACE_ICON.height;
176 rectArea.bpp = NBGL_BPP_1;
177 rectArea.x0 = keypad->obj.area.x0 + (KEY_WIDTH - rectArea.width) / 2;
178 rectArea.y0
179 = keypad->obj.area.y0 + KEYPAD_KEY_HEIGHT * 3 + (KEYPAD_KEY_HEIGHT - rectArea.height) / 2;
180 if (BACKSPACE_ICON.isFile) {
181 nbgl_frontDrawImageFile(&rectArea,
182 (uint8_t *) BACKSPACE_ICON.bitmap,
183 keypad->enableBackspace ? BLACK : WHITE,
184 ramBuffer);
185 }
186 else {
187 nbgl_frontDrawImage(&rectArea,
188 (uint8_t *) BACKSPACE_ICON.bitmap,
190 keypad->enableBackspace ? BLACK : WHITE);
191 }
192
193 // only draw '0' if not a partial refresh
194 if (!keypad->partial) {
195 // draw 0
196 key_value = GET_DIGIT_INDEX(keypad, 0) + 0x30;
197 rectArea.x0 = keypad->obj.area.x0 + KEY_WIDTH;
198 rectArea.x0 += (KEY_WIDTH - nbgl_getCharWidth(LARGE_MEDIUM_FONT, &key_value)) / 2;
199 rectArea.y0 = keypad->obj.area.y0 + KEYPAD_KEY_HEIGHT * 3 + DIGIT_OFFSET_Y;
201 &rectArea, &key_value, 1, LARGE_MEDIUM_FONT, keypad->enableDigits ? BLACK : WHITE);
202 }
203
204 // draw white background if validate not enabled
205 if (!keypad->enableValidate) {
206 rectArea.width = KEY_WIDTH - 1;
207 rectArea.height = KEYPAD_KEY_HEIGHT - 4;
208 rectArea.bpp = NBGL_BPP_1;
209 rectArea.x0 = keypad->obj.area.x0 + 2 * KEY_WIDTH + 1;
210 rectArea.y0 = keypad->obj.area.y0 + KEYPAD_KEY_HEIGHT * 3 + 4;
211 rectArea.backgroundColor = WHITE;
212 nbgl_frontDrawRect(&rectArea);
214 rectArea.backgroundColor = keypad->obj.area.backgroundColor;
215 rectArea.x0 = keypad->obj.area.x0 + 2 * KEY_WIDTH;
216 rectArea.y0 = keypad->obj.area.y0 + KEYPAD_KEY_HEIGHT * 3;
217 rectArea.width = KEY_WIDTH;
218 rectArea.height = VERTICAL_ALIGNMENT;
219 nbgl_frontDrawHorizontalLine(&rectArea, 0x1, keypad->borderColor); // 1st line (top)
221 rectArea.backgroundColor = keypad->borderColor;
222 rectArea.x0 = keypad->obj.area.x0 + 2 * KEY_WIDTH;
223 rectArea.y0 = keypad->obj.area.y0 + KEYPAD_KEY_HEIGHT * 3;
224 rectArea.width = 1;
225 rectArea.height = KEYPAD_KEY_HEIGHT;
226 nbgl_frontDrawRect(&rectArea); // 1st full line, on the left
227 }
228 else {
229 const nbgl_icon_details_t *icon;
230
231 if (keypad->softValidation) {
232 icon = &RIGHT_ARROW_ICON;
233 }
234 else {
235 icon = &VALIDATE_ICON;
236 }
237 // if enabled, draw icon in white on a black background
238 rectArea.backgroundColor = BLACK;
239 rectArea.x0 = keypad->obj.area.x0 + 2 * KEY_WIDTH;
240 rectArea.y0 = keypad->obj.area.y0 + KEYPAD_KEY_HEIGHT * 3;
241 rectArea.width = KEY_WIDTH;
242 rectArea.height = KEYPAD_KEY_HEIGHT;
243 nbgl_frontDrawRect(&rectArea);
244 rectArea.width = icon->width;
245 rectArea.height = icon->height;
246 rectArea.bpp = NBGL_BPP_1;
247 rectArea.x0 = keypad->obj.area.x0 + 2 * KEY_WIDTH + (KEY_WIDTH - rectArea.width) / 2;
248 rectArea.y0 = keypad->obj.area.y0 + KEYPAD_KEY_HEIGHT * 3
249 + (KEYPAD_KEY_HEIGHT - rectArea.height) / 2;
250 if (icon->isFile) {
251 nbgl_frontDrawImageFile(&rectArea, (uint8_t *) icon->bitmap, WHITE, ramBuffer);
252 }
253 else {
254 nbgl_frontDrawImage(&rectArea, (uint8_t *) icon->bitmap, NO_TRANSFORMATION, WHITE);
255 }
256 }
257}
258
259static void keypadDraw(nbgl_keypad_t *keypad)
260{
261 if (!keypad->partial) {
262 // At first, draw grid
263 keypadDrawGrid(keypad);
264 }
265
266 // then draw key content
267 keypadDrawDigits(keypad);
268 keypad->partial = false;
269}
270
271/**********************
272 * GLOBAL FUNCTIONS
273 **********************/
274
283{
284 uint8_t firstIndex, lastIndex;
285 nbgl_touchStatePosition_t *firstPosition, *lastPosition;
286 nbgl_keypad_t *keypad = (nbgl_keypad_t *) obj;
287
288 LOG_DEBUG(MISC_LOGGER, "nbgl_keypadTouchCallback(): eventType = %d\n", eventType);
289 if ((eventType != TOUCHED) && (eventType != TOUCH_PRESSED)) {
290 return;
291 }
292 if (nbgl_touchGetTouchedPosition(obj, &firstPosition, &lastPosition) == false) {
293 return;
294 }
295
296 // use positions relative to keypad position
297 firstIndex = getKeypadIndex(firstPosition->x - obj->area.x0, firstPosition->y - obj->area.y0);
298 if (firstIndex > VALIDATE_KEY_IDX) {
299 return;
300 }
301 lastIndex = getKeypadIndex(lastPosition->x - obj->area.x0, lastPosition->y - obj->area.y0);
302 if (lastIndex > VALIDATE_KEY_IDX) {
303 return;
304 }
305
306 // if position of finger has moved during press to another "key", drop it
307 if (lastIndex != firstIndex) {
308 return;
309 }
310
311 if ((firstIndex < 10) && (keypad->enableDigits)) {
312 // only call callback if event is TOUCHED, otherwise play tune on touch event (and not on
313 // release)
314 if (eventType == TOUCHED) {
315 keypad->callback(GET_DIGIT_INDEX(keypad, firstIndex) + 0x30);
316 }
317 else {
318 os_io_seph_cmd_piezo_play_tune(TUNE_TAP_CASUAL);
319 }
320 }
321 if ((firstIndex == BACKSPACE_KEY_IDX) && (keypad->enableBackspace)) { // backspace
322 // only call callback if event is TOUCHED, otherwise play tune on touch event (and not on
323 // release)
324 if (eventType == TOUCHED) {
325 keypad->callback(BACKSPACE_KEY);
326 }
327 else {
328 os_io_seph_cmd_piezo_play_tune(TUNE_TAP_CASUAL);
329 }
330 }
331 else if ((firstIndex == VALIDATE_KEY_IDX) && (keypad->enableValidate)) { // validate
332 // only call callback if event is TOUCHED
333 if (eventType == TOUCHED) {
334 keypad->callback(VALIDATE_KEY);
335 }
336 }
337}
338
349bool nbgl_keypadGetPosition(nbgl_keypad_t *kpd, char index, uint16_t *x, uint16_t *y)
350{
351 // if in first line
352 if ((index >= '1') && (index <= '3')) {
353 *x = kpd->obj.area.x0 + (index - '1') * KEY_WIDTH;
354 *y = kpd->obj.area.y0;
355 }
356 else if ((index >= '4') && (index <= '6')) {
357 *x = kpd->obj.area.x0 + (index - '4') * KEY_WIDTH;
358 *y = kpd->obj.area.y0 + KEYPAD_KEY_HEIGHT;
359 }
360 else if ((index >= '7') && (index <= '9')) {
361 *x = kpd->obj.area.x0 + (index - '7') * KEY_WIDTH;
362 *y = kpd->obj.area.y0 + (2 * KEYPAD_KEY_HEIGHT);
363 }
364 else if (index == BACKSPACE_KEY) { // backspace
365 *x = kpd->obj.area.x0;
366 *y = kpd->obj.area.y0 + (3 * KEYPAD_KEY_HEIGHT);
367 }
368 else if (index == '0') {
369 *x = kpd->obj.area.x0 + KEY_WIDTH;
370 *y = kpd->obj.area.y0 + (3 * KEYPAD_KEY_HEIGHT);
371 }
372 else if (index == VALIDATE_KEY) { // validate
373 *x = kpd->obj.area.x0 + (2 * KEY_WIDTH);
374 *y = kpd->obj.area.y0 + (3 * KEYPAD_KEY_HEIGHT);
375 }
376 else {
377 return false;
378 }
379 return true;
380}
381
389{
390 kpd->obj.touchMask = (1 << TOUCHED) | (1 << TOUCH_PRESSED);
391 kpd->obj.touchId = KEYPAD_ID;
392
393 // if the object has not been already used, prepare indexes of digits
394 if (kpd->digitIndexes[0] == 0) {
395 uint32_t i;
396 if (kpd->shuffled) {
397 uint8_t shuffledDigits[10] = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9};
398
399 // modern version of the Fisher-Yates shuffle
400 for (i = 0; i < 9; i++) {
401 // pick a random number k in [i:9] intervale
402 uint32_t j = cx_rng_u32_range(i, 10);
403 uint8_t tmp = shuffledDigits[j];
404
405 // exchange shuffledDigits[i] and shuffledDigits[j]
406 shuffledDigits[j] = shuffledDigits[i];
407 shuffledDigits[i] = tmp;
408 }
409 for (i = 0; i < 10; i++) {
410 // apply the permuted value to digit i
411 SET_DIGIT_INDEX(kpd, i, shuffledDigits[i]);
412 }
413 }
414 else {
415 // no shuffling
416 for (i = 0; i < 10; i++) {
417 SET_DIGIT_INDEX(kpd, i, i);
418 }
419 }
420 }
421 keypadDraw(kpd);
422}
423
424#endif // HAVE_SE_TOUCH
425#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:362
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.
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:581
#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:131
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:126
@ BLACK
Definition nbgl_types.h:123
nbgl_touchType_t
The different types of Touchscreen events.
Definition nbgl_types.h:241
@ TOUCHED
Definition nbgl_types.h:242
@ TOUCH_PRESSED
Definition nbgl_types.h:249
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:287
#define NO_TRANSFORMATION
Definition nbgl_types.h:76
@ NBGL_BPP_1
1 bit per pixel
Definition nbgl_types.h:266
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:231
int16_t y
vertical position of the touch (or for a RELEASED the last touched point)
Definition nbgl_obj.h:238
int16_t x
horizontal position of the touch (or for a RELEASED the last touched point)
Definition nbgl_obj.h:237