Event Preview

LVGL 中包含了一个 Event 事件子系统,并定义了很多事件:

/*** Type of event being sent to the object.*/
typedef enum {LV_EVENT_ALL = 0,/** Input device events*/LV_EVENT_PRESSED,             /**< The object has been pressed*/LV_EVENT_PRESSING,            /**< The object is being pressed (called continuously while pressing)*/LV_EVENT_PRESS_LOST,          /**< The object is still being pressed but slid cursor/finger off of the object */LV_EVENT_SHORT_CLICKED,       /**< The object was pressed for a short period of time, then released it. Not called if scrolled.*/LV_EVENT_LONG_PRESSED,        /**< Object has been pressed for at least `long_press_time`.  Not called if scrolled.*/LV_EVENT_LONG_PRESSED_REPEAT, /**< Called after `long_press_time` in every `long_press_repeat_time` ms.  Not called if scrolled.*/LV_EVENT_CLICKED,             /**< Called on release if not scrolled (regardless to long press)*/LV_EVENT_RELEASED,            /**< Called in every cases when the object has been released*/LV_EVENT_SCROLL_BEGIN,        /**< Scrolling begins*/LV_EVENT_SCROLL_END,          /**< Scrolling ends*/LV_EVENT_SCROLL,              /**< Scrolling*/LV_EVENT_GESTURE,             /**< A gesture is detected. Get the gesture with `lv_indev_get_gesture_dir(lv_indev_get_act());` */LV_EVENT_KEY,                 /**< A key is sent to the object. Get the key with `lv_indev_get_key(lv_indev_get_act());`*/LV_EVENT_FOCUSED,             /**< The object is focused*/LV_EVENT_DEFOCUSED,           /**< The object is defocused*/LV_EVENT_LEAVE,               /**< The object is defocused but still selected*/LV_EVENT_HIT_TEST,            /**< Perform advanced hit-testing*//** Drawing events*/LV_EVENT_COVER_CHECK,        /**< Check if the object fully covers an area. The event parameter is `lv_cover_check_info_t *`.*/LV_EVENT_REFR_EXT_DRAW_SIZE, /**< Get the required extra draw area around the object (e.g. for shadow). The event parameter is `lv_coord_t *` to store the size.*/LV_EVENT_DRAW_MAIN_BEGIN,    /**< Starting the main drawing phase*/LV_EVENT_DRAW_MAIN,          /**< Perform the main drawing*/LV_EVENT_DRAW_MAIN_END,      /**< Finishing the main drawing phase*/LV_EVENT_DRAW_POST_BEGIN,    /**< Starting the post draw phase (when all children are drawn)*/LV_EVENT_DRAW_POST,          /**< Perform the post draw phase (when all children are drawn)*/LV_EVENT_DRAW_POST_END,      /**< Finishing the post draw phase (when all children are drawn)*/LV_EVENT_DRAW_PART_BEGIN,    /**< Starting to draw a part. The event parameter is `lv_obj_draw_dsc_t *`. */LV_EVENT_DRAW_PART_END,      /**< Finishing to draw a part. The event parameter is `lv_obj_draw_dsc_t *`. *//** Special events*/LV_EVENT_VALUE_CHANGED,       /**< The object's value has changed (i.e. slider moved)*/LV_EVENT_INSERT,              /**< A text is inserted to the object. The event data is `char *` being inserted.*/LV_EVENT_REFRESH,             /**< Notify the object to refresh something on it (for the user)*/LV_EVENT_READY,               /**< A process has finished*/LV_EVENT_CANCEL,              /**< A process has been cancelled *//** Other events*/LV_EVENT_DELETE,              /**< Object is being deleted*/LV_EVENT_CHILD_CHANGED,       /**< Child was removed, added, or its size, position were changed */LV_EVENT_CHILD_CREATED,       /**< Child was created, always bubbles up to all parents*/LV_EVENT_CHILD_DELETED,       /**< Child was deleted, always bubbles up to all parents*/LV_EVENT_SCREEN_UNLOAD_START, /**< A screen unload started, fired immediately when scr_load is called*/LV_EVENT_SCREEN_LOAD_START,   /**< A screen load started, fired when the screen change delay is expired*/LV_EVENT_SCREEN_LOADED,       /**< A screen was loaded*/LV_EVENT_SCREEN_UNLOADED,     /**< A screen was unloaded*/LV_EVENT_SIZE_CHANGED,        /**< Object coordinates/size have changed*/LV_EVENT_STYLE_CHANGED,       /**< Object's style has changed*/LV_EVENT_LAYOUT_CHANGED,      /**< The children position has changed due to a layout recalculation*/LV_EVENT_GET_SELF_SIZE,       /**< Get the internal size of a widget*/_LV_EVENT_LAST,               /** Number of default events*/LV_EVENT_PREPROCESS = 0x80,   /** This is a flag that can be set with an event so it's processedbefore the class default event processing */
} lv_event_code_t;

