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