1 /**
2 * @file lv_indev.c
3 *
4 */
5
6 /*********************
7 * INCLUDES
8 ********************/
9 #include "lv_indev.h"
10 #include "lv_disp.h"
11 #include "lv_obj.h"
12
13 #include "../lv_hal/lv_hal_tick.h"
14 #include "../lv_core/lv_group.h"
15 #include "../lv_core/lv_refr.h"
16 #include "../lv_misc/lv_task.h"
17 #include "../lv_misc/lv_math.h"
18
19 /*********************
20 * DEFINES
21 *********************/
22
23 #if LV_INDEV_DEF_DRAG_THROW <= 0
24 #warning "LV_INDEV_DRAG_THROW must be greater than 0"
25 #endif
26
27 /**********************
28 * TYPEDEFS
29 **********************/
30
31 /**********************
32 * STATIC PROTOTYPES
33 **********************/
34
35 static void indev_pointer_proc(lv_indev_t * i, lv_indev_data_t * data);
36 static void indev_keypad_proc(lv_indev_t * i, lv_indev_data_t * data);
37 static void indev_encoder_proc(lv_indev_t * i, lv_indev_data_t * data);
38 static void indev_button_proc(lv_indev_t * i, lv_indev_data_t * data);
39 static void indev_proc_press(lv_indev_proc_t * proc);
40 static void indev_proc_release(lv_indev_proc_t * proc);
41 static void indev_proc_reset_query_handler(lv_indev_t * indev);
42 static void indev_click_focus(lv_indev_proc_t * proc);
43 static void indev_drag(lv_indev_proc_t * proc);
44 static void indev_drag_throw(lv_indev_proc_t * proc);
45 static lv_obj_t * get_dragged_obj(lv_obj_t * obj);
46 static void indev_gesture(lv_indev_proc_t * proc);
47 static bool indev_reset_check(lv_indev_proc_t * proc);
48
49 /**********************
50 * STATIC VARIABLES
51 **********************/
52 static lv_indev_t * indev_act;
53 static lv_obj_t * indev_obj_act = NULL;
54
55 /**********************
56 * MACROS
57 **********************/
58
59 /**********************
60 * GLOBAL FUNCTIONS
61 **********************/
62
63 /**
64 * Initialize the display input device subsystem
65 */
_lv_indev_init(void)66 void _lv_indev_init(void)
67 {
68 lv_indev_reset(NULL, NULL); /*Reset all input devices*/
69 }
70
71 /**
72 * Called periodically to read the input devices
73 * @param param pointer to and input device to read
74 */
_lv_indev_read_task(lv_task_t * task)75 void _lv_indev_read_task(lv_task_t * task)
76 {
77 LV_LOG_TRACE("indev read task started");
78
79 lv_indev_data_t data;
80
81 indev_act = task->user_data;
82
83 /*Read and process all indevs*/
84 if(indev_act->driver.disp == NULL) return; /*Not assigned to any displays*/
85
86 /*Handle reset query before processing the point*/
87 indev_proc_reset_query_handler(indev_act);
88
89 if(indev_act->proc.disabled) return;
90 bool more_to_read;
91 do {
92 /*Read the data*/
93 more_to_read = _lv_indev_read(indev_act, &data);
94
95 /*The active object might deleted even in the read function*/
96 indev_proc_reset_query_handler(indev_act);
97 indev_obj_act = NULL;
98
99 indev_act->proc.state = data.state;
100
101 /*Save the last activity time*/
102 if(indev_act->proc.state == LV_INDEV_STATE_PR) {
103 indev_act->driver.disp->last_activity_time = lv_tick_get();
104 }
105 else if(indev_act->driver.type == LV_INDEV_TYPE_ENCODER && data.enc_diff) {
106 indev_act->driver.disp->last_activity_time = lv_tick_get();
107 }
108
109 if(indev_act->driver.type == LV_INDEV_TYPE_POINTER) {
110 indev_pointer_proc(indev_act, &data);
111 }
112 else if(indev_act->driver.type == LV_INDEV_TYPE_KEYPAD) {
113 indev_keypad_proc(indev_act, &data);
114 }
115 else if(indev_act->driver.type == LV_INDEV_TYPE_ENCODER) {
116 indev_encoder_proc(indev_act, &data);
117 }
118 else if(indev_act->driver.type == LV_INDEV_TYPE_BUTTON) {
119 indev_button_proc(indev_act, &data);
120 }
121 /*Handle reset query if it happened in during processing*/
122 indev_proc_reset_query_handler(indev_act);
123 } while(more_to_read);
124
125 /*End of indev processing, so no act indev*/
126 indev_act = NULL;
127 indev_obj_act = NULL;
128
129 LV_LOG_TRACE("indev read task finished");
130 }
131
132 /**
133 * Get the currently processed input device. Can be used in action functions too.
134 * @return pointer to the currently processed input device or NULL if no input device processing
135 * right now
136 */
lv_indev_get_act(void)137 lv_indev_t * lv_indev_get_act(void)
138 {
139 return indev_act;
140 }
141
142 /**
143 * Get the type of an input device
144 * @param indev pointer to an input device
145 * @return the type of the input device from `lv_hal_indev_type_t` (`LV_INDEV_TYPE_...`)
146 */
lv_indev_get_type(const lv_indev_t * indev)147 lv_indev_type_t lv_indev_get_type(const lv_indev_t * indev)
148 {
149 if(indev == NULL) return LV_INDEV_TYPE_NONE;
150
151 return indev->driver.type;
152 }
153
154 /**
155 * Reset one or all input devices
156 * @param indev pointer to an input device to reset or NULL to reset all of them
157 * @param obj pointer to an object which triggers the reset.
158 */
lv_indev_reset(lv_indev_t * indev,lv_obj_t * obj)159 void lv_indev_reset(lv_indev_t * indev, lv_obj_t * obj)
160 {
161 if(indev) {
162 indev->proc.reset_query = 1;
163 if(indev_act == indev) indev_obj_act = NULL;
164 if(obj == NULL || indev->proc.types.pointer.last_pressed == obj) {
165 indev->proc.types.pointer.last_pressed = NULL;
166 }
167 }
168 else {
169 lv_indev_t * i = lv_indev_get_next(NULL);
170 while(i) {
171 i->proc.reset_query = 1;
172 if(indev_act == i) indev_obj_act = NULL;
173 if(obj == NULL || i->proc.types.pointer.last_pressed == obj) {
174 i->proc.types.pointer.last_pressed = NULL;
175 }
176 i = lv_indev_get_next(i);
177 }
178 }
179 }
180
181 /**
182 * Reset the long press state of an input device
183 * @param indev pointer to an input device
184 */
lv_indev_reset_long_press(lv_indev_t * indev)185 void lv_indev_reset_long_press(lv_indev_t * indev)
186 {
187 indev->proc.long_pr_sent = 0;
188 indev->proc.longpr_rep_timestamp = lv_tick_get();
189 indev->proc.pr_timestamp = lv_tick_get();
190 }
191
192 /**
193 * Enable or disable an input devices
194 * @param indev pointer to an input device
195 * @param en true: enable; false: disable
196 */
lv_indev_enable(lv_indev_t * indev,bool en)197 void lv_indev_enable(lv_indev_t * indev, bool en)
198 {
199 if(!indev) return;
200
201 indev->proc.disabled = en ? 0 : 1;
202 }
203
204 /**
205 * Set a cursor for a pointer input device (for LV_INPUT_TYPE_POINTER and LV_INPUT_TYPE_BUTTON)
206 * @param indev pointer to an input device
207 * @param cur_obj pointer to an object to be used as cursor
208 */
lv_indev_set_cursor(lv_indev_t * indev,lv_obj_t * cur_obj)209 void lv_indev_set_cursor(lv_indev_t * indev, lv_obj_t * cur_obj)
210 {
211 if(indev->driver.type != LV_INDEV_TYPE_POINTER) return;
212
213 indev->cursor = cur_obj;
214 lv_obj_set_parent(indev->cursor, lv_disp_get_layer_sys(indev->driver.disp));
215 lv_obj_set_pos(indev->cursor, indev->proc.types.pointer.act_point.x, indev->proc.types.pointer.act_point.y);
216 lv_obj_set_click(indev->cursor, false);
217 }
218
219 #if LV_USE_GROUP
220 /**
221 * Set a destination group for a keypad input device (for LV_INDEV_TYPE_KEYPAD)
222 * @param indev pointer to an input device
223 * @param group point to a group
224 */
lv_indev_set_group(lv_indev_t * indev,lv_group_t * group)225 void lv_indev_set_group(lv_indev_t * indev, lv_group_t * group)
226 {
227 if(indev->driver.type == LV_INDEV_TYPE_KEYPAD || indev->driver.type == LV_INDEV_TYPE_ENCODER) {
228 indev->group = group;
229 }
230 }
231 #endif
232
233 /**
234 * Set the an array of points for LV_INDEV_TYPE_BUTTON.
235 * These points will be assigned to the buttons to press a specific point on the screen
236 * @param indev pointer to an input device
237 * @param group point to a group
238 */
lv_indev_set_button_points(lv_indev_t * indev,const lv_point_t points[])239 void lv_indev_set_button_points(lv_indev_t * indev, const lv_point_t points[])
240 {
241 if(indev->driver.type == LV_INDEV_TYPE_BUTTON) {
242 indev->btn_points = points;
243 }
244 }
245
246 /**
247 * Get the last point of an input device (for LV_INDEV_TYPE_POINTER and LV_INDEV_TYPE_BUTTON)
248 * @param indev pointer to an input device
249 * @param point pointer to a point to store the result
250 */
lv_indev_get_point(const lv_indev_t * indev,lv_point_t * point)251 void lv_indev_get_point(const lv_indev_t * indev, lv_point_t * point)
252 {
253 if(indev == NULL) {
254 point->x = 0;
255 point->y = 0;
256 return;
257 }
258 if(indev->driver.type != LV_INDEV_TYPE_POINTER && indev->driver.type != LV_INDEV_TYPE_BUTTON) {
259 point->x = -1;
260 point->y = -1;
261 }
262 else {
263 point->x = indev->proc.types.pointer.act_point.x;
264 point->y = indev->proc.types.pointer.act_point.y;
265 }
266 }
267
268 /**
269 * Get the current gesture direct
270 * @param indev pointer to an input device
271 * @return current gesture direct
272 */
lv_indev_get_gesture_dir(const lv_indev_t * indev)273 lv_gesture_dir_t lv_indev_get_gesture_dir(const lv_indev_t * indev)
274 {
275 return indev->proc.types.pointer.gesture_dir;
276 }
277
278 /**
279 * Get the last pressed key of an input device (for LV_INDEV_TYPE_KEYPAD)
280 * @param indev pointer to an input device
281 * @return the last pressed key (0 on error)
282 */
lv_indev_get_key(const lv_indev_t * indev)283 uint32_t lv_indev_get_key(const lv_indev_t * indev)
284 {
285 if(indev->driver.type != LV_INDEV_TYPE_KEYPAD)
286 return 0;
287 else
288 return indev->proc.types.keypad.last_key;
289 }
290
291 /**
292 * Check if there is dragging with an input device or not (for LV_INDEV_TYPE_POINTER and
293 * LV_INDEV_TYPE_BUTTON)
294 * @param indev pointer to an input device
295 * @return true: drag is in progress
296 */
lv_indev_is_dragging(const lv_indev_t * indev)297 bool lv_indev_is_dragging(const lv_indev_t * indev)
298 {
299 if(indev == NULL) return false;
300 if(indev->driver.type != LV_INDEV_TYPE_POINTER && indev->driver.type != LV_INDEV_TYPE_BUTTON) return false;
301 return indev->proc.types.pointer.drag_in_prog == 0 ? false : true;
302 }
303
304 /**
305 * Get the types.pointer.vector of dragging of an input device (for LV_INDEV_TYPE_POINTER and
306 * LV_INDEV_TYPE_BUTTON)
307 * @param indev pointer to an input device
308 * @param point pointer to a point to store the types.pointer.vector
309 */
lv_indev_get_vect(const lv_indev_t * indev,lv_point_t * point)310 void lv_indev_get_vect(const lv_indev_t * indev, lv_point_t * point)
311 {
312 if(indev == NULL) {
313 point->x = 0;
314 point->y = 0;
315 return;
316 }
317
318 if(indev->driver.type != LV_INDEV_TYPE_POINTER && indev->driver.type != LV_INDEV_TYPE_BUTTON) {
319 point->x = 0;
320 point->y = 0;
321 }
322 else {
323 point->x = indev->proc.types.pointer.vect.x;
324 point->y = indev->proc.types.pointer.vect.y;
325 }
326 }
327
328 /**
329 * Manually finish dragging.
330 * `LV_SIGNAL_DRAG_END` and `LV_EVENT_DRAG_END` will be sent.
331 * @param indev pointer to an input device
332 * @return `LV_RES_INV` if the object being dragged was deleted. Else `LV_RES_OK`.
333 */
lv_indev_finish_drag(lv_indev_t * indev)334 lv_res_t lv_indev_finish_drag(lv_indev_t * indev)
335 {
336 if(indev == NULL) return LV_RES_OK;
337 if(indev->driver.type != LV_INDEV_TYPE_POINTER) return LV_RES_OK;
338 if(indev->proc.types.pointer.drag_in_prog == 0) return LV_RES_OK;
339
340 indev->proc.types.pointer.drag_in_prog = 0;
341 indev->proc.types.pointer.drag_throw_vect.x = 0;
342 indev->proc.types.pointer.drag_throw_vect.y = 0;
343
344 lv_obj_t * drag_obj;
345 drag_obj = get_dragged_obj(indev->proc.types.pointer.act_obj);
346 if(drag_obj == NULL) return LV_RES_OK;
347
348 lv_res_t res;
349 res = drag_obj->signal_cb(drag_obj, LV_SIGNAL_DRAG_END, NULL);
350 if(res != LV_RES_OK) return res;
351
352 res = lv_event_send(drag_obj, LV_EVENT_DRAG_END, NULL);
353 if(res != LV_RES_OK) return res;
354
355 return res;
356 }
357
358 /**
359 * Do nothing until the next release
360 * @param indev pointer to an input device
361 */
lv_indev_wait_release(lv_indev_t * indev)362 void lv_indev_wait_release(lv_indev_t * indev)
363 {
364 if(indev == NULL)return;
365 indev->proc.wait_until_release = 1;
366 }
367
368 /**
369 * Gets a pointer to the currently active object in the currently processed input device.
370 * @return pointer to currently active object or NULL if no active object
371 */
lv_indev_get_obj_act(void)372 lv_obj_t * lv_indev_get_obj_act(void)
373 {
374 return indev_obj_act;
375 }
376
377 /**
378 * Get a pointer to the indev read task to
379 * modify its parameters with `lv_task_...` functions.
380 * @param indev pointer to an input device
381 * @return pointer to the indev read refresher task. (NULL on error)
382 */
lv_indev_get_read_task(lv_disp_t * indev)383 lv_task_t * lv_indev_get_read_task(lv_disp_t * indev)
384 {
385 if(!indev) {
386 LV_LOG_WARN("lv_indev_get_read_task: indev was NULL");
387 return NULL;
388 }
389
390 return indev->refr_task;
391 }
392
393 /**********************
394 * STATIC FUNCTIONS
395 **********************/
396
397 /**
398 * Process a new point from LV_INDEV_TYPE_POINTER input device
399 * @param i pointer to an input device
400 * @param data pointer to the data read from the input device
401 */
indev_pointer_proc(lv_indev_t * i,lv_indev_data_t * data)402 static void indev_pointer_proc(lv_indev_t * i, lv_indev_data_t * data)
403 {
404 /*Move the cursor if set and moved*/
405 if(i->cursor != NULL &&
406 (i->proc.types.pointer.last_point.x != data->point.x || i->proc.types.pointer.last_point.y != data->point.y)) {
407 lv_obj_set_pos(i->cursor, data->point.x, data->point.y);
408 }
409
410 i->proc.types.pointer.act_point.x = data->point.x;
411 i->proc.types.pointer.act_point.y = data->point.y;
412
413 if(i->proc.state == LV_INDEV_STATE_PR) {
414 indev_proc_press(&i->proc);
415 }
416 else {
417 indev_proc_release(&i->proc);
418 }
419
420 i->proc.types.pointer.last_point.x = i->proc.types.pointer.act_point.x;
421 i->proc.types.pointer.last_point.y = i->proc.types.pointer.act_point.y;
422 }
423
424 /**
425 * Process a new point from LV_INDEV_TYPE_KEYPAD input device
426 * @param i pointer to an input device
427 * @param data pointer to the data read from the input device
428 */
indev_keypad_proc(lv_indev_t * i,lv_indev_data_t * data)429 static void indev_keypad_proc(lv_indev_t * i, lv_indev_data_t * data)
430 {
431 #if LV_USE_GROUP
432 if(data->state == LV_INDEV_STATE_PR && i->proc.wait_until_release) return;
433
434 if(i->proc.wait_until_release) {
435 i->proc.wait_until_release = 0;
436 i->proc.pr_timestamp = 0;
437 i->proc.long_pr_sent = 0;
438 i->proc.types.keypad.last_state = LV_INDEV_STATE_REL; /*To skip the processing of release*/
439 }
440
441 lv_group_t * g = i->group;
442 if(g == NULL) return;
443
444 indev_obj_act = lv_group_get_focused(g);
445 if(indev_obj_act == NULL) return;
446
447 /*Save the last key to compare it with the current latter on RELEASE*/
448 uint32_t prev_key = i->proc.types.keypad.last_key;
449
450 /* Save the last key.
451 * It must be done here else `lv_indev_get_key` will return the last key in events and signals*/
452 i->proc.types.keypad.last_key = data->key;
453
454 /* Save the previous state so we can detect state changes below and also set the last state now
455 * so if any signal/event handler on the way returns `LV_RES_INV` the last state is remembered
456 * for the next time*/
457 uint32_t prev_state = i->proc.types.keypad.last_state;
458 i->proc.types.keypad.last_state = data->state;
459
460 /*Key press happened*/
461 if(data->state == LV_INDEV_STATE_PR && prev_state == LV_INDEV_STATE_REL) {
462 i->proc.pr_timestamp = lv_tick_get();
463
464 /*Simulate a press on the object if ENTER was pressed*/
465 if(data->key == LV_KEY_ENTER) {
466 /*Send the ENTER as a normal KEY*/
467 lv_group_send_data(g, LV_KEY_ENTER);
468
469 indev_obj_act->signal_cb(indev_obj_act, LV_SIGNAL_PRESSED, NULL);
470 if(indev_reset_check(&i->proc)) return;
471 lv_event_send(indev_obj_act, LV_EVENT_PRESSED, NULL);
472 if(indev_reset_check(&i->proc)) return;
473 }
474 else if(data->key == LV_KEY_ESC) {
475 /*Send the ESC as a normal KEY*/
476 lv_group_send_data(g, LV_KEY_ESC);
477
478 lv_event_send(indev_obj_act, LV_EVENT_CANCEL, NULL);
479 if(indev_reset_check(&i->proc)) return;
480 }
481 /*Move the focus on NEXT*/
482 else if(data->key == LV_KEY_NEXT) {
483 lv_group_set_editing(g, false); /*Editing is not used by KEYPAD is be sure it is disabled*/
484 lv_group_focus_next(g);
485 if(indev_reset_check(&i->proc)) return;
486 }
487 /*Move the focus on PREV*/
488 else if(data->key == LV_KEY_PREV) {
489 lv_group_set_editing(g, false); /*Editing is not used by KEYPAD is be sure it is disabled*/
490 lv_group_focus_prev(g);
491 if(indev_reset_check(&i->proc)) return;
492 }
493 /*Just send other keys to the object (e.g. 'A' or `LV_GROUP_KEY_RIGHT`)*/
494 else {
495 lv_group_send_data(g, data->key);
496 }
497 }
498 /*Pressing*/
499 else if(data->state == LV_INDEV_STATE_PR && prev_state == LV_INDEV_STATE_PR) {
500
501 if(data->key == LV_KEY_ENTER) {
502 indev_obj_act->signal_cb(indev_obj_act, LV_SIGNAL_PRESSING, NULL);
503 if(indev_reset_check(&i->proc)) return;
504 lv_event_send(indev_obj_act, LV_EVENT_PRESSING, NULL);
505 if(indev_reset_check(&i->proc)) return;
506 }
507
508 /*Long press time has elapsed?*/
509 if(i->proc.long_pr_sent == 0 && lv_tick_elaps(i->proc.pr_timestamp) > i->driver.long_press_time) {
510 i->proc.long_pr_sent = 1;
511 if(data->key == LV_KEY_ENTER) {
512 i->proc.longpr_rep_timestamp = lv_tick_get();
513 indev_obj_act->signal_cb(indev_obj_act, LV_SIGNAL_LONG_PRESS, NULL);
514 if(indev_reset_check(&i->proc)) return;
515 lv_event_send(indev_obj_act, LV_EVENT_LONG_PRESSED, NULL);
516 if(indev_reset_check(&i->proc)) return;
517 }
518 }
519 /*Long press repeated time has elapsed?*/
520 else if(i->proc.long_pr_sent != 0 &&
521 lv_tick_elaps(i->proc.longpr_rep_timestamp) > i->driver.long_press_rep_time) {
522
523 i->proc.longpr_rep_timestamp = lv_tick_get();
524
525 /*Send LONG_PRESS_REP on ENTER*/
526 if(data->key == LV_KEY_ENTER) {
527 indev_obj_act->signal_cb(indev_obj_act, LV_SIGNAL_LONG_PRESS_REP, NULL);
528 if(indev_reset_check(&i->proc)) return;
529 lv_event_send(indev_obj_act, LV_EVENT_LONG_PRESSED_REPEAT, NULL);
530 if(indev_reset_check(&i->proc)) return;
531 }
532 /*Move the focus on NEXT again*/
533 else if(data->key == LV_KEY_NEXT) {
534 lv_group_set_editing(g, false); /*Editing is not used by KEYPAD is be sure it is disabled*/
535 lv_group_focus_next(g);
536 if(indev_reset_check(&i->proc)) return;
537 }
538 /*Move the focus on PREV again*/
539 else if(data->key == LV_KEY_PREV) {
540 lv_group_set_editing(g, false); /*Editing is not used by KEYPAD is be sure it is disabled*/
541 lv_group_focus_prev(g);
542 if(indev_reset_check(&i->proc)) return;
543 }
544 /*Just send other keys again to the object (e.g. 'A' or `LV_GORUP_KEY_RIGHT)*/
545 else {
546 lv_group_send_data(g, data->key);
547 if(indev_reset_check(&i->proc)) return;
548 }
549 }
550 }
551 /*Release happened*/
552 else if(data->state == LV_INDEV_STATE_REL && prev_state == LV_INDEV_STATE_PR) {
553 /*The user might clear the key when it was released. Always release the pressed key*/
554 data->key = prev_key;
555 if(data->key == LV_KEY_ENTER) {
556
557 indev_obj_act->signal_cb(indev_obj_act, LV_SIGNAL_RELEASED, NULL);
558 if(indev_reset_check(&i->proc)) return;
559
560 if(i->proc.long_pr_sent == 0) {
561 lv_event_send(indev_obj_act, LV_EVENT_SHORT_CLICKED, NULL);
562 if(indev_reset_check(&i->proc)) return;
563 }
564
565 lv_event_send(indev_obj_act, LV_EVENT_CLICKED, NULL);
566 if(indev_reset_check(&i->proc)) return;
567
568 lv_event_send(indev_obj_act, LV_EVENT_RELEASED, NULL);
569 if(indev_reset_check(&i->proc)) return;
570 }
571 i->proc.pr_timestamp = 0;
572 i->proc.long_pr_sent = 0;
573 }
574 indev_obj_act = NULL;
575 #else
576 (void)data; /*Unused*/
577 (void)i; /*Unused*/
578 #endif
579 }
580
581 /**
582 * Process a new point from LV_INDEV_TYPE_ENCODER input device
583 * @param i pointer to an input device
584 * @param data pointer to the data read from the input device
585 */
indev_encoder_proc(lv_indev_t * i,lv_indev_data_t * data)586 static void indev_encoder_proc(lv_indev_t * i, lv_indev_data_t * data)
587 {
588 #if LV_USE_GROUP
589
590 if(data->state == LV_INDEV_STATE_PR && i->proc.wait_until_release) return;
591
592 if(i->proc.wait_until_release) {
593 i->proc.wait_until_release = 0;
594 i->proc.pr_timestamp = 0;
595 i->proc.long_pr_sent = 0;
596 i->proc.types.keypad.last_state = LV_INDEV_STATE_REL; /*To skip the processing of release*/
597 }
598
599 /* Save the last keys before anything else.
600 * They need to be already saved if the the function returns for any reason*/
601 lv_indev_state_t last_state = i->proc.types.keypad.last_state;
602 i->proc.types.keypad.last_state = data->state;
603 i->proc.types.keypad.last_key = data->key;
604
605 lv_group_t * g = i->group;
606 if(g == NULL) return;
607
608 indev_obj_act = lv_group_get_focused(g);
609 if(indev_obj_act == NULL) return;
610
611 /*Process the steps they are valid only with released button*/
612 if(data->state != LV_INDEV_STATE_REL) {
613 data->enc_diff = 0;
614 }
615
616 /*Refresh the focused object. It might change due to lv_group_focus_prev/next*/
617 indev_obj_act = lv_group_get_focused(g);
618 if(indev_obj_act == NULL) return;
619
620 /*Button press happened*/
621 if(data->state == LV_INDEV_STATE_PR && last_state == LV_INDEV_STATE_REL) {
622
623 i->proc.pr_timestamp = lv_tick_get();
624
625 if(data->key == LV_KEY_ENTER) {
626 bool editable = false;
627 indev_obj_act->signal_cb(indev_obj_act, LV_SIGNAL_GET_EDITABLE, &editable);
628
629 if(lv_group_get_editing(g) == true || editable == false) {
630 indev_obj_act->signal_cb(indev_obj_act, LV_SIGNAL_PRESSED, NULL);
631 if(indev_reset_check(&i->proc)) return;
632
633 lv_event_send(indev_obj_act, LV_EVENT_PRESSED, NULL);
634 if(indev_reset_check(&i->proc)) return;
635 }
636 }
637 else if(data->key == LV_KEY_LEFT) {
638 /*emulate encoder left*/
639 data->enc_diff--;
640 }
641 else if(data->key == LV_KEY_RIGHT) {
642 /*emulate encoder right*/
643 data->enc_diff++;
644 }
645 else if(data->key == LV_KEY_ESC) {
646 /*Send the ESC as a normal KEY*/
647 lv_group_send_data(g, LV_KEY_ESC);
648
649 lv_event_send(indev_obj_act, LV_EVENT_CANCEL, NULL);
650 if(indev_reset_check(&i->proc)) return;
651 }
652 /*Just send other keys to the object (e.g. 'A' or `LV_GROUP_KEY_RIGHT`)*/
653 else {
654 lv_group_send_data(g, data->key);
655 }
656 }
657 /*Pressing*/
658 else if(data->state == LV_INDEV_STATE_PR && last_state == LV_INDEV_STATE_PR) {
659 /* Long press*/
660 if(i->proc.long_pr_sent == 0 && lv_tick_elaps(i->proc.pr_timestamp) > i->driver.long_press_time) {
661
662 i->proc.long_pr_sent = 1;
663 i->proc.longpr_rep_timestamp = lv_tick_get();
664
665 if(data->key == LV_KEY_ENTER) {
666 bool editable = false;
667 indev_obj_act->signal_cb(indev_obj_act, LV_SIGNAL_GET_EDITABLE, &editable);
668
669 /*On enter long press toggle edit mode.*/
670 if(editable) {
671 /*Don't leave edit mode if there is only one object (nowhere to navigate)*/
672 if(_lv_ll_is_empty(&g->obj_ll) == false) {
673 lv_group_set_editing(g, lv_group_get_editing(g) ? false : true); /*Toggle edit mode on long press*/
674 }
675 }
676 /*If not editable then just send a long press signal*/
677 else {
678 indev_obj_act->signal_cb(indev_obj_act, LV_SIGNAL_LONG_PRESS, NULL);
679 if(indev_reset_check(&i->proc)) return;
680 lv_event_send(indev_obj_act, LV_EVENT_LONG_PRESSED, NULL);
681 if(indev_reset_check(&i->proc)) return;
682 }
683 }
684
685 i->proc.long_pr_sent = 1;
686 }
687 /*Long press repeated time has elapsed?*/
688 else if(i->proc.long_pr_sent != 0 && lv_tick_elaps(i->proc.longpr_rep_timestamp) > i->driver.long_press_rep_time) {
689
690 i->proc.longpr_rep_timestamp = lv_tick_get();
691
692 if(data->key == LV_KEY_ENTER) {
693 indev_obj_act->signal_cb(indev_obj_act, LV_SIGNAL_LONG_PRESS_REP, NULL);
694 if(indev_reset_check(&i->proc)) return;
695 lv_event_send(indev_obj_act, LV_EVENT_LONG_PRESSED_REPEAT, NULL);
696 if(indev_reset_check(&i->proc)) return;
697 }
698 else if(data->key == LV_KEY_LEFT) {
699 /*emulate encoder left*/
700 data->enc_diff--;
701 }
702 else if(data->key == LV_KEY_RIGHT) {
703 /*emulate encoder right*/
704 data->enc_diff++;
705 }
706 else {
707 lv_group_send_data(g, data->key);
708 if(indev_reset_check(&i->proc)) return;
709 }
710
711 }
712
713 }
714 /*Release happened*/
715 else if(data->state == LV_INDEV_STATE_REL && last_state == LV_INDEV_STATE_PR) {
716
717 if(data->key == LV_KEY_ENTER) {
718 bool editable = false;
719 indev_obj_act->signal_cb(indev_obj_act, LV_SIGNAL_GET_EDITABLE, &editable);
720
721 /*The button was released on a non-editable object. Just send enter*/
722 if(editable == false) {
723 indev_obj_act->signal_cb(indev_obj_act, LV_SIGNAL_RELEASED, NULL);
724 if(indev_reset_check(&i->proc)) return;
725
726 if(i->proc.long_pr_sent == 0) lv_event_send(indev_obj_act, LV_EVENT_SHORT_CLICKED, NULL);
727 if(indev_reset_check(&i->proc)) return;
728
729 lv_event_send(indev_obj_act, LV_EVENT_CLICKED, NULL);
730 if(indev_reset_check(&i->proc)) return;
731
732 lv_event_send(indev_obj_act, LV_EVENT_RELEASED, NULL);
733 if(indev_reset_check(&i->proc)) return;
734 }
735 /*An object is being edited and the button is released. */
736 else if(g->editing) {
737 /*Ignore long pressed enter release because it comes from mode switch*/
738 if(!i->proc.long_pr_sent || _lv_ll_is_empty(&g->obj_ll)) {
739 indev_obj_act->signal_cb(indev_obj_act, LV_SIGNAL_RELEASED, NULL);
740 if(indev_reset_check(&i->proc)) return;
741
742 lv_event_send(indev_obj_act, LV_EVENT_SHORT_CLICKED, NULL);
743 if(indev_reset_check(&i->proc)) return;
744
745 lv_event_send(indev_obj_act, LV_EVENT_CLICKED, NULL);
746 if(indev_reset_check(&i->proc)) return;
747
748 lv_event_send(indev_obj_act, LV_EVENT_RELEASED, NULL);
749 if(indev_reset_check(&i->proc)) return;
750
751 lv_group_send_data(g, LV_KEY_ENTER);
752 }
753 }
754 /*If the focused object is editable and now in navigate mode then on enter switch edit
755 mode*/
756 else if(editable && !g->editing && !i->proc.long_pr_sent) {
757 lv_group_set_editing(g, true); /*Set edit mode*/
758 }
759 }
760
761 i->proc.pr_timestamp = 0;
762 i->proc.long_pr_sent = 0;
763 }
764 indev_obj_act = NULL;
765
766 /*if encoder steps or simulated steps via left/right keys*/
767 if(data->enc_diff != 0) {
768 /*In edit mode send LEFT/RIGHT keys*/
769 if(lv_group_get_editing(g)) {
770 int32_t s;
771 if(data->enc_diff < 0) {
772 for(s = 0; s < -data->enc_diff; s++) lv_group_send_data(g, LV_KEY_LEFT);
773 }
774 else if(data->enc_diff > 0) {
775 for(s = 0; s < data->enc_diff; s++) lv_group_send_data(g, LV_KEY_RIGHT);
776 }
777 }
778 /*In navigate mode focus on the next/prev objects*/
779 else {
780 int32_t s;
781 if(data->enc_diff < 0) {
782 for(s = 0; s < -data->enc_diff; s++) lv_group_focus_prev(g);
783 }
784 else if(data->enc_diff > 0) {
785 for(s = 0; s < data->enc_diff; s++) lv_group_focus_next(g);
786 }
787 }
788 }
789
790 #else
791 (void)data; /*Unused*/
792 (void)i; /*Unused*/
793 #endif
794 }
795
796 /**
797 * Process new points from a input device. indev->state.pressed has to be set
798 * @param indev pointer to an input device state
799 * @param x x coordinate of the next point
800 * @param y y coordinate of the next point
801 */
indev_button_proc(lv_indev_t * i,lv_indev_data_t * data)802 static void indev_button_proc(lv_indev_t * i, lv_indev_data_t * data)
803 {
804 /* Die gracefully if i->btn_points is NULL */
805 if(i->btn_points == NULL) {
806 LV_LOG_WARN("indev_button_proc: btn_points was NULL");
807 return;
808 }
809
810 i->proc.types.pointer.act_point.x = i->btn_points[data->btn_id].x;
811 i->proc.types.pointer.act_point.y = i->btn_points[data->btn_id].y;
812
813 /*Still the same point is pressed*/
814 if(i->proc.types.pointer.last_point.x == i->proc.types.pointer.act_point.x &&
815 i->proc.types.pointer.last_point.y == i->proc.types.pointer.act_point.y && data->state == LV_INDEV_STATE_PR) {
816 indev_proc_press(&i->proc);
817 }
818 else {
819 /*If a new point comes always make a release*/
820 indev_proc_release(&i->proc);
821 }
822
823 i->proc.types.pointer.last_point.x = i->proc.types.pointer.act_point.x;
824 i->proc.types.pointer.last_point.y = i->proc.types.pointer.act_point.y;
825 }
826
827 /**
828 * Process the pressed state of LV_INDEV_TYPE_POINER input devices
829 * @param indev pointer to an input device 'proc'
830 * @return LV_RES_OK: no indev reset required; LV_RES_INV: indev reset is required
831 */
indev_proc_press(lv_indev_proc_t * proc)832 static void indev_proc_press(lv_indev_proc_t * proc)
833 {
834 indev_obj_act = proc->types.pointer.act_obj;
835
836 if(proc->wait_until_release != 0) return;
837
838 lv_disp_t * disp = indev_act->driver.disp;
839 bool new_obj_searched = false;
840
841 /*If there is no last object then search*/
842 if(indev_obj_act == NULL) {
843 indev_obj_act = lv_indev_search_obj(lv_disp_get_layer_sys(disp), &proc->types.pointer.act_point);
844 if(indev_obj_act == NULL) indev_obj_act = lv_indev_search_obj(lv_disp_get_layer_top(disp),
845 &proc->types.pointer.act_point);
846 if(indev_obj_act == NULL) indev_obj_act = lv_indev_search_obj(lv_disp_get_scr_act(disp),
847 &proc->types.pointer.act_point);
848 new_obj_searched = true;
849 }
850 /*If there is last object but it is not dragged and not protected also search*/
851 else if(proc->types.pointer.drag_in_prog == 0 &&
852 lv_obj_is_protected(indev_obj_act, LV_PROTECT_PRESS_LOST) == false) {
853 indev_obj_act = lv_indev_search_obj(lv_disp_get_layer_sys(disp), &proc->types.pointer.act_point);
854 if(indev_obj_act == NULL) indev_obj_act = lv_indev_search_obj(lv_disp_get_layer_top(disp),
855 &proc->types.pointer.act_point);
856 if(indev_obj_act == NULL) indev_obj_act = lv_indev_search_obj(lv_disp_get_scr_act(disp),
857 &proc->types.pointer.act_point);
858 new_obj_searched = true;
859 }
860 /*If a draggable or a protected object was the last then keep it*/
861 else {
862 }
863
864 /*The last object might have drag throw. Stop it manually*/
865 if(new_obj_searched && proc->types.pointer.last_obj) {
866 proc->types.pointer.drag_throw_vect.x = 0;
867 proc->types.pointer.drag_throw_vect.y = 0;
868 indev_drag_throw(proc);
869 }
870
871 /*Do not use disabled objects*/
872 if(indev_obj_act && (lv_obj_get_state(indev_obj_act, LV_OBJ_PART_MAIN) & LV_STATE_DISABLED)) {
873 indev_obj_act = proc->types.pointer.act_obj;
874 }
875
876 /*If a new object was found reset some variables and send a pressed signal*/
877 if(indev_obj_act != proc->types.pointer.act_obj) {
878 proc->types.pointer.last_point.x = proc->types.pointer.act_point.x;
879 proc->types.pointer.last_point.y = proc->types.pointer.act_point.y;
880
881 /*If a new object found the previous was lost, so send a signal*/
882 if(proc->types.pointer.act_obj != NULL) {
883 /*Save the obj because in special cases `act_obj` can change in the signal function*/
884 lv_obj_t * last_obj = proc->types.pointer.act_obj;
885
886 last_obj->signal_cb(last_obj, LV_SIGNAL_PRESS_LOST, indev_act);
887 if(indev_reset_check(proc)) return;
888 lv_event_send(last_obj, LV_EVENT_PRESS_LOST, NULL);
889 if(indev_reset_check(proc)) return;
890
891 }
892
893 proc->types.pointer.act_obj = indev_obj_act; /*Save the pressed object*/
894 proc->types.pointer.last_obj = indev_obj_act;
895
896 if(indev_obj_act != NULL) {
897 /* Save the time when the obj pressed to count long press time.*/
898 proc->pr_timestamp = lv_tick_get();
899 proc->long_pr_sent = 0;
900 proc->types.pointer.drag_limit_out = 0;
901 proc->types.pointer.drag_in_prog = 0;
902 proc->types.pointer.drag_sum.x = 0;
903 proc->types.pointer.drag_sum.y = 0;
904 proc->types.pointer.drag_dir = LV_DRAG_DIR_BOTH;
905 proc->types.pointer.gesture_sent = 0;
906 proc->types.pointer.gesture_sum.x = 0;
907 proc->types.pointer.gesture_sum.y = 0;
908 proc->types.pointer.vect.x = 0;
909 proc->types.pointer.vect.y = 0;
910
911 /*Search for 'top' attribute*/
912 lv_obj_t * i = indev_obj_act;
913 lv_obj_t * last_top = NULL;
914 while(i != NULL) {
915 if(i->top) last_top = i;
916 i = lv_obj_get_parent(i);
917 }
918
919 if(last_top != NULL) {
920 /*Move the last_top object to the foreground*/
921 lv_obj_move_foreground(last_top);
922 }
923
924 /*Send a signal about the press*/
925 indev_obj_act->signal_cb(indev_obj_act, LV_SIGNAL_PRESSED, indev_act);
926 if(indev_reset_check(proc)) return;
927
928 lv_event_send(indev_obj_act, LV_EVENT_PRESSED, NULL);
929 if(indev_reset_check(proc)) return;
930
931 if(indev_act->proc.wait_until_release) return;
932
933 /*Handle focus*/
934 indev_click_focus(&indev_act->proc);
935 if(indev_reset_check(proc)) return;
936
937 }
938 }
939
940 /*Calculate the types.pointer.vector*/
941 proc->types.pointer.vect.x = proc->types.pointer.act_point.x - proc->types.pointer.last_point.x;
942 proc->types.pointer.vect.y = proc->types.pointer.act_point.y - proc->types.pointer.last_point.y;
943
944 proc->types.pointer.drag_throw_vect.x = (proc->types.pointer.drag_throw_vect.x * 5) >> 3;
945 proc->types.pointer.drag_throw_vect.y = (proc->types.pointer.drag_throw_vect.y * 5) >> 3;
946
947 if(proc->types.pointer.drag_throw_vect.x < 0)
948 proc->types.pointer.drag_throw_vect.x++;
949 else if(proc->types.pointer.drag_throw_vect.x > 0)
950 proc->types.pointer.drag_throw_vect.x--;
951
952 if(proc->types.pointer.drag_throw_vect.y < 0)
953 proc->types.pointer.drag_throw_vect.y++;
954 else if(proc->types.pointer.drag_throw_vect.y > 0)
955 proc->types.pointer.drag_throw_vect.y--;
956
957 proc->types.pointer.drag_throw_vect.x += (proc->types.pointer.vect.x * 4) >> 3;
958 proc->types.pointer.drag_throw_vect.y += (proc->types.pointer.vect.y * 4) >> 3;
959
960 /*If there is active object and it can be dragged run the drag*/
961 if(indev_obj_act != NULL) {
962 indev_obj_act->signal_cb(indev_obj_act, LV_SIGNAL_PRESSING, indev_act);
963 if(indev_reset_check(proc)) return;
964 lv_event_send(indev_obj_act, LV_EVENT_PRESSING, NULL);
965 if(indev_reset_check(proc)) return;
966 if(indev_act->proc.wait_until_release) return;
967
968 indev_drag(proc);
969 indev_gesture(proc);
970 if(indev_reset_check(proc)) return;
971
972 /*If there is no drag then check for long press time*/
973 if(proc->types.pointer.drag_in_prog == 0 && proc->long_pr_sent == 0) {
974 /*Send a signal about the long press if enough time elapsed*/
975 if(lv_tick_elaps(proc->pr_timestamp) > indev_act->driver.long_press_time) {
976 indev_obj_act->signal_cb(indev_obj_act, LV_SIGNAL_LONG_PRESS, indev_act);
977 if(indev_reset_check(proc)) return;
978 lv_event_send(indev_obj_act, LV_EVENT_LONG_PRESSED, NULL);
979 if(indev_reset_check(proc)) return;
980
981 /*Mark the signal sending to do not send it again*/
982 proc->long_pr_sent = 1;
983
984 /*Save the long press time stamp for the long press repeat handler*/
985 proc->longpr_rep_timestamp = lv_tick_get();
986 }
987 }
988 /*Send long press repeated signal*/
989 if(proc->types.pointer.drag_in_prog == 0 && proc->long_pr_sent == 1) {
990 /*Send a signal about the long press repeat if enough time elapsed*/
991 if(lv_tick_elaps(proc->longpr_rep_timestamp) > indev_act->driver.long_press_rep_time) {
992 indev_obj_act->signal_cb(indev_obj_act, LV_SIGNAL_LONG_PRESS_REP, indev_act);
993 if(indev_reset_check(proc)) return;
994 lv_event_send(indev_obj_act, LV_EVENT_LONG_PRESSED_REPEAT, NULL);
995 if(indev_reset_check(proc)) return;
996 proc->longpr_rep_timestamp = lv_tick_get();
997 }
998 }
999 }
1000 }
1001
1002 /**
1003 * Process the released state of LV_INDEV_TYPE_POINER input devices
1004 * @param proc pointer to an input device 'proc'
1005 */
indev_proc_release(lv_indev_proc_t * proc)1006 static void indev_proc_release(lv_indev_proc_t * proc)
1007 {
1008 if(proc->wait_until_release != 0) {
1009 proc->types.pointer.act_obj = NULL;
1010 proc->types.pointer.last_obj = NULL;
1011 proc->pr_timestamp = 0;
1012 proc->longpr_rep_timestamp = 0;
1013 proc->wait_until_release = 0;
1014 }
1015 indev_obj_act = proc->types.pointer.act_obj;
1016
1017 /*Forget the act obj and send a released signal */
1018 if(indev_obj_act) {
1019
1020 /* If the object was protected against press lost then it possible that
1021 * the object is already not pressed but still it is the `act_obj`.
1022 * In this case send the `LV_SIGNAL_RELEASED/CLICKED` instead of `LV_SIGNAL_PRESS_LOST` if
1023 * the indev is ON the `types.pointer.act_obj` */
1024 if(lv_obj_is_protected(indev_obj_act, LV_PROTECT_PRESS_LOST)) {
1025 indev_obj_act->signal_cb(indev_obj_act, LV_SIGNAL_RELEASED, indev_act);
1026 if(indev_reset_check(proc)) return;
1027
1028 if(proc->types.pointer.drag_in_prog == 0) {
1029 if(proc->long_pr_sent == 0) {
1030 lv_event_send(indev_obj_act, LV_EVENT_SHORT_CLICKED, NULL);
1031 if(indev_reset_check(proc)) return;
1032 }
1033
1034 lv_event_send(indev_obj_act, LV_EVENT_CLICKED, NULL);
1035 if(indev_reset_check(proc)) return;
1036 }
1037
1038 lv_event_send(indev_obj_act, LV_EVENT_RELEASED, NULL);
1039 if(indev_reset_check(proc)) return;
1040 }
1041 /* The simple case: `act_obj` was not protected against press lost.
1042 * If it is already not pressed then `indev_proc_press` would set `indev_obj_act = NULL`*/
1043 else {
1044 indev_obj_act->signal_cb(indev_obj_act, LV_SIGNAL_RELEASED, indev_act);
1045 if(indev_reset_check(proc)) return;
1046
1047 if(proc->long_pr_sent == 0 && proc->types.pointer.drag_in_prog == 0) {
1048 lv_event_send(indev_obj_act, LV_EVENT_SHORT_CLICKED, NULL);
1049 if(indev_reset_check(proc)) return;
1050 }
1051
1052 if(proc->types.pointer.drag_in_prog == 0) {
1053 lv_event_send(indev_obj_act, LV_EVENT_CLICKED, NULL);
1054 if(indev_reset_check(proc)) return;
1055 }
1056
1057 lv_event_send(indev_obj_act, LV_EVENT_RELEASED, NULL);
1058 if(indev_reset_check(proc)) return;
1059 }
1060
1061 /*Send LV_EVENT_DRAG_THROW_BEGIN if required */
1062 /*If drag parent is active check recursively the drag_parent attribute*/
1063 lv_obj_t * drag_obj = get_dragged_obj(indev_obj_act);
1064 if(drag_obj) {
1065 if(lv_obj_get_drag_throw(drag_obj) && proc->types.pointer.drag_in_prog) {
1066 if(drag_obj->signal_cb) drag_obj->signal_cb(drag_obj, LV_SIGNAL_DRAG_THROW_BEGIN, NULL);
1067 if(indev_reset_check(proc)) return;
1068
1069 lv_event_send(drag_obj, LV_EVENT_DRAG_THROW_BEGIN, NULL);
1070 if(indev_reset_check(proc)) return;
1071 }
1072 }
1073
1074 proc->types.pointer.act_obj = NULL;
1075 proc->pr_timestamp = 0;
1076 proc->longpr_rep_timestamp = 0;
1077 }
1078
1079 /*The reset can be set in the signal function.
1080 * In case of reset query ignore the remaining parts.*/
1081 if(proc->types.pointer.last_obj != NULL && proc->reset_query == 0) {
1082 indev_drag_throw(proc);
1083 if(indev_reset_check(proc)) return;
1084 }
1085 }
1086
1087 /**
1088 * Process a new point from LV_INDEV_TYPE_BUTTON input device
1089 * @param i pointer to an input device
1090 * @param data pointer to the data read from the input device
1091 * Reset input device if a reset query has been sent to it
1092 * @param indev pointer to an input device
1093 */
indev_proc_reset_query_handler(lv_indev_t * indev)1094 static void indev_proc_reset_query_handler(lv_indev_t * indev)
1095 {
1096 if(indev->proc.reset_query) {
1097 indev->proc.types.pointer.act_obj = NULL;
1098 indev->proc.types.pointer.last_obj = NULL;
1099 indev->proc.types.pointer.drag_limit_out = 0;
1100 indev->proc.types.pointer.drag_in_prog = 0;
1101 indev->proc.long_pr_sent = 0;
1102 indev->proc.pr_timestamp = 0;
1103 indev->proc.longpr_rep_timestamp = 0;
1104 indev->proc.types.pointer.drag_sum.x = 0;
1105 indev->proc.types.pointer.drag_sum.y = 0;
1106 indev->proc.types.pointer.drag_dir = LV_DRAG_DIR_BOTH;
1107 indev->proc.types.pointer.drag_throw_vect.x = 0;
1108 indev->proc.types.pointer.drag_throw_vect.y = 0;
1109 indev->proc.types.pointer.gesture_sum.x = 0;
1110 indev->proc.types.pointer.gesture_sum.y = 0;
1111 indev->proc.reset_query = 0;
1112 indev_obj_act = NULL;
1113 }
1114 }
1115 /**
1116 * Search the most top, clickable object by a point
1117 * @param obj pointer to a start object, typically the screen
1118 * @param point pointer to a point for searching the most top child
1119 * @return pointer to the found object or NULL if there was no suitable object
1120 */
lv_indev_search_obj(lv_obj_t * obj,lv_point_t * point)1121 lv_obj_t * lv_indev_search_obj(lv_obj_t * obj, lv_point_t * point)
1122 {
1123 lv_obj_t * found_p = NULL;
1124
1125 /*If the point is on this object check its children too*/
1126 if(lv_obj_hittest(obj, point)) {
1127 lv_obj_t * i;
1128
1129 _LV_LL_READ(obj->child_ll, i) {
1130 found_p = lv_indev_search_obj(i, point);
1131
1132 /*If a child was found then break*/
1133 if(found_p != NULL) {
1134 break;
1135 }
1136 }
1137
1138 /*If then the children was not ok, and this obj is clickable
1139 * and it or its parent is not hidden then save this object*/
1140 if(found_p == NULL && lv_obj_get_click(obj) != false) {
1141 lv_obj_t * hidden_i = obj;
1142 while(hidden_i != NULL) {
1143 if(lv_obj_get_hidden(hidden_i) == true) break;
1144 hidden_i = lv_obj_get_parent(hidden_i);
1145 }
1146 /*No parent found with hidden == true*/
1147 if(hidden_i == NULL) found_p = obj;
1148 }
1149 }
1150
1151 return found_p;
1152 }
1153
1154 /**
1155 * Handle focus/defocus on click for POINTER input devices
1156 * @param proc pointer to the state of the indev
1157 */
indev_click_focus(lv_indev_proc_t * proc)1158 static void indev_click_focus(lv_indev_proc_t * proc)
1159 {
1160 /*Handle click focus*/
1161 lv_obj_t * obj_to_focus = lv_obj_get_focused_obj(indev_obj_act);
1162 if(lv_obj_is_protected(indev_obj_act, LV_PROTECT_CLICK_FOCUS) == false &&
1163 proc->types.pointer.last_pressed != obj_to_focus) {
1164 #if LV_USE_GROUP
1165 lv_group_t * g_act = lv_obj_get_group(obj_to_focus);
1166 lv_group_t * g_prev = proc->types.pointer.last_pressed ? lv_obj_get_group(proc->types.pointer.last_pressed) : NULL;
1167
1168 /*If both the last and act. obj. are in the same group (or no group but it's also the same) */
1169 if(g_act == g_prev) {
1170 /*The objects are in a group*/
1171 if(g_act) {
1172 lv_group_focus_obj(obj_to_focus);
1173 if(indev_reset_check(proc)) return;
1174 }
1175 /*The object are not in group*/
1176 else {
1177 if(proc->types.pointer.last_pressed) {
1178 lv_signal_send(proc->types.pointer.last_pressed, LV_SIGNAL_DEFOCUS, NULL);
1179 if(indev_reset_check(proc)) return;
1180 lv_event_send(proc->types.pointer.last_pressed, LV_EVENT_DEFOCUSED, NULL);
1181 if(indev_reset_check(proc)) return;
1182 }
1183
1184 lv_signal_send(obj_to_focus, LV_SIGNAL_FOCUS, NULL);
1185 if(indev_reset_check(proc)) return;
1186 lv_event_send(obj_to_focus, LV_EVENT_FOCUSED, NULL);
1187 if(indev_reset_check(proc)) return;
1188 }
1189 }
1190 /*The object are not in the same group (in different group or one in not a group)*/
1191 else {
1192 /*If the prev. obj. is not in a group then defocus it.*/
1193 if(g_prev == NULL && proc->types.pointer.last_pressed) {
1194 lv_signal_send(proc->types.pointer.last_pressed, LV_SIGNAL_DEFOCUS, NULL);
1195 if(indev_reset_check(proc)) return;
1196 lv_event_send(proc->types.pointer.last_pressed, LV_EVENT_DEFOCUSED, NULL);
1197 if(indev_reset_check(proc)) return;
1198 }
1199 /*Focus on a non-group object*/
1200 else {
1201 if(proc->types.pointer.last_pressed) {
1202 /*If the prev. object also wasn't in a group defocus it*/
1203 if(g_prev == NULL) {
1204 lv_signal_send(proc->types.pointer.last_pressed, LV_SIGNAL_DEFOCUS, NULL);
1205 if(indev_reset_check(proc)) return;
1206 lv_event_send(proc->types.pointer.last_pressed, LV_EVENT_DEFOCUSED, NULL);
1207 if(indev_reset_check(proc)) return;
1208 }
1209 /*If the prev. object also was in a group at least "LEAVE" it instead of defocus*/
1210 else {
1211 lv_signal_send(proc->types.pointer.last_pressed, LV_SIGNAL_LEAVE, NULL);
1212 if(indev_reset_check(proc)) return;
1213 lv_event_send(proc->types.pointer.last_pressed, LV_EVENT_LEAVE, NULL);
1214 if(indev_reset_check(proc)) return;
1215 }
1216 }
1217 }
1218
1219 /*Focus to the act. in its group*/
1220 if(g_act) {
1221 lv_group_focus_obj(obj_to_focus);
1222 if(indev_reset_check(proc)) return;
1223 }
1224 else {
1225 lv_signal_send(obj_to_focus, LV_SIGNAL_FOCUS, NULL);
1226 if(indev_reset_check(proc)) return;
1227 lv_event_send(obj_to_focus, LV_EVENT_FOCUSED, NULL);
1228 if(indev_reset_check(proc)) return;
1229 }
1230 }
1231 #else
1232 if(proc->types.pointer.last_pressed) {
1233 lv_signal_send(proc->types.pointer.last_pressed, LV_SIGNAL_DEFOCUS, NULL);
1234 if(indev_reset_check(proc)) return;
1235 lv_event_send(proc->types.pointer.last_pressed, LV_EVENT_DEFOCUSED, NULL);
1236 if(indev_reset_check(proc)) return;
1237 }
1238
1239 lv_signal_send(obj_to_focus, LV_SIGNAL_FOCUS, NULL);
1240 if(indev_reset_check(proc)) return;
1241 lv_event_send(obj_to_focus, LV_EVENT_FOCUSED, NULL);
1242 if(indev_reset_check(proc)) return;
1243 #endif
1244 proc->types.pointer.last_pressed = obj_to_focus;
1245 }
1246
1247 }
1248
1249 /**
1250 * Handle the dragging of indev_proc_p->types.pointer.act_obj
1251 * @param indev pointer to a input device state
1252 */
indev_drag(lv_indev_proc_t * proc)1253 static void indev_drag(lv_indev_proc_t * proc)
1254 {
1255 lv_obj_t * drag_obj = get_dragged_obj(proc->types.pointer.act_obj);
1256 bool drag_just_started = false;
1257
1258 if(drag_obj == NULL) return;
1259
1260 if(lv_obj_get_drag(drag_obj) == false) return;
1261
1262
1263 lv_drag_dir_t allowed_dirs = lv_obj_get_drag_dir(drag_obj);
1264
1265 /*Count the movement by drag*/
1266 if(proc->types.pointer.drag_limit_out == 0) {
1267 proc->types.pointer.drag_sum.x += proc->types.pointer.vect.x;
1268 proc->types.pointer.drag_sum.y += proc->types.pointer.vect.y;
1269
1270 /*Enough move?*/
1271 bool hor_en = false;
1272 bool ver_en = false;
1273 if(allowed_dirs == LV_DRAG_DIR_HOR || allowed_dirs == LV_DRAG_DIR_BOTH) {
1274 hor_en = true;
1275 }
1276
1277 if(allowed_dirs == LV_DRAG_DIR_VER || allowed_dirs == LV_DRAG_DIR_BOTH) {
1278 ver_en = true;
1279 }
1280
1281 if(allowed_dirs == LV_DRAG_DIR_ONE) {
1282 if(LV_MATH_ABS(proc->types.pointer.drag_sum.x) > LV_MATH_ABS(proc->types.pointer.drag_sum.y)) {
1283 hor_en = true;
1284 }
1285 else {
1286 ver_en = true;
1287 }
1288 }
1289
1290 /*If a move is greater then LV_DRAG_LIMIT then begin the drag*/
1291 if((hor_en && LV_MATH_ABS(proc->types.pointer.drag_sum.x) >= indev_act->driver.drag_limit) ||
1292 (ver_en && LV_MATH_ABS(proc->types.pointer.drag_sum.y) >= indev_act->driver.drag_limit)) {
1293 proc->types.pointer.drag_limit_out = 1;
1294 drag_just_started = true;
1295 }
1296 }
1297
1298 /*If the drag limit is exceeded handle the dragging*/
1299 if(proc->types.pointer.drag_limit_out != 0) {
1300 /*Set new position if the vector is not zero*/
1301 if(proc->types.pointer.vect.x != 0 || proc->types.pointer.vect.y != 0) {
1302
1303 lv_coord_t prev_x = drag_obj->coords.x1;
1304 lv_coord_t prev_y = drag_obj->coords.y1;
1305 lv_coord_t prev_par_w = lv_obj_get_width(lv_obj_get_parent(drag_obj));
1306 lv_coord_t prev_par_h = lv_obj_get_height(lv_obj_get_parent(drag_obj));
1307
1308 /*Get the coordinates of the object and modify them*/
1309 lv_coord_t act_x = lv_obj_get_x(drag_obj);
1310 lv_coord_t act_y = lv_obj_get_y(drag_obj);
1311
1312 if(allowed_dirs == LV_DRAG_DIR_BOTH) {
1313 if(drag_just_started) {
1314 proc->types.pointer.drag_dir = LV_DRAG_DIR_BOTH;
1315 act_x += proc->types.pointer.drag_sum.x;
1316 act_y += proc->types.pointer.drag_sum.y;
1317 }
1318 }
1319 else if(allowed_dirs == LV_DRAG_DIR_HOR) {
1320 if(drag_just_started) {
1321 proc->types.pointer.drag_dir = LV_DRAG_DIR_HOR;
1322 proc->types.pointer.drag_sum.y = 0;
1323 act_x += proc->types.pointer.drag_sum.x;
1324 }
1325 }
1326 else if(allowed_dirs == LV_DRAG_DIR_VER) {
1327 if(drag_just_started) {
1328 proc->types.pointer.drag_dir = LV_DRAG_DIR_VER;
1329 proc->types.pointer.drag_sum.x = 0;
1330 act_y += proc->types.pointer.drag_sum.y;
1331 }
1332 }
1333 else if(allowed_dirs == LV_DRAG_DIR_ONE) {
1334 if(drag_just_started) {
1335 if(LV_MATH_ABS(proc->types.pointer.drag_sum.x) > LV_MATH_ABS(proc->types.pointer.drag_sum.y)) {
1336 proc->types.pointer.drag_dir = LV_DRAG_DIR_HOR;
1337 proc->types.pointer.drag_sum.y = 0;
1338 act_x += proc->types.pointer.drag_sum.x;
1339 }
1340 else {
1341 proc->types.pointer.drag_dir = LV_DRAG_DIR_VER;
1342 proc->types.pointer.drag_sum.x = 0;
1343 act_y += proc->types.pointer.drag_sum.y;
1344 }
1345 }
1346 }
1347
1348 /*Move the object*/
1349 if(allowed_dirs == LV_DRAG_DIR_HOR ||
1350 allowed_dirs == LV_DRAG_DIR_BOTH ||
1351 (allowed_dirs == LV_DRAG_DIR_ONE &&
1352 LV_MATH_ABS(proc->types.pointer.drag_sum.x) > LV_MATH_ABS(proc->types.pointer.drag_sum.y))) {
1353 act_x += proc->types.pointer.vect.x;
1354 }
1355 if(allowed_dirs == LV_DRAG_DIR_VER ||
1356 allowed_dirs == LV_DRAG_DIR_BOTH ||
1357 (allowed_dirs == LV_DRAG_DIR_ONE &&
1358 LV_MATH_ABS(proc->types.pointer.drag_sum.x) < LV_MATH_ABS(proc->types.pointer.drag_sum.y))) {
1359 act_y += proc->types.pointer.vect.y;
1360 }
1361
1362 uint16_t inv_buf_size =
1363 lv_disp_get_inv_buf_size(indev_act->driver.disp); /*Get the number of currently invalidated areas*/
1364
1365 lv_obj_set_pos(drag_obj, act_x, act_y);
1366 proc->types.pointer.drag_in_prog = 1;
1367
1368 /*If the object didn't moved then clear the invalidated areas*/
1369 if(drag_obj->coords.x1 == prev_x && drag_obj->coords.y1 == prev_y) {
1370 /*In a special case if the object is moved on a page and
1371 * the scrollable has fit == true and the object is dragged of the page then
1372 * while its coordinate is not changing only the parent's size is reduced */
1373 lv_coord_t act_par_w = lv_obj_get_width(lv_obj_get_parent(drag_obj));
1374 lv_coord_t act_par_h = lv_obj_get_height(lv_obj_get_parent(drag_obj));
1375 if(act_par_w == prev_par_w && act_par_h == prev_par_h) {
1376 uint16_t new_inv_buf_size = lv_disp_get_inv_buf_size(indev_act->driver.disp);
1377 _lv_disp_pop_from_inv_buf(indev_act->driver.disp, new_inv_buf_size - inv_buf_size);
1378 }
1379 }
1380
1381 /*Set the drag in progress flag*/
1382 /*Send the drag begin signal on first move*/
1383 if(drag_just_started) {
1384 drag_obj->signal_cb(drag_obj, LV_SIGNAL_DRAG_BEGIN, indev_act);
1385 if(indev_reset_check(proc)) return;
1386
1387 lv_event_send(drag_obj, LV_EVENT_DRAG_BEGIN, NULL);
1388 if(indev_reset_check(proc)) return;
1389 }
1390
1391 }
1392 }
1393 }
1394
1395 /**
1396 * Handle throwing by drag if the drag is ended
1397 * @param indev pointer to an input device state
1398 */
indev_drag_throw(lv_indev_proc_t * proc)1399 static void indev_drag_throw(lv_indev_proc_t * proc)
1400 {
1401 if(proc->types.pointer.drag_in_prog == 0) return;
1402
1403 lv_obj_t * drag_obj = get_dragged_obj(proc->types.pointer.last_obj);
1404
1405 if(drag_obj == NULL) return;
1406
1407 /*Return if the drag throw is not enabled*/
1408 if(lv_obj_get_drag_throw(drag_obj) == false) {
1409 proc->types.pointer.drag_in_prog = 0;
1410 drag_obj->signal_cb(drag_obj, LV_SIGNAL_DRAG_END, indev_act);
1411 if(indev_reset_check(proc)) return;
1412
1413 lv_event_send(drag_obj, LV_EVENT_DRAG_END, NULL);
1414 return;
1415 }
1416
1417 lv_drag_dir_t allowed_dirs = lv_obj_get_drag_dir(drag_obj);
1418
1419 /*Reduce the vectors*/
1420 proc->types.pointer.drag_throw_vect.x =
1421 proc->types.pointer.drag_throw_vect.x * (100 - indev_act->driver.drag_throw) / 100;
1422 proc->types.pointer.drag_throw_vect.y =
1423 proc->types.pointer.drag_throw_vect.y * (100 - indev_act->driver.drag_throw) / 100;
1424
1425 if(proc->types.pointer.drag_throw_vect.x != 0 || proc->types.pointer.drag_throw_vect.y != 0) {
1426 /*Get the coordinates and modify them*/
1427 lv_area_t coords_ori;
1428 lv_obj_get_coords(drag_obj, &coords_ori);
1429 lv_coord_t act_x = lv_obj_get_x(drag_obj) + proc->types.pointer.drag_throw_vect.x;
1430 lv_coord_t act_y = lv_obj_get_y(drag_obj) + proc->types.pointer.drag_throw_vect.y;
1431
1432 if(allowed_dirs == LV_DRAG_DIR_BOTH) lv_obj_set_pos(drag_obj, act_x, act_y);
1433 else if(allowed_dirs == LV_DRAG_DIR_HOR) lv_obj_set_x(drag_obj, act_x);
1434 else if(allowed_dirs == LV_DRAG_DIR_VER) lv_obj_set_y(drag_obj, act_y);
1435 else if(allowed_dirs == LV_DRAG_DIR_ONE) {
1436 if(proc->types.pointer.drag_sum.x) lv_obj_set_x(drag_obj, act_x);
1437 else lv_obj_set_y(drag_obj, act_y);
1438 }
1439 lv_area_t coord_new;
1440 lv_obj_get_coords(drag_obj, &coord_new);
1441
1442 /*If non of the coordinates are changed then do not continue throwing*/
1443 if((coords_ori.x1 == coord_new.x1 || proc->types.pointer.drag_throw_vect.x == 0) &&
1444 (coords_ori.y1 == coord_new.y1 || proc->types.pointer.drag_throw_vect.y == 0)) {
1445 proc->types.pointer.drag_in_prog = 0;
1446 proc->types.pointer.vect.x = 0;
1447 proc->types.pointer.vect.y = 0;
1448 proc->types.pointer.drag_throw_vect.x = 0;
1449 proc->types.pointer.drag_throw_vect.y = 0;
1450 drag_obj->signal_cb(drag_obj, LV_SIGNAL_DRAG_END, indev_act);
1451 if(indev_reset_check(proc)) return;
1452
1453 lv_event_send(drag_obj, LV_EVENT_DRAG_END, NULL);
1454 if(indev_reset_check(proc)) return;
1455 }
1456 }
1457 /*If the types.pointer.vectors become 0 -> types.pointer.drag_in_prog = 0 and send a drag end
1458 signal*/
1459 else {
1460 proc->types.pointer.drag_in_prog = 0;
1461 drag_obj->signal_cb(drag_obj, LV_SIGNAL_DRAG_END, indev_act);
1462 if(indev_reset_check(proc)) return;
1463 lv_event_send(drag_obj, LV_EVENT_DRAG_END, NULL);
1464 if(indev_reset_check(proc)) return;
1465 }
1466 }
1467
1468
1469 /**
1470 * Get the really dragged object by taking `drag_parent` into account.
1471 * @param obj the start object
1472 * @return the object to really drag
1473 */
get_dragged_obj(lv_obj_t * obj)1474 static lv_obj_t * get_dragged_obj(lv_obj_t * obj)
1475 {
1476 if(obj == NULL) return NULL;
1477 lv_obj_t * drag_obj = obj;
1478 while(lv_obj_get_drag_parent(drag_obj) != false && drag_obj != NULL) {
1479 drag_obj = lv_obj_get_parent(drag_obj);
1480 }
1481
1482 return drag_obj;
1483 }
1484
1485
1486 /**
1487 * Handle the gesture of indev_proc_p->types.pointer.act_obj
1488 * @param indev pointer to a input device state
1489 */
indev_gesture(lv_indev_proc_t * proc)1490 static void indev_gesture(lv_indev_proc_t * proc)
1491 {
1492
1493 if(proc->types.pointer.gesture_sent) return;
1494
1495 lv_obj_t * gesture_obj = proc->types.pointer.act_obj;
1496
1497 /*If gesture parent is active check recursively the gesture attribute*/
1498 while(gesture_obj && lv_obj_get_gesture_parent(gesture_obj)) {
1499 gesture_obj = lv_obj_get_parent(gesture_obj);
1500 }
1501
1502 if(gesture_obj == NULL) return;
1503
1504
1505 if((LV_MATH_ABS(proc->types.pointer.vect.x) < indev_act->driver.gesture_min_velocity) &&
1506 (LV_MATH_ABS(proc->types.pointer.vect.y) < indev_act->driver.gesture_min_velocity)) {
1507 proc->types.pointer.gesture_sum.x = 0;
1508 proc->types.pointer.gesture_sum.y = 0;
1509 }
1510
1511 /*Count the movement by gesture*/
1512 proc->types.pointer.gesture_sum.x += proc->types.pointer.vect.x;
1513 proc->types.pointer.gesture_sum.y += proc->types.pointer.vect.y;
1514
1515 if((LV_MATH_ABS(proc->types.pointer.gesture_sum.x) > indev_act->driver.gesture_limit) ||
1516 (LV_MATH_ABS(proc->types.pointer.gesture_sum.y) > indev_act->driver.gesture_limit)) {
1517
1518 proc->types.pointer.gesture_sent = 1;
1519
1520 if(LV_MATH_ABS(proc->types.pointer.gesture_sum.x) > LV_MATH_ABS(proc->types.pointer.gesture_sum.y)) {
1521 if(proc->types.pointer.gesture_sum.x > 0)
1522 proc->types.pointer.gesture_dir = LV_GESTURE_DIR_RIGHT;
1523 else
1524 proc->types.pointer.gesture_dir = LV_GESTURE_DIR_LEFT;
1525 }
1526 else {
1527 if(proc->types.pointer.gesture_sum.y > 0)
1528 proc->types.pointer.gesture_dir = LV_GESTURE_DIR_BOTTOM;
1529 else
1530 proc->types.pointer.gesture_dir = LV_GESTURE_DIR_TOP;
1531 }
1532
1533 gesture_obj->signal_cb(gesture_obj, LV_SIGNAL_GESTURE, indev_act);
1534 if(indev_reset_check(proc)) return;
1535 lv_event_send(gesture_obj, LV_EVENT_GESTURE, NULL);
1536 if(indev_reset_check(proc)) return;
1537 }
1538 }
1539
1540
1541 /**
1542 * Checks if the reset_query flag has been set. If so, perform necessary global indev cleanup actions
1543 * @param proc pointer to an input device 'proc'
1544 * @return true if indev query should be immediately truncated.
1545 */
indev_reset_check(lv_indev_proc_t * proc)1546 static bool indev_reset_check(lv_indev_proc_t * proc)
1547 {
1548 if(proc->reset_query) {
1549 indev_obj_act = NULL;
1550 }
1551
1552 return proc->reset_query ? true : false;
1553 }
1554