/** * @file lv_event.c * */ /********************* * INCLUDES *********************/ #include "lv_obj.h" #include "lv_indev.h" /********************* * DEFINES *********************/ #define MY_CLASS &lv_obj_class /********************** * TYPEDEFS **********************/ typedef struct _lv_event_dsc_t { lv_event_cb_t cb; void * user_data; lv_event_code_t filter : 8; } lv_event_dsc_t; /********************** * STATIC PROTOTYPES **********************/ static lv_event_dsc_t * lv_obj_get_event_dsc(const lv_obj_t * obj, uint32_t id); static lv_res_t event_send_core(lv_event_t * e); static bool event_is_bubbled(lv_event_t * e); /********************** * STATIC VARIABLES **********************/ static lv_event_t * event_head; /********************** * MACROS **********************/ #if LV_LOG_TRACE_EVENT #define EVENT_TRACE(...) LV_LOG_TRACE(__VA_ARGS__) #else #define EVENT_TRACE(...) #endif /********************** * GLOBAL FUNCTIONS **********************/ lv_res_t lv_event_send(lv_obj_t * obj, lv_event_code_t event_code, void * param) { if(obj == NULL) return LV_RES_OK; LV_ASSERT_OBJ(obj, MY_CLASS); lv_event_t e; e.target = obj; e.current_target = obj; e.code = event_code; e.user_data = NULL; e.param = param; e.deleted = 0; e.stop_bubbling = 0; e.stop_processing = 0; /*Build a simple linked list from the objects used in the events *It's important to know if this object was deleted by a nested event *called from this `event_cb`.*/ e.prev = event_head; event_head = &e; /*Send the event*/ lv_res_t res = event_send_core(&e); /*Remove this element from the list*/ event_head = e.prev; return res; } lv_res_t lv_obj_event_base(const lv_obj_class_t * class_p, lv_event_t * e) { const lv_obj_class_t * base; if(class_p == NULL) base = e->current_target->class_p; else base = class_p->base_class; /*Find a base in which call the ancestor's event handler_cb if set*/ while(base && base->event_cb == NULL) base = base->base_class; if(base == NULL) return LV_RES_OK; if(base->event_cb == NULL) return LV_RES_OK; /*Call the actual event callback*/ e->user_data = NULL; base->event_cb(base, e); lv_res_t res = LV_RES_OK; /*Stop if the object is deleted*/ if(e->deleted) res = LV_RES_INV; return res; } lv_obj_t * lv_event_get_target(lv_event_t * e) { return e->target; } lv_obj_t * lv_event_get_current_target(lv_event_t * e) { return e->current_target; } lv_event_code_t lv_event_get_code(lv_event_t * e) { return e->code & ~LV_EVENT_PREPROCESS; } void * lv_event_get_param(lv_event_t * e) { return e->param; } void * lv_event_get_user_data(lv_event_t * e) { return e->user_data; } void lv_event_stop_bubbling(lv_event_t * e) { e->stop_bubbling = 1; } void lv_event_stop_processing(lv_event_t * e) { e->stop_processing = 1; } uint32_t lv_event_register_id(void) { static uint32_t last_id = _LV_EVENT_LAST; last_id ++; return last_id; } void _lv_event_mark_deleted(lv_obj_t * obj) { lv_event_t * e = event_head; while(e) { if(e->current_target == obj || e->target == obj) e->deleted = 1; e = e->prev; } } struct _lv_event_dsc_t * lv_obj_add_event_cb(lv_obj_t * obj, lv_event_cb_t event_cb, lv_event_code_t filter, void * user_data) { LV_ASSERT_OBJ(obj, MY_CLASS); lv_obj_allocate_spec_attr(obj); obj->spec_attr->event_dsc_cnt++; obj->spec_attr->event_dsc = lv_mem_realloc(obj->spec_attr->event_dsc, obj->spec_attr->event_dsc_cnt * sizeof(lv_event_dsc_t)); LV_ASSERT_MALLOC(obj->spec_attr->event_dsc); obj->spec_attr->event_dsc[obj->spec_attr->event_dsc_cnt - 1].cb = event_cb; obj->spec_attr->event_dsc[obj->spec_attr->event_dsc_cnt - 1].filter = filter; obj->spec_attr->event_dsc[obj->spec_attr->event_dsc_cnt - 1].user_data = user_data; return &obj->spec_attr->event_dsc[obj->spec_attr->event_dsc_cnt - 1]; } bool lv_obj_remove_event_cb(lv_obj_t * obj, lv_event_cb_t event_cb) { LV_ASSERT_OBJ(obj, MY_CLASS); if(obj->spec_attr == NULL) return false; int32_t i = 0; for(i = 0; i < obj->spec_attr->event_dsc_cnt; i++) { if(event_cb == NULL || obj->spec_attr->event_dsc[i].cb == event_cb) { /*Shift the remaining event handlers forward*/ for(; i < (obj->spec_attr->event_dsc_cnt - 1); i++) { obj->spec_attr->event_dsc[i] = obj->spec_attr->event_dsc[i + 1]; } obj->spec_attr->event_dsc_cnt--; obj->spec_attr->event_dsc = lv_mem_realloc(obj->spec_attr->event_dsc, obj->spec_attr->event_dsc_cnt * sizeof(lv_event_dsc_t)); LV_ASSERT_MALLOC(obj->spec_attr->event_dsc); return true; } } /*No event handler found*/ return false; } bool lv_obj_remove_event_cb_with_user_data(lv_obj_t * obj, lv_event_cb_t event_cb, const void * user_data) { LV_ASSERT_OBJ(obj, MY_CLASS); if(obj->spec_attr == NULL) return false; int32_t i = 0; for(i = 0; i < obj->spec_attr->event_dsc_cnt; i++) { if((event_cb == NULL || obj->spec_attr->event_dsc[i].cb == event_cb) && obj->spec_attr->event_dsc[i].user_data == user_data) { /*Shift the remaining event handlers forward*/ for(; i < (obj->spec_attr->event_dsc_cnt - 1); i++) { obj->spec_attr->event_dsc[i] = obj->spec_attr->event_dsc[i + 1]; } obj->spec_attr->event_dsc_cnt--; obj->spec_attr->event_dsc = lv_mem_realloc(obj->spec_attr->event_dsc, obj->spec_attr->event_dsc_cnt * sizeof(lv_event_dsc_t)); LV_ASSERT_MALLOC(obj->spec_attr->event_dsc); return true; } } /*No event handler found*/ return false; } bool lv_obj_remove_event_dsc(lv_obj_t * obj, struct _lv_event_dsc_t * event_dsc) { LV_ASSERT_OBJ(obj, MY_CLASS); if(obj->spec_attr == NULL) return false; int32_t i = 0; for(i = 0; i < obj->spec_attr->event_dsc_cnt; i++) { if(&obj->spec_attr->event_dsc[i] == event_dsc) { /*Shift the remaining event handlers forward*/ for(; i < (obj->spec_attr->event_dsc_cnt - 1); i++) { obj->spec_attr->event_dsc[i] = obj->spec_attr->event_dsc[i + 1]; } obj->spec_attr->event_dsc_cnt--; obj->spec_attr->event_dsc = lv_mem_realloc(obj->spec_attr->event_dsc, obj->spec_attr->event_dsc_cnt * sizeof(lv_event_dsc_t)); LV_ASSERT_MALLOC(obj->spec_attr->event_dsc); return true; } } /*No event handler found*/ return false; } void * lv_obj_get_event_user_data(struct _lv_obj_t * obj, lv_event_cb_t event_cb) { LV_ASSERT_OBJ(obj, MY_CLASS); if(obj->spec_attr == NULL) return NULL; int32_t i = 0; for(i = 0; i < obj->spec_attr->event_dsc_cnt; i++) { if(event_cb == obj->spec_attr->event_dsc[i].cb) return obj->spec_attr->event_dsc[i].user_data; } return NULL; } lv_indev_t * lv_event_get_indev(lv_event_t * e) { if(e->code == LV_EVENT_PRESSED || e->code == LV_EVENT_PRESSING || e->code == LV_EVENT_PRESS_LOST || e->code == LV_EVENT_SHORT_CLICKED || e->code == LV_EVENT_LONG_PRESSED || e->code == LV_EVENT_LONG_PRESSED_REPEAT || e->code == LV_EVENT_CLICKED || e->code == LV_EVENT_RELEASED || e->code == LV_EVENT_SCROLL_BEGIN || e->code == LV_EVENT_SCROLL_END || e->code == LV_EVENT_SCROLL || e->code == LV_EVENT_GESTURE || e->code == LV_EVENT_KEY || e->code == LV_EVENT_FOCUSED || e->code == LV_EVENT_DEFOCUSED || e->code == LV_EVENT_LEAVE) { return lv_event_get_param(e); } else { LV_LOG_WARN("Not interpreted with this event code"); return NULL; } } lv_obj_draw_part_dsc_t * lv_event_get_draw_part_dsc(lv_event_t * e) { if(e->code == LV_EVENT_DRAW_PART_BEGIN || e->code == LV_EVENT_DRAW_PART_END) { return lv_event_get_param(e); } else { LV_LOG_WARN("Not interpreted with this event code"); return NULL; } } lv_draw_ctx_t * lv_event_get_draw_ctx(lv_event_t * e) { if(e->code == LV_EVENT_DRAW_MAIN || e->code == LV_EVENT_DRAW_MAIN_BEGIN || e->code == LV_EVENT_DRAW_MAIN_END || e->code == LV_EVENT_DRAW_POST || e->code == LV_EVENT_DRAW_POST_BEGIN || e->code == LV_EVENT_DRAW_POST_END) { return lv_event_get_param(e); } else { LV_LOG_WARN("Not interpreted with this event code"); return NULL; } } const lv_area_t * lv_event_get_old_size(lv_event_t * e) { if(e->code == LV_EVENT_SIZE_CHANGED) { return lv_event_get_param(e); } else { LV_LOG_WARN("Not interpreted with this event code"); return NULL; } } uint32_t lv_event_get_key(lv_event_t * e) { if(e->code == LV_EVENT_KEY) { uint32_t * k = lv_event_get_param(e); if(k) return *k; else return 0; } else { LV_LOG_WARN("Not interpreted with this event code"); return 0; } } lv_anim_t * lv_event_get_scroll_anim(lv_event_t * e) { if(e->code == LV_EVENT_SCROLL_BEGIN) { return lv_event_get_param(e); } else { LV_LOG_WARN("Not interpreted with this event code"); return 0; } } void lv_event_set_ext_draw_size(lv_event_t * e, lv_coord_t size) { if(e->code == LV_EVENT_REFR_EXT_DRAW_SIZE) { lv_coord_t * cur_size = lv_event_get_param(e); *cur_size = LV_MAX(*cur_size, size); } else { LV_LOG_WARN("Not interpreted with this event code"); } } lv_point_t * lv_event_get_self_size_info(lv_event_t * e) { if(e->code == LV_EVENT_GET_SELF_SIZE) { return lv_event_get_param(e); } else { LV_LOG_WARN("Not interpreted with this event code"); return 0; } } lv_hit_test_info_t * lv_event_get_hit_test_info(lv_event_t * e) { if(e->code == LV_EVENT_HIT_TEST) { return lv_event_get_param(e); } else { LV_LOG_WARN("Not interpreted with this event code"); return 0; } } const lv_area_t * lv_event_get_cover_area(lv_event_t * e) { if(e->code == LV_EVENT_COVER_CHECK) { lv_cover_check_info_t * p = lv_event_get_param(e); return p->area; } else { LV_LOG_WARN("Not interpreted with this event code"); return NULL; } } void lv_event_set_cover_res(lv_event_t * e, lv_cover_res_t res) { if(e->code == LV_EVENT_COVER_CHECK) { lv_cover_check_info_t * p = lv_event_get_param(e); if(res > p->res) p->res = res; /*Save only "stronger" results*/ } else { LV_LOG_WARN("Not interpreted with this event code"); } } /********************** * STATIC FUNCTIONS **********************/ static lv_event_dsc_t * lv_obj_get_event_dsc(const lv_obj_t * obj, uint32_t id) { LV_ASSERT_OBJ(obj, MY_CLASS); if(!obj->spec_attr) return NULL; if(id >= obj->spec_attr->event_dsc_cnt) return NULL; return &obj->spec_attr->event_dsc[id]; } static lv_res_t event_send_core(lv_event_t * e) { EVENT_TRACE("Sending event %d to %p with %p param", e->code, (void *)e->current_target, e->param); /*Call the input device's feedback callback if set*/ lv_indev_t * indev_act = lv_indev_get_act(); if(indev_act) { if(indev_act->driver->feedback_cb) indev_act->driver->feedback_cb(indev_act->driver, e->code); if(e->stop_processing) return LV_RES_OK; if(e->deleted) return LV_RES_INV; } lv_res_t res = LV_RES_OK; lv_event_dsc_t * event_dsc = lv_obj_get_event_dsc(e->current_target, 0); uint32_t i = 0; while(event_dsc && res == LV_RES_OK) { if(event_dsc->cb && ((event_dsc->filter & LV_EVENT_PREPROCESS) == LV_EVENT_PREPROCESS) && (event_dsc->filter == (LV_EVENT_ALL | LV_EVENT_PREPROCESS) || (event_dsc->filter & ~LV_EVENT_PREPROCESS) == e->code)) { e->user_data = event_dsc->user_data; event_dsc->cb(e); if(e->stop_processing) return LV_RES_OK; /*Stop if the object is deleted*/ if(e->deleted) return LV_RES_INV; } i++; event_dsc = lv_obj_get_event_dsc(e->current_target, i); } res = lv_obj_event_base(NULL, e); event_dsc = res == LV_RES_INV ? NULL : lv_obj_get_event_dsc(e->current_target, 0); i = 0; while(event_dsc && res == LV_RES_OK) { if(event_dsc->cb && ((event_dsc->filter & LV_EVENT_PREPROCESS) == 0) && (event_dsc->filter == LV_EVENT_ALL || event_dsc->filter == e->code)) { e->user_data = event_dsc->user_data; event_dsc->cb(e); if(e->stop_processing) return LV_RES_OK; /*Stop if the object is deleted*/ if(e->deleted) return LV_RES_INV; } i++; event_dsc = lv_obj_get_event_dsc(e->current_target, i); } if(res == LV_RES_OK && e->current_target->parent && event_is_bubbled(e)) { e->current_target = e->current_target->parent; res = event_send_core(e); if(res != LV_RES_OK) return LV_RES_INV; } return res; } static bool event_is_bubbled(lv_event_t * e) { if(e->stop_bubbling) return false; /*Event codes that always bubble*/ switch(e->code) { case LV_EVENT_CHILD_CREATED: case LV_EVENT_CHILD_DELETED: return true; default: break; } /*Check other codes only if bubbling is enabled*/ if(lv_obj_has_flag(e->current_target, LV_OBJ_FLAG_EVENT_BUBBLE) == false) return false; switch(e->code) { case LV_EVENT_HIT_TEST: case LV_EVENT_COVER_CHECK: case LV_EVENT_REFR_EXT_DRAW_SIZE: case LV_EVENT_DRAW_MAIN_BEGIN: case LV_EVENT_DRAW_MAIN: case LV_EVENT_DRAW_MAIN_END: case LV_EVENT_DRAW_POST_BEGIN: case LV_EVENT_DRAW_POST: case LV_EVENT_DRAW_POST_END: case LV_EVENT_DRAW_PART_BEGIN: case LV_EVENT_DRAW_PART_END: case LV_EVENT_REFRESH: case LV_EVENT_DELETE: case LV_EVENT_CHILD_CREATED: case LV_EVENT_CHILD_DELETED: case LV_EVENT_CHILD_CHANGED: case LV_EVENT_SIZE_CHANGED: case LV_EVENT_STYLE_CHANGED: case LV_EVENT_GET_SELF_SIZE: return false; default: return true; } }