acid-drop

- Hacking the planet from a LilyGo T-Deck using custom firmware
git clone git://git.acid.vegas/acid-drop.git
Log | Files | Refs | Archive | README | LICENSE

lv_indev.c (43669B)

      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 #include "lv_indev_scroll.h"
     13 #include "lv_group.h"
     14 #include "lv_refr.h"
     15 
     16 #include "../hal/lv_hal_tick.h"
     17 #include "../misc/lv_timer.h"
     18 #include "../misc/lv_math.h"
     19 
     20 /*********************
     21  *      DEFINES
     22  *********************/
     23 #if LV_INDEV_DEF_SCROLL_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 static void indev_pointer_proc(lv_indev_t * i, lv_indev_data_t * data);
     35 static void indev_keypad_proc(lv_indev_t * i, lv_indev_data_t * data);
     36 static void indev_encoder_proc(lv_indev_t * i, lv_indev_data_t * data);
     37 static void indev_button_proc(lv_indev_t * i, lv_indev_data_t * data);
     38 static void indev_proc_press(_lv_indev_proc_t * proc);
     39 static void indev_proc_release(_lv_indev_proc_t * proc);
     40 static void indev_proc_reset_query_handler(lv_indev_t * indev);
     41 static void indev_click_focus(_lv_indev_proc_t * proc);
     42 static void indev_gesture(_lv_indev_proc_t * proc);
     43 static bool indev_reset_check(_lv_indev_proc_t * proc);
     44 
     45 /**********************
     46  *  STATIC VARIABLES
     47  **********************/
     48 static lv_indev_t * indev_act;
     49 static lv_obj_t * indev_obj_act = NULL;
     50 
     51 /**********************
     52  *      MACROS
     53  **********************/
     54 #if LV_LOG_TRACE_INDEV
     55     #define INDEV_TRACE(...) LV_LOG_TRACE(__VA_ARGS__)
     56 #else
     57     #define INDEV_TRACE(...)
     58 #endif
     59 
     60 /**********************
     61  *   GLOBAL FUNCTIONS
     62  **********************/
     63 
     64 void lv_indev_read_timer_cb(lv_timer_t * timer)
     65 {
     66     INDEV_TRACE("begin");
     67 
     68     lv_indev_data_t data;
     69 
     70     indev_act = timer->user_data;
     71 
     72     /*Read and process all indevs*/
     73     if(indev_act->driver->disp == NULL) return; /*Not assigned to any displays*/
     74 
     75     /*Handle reset query before processing the point*/
     76     indev_proc_reset_query_handler(indev_act);
     77 
     78     if(indev_act->proc.disabled ||
     79        indev_act->driver->disp->prev_scr != NULL) return; /*Input disabled or screen animation active*/
     80     bool continue_reading;
     81     do {
     82         /*Read the data*/
     83         _lv_indev_read(indev_act, &data);
     84         continue_reading = data.continue_reading;
     85 
     86         /*The active object might be deleted even in the read function*/
     87         indev_proc_reset_query_handler(indev_act);
     88         indev_obj_act = NULL;
     89 
     90         indev_act->proc.state = data.state;
     91 
     92         /*Save the last activity time*/
     93         if(indev_act->proc.state == LV_INDEV_STATE_PRESSED) {
     94             indev_act->driver->disp->last_activity_time = lv_tick_get();
     95         }
     96         else if(indev_act->driver->type == LV_INDEV_TYPE_ENCODER && data.enc_diff) {
     97             indev_act->driver->disp->last_activity_time = lv_tick_get();
     98         }
     99 
    100         if(indev_act->driver->type == LV_INDEV_TYPE_POINTER) {
    101             indev_pointer_proc(indev_act, &data);
    102         }
    103         else if(indev_act->driver->type == LV_INDEV_TYPE_KEYPAD) {
    104             indev_keypad_proc(indev_act, &data);
    105         }
    106         else if(indev_act->driver->type == LV_INDEV_TYPE_ENCODER) {
    107             indev_encoder_proc(indev_act, &data);
    108         }
    109         else if(indev_act->driver->type == LV_INDEV_TYPE_BUTTON) {
    110             indev_button_proc(indev_act, &data);
    111         }
    112         /*Handle reset query if it happened in during processing*/
    113         indev_proc_reset_query_handler(indev_act);
    114     } while(continue_reading);
    115 
    116     /*End of indev processing, so no act indev*/
    117     indev_act     = NULL;
    118     indev_obj_act = NULL;
    119 
    120     INDEV_TRACE("finished");
    121 }
    122 
    123 void lv_indev_enable(lv_indev_t * indev, bool en)
    124 {
    125     uint8_t enable = en ? 0 : 1;
    126 
    127     if(indev) {
    128         indev->proc.disabled = enable;
    129     }
    130     else {
    131         lv_indev_t * i = lv_indev_get_next(NULL);
    132         while(i) {
    133             i->proc.disabled = enable;
    134             i = lv_indev_get_next(i);
    135         }
    136     }
    137 }
    138 
    139 lv_indev_t * lv_indev_get_act(void)
    140 {
    141     return indev_act;
    142 }
    143 
    144 lv_indev_type_t lv_indev_get_type(const lv_indev_t * indev)
    145 {
    146     if(indev == NULL) return LV_INDEV_TYPE_NONE;
    147 
    148     return indev->driver->type;
    149 }
    150 
    151 void lv_indev_reset(lv_indev_t * indev, lv_obj_t * obj)
    152 {
    153     if(indev) {
    154         indev->proc.reset_query = 1;
    155         if(indev_act == indev) indev_obj_act = NULL;
    156         if(indev->driver->type == LV_INDEV_TYPE_POINTER || indev->driver->type == LV_INDEV_TYPE_KEYPAD) {
    157             if(obj == NULL || indev->proc.types.pointer.last_pressed == obj) {
    158                 indev->proc.types.pointer.last_pressed = NULL;
    159             }
    160             if(obj == NULL || indev->proc.types.pointer.act_obj == obj) {
    161                 indev->proc.types.pointer.act_obj = NULL;
    162             }
    163             if(obj == NULL || indev->proc.types.pointer.last_obj == obj) {
    164                 indev->proc.types.pointer.last_obj = NULL;
    165             }
    166         }
    167     }
    168     else {
    169         lv_indev_t * i = lv_indev_get_next(NULL);
    170         while(i) {
    171             i->proc.reset_query = 1;
    172             if(i->driver->type == LV_INDEV_TYPE_POINTER || i->driver->type == LV_INDEV_TYPE_KEYPAD) {
    173                 if(obj == NULL || i->proc.types.pointer.last_pressed == obj) {
    174                     i->proc.types.pointer.last_pressed = NULL;
    175                 }
    176                 if(obj == NULL || i->proc.types.pointer.act_obj == obj) {
    177                     i->proc.types.pointer.act_obj = NULL;
    178                 }
    179                 if(obj == NULL || i->proc.types.pointer.last_obj == obj) {
    180                     i->proc.types.pointer.last_obj = NULL;
    181                 }
    182             }
    183             i = lv_indev_get_next(i);
    184         }
    185         indev_obj_act = NULL;
    186     }
    187 }
    188 
    189 void lv_indev_reset_long_press(lv_indev_t * indev)
    190 {
    191     indev->proc.long_pr_sent         = 0;
    192     indev->proc.longpr_rep_timestamp = lv_tick_get();
    193     indev->proc.pr_timestamp         = lv_tick_get();
    194 }
    195 
    196 void lv_indev_set_cursor(lv_indev_t * indev, lv_obj_t * cur_obj)
    197 {
    198     if(indev->driver->type != LV_INDEV_TYPE_POINTER) return;
    199 
    200     indev->cursor = cur_obj;
    201     lv_obj_set_parent(indev->cursor, lv_disp_get_layer_sys(indev->driver->disp));
    202     lv_obj_set_pos(indev->cursor, indev->proc.types.pointer.act_point.x, indev->proc.types.pointer.act_point.y);
    203     lv_obj_clear_flag(indev->cursor, LV_OBJ_FLAG_CLICKABLE);
    204     lv_obj_add_flag(indev->cursor, LV_OBJ_FLAG_IGNORE_LAYOUT | LV_OBJ_FLAG_FLOATING);
    205 }
    206 
    207 void lv_indev_set_group(lv_indev_t * indev, lv_group_t * group)
    208 {
    209     if(indev->driver->type == LV_INDEV_TYPE_KEYPAD || indev->driver->type == LV_INDEV_TYPE_ENCODER) {
    210         indev->group = group;
    211     }
    212 }
    213 
    214 void lv_indev_set_button_points(lv_indev_t * indev, const lv_point_t points[])
    215 {
    216     if(indev->driver->type == LV_INDEV_TYPE_BUTTON) {
    217         indev->btn_points = points;
    218     }
    219 }
    220 
    221 void lv_indev_get_point(const lv_indev_t * indev, lv_point_t * point)
    222 {
    223     if(indev == NULL) {
    224         point->x = 0;
    225         point->y = 0;
    226         return;
    227     }
    228     if(indev->driver->type != LV_INDEV_TYPE_POINTER && indev->driver->type != LV_INDEV_TYPE_BUTTON) {
    229         point->x = -1;
    230         point->y = -1;
    231     }
    232     else {
    233         point->x = indev->proc.types.pointer.act_point.x;
    234         point->y = indev->proc.types.pointer.act_point.y;
    235     }
    236 }
    237 
    238 lv_dir_t lv_indev_get_gesture_dir(const lv_indev_t * indev)
    239 {
    240     return indev->proc.types.pointer.gesture_dir;
    241 }
    242 
    243 uint32_t lv_indev_get_key(const lv_indev_t * indev)
    244 {
    245     if(indev->driver->type != LV_INDEV_TYPE_KEYPAD)
    246         return 0;
    247     else
    248         return indev->proc.types.keypad.last_key;
    249 }
    250 
    251 lv_dir_t lv_indev_get_scroll_dir(const lv_indev_t * indev)
    252 {
    253     if(indev == NULL) return false;
    254     if(indev->driver->type != LV_INDEV_TYPE_POINTER && indev->driver->type != LV_INDEV_TYPE_BUTTON) return false;
    255     return indev->proc.types.pointer.scroll_dir;
    256 }
    257 
    258 lv_obj_t * lv_indev_get_scroll_obj(const lv_indev_t * indev)
    259 {
    260     if(indev == NULL) return NULL;
    261     if(indev->driver->type != LV_INDEV_TYPE_POINTER && indev->driver->type != LV_INDEV_TYPE_BUTTON) return NULL;
    262     return indev->proc.types.pointer.scroll_obj;
    263 }
    264 
    265 void lv_indev_get_vect(const lv_indev_t * indev, lv_point_t * point)
    266 {
    267     point->x = 0;
    268     point->y = 0;
    269 
    270     if(indev == NULL) return;
    271 
    272     if(indev->driver->type == LV_INDEV_TYPE_POINTER || indev->driver->type == LV_INDEV_TYPE_BUTTON) {
    273         point->x = indev->proc.types.pointer.vect.x;
    274         point->y = indev->proc.types.pointer.vect.y;
    275     }
    276 }
    277 
    278 void lv_indev_wait_release(lv_indev_t * indev)
    279 {
    280     if(indev == NULL)return;
    281     indev->proc.wait_until_release = 1;
    282 }
    283 
    284 lv_obj_t * lv_indev_get_obj_act(void)
    285 {
    286     return indev_obj_act;
    287 }
    288 
    289 lv_timer_t * lv_indev_get_read_timer(lv_disp_t * indev)
    290 {
    291     if(!indev) {
    292         LV_LOG_WARN("lv_indev_get_read_timer: indev was NULL");
    293         return NULL;
    294     }
    295 
    296     return indev->refr_timer;
    297 }
    298 
    299 lv_obj_t * lv_indev_search_obj(lv_obj_t * obj, lv_point_t * point)
    300 {
    301     lv_obj_t * found_p = NULL;
    302 
    303     /*If this obj is hidden the children are hidden too so return immediately*/
    304     if(lv_obj_has_flag(obj, LV_OBJ_FLAG_HIDDEN)) return NULL;
    305 
    306     bool hit_test_ok = lv_obj_hit_test(obj, point);
    307 
    308     /*If the point is on this object or has overflow visible check its children too*/
    309     if(_lv_area_is_point_on(&obj->coords, point, 0) || lv_obj_has_flag(obj, LV_OBJ_FLAG_OVERFLOW_VISIBLE)) {
    310         int32_t i;
    311         uint32_t child_cnt = lv_obj_get_child_cnt(obj);
    312         /*If a child matches use it*/
    313         for(i = child_cnt - 1; i >= 0; i--) {
    314             lv_obj_t * child = obj->spec_attr->children[i];
    315             found_p = lv_indev_search_obj(child, point);
    316             if(found_p) return found_p;
    317         }
    318     }
    319 
    320     /*If not return earlier for a clicked child and this obj's hittest was ok use it
    321      *else return NULL*/
    322     if(hit_test_ok) return obj;
    323     else return NULL;
    324 }
    325 
    326 /**********************
    327  *   STATIC FUNCTIONS
    328  **********************/
    329 
    330 /**
    331  * Process a new point from LV_INDEV_TYPE_POINTER input device
    332  * @param i pointer to an input device
    333  * @param data pointer to the data read from the input device
    334  */
    335 static void indev_pointer_proc(lv_indev_t * i, lv_indev_data_t * data)
    336 {
    337     lv_disp_t * disp = i->driver->disp;
    338     /*Save the raw points so they can be used again in _lv_indev_read*/
    339     i->proc.types.pointer.last_raw_point.x = data->point.x;
    340     i->proc.types.pointer.last_raw_point.y = data->point.y;
    341 
    342     if(disp->driver->rotated == LV_DISP_ROT_180 || disp->driver->rotated == LV_DISP_ROT_270) {
    343         data->point.x = disp->driver->hor_res - data->point.x - 1;
    344         data->point.y = disp->driver->ver_res - data->point.y - 1;
    345     }
    346     if(disp->driver->rotated == LV_DISP_ROT_90 || disp->driver->rotated == LV_DISP_ROT_270) {
    347         lv_coord_t tmp = data->point.y;
    348         data->point.y = data->point.x;
    349         data->point.x = disp->driver->ver_res - tmp - 1;
    350     }
    351 
    352     /*Simple sanity check*/
    353     if(data->point.x < 0) {
    354         LV_LOG_WARN("X is %d which is smaller than zero", data->point.x);
    355     }
    356     if(data->point.x >= lv_disp_get_hor_res(i->driver->disp)) {
    357         LV_LOG_WARN("X is %d which is greater than hor. res", data->point.x);
    358     }
    359     if(data->point.y < 0) {
    360         LV_LOG_WARN("Y is %d which is smaller than zero", data->point.y);
    361     }
    362     if(data->point.y >= lv_disp_get_ver_res(i->driver->disp)) {
    363         LV_LOG_WARN("Y is %d which is greater than ver. res", data->point.y);
    364     }
    365 
    366     /*Move the cursor if set and moved*/
    367     if(i->cursor != NULL &&
    368        (i->proc.types.pointer.last_point.x != data->point.x || i->proc.types.pointer.last_point.y != data->point.y)) {
    369         lv_obj_set_pos(i->cursor, data->point.x, data->point.y);
    370     }
    371 
    372     i->proc.types.pointer.act_point.x = data->point.x;
    373     i->proc.types.pointer.act_point.y = data->point.y;
    374 
    375     if(i->proc.state == LV_INDEV_STATE_PRESSED) {
    376         indev_proc_press(&i->proc);
    377     }
    378     else {
    379         indev_proc_release(&i->proc);
    380     }
    381 
    382     i->proc.types.pointer.last_point.x = i->proc.types.pointer.act_point.x;
    383     i->proc.types.pointer.last_point.y = i->proc.types.pointer.act_point.y;
    384 }
    385 
    386 /**
    387  * Process a new point from LV_INDEV_TYPE_KEYPAD input device
    388  * @param i pointer to an input device
    389  * @param data pointer to the data read from the input device
    390  */
    391 static void indev_keypad_proc(lv_indev_t * i, lv_indev_data_t * data)
    392 {
    393     if(data->state == LV_INDEV_STATE_PRESSED && i->proc.wait_until_release) return;
    394 
    395     if(i->proc.wait_until_release) {
    396         i->proc.wait_until_release      = 0;
    397         i->proc.pr_timestamp            = 0;
    398         i->proc.long_pr_sent            = 0;
    399         i->proc.types.keypad.last_state = LV_INDEV_STATE_RELEASED; /*To skip the processing of release*/
    400     }
    401 
    402     lv_group_t * g = i->group;
    403     if(g == NULL) return;
    404 
    405     indev_obj_act = lv_group_get_focused(g);
    406     if(indev_obj_act == NULL) return;
    407 
    408     bool dis = lv_obj_has_state(indev_obj_act, LV_STATE_DISABLED);
    409 
    410     /*Save the last key to compare it with the current latter on RELEASE*/
    411     uint32_t prev_key = i->proc.types.keypad.last_key;
    412 
    413     /*Save the last key.
    414      *It must be done here else `lv_indev_get_key` will return the last key in events*/
    415     i->proc.types.keypad.last_key = data->key;
    416 
    417     /*Save the previous state so we can detect state changes below and also set the last state now
    418      *so if any event handler on the way returns `LV_RES_INV` the last state is remembered
    419      *for the next time*/
    420     uint32_t prev_state             = i->proc.types.keypad.last_state;
    421     i->proc.types.keypad.last_state = data->state;
    422 
    423     /*Key press happened*/
    424     if(data->state == LV_INDEV_STATE_PRESSED && prev_state == LV_INDEV_STATE_RELEASED) {
    425         LV_LOG_INFO("%" LV_PRIu32 " key is pressed", data->key);
    426         i->proc.pr_timestamp = lv_tick_get();
    427 
    428         /*Move the focus on NEXT*/
    429         if(data->key == LV_KEY_NEXT) {
    430             lv_group_set_editing(g, false); /*Editing is not used by KEYPAD is be sure it is disabled*/
    431             lv_group_focus_next(g);
    432             if(indev_reset_check(&i->proc)) return;
    433         }
    434         /*Move the focus on PREV*/
    435         else if(data->key == LV_KEY_PREV) {
    436             lv_group_set_editing(g, false); /*Editing is not used by KEYPAD is be sure it is disabled*/
    437             lv_group_focus_prev(g);
    438             if(indev_reset_check(&i->proc)) return;
    439         }
    440         else if(!dis) {
    441             /*Simulate a press on the object if ENTER was pressed*/
    442             if(data->key == LV_KEY_ENTER) {
    443                 /*Send the ENTER as a normal KEY*/
    444                 lv_group_send_data(g, LV_KEY_ENTER);
    445                 if(indev_reset_check(&i->proc)) return;
    446 
    447                 if(!dis) lv_event_send(indev_obj_act, LV_EVENT_PRESSED, indev_act);
    448                 if(indev_reset_check(&i->proc)) return;
    449             }
    450             else if(data->key == LV_KEY_ESC) {
    451                 /*Send the ESC as a normal KEY*/
    452                 lv_group_send_data(g, LV_KEY_ESC);
    453                 if(indev_reset_check(&i->proc)) return;
    454 
    455                 lv_event_send(indev_obj_act, LV_EVENT_CANCEL, indev_act);
    456                 if(indev_reset_check(&i->proc)) return;
    457             }
    458             /*Just send other keys to the object (e.g. 'A' or `LV_GROUP_KEY_RIGHT`)*/
    459             else {
    460                 lv_group_send_data(g, data->key);
    461                 if(indev_reset_check(&i->proc)) return;
    462             }
    463         }
    464     }
    465     /*Pressing*/
    466     else if(!dis && data->state == LV_INDEV_STATE_PRESSED && prev_state == LV_INDEV_STATE_PRESSED) {
    467 
    468         if(data->key == LV_KEY_ENTER) {
    469             lv_event_send(indev_obj_act, LV_EVENT_PRESSING, indev_act);
    470             if(indev_reset_check(&i->proc)) return;
    471         }
    472 
    473         /*Long press time has elapsed?*/
    474         if(i->proc.long_pr_sent == 0 && lv_tick_elaps(i->proc.pr_timestamp) > i->driver->long_press_time) {
    475             i->proc.long_pr_sent = 1;
    476             if(data->key == LV_KEY_ENTER) {
    477                 i->proc.longpr_rep_timestamp = lv_tick_get();
    478                 lv_event_send(indev_obj_act, LV_EVENT_LONG_PRESSED, indev_act);
    479                 if(indev_reset_check(&i->proc)) return;
    480             }
    481         }
    482         /*Long press repeated time has elapsed?*/
    483         else if(i->proc.long_pr_sent != 0 &&
    484                 lv_tick_elaps(i->proc.longpr_rep_timestamp) > i->driver->long_press_repeat_time) {
    485 
    486             i->proc.longpr_rep_timestamp = lv_tick_get();
    487 
    488             /*Send LONG_PRESS_REP on ENTER*/
    489             if(data->key == LV_KEY_ENTER) {
    490                 lv_event_send(indev_obj_act, LV_EVENT_LONG_PRESSED_REPEAT, indev_act);
    491                 if(indev_reset_check(&i->proc)) return;
    492             }
    493             /*Move the focus on NEXT again*/
    494             else if(data->key == LV_KEY_NEXT) {
    495                 lv_group_set_editing(g, false); /*Editing is not used by KEYPAD is be sure it is disabled*/
    496                 lv_group_focus_next(g);
    497                 if(indev_reset_check(&i->proc)) return;
    498             }
    499             /*Move the focus on PREV again*/
    500             else if(data->key == LV_KEY_PREV) {
    501                 lv_group_set_editing(g, false); /*Editing is not used by KEYPAD is be sure it is disabled*/
    502                 lv_group_focus_prev(g);
    503                 if(indev_reset_check(&i->proc)) return;
    504             }
    505             /*Just send other keys again to the object (e.g. 'A' or `LV_GROUP_KEY_RIGHT)*/
    506             else {
    507                 lv_group_send_data(g, data->key);
    508                 if(indev_reset_check(&i->proc)) return;
    509             }
    510         }
    511     }
    512     /*Release happened*/
    513     else if(!dis && data->state == LV_INDEV_STATE_RELEASED && prev_state == LV_INDEV_STATE_PRESSED) {
    514         LV_LOG_INFO("%" LV_PRIu32 " key is released", data->key);
    515         /*The user might clear the key when it was released. Always release the pressed key*/
    516         data->key = prev_key;
    517         if(data->key == LV_KEY_ENTER) {
    518 
    519             lv_event_send(indev_obj_act, LV_EVENT_RELEASED, indev_act);
    520             if(indev_reset_check(&i->proc)) return;
    521 
    522             if(i->proc.long_pr_sent == 0) {
    523                 lv_event_send(indev_obj_act, LV_EVENT_SHORT_CLICKED, indev_act);
    524                 if(indev_reset_check(&i->proc)) return;
    525             }
    526 
    527             lv_event_send(indev_obj_act, LV_EVENT_CLICKED, indev_act);
    528             if(indev_reset_check(&i->proc)) return;
    529 
    530         }
    531         i->proc.pr_timestamp = 0;
    532         i->proc.long_pr_sent = 0;
    533     }
    534     indev_obj_act = NULL;
    535 }
    536 
    537 /**
    538  * Process a new point from LV_INDEV_TYPE_ENCODER input device
    539  * @param i pointer to an input device
    540  * @param data pointer to the data read from the input device
    541  */
    542 static void indev_encoder_proc(lv_indev_t * i, lv_indev_data_t * data)
    543 {
    544     if(data->state == LV_INDEV_STATE_PRESSED && i->proc.wait_until_release) return;
    545 
    546     if(i->proc.wait_until_release) {
    547         i->proc.wait_until_release      = 0;
    548         i->proc.pr_timestamp            = 0;
    549         i->proc.long_pr_sent            = 0;
    550         i->proc.types.keypad.last_state = LV_INDEV_STATE_RELEASED; /*To skip the processing of release*/
    551     }
    552 
    553     /*Save the last keys before anything else.
    554      *They need to be already saved if the function returns for any reason*/
    555     lv_indev_state_t last_state     = i->proc.types.keypad.last_state;
    556     i->proc.types.keypad.last_state = data->state;
    557     i->proc.types.keypad.last_key   = data->key;
    558 
    559     lv_group_t * g = i->group;
    560     if(g == NULL) return;
    561 
    562     indev_obj_act = lv_group_get_focused(g);
    563     if(indev_obj_act == NULL) return;
    564 
    565     /*Process the steps they are valid only with released button*/
    566     if(data->state != LV_INDEV_STATE_RELEASED) {
    567         data->enc_diff = 0;
    568     }
    569 
    570     /*Refresh the focused object. It might change due to lv_group_focus_prev/next*/
    571     indev_obj_act = lv_group_get_focused(g);
    572     if(indev_obj_act == NULL) return;
    573 
    574     /*Button press happened*/
    575     if(data->state == LV_INDEV_STATE_PRESSED && last_state == LV_INDEV_STATE_RELEASED) {
    576         LV_LOG_INFO("pressed");
    577 
    578         i->proc.pr_timestamp = lv_tick_get();
    579 
    580         if(data->key == LV_KEY_ENTER) {
    581             bool editable_or_scrollable = lv_obj_is_editable(indev_obj_act) ||
    582                                           lv_obj_has_flag(indev_obj_act, LV_OBJ_FLAG_SCROLLABLE);
    583             if(lv_group_get_editing(g) == true || editable_or_scrollable == false) {
    584                 lv_event_send(indev_obj_act, LV_EVENT_PRESSED, indev_act);
    585                 if(indev_reset_check(&i->proc)) return;
    586             }
    587         }
    588         else if(data->key == LV_KEY_LEFT) {
    589             /*emulate encoder left*/
    590             data->enc_diff--;
    591         }
    592         else if(data->key == LV_KEY_RIGHT) {
    593             /*emulate encoder right*/
    594             data->enc_diff++;
    595         }
    596         else if(data->key == LV_KEY_ESC) {
    597             /*Send the ESC as a normal KEY*/
    598             lv_group_send_data(g, LV_KEY_ESC);
    599             if(indev_reset_check(&i->proc)) return;
    600 
    601             lv_event_send(indev_obj_act, LV_EVENT_CANCEL, indev_act);
    602             if(indev_reset_check(&i->proc)) return;
    603         }
    604         /*Just send other keys to the object (e.g. 'A' or `LV_GROUP_KEY_RIGHT`)*/
    605         else {
    606             lv_group_send_data(g, data->key);
    607             if(indev_reset_check(&i->proc)) return;
    608         }
    609     }
    610     /*Pressing*/
    611     else if(data->state == LV_INDEV_STATE_PRESSED && last_state == LV_INDEV_STATE_PRESSED) {
    612         /*Long press*/
    613         if(i->proc.long_pr_sent == 0 && lv_tick_elaps(i->proc.pr_timestamp) > i->driver->long_press_time) {
    614 
    615             i->proc.long_pr_sent = 1;
    616             i->proc.longpr_rep_timestamp = lv_tick_get();
    617 
    618             if(data->key == LV_KEY_ENTER) {
    619                 bool editable_or_scrollable = lv_obj_is_editable(indev_obj_act) ||
    620                                               lv_obj_has_flag(indev_obj_act, LV_OBJ_FLAG_SCROLLABLE);
    621 
    622                 /*On enter long press toggle edit mode.*/
    623                 if(editable_or_scrollable) {
    624                     /*Don't leave edit mode if there is only one object (nowhere to navigate)*/
    625                     if(lv_group_get_obj_count(g) > 1) {
    626                         LV_LOG_INFO("toggling edit mode");
    627                         lv_group_set_editing(g, lv_group_get_editing(g) ? false : true); /*Toggle edit mode on long press*/
    628                         lv_obj_clear_state(indev_obj_act, LV_STATE_PRESSED);    /*Remove the pressed state manually*/
    629                     }
    630                 }
    631                 /*If not editable then just send a long press Call the ancestor's event handler*/
    632                 else {
    633                     lv_event_send(indev_obj_act, LV_EVENT_LONG_PRESSED, indev_act);
    634                     if(indev_reset_check(&i->proc)) return;
    635                 }
    636             }
    637 
    638             i->proc.long_pr_sent = 1;
    639         }
    640         /*Long press repeated time has elapsed?*/
    641         else if(i->proc.long_pr_sent != 0 && lv_tick_elaps(i->proc.longpr_rep_timestamp) > i->driver->long_press_repeat_time) {
    642 
    643             i->proc.longpr_rep_timestamp = lv_tick_get();
    644 
    645             if(data->key == LV_KEY_ENTER) {
    646                 lv_event_send(indev_obj_act, LV_EVENT_LONG_PRESSED_REPEAT, indev_act);
    647                 if(indev_reset_check(&i->proc)) return;
    648             }
    649             else if(data->key == LV_KEY_LEFT) {
    650                 /*emulate encoder left*/
    651                 data->enc_diff--;
    652             }
    653             else if(data->key == LV_KEY_RIGHT) {
    654                 /*emulate encoder right*/
    655                 data->enc_diff++;
    656             }
    657             else {
    658                 lv_group_send_data(g, data->key);
    659                 if(indev_reset_check(&i->proc)) return;
    660             }
    661 
    662         }
    663 
    664     }
    665     /*Release happened*/
    666     else if(data->state == LV_INDEV_STATE_RELEASED && last_state == LV_INDEV_STATE_PRESSED) {
    667         LV_LOG_INFO("released");
    668 
    669         if(data->key == LV_KEY_ENTER) {
    670             bool editable_or_scrollable = lv_obj_is_editable(indev_obj_act) ||
    671                                           lv_obj_has_flag(indev_obj_act, LV_OBJ_FLAG_SCROLLABLE);
    672 
    673             /*The button was released on a non-editable object. Just send enter*/
    674             if(editable_or_scrollable == false) {
    675                 lv_event_send(indev_obj_act, LV_EVENT_RELEASED, indev_act);
    676                 if(indev_reset_check(&i->proc)) return;
    677 
    678                 if(i->proc.long_pr_sent == 0) lv_event_send(indev_obj_act, LV_EVENT_SHORT_CLICKED, indev_act);
    679                 if(indev_reset_check(&i->proc)) return;
    680 
    681                 lv_event_send(indev_obj_act, LV_EVENT_CLICKED, indev_act);
    682                 if(indev_reset_check(&i->proc)) return;
    683 
    684             }
    685             /*An object is being edited and the button is released.*/
    686             else if(lv_group_get_editing(g)) {
    687                 /*Ignore long pressed enter release because it comes from mode switch*/
    688                 if(!i->proc.long_pr_sent || lv_group_get_obj_count(g) <= 1) {
    689                     lv_event_send(indev_obj_act, LV_EVENT_RELEASED, indev_act);
    690                     if(indev_reset_check(&i->proc)) return;
    691 
    692                     lv_event_send(indev_obj_act, LV_EVENT_SHORT_CLICKED, indev_act);
    693                     if(indev_reset_check(&i->proc)) return;
    694 
    695                     lv_event_send(indev_obj_act, LV_EVENT_CLICKED, indev_act);
    696                     if(indev_reset_check(&i->proc)) return;
    697 
    698                     lv_group_send_data(g, LV_KEY_ENTER);
    699                     if(indev_reset_check(&i->proc)) return;
    700                 }
    701                 else {
    702                     lv_obj_clear_state(indev_obj_act, LV_STATE_PRESSED);    /*Remove the pressed state manually*/
    703                 }
    704             }
    705             /*If the focused object is editable and now in navigate mode then on enter switch edit
    706                mode*/
    707             else if(!i->proc.long_pr_sent) {
    708                 LV_LOG_INFO("entering edit mode");
    709                 lv_group_set_editing(g, true); /*Set edit mode*/
    710             }
    711         }
    712 
    713         i->proc.pr_timestamp = 0;
    714         i->proc.long_pr_sent = 0;
    715     }
    716     indev_obj_act = NULL;
    717 
    718     /*if encoder steps or simulated steps via left/right keys*/
    719     if(data->enc_diff != 0) {
    720         /*In edit mode send LEFT/RIGHT keys*/
    721         if(lv_group_get_editing(g)) {
    722             LV_LOG_INFO("rotated by %+d (edit)", data->enc_diff);
    723             int32_t s;
    724             if(data->enc_diff < 0) {
    725                 for(s = 0; s < -data->enc_diff; s++) {
    726                     lv_group_send_data(g, LV_KEY_LEFT);
    727                     if(indev_reset_check(&i->proc)) return;
    728                 }
    729             }
    730             else if(data->enc_diff > 0) {
    731                 for(s = 0; s < data->enc_diff; s++) {
    732                     lv_group_send_data(g, LV_KEY_RIGHT);
    733                     if(indev_reset_check(&i->proc)) return;
    734                 }
    735             }
    736         }
    737         /*In navigate mode focus on the next/prev objects*/
    738         else {
    739             LV_LOG_INFO("rotated by %+d (nav)", data->enc_diff);
    740             int32_t s;
    741             if(data->enc_diff < 0) {
    742                 for(s = 0; s < -data->enc_diff; s++) {
    743                     lv_group_focus_prev(g);
    744                     if(indev_reset_check(&i->proc)) return;
    745                 }
    746             }
    747             else if(data->enc_diff > 0) {
    748                 for(s = 0; s < data->enc_diff; s++) {
    749                     lv_group_focus_next(g);
    750                     if(indev_reset_check(&i->proc)) return;
    751                 }
    752             }
    753         }
    754     }
    755 }
    756 
    757 /**
    758  * Process new points from an input device. indev->state.pressed has to be set
    759  * @param indev pointer to an input device state
    760  * @param x x coordinate of the next point
    761  * @param y y coordinate of the next point
    762  */
    763 static void indev_button_proc(lv_indev_t * i, lv_indev_data_t * data)
    764 {
    765     /*Die gracefully if i->btn_points is NULL*/
    766     if(i->btn_points == NULL) {
    767         LV_LOG_WARN("btn_points is NULL");
    768         return;
    769     }
    770 
    771     lv_coord_t x = i->btn_points[data->btn_id].x;
    772     lv_coord_t y = i->btn_points[data->btn_id].y;
    773 
    774     static lv_indev_state_t prev_state = LV_INDEV_STATE_RELEASED;
    775     if(prev_state != data->state) {
    776         if(data->state == LV_INDEV_STATE_PRESSED) {
    777             LV_LOG_INFO("button %" LV_PRIu32 " is pressed (x:%d y:%d)", data->btn_id, x, y);
    778         }
    779         else {
    780             LV_LOG_INFO("button %" LV_PRIu32 " is released (x:%d y:%d)", data->btn_id, x, y);
    781         }
    782     }
    783 
    784     /*If a new point comes always make a release*/
    785     if(data->state == LV_INDEV_STATE_PRESSED) {
    786         if(i->proc.types.pointer.last_point.x != x ||
    787            i->proc.types.pointer.last_point.y != y) {
    788             indev_proc_release(&i->proc);
    789         }
    790     }
    791 
    792     if(indev_reset_check(&i->proc)) return;
    793 
    794     /*Save the new points*/
    795     i->proc.types.pointer.act_point.x = x;
    796     i->proc.types.pointer.act_point.y = y;
    797 
    798     if(data->state == LV_INDEV_STATE_PRESSED) indev_proc_press(&i->proc);
    799     else indev_proc_release(&i->proc);
    800 
    801     if(indev_reset_check(&i->proc)) return;
    802 
    803     i->proc.types.pointer.last_point.x = i->proc.types.pointer.act_point.x;
    804     i->proc.types.pointer.last_point.y = i->proc.types.pointer.act_point.y;
    805 }
    806 
    807 /**
    808  * Process the pressed state of LV_INDEV_TYPE_POINTER input devices
    809  * @param indev pointer to an input device 'proc'
    810  * @return LV_RES_OK: no indev reset required; LV_RES_INV: indev reset is required
    811  */
    812 static void indev_proc_press(_lv_indev_proc_t * proc)
    813 {
    814     LV_LOG_INFO("pressed at x:%d y:%d", proc->types.pointer.act_point.x, proc->types.pointer.act_point.y);
    815     indev_obj_act = proc->types.pointer.act_obj;
    816 
    817     if(proc->wait_until_release != 0) return;
    818 
    819     lv_disp_t * disp = indev_act->driver->disp;
    820     bool new_obj_searched = false;
    821 
    822     /*If there is no last object then search*/
    823     if(indev_obj_act == NULL) {
    824         indev_obj_act = lv_indev_search_obj(lv_disp_get_layer_sys(disp), &proc->types.pointer.act_point);
    825         if(indev_obj_act == NULL) indev_obj_act = lv_indev_search_obj(lv_disp_get_layer_top(disp),
    826                                                                           &proc->types.pointer.act_point);
    827         if(indev_obj_act == NULL) indev_obj_act = lv_indev_search_obj(lv_disp_get_scr_act(disp),
    828                                                                           &proc->types.pointer.act_point);
    829         new_obj_searched = true;
    830     }
    831     /*If there is last object but it is not scrolled and not protected also search*/
    832     else if(proc->types.pointer.scroll_obj == NULL &&
    833             lv_obj_has_flag(indev_obj_act, LV_OBJ_FLAG_PRESS_LOCK) == false) {
    834         indev_obj_act = lv_indev_search_obj(lv_disp_get_layer_sys(disp), &proc->types.pointer.act_point);
    835         if(indev_obj_act == NULL) indev_obj_act = lv_indev_search_obj(lv_disp_get_layer_top(disp),
    836                                                                           &proc->types.pointer.act_point);
    837         if(indev_obj_act == NULL) indev_obj_act = lv_indev_search_obj(lv_disp_get_scr_act(disp),
    838                                                                           &proc->types.pointer.act_point);
    839         new_obj_searched = true;
    840     }
    841 
    842     /*The last object might have scroll throw. Stop it manually*/
    843     if(new_obj_searched && proc->types.pointer.last_obj) {
    844         proc->types.pointer.scroll_throw_vect.x = 0;
    845         proc->types.pointer.scroll_throw_vect.y = 0;
    846         _lv_indev_scroll_throw_handler(proc);
    847         if(indev_reset_check(proc)) return;
    848     }
    849 
    850     /*If a new object was found reset some variables and send a pressed Call the ancestor's event handler*/
    851     if(indev_obj_act != proc->types.pointer.act_obj) {
    852         proc->types.pointer.last_point.x = proc->types.pointer.act_point.x;
    853         proc->types.pointer.last_point.y = proc->types.pointer.act_point.y;
    854 
    855         /*If a new object found the previous was lost, so send a Call the ancestor's event handler*/
    856         if(proc->types.pointer.act_obj != NULL) {
    857             /*Save the obj because in special cases `act_obj` can change in the Call the ancestor's event handler function*/
    858             lv_obj_t * last_obj = proc->types.pointer.act_obj;
    859 
    860             lv_event_send(last_obj, LV_EVENT_PRESS_LOST, indev_act);
    861             if(indev_reset_check(proc)) return;
    862         }
    863 
    864         proc->types.pointer.act_obj  = indev_obj_act; /*Save the pressed object*/
    865         proc->types.pointer.last_obj = indev_obj_act;
    866 
    867         if(indev_obj_act != NULL) {
    868             /*Save the time when the obj pressed to count long press time.*/
    869             proc->pr_timestamp                 = lv_tick_get();
    870             proc->long_pr_sent                 = 0;
    871             proc->types.pointer.scroll_sum.x     = 0;
    872             proc->types.pointer.scroll_sum.y     = 0;
    873             proc->types.pointer.scroll_dir = LV_DIR_NONE;
    874             proc->types.pointer.gesture_dir = LV_DIR_NONE;
    875             proc->types.pointer.gesture_sent   = 0;
    876             proc->types.pointer.gesture_sum.x  = 0;
    877             proc->types.pointer.gesture_sum.y  = 0;
    878             proc->types.pointer.vect.x         = 0;
    879             proc->types.pointer.vect.y         = 0;
    880 
    881             /*Call the ancestor's event handler about the press*/
    882             lv_event_send(indev_obj_act, LV_EVENT_PRESSED, indev_act);
    883             if(indev_reset_check(proc)) return;
    884 
    885             if(indev_act->proc.wait_until_release) return;
    886 
    887             /*Handle focus*/
    888             indev_click_focus(&indev_act->proc);
    889             if(indev_reset_check(proc)) return;
    890 
    891         }
    892     }
    893 
    894     /*Calculate the vector and apply a low pass filter: new value = 0.5 * old_value + 0.5 * new_value*/
    895     proc->types.pointer.vect.x = proc->types.pointer.act_point.x - proc->types.pointer.last_point.x;
    896     proc->types.pointer.vect.y = proc->types.pointer.act_point.y - proc->types.pointer.last_point.y;
    897 
    898     proc->types.pointer.scroll_throw_vect.x = (proc->types.pointer.scroll_throw_vect.x * 4) >> 3;
    899     proc->types.pointer.scroll_throw_vect.y = (proc->types.pointer.scroll_throw_vect.y * 4) >> 3;
    900 
    901     proc->types.pointer.scroll_throw_vect.x += (proc->types.pointer.vect.x * 4) >> 3;
    902     proc->types.pointer.scroll_throw_vect.y += (proc->types.pointer.vect.y * 4) >> 3;
    903 
    904     proc->types.pointer.scroll_throw_vect_ori = proc->types.pointer.scroll_throw_vect;
    905 
    906     if(indev_obj_act) {
    907         lv_event_send(indev_obj_act, LV_EVENT_PRESSING, indev_act);
    908         if(indev_reset_check(proc)) return;
    909 
    910         if(indev_act->proc.wait_until_release) return;
    911 
    912         _lv_indev_scroll_handler(proc);
    913         if(indev_reset_check(proc)) return;
    914         indev_gesture(proc);
    915         if(indev_reset_check(proc)) return;
    916 
    917         /*If there is no scrolling then check for long press time*/
    918         if(proc->types.pointer.scroll_obj == NULL && proc->long_pr_sent == 0) {
    919             /*Call the ancestor's event handler about the long press if enough time elapsed*/
    920             if(lv_tick_elaps(proc->pr_timestamp) > indev_act->driver->long_press_time) {
    921                 lv_event_send(indev_obj_act, LV_EVENT_LONG_PRESSED, indev_act);
    922                 if(indev_reset_check(proc)) return;
    923 
    924                 /*Mark the Call the ancestor's event handler sending to do not send it again*/
    925                 proc->long_pr_sent = 1;
    926 
    927                 /*Save the long press time stamp for the long press repeat handler*/
    928                 proc->longpr_rep_timestamp = lv_tick_get();
    929             }
    930         }
    931 
    932         /*Send long press repeated Call the ancestor's event handler*/
    933         if(proc->types.pointer.scroll_obj == NULL && proc->long_pr_sent == 1) {
    934             /*Call the ancestor's event handler about the long press repeat if enough time elapsed*/
    935             if(lv_tick_elaps(proc->longpr_rep_timestamp) > indev_act->driver->long_press_repeat_time) {
    936                 lv_event_send(indev_obj_act, LV_EVENT_LONG_PRESSED_REPEAT, indev_act);
    937                 if(indev_reset_check(proc)) return;
    938                 proc->longpr_rep_timestamp = lv_tick_get();
    939             }
    940         }
    941     }
    942 }
    943 
    944 /**
    945  * Process the released state of LV_INDEV_TYPE_POINTER input devices
    946  * @param proc pointer to an input device 'proc'
    947  */
    948 static void indev_proc_release(_lv_indev_proc_t * proc)
    949 {
    950     if(proc->wait_until_release != 0) {
    951         proc->types.pointer.act_obj  = NULL;
    952         proc->types.pointer.last_obj = NULL;
    953         proc->pr_timestamp           = 0;
    954         proc->longpr_rep_timestamp   = 0;
    955         proc->wait_until_release     = 0;
    956     }
    957     indev_obj_act = proc->types.pointer.act_obj;
    958     lv_obj_t * scroll_obj = proc->types.pointer.scroll_obj;
    959 
    960     /*Forget the act obj and send a released Call the ancestor's event handler*/
    961     if(indev_obj_act) {
    962         LV_LOG_INFO("released");
    963 
    964         /*Send RELEASE Call the ancestor's event handler and event*/
    965         lv_event_send(indev_obj_act, LV_EVENT_RELEASED, indev_act);
    966         if(indev_reset_check(proc)) return;
    967 
    968         /*Send CLICK if no scrolling*/
    969         if(scroll_obj == NULL) {
    970             if(proc->long_pr_sent == 0) {
    971                 lv_event_send(indev_obj_act, LV_EVENT_SHORT_CLICKED, indev_act);
    972                 if(indev_reset_check(proc)) return;
    973             }
    974 
    975             lv_event_send(indev_obj_act, LV_EVENT_CLICKED, indev_act);
    976             if(indev_reset_check(proc)) return;
    977         }
    978 
    979         proc->types.pointer.act_obj = NULL;
    980         proc->pr_timestamp          = 0;
    981         proc->longpr_rep_timestamp  = 0;
    982 
    983     }
    984 
    985     /*The reset can be set in the Call the ancestor's event handler function.
    986      * In case of reset query ignore the remaining parts.*/
    987     if(scroll_obj) {
    988         _lv_indev_scroll_throw_handler(proc);
    989         if(indev_reset_check(proc)) return;
    990     }
    991 }
    992 
    993 /**
    994  * Process a new point from LV_INDEV_TYPE_BUTTON input device
    995  * @param i pointer to an input device
    996  * @param data pointer to the data read from the input device
    997  * Reset input device if a reset query has been sent to it
    998  * @param indev pointer to an input device
    999  */
   1000 static void indev_proc_reset_query_handler(lv_indev_t * indev)
   1001 {
   1002     if(indev->proc.reset_query) {
   1003         indev->proc.types.pointer.act_obj           = NULL;
   1004         indev->proc.types.pointer.last_obj          = NULL;
   1005         indev->proc.types.pointer.scroll_obj          = NULL;
   1006         indev->proc.long_pr_sent                    = 0;
   1007         indev->proc.pr_timestamp                    = 0;
   1008         indev->proc.longpr_rep_timestamp            = 0;
   1009         indev->proc.types.pointer.scroll_sum.x        = 0;
   1010         indev->proc.types.pointer.scroll_sum.y        = 0;
   1011         indev->proc.types.pointer.scroll_dir = LV_DIR_NONE;
   1012         indev->proc.types.pointer.scroll_throw_vect.x = 0;
   1013         indev->proc.types.pointer.scroll_throw_vect.y = 0;
   1014         indev->proc.types.pointer.gesture_sum.x     = 0;
   1015         indev->proc.types.pointer.gesture_sum.y     = 0;
   1016         indev->proc.reset_query                     = 0;
   1017         indev_obj_act                               = NULL;
   1018     }
   1019 }
   1020 
   1021 /**
   1022  * Handle focus/defocus on click for POINTER input devices
   1023  * @param proc pointer to the state of the indev
   1024  */
   1025 static void indev_click_focus(_lv_indev_proc_t * proc)
   1026 {
   1027     /*Handle click focus*/
   1028     if(lv_obj_has_flag(indev_obj_act, LV_OBJ_FLAG_CLICK_FOCUSABLE) == false ||
   1029        proc->types.pointer.last_pressed == indev_obj_act) {
   1030         return;
   1031     }
   1032 
   1033     lv_group_t * g_act = lv_obj_get_group(indev_obj_act);
   1034     lv_group_t * g_prev = proc->types.pointer.last_pressed ? lv_obj_get_group(proc->types.pointer.last_pressed) : NULL;
   1035 
   1036     /*If both the last and act. obj. are in the same group (or have no group)*/
   1037     if(g_act == g_prev) {
   1038         /*The objects are in a group*/
   1039         if(g_act) {
   1040             lv_group_focus_obj(indev_obj_act);
   1041             if(indev_reset_check(proc)) return;
   1042         }
   1043         /*The object are not in group*/
   1044         else {
   1045             if(proc->types.pointer.last_pressed) {
   1046                 lv_event_send(proc->types.pointer.last_pressed, LV_EVENT_DEFOCUSED, indev_act);
   1047                 if(indev_reset_check(proc)) return;
   1048             }
   1049 
   1050             lv_event_send(indev_obj_act, LV_EVENT_FOCUSED, indev_act);
   1051             if(indev_reset_check(proc)) return;
   1052         }
   1053     }
   1054     /*The object are not in the same group (in different groups or one has no group)*/
   1055     else {
   1056         /*If the prev. obj. is not in a group then defocus it.*/
   1057         if(g_prev == NULL && proc->types.pointer.last_pressed) {
   1058             lv_event_send(proc->types.pointer.last_pressed, LV_EVENT_DEFOCUSED, indev_act);
   1059             if(indev_reset_check(proc)) return;
   1060         }
   1061         /*Focus on a non-group object*/
   1062         else {
   1063             if(proc->types.pointer.last_pressed) {
   1064                 /*If the prev. object also wasn't in a group defocus it*/
   1065                 if(g_prev == NULL) {
   1066                     lv_event_send(proc->types.pointer.last_pressed, LV_EVENT_DEFOCUSED, indev_act);
   1067                     if(indev_reset_check(proc)) return;
   1068                 }
   1069                 /*If the prev. object also was in a group at least "LEAVE" it instead of defocus*/
   1070                 else {
   1071                     lv_event_send(proc->types.pointer.last_pressed, LV_EVENT_LEAVE, indev_act);
   1072                     if(indev_reset_check(proc)) return;
   1073                 }
   1074             }
   1075         }
   1076 
   1077         /*Focus to the act. in its group*/
   1078         if(g_act) {
   1079             lv_group_focus_obj(indev_obj_act);
   1080             if(indev_reset_check(proc)) return;
   1081         }
   1082         else {
   1083             lv_event_send(indev_obj_act, LV_EVENT_FOCUSED, indev_act);
   1084             if(indev_reset_check(proc)) return;
   1085         }
   1086     }
   1087     proc->types.pointer.last_pressed = indev_obj_act;
   1088 }
   1089 
   1090 /**
   1091 * Handle the gesture of indev_proc_p->types.pointer.act_obj
   1092 * @param indev pointer to an input device state
   1093 */
   1094 void indev_gesture(_lv_indev_proc_t * proc)
   1095 {
   1096 
   1097     if(proc->types.pointer.scroll_obj) return;
   1098     if(proc->types.pointer.gesture_sent) return;
   1099 
   1100     lv_obj_t * gesture_obj = proc->types.pointer.act_obj;
   1101 
   1102     /*If gesture parent is active check recursively the gesture attribute*/
   1103     while(gesture_obj && lv_obj_has_flag(gesture_obj, LV_OBJ_FLAG_GESTURE_BUBBLE)) {
   1104         gesture_obj = lv_obj_get_parent(gesture_obj);
   1105     }
   1106 
   1107     if(gesture_obj == NULL) return;
   1108 
   1109     if((LV_ABS(proc->types.pointer.vect.x) < indev_act->driver->gesture_min_velocity) &&
   1110        (LV_ABS(proc->types.pointer.vect.y) < indev_act->driver->gesture_min_velocity)) {
   1111         proc->types.pointer.gesture_sum.x = 0;
   1112         proc->types.pointer.gesture_sum.y = 0;
   1113     }
   1114 
   1115     /*Count the movement by gesture*/
   1116     proc->types.pointer.gesture_sum.x += proc->types.pointer.vect.x;
   1117     proc->types.pointer.gesture_sum.y += proc->types.pointer.vect.y;
   1118 
   1119     if((LV_ABS(proc->types.pointer.gesture_sum.x) > indev_act->driver->gesture_limit) ||
   1120        (LV_ABS(proc->types.pointer.gesture_sum.y) > indev_act->driver->gesture_limit)) {
   1121 
   1122         proc->types.pointer.gesture_sent = 1;
   1123 
   1124         if(LV_ABS(proc->types.pointer.gesture_sum.x) > LV_ABS(proc->types.pointer.gesture_sum.y)) {
   1125             if(proc->types.pointer.gesture_sum.x > 0)
   1126                 proc->types.pointer.gesture_dir = LV_DIR_RIGHT;
   1127             else
   1128                 proc->types.pointer.gesture_dir = LV_DIR_LEFT;
   1129         }
   1130         else {
   1131             if(proc->types.pointer.gesture_sum.y > 0)
   1132                 proc->types.pointer.gesture_dir = LV_DIR_BOTTOM;
   1133             else
   1134                 proc->types.pointer.gesture_dir = LV_DIR_TOP;
   1135         }
   1136 
   1137         lv_event_send(gesture_obj, LV_EVENT_GESTURE, indev_act);
   1138         if(indev_reset_check(proc)) return;
   1139     }
   1140 }
   1141 
   1142 /**
   1143  * Checks if the reset_query flag has been set. If so, perform necessary global indev cleanup actions
   1144  * @param proc pointer to an input device 'proc'
   1145  * @return true if indev query should be immediately truncated.
   1146  */
   1147 static bool indev_reset_check(_lv_indev_proc_t * proc)
   1148 {
   1149     if(proc->reset_query) {
   1150         indev_obj_act = NULL;
   1151     }
   1152 
   1153     return proc->reset_query ? true : false;
   1154 }