405 lines
11 KiB
C
405 lines
11 KiB
C
/**
|
|
* @file lv_event.c
|
|
*
|
|
*/
|
|
|
|
/*********************
|
|
* INCLUDES
|
|
*********************/
|
|
#include "lv_event_private.h"
|
|
#include "../core/lv_global.h"
|
|
#include "../stdlib/lv_mem.h"
|
|
#include "lv_assert.h"
|
|
#include "lv_types.h"
|
|
|
|
/*********************
|
|
* DEFINES
|
|
*********************/
|
|
|
|
#define event_head LV_GLOBAL_DEFAULT()->event_header
|
|
#define event_last_id LV_GLOBAL_DEFAULT()->event_last_register_id
|
|
|
|
/**********************
|
|
* TYPEDEFS
|
|
**********************/
|
|
|
|
/**********************
|
|
* STATIC PROTOTYPES
|
|
**********************/
|
|
|
|
/* Traverse the list to delete the objects marked for deletion */
|
|
static void cleanup_event_list(lv_event_list_t * list);
|
|
static void cleanup_event_list_core(lv_array_t * array);
|
|
|
|
static void event_mark_deleting(lv_event_list_t * list, lv_event_dsc_t * dsc);
|
|
static bool event_is_marked_deleting(lv_event_dsc_t * dsc);
|
|
static uint32_t event_array_size(lv_event_list_t * list);
|
|
static lv_event_dsc_t ** event_array_at(lv_event_list_t * list, uint32_t index);
|
|
|
|
/**********************
|
|
* STATIC VARIABLES
|
|
**********************/
|
|
|
|
/**********************
|
|
* MACROS
|
|
**********************/
|
|
|
|
#if LV_USE_LOG && LV_LOG_TRACE_EVENT
|
|
#define LV_TRACE_EVENT(...) LV_LOG_TRACE(__VA_ARGS__)
|
|
#else
|
|
#define LV_TRACE_EVENT(...)
|
|
#endif
|
|
|
|
/**********************
|
|
* GLOBAL FUNCTIONS
|
|
**********************/
|
|
|
|
void lv_event_push(lv_event_t * e)
|
|
{
|
|
/*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;
|
|
|
|
}
|
|
|
|
void lv_event_pop(lv_event_t * e)
|
|
{
|
|
event_head = e->prev;
|
|
}
|
|
|
|
lv_result_t lv_event_send(lv_event_list_t * list, lv_event_t * e, bool preprocess)
|
|
{
|
|
if(list == NULL) return LV_RESULT_OK;
|
|
if(e->deleted) return LV_RESULT_INVALID;
|
|
|
|
/* When obj is deleted in its own event, it will cause the `list->array` header to be released,
|
|
* but the content still exists, which leads to memory leakage.
|
|
* Therefore, back up the header in advance,
|
|
* which can strive to release the memory and prevent used-after-free. */
|
|
lv_array_t back_array_head = list->array;
|
|
|
|
/* Dealing with the problem of nested event deletion event */
|
|
const bool is_traversing = list->is_traversing;
|
|
list->is_traversing = true;
|
|
|
|
lv_result_t res = LV_RESULT_OK;
|
|
const uint32_t size = event_array_size(list);
|
|
for(uint32_t i = 0; i < size && !e->deleted; i++) {
|
|
lv_event_dsc_t * dsc = *event_array_at(list, i);
|
|
if(dsc->cb == NULL) continue;
|
|
if(event_is_marked_deleting(dsc)) continue;
|
|
const bool is_preprocessed = (dsc->filter & LV_EVENT_PREPROCESS) != 0;
|
|
if(is_preprocessed != preprocess) continue;
|
|
lv_event_code_t filter = dsc->filter & ~LV_EVENT_PREPROCESS;
|
|
if(filter == LV_EVENT_ALL || filter == e->code) {
|
|
e->user_data = dsc->user_data;
|
|
dsc->cb(e);
|
|
if(e->stop_processing) break;
|
|
|
|
/*Stop if the object is deleted*/
|
|
if(e->deleted) {
|
|
res = LV_RESULT_INVALID;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
if(is_traversing) return res;
|
|
|
|
if(e->deleted) cleanup_event_list_core(&back_array_head);
|
|
else {
|
|
list->is_traversing = false;
|
|
cleanup_event_list(list);
|
|
}
|
|
|
|
return res;
|
|
}
|
|
|
|
lv_event_dsc_t * lv_event_add(lv_event_list_t * list, lv_event_cb_t cb, lv_event_code_t filter,
|
|
void * user_data)
|
|
{
|
|
lv_event_dsc_t * dsc = lv_malloc(sizeof(lv_event_dsc_t));
|
|
LV_ASSERT_NULL(dsc);
|
|
|
|
dsc->cb = cb;
|
|
dsc->filter = filter;
|
|
dsc->user_data = user_data;
|
|
|
|
if(event_array_size(list) == 0) {
|
|
/*event list hasn't been initialized.*/
|
|
lv_array_init(&list->array, 1, sizeof(lv_event_dsc_t *));
|
|
}
|
|
|
|
lv_array_push_back(&list->array, &dsc);
|
|
return dsc;
|
|
}
|
|
|
|
bool lv_event_remove_dsc(lv_event_list_t * list, lv_event_dsc_t * dsc)
|
|
{
|
|
LV_ASSERT_NULL(list);
|
|
LV_ASSERT_NULL(dsc);
|
|
|
|
const uint32_t size = event_array_size(list);
|
|
for(uint32_t i = 0; i < size; i++) {
|
|
lv_event_dsc_t * event = *event_array_at(list, i);
|
|
if(event == dsc) {
|
|
event_mark_deleting(list, event);
|
|
cleanup_event_list(list);
|
|
return true;
|
|
}
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
uint32_t lv_event_get_count(lv_event_list_t * list)
|
|
{
|
|
LV_ASSERT_NULL(list);
|
|
return event_array_size(list);
|
|
}
|
|
|
|
lv_event_dsc_t * lv_event_get_dsc(lv_event_list_t * list, uint32_t index)
|
|
{
|
|
LV_ASSERT_NULL(list);
|
|
lv_event_dsc_t ** dsc = event_array_at(list, index);
|
|
return dsc ? *dsc : NULL;
|
|
}
|
|
|
|
lv_event_cb_t lv_event_dsc_get_cb(lv_event_dsc_t * dsc)
|
|
{
|
|
LV_ASSERT_NULL(dsc);
|
|
return dsc->cb;
|
|
}
|
|
|
|
void * lv_event_dsc_get_user_data(lv_event_dsc_t * dsc)
|
|
{
|
|
LV_ASSERT_NULL(dsc);
|
|
return dsc->user_data;
|
|
|
|
}
|
|
|
|
bool lv_event_remove(lv_event_list_t * list, uint32_t index)
|
|
{
|
|
LV_ASSERT_NULL(list);
|
|
lv_event_dsc_t * dsc = lv_event_get_dsc(list, index);
|
|
if(dsc == NULL) return false;
|
|
event_mark_deleting(list, dsc);
|
|
cleanup_event_list(list);
|
|
return true;
|
|
}
|
|
|
|
void lv_event_remove_all(lv_event_list_t * list)
|
|
{
|
|
LV_ASSERT_NULL(list);
|
|
const uint32_t size = event_array_size(list);
|
|
for(uint32_t i = 0; i < size; i++)
|
|
event_mark_deleting(list, *event_array_at(list, i));
|
|
cleanup_event_list(list);
|
|
}
|
|
|
|
void * lv_event_get_current_target(lv_event_t * e)
|
|
{
|
|
return e->current_target;
|
|
}
|
|
|
|
void * lv_event_get_target(lv_event_t * e)
|
|
{
|
|
return e->original_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)
|
|
{
|
|
event_last_id ++;
|
|
return event_last_id;
|
|
}
|
|
|
|
void lv_event_mark_deleted(void * target)
|
|
{
|
|
lv_event_t * e = event_head;
|
|
|
|
while(e) {
|
|
if(e->original_target == target || e->current_target == target) e->deleted = 1;
|
|
e = e->prev;
|
|
}
|
|
}
|
|
|
|
const char * lv_event_code_get_name(lv_event_code_t code)
|
|
{
|
|
/*Remove the preprocess flag*/
|
|
code &= ~LV_EVENT_PREPROCESS;
|
|
|
|
#define ENUM_CASE(x) case LV_##x: return #x
|
|
|
|
switch(code) {
|
|
ENUM_CASE(EVENT_ALL);
|
|
|
|
/** Input device events*/
|
|
ENUM_CASE(EVENT_PRESSED);
|
|
ENUM_CASE(EVENT_PRESSING);
|
|
ENUM_CASE(EVENT_PRESS_LOST);
|
|
ENUM_CASE(EVENT_SHORT_CLICKED);
|
|
ENUM_CASE(EVENT_SINGLE_CLICKED);
|
|
ENUM_CASE(EVENT_DOUBLE_CLICKED);
|
|
ENUM_CASE(EVENT_TRIPLE_CLICKED);
|
|
ENUM_CASE(EVENT_LONG_PRESSED);
|
|
ENUM_CASE(EVENT_LONG_PRESSED_REPEAT);
|
|
ENUM_CASE(EVENT_CLICKED);
|
|
ENUM_CASE(EVENT_RELEASED);
|
|
ENUM_CASE(EVENT_SCROLL_BEGIN);
|
|
ENUM_CASE(EVENT_SCROLL_THROW_BEGIN);
|
|
ENUM_CASE(EVENT_SCROLL_END);
|
|
ENUM_CASE(EVENT_SCROLL);
|
|
ENUM_CASE(EVENT_GESTURE);
|
|
ENUM_CASE(EVENT_KEY);
|
|
ENUM_CASE(EVENT_ROTARY);
|
|
ENUM_CASE(EVENT_FOCUSED);
|
|
ENUM_CASE(EVENT_DEFOCUSED);
|
|
ENUM_CASE(EVENT_LEAVE);
|
|
ENUM_CASE(EVENT_HIT_TEST);
|
|
ENUM_CASE(EVENT_INDEV_RESET);
|
|
ENUM_CASE(EVENT_HOVER_OVER);
|
|
ENUM_CASE(EVENT_HOVER_LEAVE);
|
|
|
|
/** Drawing events*/
|
|
ENUM_CASE(EVENT_COVER_CHECK);
|
|
ENUM_CASE(EVENT_REFR_EXT_DRAW_SIZE);
|
|
ENUM_CASE(EVENT_DRAW_MAIN_BEGIN);
|
|
ENUM_CASE(EVENT_DRAW_MAIN);
|
|
ENUM_CASE(EVENT_DRAW_MAIN_END);
|
|
ENUM_CASE(EVENT_DRAW_POST_BEGIN);
|
|
ENUM_CASE(EVENT_DRAW_POST);
|
|
ENUM_CASE(EVENT_DRAW_POST_END);
|
|
ENUM_CASE(EVENT_DRAW_TASK_ADDED);
|
|
|
|
/** Special events*/
|
|
ENUM_CASE(EVENT_VALUE_CHANGED);
|
|
ENUM_CASE(EVENT_INSERT);
|
|
ENUM_CASE(EVENT_REFRESH);
|
|
ENUM_CASE(EVENT_READY);
|
|
ENUM_CASE(EVENT_CANCEL);
|
|
|
|
/** Other events*/
|
|
ENUM_CASE(EVENT_CREATE);
|
|
ENUM_CASE(EVENT_DELETE);
|
|
ENUM_CASE(EVENT_CHILD_CHANGED);
|
|
ENUM_CASE(EVENT_CHILD_CREATED);
|
|
ENUM_CASE(EVENT_CHILD_DELETED);
|
|
ENUM_CASE(EVENT_SCREEN_UNLOAD_START);
|
|
ENUM_CASE(EVENT_SCREEN_LOAD_START);
|
|
ENUM_CASE(EVENT_SCREEN_LOADED);
|
|
ENUM_CASE(EVENT_SCREEN_UNLOADED);
|
|
ENUM_CASE(EVENT_SIZE_CHANGED);
|
|
ENUM_CASE(EVENT_STYLE_CHANGED);
|
|
ENUM_CASE(EVENT_LAYOUT_CHANGED);
|
|
ENUM_CASE(EVENT_GET_SELF_SIZE);
|
|
|
|
/** Events of optional LVGL components*/
|
|
ENUM_CASE(EVENT_INVALIDATE_AREA);
|
|
ENUM_CASE(EVENT_RESOLUTION_CHANGED);
|
|
ENUM_CASE(EVENT_COLOR_FORMAT_CHANGED);
|
|
ENUM_CASE(EVENT_REFR_REQUEST);
|
|
ENUM_CASE(EVENT_REFR_START);
|
|
ENUM_CASE(EVENT_REFR_READY);
|
|
ENUM_CASE(EVENT_RENDER_START);
|
|
ENUM_CASE(EVENT_RENDER_READY);
|
|
ENUM_CASE(EVENT_FLUSH_START);
|
|
ENUM_CASE(EVENT_FLUSH_FINISH);
|
|
ENUM_CASE(EVENT_FLUSH_WAIT_START);
|
|
ENUM_CASE(EVENT_FLUSH_WAIT_FINISH);
|
|
|
|
ENUM_CASE(EVENT_VSYNC);
|
|
ENUM_CASE(EVENT_VSYNC_REQUEST);
|
|
|
|
/* Special event flags */
|
|
case LV_EVENT_LAST:
|
|
case LV_EVENT_PREPROCESS:
|
|
case LV_EVENT_MARKED_DELETING:
|
|
break;
|
|
|
|
/* Note that default is not added here because when adding new event code,
|
|
* if forget to add case, the compiler will automatically report a warning.
|
|
*/
|
|
}
|
|
|
|
#undef ENUM_CASE
|
|
|
|
return "EVENT_UNKNOWN";
|
|
}
|
|
|
|
/**********************
|
|
* STATIC FUNCTIONS
|
|
**********************/
|
|
|
|
static void cleanup_event_list_core(lv_array_t * array)
|
|
{
|
|
const uint32_t size = lv_array_size(array);
|
|
uint32_t kept_count = 0;
|
|
for(uint32_t i = 0; i < size; i++) {
|
|
lv_event_dsc_t ** dsc_i = lv_array_at(array, i);
|
|
lv_event_dsc_t ** dsc_kept = lv_array_at(array, kept_count);
|
|
if(event_is_marked_deleting(*dsc_i)) lv_free(*dsc_i);
|
|
else {
|
|
*dsc_kept = *dsc_i;
|
|
kept_count++;
|
|
}
|
|
}
|
|
|
|
if(kept_count == 0) lv_array_deinit(array);
|
|
else lv_array_resize(array, kept_count);
|
|
}
|
|
|
|
static void cleanup_event_list(lv_event_list_t * list)
|
|
{
|
|
if(list->is_traversing) return;
|
|
if(list->has_marked_deleting == false) return;
|
|
|
|
cleanup_event_list_core(&list->array);
|
|
|
|
list->has_marked_deleting = false;
|
|
}
|
|
|
|
static void event_mark_deleting(lv_event_list_t * list, lv_event_dsc_t * dsc)
|
|
{
|
|
list->has_marked_deleting = true;
|
|
dsc->filter |= LV_EVENT_MARKED_DELETING;
|
|
}
|
|
static bool event_is_marked_deleting(lv_event_dsc_t * dsc)
|
|
{
|
|
return (dsc->filter & LV_EVENT_MARKED_DELETING) != 0;
|
|
}
|
|
static uint32_t event_array_size(lv_event_list_t * list)
|
|
{
|
|
return lv_array_size(&list->array);
|
|
}
|
|
static lv_event_dsc_t ** event_array_at(lv_event_list_t * list, uint32_t index)
|
|
{
|
|
return lv_array_at(&list->array, index);
|
|
}
|