事件主要分为了几类:

  1. Input 事件:点击,长按,滚动,焦点,等等;
  2. Draw 事件:Main 绘制开始,绘制中,完成绘制,Post 绘制等;
  3. Special 事件:刷新事件等;
  4. Others:Object 被删除,Child 被删除,等等;

LVGL Event 贯穿了整个 LVGL,是其中不可或缺的一部分,它可以将事件有效的通知到组件,应用层可以通过注册事件回调,来监测并响应事件;当然 LVGL 内部的绘制流程,也会通过 Event 机制,通知到各个组件,进行绘制等操作;

LVGL 中事件的发送,靠的是一个叫 lv_event_sent 的函数;

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_event_t 的结构,并将他初始化;

然后通过 event_send_core 来传递 Event 到事件处理中心;event 发送和处理之间是同步调用,也就是 event_sent_core 中即有发送,也处理了接收,并调用了对应的监听某 Event 的 obj 注册下去的回调函数;

Event callback

要看明白 event_send_core 函数,需要先了解 obj event 的回调函数在哪定义的,obj 的定义如下:

可以看到有 2 个叫做 event callback 的地方:

lv_obj_t
|--> lv_obj_class_t|--> void (*event_cb)

另一个是:

lv_obj_t
|--> lv_obj_spec_attr_t|--> _lv_event_dsc_t|--> lv_event_cb_t

同样是 event callback 这两个有啥区别呢?

我们来看使用场景:

我给一个 button 添加一个按键的响应,那么代码如下:

lv_obj_t * btn = lv_btn_create(lv_scr_act());
lv_obj_add_event_cb(btn, my_event_cb, LV_EVENT_CLICKED, NULL);   /*Assign an event callback*/...static void my_event_cb(lv_event_t * event)
{printf("Clicked\n");
}

使用 lv_obj_add_event_cb 函数,为 button 指定了它收到 Event (被点击事件) 后的响应函数为 my_event_cb;

这个 lv_obj_add_event_cb 函数的实现为:

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];
}

可以看到,用户层通过 lv_obj_add_event_cb 函数调用注册下去的响应回调,是通过插入到对象 obj 的 obj->spec_attr->event_dsc 这个地方的,也就是:

lv_obj_t
|--> lv_obj_spec_attr_t|--> _lv_event_dsc_t|--> lv_event_cb_t

我们在创建一个 label 的时候,使用 lv_label_create:

#define MY_CLASS &lv_label_classconst lv_obj_class_t lv_label_class = {.constructor_cb = lv_label_constructor,.destructor_cb = lv_label_destructor,.event_cb = lv_label_event,.width_def = LV_SIZE_CONTENT,.height_def = LV_SIZE_CONTENT,.instance_size = sizeof(lv_label_t),.base_class = &lv_obj_class
};/***********************      MACROS**********************//***********************   GLOBAL FUNCTIONS**********************/lv_obj_t * lv_label_create(lv_obj_t * parent)
{LV_LOG_INFO("begin");lv_obj_t * obj = lv_obj_class_create_obj(MY_CLASS, parent);lv_obj_class_init_obj(obj);return obj;
}

可以看到,静态的 lv_obj_class_t lv_label_class中,它的 event_cb 被赋值为了 lv_label_event 函数;

这里为什么我不拿 lv_btn_create 打比方呢,因为 button 的这个 class 没有赋值 event_cb;

所以,得出了结论就是,这两个 Event callback,一个是面向用户的,一个是面向 LVGL 系统的!

Event Core

接着看 event_send_core 的实现,这里分为几部分来分析源码;

首先,获取 input 系统的句柄,调用 feedback_cb 来处理触摸,按键这些事件;

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;}.........return res;
}

其次,获取被应用层注册了的 obj 的回调 (e->current_target 就是构建 lv_event_t 的时候传入的 obj),然后判断应用层指定的 event code 是否满足 EVENT PREPROCESS,如果是,那么进行调用应用层指定的 event_dsc->cb,

static lv_res_t event_send_core(lv_event_t * e)
{
........lv_res_t res = LV_RES_OK;lv_event_dsc_t * event_dsc = res == LV_RES_INV ? NULL : 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);}
........return res;
}

接着,通过调用 lv_obj_event_base 处理 obj 的 lv_obj_class_t 中的 event_cb 被静态初始化的部分:

static lv_res_t event_send_core(lv_event_t * e)
{
....res = lv_obj_event_base(NULL, e);....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;
}

再接着,处理应用层指定的 event code 相等的部分,即,比较 event code 等于当前的 e->code 的时候,才调用 callback;循环的取并执行

static lv_res_t event_send_core(lv_event_t * 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);}......return res;
}

最后,处理 bubbled 的部分,

Bubbled 是冒泡的意思,Event 冒泡指的是该 obj 的 Event 可以向它的 parent 进行传递,如果当前 obj 是支持 Bubble 的话,那么它会向它的 parent (如果有 parent)进行传递,直到某一个 parent 不支持 bubble 为止;

