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 lv_obj_t * indev_search_obj(const lv_indev_proc_t * proc, lv_obj_t * obj);
43 static void indev_drag(lv_indev_proc_t * state);
44 static void indev_drag_throw(lv_indev_proc_t * proc);
45 static bool indev_reset_check(lv_indev_proc_t * proc);
46 
47 /**********************
48  *  STATIC VARIABLES
49  **********************/
50 static lv_indev_t * indev_act;
51 static lv_obj_t * indev_obj_act = NULL;
52 
53 /**********************
54  *      MACROS
55  **********************/
56 
57 /**********************
58  *   GLOBAL FUNCTIONS
59  **********************/
60 
61 /**
62  * Initialize the display input device subsystem
63  */
lv_indev_init(void)64 void lv_indev_init(void)
65 {
66     lv_indev_reset(NULL); /*Reset all input devices*/
67 }
68 
69 /**
70  * Called periodically to read the input devices
71  * @param param pointer to and input device to read
72  */
lv_indev_read_task(lv_task_t * task)73 void lv_indev_read_task(lv_task_t * task)
74 {
75     LV_LOG_TRACE("indev read task started");
76 
77     lv_indev_data_t data;
78 
79     indev_act = task->user_data;
80 
81     /*Read and process all indevs*/
82     if(indev_act->driver.disp == NULL) return; /*Not assigned to any displays*/
83 
84     /*Handle reset query before processing the point*/
85     indev_proc_reset_query_handler(indev_act);
86 
87     if(indev_act->proc.disabled) return;
88     bool more_to_read;
89     do {
90         /*Read the data*/
91         more_to_read = lv_indev_read(indev_act, &data);
92 
93         /*The active object might deleted even in the read function*/
94         indev_proc_reset_query_handler(indev_act);
95         indev_obj_act = NULL;
96 
97         indev_act->proc.state = data.state;
98 
99         /*Save the last activity time*/
100         if(indev_act->proc.state == LV_INDEV_STATE_PR) {
101             indev_act->driver.disp->last_activity_time = lv_tick_get();
102         } else if(indev_act->driver.type == LV_INDEV_TYPE_ENCODER && data.enc_diff) {
103             indev_act->driver.disp->last_activity_time = lv_tick_get();
104         }
105 
106         if(indev_act->driver.type == LV_INDEV_TYPE_POINTER) {
107             indev_pointer_proc(indev_act, &data);
108         } else if(indev_act->driver.type == LV_INDEV_TYPE_KEYPAD) {
109             indev_keypad_proc(indev_act, &data);
110         } else if(indev_act->driver.type == LV_INDEV_TYPE_ENCODER) {
111             indev_encoder_proc(indev_act, &data);
112         } else if(indev_act->driver.type == LV_INDEV_TYPE_BUTTON) {
113             indev_button_proc(indev_act, &data);
114         }
115         /*Handle reset query if it happened in during processing*/
116         indev_proc_reset_query_handler(indev_act);
117     } while(more_to_read);
118 
119     /*End of indev processing, so no act indev*/
120     indev_act     = NULL;
121     indev_obj_act = NULL;
122 
123     LV_LOG_TRACE("indev read task finished");
124 }
125 
126 /**
127  * Get the currently processed input device. Can be used in action functions too.
128  * @return pointer to the currently processed input device or NULL if no input device processing
129  * right now
130  */
lv_indev_get_act(void)131 lv_indev_t * lv_indev_get_act(void)
132 {
133     return indev_act;
134 }
135 
136 /**
137  * Get the type of an input device
138  * @param indev pointer to an input device
139  * @return the type of the input device from `lv_hal_indev_type_t` (`LV_INDEV_TYPE_...`)
140  */
lv_indev_get_type(const lv_indev_t * indev)141 lv_indev_type_t lv_indev_get_type(const lv_indev_t * indev)
142 {
143     if(indev == NULL) return LV_INDEV_TYPE_NONE;
144 
145     return indev->driver.type;
146 }
147 /**
148  * Reset one or all input devices
149  * @param indev pointer to an input device to reset or NULL to reset all of them
150  */
lv_indev_reset(lv_indev_t * indev)151 void lv_indev_reset(lv_indev_t * indev)
152 {
153     if(indev)
154         indev->proc.reset_query = 1;
155     else {
156         lv_indev_t * i = lv_indev_get_next(NULL);
157         while(i) {
158             i->proc.reset_query = 1;
159 
160             i = lv_indev_get_next(i);
161         }
162     }
163 }
164 
165 /**
166  * Reset the long press state of an input device
167  * @param indev pointer to an input device
168  */
lv_indev_reset_long_press(lv_indev_t * indev)169 void lv_indev_reset_long_press(lv_indev_t * indev)
170 {
171     indev->proc.long_pr_sent         = 0;
172     indev->proc.longpr_rep_timestamp = lv_tick_get();
173     indev->proc.pr_timestamp         = lv_tick_get();
174 }
175 
176 /**
177  * Enable or disable an input devices
178  * @param indev pointer to an input device
179  * @param en true: enable; false: disable
180  */
lv_indev_enable(lv_indev_t * indev,bool en)181 void lv_indev_enable(lv_indev_t * indev, bool en)
182 {
183     if(!indev) return;
184 
185     indev->proc.disabled = en ? 1 : 0;
186 }
187 
188 /**
189  * Set a cursor for a pointer input device (for LV_INPUT_TYPE_POINTER and LV_INPUT_TYPE_BUTTON)
190  * @param indev pointer to an input device
191  * @param cur_obj pointer to an object to be used as cursor
192  */
lv_indev_set_cursor(lv_indev_t * indev,lv_obj_t * cur_obj)193 void lv_indev_set_cursor(lv_indev_t * indev, lv_obj_t * cur_obj)
194 {
195     if(indev->driver.type != LV_INDEV_TYPE_POINTER) return;
196 
197     indev->cursor = cur_obj;
198     lv_obj_set_parent(indev->cursor, lv_disp_get_layer_sys(indev->driver.disp));
199     lv_obj_set_pos(indev->cursor, indev->proc.types.pointer.act_point.x, indev->proc.types.pointer.act_point.y);
200 }
201 
202 #if LV_USE_GROUP
203 /**
204  * Set a destination group for a keypad input device (for LV_INDEV_TYPE_KEYPAD)
205  * @param indev pointer to an input device
206  * @param group point to a group
207  */
lv_indev_set_group(lv_indev_t * indev,lv_group_t * group)208 void lv_indev_set_group(lv_indev_t * indev, lv_group_t * group)
209 {
210     if(indev->driver.type == LV_INDEV_TYPE_KEYPAD || indev->driver.type == LV_INDEV_TYPE_ENCODER) {
211         indev->group = group;
212     }
213 }
214 #endif
215 
216 /**
217  * Set the an array of points for LV_INDEV_TYPE_BUTTON.
218  * These points will be assigned to the buttons to press a specific point on the screen
219  * @param indev pointer to an input device
220  * @param group point to a group
221  */
lv_indev_set_button_points(lv_indev_t * indev,const lv_point_t * points)222 void lv_indev_set_button_points(lv_indev_t * indev, const lv_point_t * points)
223 {
224     if(indev->driver.type == LV_INDEV_TYPE_BUTTON) {
225         indev->btn_points = points;
226     }
227 }
228 
229 /**
230  * Get the last point of an input device (for LV_INDEV_TYPE_POINTER and LV_INDEV_TYPE_BUTTON)
231  * @param indev pointer to an input device
232  * @param point pointer to a point to store the result
233  */
lv_indev_get_point(const lv_indev_t * indev,lv_point_t * point)234 void lv_indev_get_point(const lv_indev_t * indev, lv_point_t * point)
235 {
236     if(indev->driver.type != LV_INDEV_TYPE_POINTER && indev->driver.type != LV_INDEV_TYPE_BUTTON) {
237         point->x = -1;
238         point->y = -1;
239     } else {
240         point->x = indev->proc.types.pointer.act_point.x;
241         point->y = indev->proc.types.pointer.act_point.y;
242     }
243 }
244 
245 /**
246  * Get the last pressed key of an input device (for LV_INDEV_TYPE_KEYPAD)
247  * @param indev pointer to an input device
248  * @return the last pressed key (0 on error)
249  */
lv_indev_get_key(const lv_indev_t * indev)250 uint32_t lv_indev_get_key(const lv_indev_t * indev)
251 {
252     if(indev->driver.type != LV_INDEV_TYPE_KEYPAD)
253         return 0;
254     else
255         return indev->proc.types.keypad.last_key;
256 }
257 
258 /**
259  * Check if there is dragging with an input device or not (for LV_INDEV_TYPE_POINTER and
260  * LV_INDEV_TYPE_BUTTON)
261  * @param indev pointer to an input device
262  * @return true: drag is in progress
263  */
lv_indev_is_dragging(const lv_indev_t * indev)264 bool lv_indev_is_dragging(const lv_indev_t * indev)
265 {
266     if(indev == NULL) return false;
267     if(indev->driver.type != LV_INDEV_TYPE_POINTER && indev->driver.type != LV_INDEV_TYPE_BUTTON) return false;
268     return indev->proc.types.pointer.drag_in_prog == 0 ? false : true;
269 }
270 
271 /**
272  * Get the types.pointer.vector of dragging of an input device (for LV_INDEV_TYPE_POINTER and
273  * LV_INDEV_TYPE_BUTTON)
274  * @param indev pointer to an input device
275  * @param point pointer to a point to store the types.pointer.vector
276  */
lv_indev_get_vect(const lv_indev_t * indev,lv_point_t * point)277 void lv_indev_get_vect(const lv_indev_t * indev, lv_point_t * point)
278 {
279     if(indev == NULL) {
280         point->x = 0;
281         point->y = 0;
282         return;
283     }
284 
285     if(indev->driver.type != LV_INDEV_TYPE_POINTER && indev->driver.type != LV_INDEV_TYPE_BUTTON) {
286         point->x = 0;
287         point->y = 0;
288     } else {
289         point->x = indev->proc.types.pointer.vect.x;
290         point->y = indev->proc.types.pointer.vect.y;
291     }
292 }
293 
294 /**
295  * Do nothing until the next release
296  * @param indev pointer to an input device
297  */
lv_indev_wait_release(lv_indev_t * indev)298 void lv_indev_wait_release(lv_indev_t * indev)
299 {
300     indev->proc.wait_until_release = 1;
301 }
302 
303 /**
304  * Get a pointer to the indev read task to
305  * modify its parameters with `lv_task_...` functions.
306  * @param indev pointer to an input device
307  * @return pointer to the indev read refresher task. (NULL on error)
308  */
lv_indev_get_read_task(lv_disp_t * indev)309 lv_task_t * lv_indev_get_read_task(lv_disp_t * indev)
310 {
311     if(!indev) {
312         LV_LOG_WARN("lv_indev_get_read_task: indev was NULL");
313         return NULL;
314     }
315 
316     return indev->refr_task;
317 }
318 
319 /**
320  * Gets a pointer to the currently active object in the currently processed input device.
321  * @return pointer to currently active object or NULL if no active object
322  */
lv_indev_get_obj_act(void)323 lv_obj_t * lv_indev_get_obj_act(void)
324 {
325     return indev_obj_act;
326 }
327 
328 /**********************
329  *   STATIC FUNCTIONS
330  **********************/
331 
332 /**
333  * Process a new point from LV_INDEV_TYPE_POINTER input device
334  * @param i pointer to an input device
335  * @param data pointer to the data read from the input device
336  */
indev_pointer_proc(lv_indev_t * i,lv_indev_data_t * data)337 static void indev_pointer_proc(lv_indev_t * i, lv_indev_data_t * data)
338 {
339     /*Move the cursor if set and moved*/
340     if(i->cursor != NULL &&
341        (i->proc.types.pointer.last_point.x != data->point.x || i->proc.types.pointer.last_point.y != data->point.y)) {
342         lv_obj_set_pos(i->cursor, data->point.x, data->point.y);
343     }
344 
345     i->proc.types.pointer.act_point.x = data->point.x;
346     i->proc.types.pointer.act_point.y = data->point.y;
347 
348     if(i->proc.state == LV_INDEV_STATE_PR) {
349         indev_proc_press(&i->proc);
350     } else {
351         indev_proc_release(&i->proc);
352     }
353 
354     i->proc.types.pointer.last_point.x = i->proc.types.pointer.act_point.x;
355     i->proc.types.pointer.last_point.y = i->proc.types.pointer.act_point.y;
356 }
357 
358 /**
359  * Process a new point from LV_INDEV_TYPE_KEYPAD input device
360  * @param i pointer to an input device
361  * @param data pointer to the data read from the input device
362  */
indev_keypad_proc(lv_indev_t * i,lv_indev_data_t * data)363 static void indev_keypad_proc(lv_indev_t * i, lv_indev_data_t * data)
364 {
365 #if LV_USE_GROUP
366     if(data->state == LV_INDEV_STATE_PR && i->proc.wait_until_release) return;
367 
368     if(i->proc.wait_until_release) {
369         i->proc.wait_until_release      = 0;
370         i->proc.pr_timestamp            = 0;
371         i->proc.long_pr_sent            = 0;
372         i->proc.types.keypad.last_state = LV_INDEV_STATE_REL; /*To skip the processing of release*/
373     }
374 
375     lv_group_t * g = i->group;
376     if(g == NULL) return;
377 
378     indev_obj_act = lv_group_get_focused(g);
379     if(indev_obj_act == NULL) return;
380 
381     /*Save the last key to compare it with the current latter on RELEASE*/
382     uint32_t prev_key = i->proc.types.keypad.last_key;
383 
384     /* Save the last key.
385      * It must be done here else `lv_indev_get_key` will return the last key in events and signals*/
386     i->proc.types.keypad.last_key = data->key;
387 
388     /* Save the previous state so we can detect state changes below and also set the last state now
389      * so if any signal/event handler on the way returns `LV_RES_INV` the last state is remembered
390      * for the next time*/
391     uint32_t prev_state             = i->proc.types.keypad.last_state;
392     i->proc.types.keypad.last_state = data->state;
393 
394     /*Key press happened*/
395     if(data->state == LV_INDEV_STATE_PR && prev_state == LV_INDEV_STATE_REL) {
396         i->proc.pr_timestamp = lv_tick_get();
397 
398         /*Simulate a press on the object if ENTER was pressed*/
399         if(data->key == LV_KEY_ENTER) {
400             /*Send the ENTER as a normal KEY*/
401             lv_group_send_data(g, LV_KEY_ENTER);
402 
403             indev_obj_act->signal_cb(indev_obj_act, LV_SIGNAL_PRESSED, NULL);
404             if(indev_reset_check(&i->proc)) return;
405             lv_event_send(indev_obj_act, LV_EVENT_PRESSED, NULL);
406             if(indev_reset_check(&i->proc)) return;
407         } else if(data->key == LV_KEY_ESC) {
408             /*Send the ESC as a normal KEY*/
409             lv_group_send_data(g, LV_KEY_ESC);
410 
411             lv_event_send(indev_obj_act, LV_EVENT_CANCEL, NULL);
412             if(indev_reset_check(&i->proc)) return;
413         }
414         /*Move the focus on NEXT*/
415         else if(data->key == LV_KEY_NEXT) {
416             lv_group_set_editing(g, false); /*Editing is not used by KEYPAD is be sure it is disabled*/
417             lv_group_focus_next(g);
418             if(indev_reset_check(&i->proc)) return;
419         }
420         /*Move the focus on PREV*/
421         else if(data->key == LV_KEY_PREV) {
422             lv_group_set_editing(g, false); /*Editing is not used by KEYPAD is be sure it is disabled*/
423             lv_group_focus_prev(g);
424             if(indev_reset_check(&i->proc)) return;
425         }
426         /*Just send other keys to the object (e.g. 'A' or `LV_GROUP_KEY_RIGHT`)*/
427         else {
428             lv_group_send_data(g, data->key);
429         }
430     }
431     /*Pressing*/
432     else if(data->state == LV_INDEV_STATE_PR && prev_state == LV_INDEV_STATE_PR) {
433         /*Long press time has elapsed?*/
434         if(i->proc.long_pr_sent == 0 && lv_tick_elaps(i->proc.pr_timestamp) > i->driver.long_press_time) {
435             i->proc.long_pr_sent = 1;
436             if(data->key == LV_KEY_ENTER) {
437                 i->proc.longpr_rep_timestamp = lv_tick_get();
438                 indev_obj_act->signal_cb(indev_obj_act, LV_SIGNAL_LONG_PRESS, NULL);
439                 if(indev_reset_check(&i->proc)) return;
440                 lv_event_send(indev_obj_act, LV_EVENT_LONG_PRESSED, NULL);
441                 if(indev_reset_check(&i->proc)) return;
442             }
443         }
444         /*Long press repeated time has elapsed?*/
445         else if(i->proc.long_pr_sent != 0 &&
446                 lv_tick_elaps(i->proc.longpr_rep_timestamp) > i->driver.long_press_rep_time) {
447 
448             i->proc.longpr_rep_timestamp = lv_tick_get();
449 
450             /*Send LONG_PRESS_REP on ENTER*/
451             if(data->key == LV_KEY_ENTER) {
452                 indev_obj_act->signal_cb(indev_obj_act, LV_SIGNAL_LONG_PRESS_REP, NULL);
453                 if(indev_reset_check(&i->proc)) return;
454                 lv_event_send(indev_obj_act, LV_EVENT_LONG_PRESSED_REPEAT, NULL);
455                 if(indev_reset_check(&i->proc)) return;
456             }
457             /*Move the focus on NEXT again*/
458             else if(data->key == LV_KEY_NEXT) {
459                 lv_group_set_editing(g, false); /*Editing is not used by KEYPAD is be sure it is disabled*/
460                 lv_group_focus_next(g);
461                 if(indev_reset_check(&i->proc)) return;
462             }
463             /*Move the focus on PREV again*/
464             else if(data->key == LV_KEY_PREV) {
465                 lv_group_set_editing(g, false); /*Editing is not used by KEYPAD is be sure it is disabled*/
466                 lv_group_focus_prev(g);
467                 if(indev_reset_check(&i->proc)) return;
468             }
469             /*Just send other keys again to the object (e.g. 'A' or `LV_GORUP_KEY_RIGHT)*/
470             else {
471                 lv_group_send_data(g, data->key);
472                 if(indev_reset_check(&i->proc)) return;
473             }
474         }
475     }
476     /*Release happened*/
477     else if(data->state == LV_INDEV_STATE_REL && prev_state == LV_INDEV_STATE_PR) {
478         /*The user might clear the key when it was released. Always release the pressed key*/
479         data->key = prev_key;
480         if(data->key == LV_KEY_ENTER) {
481 
482             indev_obj_act->signal_cb(indev_obj_act, LV_SIGNAL_RELEASED, NULL);
483             if(indev_reset_check(&i->proc)) return;
484 
485             if(i->proc.long_pr_sent == 0) {
486                 lv_event_send(indev_obj_act, LV_EVENT_SHORT_CLICKED, NULL);
487                 if(indev_reset_check(&i->proc)) return;
488             }
489 
490             lv_event_send(indev_obj_act, LV_EVENT_CLICKED, NULL);
491             if(indev_reset_check(&i->proc)) return;
492 
493             lv_event_send(indev_obj_act, LV_EVENT_RELEASED, NULL);
494             if(indev_reset_check(&i->proc)) return;
495         }
496         i->proc.pr_timestamp = 0;
497         i->proc.long_pr_sent = 0;
498     }
499     indev_obj_act = NULL;
500 #else
501     (void)data; /*Unused*/
502     (void)i;    /*Unused*/
503 #endif
504 }
505 
506 /**
507  * Process a new point from LV_INDEV_TYPE_ENCODER input device
508  * @param i pointer to an input device
509  * @param data pointer to the data read from the input device
510  */
indev_encoder_proc(lv_indev_t * i,lv_indev_data_t * data)511 static void indev_encoder_proc(lv_indev_t * i, lv_indev_data_t * data)
512 {
513 #if LV_USE_GROUP
514 
515     if(data->state == LV_INDEV_STATE_PR && i->proc.wait_until_release) return;
516 
517     if(i->proc.wait_until_release) {
518         i->proc.wait_until_release      = 0;
519         i->proc.pr_timestamp            = 0;
520         i->proc.long_pr_sent            = 0;
521         i->proc.types.keypad.last_state = LV_INDEV_STATE_REL; /*To skip the processing of release*/
522     }
523 
524     /* Save the last keys before anything else.
525      * They need to be already saved if the the function returns for any reason*/
526     lv_indev_state_t last_state     = i->proc.types.keypad.last_state;
527     i->proc.types.keypad.last_state = data->state;
528     i->proc.types.keypad.last_key   = data->key;
529 
530     lv_group_t * g = i->group;
531     if(g == NULL) return;
532 
533     indev_obj_act = lv_group_get_focused(g);
534     if(indev_obj_act == NULL) return;
535 
536     /*Process the steps first. They are valid only with released button*/
537     if(data->state == LV_INDEV_STATE_REL) {
538         /*In edit mode send LEFT/RIGHT keys*/
539         if(lv_group_get_editing(g)) {
540             int32_t s;
541             if(data->enc_diff < 0) {
542                 for(s = 0; s < -data->enc_diff; s++) lv_group_send_data(g, LV_KEY_LEFT);
543             } else if(data->enc_diff > 0) {
544                 for(s = 0; s < data->enc_diff; s++) lv_group_send_data(g, LV_KEY_RIGHT);
545             }
546         }
547         /*In navigate mode focus on the next/prev objects*/
548         else {
549             int32_t s;
550             if(data->enc_diff < 0) {
551                 for(s = 0; s < -data->enc_diff; s++) lv_group_focus_prev(g);
552             } else if(data->enc_diff > 0) {
553                 for(s = 0; s < data->enc_diff; s++) lv_group_focus_next(g);
554             }
555         }
556     }
557 
558     /*Refresh the focused object. It might change due to lv_group_focus_prev/next*/
559     indev_obj_act = lv_group_get_focused(g);
560     if(indev_obj_act == NULL) return;
561 
562     /*Button press happened*/
563     if(data->state == LV_INDEV_STATE_PR && last_state == LV_INDEV_STATE_REL) {
564         bool editable = false;
565         indev_obj_act->signal_cb(indev_obj_act, LV_SIGNAL_GET_EDITABLE, &editable);
566 
567         i->proc.pr_timestamp = lv_tick_get();
568         if(lv_group_get_editing(g) == true || editable == false) {
569             indev_obj_act->signal_cb(indev_obj_act, LV_SIGNAL_PRESSED, NULL);
570             if(indev_reset_check(&i->proc)) return;
571 
572             lv_event_send(indev_obj_act, LV_EVENT_PRESSED, NULL);
573             if(indev_reset_check(&i->proc)) return;
574         }
575     }
576     /*Pressing*/
577     else if(data->state == LV_INDEV_STATE_PR && last_state == LV_INDEV_STATE_PR) {
578         if(i->proc.long_pr_sent == 0 && lv_tick_elaps(i->proc.pr_timestamp) > i->driver.long_press_time) {
579             bool editable = false;
580             indev_obj_act->signal_cb(indev_obj_act, LV_SIGNAL_GET_EDITABLE, &editable);
581 
582             /*On enter long press toggle edit mode.*/
583             if(editable) {
584                 /*Don't leave edit mode if there is only one object (nowhere to navigate)*/
585                 if(lv_ll_is_empty(&g->obj_ll) == false) {
586                     lv_group_set_editing(g, lv_group_get_editing(g) ? false : true); /*Toggle edit mode on long press*/
587                 }
588             }
589             /*If not editable then just send a long press signal*/
590             else {
591                 indev_obj_act->signal_cb(indev_obj_act, LV_SIGNAL_LONG_PRESS, NULL);
592                 if(indev_reset_check(&i->proc)) return;
593                 lv_event_send(indev_obj_act, LV_EVENT_LONG_PRESSED, NULL);
594                 if(indev_reset_check(&i->proc)) return;
595             }
596             i->proc.long_pr_sent = 1;
597         }
598     }
599     /*Release happened*/
600     else if(data->state == LV_INDEV_STATE_REL && last_state == LV_INDEV_STATE_PR) {
601 
602         bool editable = false;
603         indev_obj_act->signal_cb(indev_obj_act, LV_SIGNAL_GET_EDITABLE, &editable);
604 
605         /*The button was released on a non-editable object. Just send enter*/
606         if(editable == false) {
607             indev_obj_act->signal_cb(indev_obj_act, LV_SIGNAL_RELEASED, NULL);
608             if(indev_reset_check(&i->proc)) return;
609 
610             if(i->proc.long_pr_sent == 0) lv_event_send(indev_obj_act, LV_EVENT_SHORT_CLICKED, NULL);
611             if(indev_reset_check(&i->proc)) return;
612 
613             lv_event_send(indev_obj_act, LV_EVENT_CLICKED, NULL);
614             if(indev_reset_check(&i->proc)) return;
615 
616             lv_event_send(indev_obj_act, LV_EVENT_RELEASED, NULL);
617             if(indev_reset_check(&i->proc)) return;
618         }
619         /*An object is being edited and the button is released. */
620         else if(g->editing) {
621             /*Ignore long pressed enter release because it comes from mode switch*/
622             if(!i->proc.long_pr_sent || lv_ll_is_empty(&g->obj_ll)) {
623                 indev_obj_act->signal_cb(indev_obj_act, LV_SIGNAL_RELEASED, NULL);
624                 if(indev_reset_check(&i->proc)) return;
625 
626                 lv_event_send(indev_obj_act, LV_EVENT_SHORT_CLICKED, NULL);
627                 if(indev_reset_check(&i->proc)) return;
628 
629                 lv_event_send(indev_obj_act, LV_EVENT_CLICKED, NULL);
630                 if(indev_reset_check(&i->proc)) return;
631 
632                 lv_event_send(indev_obj_act, LV_EVENT_RELEASED, NULL);
633                 if(indev_reset_check(&i->proc)) return;
634 
635                 lv_group_send_data(g, LV_KEY_ENTER);
636             }
637         }
638         /*If the focused object is editable and now in navigate mode then on enter switch edit
639            mode*/
640         else if(editable && !g->editing && !i->proc.long_pr_sent) {
641             lv_group_set_editing(g, true); /*Set edit mode*/
642         }
643 
644         i->proc.pr_timestamp = 0;
645         i->proc.long_pr_sent = 0;
646     }
647     indev_obj_act = NULL;
648 #else
649     (void)data; /*Unused*/
650     (void)i;    /*Unused*/
651 #endif
652 }
653 
654 /**
655  * Process new points from a input device. indev->state.pressed has to be set
656  * @param indev pointer to an input device state
657  * @param x x coordinate of the next point
658  * @param y y coordinate of the next point
659  */
indev_button_proc(lv_indev_t * i,lv_indev_data_t * data)660 static void indev_button_proc(lv_indev_t * i, lv_indev_data_t * data)
661 {
662     i->proc.types.pointer.act_point.x = i->btn_points[data->btn_id].x;
663     i->proc.types.pointer.act_point.y = i->btn_points[data->btn_id].y;
664 
665     /*Still the same point is pressed*/
666     if(i->proc.types.pointer.last_point.x == i->proc.types.pointer.act_point.x &&
667        i->proc.types.pointer.last_point.y == i->proc.types.pointer.act_point.y && data->state == LV_INDEV_STATE_PR) {
668         indev_proc_press(&i->proc);
669     } else {
670         /*If a new point comes always make a release*/
671         indev_proc_release(&i->proc);
672     }
673 
674     i->proc.types.pointer.last_point.x = i->proc.types.pointer.act_point.x;
675     i->proc.types.pointer.last_point.y = i->proc.types.pointer.act_point.y;
676 }
677 
678 /**
679  * Process the pressed state of LV_INDEV_TYPE_POINER input devices
680  * @param indev pointer to an input device 'proc'
681  * @return LV_RES_OK: no indev reset required; LV_RES_INV: indev reset is required
682  */
indev_proc_press(lv_indev_proc_t * proc)683 static void indev_proc_press(lv_indev_proc_t * proc)
684 {
685     indev_obj_act = proc->types.pointer.act_obj;
686 
687     if(proc->wait_until_release != 0) return;
688 
689     lv_disp_t * disp = indev_act->driver.disp;
690 
691     /*If there is no last object then search*/
692     if(indev_obj_act == NULL) {
693         indev_obj_act = indev_search_obj(proc, lv_disp_get_layer_sys(disp));
694         if(indev_obj_act == NULL) indev_obj_act = indev_search_obj(proc, lv_disp_get_layer_top(disp));
695         if(indev_obj_act == NULL) indev_obj_act = indev_search_obj(proc, lv_disp_get_scr_act(disp));
696     }
697     /*If there is last object but it is not dragged and not protected also search*/
698     else if(proc->types.pointer.drag_in_prog == 0 &&
699             lv_obj_is_protected(indev_obj_act, LV_PROTECT_PRESS_LOST) == false) {
700         indev_obj_act = indev_search_obj(proc, lv_disp_get_layer_sys(disp));
701         if(indev_obj_act == NULL) indev_obj_act = indev_search_obj(proc, lv_disp_get_layer_top(disp));
702         if(indev_obj_act == NULL) indev_obj_act = indev_search_obj(proc, lv_disp_get_scr_act(disp));
703     }
704     /*If a dragable or a protected object was the last then keep it*/
705     else {
706     }
707 
708     /*If a new object was found reset some variables and send a pressed signal*/
709     if(indev_obj_act != proc->types.pointer.act_obj) {
710 
711         proc->types.pointer.last_point.x = proc->types.pointer.act_point.x;
712         proc->types.pointer.last_point.y = proc->types.pointer.act_point.y;
713 
714         /*If a new object found the previous was lost, so send a signal*/
715         if(proc->types.pointer.act_obj != NULL) {
716             /*Save the obj because in special cases `act_obj` can change in the signal function*/
717             lv_obj_t * last_obj = proc->types.pointer.act_obj;
718 
719             last_obj->signal_cb(last_obj, LV_SIGNAL_PRESS_LOST, indev_act);
720             if(indev_reset_check(proc)) return;
721             lv_event_send(last_obj, LV_EVENT_PRESS_LOST, NULL);
722             if(indev_reset_check(proc)) return;
723         }
724 
725         proc->types.pointer.act_obj  = indev_obj_act; /*Save the pressed object*/
726         proc->types.pointer.last_obj = indev_obj_act;
727 
728         if(indev_obj_act != NULL) {
729             /* Save the time when the obj pressed.
730              * It is necessary to count the long press time.*/
731             proc->pr_timestamp                 = lv_tick_get();
732             proc->long_pr_sent                 = 0;
733             proc->types.pointer.drag_limit_out = 0;
734             proc->types.pointer.drag_in_prog   = 0;
735             proc->types.pointer.drag_sum.x     = 0;
736             proc->types.pointer.drag_sum.y     = 0;
737             proc->types.pointer.vect.x         = 0;
738             proc->types.pointer.vect.y         = 0;
739 
740             /*Search for 'top' attribute*/
741             lv_obj_t * i        = indev_obj_act;
742             lv_obj_t * last_top = NULL;
743             while(i != NULL) {
744                 if(i->top) last_top = i;
745                 i = lv_obj_get_parent(i);
746             }
747 
748             if(last_top != NULL) {
749                 /*Move the last_top object to the foreground*/
750                 lv_obj_move_foreground(last_top);
751             }
752 
753             /*Send a signal about the press*/
754             indev_obj_act->signal_cb(indev_obj_act, LV_SIGNAL_PRESSED, indev_act);
755             if(indev_reset_check(proc)) return;
756 
757             lv_event_send(indev_obj_act, LV_EVENT_PRESSED, NULL);
758             if(indev_reset_check(proc)) return;
759         }
760     }
761 
762     /*Calculate the types.pointer.vector*/
763     proc->types.pointer.vect.x = proc->types.pointer.act_point.x - proc->types.pointer.last_point.x;
764     proc->types.pointer.vect.y = proc->types.pointer.act_point.y - proc->types.pointer.last_point.y;
765 
766     proc->types.pointer.drag_throw_vect.x = (proc->types.pointer.drag_throw_vect.x * 5) >> 3;
767     proc->types.pointer.drag_throw_vect.y = (proc->types.pointer.drag_throw_vect.y * 5) >> 3;
768 
769     if(proc->types.pointer.drag_throw_vect.x < 0)
770         proc->types.pointer.drag_throw_vect.x++;
771     else if(proc->types.pointer.drag_throw_vect.x > 0)
772         proc->types.pointer.drag_throw_vect.x--;
773 
774     if(proc->types.pointer.drag_throw_vect.y < 0)
775         proc->types.pointer.drag_throw_vect.y++;
776     else if(proc->types.pointer.drag_throw_vect.y > 0)
777         proc->types.pointer.drag_throw_vect.y--;
778 
779     proc->types.pointer.drag_throw_vect.x += (proc->types.pointer.vect.x * 4) >> 3;
780     proc->types.pointer.drag_throw_vect.y += (proc->types.pointer.vect.y * 4) >> 3;
781 
782     /*If there is active object and it can be dragged run the drag*/
783     if(indev_obj_act != NULL) {
784         indev_obj_act->signal_cb(indev_obj_act, LV_SIGNAL_PRESSING, indev_act);
785         if(indev_reset_check(proc)) return;
786         lv_event_send(indev_obj_act, LV_EVENT_PRESSING, NULL);
787         if(indev_reset_check(proc)) return;
788 
789         indev_drag(proc);
790         if(indev_reset_check(proc)) return;
791 
792         /*If there is no drag then check for long press time*/
793         if(proc->types.pointer.drag_in_prog == 0 && proc->long_pr_sent == 0) {
794             /*Send a signal about the long press if enough time elapsed*/
795             if(lv_tick_elaps(proc->pr_timestamp) > indev_act->driver.long_press_time) {
796                 indev_obj_act->signal_cb(indev_obj_act, LV_SIGNAL_LONG_PRESS, indev_act);
797                 if(indev_reset_check(proc)) return;
798                 lv_event_send(indev_obj_act, LV_EVENT_LONG_PRESSED, NULL);
799                 if(indev_reset_check(proc)) return;
800 
801                 /*Mark the signal sending to do not send it again*/
802                 proc->long_pr_sent = 1;
803 
804                 /*Save the long press time stamp for the long press repeat handler*/
805                 proc->longpr_rep_timestamp = lv_tick_get();
806             }
807         }
808         /*Send long press repeated signal*/
809         if(proc->types.pointer.drag_in_prog == 0 && proc->long_pr_sent == 1) {
810             /*Send a signal about the long press repeat if enough time elapsed*/
811             if(lv_tick_elaps(proc->longpr_rep_timestamp) > indev_act->driver.long_press_rep_time) {
812                 indev_obj_act->signal_cb(indev_obj_act, LV_SIGNAL_LONG_PRESS_REP, indev_act);
813                 if(indev_reset_check(proc)) return;
814                 lv_event_send(indev_obj_act, LV_EVENT_LONG_PRESSED_REPEAT, NULL);
815                 if(indev_reset_check(proc)) return;
816                 proc->longpr_rep_timestamp = lv_tick_get();
817             }
818         }
819     }
820 }
821 
822 /**
823  * Process the released state of LV_INDEV_TYPE_POINER input devices
824  * @param proc pointer to an input device 'proc'
825  */
indev_proc_release(lv_indev_proc_t * proc)826 static void indev_proc_release(lv_indev_proc_t * proc)
827 {
828     if(proc->wait_until_release != 0) {
829         proc->types.pointer.act_obj  = NULL;
830         proc->types.pointer.last_obj = NULL;
831         proc->pr_timestamp           = 0;
832         proc->longpr_rep_timestamp   = 0;
833         proc->wait_until_release     = 0;
834     }
835     indev_obj_act = proc->types.pointer.act_obj;
836 
837     /*Forget the act obj and send a released signal */
838     if(indev_obj_act) {
839         /* If the object was protected against press lost then it possible that
840          * the object is already not pressed but still it is the `act_obj`.
841          * In this case send the `LV_SIGNAL_RELEASED/CLICKED` instead of `LV_SIGNAL_PRESS_LOST` if
842          * the indev is ON the `types.pointer.act_obj` */
843         if(lv_obj_is_protected(indev_obj_act, LV_PROTECT_PRESS_LOST)) {
844             indev_obj_act->signal_cb(indev_obj_act, LV_SIGNAL_RELEASED, indev_act);
845             if(indev_reset_check(proc)) return;
846 
847             if(proc->types.pointer.drag_in_prog == 0) {
848                 if(proc->long_pr_sent == 0) {
849                     lv_event_send(indev_obj_act, LV_EVENT_SHORT_CLICKED, NULL);
850                     if(indev_reset_check(proc)) return;
851                 }
852 
853                 lv_event_send(indev_obj_act, LV_EVENT_CLICKED, NULL);
854                 if(indev_reset_check(proc)) return;
855             }
856 
857             lv_event_send(indev_obj_act, LV_EVENT_RELEASED, NULL);
858             if(indev_reset_check(proc)) return;
859         }
860         /* The simple case: `act_obj` was not protected against press lost.
861          * If it is already not pressed then `indev_proc_press` would set `indev_obj_act = NULL`*/
862         else {
863             indev_obj_act->signal_cb(indev_obj_act, LV_SIGNAL_RELEASED, indev_act);
864             if(indev_reset_check(proc)) return;
865 
866             if(proc->long_pr_sent == 0 && proc->types.pointer.drag_in_prog == 0) {
867                 lv_event_send(indev_obj_act, LV_EVENT_SHORT_CLICKED, NULL);
868                 if(indev_reset_check(proc)) return;
869             }
870 
871             if(proc->types.pointer.drag_in_prog == 0) {
872                 lv_event_send(indev_obj_act, LV_EVENT_CLICKED, NULL);
873                 if(indev_reset_check(proc)) return;
874             }
875 
876             lv_event_send(indev_obj_act, LV_EVENT_RELEASED, NULL);
877             if(indev_reset_check(proc)) return;
878         }
879 
880         if(indev_reset_check(proc)) return;
881 
882         /*Handle click focus*/
883         bool click_focus_sent = false;
884 #if LV_USE_GROUP
885         lv_group_t * g = lv_obj_get_group(indev_obj_act);
886 
887         /*Check, if the parent is in a group and focus on it.*/
888         /*Respect the click focus protection*/
889         if(lv_obj_is_protected(indev_obj_act, LV_PROTECT_CLICK_FOCUS) == false) {
890             lv_obj_t * parent = indev_obj_act;
891 
892             while(g == NULL) {
893                 parent = lv_obj_get_parent(parent);
894                 if(parent == NULL) break;
895 
896                 /*Ignore is the protected against click focus*/
897                 if(lv_obj_is_protected(parent, LV_PROTECT_CLICK_FOCUS)) {
898                     parent = NULL;
899                     break;
900                 }
901                 g = lv_obj_get_group(parent);
902             }
903 
904             /* If a parent is in a group make it focused.
905              * `LV_EVENT_FOCUSED/DEFOCUSED` will be sent by `lv_group_focus_obj`*/
906             if(g && parent) {
907                 if(lv_group_get_click_focus(g)) {
908                     click_focus_sent = true;
909                     lv_group_focus_obj(parent);
910                 }
911             }
912         }
913 #endif
914 
915         /* Send defocus to the lastly "active" object and foucus to the new one.
916          * DO not sent the events if they was sent by the click focus*/
917         if(proc->types.pointer.last_pressed != indev_obj_act && click_focus_sent == false) {
918             lv_event_send(proc->types.pointer.last_pressed, LV_EVENT_DEFOCUSED, NULL);
919             if(indev_reset_check(proc)) return;
920 
921             lv_event_send(proc->types.pointer.act_obj, LV_EVENT_FOCUSED, NULL);
922             if(indev_reset_check(proc)) return;
923 
924             proc->types.pointer.last_pressed = indev_obj_act;
925         }
926 
927         if(indev_reset_check(proc)) return;
928 
929         /*Send LV_EVENT_DRAG_THROW_BEGIN if required */
930         /*If drag parent is active check recursively the drag_parent attribute*/
931         lv_obj_t * drag_obj = indev_obj_act;
932         while(lv_obj_get_drag_parent(drag_obj) != false && drag_obj != NULL) {
933             drag_obj = lv_obj_get_parent(drag_obj);
934         }
935 
936         if(drag_obj) {
937             if(lv_obj_get_drag_throw(drag_obj) && proc->types.pointer.drag_in_prog) {
938                 lv_event_send(drag_obj, LV_EVENT_DRAG_THROW_BEGIN, NULL);
939                 if(indev_reset_check(proc)) return;
940             }
941         }
942 
943         proc->types.pointer.act_obj = NULL;
944         proc->pr_timestamp          = 0;
945         proc->longpr_rep_timestamp  = 0;
946     }
947 
948     /*The reset can be set in the signal function.
949      * In case of reset query ignore the remaining parts.*/
950     if(proc->types.pointer.last_obj != NULL && proc->reset_query == 0) {
951         indev_drag_throw(proc);
952         if(indev_reset_check(proc)) return;
953     }
954 }
955 
956 /**
957  * Process a new point from LV_INDEV_TYPE_BUTTON input device
958  * @param i pointer to an input device
959  * @param data pointer to the data read from the input device
960  * Reset input device if a reset query has been sent to it
961  * @param indev pointer to an input device
962  */
indev_proc_reset_query_handler(lv_indev_t * indev)963 static void indev_proc_reset_query_handler(lv_indev_t * indev)
964 {
965     if(indev->proc.reset_query) {
966         indev->proc.types.pointer.act_obj           = NULL;
967         indev->proc.types.pointer.last_obj          = NULL;
968         indev->proc.types.pointer.last_pressed      = NULL;
969         indev->proc.types.pointer.drag_limit_out    = 0;
970         indev->proc.types.pointer.drag_in_prog      = 0;
971         indev->proc.long_pr_sent                    = 0;
972         indev->proc.pr_timestamp                    = 0;
973         indev->proc.longpr_rep_timestamp            = 0;
974         indev->proc.types.pointer.drag_sum.x        = 0;
975         indev->proc.types.pointer.drag_sum.y        = 0;
976         indev->proc.types.pointer.drag_throw_vect.x = 0;
977         indev->proc.types.pointer.drag_throw_vect.y = 0;
978         indev->proc.reset_query                     = 0;
979         indev_obj_act                               = NULL;
980     }
981 }
982 /**
983  * Search the most top, clickable object on the last point of an input device
984  * @param proc pointer to  the `lv_indev_proc_t` part of the input device
985  * @param obj pointer to a start object, typically the screen
986  * @return pointer to the found object or NULL if there was no suitable object
987  */
indev_search_obj(const lv_indev_proc_t * proc,lv_obj_t * obj)988 static lv_obj_t * indev_search_obj(const lv_indev_proc_t * proc, lv_obj_t * obj)
989 {
990     lv_obj_t * found_p = NULL;
991 
992     /*If the point is on this object check its children too*/
993 #if LV_USE_EXT_CLICK_AREA == LV_EXT_CLICK_AREA_TINY
994     lv_area_t ext_area;
995     ext_area.x1 = obj->coords.x1 - obj->ext_click_pad_hor;
996     ext_area.x2 = obj->coords.x2 + obj->ext_click_pad_hor;
997     ext_area.y1 = obj->coords.y1 - obj->ext_click_pad_ver;
998     ext_area.y2 = obj->coords.y2 + obj->ext_click_pad_ver;
999 
1000     if(lv_area_is_point_on(&ext_area, &proc->types.pointer.act_point)) {
1001 #elif LV_USE_EXT_CLICK_AREA == LV_EXT_CLICK_AREA_FULL
1002     lv_area_t ext_area;
1003     ext_area.x1 = obj->coords.x1 - obj->ext_click_pad.x1;
1004     ext_area.x2 = obj->coords.x2 + obj->ext_click_pad.x2;
1005     ext_area.y1 = obj->coords.y1 - obj->ext_click_pad.y1;
1006     ext_area.y2 = obj->coords.y2 + obj->ext_click_pad.y2;
1007 
1008     if(lv_area_is_point_on(&ext_area, &proc->types.pointer.act_point)) {
1009 #else
1010     if(lv_area_is_point_on(&obj->coords, &proc->types.pointer.act_point)) {
1011 #endif
1012         lv_obj_t * i;
1013 
1014         LV_LL_READ(obj->child_ll, i)
1015         {
1016             found_p = indev_search_obj(proc, i);
1017 
1018             /*If a child was found then break*/
1019             if(found_p != NULL) {
1020                 break;
1021             }
1022         }
1023 
1024         /*If then the children was not ok, and this obj is clickable
1025          * and it or its parent is not hidden then save this object*/
1026         if(found_p == NULL && lv_obj_get_click(obj) != false) {
1027             lv_obj_t * hidden_i = obj;
1028             while(hidden_i != NULL) {
1029                 if(lv_obj_get_hidden(hidden_i) == true) break;
1030                 hidden_i = lv_obj_get_parent(hidden_i);
1031             }
1032             /*No parent found with hidden == true*/
1033             if(hidden_i == NULL) found_p = obj;
1034         }
1035     }
1036 
1037     return found_p;
1038 }
1039 
1040 /**
1041  * Handle the dragging of indev_proc_p->types.pointer.act_obj
1042  * @param indev pointer to a input device state
1043  */
1044 static void indev_drag(lv_indev_proc_t * state)
1045 {
1046     lv_obj_t * drag_obj    = state->types.pointer.act_obj;
1047     bool drag_just_started = false;
1048 
1049     /*If drag parent is active check recursively the drag_parent attribute*/
1050     while(lv_obj_get_drag_parent(drag_obj) != false && drag_obj != NULL) {
1051         drag_obj = lv_obj_get_parent(drag_obj);
1052     }
1053 
1054     if(drag_obj == NULL) return;
1055 
1056     if(lv_obj_get_drag(drag_obj) == false) return;
1057 
1058     lv_drag_dir_t allowed_dirs = lv_obj_get_drag_dir(drag_obj);
1059 
1060     /*Count the movement by drag*/
1061     state->types.pointer.drag_sum.x += state->types.pointer.vect.x;
1062     state->types.pointer.drag_sum.y += state->types.pointer.vect.y;
1063 
1064     /*Enough move?*/
1065     if(state->types.pointer.drag_limit_out == 0) {
1066         /*If a move is greater then LV_DRAG_LIMIT then begin the drag*/
1067         if(((allowed_dirs & LV_DRAG_DIR_HOR) &&
1068             LV_MATH_ABS(state->types.pointer.drag_sum.x) >= indev_act->driver.drag_limit) ||
1069            ((allowed_dirs & LV_DRAG_DIR_VER) &&
1070             LV_MATH_ABS(state->types.pointer.drag_sum.y) >= indev_act->driver.drag_limit)) {
1071             state->types.pointer.drag_limit_out = 1;
1072             drag_just_started                   = true;
1073         }
1074     }
1075 
1076     /*If the drag limit is exceeded handle the dragging*/
1077     if(state->types.pointer.drag_limit_out != 0) {
1078         /*Set new position if the vector is not zero*/
1079         if(state->types.pointer.vect.x != 0 || state->types.pointer.vect.y != 0) {
1080 
1081             uint16_t inv_buf_size =
1082                 lv_disp_get_inv_buf_size(indev_act->driver.disp); /*Get the number of currently invalidated areas*/
1083 
1084             lv_coord_t prev_x     = drag_obj->coords.x1;
1085             lv_coord_t prev_y     = drag_obj->coords.y1;
1086             lv_coord_t prev_par_w = lv_obj_get_width(lv_obj_get_parent(drag_obj));
1087             lv_coord_t prev_par_h = lv_obj_get_height(lv_obj_get_parent(drag_obj));
1088 
1089             /*Get the coordinates of the object and modify them*/
1090             lv_coord_t act_x = lv_obj_get_x(drag_obj);
1091             lv_coord_t act_y = lv_obj_get_y(drag_obj);
1092 
1093             if(allowed_dirs == LV_DRAG_DIR_ALL) {
1094                 if(drag_just_started) {
1095                     act_x += state->types.pointer.drag_sum.x;
1096                     act_y += state->types.pointer.drag_sum.y;
1097                 }
1098                 lv_obj_set_pos(drag_obj, act_x + state->types.pointer.vect.x, act_y + state->types.pointer.vect.y);
1099             } else if(allowed_dirs & LV_DRAG_DIR_HOR) {
1100                 if(drag_just_started) {
1101                     act_x += state->types.pointer.drag_sum.x;
1102                 }
1103                 lv_obj_set_x(drag_obj, act_x + state->types.pointer.vect.x);
1104             } else if(allowed_dirs & LV_DRAG_DIR_VER) {
1105                 if(drag_just_started) {
1106                     act_y += state->types.pointer.drag_sum.y;
1107                 }
1108                 lv_obj_set_y(drag_obj, act_y + state->types.pointer.vect.y);
1109             }
1110 
1111 
1112 
1113 
1114             /*If the object didn't moved then clear the invalidated areas*/
1115             if(drag_obj->coords.x1 == prev_x && drag_obj->coords.y1 == prev_y) {
1116 //                state->types.pointer.drag_in_prog = 0;
1117                 /*In a special case if the object is moved on a page and
1118                  * the scrollable has fit == true and the object is dragged of the page then
1119                  * while its coordinate is not changing only the parent's size is reduced */
1120                 lv_coord_t act_par_w = lv_obj_get_width(lv_obj_get_parent(drag_obj));
1121                 lv_coord_t act_par_h = lv_obj_get_height(lv_obj_get_parent(drag_obj));
1122                 if(act_par_w == prev_par_w && act_par_h == prev_par_h) {
1123                     uint16_t new_inv_buf_size = lv_disp_get_inv_buf_size(indev_act->driver.disp);
1124                     lv_disp_pop_from_inv_buf(indev_act->driver.disp, new_inv_buf_size - inv_buf_size);
1125                 }
1126             } else {
1127                 state->types.pointer.drag_in_prog = 1;
1128                 /*Set the drag in progress flag*/
1129                 /*Send the drag begin signal on first move*/
1130                 if(drag_just_started) {
1131                     drag_obj->signal_cb(drag_obj, LV_SIGNAL_DRAG_BEGIN, indev_act);
1132                     if(indev_reset_check(state)) return;
1133                     lv_event_send(drag_obj, LV_EVENT_DRAG_BEGIN, NULL);
1134                     if(indev_reset_check(state)) return;
1135                 }
1136             }
1137         }
1138     }
1139 }
1140 
1141 /**
1142  * Handle throwing by drag if the drag is ended
1143  * @param indev pointer to an input device state
1144  */
1145 static void indev_drag_throw(lv_indev_proc_t * proc)
1146 {
1147     if(proc->types.pointer.drag_in_prog == 0) return;
1148 
1149     lv_obj_t * drag_obj = proc->types.pointer.last_obj;
1150 
1151     /*If drag parent is active check recursively the drag_parent attribute*/
1152     while(lv_obj_get_drag_parent(drag_obj) != false && drag_obj != NULL) {
1153         drag_obj = lv_obj_get_parent(drag_obj);
1154     }
1155 
1156     if(drag_obj == NULL) {
1157         return;
1158     }
1159 
1160     /*Return if the drag throw is not enabled*/
1161     if(lv_obj_get_drag_throw(drag_obj) == false) {
1162         proc->types.pointer.drag_in_prog = 0;
1163         drag_obj->signal_cb(drag_obj, LV_SIGNAL_DRAG_END, indev_act);
1164         lv_event_send(drag_obj, LV_EVENT_DRAG_END, NULL);
1165         if(indev_reset_check(proc)) return;
1166 
1167         lv_event_send(drag_obj, LV_EVENT_DRAG_END, NULL);
1168         return;
1169     }
1170 
1171     lv_drag_dir_t allowed_dirs = lv_obj_get_drag_dir(drag_obj);
1172 
1173     /*Reduce the vectors*/
1174     proc->types.pointer.drag_throw_vect.x =
1175         proc->types.pointer.drag_throw_vect.x * (100 - indev_act->driver.drag_throw) / 100;
1176     proc->types.pointer.drag_throw_vect.y =
1177         proc->types.pointer.drag_throw_vect.y * (100 - indev_act->driver.drag_throw) / 100;
1178 
1179     if(proc->types.pointer.drag_throw_vect.x != 0 || proc->types.pointer.drag_throw_vect.y != 0) {
1180         /*Get the coordinates and modify them*/
1181         lv_area_t coords_ori;
1182         lv_obj_get_coords(drag_obj, &coords_ori);
1183         lv_coord_t act_x = lv_obj_get_x(drag_obj) + proc->types.pointer.drag_throw_vect.x;
1184         lv_coord_t act_y = lv_obj_get_y(drag_obj) + proc->types.pointer.drag_throw_vect.y;
1185 
1186         if(allowed_dirs == LV_DRAG_DIR_ALL)
1187             lv_obj_set_pos(drag_obj, act_x, act_y);
1188         else if(allowed_dirs & LV_DRAG_DIR_HOR)
1189             lv_obj_set_x(drag_obj, act_x);
1190         else if(allowed_dirs & LV_DRAG_DIR_VER)
1191             lv_obj_set_y(drag_obj, act_y);
1192 
1193         lv_area_t coord_new;
1194         lv_obj_get_coords(drag_obj, &coord_new);
1195 
1196         /*If non of the coordinates are changed then do not continue throwing*/
1197         if((coords_ori.x1 == coord_new.x1 || proc->types.pointer.drag_throw_vect.x == 0) &&
1198            (coords_ori.y1 == coord_new.y1 || proc->types.pointer.drag_throw_vect.y == 0)) {
1199             proc->types.pointer.drag_in_prog      = 0;
1200             proc->types.pointer.vect.x            = 0;
1201             proc->types.pointer.vect.y            = 0;
1202             proc->types.pointer.drag_throw_vect.x = 0;
1203             proc->types.pointer.drag_throw_vect.y = 0;
1204             drag_obj->signal_cb(drag_obj, LV_SIGNAL_DRAG_END, indev_act);
1205             if(indev_reset_check(proc)) return;
1206             lv_event_send(drag_obj, LV_EVENT_DRAG_END, NULL);
1207             if(indev_reset_check(proc)) return;
1208         }
1209     }
1210     /*If the types.pointer.vectors become 0 -> types.pointer.drag_in_prog = 0 and send a drag end
1211        signal*/
1212     else {
1213         proc->types.pointer.drag_in_prog = 0;
1214         drag_obj->signal_cb(drag_obj, LV_SIGNAL_DRAG_END, indev_act);
1215         if(indev_reset_check(proc)) return;
1216         lv_event_send(drag_obj, LV_EVENT_DRAG_END, NULL);
1217         if(indev_reset_check(proc)) return;
1218     }
1219 }
1220 
1221 /**
1222  * Checks if the reset_query flag has been set. If so, perform necessary global indev cleanup actions
1223  * @param proc pointer to an input device 'proc'
1224  * return true if indev query should be immediately truncated.
1225  */
1226 static bool indev_reset_check(lv_indev_proc_t * proc)
1227 {
1228     if(proc->reset_query) {
1229         indev_obj_act = NULL;
1230     }
1231 
1232     return proc->reset_query ? true : false;
1233 }
1234