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