static lv_res_t event_send_core(lv_event_t * e)
{
........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;
}

LVGL (9) Event 机制实现相关推荐

  1. 深入理解jQuery的Event机制

    2019独角兽企业重金招聘Python工程师标准>>> jQuery的Event模块非常强大.其功能远远比原生事件监听器强大许多,对同一个元素的监听只用一个eventListener ...

  2. C++Event机制的简单实现

    转自:http://www.cnblogs.com/bastard/archive/2012/01/10/2318417.html C++ Event Model 一 事件模型 对发生的事件作出的响应 ...

  3. JAVA什么叫event_Java 的Event机制浅析

    通常Java添加监听类似C里面的回调,通常在使用时比较简单,自己定义的事件类(继承EventObject),定义监听器接口(继承EventListener),定义一个者向量来保存添加的这些监听器,通过 ...

  4. uvm event 事件机制

    event 机制开始是在做Linux 系统开发的时候使用的,作用是从kernel 的内核层像用户层发送消息和数据等,内核层发送事件的api: 1 /**2 * kobject_uevent_env - ...

  5. LVGL (8) 绘制流程

    目录 Preview LVGL Tick Timer Handler lv_timer_exec 显式刷屏任务 _lv_disp_refr_timer 小结 Preview 整个 LVGL Frame ...

  6. python 信号量,Event, 定时器

    信号量 信号量也是一把锁,可以指定信号量为5,对比互斥锁同一时间只能有一个任务抢到锁去执行,信号量同一时间可以有5个任务拿到锁去执行. 如果说互斥锁是合租房屋的人去抢一个厕所,那么信号量就相当于一群路 ...

  7. python并发入门(part5 event对象)

    一.引入event.  每个线程,都是一个独立运行的个体,并且每个线程的运行状态是无法预测的. 如果一个程序中有很多个线程,程序的其他线程需要判断某个线程的运行状态,来确定自己下一步要执行哪些操作.  ...

  8. Day28:Event对象、队列、multiprocessing模块

    一.Event对象 线程的一个关键特性是每个线程都是独立运行且状态不可预测.如果程序中的其他线程需要通过判断某个线程的状态来确定自己下一步的操作,这时线程同步问题就 会变得非常棘手.为了解决这些问题, ...

  9. boost源码剖析之:多重回调机制signal(上)

    boost源码剖析之:多重回调机制signal(上) 刘未鹏 C++的罗浮宫(http://blog.csdn.net/pongba) boost库固然是技术的宝库,却更是思想的宝库.大多数程序员都知 ...

  10. 11.python并发入门(part5 event对象)

    一.引入event. 每个线程,都是一个独立运行的个体,并且每个线程的运行状态是无法预测的. 如果一个程序中有很多个线程,程序的其他线程需要判断某个线程的运行状态,来确定自己下一步要执行哪些操作. t ...

最新文章

  1. 安全攻防之SQL注入
  2. 腾达fh365虚拟服务器,腾达(Tenda)FH365路由器怎么设置?
  3. MapReduce+Docker:Archer简化Netflix媒体处理
  4. 【C#】设计模式的学习征途系列文章目录(2019版)
  5. Powershell 时间相关
  6. 论文浅尝 | EARL: Joint Entity and Relation Linking for QA over KG
  7. 2025美妆行业科技应用前瞻报告
  8. Linux 双网卡绑定方法
  9. What Are The Differences Between Base, Medium, and Full Camera Link Configurations?
  10. sqlmap重要参数详解+用法,解决入门难题
  11. 国内电影票务系统浅析
  12. 【Prescan学习】Prescan环境配置(介绍+安装+学习资源)
  13. 镜像下载vscode
  14. 怎样在线制作gif表情包?教你快速制作gif表情包
  15. 【哈利波特】Sherbert Lemon对HP的解读之10
  16. 连接数据库出现错误代码为18456
  17. Android5.1--PowerManagerService电源管理
  18. 软件模拟中美gdp今后几年的变化情况
  19. Docute 创建文档网站(docute v3)
  20. php实现阿里云视频合成

热门文章

  1. PageOffice 在线打开 word 文件实现痕迹保留、键盘批注、手写批注
  2. CSS选择器(随笔)
  3. 2017-2018-2 20155314《网络对抗技术》Exp4 恶意代码分析
  4. mac宽带连接找不到pppoe服务器,macbookAIR 使用以太网转接头连接宽带… - Apple 社区...
  5. VBox 虚拟机完美迁移/复制(带快照)
  6. linux查看当前账号权限,Linux账号权限管理
  7. SpringBoot+PageHelper实现分页功能
  8. Windows7 Ghost 旗舰装机版
  9. 【rzxt】详细了解taskmgr.exe进程的基本信息
  10. android开发 页面关闭时,关闭软键盘