Browse Source

Merge branch 'master' of https://gitee.com/openLuat/LuatOS

江访 1 month ago
parent
commit
f293b80db2
100 changed files with 25525 additions and 62 deletions
  1. 61 48
      bsp/pc/include/luat_conf_bsp.h
  2. 3 0
      bsp/pc/port/luat_base_mini.c
  3. 206 0
      bsp/pc/port/rtos/luat_rtos_queue_pc.c
  4. 0 1
      bsp/pc/src/main_mini.c
  5. 27 0
      bsp/pc/test/111.easylvgl/readme.md
  6. 45 0
      bsp/pc/test/111.easylvgl/test_button/main.lua
  7. 44 0
      bsp/pc/test/111.easylvgl/test_dropdown/main.lua
  8. BIN
      bsp/pc/test/111.easylvgl/test_image/logo.png
  9. 59 0
      bsp/pc/test/111.easylvgl/test_image/main.lua
  10. BIN
      bsp/pc/test/111.easylvgl/test_image/sunset.png
  11. 44 0
      bsp/pc/test/111.easylvgl/test_input/main.lua
  12. 42 0
      bsp/pc/test/111.easylvgl/test_input_real_keyboard/main.lua
  13. 40 0
      bsp/pc/test/111.easylvgl/test_label/main.lua
  14. 48 0
      bsp/pc/test/111.easylvgl/test_label_hzfont/main.lua
  15. 32 0
      bsp/pc/test/111.easylvgl/test_msgbox/main.lua
  16. 30 0
      bsp/pc/test/111.easylvgl/test_switch/main.lua
  17. BIN
      bsp/pc/test/111.easylvgl/test_win/logo.png
  18. 81 0
      bsp/pc/test/111.easylvgl/test_win/main.lua
  19. 48 11
      bsp/pc/xmake.lua
  20. 4 1
      components/airtalk/core/airtalk_network.c
  21. 3 1
      components/airtalk/include/luat_airtalk.h
  22. 391 0
      components/easylvgl/binding/luat_lib_easylvgl.c
  23. 143 0
      components/easylvgl/binding/luat_lib_easylvgl_button.c
  24. 99 0
      components/easylvgl/binding/luat_lib_easylvgl_container.c
  25. 158 0
      components/easylvgl/binding/luat_lib_easylvgl_dropdown.c
  26. 155 0
      components/easylvgl/binding/luat_lib_easylvgl_image.c
  27. 196 0
      components/easylvgl/binding/luat_lib_easylvgl_keyboard.c
  28. 171 0
      components/easylvgl/binding/luat_lib_easylvgl_label.c
  29. 181 0
      components/easylvgl/binding/luat_lib_easylvgl_msgbox.c
  30. 151 0
      components/easylvgl/binding/luat_lib_easylvgl_switch.c
  31. 92 0
      components/easylvgl/binding/luat_lib_easylvgl_table.c
  32. 111 0
      components/easylvgl/binding/luat_lib_easylvgl_tabview.c
  33. 198 0
      components/easylvgl/binding/luat_lib_easylvgl_textarea.c
  34. 197 0
      components/easylvgl/binding/luat_lib_easylvgl_win.c
  35. 281 0
      components/easylvgl/inc/luat_easylvgl.h
  36. 51 0
      components/easylvgl/inc/luat_easylvgl_binding.h
  37. 339 0
      components/easylvgl/inc/luat_easylvgl_component.h
  38. 68 0
      components/easylvgl/inc/luat_easylvgl_conf.h
  39. 69 0
      components/easylvgl/inc/luat_easylvgl_symbol.h
  40. 1503 0
      components/easylvgl/lvgl9/lv_conf.h
  41. 17 0
      components/easylvgl/lvgl9/lv_version.h
  42. 218 0
      components/easylvgl/lvgl9/lvgl.h
  43. 318 0
      components/easylvgl/lvgl9/src/core/lv_global.h
  44. 530 0
      components/easylvgl/lvgl9/src/core/lv_group.c
  45. 248 0
      components/easylvgl/lvgl9/src/core/lv_group.h
  46. 75 0
      components/easylvgl/lvgl9/src/core/lv_group_private.h
  47. 1229 0
      components/easylvgl/lvgl9/src/core/lv_obj.c
  48. 486 0
      components/easylvgl/lvgl9/src/core/lv_obj.h
  49. 204 0
      components/easylvgl/lvgl9/src/core/lv_obj_class.c
  50. 72 0
      components/easylvgl/lvgl9/src/core/lv_obj_class.h
  51. 78 0
      components/easylvgl/lvgl9/src/core/lv_obj_class_private.h
  52. 458 0
      components/easylvgl/lvgl9/src/core/lv_obj_draw.c
  53. 129 0
      components/easylvgl/lvgl9/src/core/lv_obj_draw.h
  54. 48 0
      components/easylvgl/lvgl9/src/core/lv_obj_draw_private.h
  55. 480 0
      components/easylvgl/lvgl9/src/core/lv_obj_event.c
  56. 204 0
      components/easylvgl/lvgl9/src/core/lv_obj_event.h
  57. 62 0
      components/easylvgl/lvgl9/src/core/lv_obj_event_private.h
  58. 122 0
      components/easylvgl/lvgl9/src/core/lv_obj_id_builtin.c
  59. 1318 0
      components/easylvgl/lvgl9/src/core/lv_obj_pos.c
  60. 473 0
      components/easylvgl/lvgl9/src/core/lv_obj_pos.h
  61. 93 0
      components/easylvgl/lvgl9/src/core/lv_obj_private.h
  62. 339 0
      components/easylvgl/lvgl9/src/core/lv_obj_property.c
  63. 246 0
      components/easylvgl/lvgl9/src/core/lv_obj_property.h
  64. 813 0
      components/easylvgl/lvgl9/src/core/lv_obj_scroll.c
  65. 301 0
      components/easylvgl/lvgl9/src/core/lv_obj_scroll.h
  66. 50 0
      components/easylvgl/lvgl9/src/core/lv_obj_scroll_private.h
  67. 1219 0
      components/easylvgl/lvgl9/src/core/lv_obj_style.c
  68. 405 0
      components/easylvgl/lvgl9/src/core/lv_obj_style.h
  69. 969 0
      components/easylvgl/lvgl9/src/core/lv_obj_style_gen.c
  70. 938 0
      components/easylvgl/lvgl9/src/core/lv_obj_style_gen.h
  71. 96 0
      components/easylvgl/lvgl9/src/core/lv_obj_style_private.h
  72. 845 0
      components/easylvgl/lvgl9/src/core/lv_obj_tree.c
  73. 321 0
      components/easylvgl/lvgl9/src/core/lv_obj_tree.h
  74. 1450 0
      components/easylvgl/lvgl9/src/core/lv_refr.c
  75. 74 0
      components/easylvgl/lvgl9/src/core/lv_refr.h
  76. 84 0
      components/easylvgl/lvgl9/src/core/lv_refr_private.h
  77. 1372 0
      components/easylvgl/lvgl9/src/display/lv_display.c
  78. 732 0
      components/easylvgl/lvgl9/src/display/lv_display.h
  79. 188 0
      components/easylvgl/lvgl9/src/display/lv_display_private.h
  80. 100 0
      components/easylvgl/lvgl9/src/draw/convert/helium/lv_draw_buf_convert_helium.c
  81. 57 0
      components/easylvgl/lvgl9/src/draw/convert/helium/lv_draw_buf_convert_helium.h
  82. 130 0
      components/easylvgl/lvgl9/src/draw/convert/lv_draw_buf_convert.c
  83. 39 0
      components/easylvgl/lvgl9/src/draw/convert/lv_draw_buf_convert.h
  84. 132 0
      components/easylvgl/lvgl9/src/draw/convert/neon/lv_draw_buf_convert_neon.c
  85. 58 0
      components/easylvgl/lvgl9/src/draw/convert/neon/lv_draw_buf_convert_neon.h
  86. 394 0
      components/easylvgl/lvgl9/src/draw/dma2d/lv_draw_dma2d.c
  87. 49 0
      components/easylvgl/lvgl9/src/draw/dma2d/lv_draw_dma2d.h
  88. 133 0
      components/easylvgl/lvgl9/src/draw/dma2d/lv_draw_dma2d_fill.c
  89. 262 0
      components/easylvgl/lvgl9/src/draw/dma2d/lv_draw_dma2d_img.c
  90. 155 0
      components/easylvgl/lvgl9/src/draw/dma2d/lv_draw_dma2d_private.h
  91. 255 0
      components/easylvgl/lvgl9/src/draw/espressif/ppa/lv_draw_ppa.c
  92. 57 0
      components/easylvgl/lvgl9/src/draw/espressif/ppa/lv_draw_ppa.h
  93. 52 0
      components/easylvgl/lvgl9/src/draw/espressif/ppa/lv_draw_ppa_buf.c
  94. 48 0
      components/easylvgl/lvgl9/src/draw/espressif/ppa/lv_draw_ppa_fill.c
  95. 77 0
      components/easylvgl/lvgl9/src/draw/espressif/ppa/lv_draw_ppa_img.c
  96. 185 0
      components/easylvgl/lvgl9/src/draw/espressif/ppa/lv_draw_ppa_private.h
  97. 164 0
      components/easylvgl/lvgl9/src/draw/eve/lv_draw_eve.c
  98. 57 0
      components/easylvgl/lvgl9/src/draw/eve/lv_draw_eve.h
  99. 325 0
      components/easylvgl/lvgl9/src/draw/eve/lv_draw_eve_arc.c
  100. 121 0
      components/easylvgl/lvgl9/src/draw/eve/lv_draw_eve_fill.c

+ 61 - 48
bsp/pc/include/luat_conf_bsp.h

@@ -132,10 +132,15 @@
 
 #ifdef LUAT_USE_GUI
 //---------------------
+
+// TP 模块与PC触摸驱动
+#define LUAT_USE_TP 1
+#define LUAT_USE_TP_PC 1
+
 // UI
 // LCD  是彩屏, 若使用LVGL就必须启用LCD
 #define LUAT_USE_LCD
-#define LUAT_USE_TJPGD
+// #define LUAT_USE_TJPGD
 // GT 字库:PC 模拟器仿真启用
 #define LUAT_USE_GTFONT 1
 // 若需要直接绘制 UTF8 字符串(lcd/u8g2 的 UTF8 接口),启用
@@ -187,55 +192,63 @@
 #define LV_FONT_OPPOSANS_M_16
 
 //---------------------
-// LVGL
-// 主推的UI库, 功能强大但API繁琐
-#define LUAT_USE_LVGL      1
 
-#define LUAT_USE_LVGL_JPG 1 // 启用JPG解码支持
-#define LUAT_USE_LVGL_PNG 1 // 启用PNG解码支持
-#define LUAT_USE_LVGL_BMP 1 // 启用BMP解码支持
-
-#define LUAT_USE_LVGL_INDEV 1 // 输入设备
-
-// TP 模块与PC触摸驱动
-#define LUAT_USE_TP 1
-#define LUAT_USE_TP_PC 1
+#define LUAT_USE_EASYLVGL 1
+#define LUAT_USE_EASYLVGL_SDL2 1
+
+// #else
+// // LVGL
+// // 主推的UI库, 功能强大但API繁琐
+// #define LUAT_USE_LVGL      1
+// #define LUAT_USE_TJPGD 1
+
+// #define LUAT_USE_LVGL_JPG 1 // 启用JPG解码支持
+// #define LUAT_USE_LVGL_PNG 1 // 启用PNG解码支持
+// #define LUAT_USE_LVGL_BMP 1 // 启用BMP解码支持
+
+// #define LUAT_USE_LVGL_INDEV 1 // 输入设备
+
+// // TP 模块与PC触摸驱动
+// #define LUAT_USE_TP 1
+// #define LUAT_USE_TP_PC 1
+
+// #define LUAT_USE_LVGL_ARC   //圆弧 无依赖
+// #define LUAT_USE_LVGL_BAR   //进度条 无依赖
+// #define LUAT_USE_LVGL_BTN   //按钮 依赖容器CONT
+// #define LUAT_USE_LVGL_BTNMATRIX   //按钮矩阵 无依赖
+// #define LUAT_USE_LVGL_CALENDAR   //日历 无依赖
+// #define LUAT_USE_LVGL_CANVAS   //画布 依赖图片IMG
+// #define LUAT_USE_LVGL_CHECKBOX   //复选框 依赖按钮BTN 标签LABEL
+// #define LUAT_USE_LVGL_CHART   //图表 无依赖
+// #define LUAT_USE_LVGL_CONT   //容器 无依赖
+// #define LUAT_USE_LVGL_CPICKER   //颜色选择器 无依赖
+// #define LUAT_USE_LVGL_DROPDOWN   //下拉列表 依赖页面PAGE 标签LABEL
+// #define LUAT_USE_LVGL_GAUGE   //仪表 依赖进度条BAR 仪表(弧形刻度)LINEMETER
+// #define LUAT_USE_LVGL_IMG   //图片 依赖标签LABEL
+// #define LUAT_USE_LVGL_IMGBTN   //图片按钮 依赖按钮BTN
+// #define LUAT_USE_LVGL_KEYBOARD   //键盘 依赖图片按钮IMGBTN
+// #define LUAT_USE_LVGL_LABEL   //标签 无依赖
+// #define LUAT_USE_LVGL_LED   //LED 无依赖
+// #define LUAT_USE_LVGL_LINE   //线 无依赖
+// #define LUAT_USE_LVGL_LIST   //列表 依赖页面PAGE 按钮BTN 标签LABEL
+// #define LUAT_USE_LVGL_LINEMETER   //仪表(弧形刻度) 无依赖
+// #define LUAT_USE_LVGL_OBJMASK   //对象蒙版 无依赖
+// #define LUAT_USE_LVGL_MSGBOX   //消息框 依赖图片按钮IMGBTN 标签LABEL
+// #define LUAT_USE_LVGL_PAGE   //页面 依赖容器CONT
+// #define LUAT_USE_LVGL_SPINNER   //旋转器 依赖圆弧ARC 动画ANIM
+// #define LUAT_USE_LVGL_ROLLER   //滚筒 无依赖
+// #define LUAT_USE_LVGL_SLIDER   //滑杆 依赖进度条BAR
+// #define LUAT_USE_LVGL_SPINBOX   //数字调整框 无依赖
+// #define LUAT_USE_LVGL_SWITCH   //开关 依赖滑杆SLIDER
+// #define LUAT_USE_LVGL_TEXTAREA   //文本框 依赖标签LABEL 页面PAGE
+// #define LUAT_USE_LVGL_TABLE   //表格 依赖标签LABEL
+// #define LUAT_USE_LVGL_TABVIEW   //页签 依赖页面PAGE 图片按钮IMGBTN
+// #define LUAT_USE_LVGL_TILEVIEW   //平铺视图 依赖页面PAGE
+// #define LUAT_USE_LVGL_WIN   //窗口 依赖容器CONT 按钮BTN 标签LABEL 图片IMG 页面PAGE
+
+// // #define LUAT_USE_AIRUI 1
+// #endif
 
-#define LUAT_USE_LVGL_ARC   //圆弧 无依赖
-#define LUAT_USE_LVGL_BAR   //进度条 无依赖
-#define LUAT_USE_LVGL_BTN   //按钮 依赖容器CONT
-#define LUAT_USE_LVGL_BTNMATRIX   //按钮矩阵 无依赖
-#define LUAT_USE_LVGL_CALENDAR   //日历 无依赖
-#define LUAT_USE_LVGL_CANVAS   //画布 依赖图片IMG
-#define LUAT_USE_LVGL_CHECKBOX   //复选框 依赖按钮BTN 标签LABEL
-#define LUAT_USE_LVGL_CHART   //图表 无依赖
-#define LUAT_USE_LVGL_CONT   //容器 无依赖
-#define LUAT_USE_LVGL_CPICKER   //颜色选择器 无依赖
-#define LUAT_USE_LVGL_DROPDOWN   //下拉列表 依赖页面PAGE 标签LABEL
-#define LUAT_USE_LVGL_GAUGE   //仪表 依赖进度条BAR 仪表(弧形刻度)LINEMETER
-#define LUAT_USE_LVGL_IMG   //图片 依赖标签LABEL
-#define LUAT_USE_LVGL_IMGBTN   //图片按钮 依赖按钮BTN
-#define LUAT_USE_LVGL_KEYBOARD   //键盘 依赖图片按钮IMGBTN
-#define LUAT_USE_LVGL_LABEL   //标签 无依赖
-#define LUAT_USE_LVGL_LED   //LED 无依赖
-#define LUAT_USE_LVGL_LINE   //线 无依赖
-#define LUAT_USE_LVGL_LIST   //列表 依赖页面PAGE 按钮BTN 标签LABEL
-#define LUAT_USE_LVGL_LINEMETER   //仪表(弧形刻度) 无依赖
-#define LUAT_USE_LVGL_OBJMASK   //对象蒙版 无依赖
-#define LUAT_USE_LVGL_MSGBOX   //消息框 依赖图片按钮IMGBTN 标签LABEL
-#define LUAT_USE_LVGL_PAGE   //页面 依赖容器CONT
-#define LUAT_USE_LVGL_SPINNER   //旋转器 依赖圆弧ARC 动画ANIM
-#define LUAT_USE_LVGL_ROLLER   //滚筒 无依赖
-#define LUAT_USE_LVGL_SLIDER   //滑杆 依赖进度条BAR
-#define LUAT_USE_LVGL_SPINBOX   //数字调整框 无依赖
-#define LUAT_USE_LVGL_SWITCH   //开关 依赖滑杆SLIDER
-#define LUAT_USE_LVGL_TEXTAREA   //文本框 依赖标签LABEL 页面PAGE
-#define LUAT_USE_LVGL_TABLE   //表格 依赖标签LABEL
-#define LUAT_USE_LVGL_TABVIEW   //页签 依赖页面PAGE 图片按钮IMGBTN
-#define LUAT_USE_LVGL_TILEVIEW   //平铺视图 依赖页面PAGE
-#define LUAT_USE_LVGL_WIN   //窗口 依赖容器CONT 按钮BTN 标签LABEL 图片IMG 页面PAGE
-
-// #define LUAT_USE_AIRUI 1
 #endif
 
 

+ 3 - 0
bsp/pc/port/luat_base_mini.c

@@ -152,6 +152,9 @@ static const luaL_Reg loadedlibs[] = {
 #ifdef LUAT_USE_LVGL
   {"lvgl",   luaopen_lvgl},
 #endif
+#ifdef LUAT_USE_EASYLVGL
+  {"easylvgl", luaopen_easylvgl},
+#endif
 #ifdef LUAT_USE_LCD
   {"lcd",    luaopen_lcd},
 #endif

+ 206 - 0
bsp/pc/port/rtos/luat_rtos_queue_pc.c

@@ -0,0 +1,206 @@
+/**
+ * @file luat_rtos_queue_pc.c
+ * @summary PC 平台 RTOS 队列实现
+ * @responsible 基于 uv_queue 实现 RTOS 队列接口
+ */
+
+#include "luat_base.h"
+#include "luat_rtos.h"
+#include "luat_malloc.h"
+#include "luat_queue_pc.h"
+#include "uv.h"
+#include "c_common.h"
+#include <string.h>
+
+#define LUAT_LOG_TAG "rtos.queue"
+#include "luat_log.h"
+
+/** 队列结构体 */
+typedef struct {
+    uv_queue_item_t queue;      /**< 队列头节点 */
+    uv_mutex_t mutex;           /**< 互斥锁 */
+    uint32_t item_count;        /**< 队列容量 */
+    uint32_t item_size;         /**< 元素大小 */
+    uint32_t current_count;     /**< 当前元素数量 */
+} luat_rtos_queue_pc_t;
+
+/**
+ * 创建队列
+ */
+int luat_rtos_queue_create(luat_rtos_queue_t *queue_handle, uint32_t item_count, uint32_t item_size)
+{
+    if (queue_handle == NULL || item_count == 0 || item_size == 0) {
+        return -1;
+    }
+    
+    luat_rtos_queue_pc_t *queue = luat_heap_malloc(sizeof(luat_rtos_queue_pc_t));
+    if (queue == NULL) {
+        return -1;
+    }
+    
+    memset(queue, 0, sizeof(luat_rtos_queue_pc_t));
+    queue->item_count = item_count;
+    queue->item_size = item_size;
+    queue->current_count = 0;
+    queue->queue.next = NULL;
+    queue->queue.size = 0;
+    
+    uv_mutex_init(&queue->mutex);
+    
+    *queue_handle = queue;
+    return 0;
+}
+
+/**
+ * 删除队列
+ */
+int luat_rtos_queue_delete(luat_rtos_queue_t queue_handle)
+{
+    if (queue_handle == NULL) {
+        return -1;
+    }
+    
+    luat_rtos_queue_pc_t *queue = (luat_rtos_queue_pc_t *)queue_handle;
+    
+    uv_mutex_lock(&queue->mutex);
+    
+    // 清空队列中的所有元素
+    while (queue->queue.next != NULL) {
+        uv_queue_item_t *item = (uv_queue_item_t *)queue->queue.next;
+        queue->queue.next = item->next;
+        luat_heap_free(item);
+    }
+    
+    uv_mutex_unlock(&queue->mutex);
+    uv_mutex_destroy(&queue->mutex);
+    
+    luat_heap_free(queue);
+    return 0;
+}
+
+/**
+ * 发送元素到队列
+ */
+int luat_rtos_queue_send(luat_rtos_queue_t queue_handle, void *item, uint32_t item_size, uint32_t timeout)
+{
+    if (queue_handle == NULL || item == NULL) {
+        return -1;
+    }
+    
+    luat_rtos_queue_pc_t *queue = (luat_rtos_queue_pc_t *)queue_handle;
+    
+    // 检查元素大小是否匹配
+    if (item_size != queue->item_size) {
+        return -1;
+    }
+    
+    uv_mutex_lock(&queue->mutex);
+    
+    // 检查队列是否已满
+    if (queue->current_count >= queue->item_count) {
+        uv_mutex_unlock(&queue->mutex);
+        if (timeout == 0) {
+            return -1; // 非阻塞模式,立即返回
+        }
+        // 阻塞模式:等待空间(简化实现,实际应该使用条件变量)
+        uv_mutex_unlock(&queue->mutex);
+        uv_sleep(1);
+        uv_mutex_lock(&queue->mutex);
+        if (queue->current_count >= queue->item_count) {
+            uv_mutex_unlock(&queue->mutex);
+            return -1;
+        }
+    }
+    
+    // 分配队列项
+    uv_queue_item_t *queue_item = luat_heap_malloc(sizeof(uv_queue_item_t) + queue->item_size - 4);
+    if (queue_item == NULL) {
+        uv_mutex_unlock(&queue->mutex);
+        return -1;
+    }
+    
+    queue_item->next = NULL;
+    queue_item->size = queue->item_size;
+    memcpy(queue_item->msg, item, queue->item_size);
+    
+    // 添加到队列
+    int ret = luat_queue_push(&queue->queue, queue_item);
+    if (ret == 0) {
+        queue->current_count++;
+    } else {
+        luat_heap_free(queue_item);
+    }
+    
+    uv_mutex_unlock(&queue->mutex);
+    return ret;
+}
+
+/**
+ * 从队列接收元素
+ */
+int luat_rtos_queue_recv(luat_rtos_queue_t queue_handle, void *item, uint32_t item_size, uint32_t timeout)
+{
+    if (queue_handle == NULL || item == NULL) {
+        return -1;
+    }
+    
+    luat_rtos_queue_pc_t *queue = (luat_rtos_queue_pc_t *)queue_handle;
+    
+    // 检查元素大小是否匹配
+    if (item_size != queue->item_size) {
+        return -1;
+    }
+    
+    uint32_t wait_time = timeout;
+    
+    while (1) {
+        uv_mutex_lock(&queue->mutex);
+        
+        // 尝试从队列取出元素
+        uv_queue_item_t queue_item;
+        int ret = luat_queue_pop(&queue->queue, &queue_item);
+        
+        if (ret == 0) {
+            // 成功取出
+            memcpy(item, queue_item.msg, queue->item_size);
+            queue->current_count--;
+            uv_mutex_unlock(&queue->mutex);
+            return 0;
+        }
+        
+        uv_mutex_unlock(&queue->mutex);
+        
+        // 队列为空
+        if (timeout == 0) {
+            return -1; // 非阻塞模式,立即返回
+        }
+        
+        if (timeout != (uint32_t)(-1)) {
+            if (wait_time == 0) {
+                return -1; // 超时
+            }
+            wait_time--;
+        }
+        
+        uv_sleep(1);
+    }
+}
+
+/**
+ * 获取队列中元素数量
+ */
+int luat_rtos_queue_get_cnt(luat_rtos_queue_t queue_handle, uint32_t *item_cnt)
+{
+    if (queue_handle == NULL || item_cnt == NULL) {
+        return -1;
+    }
+    
+    luat_rtos_queue_pc_t *queue = (luat_rtos_queue_pc_t *)queue_handle;
+    
+    uv_mutex_lock(&queue->mutex);
+    *item_cnt = queue->current_count;
+    uv_mutex_unlock(&queue->mutex);
+    
+    return 0;
+}
+

+ 0 - 1
bsp/pc/src/main_mini.c

@@ -175,7 +175,6 @@ static void lvgl_timer_cb(uv_timer_t* lvgl_timer) {
 }
 #endif
 
-
 void sys_check_timeouts(void);
 static void timer_lwip(uv_timer_t *handle) {
     (void)handle;

+ 27 - 0
bsp/pc/test/111.easylvgl/readme.md

@@ -0,0 +1,27 @@
+# EasyLVGL 示例使用
+
+说明:本目录包含借助 EasyLVGL 封装的一系列测试脚本,通过 `luatos-lua.exe` 可直接在 PC 或设备上运行。进入 `code/easylvgl` 后执行以下命令可分别启动对应示例:
+```
+# 当前有8个组件可以测试
+cd .\easylvgl\
+# 按钮测试
+.\luatos-lua.exe .\test_button\
+# 下拉框测试
+.\luatos-lua.exe .\test_dropdown\
+# 图像测试
+.\luatos-lua.exe .\test_image\
+# 虚拟键盘输入
+.\luatos-lua.exe .\test_input\
+# 实物键盘输入 打开hzfont后支持中文输入
+.\luatos-lua.exe .\test_input_real_keyboard\
+# 标签测试
+.\luatos-lua.exe .\test_label\
+# hzfont标签测试
+.\luatos-lua.exe .\test_label_hzfont\
+# 消息框示例
+.\luatos-lua.exe .\test_msgbox\
+# 开关控件示例
+.\luatos-lua.exe .\test_switch\
+# 窗口组件示例
+.\luatos-lua.exe .\test_win\
+```

+ 45 - 0
bsp/pc/test/111.easylvgl/test_button/main.lua

@@ -0,0 +1,45 @@
+-- Button 组件测试脚本
+PROJECT = "easylvgl"
+VERSION = "1.0.0"
+
+sys.taskInit(function()
+    
+    -- 1. 初始化 EasyLVGL
+    -- easylvgl.init(w, h, buff_size, buff_mode)
+    -- w: 屏幕宽,可选,默认480
+    -- h: 屏幕高,可选,默认320
+    -- color_format: 颜色格式,可选,默认ARGB8888
+    --                   可用值:easylvgl.COLOR_FORMAT_RGB565(默认,嵌入式,节省内存)
+    --                          easylvgl.COLOR_FORMAT_ARGB8888(pc,高质量)
+    local ret = easylvgl.init(800, 600, easylvgl.COLOR_FORMAT_ARGB8888)
+    if not ret then
+        log.error("easylvgl", "init failed")
+        return
+    end
+    
+    local btn = easylvgl.button({
+        text = "LuatOS!",
+        x = 20, y = 80, w = 160, h = 48,
+        on_click = function(self)
+            log.info("Button clicked")
+        end
+    })
+
+    -- 3. 创建一个任务来动态修改按钮文本
+    sys.taskInit(function()
+        sys.wait(2000)  -- 等待 2 秒
+        btn:set_text("Button1")  -- 修改按钮文本
+        
+        sys.wait(2000)  -- 再等待 2 秒
+        btn:set_text("Button2")  -- 再次修改
+    end)
+
+    while true do
+        easylvgl.refresh()
+        sys.wait(10)
+    end
+
+end)
+
+sys.run()
+

+ 44 - 0
bsp/pc/test/111.easylvgl/test_dropdown/main.lua

@@ -0,0 +1,44 @@
+-- Dropdown 组件测试脚本
+PROJECT = "easylvgl"
+VERSION = "1.0.0"
+
+sys.taskInit(function()
+    local ret = easylvgl.init(800, 600, easylvgl.COLOR_FORMAT_ARGB8888)
+    if not ret then
+        log.error("easylvgl", "dropdown init failed")
+        return
+    end
+
+    -- 下拉框组件
+    local dropdown = easylvgl.dropdown({
+        parent = easylvgl.screen, -- 父对象,可选,默认当前屏幕
+        options = {"Option A", "Option B", "Option C"}, -- 选项列表(字符串数组)
+        default_index = 2, -- 默认选中项索引,默认 -1
+        x = 40, y = 60, w = 180, h = 50,
+        on_change = function(self, index) -- 选中项变化回调
+            log.info("dropdown", "selected index", index)
+        end
+    })
+
+    local btn = easylvgl.button({
+        parent = easylvgl.screen, -- 父对象,可选,默认当前屏幕
+        text = "set selected to A",
+        x = 240, y = 60, w = 160, h = 48,
+        on_click = function(self)
+            local selected = dropdown:get_selected() -- 获取当前选中项索引
+            log.info("dropdown", "previous selected index is", selected)
+            dropdown:set_selected(0)  -- 设置选中项为1  
+            log.info("dropdown", "now set selected to A")
+        end
+    })
+    
+
+
+    while true do
+        easylvgl.refresh()
+        sys.wait(10)
+    end
+end)
+
+sys.run()
+

BIN
bsp/pc/test/111.easylvgl/test_image/logo.png


+ 59 - 0
bsp/pc/test/111.easylvgl/test_image/main.lua

@@ -0,0 +1,59 @@
+-- Image 组件测试脚本
+PROJECT = "easylvgl"
+VERSION = "1.0.0"
+
+sys.taskInit(function()
+    local ret = easylvgl.init(800, 600, easylvgl.COLOR_FORMAT_ARGB8888)
+    if not ret then
+        log.error("easylvgl", "init failed")
+        return
+    end
+
+    -- 当前只支持png图片,后续将支持jpg等格式的图片
+    test_png = "/luadb/logo.png"
+    sunset_png = "/luadb/sunset.png"
+
+    local exists = io.exists(test_png)
+    log.info("easylvgl.image", "png exists: %s", exists)
+
+    -- 可点击图片
+    local img = easylvgl.image({
+        parent = easylvgl.screen, -- 父对象,可选,默认当前屏幕
+        src = test_png,
+        x = 240, y = 160, w = 80, h = 80,
+        zoom = 256, -- 缩放比例,默认 256(100%)
+        opacity = 255, -- 透明度,默认 255(不透明),范围 0-255
+        on_click = function(self)
+            log.info("easylvgl.image", "image clicked")
+        end
+    })
+
+    -- 不可点击图片
+    local img1 = easylvgl.image({
+        parent = easylvgl.screen, -- 父对象,可选,默认当前屏幕
+        src = test_png,
+        x = 400, y = 160, w = 80, h = 80,
+        zoom = 256, -- 缩放比例,默认 256(100%)
+        opacity = 255, -- 透明度,默认 255(不透明),范围 0-255
+    })
+
+    local btn = easylvgl.button({
+        parent = easylvgl.screen, -- 父对象,可选,默认当前屏幕
+        text = "set src to sunset",
+        x = 280, y = 260, w = 160, h = 48,
+        on_click = function(self)
+            img:set_src(sunset_png)
+        end
+    })
+
+
+    while true do
+        easylvgl.refresh()
+        sys.wait(10)
+    end
+
+
+end)
+
+sys.run()
+

BIN
bsp/pc/test/111.easylvgl/test_image/sunset.png


+ 44 - 0
bsp/pc/test/111.easylvgl/test_input/main.lua

@@ -0,0 +1,44 @@
+-- Textarea + 虚拟键盘输入测试
+PROJECT = "easylvgl"
+VERSION = "1.0.0"
+
+sys.taskInit(function()
+    -- 初始化 EasyLVGL,800x600 分辨率,使用 ARGB8888(PC 模拟)
+    local ret = easylvgl.init(800, 600, easylvgl.COLOR_FORMAT_ARGB8888)
+    if not ret then
+        log.error("easylvgl", "init failed")
+        return
+    end
+
+    -- 创建 textarea,通过虚拟键盘输入文本
+    local textarea = easylvgl.textarea({
+        parent = easylvgl.screen, -- 父对象,可选,默认当前屏幕
+        x = 20, y = 20, w = 760, h = 180,
+        max_len = 256, -- 最大字符数,默认 256
+        text = "Hello, World!", -- 初始文本
+        placeholder = "Please input text, the virtual keyboard will auto bind"
+    })
+
+    -- 注册虚拟键盘,将 textarea 作为目标,并监听确认事件
+    local keyboard = easylvgl.keyboard({
+        x = 0, y = 0, w = 760, h = 220, -- x, y, 从左边下方开始计算
+        mode = "text", -- 键盘模式,可选 "text"/"upper"/"special"/"numeric"
+        target = textarea, -- 关联的 Textarea 对象,可选
+        on_commit = function() -- 确认事件回调,只有在按下确认键时才会触发
+            log.info("keyboard", "commit -> " .. (textarea:get_text() or ""))
+        end
+    })
+
+    -- 持续打印 textarea 变化状态
+    textarea:set_on_text_change(function()
+        log.info("textarea", "text changed -> " .. (textarea:get_text() or ""))
+    end)
+
+    while true do
+        easylvgl.refresh()
+        sys.wait(10)
+    end
+end)
+
+sys.run()
+

+ 42 - 0
bsp/pc/test/111.easylvgl/test_input_real_keyboard/main.lua

@@ -0,0 +1,42 @@
+-- 使用 PC 物理键盘输入 textarea(不弹虚拟键盘)
+PROJECT = "easylvgl"
+VERSION = "1.0.0"
+
+sys.taskInit(function()
+    local ret = easylvgl.init(800, 600, easylvgl.COLOR_FORMAT_ARGB8888)
+    if not ret then
+        log.error("easylvgl", "init failed")
+        return
+    end
+
+    -- 加载hzfont字库,从而支持中文显示
+    easylvgl.font_load({
+        type = "hzfont", -- 字体类型,可选 "hzfont" 或 "bin"
+        path = nil, -- 字体路径,对于 "hzfont",传 nil 则使用内置字库
+        size = 16, -- 字体大小,默认 16
+        cache_size = 2048, -- 缓存字数大小,默认 2048
+        antialias = 4, -- 抗锯齿等级,默认 4
+    })
+
+    -- 允许系统键盘事件转发给当前 textarea, 当前只支持pc模拟器中使用
+    easylvgl.keyboard_enable_system(true)
+
+    local textarea = easylvgl.textarea({
+        x = 20, y = 20,
+        w = 760, h = 280,
+        max_len = 256,
+        placeholder = "Click here and use your physical keyboard input (Windows/SDL)"
+    })
+
+    textarea:set_on_text_change(function()
+        log.info("textarea", "text -> " .. (textarea:get_text() or ""))
+    end)
+
+    while true do
+        easylvgl.refresh()
+        sys.wait(10)
+    end
+end)
+
+sys.run()
+

+ 40 - 0
bsp/pc/test/111.easylvgl/test_label/main.lua

@@ -0,0 +1,40 @@
+-- Button 组件测试脚本
+PROJECT = "easylvgl"
+VERSION = "1.0.0"
+
+sys.taskInit(function()
+
+    -- 1. 初始化 EasyLVGL
+    -- easylvgl.init(w, h, buff_size, buff_mode)
+    -- w: 屏幕宽,可选,默认480
+    -- h: 屏幕高,可选,默认320
+    -- color_format: 颜色格式,可选,默认ARGB8888
+    --                   可用值:easylvgl.COLOR_FORMAT_RGB565(默认,嵌入式,节省内存)
+    --                          easylvgl.COLOR_FORMAT_ARGB8888(pc,高质量)
+    local ret = easylvgl.init(800, 600, easylvgl.COLOR_FORMAT_ARGB8888)
+    if not ret then
+        log.error("easylvgl", "init failed")
+        return
+    end
+
+
+    local label = easylvgl.label({
+        text = "Hello, World!",
+        x = 20, y = 80, w = 500, h = 500,
+    })
+
+    local label2 = easylvgl.label({
+        parent = label,
+        text = "my parent is label",
+        x = 20, y = 60, w = 300, h = 200,
+    })
+
+    while true do
+        easylvgl.refresh()
+        sys.wait(10)
+    end
+
+end)
+
+sys.run()
+

+ 48 - 0
bsp/pc/test/111.easylvgl/test_label_hzfont/main.lua

@@ -0,0 +1,48 @@
+-- Button 组件测试脚本
+PROJECT = "easylvgl"
+VERSION = "1.0.0"
+
+sys.taskInit(function()
+
+    -- 1. 初始化 EasyLVGL
+    -- easylvgl.init(w, h, buff_size, buff_mode)
+    -- w: 屏幕宽,可选,默认480
+    -- h: 屏幕高,可选,默认320
+    -- color_format: 颜色格式,可选,默认ARGB8888
+    --                   可用值:easylvgl.COLOR_FORMAT_RGB565(默认,嵌入式,节省内存)
+    --                          easylvgl.COLOR_FORMAT_ARGB8888(pc,高质量)
+    local ret = easylvgl.init(800, 600, easylvgl.COLOR_FORMAT_ARGB8888)
+    if not ret then
+        log.error("easylvgl", "init failed")
+        return
+    end
+
+    -- 加载hzfont字库,从而支持中文显示
+    easylvgl.font_load({
+        type = "hzfont", -- 字体类型,可选 "hzfont" 或 "bin"
+        path = nil, -- 字体路径,对于 "hzfont",传 nil 则使用内置字库
+        size = 16, -- 字体大小,默认 16
+        cache_size = 2048, -- 缓存字数大小,默认 2048
+        antialias = 4, -- 抗锯齿等级,默认 4
+    })
+
+    local label = easylvgl.label({
+        text = "Hello, World!",
+        x = 20, y = 80, w = 500, h = 500,
+    })
+
+    local label2 = easylvgl.label({
+        parent = label,
+        text = "你好,世界!\n你好,宇宙!\n你好,人类!",
+        x = 20, y = 60, w = 300, h = 200,
+    })
+
+    while true do
+        easylvgl.refresh()
+        sys.wait(10)
+    end
+
+end)
+
+sys.run()
+

+ 32 - 0
bsp/pc/test/111.easylvgl/test_msgbox/main.lua

@@ -0,0 +1,32 @@
+-- Msgbox component test script
+PROJECT = "easylvgl"
+VERSION = "1.0.0"
+
+sys.taskInit(function()
+    local ret = easylvgl.init(800, 600, easylvgl.COLOR_FORMAT_ARGB8888)
+    if not ret then
+        log.error("easylvgl", "msgbox init failed")
+        return
+    end
+
+    -- 消息框组件
+    local msgbox = easylvgl.msgbox({
+        title = "Notice", -- 标题文本,可选
+        text = "Please select an action button", -- 内容文本,可选
+        buttons = {"OK", "Cancel", "Close"}, -- 按钮标签数组,默认 ["OK"]
+        timeout = 0, -- 自动关闭时间(毫秒),默认 0
+        auto_center = true, -- 是否自动居中,默认 true
+        on_action = function(self, text) -- 按钮点击回调
+            log.info("msgbox", "action", text)
+            self:release()
+        end,
+    })
+
+    while true do
+        easylvgl.refresh()
+        sys.wait(10)
+    end
+end)
+
+sys.run()
+

+ 30 - 0
bsp/pc/test/111.easylvgl/test_switch/main.lua

@@ -0,0 +1,30 @@
+-- Switch 组件测试脚本
+PROJECT = "easylvgl"
+VERSION = "1.0.0"
+
+sys.taskInit(function()
+    local ret = easylvgl.init(800, 600, easylvgl.COLOR_FORMAT_ARGB8888)
+    if not ret then
+        log.error("easylvgl", "switch init failed")
+        return
+    end
+
+    -- 开关组件
+    local sw = easylvgl.switch({
+        parent = easylvgl.screen,
+        checked = true, -- 初始状态,默认 false
+        x = 40, y = 120, w = 120, h = 60, -- x, y, w, h
+        style = "success", -- 预设样式,如 "danger"/"success"
+        on_change = function(self) -- 状态变更回调
+            log.info("switch", "state changed", self:get_state())
+        end
+    })
+
+    while true do
+        easylvgl.refresh()
+        sys.wait(10)
+    end
+end)
+
+sys.run()
+

BIN
bsp/pc/test/111.easylvgl/test_win/logo.png


+ 81 - 0
bsp/pc/test/111.easylvgl/test_win/main.lua

@@ -0,0 +1,81 @@
+-- Win 组件测试脚本
+PROJECT = "easylvgl"
+VERSION = "1.0.0"
+
+sys.taskInit(function()
+    -- 1. 初始化 EasyLVGL
+    -- easylvgl.init(w, h, buff_size, buff_mode)
+    -- w: 屏幕宽,可选,默认480
+    -- h: 屏幕高,可选,默认320
+    -- color_format: 颜色格式,可选,默认ARGB8888
+    --                   可用值:easylvgl.COLOR_FORMAT_RGB565(默认,嵌入式,节省内存)
+    --                          easylvgl.COLOR_FORMAT_ARGB8888(pc,高质量)
+    local ret = easylvgl.init(800, 600, easylvgl.COLOR_FORMAT_ARGB8888)
+    if not ret then
+        log.error("easylvgl", "init failed")
+        return
+    end
+
+    -- 窗口组件
+    local win = easylvgl.win({
+        parent = easylvgl.screen,
+        title = "Settings", -- 标题文本,可选
+        x = 40, y = 40, w = 600, h = 400, -- x, y, w, h
+        close_btn = false, -- 是否显示关闭按钮,默认 false
+        auto_center = false, -- 是否自动居中,默认 true
+        on_close = function(self) -- 关闭回调
+            log.info("easylvgl.win", "win:on_close called")
+        end
+    })
+
+    --label
+    local label = easylvgl.label({
+        text = "win test",
+        x = 0, y = 0, w = 120, h = 40,
+    })
+
+    -- 下拉框组件
+    local dropdown = easylvgl.dropdown({
+        options = {"Option A", "Option B", "Option C"}, -- 选项列表(字符串数组)
+        default_index = 2, -- 默认选中项索引,默认 -1
+        x = 0, y = 40, w = 180, h = 50,
+        on_change = function(self, index) -- 选中项变化回调
+            log.info("dropdown", "selected index", index)
+        end
+    })
+
+    -- 不可点击图片
+    local img = easylvgl.image({
+        src = "/luadb/logo.png",
+        x = 0, y = 240, w = 80, h = 80,
+        zoom = 256, -- 缩放比例,默认 256(100%)
+        opacity = 255, -- 透明度,默认 255(不透明),范围 0-255
+    })
+
+    --先创建按钮(不指定 parent,默认添加到屏幕)
+    local inner_btn = easylvgl.button({
+        text = "Close",
+        x = 0, y = 340, w = 120, h = 40,
+        on_click = function(self)
+            log.info("easylvgl.win", "inner button closing window")
+            win:close()
+        end
+    })
+
+    -- 将按钮添加到窗口内容区域
+    win:add_content(inner_btn)
+    win:add_content(label)
+    win:add_content(dropdown)
+    win:add_content(img)
+    -- 设置窗口标题
+    win:set_title("Settings Window")
+
+    while true do
+        easylvgl.refresh()
+        sys.wait(10)
+    end
+
+end)
+
+sys.run()
+

+ 48 - 11
bsp/pc/xmake.lua

@@ -36,6 +36,10 @@ if os.getenv("LUAT_USE_GUI") == "y" then
     add_packages("libsdl2")
 end
 
+if os.getenv("LUAT_USE_LVGL9") == "y" then
+    add_defines("LUAT_USE_LVGL9=1")
+end
+
 if is_host("windows") then
     add_defines("LUAT_USE_WINDOWS")
     add_defines("_CRT_SECURE_NO_WARNINGS")
@@ -309,17 +313,50 @@ target("luatos-lua")
         -- lcd
         add_includedirs(luatos.."components/lcd")
         add_files(luatos.."components/lcd/*.c")
-        -- lvgl
-        add_includedirs(luatos.."components/lvgl")
-        add_includedirs(luatos.."components/lvgl/binding")
-        add_includedirs(luatos.."components/lvgl/gen")
-        add_includedirs(luatos.."components/lvgl/src")
-        add_includedirs(luatos.."components/lvgl/font")
-        add_includedirs(luatos.."components/lvgl/src/lv_font")
-        add_includedirs(luatos.."components/lvgl/sdl2")
-        add_files(luatos.."components/lvgl/**.c")
-        -- 默认不编译lv的demos, 节省大量的编译时间
-        remove_files(luatos.."components/lvgl/lv_demos/**.c")
+        
+        -- LVGL 9.4 + EasyLVGL - 最基础组件编译
+        -- 宏定义:启用 EasyLVGL 和 SDL2 平台
+        -- add_defines("LUAT_USE_EASYLVGL=1")
+        -- add_defines("LUAT_USE_EASYLVGL_SDL2=1")
+        -- 头文件添加:lvgl9 
+        add_includedirs(luatos.."components/easylvgl")
+        add_includedirs(luatos.."components/easylvgl/lvgl9")
+        add_includedirs(luatos.."components/easylvgl/lvgl9/src")
+        
+        -- 先添加所有源文件
+        add_files(luatos.."components/easylvgl/lvgl9/src/**.c")
+        
+        -- 排除不需要的组件(按优先级排序)
+        -- 1. 硬件驱动(PC 模拟器不需要)
+        remove_files(luatos.."components/easylvgl/lvgl9/src/drivers/**/*.c")
+        remove_files(luatos.."components/easylvgl/lvgl9/src/drivers/**/*.cpp")
+        
+        -- 2. 硬件加速绘制引擎(只保留软件渲染 SW)
+        remove_files(luatos.."components/easylvgl/lvgl9/src/draw/dma2d/**/*.c")
+        remove_files(luatos.."components/easylvgl/lvgl9/src/draw/eve/**/*.c")
+        remove_files(luatos.."components/easylvgl/lvgl9/src/draw/nema_gfx/**/*.c")
+        remove_files(luatos.."components/easylvgl/lvgl9/src/draw/nxp/**/*.c")
+        remove_files(luatos.."components/easylvgl/lvgl9/src/draw/opengles/**/*.c")
+        remove_files(luatos.."components/easylvgl/lvgl9/src/draw/renesas/**/*.c")
+        remove_files(luatos.."components/easylvgl/lvgl9/src/draw/vg_lite/**/*.c")
+        remove_files(luatos.."components/easylvgl/lvgl9/src/draw/sdl/**/*.c")
+        remove_files(luatos.."components/easylvgl/lvgl9/src/draw/espressif/**/*.c")
+        remove_files(luatos.."components/easylvgl/lvgl9/src/draw/convert/**/*.c")
+        
+        -- 3. 库:排除不需要的库(可选功能)
+        -- remove_files(luatos.."components/easylvgl/lvgl9/src/libs/**/*.c")
+        
+        -- EasyLVGL 架构配置
+        -- 1. 公共头文件
+        add_includedirs(luatos.."components/easylvgl/inc")
+        
+        -- 2. 包含 src 目录下的所有文件(递归)
+        add_includedirs(luatos.."components/easylvgl/src")
+        add_files(luatos.."components/easylvgl/src/**/*.c")
+        
+        -- 3. Lua 绑定层(binding,不在 src 目录下,需单独处理)
+        add_includedirs(luatos.."components/easylvgl/binding")
+        add_files(luatos.."components/easylvgl/binding/*.c")
 
         -- qrcode 和 tjpgd
         add_includedirs(luatos.."components/qrcode")

+ 4 - 1
components/airtalk/core/airtalk_network.c

@@ -317,18 +317,21 @@ TX_DATA_DONE:
 			luat_mutex_unlock(prv_network.record_cache_locker);
 			luat_start_rtos_timer(prv_network.download_check_timer, prv_network.download_no_data_time, 1);
 			prv_network.new_data_flag = 0;
+			luat_airtalk_callback(LUAT_AIRTALK_CB_DATA_START, NULL, 0);
 			break;
 		case AIRTALK_EVENT_NETWORK_FORCE_SYNC:
 			LUAT_DEBUG_PRINT("sync lost resync!");
 			sync_lost = 1;
+			luat_airtalk_callback(LUAT_AIRTALK_CB_DATA_RESYNC, NULL, 0);
 			break;
 		case AIRTALK_EVENT_NETWORK_FORCE_STOP:
 			if (prv_network.is_ready)
 			{
-				sync_lost = 1;
 				prv_network.is_ready = 0;
 				airtalk_full_stop();
 			}
+			sync_lost = 1;
+			luat_airtalk_callback(LUAT_AIRTALK_CB_DATA_STOP, NULL, 0);
 			break;
 		case AIRTALK_EVENT_NETWORK_MSG:
 			break;

+ 3 - 1
components/airtalk/include/luat_airtalk.h

@@ -25,7 +25,9 @@ enum
 	LUAT_AIRTALK_CB_AUDIO_START,
 	LUAT_AIRTALK_CB_AUDIO_END,
 	LUAT_AIRTALK_CB_ERROR,
-
+	LUAT_AIRTALK_CB_DATA_START,
+	LUAT_AIRTALK_CB_DATA_STOP,
+	LUAT_AIRTALK_CB_DATA_RESYNC,
 	LUAT_AIRTALL_ERR_LONG_TIME_NO_DATA = 0,
 };
 void luat_airtalk_callback(uint32_t event, void *param, uint32_t param_len);

+ 391 - 0
components/easylvgl/binding/luat_lib_easylvgl.c

@@ -0,0 +1,391 @@
+/*
+@module  easylvgl
+@summary EasyLVGL图像库 (LVGL 9.4) - 重构版本
+@version 0.1.0
+@date    2025.12.02
+@tag     LUAT_USE_EASYLVGL
+*/
+
+#include "luat_base.h"
+#include "luat_log.h"
+#include "lua.h"
+#include "lauxlib.h"
+#include "rotable2.h"
+#include "luat_malloc.h"
+#include "../inc/luat_easylvgl.h"
+#include "../inc/luat_easylvgl_component.h"
+#include "../inc/luat_easylvgl_binding.h"
+#include "../inc/luat_easylvgl_symbol.h"
+#include "luat_conf_bsp.h"
+#if defined(LUAT_USE_EASYLVGL_BK7258)
+#include "../src/platform/bk7258/luat_easylvgl_platform_bk7258.h"
+#endif
+#include <string.h>
+
+#define LUAT_LOG_TAG "easylvgl"
+#include "luat_log.h"
+
+// 全局上下文(单例模式)
+static easylvgl_ctx_t *g_ctx = NULL;
+
+// 函数声明
+static int l_easylvgl_init(lua_State *L);
+static int l_easylvgl_deinit(lua_State *L);
+static int l_easylvgl_refresh(lua_State *L);
+static int l_easylvgl_indev_bind_touch(lua_State *L);
+static int l_easylvgl_keyboard_enable_system(lua_State *L);
+static int l_easylvgl_font_load(lua_State *L);
+
+// Button 模块声明
+extern void easylvgl_register_button_meta(lua_State *L);
+extern int easylvgl_button_create(lua_State *L);
+
+// Label 模块声明
+extern void easylvgl_register_label_meta(lua_State *L);
+extern int easylvgl_label_create(lua_State *L);
+
+// Image 模块声明
+extern void easylvgl_register_image_meta(lua_State *L);
+extern int easylvgl_image_create(lua_State *L);
+
+// Container 模块声明
+extern void easylvgl_register_container_meta(lua_State *L);
+extern int easylvgl_container_create(lua_State *L);
+
+// Table 模块声明
+extern void easylvgl_register_table_meta(lua_State *L);
+extern int easylvgl_table_create(lua_State *L);
+
+// TabView 模块声明
+extern void easylvgl_register_tabview_meta(lua_State *L);
+extern int easylvgl_tabview_create(lua_State *L);
+
+// Win 模块声明
+extern void easylvgl_register_win_meta(lua_State *L);
+extern int easylvgl_win_create(lua_State *L);
+
+// Dropdown 模块声明
+extern void easylvgl_register_dropdown_meta(lua_State *L);
+extern int easylvgl_dropdown_create(lua_State *L);
+
+// Switch 模块声明
+extern void easylvgl_register_switch_meta(lua_State *L);
+extern int easylvgl_switch_create(lua_State *L);
+
+// Msgbox 模块声明
+extern void easylvgl_register_msgbox_meta(lua_State *L);
+extern int easylvgl_msgbox_create(lua_State *L);
+
+// Textarea 模块声明
+extern void easylvgl_register_textarea_meta(lua_State *L);
+extern int easylvgl_textarea_create(lua_State *L);
+
+// Keyboard 模块声明
+extern void easylvgl_register_keyboard_meta(lua_State *L);
+extern int easylvgl_keyboard_create(lua_State *L);
+
+// 模块注册表
+static const rotable_Reg_t reg_easylvgl[] = {
+    // 基础设置
+    {"init", ROREG_FUNC(l_easylvgl_init)},
+    {"deinit", ROREG_FUNC(l_easylvgl_deinit)},
+    {"refresh", ROREG_FUNC(l_easylvgl_refresh)},
+    {"indev_bind_touch", ROREG_FUNC(l_easylvgl_indev_bind_touch)},
+    {"keyboard_enable_system", ROREG_FUNC(l_easylvgl_keyboard_enable_system)},
+    {"font_load", ROREG_FUNC(l_easylvgl_font_load)},
+    // 组件注册
+    {"button", ROREG_FUNC(easylvgl_button_create)},
+    {"label", ROREG_FUNC(easylvgl_label_create)},
+    {"image", ROREG_FUNC(easylvgl_image_create)},
+    {"container", ROREG_FUNC(easylvgl_container_create)},
+    {"table", ROREG_FUNC(easylvgl_table_create)},
+    {"tabview", ROREG_FUNC(easylvgl_tabview_create)},
+    {"win", ROREG_FUNC(easylvgl_win_create)},
+    {"dropdown", ROREG_FUNC(easylvgl_dropdown_create)},
+    {"switch", ROREG_FUNC(easylvgl_switch_create)},
+    {"msgbox", ROREG_FUNC(easylvgl_msgbox_create)},
+    {"textarea", ROREG_FUNC(easylvgl_textarea_create)},
+    {"keyboard", ROREG_FUNC(easylvgl_keyboard_create)},
+    // 颜色格式常量
+    {"COLOR_FORMAT_RGB565", ROREG_INT(EASYLVGL_COLOR_FORMAT_RGB565)},
+    {"COLOR_FORMAT_ARGB8888", ROREG_INT(EASYLVGL_COLOR_FORMAT_ARGB8888)},
+    // 文本对齐常量
+    {"EASYLVGL_TEXT_ALIGN_LEFT", ROREG_INT(LV_TEXT_ALIGN_LEFT)},
+    {"EASYLVGL_TEXT_ALIGN_CENTER", ROREG_INT(LV_TEXT_ALIGN_CENTER)},
+    {"EASYLVGL_TEXT_ALIGN_RIGHT", ROREG_INT(LV_TEXT_ALIGN_RIGHT)},
+    // TabView 对齐常量
+    {"EASYLVGL_TABVIEW_PAD_ALL", ROREG_INT(EASYLVGL_TABVIEW_PAD_ALL)},
+    {"EASYLVGL_TABVIEW_PAD_HOR", ROREG_INT(EASYLVGL_TABVIEW_PAD_HOR)},
+    {"EASYLVGL_TABVIEW_PAD_VER", ROREG_INT(EASYLVGL_TABVIEW_PAD_VER)},
+    {"EASYLVGL_TABVIEW_PAD_TOP", ROREG_INT(EASYLVGL_TABVIEW_PAD_TOP)},
+    {"EASYLVGL_TABVIEW_PAD_BOTTOM", ROREG_INT(EASYLVGL_TABVIEW_PAD_BOTTOM)},
+    {"EASYLVGL_TABVIEW_PAD_LEFT", ROREG_INT(EASYLVGL_TABVIEW_PAD_LEFT)},
+    {"EASYLVGL_TABVIEW_PAD_RIGHT", ROREG_INT(EASYLVGL_TABVIEW_PAD_RIGHT)},
+    // 图标常量
+    EASYLVGL_SYMBOL_REG,
+    {NULL, ROREG_INT(0)}
+};
+
+LUAMOD_API int luaopen_easylvgl(lua_State *L) {
+    // 注册各组件元表
+    easylvgl_register_button_meta(L);
+    easylvgl_register_label_meta(L);
+    easylvgl_register_image_meta(L);
+    easylvgl_register_container_meta(L);
+    easylvgl_register_table_meta(L);
+    easylvgl_register_tabview_meta(L);
+    easylvgl_register_win_meta(L);
+    easylvgl_register_dropdown_meta(L);
+    easylvgl_register_switch_meta(L);
+    easylvgl_register_msgbox_meta(L);
+    easylvgl_register_textarea_meta(L);
+    easylvgl_register_keyboard_meta(L);
+    
+    // 注册模块函数
+    luat_newlib2(L, reg_easylvgl);
+    return 1;
+}
+
+/**
+ * 初始化 EasyLVGL
+ * @api easylvgl.init(width, height, color_format)
+ * @int width 屏幕宽度,默认 480
+ * @int height 屏幕高度,默认 320
+ * @int color_format 颜色格式,可选,默认 RGB565
+ *                   可用值:easylvgl.COLOR_FORMAT_RGB565(嵌入式,节省内存)
+ *                          easylvgl.COLOR_FORMAT_ARGB8888(默认,高质量)
+ * @return bool 成功返回 true,失败返回 false
+ */
+static int l_easylvgl_init(lua_State *L) {
+    if (g_ctx != NULL) {
+        LLOGE("easylvgl already initialized");
+        lua_pushboolean(L, 0);
+        return 1;
+    }
+    
+    int w = luaL_optinteger(L, 1, 480);
+    int h = luaL_optinteger(L, 2, 320);
+    
+    // 获取颜色格式参数,默认为RGB565
+    lv_color_format_t color_format = (lv_color_format_t)luaL_optinteger(L, 3, EASYLVGL_COLOR_FORMAT_RGB565);
+    
+    // 验证颜色格式是否有效,需要和lvgl的颜色格式对应(只支持 RGB565 和 ARGB8888)
+    if (color_format != EASYLVGL_COLOR_FORMAT_RGB565 && 
+        color_format != EASYLVGL_COLOR_FORMAT_ARGB8888) {
+        LLOGE("easylvgl.init: invalid color format %d, using RGB565", color_format);
+        color_format = EASYLVGL_COLOR_FORMAT_RGB565;
+    }
+    
+    // 分配上下文
+    g_ctx = luat_heap_malloc(sizeof(easylvgl_ctx_t));
+    if (g_ctx == NULL) {
+        lua_pushboolean(L, 0);
+        return 1;
+    }
+    
+    // 创建上下文(自动选择平台驱动)
+    int ret = easylvgl_ctx_create(g_ctx, NULL);
+    if (ret != 0) {
+        luat_heap_free(g_ctx);
+        g_ctx = NULL;
+        lua_pushboolean(L, 0);
+        return 1;
+    }
+    
+    // 存储 Lua 状态
+    g_ctx->L = L;
+    
+    // 将上下文存储到注册表
+    lua_pushlightuserdata(L, g_ctx);
+    lua_setfield(L, LUA_REGISTRYINDEX, "easylvgl_ctx");
+    
+    // 初始化 EasyLVGL(使用指定的颜色格式)
+    ret = easylvgl_init(g_ctx, w, h, color_format);
+    if (ret != 0) {
+        easylvgl_ctx_create(g_ctx, NULL);  // 清理
+        luat_heap_free(g_ctx);
+        g_ctx = NULL;
+        lua_pushboolean(L, 0);
+        return 1;
+    }
+    
+    lua_pushboolean(L, 1);
+    return 1;
+}
+
+/**
+ * 反初始化 EasyLVGL
+ * @api easylvgl.deinit()
+ * @return nil
+ */
+static int l_easylvgl_deinit(lua_State *L) {
+    (void)L;
+    
+    if (g_ctx != NULL) {
+        easylvgl_deinit(g_ctx);
+        luat_heap_free(g_ctx);
+        g_ctx = NULL;
+        
+        // 清除注册表中的上下文
+        lua_pushnil(L);
+        lua_setfield(L, LUA_REGISTRYINDEX, "easylvgl_ctx");
+    }
+    
+    return 0;
+}
+
+/**
+ * 刷新 LVGL 显示(执行定时器处理)
+ * @api easylvgl.refresh()
+ * @return nil
+ */
+static int l_easylvgl_refresh(lua_State *L) {
+    (void)L;
+    
+    if (g_ctx == NULL) {
+        luaL_error(L, "easylvgl not initialized, call easylvgl.init() first");
+        return 0;
+    }
+    
+    // 执行 LVGL 定时器处理(处理重绘、动画、事件等)
+    lv_timer_handler();
+    
+    return 0;
+}
+
+/**
+ * 绑定触摸输入配置到 BK7258 平台
+ * @api easylvgl.indev_bind_touch(tp_cfg)
+ * @userdata tp_cfg luat_tp_config_t*(lightuserdata)
+ * @return bool 绑定是否成功
+ */
+static int l_easylvgl_indev_bind_touch(lua_State *L) {
+#if defined(LUAT_USE_EASYLVGL_BK7258)
+    luat_tp_config_t *tp_cfg = (luat_tp_config_t *)lua_touserdata(L, 1);
+    if (tp_cfg == NULL) {
+        LLOGE("indev_bind_touch tp_cfg is NULL");
+        lua_pushboolean(L, 0);
+        return 1;
+    }
+
+    /* 保存到平台绑定,供初始化时同步 */
+    easylvgl_platform_bk7258_bind_tp(tp_cfg);
+
+    /* 如果上下文已存在且平台数据已分配,运行时也同步一份 */
+    if (g_ctx && g_ctx->platform_data) {
+        bk7258_platform_data_t *data = (bk7258_platform_data_t *)g_ctx->platform_data;
+        data->tp_config = tp_cfg;
+    }
+
+    LLOGD("indev_bind_touch bind %p", tp_cfg);
+    lua_pushboolean(L, 1);
+    return 1;
+// SDL2 路径:SDL 输入已经通过事件轮询获取,无需绑定 TP
+#elif defined(LUAT_USE_EASYLVGL_SDL2)
+    (void)L;
+    LLOGI("indev_bind_touch ignored on SDL2 (mouse/SDL events used)");
+    lua_pushboolean(L, 1);
+    return 1;
+#else
+    (void)L;
+    LLOGE("indev_bind_touch unsupported on this platform");
+    lua_pushboolean(L, 0);
+    return 1;
+#endif
+}
+
+static int l_easylvgl_keyboard_enable_system(lua_State *L) {
+    bool enable = lua_toboolean(L, 1);
+    easylvgl_ctx_t *ctx = NULL;
+    lua_getfield(L, LUA_REGISTRYINDEX, "easylvgl_ctx");
+    if (lua_type(L, -1) == LUA_TLIGHTUSERDATA) {
+        ctx = (easylvgl_ctx_t *)lua_touserdata(L, -1);
+    }
+    lua_pop(L, 1);
+
+    if (ctx == NULL) {
+        luaL_error(L, "easylvgl not initialized");
+        return 0;
+    }
+
+    int ret = easylvgl_system_keyboard_enable(ctx, enable);
+    lua_pushboolean(L, ret == EASYLVGL_OK);
+    return 1;
+}
+
+/**
+ * 推送组件 userdata(通用辅助函数)
+ */
+void easylvgl_push_component_userdata(lua_State *L, lv_obj_t *obj, const char *mt) {
+    easylvgl_component_ud_t *ud = (easylvgl_component_ud_t *)lua_newuserdata(L, sizeof(easylvgl_component_ud_t));
+    ud->obj = obj;
+    luaL_getmetatable(L, mt);
+    lua_setmetatable(L, -2);
+}
+
+/**
+ * 检查组件 userdata(通用辅助函数)
+ */
+lv_obj_t *easylvgl_check_component(lua_State *L, int index, const char *mt) {
+    easylvgl_component_ud_t *ud = (easylvgl_component_ud_t *)luaL_checkudata(L, index, mt);
+    if (ud == NULL || ud->obj == NULL) {
+        luaL_error(L, "invalid %s object", mt);
+    }
+    return ud->obj;
+}
+
+/**
+ * 加载字体
+ * @api easylvgl.font_load(config)
+ * @table config 配置表
+ * @string config.type 字体类型,"hzfont" 或 "bin"
+ * @string config.path 字体路径,对于 "hzfont",传 nil 则使用内置字库
+ * @int config.size 可选,TTF 字体大小,默认 16
+ * @int config.cache_size 可选,TTF 缓存数量,默认 256
+ * @int config.antialias 可选,TTF 抗锯齿等级,默认 -1(自动)
+ * @return userdata 字体指针
+ */
+static int l_easylvgl_font_load(lua_State *L) {
+    luaL_checktype(L, 1, LUA_TTABLE);
+    const char *type = easylvgl_marshal_string(L, 1, "type", NULL);
+    if (type == NULL) {
+        luaL_error(L, "font_load: config.type is required");
+        return 0;
+    }
+    lv_font_t *font = NULL;
+
+    if (strcmp(type, "hzfont") == 0) {
+        const char *path = easylvgl_marshal_string(L, 1, "path", NULL);
+        uint16_t size = easylvgl_marshal_integer(L, 1, "size", 16);
+        uint32_t cache_size = easylvgl_marshal_integer(L, 1, "cache_size", 256);
+        int antialias = easylvgl_marshal_integer(L, 1, "antialias", -1);
+        font = easylvgl_font_hzfont_create(path, size, cache_size, antialias);
+        if (font == NULL) {
+            LLOGE("font_load: failed to create hzfont");
+            return 0;
+        }
+    } else if (strcmp(type, "bin") == 0) {
+        const char *path = luaL_checkstring(L, 2);
+        font = lv_binfont_create(path);
+        if (font == NULL) {
+            LLOGE("font_load: failed to create bin font");
+            return 0;
+        }
+    } else {
+        LLOGE("font_load: unsupported type %s", type);
+        return 0;
+    }
+
+    if (font) {
+        // 设置为全局默认字体
+        lv_obj_set_style_text_font(lv_screen_active(), font, 0);
+        lua_pushlightuserdata(L, font);
+        return 1;
+    }else{
+        LLOGE("font_load: failed to create font");
+        return 0;
+    }
+    
+    return 0;
+}
+

+ 143 - 0
components/easylvgl/binding/luat_lib_easylvgl_button.c

@@ -0,0 +1,143 @@
+/*
+@module  easylvgl.button
+@summary EasyLVGL Button 组件 Lua 绑定
+@version 0.1.0
+@date    2025.12.02
+@tag     LUAT_USE_EASYLVGL
+*/
+
+#include "luat_base.h"
+#include "luat_log.h"
+#include "lua.h"
+#include "lauxlib.h"
+#include "../inc/luat_easylvgl.h"
+#include "../inc/luat_easylvgl_component.h"
+#include "../inc/luat_easylvgl_binding.h"
+#include <string.h>
+
+#define LUAT_LOG_TAG "easylvgl.button"
+#include "luat_log.h"
+
+// 元表名称
+#define EASYLVGL_BUTTON_MT "easylvgl.button"
+
+/**
+ * 创建 Button 组件
+ * @api easylvgl.button(config)
+ * @table config 配置表
+ * @int config.x X 坐标,默认 0
+ * @int config.y Y 坐标,默认 0
+ * @int config.w 宽度,默认 100
+ * @int config.h 高度,默认 40
+ * @string config.text 文本内容,可选
+ * @function config.on_click 点击回调函数,可选
+ * @userdata config.parent 父对象,可选,默认当前屏幕
+ * @return userdata Button 对象
+ */
+static int l_easylvgl_button(lua_State *L) {
+    // 检查上下文是否已初始化(从注册表获取)
+    easylvgl_ctx_t *ctx = NULL;
+    lua_getfield(L, LUA_REGISTRYINDEX, "easylvgl_ctx");
+    if (lua_type(L, -1) == LUA_TLIGHTUSERDATA) {
+        ctx = (easylvgl_ctx_t *)lua_touserdata(L, -1);
+    }
+    lua_pop(L, 1);
+    
+    if (ctx == NULL) {
+        luaL_error(L, "easylvgl not initialized, call easylvgl.init() first");
+        return 0;
+    }
+    
+    luaL_checktype(L, 1, LUA_TTABLE);
+    
+    lv_obj_t *btn = easylvgl_button_create_from_config(L, 1);
+    if (btn == NULL) {
+        lua_pushnil(L);
+        return 1;
+    }
+    
+    easylvgl_push_component_userdata(L, btn, EASYLVGL_BUTTON_MT);
+    return 1;
+}
+
+/**
+ * Button:set_text(text)
+ * @api button:set_text(text)
+ * @string text 文本内容
+ * @return nil
+ */
+static int l_button_set_text(lua_State *L) {
+    lv_obj_t *btn = easylvgl_check_component(L, 1, EASYLVGL_BUTTON_MT);
+    const char *text = luaL_checkstring(L, 2);
+    easylvgl_button_set_text(btn, text);
+    return 0;
+}
+
+/**
+ * Button:set_on_click(callback)
+ * @api button:set_on_click(callback)
+ * @function callback 回调函数
+ * @return nil
+ */
+static int l_button_set_on_click(lua_State *L) {
+    lv_obj_t *btn = easylvgl_check_component(L, 1, EASYLVGL_BUTTON_MT);
+    luaL_checktype(L, 2, LUA_TFUNCTION);
+    
+    // 保存回调函数到 registry
+    lua_pushvalue(L, 2);
+    int ref = luaL_ref(L, LUA_REGISTRYINDEX);
+    
+    easylvgl_button_set_on_click(btn, ref);
+    return 0;
+}
+
+/**
+ * Button GC(垃圾回收)
+ */
+static int l_button_gc(lua_State *L) {
+    easylvgl_component_ud_t *ud = (easylvgl_component_ud_t *)luaL_checkudata(L, 1, EASYLVGL_BUTTON_MT);
+    if (ud != NULL && ud->obj != NULL) {
+        // 获取元数据并释放
+        easylvgl_component_meta_t *meta = easylvgl_component_meta_get(ud->obj);
+        if (meta != NULL) {
+            easylvgl_component_meta_free(meta);
+        }
+        
+        // 删除 LVGL 对象
+        lv_obj_delete(ud->obj);
+        ud->obj = NULL;
+    }
+    return 0;
+}
+
+/**
+ * 注册 Button 元表
+ * @param L Lua 状态
+ */
+void easylvgl_register_button_meta(lua_State *L) {
+    luaL_newmetatable(L, EASYLVGL_BUTTON_MT);
+    
+    // 设置元方法
+    lua_pushcfunction(L, l_button_gc);
+    lua_setfield(L, -2, "__gc");
+    
+    // 设置方法表
+    static const luaL_Reg methods[] = {
+        {"set_text", l_button_set_text},
+        {"set_on_click", l_button_set_on_click},
+        {NULL, NULL}
+    };
+    
+    luaL_newlib(L, methods);
+    lua_setfield(L, -2, "__index");
+    
+    lua_pop(L, 1);
+}
+
+/**
+ * Button 创建函数(供主模块注册)
+ */
+int easylvgl_button_create(lua_State *L) {
+    return l_easylvgl_button(L);
+}
+

+ 99 - 0
components/easylvgl/binding/luat_lib_easylvgl_container.c

@@ -0,0 +1,99 @@
+/*
+@module  easylvgl.container
+@summary EasyLVGL Container 组件 Lua 绑定
+@version 0.1.0
+@date    2025.12.25
+@tag     LUAT_USE_EASYLVGL
+*/
+
+#include "luat_base.h"
+#include "lua.h"
+#include "lauxlib.h"
+#include "../inc/luat_easylvgl.h"
+#include "../inc/luat_easylvgl_component.h"
+#include "../inc/luat_easylvgl_binding.h"
+
+#define EASYLVGL_CONTAINER_MT "easylvgl.container"
+
+/**
+ * 创建 Container 组件
+ * @api easylvgl.container(config)
+ */
+static int l_easylvgl_container(lua_State *L) {
+    easylvgl_ctx_t *ctx = NULL;
+    lua_getfield(L, LUA_REGISTRYINDEX, "easylvgl_ctx");
+    if (lua_type(L, -1) == LUA_TLIGHTUSERDATA) {
+        ctx = (easylvgl_ctx_t *)lua_touserdata(L, -1);
+    }
+    lua_pop(L, 1);
+
+    if (ctx == NULL) {
+        luaL_error(L, "easylvgl not initialized, call easylvgl.init() first");
+        return 0;
+    }
+
+    luaL_checktype(L, 1, LUA_TTABLE);
+
+    lv_obj_t *container = easylvgl_container_create_from_config(L, 1);
+    if (container == NULL) {
+        lua_pushnil(L);
+        return 1;
+    }
+
+    easylvgl_push_component_userdata(L, container, EASYLVGL_CONTAINER_MT);
+    return 1;
+}
+
+/**
+ * Container:set_color(color)
+ * @api container:set_color(color)
+ * @int color 背景色(0xRRGGBB)
+ */
+static int l_container_set_color(lua_State *L) {
+    lv_obj_t *container = easylvgl_check_component(L, 1, EASYLVGL_CONTAINER_MT);
+    uint32_t color = (uint32_t)luaL_checkinteger(L, 2);
+    easylvgl_container_set_color(container, color);
+    return 0;
+}
+
+/**
+ * Container GC
+ */
+static int l_container_gc(lua_State *L) {
+    easylvgl_component_ud_t *ud = (easylvgl_component_ud_t *)luaL_checkudata(L, 1, EASYLVGL_CONTAINER_MT);
+    if (ud != NULL && ud->obj != NULL) {
+        easylvgl_component_meta_t *meta = easylvgl_component_meta_get(ud->obj);
+        if (meta != NULL) {
+            easylvgl_component_meta_free(meta);
+        }
+        lv_obj_delete(ud->obj);
+        ud->obj = NULL;
+    }
+    return 0;
+}
+
+/**
+ * 注册 Container 元表
+ */
+void easylvgl_register_container_meta(lua_State *L) {
+    luaL_newmetatable(L, EASYLVGL_CONTAINER_MT);
+    lua_pushcfunction(L, l_container_gc);
+    lua_setfield(L, -2, "__gc");
+
+    static const luaL_Reg methods[] = {
+        {"set_color", l_container_set_color},
+        {NULL, NULL}
+    };
+
+    luaL_newlib(L, methods);
+    lua_setfield(L, -2, "__index");
+    lua_pop(L, 1);
+}
+
+/**
+ * Container 创建函数(供主模块注册)
+ */
+int easylvgl_container_create(lua_State *L) {
+    return l_easylvgl_container(L);
+}
+

+ 158 - 0
components/easylvgl/binding/luat_lib_easylvgl_dropdown.c

@@ -0,0 +1,158 @@
+/*
+@module  easylvgl.dropdown
+@summary EasyLVGL Dropdown 组件
+@version 0.2.0
+@date    2025.12.12
+@tag     LUAT_USE_EASYLVGL
+*/
+
+#include "luat_base.h"
+#include "luat_log.h"
+#include "lua.h"
+#include "lauxlib.h"
+#include "../inc/luat_easylvgl.h"
+#include "../inc/luat_easylvgl_component.h"
+#include "../inc/luat_easylvgl_binding.h"
+#include "lvgl9/src/widgets/dropdown/lv_dropdown.h"
+#include "luat_malloc.h"
+
+#define LUAT_LOG_TAG "easylvgl.dropdown"
+#include "luat_log.h"
+
+#define EASYLVGL_DROPDOWN_MT "easylvgl.dropdown"
+
+/**
+ * 创建 Dropdown 组件
+ * @api easylvgl.dropdown(config)
+ * @table config 配置表
+ * @int config.x X 坐标,默认 0
+ * @int config.y Y 坐标,默认 0
+ * @int config.w 宽度,默认 140
+ * @int config.h 高度,默认 40
+ * @table config.options 选项列表(字符串数组)
+ * @int config.default_index 默认选中项索引,默认 -1
+ * @function config.on_change 选中项变化回调
+ * @userdata config.parent 父对象,可覆盖默认屏幕
+ * @return userdata Dropdown 对象,失败返回 nil
+ */
+static int l_easylvgl_dropdown(lua_State *L)
+{
+    easylvgl_ctx_t *ctx = NULL;
+    lua_getfield(L, LUA_REGISTRYINDEX, "easylvgl_ctx");
+    if (lua_type(L, -1) == LUA_TLIGHTUSERDATA) {
+        ctx = (easylvgl_ctx_t *)lua_touserdata(L, -1);
+    }
+    lua_pop(L, 1);
+
+    if (ctx == NULL) {
+        luaL_error(L, "easylvgl not initialized, call easylvgl.init() first");
+        return 0;
+    }
+
+    luaL_checktype(L, 1, LUA_TTABLE);
+
+    lv_obj_t *dropdown = easylvgl_dropdown_create_from_config(L, 1);
+    if (dropdown == NULL) {
+        lua_pushnil(L);
+        return 1;
+    }
+
+    easylvgl_push_component_userdata(L, dropdown, EASYLVGL_DROPDOWN_MT);
+    return 1;
+}
+
+/**
+ * Dropdown:set_selected(index)
+ * @api dropdown:set_selected(index)
+ * @int index 选中项索引,0 起始
+ * @return nil
+ */
+static int l_dropdown_set_selected(lua_State *L)
+{
+    lv_obj_t *dropdown = easylvgl_check_component(L, 1, EASYLVGL_DROPDOWN_MT);
+    int index = luaL_checkinteger(L, 2);
+    easylvgl_dropdown_set_selected(dropdown, index);
+    return 0;
+}
+
+/**
+ * Dropdown:get_selected()
+ * @api dropdown:get_selected()
+ * @return int 当前选中项索引
+ */
+static int l_dropdown_get_selected(lua_State *L)
+{
+    lv_obj_t *dropdown = easylvgl_check_component(L, 1, EASYLVGL_DROPDOWN_MT);
+    int index = easylvgl_dropdown_get_selected(dropdown);
+    lua_pushinteger(L, index);
+    return 1;
+}
+
+/**
+ * Dropdown:set_on_change(callback)
+ * @api dropdown:set_on_change(callback)
+ * @function callback 选中项改变回调
+ * @return nil
+ */
+static int l_dropdown_set_on_change(lua_State *L)
+{
+    lv_obj_t *dropdown = easylvgl_check_component(L, 1, EASYLVGL_DROPDOWN_MT);
+    luaL_checktype(L, 2, LUA_TFUNCTION);
+    lua_pushvalue(L, 2);
+    int ref = luaL_ref(L, LUA_REGISTRYINDEX);
+    easylvgl_dropdown_set_on_change(dropdown, ref);
+    return 0;
+}
+
+/**
+ * Dropdown GC(垃圾回收)
+ */
+static int l_dropdown_gc(lua_State *L)
+{
+    easylvgl_component_ud_t *ud = (easylvgl_component_ud_t *)luaL_checkudata(L, 1, EASYLVGL_DROPDOWN_MT);
+    if (ud != NULL && ud->obj != NULL) {
+        easylvgl_component_meta_t *meta = easylvgl_component_meta_get(ud->obj);
+        if (meta != NULL) {
+            if (meta->user_data != NULL) {
+                luat_heap_free(meta->user_data);
+                meta->user_data = NULL;
+            }
+            easylvgl_component_meta_free(meta);
+        }
+        lv_obj_delete(ud->obj);
+        ud->obj = NULL;
+    }
+    return 0;
+}
+
+/**
+ * 注册 Dropdown 元表
+ * @param L Lua 状态
+ */
+void easylvgl_register_dropdown_meta(lua_State *L)
+{
+    luaL_newmetatable(L, EASYLVGL_DROPDOWN_MT);
+
+    lua_pushcfunction(L, l_dropdown_gc);
+    lua_setfield(L, -2, "__gc");
+
+    static const luaL_Reg methods[] = {
+        {"set_selected", l_dropdown_set_selected},
+        {"get_selected", l_dropdown_get_selected},
+        {"set_on_change", l_dropdown_set_on_change},
+        {NULL, NULL}
+    };
+
+    luaL_newlib(L, methods);
+    lua_setfield(L, -2, "__index");
+    lua_pop(L, 1);
+}
+
+/**
+ * Dropdown 创建函数(供主模块注册)
+ */
+int easylvgl_dropdown_create(lua_State *L)
+{
+    return l_easylvgl_dropdown(L);
+}
+

+ 155 - 0
components/easylvgl/binding/luat_lib_easylvgl_image.c

@@ -0,0 +1,155 @@
+/*
+@module  easylvgl.image
+@summary EasyLVGL Image 组件 Lua 绑定
+@version 0.1.0
+@date    2025.12.02
+@tag     LUAT_USE_EASYLVGL
+*/
+
+#include "luat_base.h"
+#include "luat_log.h"
+#include "lua.h"
+#include "lauxlib.h"
+#include "../inc/luat_easylvgl.h"
+#include "../inc/luat_easylvgl_component.h"
+#include "../inc/luat_easylvgl_binding.h"
+#include <string.h>
+
+#define LUAT_LOG_TAG "easylvgl.image"
+#include "luat_log.h"
+
+// 元表名称
+#define EASYLVGL_IMAGE_MT "easylvgl.image"
+
+/**
+ * 创建 Image 组件
+ * @api easylvgl.image(config)
+ * @table config 配置表
+ * @int config.x X 坐标,默认 0
+ * @int config.y Y 坐标,默认 0
+ * @int config.w 宽度,默认 100
+ * @int config.h 高度,默认 100
+ * @string config.src 图片路径,可选
+ * @table config.pivot 旋转中心点,可选,格式 {x=0, y=0}
+ * @int config.zoom 缩放比例,默认 256(100%)
+ * @int config.opacity 透明度,默认 255(不透明),范围 0-255
+ * @function config.on_click 点击回调函数,可选
+ * @userdata config.parent 父对象,可选,默认当前屏幕
+ * @return userdata Image 对象
+ */
+static int l_easylvgl_image(lua_State *L) {
+    // 检查上下文是否已初始化(从注册表获取)
+    easylvgl_ctx_t *ctx = NULL;
+    lua_getfield(L, LUA_REGISTRYINDEX, "easylvgl_ctx");
+    if (lua_type(L, -1) == LUA_TLIGHTUSERDATA) {
+        ctx = (easylvgl_ctx_t *)lua_touserdata(L, -1);
+    }
+    lua_pop(L, 1);
+    
+    if (ctx == NULL) {
+        luaL_error(L, "easylvgl not initialized, call easylvgl.init() first");
+        return 0;
+    }
+    
+    luaL_checktype(L, 1, LUA_TTABLE);
+    
+    lv_obj_t *img = easylvgl_image_create_from_config(L, 1);
+    if (img == NULL) {
+        lua_pushnil(L);
+        return 1;
+    }
+    
+    easylvgl_push_component_userdata(L, img, EASYLVGL_IMAGE_MT);
+    return 1;
+}
+
+/**
+ * Image:set_src(src)
+ * @api image:set_src(src)
+ * @string src 图片路径或符号
+ * @return nil
+ */
+static int l_image_set_src(lua_State *L) {
+    lv_obj_t *img = easylvgl_check_component(L, 1, EASYLVGL_IMAGE_MT);
+    const char *src = luaL_checkstring(L, 2);
+    easylvgl_image_set_src(img, src);
+    return 0;
+}
+
+/**
+ * Image:set_zoom(zoom)
+ * @api image:set_zoom(zoom)
+ * @int zoom 缩放比例,256 = 100%
+ * @return nil
+ */
+static int l_image_set_zoom(lua_State *L) {
+    lv_obj_t *img = easylvgl_check_component(L, 1, EASYLVGL_IMAGE_MT);
+    int zoom = luaL_checkinteger(L, 2);
+    easylvgl_image_set_zoom(img, zoom);
+    return 0;
+}
+
+/**
+ * Image:set_opacity(opacity)
+ * @api image:set_opacity(opacity)
+ * @int opacity 透明度,0-255
+ * @return nil
+ */
+static int l_image_set_opacity(lua_State *L) {
+    lv_obj_t *img = easylvgl_check_component(L, 1, EASYLVGL_IMAGE_MT);
+    int opacity = luaL_checkinteger(L, 2);
+    easylvgl_image_set_opacity(img, opacity);
+    return 0;
+}
+
+/**
+ * Image GC(垃圾回收)
+ */
+static int l_image_gc(lua_State *L) {
+    easylvgl_component_ud_t *ud = (easylvgl_component_ud_t *)luaL_checkudata(L, 1, EASYLVGL_IMAGE_MT);
+    if (ud != NULL && ud->obj != NULL) {
+        // 获取元数据并释放
+        easylvgl_component_meta_t *meta = easylvgl_component_meta_get(ud->obj);
+        if (meta != NULL) {
+            easylvgl_component_meta_free(meta);
+        }
+        
+        // 删除 LVGL 对象
+        lv_obj_delete(ud->obj);
+        ud->obj = NULL;
+    }
+    return 0;
+}
+
+/**
+ * 注册 Image 元表
+ * @param L Lua 状态
+ */
+void easylvgl_register_image_meta(lua_State *L) {
+    luaL_newmetatable(L, EASYLVGL_IMAGE_MT);
+    
+    // 设置元方法
+    lua_pushcfunction(L, l_image_gc);
+    lua_setfield(L, -2, "__gc");
+    
+    // 设置方法表
+    static const luaL_Reg methods[] = {
+        {"set_src", l_image_set_src},
+        {"set_zoom", l_image_set_zoom},
+        {"set_opacity", l_image_set_opacity},
+        {NULL, NULL}
+    };
+    
+    luaL_newlib(L, methods);
+    lua_setfield(L, -2, "__index");
+    
+    lua_pop(L, 1);
+}
+
+/**
+ * Image 创建函数(供主模块注册)
+ */
+int easylvgl_image_create(lua_State *L) {
+    return l_easylvgl_image(L);
+}
+

+ 196 - 0
components/easylvgl/binding/luat_lib_easylvgl_keyboard.c

@@ -0,0 +1,196 @@
+/*
+@module  easylvgl.keyboard
+@summary EasyLVGL Keyboard 组件 Lua 绑定
+@version 0.3.0
+@date    2025.12.15
+@tag     LUAT_USE_EASYLVGL
+*/
+
+#include "luat_base.h"
+#include "luat_log.h"
+#include "lua.h"
+#include "lauxlib.h"
+#include "../inc/luat_easylvgl.h"
+#include "../inc/luat_easylvgl_component.h"
+#include "../inc/luat_easylvgl_binding.h"
+#include "luat_malloc.h"
+
+#define LUAT_LOG_TAG "easylvgl.keyboard"
+#include "luat_log.h"
+
+/**
+ * 创建 Keyboard 组件
+ * @api easylvgl.keyboard(config)
+ * @table config 配置表
+ * @int config.x X 坐标,默认 0
+ * @int config.y Y 坐标,默认 ctx->height-160(或 0)
+ * @int config.w 宽度,默认 ctx->width(或 480)
+ * @int config.h 高度,默认 160
+ * @string config.mode 键盘模式,如 "text"/"upper"/"special"/"numeric"
+ * @boolean config.popovers 是否启用提示弹窗,默认 true
+ * @userdata config.target 关联的 Textarea 对象,可选
+ * @return userdata Keyboard 对象,失败返回 nil
+ */
+static int l_easylvgl_keyboard(lua_State *L) {
+    easylvgl_ctx_t *ctx = NULL;
+    lua_getfield(L, LUA_REGISTRYINDEX, "easylvgl_ctx");
+    if (lua_type(L, -1) == LUA_TLIGHTUSERDATA) {
+        ctx = (easylvgl_ctx_t *)lua_touserdata(L, -1);
+    }
+    lua_pop(L, 1);
+    
+    if (ctx == NULL) {
+        luaL_error(L, "easylvgl not initialized, call easylvgl.init() first");
+        return 0;
+    }
+
+    luaL_checktype(L, 1, LUA_TTABLE);
+    lv_obj_t *keyboard = easylvgl_keyboard_create_from_config(L, 1);
+    if (keyboard == NULL) {
+        lua_pushnil(L);
+        return 1;
+    }
+
+    easylvgl_push_component_userdata(L, keyboard, EASYLVGL_KEYBOARD_MT);
+    return 1;
+}
+
+/**
+ * Keyboard:set_target(textarea)
+ * @api keyboard:set_target(textarea)
+ * @userdata textarea 目标 Textarea 对象
+ * @return nil
+ */
+static int l_keyboard_set_target(lua_State *L) {
+    lv_obj_t *keyboard = easylvgl_check_component(L, 1, EASYLVGL_KEYBOARD_MT);
+    lv_obj_t *textarea = easylvgl_check_component(L, 2, EASYLVGL_TEXTAREA_MT);
+    easylvgl_keyboard_set_target(keyboard, textarea);
+    return 0;
+}
+
+/**
+ * Keyboard:show()
+ * @api keyboard:show()
+ * @return nil
+ */
+static int l_keyboard_show(lua_State *L) {
+    lv_obj_t *keyboard = easylvgl_check_component(L, 1, EASYLVGL_KEYBOARD_MT);
+    easylvgl_keyboard_show(keyboard);
+    return 0;
+}
+
+/**
+ * Keyboard:hide()
+ * @api keyboard:hide()
+ * @return nil
+ */
+static int l_keyboard_hide(lua_State *L) {
+    lv_obj_t *keyboard = easylvgl_check_component(L, 1, EASYLVGL_KEYBOARD_MT);
+    easylvgl_keyboard_hide(keyboard);
+    return 0;
+}
+
+/**
+ * Keyboard:set_on_commit(callback)
+ * @api keyboard:set_on_commit(callback)
+ * @function callback 提交事件回调
+ * @return nil
+ */
+static int l_keyboard_set_on_commit(lua_State *L) {
+    lv_obj_t *keyboard = easylvgl_check_component(L, 1, EASYLVGL_KEYBOARD_MT);
+    luaL_checktype(L, 2, LUA_TFUNCTION);
+    lua_pushvalue(L, 2);
+    int ref = luaL_ref(L, LUA_REGISTRYINDEX);
+    easylvgl_keyboard_set_on_commit(keyboard, ref);
+    return 0;
+}
+
+/**
+ * Keyboard:set_layout(layout)
+ * @api keyboard:set_layout(layout)
+ * @string layout 布局名称
+ * @return nil
+ */
+static int l_keyboard_set_layout(lua_State *L) {
+    lv_obj_t *keyboard = easylvgl_check_component(L, 1, EASYLVGL_KEYBOARD_MT);
+    const char *layout = luaL_checkstring(L, 2);
+    easylvgl_keyboard_set_layout(keyboard, layout);
+    return 0;
+}
+
+/**
+ * Keyboard:get_target()
+ * @api keyboard:get_target()
+ * @return userdata|null 当前关联 Textarea
+ */
+static int l_keyboard_get_target(lua_State *L) {
+    lv_obj_t *keyboard = easylvgl_check_component(L, 1, EASYLVGL_KEYBOARD_MT);
+    easylvgl_component_meta_t *meta = easylvgl_component_meta_get(keyboard);
+    if (meta == NULL || meta->user_data == NULL) {
+        lua_pushnil(L);
+        return 1;
+    }
+
+    easylvgl_keyboard_data_t *data = (easylvgl_keyboard_data_t *)meta->user_data;
+    if (data == NULL || data->target == NULL) {
+        lua_pushnil(L);
+        return 1;
+    }
+
+    easylvgl_push_component_userdata(L, data->target, EASYLVGL_TEXTAREA_MT);
+    return 1;
+}
+
+/**
+ * Keyboard GC(释放资源)
+ */
+static int l_keyboard_gc(lua_State *L) {
+    easylvgl_component_ud_t *ud = (easylvgl_component_ud_t *)luaL_checkudata(L, 1, EASYLVGL_KEYBOARD_MT);
+    if (ud != NULL && ud->obj != NULL) {
+        easylvgl_component_meta_t *meta = easylvgl_component_meta_get(ud->obj);
+        if (meta != NULL) {
+            if (meta->user_data != NULL) {
+                luat_heap_free(meta->user_data);
+                meta->user_data = NULL;
+            }
+            easylvgl_component_meta_free(meta);
+        }
+
+        lv_obj_delete(ud->obj);
+        ud->obj = NULL;
+    }
+    return 0;
+}
+
+/**
+ * 注册 Keyboard 元表
+ * @param L Lua 状态
+ */
+void easylvgl_register_keyboard_meta(lua_State *L) {
+    luaL_newmetatable(L, EASYLVGL_KEYBOARD_MT);
+
+    lua_pushcfunction(L, l_keyboard_gc);
+    lua_setfield(L, -2, "__gc");
+
+    static const luaL_Reg methods[] = {
+        {"set_target", l_keyboard_set_target},
+        {"show", l_keyboard_show},
+        {"hide", l_keyboard_hide},
+        {"set_on_commit", l_keyboard_set_on_commit},
+        {"set_layout", l_keyboard_set_layout},
+        {"get_target", l_keyboard_get_target},
+        {NULL, NULL}
+    };
+
+    luaL_newlib(L, methods);
+    lua_setfield(L, -2, "__index");
+    lua_pop(L, 1);
+}
+
+/**
+ * Keyboard 创建函数(供主模块注册)
+ */
+int easylvgl_keyboard_create(lua_State *L) {
+    return l_easylvgl_keyboard(L);
+}
+

+ 171 - 0
components/easylvgl/binding/luat_lib_easylvgl_label.c

@@ -0,0 +1,171 @@
+/*
+@module  easylvgl.label
+@summary EasyLVGL Label 组件 Lua 绑定
+@version 0.1.0
+@date    2025.12.02
+@tag     LUAT_USE_EASYLVGL
+*/
+
+#include "luat_base.h"
+#include "luat_log.h"
+#include "lua.h"
+#include "lauxlib.h"
+#include "../inc/luat_easylvgl.h"
+#include "../inc/luat_easylvgl_component.h"
+#include "../inc/luat_easylvgl_binding.h"
+#include <string.h>
+
+#define LUAT_LOG_TAG "easylvgl.label"
+#include "luat_log.h"
+
+// 元表名称
+#define EASYLVGL_LABEL_MT "easylvgl.label"
+
+/**
+ * 创建 Label 组件
+ * @api easylvgl.label(config)
+ * @table config 配置表
+ * @int config.x X 坐标,默认 0
+ * @int config.y Y 坐标,默认 0
+ * @int config.w 宽度,默认 100
+ * @int config.h 高度,默认 40
+ * @string config.text 文本内容,可选
+ * @userdata config.parent 父对象,可选,默认当前屏幕
+ * @return userdata Label 对象
+ */
+static int l_easylvgl_label(lua_State *L) {
+    // 检查上下文是否已初始化(从注册表获取)
+    easylvgl_ctx_t *ctx = NULL;
+    lua_getfield(L, LUA_REGISTRYINDEX, "easylvgl_ctx");
+    if (lua_type(L, -1) == LUA_TLIGHTUSERDATA) {
+        ctx = (easylvgl_ctx_t *)lua_touserdata(L, -1);
+    }
+    lua_pop(L, 1);
+    
+    if (ctx == NULL) {
+        luaL_error(L, "easylvgl not initialized, call easylvgl.init() first");
+        return 0;
+    }
+    
+    luaL_checktype(L, 1, LUA_TTABLE);
+    
+    lv_obj_t *label = easylvgl_label_create_from_config(L, 1);
+    if (label == NULL) {
+        lua_pushnil(L);
+        return 1;
+    }
+    
+    easylvgl_push_component_userdata(L, label, EASYLVGL_LABEL_MT);
+    return 1;
+}
+
+/**
+ * Label:set_text(text)
+ * @api label:set_text(text)
+ * @string text 文本内容
+ * @return nil
+ */
+static int l_label_set_text(lua_State *L) {
+    lv_obj_t *label = easylvgl_check_component(L, 1, EASYLVGL_LABEL_MT);
+    const char *text = luaL_checkstring(L, 2);
+    easylvgl_label_set_text(label, text);
+    return 0;
+}
+
+static int l_label_set_symbol(lua_State *L) {
+    lv_obj_t *label = easylvgl_check_component(L, 1, EASYLVGL_LABEL_MT);
+    const char *symbol = luaL_checkstring(L, 2);
+    if (symbol == NULL) {
+        return 0;
+    }
+    lv_label_set_text(label, symbol);
+    return 0;
+}
+
+static int l_label_set_on_click(lua_State *L) {
+    lv_obj_t *label = easylvgl_check_component(L, 1, EASYLVGL_LABEL_MT);
+    luaL_checktype(L, 2, LUA_TFUNCTION);
+
+    lua_pushvalue(L, 2);
+    int ref = luaL_ref(L, LUA_REGISTRYINDEX);
+
+    lv_obj_add_flag(label, LV_OBJ_FLAG_CLICKABLE);
+
+    easylvgl_component_meta_t *meta = easylvgl_component_meta_get(label);
+    if (meta != NULL) {
+        easylvgl_component_bind_event(meta, EASYLVGL_EVENT_CLICKED, ref);
+    } else {
+        luaL_unref(L, LUA_REGISTRYINDEX, ref);
+    }
+
+    return 0;
+}
+
+/**
+ * Label:get_text()
+ * @api label:get_text()
+ * @return string 文本内容
+ */
+static int l_label_get_text(lua_State *L) {
+    lv_obj_t *label = easylvgl_check_component(L, 1, EASYLVGL_LABEL_MT);
+    const char *text = easylvgl_label_get_text(label);
+    if (text != NULL) {
+        lua_pushstring(L, text);
+    } else {
+        lua_pushstring(L, "");
+    }
+    return 1;
+}
+
+/**
+ * Label GC(垃圾回收)
+ */
+static int l_label_gc(lua_State *L) {
+    easylvgl_component_ud_t *ud = (easylvgl_component_ud_t *)luaL_checkudata(L, 1, EASYLVGL_LABEL_MT);
+    if (ud != NULL && ud->obj != NULL) {
+        // 获取元数据并释放
+        easylvgl_component_meta_t *meta = easylvgl_component_meta_get(ud->obj);
+        if (meta != NULL) {
+            easylvgl_component_meta_free(meta);
+        }
+        
+        // 删除 LVGL 对象
+        lv_obj_delete(ud->obj);
+        ud->obj = NULL;
+    }
+    return 0;
+}
+
+/**
+ * 注册 Label 元表
+ * @param L Lua 状态
+ */
+void easylvgl_register_label_meta(lua_State *L) {
+    luaL_newmetatable(L, EASYLVGL_LABEL_MT);
+    
+    // 设置元方法
+    lua_pushcfunction(L, l_label_gc);
+    lua_setfield(L, -2, "__gc");
+    
+    // 设置方法表
+    static const luaL_Reg methods[] = {
+        {"set_text", l_label_set_text},
+        {"set_symbol", l_label_set_symbol},
+        {"set_on_click", l_label_set_on_click},
+        {"get_text", l_label_get_text},
+        {NULL, NULL}
+    };
+    
+    luaL_newlib(L, methods);
+    lua_setfield(L, -2, "__index");
+    
+    lua_pop(L, 1);
+}
+
+/**
+ * Label 创建函数(供主模块注册)
+ */
+int easylvgl_label_create(lua_State *L) {
+    return l_easylvgl_label(L);
+}
+

+ 181 - 0
components/easylvgl/binding/luat_lib_easylvgl_msgbox.c

@@ -0,0 +1,181 @@
+/*
+@module  easylvgl.msgbox
+@summary EasyLVGL Msgbox 组件
+@version 0.2.0
+@date    2025.12.12
+@tag     LUAT_USE_EASYLVGL
+*/
+
+#include "luat_base.h"
+#include "luat_log.h"
+#include "lua.h"
+#include "lauxlib.h"
+#include "../inc/luat_easylvgl.h"
+#include "../inc/luat_easylvgl_component.h"
+#include "../inc/luat_easylvgl_binding.h"
+#include "lvgl9/src/widgets/msgbox/lv_msgbox.h"
+#include "lvgl9/src/misc/lv_timer.h"
+#include "lvgl9/src/misc/lv_event.h"
+
+#define LUAT_LOG_TAG "easylvgl.msgbox"
+#include "luat_log.h"
+
+#define EASYLVGL_MSGBOX_MT "easylvgl.msgbox"
+
+/**
+ * 清理 msgbox Lua 侧关联数据
+ * @param ud 组件用户数据
+ */
+static void easylvgl_msgbox_lua_cleanup(easylvgl_component_ud_t *ud)
+{
+    if (ud == NULL || ud->obj == NULL) {
+        return;
+    }
+
+    easylvgl_component_meta_t *meta = easylvgl_component_meta_get(ud->obj);
+    if (meta != NULL) {
+        lv_timer_t *timer = easylvgl_msgbox_release_user_data(meta);
+        if (timer != NULL) {
+            lv_timer_delete(timer);
+        }
+        easylvgl_component_meta_free(meta);
+    }
+
+    lv_msgbox_close(ud->obj);
+    ud->obj = NULL;
+}
+
+/**
+ * 创建 Msgbox 组件
+ * @api easylvgl.msgbox(config)
+ * @table config 配置表
+ * @string config.title 标题文本,可选
+ * @string config.text 内容文本,可选
+ * @boolean config.auto_center 是否自动居中,默认 true
+ * @int config.timeout 自动关闭时间(毫秒),默认 0
+ * @table config.buttons 按钮标签数组,默认 ["OK"]
+ * @function config.on_action 按钮点击回调
+ * @userdata config.parent 父对象,可选
+ * @return userdata Msgbox 对象,失败返回 nil
+ */
+static int l_easylvgl_msgbox(lua_State *L)
+{
+    easylvgl_ctx_t *ctx = NULL;
+    lua_getfield(L, LUA_REGISTRYINDEX, "easylvgl_ctx");
+    if (lua_type(L, -1) == LUA_TLIGHTUSERDATA) {
+        ctx = (easylvgl_ctx_t *)lua_touserdata(L, -1);
+    }
+    lua_pop(L, 1);
+
+    if (ctx == NULL) {
+        luaL_error(L, "easylvgl not initialized, call easylvgl.init() first");
+        return 0;
+    }
+
+    luaL_checktype(L, 1, LUA_TTABLE);
+    lv_obj_t *msgbox = easylvgl_msgbox_create_from_config(L, 1);
+    if (msgbox == NULL) {
+        lua_pushnil(L);
+        return 1;
+    }
+
+    easylvgl_push_component_userdata(L, msgbox, EASYLVGL_MSGBOX_MT);
+    return 1;
+}
+
+/**
+ * Msgbox:show()
+ * @api msgbox:show()
+ * @return nil
+ */
+static int l_msgbox_show(lua_State *L)
+{
+    lv_obj_t *msgbox = easylvgl_check_component(L, 1, EASYLVGL_MSGBOX_MT);
+    easylvgl_msgbox_show(msgbox);
+    return 0;
+}
+
+/**
+ * Msgbox:hide()
+ * @api msgbox:hide()
+ * @return nil
+ */
+static int l_msgbox_hide(lua_State *L)
+{
+    lv_obj_t *msgbox = easylvgl_check_component(L, 1, EASYLVGL_MSGBOX_MT);
+    easylvgl_msgbox_hide(msgbox);
+    return 0;
+}
+
+/**
+ * Msgbox:set_on_action(callback)
+ * @api msgbox:set_on_action(callback)
+ * @function callback 操作回调(按键 ID 传参)
+ * @return nil
+ */
+static int l_msgbox_set_on_action(lua_State *L)
+{
+    lv_obj_t *msgbox = easylvgl_check_component(L, 1, EASYLVGL_MSGBOX_MT);
+    luaL_checktype(L, 2, LUA_TFUNCTION);
+    lua_pushvalue(L, 2);
+    int ref = luaL_ref(L, LUA_REGISTRYINDEX);
+    easylvgl_msgbox_set_on_action(msgbox, ref);
+    return 0;
+}
+
+/**
+ * Msgbox:release()
+ * @api msgbox:release()
+ * @return nil
+ */
+static int l_msgbox_release(lua_State *L)
+{
+    easylvgl_component_ud_t *ud = (easylvgl_component_ud_t *)luaL_checkudata(L, 1, EASYLVGL_MSGBOX_MT);
+    if (ud != NULL && ud->obj != NULL) {
+        easylvgl_msgbox_lua_cleanup(ud);
+    }
+    return 0;
+}
+
+/**
+ * Msgbox GC(Lua 垃圾回收)
+ */
+static int l_msgbox_gc(lua_State *L)
+{
+    easylvgl_component_ud_t *ud = (easylvgl_component_ud_t *)luaL_checkudata(L, 1, EASYLVGL_MSGBOX_MT);
+    easylvgl_msgbox_lua_cleanup(ud);
+    return 0;
+}
+
+/**
+ * 注册 Msgbox 元表
+ * @param L Lua 状态
+ */
+void easylvgl_register_msgbox_meta(lua_State *L)
+{
+    luaL_newmetatable(L, EASYLVGL_MSGBOX_MT);
+
+    lua_pushcfunction(L, l_msgbox_gc);
+    lua_setfield(L, -2, "__gc");
+
+    static const luaL_Reg methods[] = {
+        {"show", l_msgbox_show},
+        {"hide", l_msgbox_hide},
+        {"set_on_action", l_msgbox_set_on_action},
+        {"release", l_msgbox_release},
+        {NULL, NULL}
+    };
+
+    luaL_newlib(L, methods);
+    lua_setfield(L, -2, "__index");
+    lua_pop(L, 1);
+}
+
+/**
+ * Msgbox 创建函数(供主模块注册)
+ */
+int easylvgl_msgbox_create(lua_State *L)
+{
+    return l_easylvgl_msgbox(L);
+}
+

+ 151 - 0
components/easylvgl/binding/luat_lib_easylvgl_switch.c

@@ -0,0 +1,151 @@
+/*
+@module  easylvgl.switch
+@summary EasyLVGL Switch 组件
+@version 0.2.0
+@date    2025.12.12
+@tag     LUAT_USE_EASYLVGL
+*/
+
+#include "luat_base.h"
+#include "luat_log.h"
+#include "lua.h"
+#include "lauxlib.h"
+#include "../inc/luat_easylvgl.h"
+#include "../inc/luat_easylvgl_component.h"
+#include "../inc/luat_easylvgl_binding.h"
+
+#define LUAT_LOG_TAG "easylvgl.switch"
+#include "luat_log.h"
+
+#define EASYLVGL_SWITCH_MT "easylvgl.switch"
+
+/**
+ * 创建 Switch 组件
+ * @api easylvgl.switch(config)
+ * @table config 配置表
+ * @int config.x X 坐标,默认 0
+ * @int config.y Y 坐标,默认 0
+ * @int config.w 宽度,默认 70
+ * @int config.h 高度,默认 40
+ * @boolean config.checked 初始状态,默认 false
+ * @string config.style 预设样式,如 "danger"/"success"
+ * @function config.on_change 状态变更回调
+ * @userdata config.parent 父对象,可选
+ * @return userdata Switch 对象,失败返回 nil
+ */
+static int l_easylvgl_switch(lua_State *L)
+{
+    easylvgl_ctx_t *ctx = NULL;
+    lua_getfield(L, LUA_REGISTRYINDEX, "easylvgl_ctx");
+    if (lua_type(L, -1) == LUA_TLIGHTUSERDATA) {
+        ctx = (easylvgl_ctx_t *)lua_touserdata(L, -1);
+    }
+    lua_pop(L, 1);
+
+    if (ctx == NULL) {
+        luaL_error(L, "easylvgl not initialized, call easylvgl.init() first");
+        return 0;
+    }
+
+    luaL_checktype(L, 1, LUA_TTABLE);
+    lv_obj_t *sw = easylvgl_switch_create_from_config(L, 1);
+    if (sw == NULL) {
+        lua_pushnil(L);
+        return 1;
+    }
+
+    easylvgl_push_component_userdata(L, sw, EASYLVGL_SWITCH_MT);
+    return 1;
+}
+
+/**
+ * Switch:set_state(state)
+ * @api switch:set_state(state)
+ * @boolean state 勾选状态
+ * @return nil
+ */
+static int l_switch_set_state(lua_State *L)
+{
+    lv_obj_t *sw = easylvgl_check_component(L, 1, EASYLVGL_SWITCH_MT);
+    bool checked = lua_toboolean(L, 2);
+    easylvgl_switch_set_state(sw, checked);
+    return 0;
+}
+
+/**
+ * Switch:get_state()
+ * @api switch:get_state()
+ * @return boolean 当前状态
+ */
+static int l_switch_get_state(lua_State *L)
+{
+    lv_obj_t *sw = easylvgl_check_component(L, 1, EASYLVGL_SWITCH_MT);
+    bool checked = easylvgl_switch_get_state(sw);
+    lua_pushboolean(L, checked);
+    return 1;
+}
+
+/**
+ * Switch:set_on_change(callback)
+ * @api switch:set_on_change(callback)
+ * @function callback 状态变化回调
+ * @return nil
+ */
+static int l_switch_set_on_change(lua_State *L)
+{
+    lv_obj_t *sw = easylvgl_check_component(L, 1, EASYLVGL_SWITCH_MT);
+    luaL_checktype(L, 2, LUA_TFUNCTION);
+    lua_pushvalue(L, 2);
+    int ref = luaL_ref(L, LUA_REGISTRYINDEX);
+    easylvgl_switch_set_on_change(sw, ref);
+    return 0;
+}
+
+/**
+ * Switch GC(释放 LVGL 对象)
+ */
+static int l_switch_gc(lua_State *L)
+{
+    easylvgl_component_ud_t *ud = (easylvgl_component_ud_t *)luaL_checkudata(L, 1, EASYLVGL_SWITCH_MT);
+    if (ud != NULL && ud->obj != NULL) {
+        easylvgl_component_meta_t *meta = easylvgl_component_meta_get(ud->obj);
+        if (meta != NULL) {
+            easylvgl_component_meta_free(meta);
+        }
+        lv_obj_delete(ud->obj);
+        ud->obj = NULL;
+    }
+    return 0;
+}
+
+/**
+ * 注册 Switch 元表
+ * @param L Lua 状态
+ */
+void easylvgl_register_switch_meta(lua_State *L)
+{
+    luaL_newmetatable(L, EASYLVGL_SWITCH_MT);
+
+    lua_pushcfunction(L, l_switch_gc);
+    lua_setfield(L, -2, "__gc");
+
+    static const luaL_Reg methods[] = {
+        {"set_state", l_switch_set_state},
+        {"get_state", l_switch_get_state},
+        {"set_on_change", l_switch_set_on_change},
+        {NULL, NULL}
+    };
+
+    luaL_newlib(L, methods);
+    lua_setfield(L, -2, "__index");
+    lua_pop(L, 1);
+}
+
+/**
+ * Switch 创建函数(供主模块注册)
+ */
+int easylvgl_switch_create(lua_State *L)
+{
+    return l_easylvgl_switch(L);
+}
+

+ 92 - 0
components/easylvgl/binding/luat_lib_easylvgl_table.c

@@ -0,0 +1,92 @@
+/*
+@module  easylvgl.table
+@summary EasyLVGL Table 组件
+@version 0.1.0
+@date    2025.12.26
+@tag     LUAT_USE_EASYLVGL
+*/
+
+#include "luat_base.h"
+#include "lua.h"
+#include "lauxlib.h"
+#include "../inc/luat_easylvgl.h"
+#include "../inc/luat_easylvgl_component.h"
+#include "../inc/luat_easylvgl_binding.h"
+
+#define EASYLVGL_TABLE_MT "easylvgl.table"
+
+static int l_easylvgl_table(lua_State *L) {
+    easylvgl_ctx_t *ctx = NULL;
+    lua_getfield(L, LUA_REGISTRYINDEX, "easylvgl_ctx");
+    if (lua_type(L, -1) == LUA_TLIGHTUSERDATA) {
+        ctx = (easylvgl_ctx_t *)lua_touserdata(L, -1);
+    }
+    lua_pop(L, 1);
+
+    if (ctx == NULL) {
+        luaL_error(L, "easylvgl not initialized, call easylvgl.init() first");
+        return 0;
+    }
+
+    luaL_checktype(L, 1, LUA_TTABLE);
+
+    lv_obj_t *table = easylvgl_table_create_from_config(L, 1);
+    if (table == NULL) {
+        lua_pushnil(L);
+        return 1;
+    }
+
+    easylvgl_push_component_userdata(L, table, EASYLVGL_TABLE_MT);
+    return 1;
+}
+
+static int l_table_set_cell_text(lua_State *L) {
+    lv_obj_t *table = easylvgl_check_component(L, 1, EASYLVGL_TABLE_MT);
+    int row = luaL_checkinteger(L, 2);
+    int col = luaL_checkinteger(L, 3);
+    const char *text = luaL_optstring(L, 4, "");
+    easylvgl_table_set_cell_text(table, row, col, text);
+    return 0;
+}
+
+static int l_table_set_col_width(lua_State *L) {
+    lv_obj_t *table = easylvgl_check_component(L, 1, EASYLVGL_TABLE_MT);
+    int col = luaL_checkinteger(L, 2);
+    int width = luaL_checkinteger(L, 3);
+    easylvgl_table_set_col_width(table, col, width);
+    return 0;
+}
+
+static int l_table_gc(lua_State *L) {
+    easylvgl_component_ud_t *ud = (easylvgl_component_ud_t *)luaL_checkudata(L, 1, EASYLVGL_TABLE_MT);
+    if (ud != NULL && ud->obj != NULL) {
+        easylvgl_component_meta_t *meta = easylvgl_component_meta_get(ud->obj);
+        if (meta != NULL) {
+            easylvgl_component_meta_free(meta);
+        }
+        lv_obj_delete(ud->obj);
+        ud->obj = NULL;
+    }
+    return 0;
+}
+
+void easylvgl_register_table_meta(lua_State *L) {
+    luaL_newmetatable(L, EASYLVGL_TABLE_MT);
+    lua_pushcfunction(L, l_table_gc);
+    lua_setfield(L, -2, "__gc");
+
+    static const luaL_Reg methods[] = {
+        {"set_cell_text", l_table_set_cell_text},
+        {"set_col_width", l_table_set_col_width},
+        {NULL, NULL}
+    };
+
+    luaL_newlib(L, methods);
+    lua_setfield(L, -2, "__index");
+    lua_pop(L, 1);
+}
+
+int easylvgl_table_create(lua_State *L) {
+    return l_easylvgl_table(L);
+}
+

+ 111 - 0
components/easylvgl/binding/luat_lib_easylvgl_tabview.c

@@ -0,0 +1,111 @@
+/*
+@module  easylvgl.tabview
+@summary EasyLVGL TabView 组件
+@version 0.1.0
+@date    2025.12.26
+@tag     LUAT_USE_EASYLVGL
+*/
+
+#include "luat_base.h"
+#include "lua.h"
+#include "lauxlib.h"
+#include "../inc/luat_easylvgl.h"
+#include "../inc/luat_easylvgl_component.h"
+#include "../inc/luat_easylvgl_binding.h"
+
+#define EASYLVGL_TABVIEW_MT "easylvgl.tabview"
+
+static int l_easylvgl_tabview(lua_State *L) {
+    easylvgl_ctx_t *ctx = NULL;
+    lua_getfield(L, LUA_REGISTRYINDEX, "easylvgl_ctx");
+    if (lua_type(L, -1) == LUA_TLIGHTUSERDATA) {
+        ctx = (easylvgl_ctx_t *)lua_touserdata(L, -1);
+    }
+    lua_pop(L, 1);
+
+    if (ctx == NULL) {
+        luaL_error(L, "easylvgl not initialized, call easylvgl.init() first");
+        return 0;
+    }
+
+    luaL_checktype(L, 1, LUA_TTABLE);
+
+    lv_obj_t *tabview = easylvgl_tabview_create_from_config(L, 1);
+    if (tabview == NULL) {
+        lua_pushnil(L);
+        return 1;
+    }
+
+    easylvgl_push_component_userdata(L, tabview, EASYLVGL_TABVIEW_MT);
+    return 1;
+}
+
+static int l_tabview_set_active(lua_State *L) {
+    lv_obj_t *tabview = easylvgl_check_component(L, 1, EASYLVGL_TABVIEW_MT);
+    int index = luaL_checkinteger(L, 2);
+    easylvgl_tabview_set_active(tabview, index);
+    return 0;
+}
+
+static int l_tabview_get_content(lua_State *L) {
+    lv_obj_t *tabview = easylvgl_check_component(L, 1, EASYLVGL_TABVIEW_MT);
+    int index = luaL_checkinteger(L, 2);
+    lv_obj_t *page = easylvgl_tabview_get_content(tabview, index);
+    if (page == NULL) {
+        lua_pushnil(L);
+        return 1;
+    }
+    easylvgl_component_ud_t *ud = (easylvgl_component_ud_t *)lua_newuserdata(L, sizeof(easylvgl_component_ud_t));
+    ud->obj = page;
+    return 1;
+}
+
+static int l_tabview_set_on_change(lua_State *L) {
+    lv_obj_t *tabview = easylvgl_check_component(L, 1, EASYLVGL_TABVIEW_MT);
+    luaL_checktype(L, 2, LUA_TFUNCTION);
+    lua_pushvalue(L, 2);
+    int ref = luaL_ref(L, LUA_REGISTRYINDEX);
+    easylvgl_component_meta_t *meta = easylvgl_component_meta_get(tabview);
+    if (meta != NULL) {
+        easylvgl_component_bind_event(meta, EASYLVGL_EVENT_VALUE_CHANGED, ref);
+    } else {
+        luaL_unref(L, LUA_REGISTRYINDEX, ref);
+    }
+    return 0;
+}
+
+static int l_tabview_gc(lua_State *L) {
+    easylvgl_component_ud_t *ud = (easylvgl_component_ud_t *)luaL_checkudata(L, 1, EASYLVGL_TABVIEW_MT);
+    if (ud != NULL && ud->obj != NULL) {
+        easylvgl_component_meta_t *meta = easylvgl_component_meta_get(ud->obj);
+        if (meta != NULL) {
+            easylvgl_tabview_release_data(meta);
+            easylvgl_component_meta_free(meta);
+        }
+        lv_obj_delete(ud->obj);
+        ud->obj = NULL;
+    }
+    return 0;
+}
+
+void easylvgl_register_tabview_meta(lua_State *L) {
+    luaL_newmetatable(L, EASYLVGL_TABVIEW_MT);
+    lua_pushcfunction(L, l_tabview_gc);
+    lua_setfield(L, -2, "__gc");
+
+    static const luaL_Reg methods[] = {
+        {"set_active", l_tabview_set_active},
+        {"get_content", l_tabview_get_content},
+        {"set_on_change", l_tabview_set_on_change},
+        {NULL, NULL}
+    };
+
+    luaL_newlib(L, methods);
+    lua_setfield(L, -2, "__index");
+    lua_pop(L, 1);
+}
+
+int easylvgl_tabview_create(lua_State *L) {
+    return l_easylvgl_tabview(L);
+}
+

+ 198 - 0
components/easylvgl/binding/luat_lib_easylvgl_textarea.c

@@ -0,0 +1,198 @@
+/*
+@module  easylvgl.textarea
+@summary EasyLVGL Textarea 组件 Lua 绑定
+@version 0.3.0
+@date    2025.12.15
+@tag     LUAT_USE_EASYLVGL
+*/
+
+#include "luat_base.h"
+#include "luat_log.h"
+#include "lua.h"
+#include "lauxlib.h"
+#include "../inc/luat_easylvgl.h"
+#include "../inc/luat_easylvgl_component.h"
+#include "../inc/luat_easylvgl_binding.h"
+#include "luat_malloc.h"
+
+#define LUAT_LOG_TAG "easylvgl.textarea"
+#include "luat_log.h"
+
+/************************************************************************
+ * Lua 接口定义
+ ************************************************************************/
+
+/**
+ * 创建 Textarea 组件
+ * @api easylvgl.textarea(config)
+ * @table config 配置表
+ * @int config.x X 坐标,默认 0
+ * @int config.y Y 坐标,默认 0
+ * @int config.w 宽度,默认 ctx->width - x 或 200
+ * @int config.h 高度,默认 120
+ * @int config.max_len 最大字符数,默认 256
+ * @string config.text 初始文本
+ * @string config.placeholder 占位提示
+ * @function config.on_text_change 文本变更回调
+ * @table config.keyboard 内嵌 Keyboard 配置(table)
+ * @userdata config.parent 父对象,可选
+ * @return userdata Textarea 对象,失败返回 nil
+ */
+static int l_easylvgl_textarea(lua_State *L) {
+    easylvgl_ctx_t *ctx = NULL;
+    lua_getfield(L, LUA_REGISTRYINDEX, "easylvgl_ctx");
+    if (lua_type(L, -1) == LUA_TLIGHTUSERDATA) {
+        ctx = (easylvgl_ctx_t *)lua_touserdata(L, -1);
+    }
+    lua_pop(L, 1);
+
+    if (ctx == NULL) {
+        luaL_error(L, "easylvgl not initialized, call easylvgl.init() first");
+        return 0;
+    }
+
+    luaL_checktype(L, 1, LUA_TTABLE);
+    lv_obj_t *textarea = easylvgl_textarea_create_from_config(L, 1);
+    if (textarea == NULL) {
+        lua_pushnil(L);
+        return 1;
+    }
+
+    easylvgl_push_component_userdata(L, textarea, EASYLVGL_TEXTAREA_MT);
+    return 1;
+}
+
+/**
+ * Textarea:set_text(text)
+ * @api textarea:set_text(text)
+ * @string text 文本内容
+ * @return nil
+ */
+static int l_textarea_set_text(lua_State *L) {
+    lv_obj_t *textarea = easylvgl_check_component(L, 1, EASYLVGL_TEXTAREA_MT);
+    const char *text = luaL_checkstring(L, 2);
+    easylvgl_textarea_set_text(textarea, text);
+    return 0;
+}
+
+/**
+ * Textarea:get_text()
+ * @api textarea:get_text()
+ * @return string 当前文本
+ */
+static int l_textarea_get_text(lua_State *L) {
+    lv_obj_t *textarea = easylvgl_check_component(L, 1, EASYLVGL_TEXTAREA_MT);
+    const char *text = easylvgl_textarea_get_text(textarea);
+    lua_pushstring(L, text ? text : "");
+    return 1;
+}
+
+/**
+ * Textarea:set_cursor(pos)
+ * @api textarea:set_cursor(pos)
+ * @int pos 光标位置
+ * @return nil
+ */
+static int l_textarea_set_cursor(lua_State *L) {
+    lv_obj_t *textarea = easylvgl_check_component(L, 1, EASYLVGL_TEXTAREA_MT);
+    uint32_t pos = (uint32_t)luaL_checkinteger(L, 2);
+    easylvgl_textarea_set_cursor(textarea, pos);
+    return 0;
+}
+
+/**
+ * Textarea:set_on_text_change(callback)
+ * @api textarea:set_on_text_change(callback)
+ * @function callback 文本变化回调
+ * @return nil
+ */
+static int l_textarea_set_on_change(lua_State *L) {
+    lv_obj_t *textarea = easylvgl_check_component(L, 1, EASYLVGL_TEXTAREA_MT);
+    luaL_checktype(L, 2, LUA_TFUNCTION);
+    lua_pushvalue(L, 2);
+    int ref = luaL_ref(L, LUA_REGISTRYINDEX);
+    easylvgl_textarea_set_on_text_change(textarea, ref);
+    return 0;
+}
+
+/**
+ * Textarea:attach_keyboard(keyboard)
+ * @api textarea:attach_keyboard(keyboard)
+ * @userdata keyboard Keyboard 对象
+ * @return nil
+ */
+static int l_textarea_attach_keyboard(lua_State *L) {
+    lv_obj_t *textarea = easylvgl_check_component(L, 1, EASYLVGL_TEXTAREA_MT);
+    lv_obj_t *keyboard = easylvgl_check_component(L, 2, EASYLVGL_KEYBOARD_MT);
+    easylvgl_textarea_attach_keyboard(textarea, keyboard);
+    return 0;
+}
+
+/**
+ * Textarea:get_keyboard()
+ * @api textarea:get_keyboard()
+ * @return userdata|null 当前绑定的 Keyboard
+ */
+static int l_textarea_get_keyboard(lua_State *L) {
+    lv_obj_t *textarea = easylvgl_check_component(L, 1, EASYLVGL_TEXTAREA_MT);
+    lv_obj_t *keyboard = easylvgl_textarea_get_keyboard(textarea);
+    if (keyboard == NULL) {
+        lua_pushnil(L);
+        return 1;
+    }
+    easylvgl_push_component_userdata(L, keyboard, EASYLVGL_KEYBOARD_MT);
+    return 1;
+}
+
+/**
+ * Textarea GC(销毁组件)
+ */
+static int l_textarea_gc(lua_State *L) {
+    easylvgl_component_ud_t *ud = (easylvgl_component_ud_t *)luaL_checkudata(L, 1, EASYLVGL_TEXTAREA_MT);
+    if (ud != NULL && ud->obj != NULL) {
+        easylvgl_component_meta_t *meta = easylvgl_component_meta_get(ud->obj);
+        if (meta != NULL) {
+            if (meta->user_data != NULL) {
+                luat_heap_free(meta->user_data);
+                meta->user_data = NULL;
+            }
+            easylvgl_component_meta_free(meta);
+        }
+        lv_obj_delete(ud->obj);
+        ud->obj = NULL;
+    }
+    return 0;
+}
+
+/**
+ * 注册 Textarea 元表
+ * @param L Lua 状态
+ */
+void easylvgl_register_textarea_meta(lua_State *L) {
+    luaL_newmetatable(L, EASYLVGL_TEXTAREA_MT);
+
+    lua_pushcfunction(L, l_textarea_gc);
+    lua_setfield(L, -2, "__gc");
+
+    static const luaL_Reg functions[] = {
+        {"set_text", l_textarea_set_text},
+        {"get_text", l_textarea_get_text},
+        {"set_cursor", l_textarea_set_cursor},
+        {"set_on_text_change", l_textarea_set_on_change},
+        {"attach_keyboard", l_textarea_attach_keyboard},
+        {"get_keyboard", l_textarea_get_keyboard},
+        {NULL, NULL}
+    };
+
+    luaL_newlib(L, functions);
+    lua_setfield(L, -2, "__index");
+    lua_pop(L, 1);
+}
+
+/**
+ * Textarea 创建函数(供主模块注册)
+ */
+int easylvgl_textarea_create(lua_State *L) {
+    return l_easylvgl_textarea(L);
+}
+

+ 197 - 0
components/easylvgl/binding/luat_lib_easylvgl_win.c

@@ -0,0 +1,197 @@
+/*
+@module  easylvgl.win
+@summary EasyLVGL Win 组件 Lua 绑定
+@version 0.1.0
+@date    2025.12.02
+@tag     LUAT_USE_EASYLVGL
+*/
+
+#include "luat_base.h"
+#include "luat_log.h"
+#include "lua.h"
+#include "lauxlib.h"
+#include "../inc/luat_easylvgl.h"
+#include "../inc/luat_easylvgl_component.h"
+#include "../inc/luat_easylvgl_binding.h"
+#include "luat_malloc.h"
+#include <string.h>
+
+#define LUAT_LOG_TAG "easylvgl.win"
+#include "luat_log.h"
+
+// 元表名称
+#define EASYLVGL_WIN_MT "easylvgl.win"
+
+/**
+ * 创建 Win 组件
+ * @api easylvgl.win(config)
+ * @table config 配置表
+ * @int config.x X 坐标,默认 0
+ * @int config.y Y 坐标,默认 0
+ * @int config.w 宽度,默认 400
+ * @int config.h 高度,默认 300
+ * @string config.title 标题文本,可选
+ * @bool config.close_btn 是否显示关闭按钮,默认 false
+ * @bool config.auto_center 是否自动居中,默认 false
+ * @table config.style 样式配置,可选
+ * @int config.style.radius 圆角半径
+ * @int config.style.pad 内边距
+ * @int config.style.border_width 边框宽度
+ * @function config.on_close 关闭回调函数,可选
+ * @userdata config.parent 父对象,可选,默认当前屏幕
+ * @return userdata Win 对象
+ */
+static int l_easylvgl_win(lua_State *L) {
+    // 检查上下文是否已初始化(从注册表获取)
+    easylvgl_ctx_t *ctx = NULL;
+    lua_getfield(L, LUA_REGISTRYINDEX, "easylvgl_ctx");
+    if (lua_type(L, -1) == LUA_TLIGHTUSERDATA) {
+        ctx = (easylvgl_ctx_t *)lua_touserdata(L, -1);
+    }
+    lua_pop(L, 1);
+    
+    if (ctx == NULL) {
+        luaL_error(L, "easylvgl not initialized, call easylvgl.init() first");
+        return 0;
+    }
+    
+    luaL_checktype(L, 1, LUA_TTABLE);
+    
+    lv_obj_t *win = easylvgl_win_create_from_config(L, 1);
+    if (win == NULL) {
+        lua_pushnil(L);
+        return 1;
+    }
+    
+    easylvgl_push_component_userdata(L, win, EASYLVGL_WIN_MT);
+    return 1;
+}
+
+/**
+ * Win:set_title(title)
+ * @api win:set_title(title)
+ * @string title 标题文本
+ * @return nil
+ */
+static int l_win_set_title(lua_State *L) {
+    lv_obj_t *win = easylvgl_check_component(L, 1, EASYLVGL_WIN_MT);
+    const char *title = luaL_checkstring(L, 2);
+    easylvgl_win_set_title(win, title);
+    return 0;
+}
+
+/**
+ * Win:add_content(child)
+ * @api win:add_content(child)
+ * @userdata child 子组件对象
+ * @return nil
+ */
+static int l_win_add_content(lua_State *L) {
+    lv_obj_t *win = easylvgl_check_component(L, 1, EASYLVGL_WIN_MT);
+    
+    // 检查第二个参数是否是 userdata
+    if (lua_type(L, 2) != LUA_TUSERDATA) {
+        luaL_error(L, "expected userdata as child object");
+        return 0;
+    }
+    
+    // 安全地从 userdata 中提取对象指针
+    // 所有组件都使用相同的 easylvgl_component_ud_t 结构
+    easylvgl_component_ud_t *child_ud = (easylvgl_component_ud_t *)lua_touserdata(L, 2);
+    if (child_ud == NULL) {
+        luaL_error(L, "invalid userdata");
+        return 0;
+    }
+    
+    // 验证对象指针是否有效
+    if (child_ud->obj == NULL) {
+        luaL_error(L, "child object is NULL");
+        return 0;
+    }
+    
+    easylvgl_win_add_content(win, child_ud->obj);
+    return 0;
+}
+
+/**
+ * Win:close()
+ * @api win:close()
+ * @return nil
+ */
+static int l_win_close(lua_State *L) {
+    easylvgl_component_ud_t *ud = (easylvgl_component_ud_t *)luaL_checkudata(L, 1, EASYLVGL_WIN_MT);
+    if (ud != NULL && ud->obj != NULL) {
+        // 获取元数据并释放 Win 私有数据
+        easylvgl_component_meta_t *meta = easylvgl_component_meta_get(ud->obj);
+        if (meta != NULL) {
+            // 释放 Win 私有数据
+            if (meta->user_data != NULL) {
+                luat_heap_free(meta->user_data);
+                meta->user_data = NULL;
+            }
+        }
+        
+        // 删除窗口对象(会触发 on_close 回调)
+        lv_obj_delete(ud->obj);
+        ud->obj = NULL;
+    }
+    return 0;
+}
+
+/**
+ * Win GC(垃圾回收)
+ */
+static int l_win_gc(lua_State *L) {
+    easylvgl_component_ud_t *ud = (easylvgl_component_ud_t *)luaL_checkudata(L, 1, EASYLVGL_WIN_MT);
+    if (ud != NULL && ud->obj != NULL) {
+        // 获取元数据并释放
+        easylvgl_component_meta_t *meta = easylvgl_component_meta_get(ud->obj);
+        if (meta != NULL) {
+            // 释放 Win 私有数据
+            if (meta->user_data != NULL) {
+                luat_heap_free(meta->user_data);
+                meta->user_data = NULL;
+            }
+            
+            easylvgl_component_meta_free(meta);
+        }
+        
+        // 删除 LVGL 对象
+        lv_obj_delete(ud->obj);
+        ud->obj = NULL;
+    }
+    return 0;
+}
+
+/**
+ * 注册 Win 元表
+ * @param L Lua 状态
+ */
+void easylvgl_register_win_meta(lua_State *L) {
+    luaL_newmetatable(L, EASYLVGL_WIN_MT);
+    
+    // 设置元方法
+    lua_pushcfunction(L, l_win_gc);
+    lua_setfield(L, -2, "__gc");
+    
+    // 设置方法表
+    static const luaL_Reg methods[] = {
+        {"set_title", l_win_set_title},
+        {"add_content", l_win_add_content},
+        {"close", l_win_close},
+        {NULL, NULL}
+    };
+    
+    luaL_newlib(L, methods);
+    lua_setfield(L, -2, "__index");
+    
+    lua_pop(L, 1);
+}
+
+/**
+ * Win 创建函数(供主模块注册)
+ */
+int easylvgl_win_create(lua_State *L) {
+    return l_easylvgl_win(L);
+}
+

+ 281 - 0
components/easylvgl/inc/luat_easylvgl.h

@@ -0,0 +1,281 @@
+/**
+ * @file luat_easylvgl.h
+ * @summary EasyLVGL 主头文件:上下文、平台操作接口、错误码
+ * @responsible EasyLVGL 核心数据结构定义与平台抽象接口
+ */
+
+#ifndef EASYLVGL_H
+#define EASYLVGL_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include <stdint.h>
+#include <stdbool.h>
+#include <stddef.h>
+#include "lvgl9/lvgl.h"
+
+/*********************
+ *      DEFINES
+ *********************/
+
+/** 回调函数类型最大数量 */
+#define EASYLVGL_CALLBACK_MAX 16
+
+/** 颜色格式常量(用于 Lua API,直接使用 LVGL 常量值) */
+#define EASYLVGL_COLOR_FORMAT_RGB565    LV_COLOR_FORMAT_RGB565     /**< RGB565 格式,16位,适用于嵌入式设备 */
+#define EASYLVGL_COLOR_FORMAT_ARGB8888  LV_COLOR_FORMAT_ARGB8888   /**< ARGB8888 格式,32位,默认格式 */
+
+/**********************
+ *      TYPEDEFS
+ *********************/
+
+/** 前向声明 */
+typedef struct easylvgl_ctx easylvgl_ctx_t;
+typedef struct easylvgl_buffer easylvgl_buffer_t;
+typedef struct easylvgl_component_meta easylvgl_component_meta_t;
+
+/**
+ * 缓冲模式
+ */
+typedef enum {
+    EASYLVGL_BUFFER_MODE_SINGLE,        /**< 单缓冲 */
+    EASYLVGL_BUFFER_MODE_DOUBLE,        /**< 双缓冲 */
+    EASYLVGL_BUFFER_MODE_LCD_SHARED,    /**< LCD 共享缓冲 */
+    EASYLVGL_BUFFER_MODE_EXTERNAL       /**< 外部缓冲 */
+} easylvgl_buffer_mode_t;
+
+/**
+ * 缓冲所有权
+ */
+typedef enum {
+    EASYLVGL_BUFFER_OWNER_SYSTEM,       /**< 系统 heap */
+    EASYLVGL_BUFFER_OWNER_LUA           /**< Lua heap */
+} easylvgl_buffer_owner_t;
+
+/**
+ * 错误码
+ */
+typedef enum {
+    EASYLVGL_OK = 0,
+    EASYLVGL_ERR_INVALID_PARAM = -1,
+    EASYLVGL_ERR_NO_MEM = -2,
+    EASYLVGL_ERR_INIT_FAILED = -3,
+    EASYLVGL_ERR_NOT_INITIALIZED = -4,
+    EASYLVGL_ERR_PLATFORM_ERROR = -5
+} easylvgl_err_t;
+
+/**
+ * 显示驱动操作接口
+ */
+typedef struct {
+    int (*init)(easylvgl_ctx_t *ctx, uint16_t w, uint16_t h, lv_color_format_t fmt);
+    void (*flush)(easylvgl_ctx_t *ctx, const lv_area_t *area, const uint8_t *px_map);
+    void (*wait_vsync)(easylvgl_ctx_t *ctx);
+    void (*deinit)(easylvgl_ctx_t *ctx);
+} easylvgl_display_ops_t;
+
+/**
+ * 文件系统操作接口
+ */
+typedef struct {
+    void *(*open)(easylvgl_ctx_t *ctx, const char *path, lv_fs_mode_t mode);
+    lv_fs_res_t (*read)(easylvgl_ctx_t *ctx, void *file, void *buf, uint32_t len, uint32_t *read);
+    lv_fs_res_t (*write)(easylvgl_ctx_t *ctx, void *file, const void *buf, uint32_t len, uint32_t *written);
+    lv_fs_res_t (*seek)(easylvgl_ctx_t *ctx, void *file, uint32_t pos, lv_fs_whence_t whence);
+    lv_fs_res_t (*tell)(easylvgl_ctx_t *ctx, void *file, uint32_t *pos);
+    lv_fs_res_t (*close)(easylvgl_ctx_t *ctx, void *file);
+} easylvgl_fs_ops_t;
+
+/**
+ * 输入设备操作接口
+ */
+typedef struct {
+    bool (*read_pointer)(easylvgl_ctx_t *ctx, lv_indev_data_t *data);
+    bool (*read_keypad)(easylvgl_ctx_t *ctx, lv_indev_data_t *data);
+    void (*calibration)(easylvgl_ctx_t *ctx, int16_t *x, int16_t *y);
+} easylvgl_input_ops_t;
+
+/**
+ * 时基操作接口
+ */
+typedef struct {
+    uint32_t (*get_tick)(easylvgl_ctx_t *ctx);
+    void (*delay_ms)(easylvgl_ctx_t *ctx, uint32_t ms);
+} easylvgl_time_ops_t;
+
+/**
+ * 日志操作接口(可选)
+ */
+typedef struct {
+    void (*log)(easylvgl_ctx_t *ctx, lv_log_level_t level, const char *fmt, ...);
+} easylvgl_log_ops_t;
+
+/**
+ * 平台操作接口集合
+ */
+typedef struct {
+    const easylvgl_display_ops_t *display_ops;
+    const easylvgl_fs_ops_t *fs_ops;
+    const easylvgl_input_ops_t *input_ops;
+    const easylvgl_time_ops_t *time_ops;
+    const easylvgl_log_ops_t *log_ops;  /**< 可选 */
+} easylvgl_platform_ops_t;
+
+/**
+ * EasyLVGL 上下文对象
+ * 统一管理所有运行态数据,替代全局变量
+ */
+struct easylvgl_ctx {
+    // LVGL 驱动实例
+    lv_display_t *display;          /**< 显示设备 */
+    lv_indev_t *indev;               /**< 输入设备 */
+    lv_fs_drv_t fs_drv[2];          /**< 文件系统驱动(L:/ 和 /) */
+    
+    // 缓冲管理
+    easylvgl_buffer_t *buffer;       /**< 缓冲管理器 */
+    
+    // 平台操作接口
+    const easylvgl_platform_ops_t *ops;  /**< 平台驱动 ops */
+    
+    // Lua 状态
+    void *L;                         /**< Lua 状态指针(lua_State*,避免直接依赖) */
+    
+    // 配置标志
+    uint8_t flags;                   /**< 配置标志位 */
+    uint16_t width;                 /**< 屏幕宽度 */
+    uint16_t height;                 /**< 屏幕高度 */
+    
+    // 内部状态
+    lv_timer_t *tick_timer;          /**< Tick 定时器(可选) */
+    void *platform_data;             /**< 平台私有数据 */
+    lv_obj_t *focused_textarea;      /**< 当前聚焦的 textarea,供系统键盘使用 */
+    bool system_keyboard_enabled;    /**< 是否允许系统键盘输入 */
+    int32_t system_keyboard_preedit_pos; /**< 上一次插入的 SDL 预编辑文本起始位置 */
+    int32_t system_keyboard_preedit_len; /**< 上一次插入的 SDL 预编辑文本长度(字符数) */
+    bool system_keyboard_preedit_active; /**< 当前是否处于 SDL 预编辑(拼音)阶段 */
+};
+
+/**********************
+ * GLOBAL PROTOTYPES
+ **********************/
+
+/**
+ * 创建 EasyLVGL 上下文对象
+ * @param ctx 上下文指针(输出)
+ * @param ops 平台操作接口
+ * @return 0 成功,<0 失败
+ * @pre-condition ops 必须非空
+ * @post-condition ctx 已初始化,可调用 easylvgl_init
+ */
+int easylvgl_ctx_create(easylvgl_ctx_t *ctx, const easylvgl_platform_ops_t *ops);
+
+/**
+ * 初始化 EasyLVGL
+ * @param ctx 上下文指针
+ * @param width 屏幕宽度
+ * @param height 屏幕高度
+ * @param color_format 颜色格式
+ * @return 0 成功,<0 失败
+ * @pre-condition ctx 已通过 easylvgl_ctx_create 创建
+ * @post-condition LVGL 已初始化,驱动已注册,缓冲已申请
+ */
+int easylvgl_init(easylvgl_ctx_t *ctx, uint16_t width, uint16_t height, lv_color_format_t color_format);
+
+/**
+ * 反初始化 EasyLVGL
+ * @param ctx 上下文指针
+ * @pre-condition ctx 已初始化
+ * @post-condition 所有资源已释放
+ */
+void easylvgl_deinit(easylvgl_ctx_t *ctx);
+
+/**
+ * 错误码转字符串
+ * @param err 错误码
+ * @return 错误描述字符串
+ */
+const char *easylvgl_strerror(easylvgl_err_t err);
+
+/**
+ * 创建缓冲管理器
+ * @return 缓冲管理器指针,失败返回 NULL
+ */
+easylvgl_buffer_t *easylvgl_buffer_create(void);
+
+/**
+ * 分配缓冲
+ * @param ctx 上下文指针
+ * @param size 缓冲大小(字节)
+ * @param owner 缓冲所有权
+ * @return 缓冲指针,失败返回 NULL
+ */
+void *easylvgl_buffer_alloc(easylvgl_ctx_t *ctx, size_t size, easylvgl_buffer_owner_t owner);
+
+/**
+ * 释放所有缓冲
+ * @param ctx 上下文指针
+ * @pre-condition ctx 必须非空
+ * @post-condition 所有缓冲已按所有权释放
+ */
+void easylvgl_buffer_free_all(easylvgl_ctx_t *ctx);
+
+/**
+ * 创建 HZFont(TTF)字体,用于 EasyLVGL
+ * @param path TTF 文件路径,为 NULL 则使用内置字库
+ * @param size 字号
+ * @param cache_size 缓存容量
+ * @param antialias 抗锯齿等级
+ * @return lv_font_t 字体对象,失败返回 NULL
+ */
+lv_font_t * easylvgl_font_hzfont_create(const char * path, uint16_t size, uint32_t cache_size, int antialias);
+
+/**
+ * 设置显示缓冲
+ * @param ctx 上下文指针
+ * @param buf1 缓冲1指针
+ * @param buf2 缓冲2指针(可选,双缓冲时使用)
+ * @param buf_size 缓冲大小(字节)
+ * @param mode 缓冲模式
+ * @return 0 成功,<0 失败
+ */
+int easylvgl_display_set_buffers(
+    easylvgl_ctx_t *ctx,
+    void *buf1,
+    void *buf2,
+    uint32_t buf_size,
+    easylvgl_buffer_mode_t mode);
+
+/**
+ * 初始化文件系统驱动
+ * @param ctx 上下文指针
+ * @return 0 成功,<0 失败
+ * @pre-condition ctx 必须非空且已初始化
+ * @post-condition 文件系统驱动已注册到 LVGL
+ */
+int easylvgl_fs_init(easylvgl_ctx_t *ctx);
+
+/**
+ * 记录当前聚焦的 textarea,用于系统键盘转发
+ */
+void easylvgl_ctx_set_focused_textarea(easylvgl_ctx_t *ctx, lv_obj_t *textarea);
+lv_obj_t *easylvgl_ctx_get_focused_textarea(easylvgl_ctx_t *ctx);
+
+/**
+ * 控制系统键盘输入
+ */
+int easylvgl_system_keyboard_enable(easylvgl_ctx_t *ctx, bool enable);
+bool easylvgl_system_keyboard_is_enabled(easylvgl_ctx_t *ctx);
+void easylvgl_system_keyboard_post_key(easylvgl_ctx_t *ctx, uint32_t key, bool pressed);
+void easylvgl_system_keyboard_post_text(easylvgl_ctx_t *ctx, const char *text);
+void easylvgl_system_keyboard_set_preedit(easylvgl_ctx_t *ctx, const char *text);
+void easylvgl_system_keyboard_clear_preedit(easylvgl_ctx_t *ctx);
+
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* EASYLVGL_H */
+

+ 51 - 0
components/easylvgl/inc/luat_easylvgl_binding.h

@@ -0,0 +1,51 @@
+/**
+ * @file luat_easylvgl_binding.h
+ * @summary EasyLVGL Lua 绑定公共头文件
+ * @responsible 定义通用组件辅助函数和数据结构
+ */
+
+#ifndef LUAT_EASYLVGL_BINDING_H
+#define LUAT_EASYLVGL_BINDING_H
+
+#include "lua.h"
+#include "lauxlib.h"
+#include "lvgl9/src/core/lv_obj.h"
+
+#define EASYLVGL_TEXTAREA_MT "easylvgl.textarea"
+#define EASYLVGL_KEYBOARD_MT "easylvgl.keyboard"
+#define EASYLVGL_CONTAINER_MT "easylvgl.container"
+#define EASYLVGL_TABLE_MT "easylvgl.table"
+#define EASYLVGL_TABVIEW_MT "easylvgl.tabview"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+// 组件 userdata 结构(所有组件共享)
+typedef struct {
+    lv_obj_t *obj;
+} easylvgl_component_ud_t;
+
+/**
+ * 推送组件 userdata 到 Lua 栈
+ * @param L Lua 状态
+ * @param obj LVGL 对象指针
+ * @param mt 元表名称
+ */
+void easylvgl_push_component_userdata(lua_State *L, lv_obj_t *obj, const char *mt);
+
+/**
+ * 检查并获取组件 userdata
+ * @param L Lua 状态
+ * @param index 栈索引
+ * @param mt 元表名称
+ * @return LVGL 对象指针,失败时抛出错误
+ */
+lv_obj_t *easylvgl_check_component(lua_State *L, int index, const char *mt);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* LUAT_EASYLVGL_BINDING_H */
+

+ 339 - 0
components/easylvgl/inc/luat_easylvgl_component.h

@@ -0,0 +1,339 @@
+/**
+ * @file luat_easylvgl_component.h
+ * @summary EasyLVGL 组件基类接口
+ * @responsible 组件元数据、事件绑定、配置表解析
+ */
+
+#ifndef EASYLVGL_COMPONENT_H
+#define EASYLVGL_COMPONENT_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include "luat_easylvgl.h"
+
+/*********************
+ *      DEFINES
+ *********************/
+
+/** 组件类型 */
+typedef enum {
+    EASYLVGL_COMPONENT_BUTTON = 1,
+    EASYLVGL_COMPONENT_LABEL,
+    EASYLVGL_COMPONENT_IMAGE,
+    EASYLVGL_COMPONENT_WIN,
+    EASYLVGL_COMPONENT_DROPDOWN,
+    EASYLVGL_COMPONENT_SWITCH,
+    EASYLVGL_COMPONENT_MSGBOX,
+    EASYLVGL_COMPONENT_CONTAINER,
+    EASYLVGL_COMPONENT_TABLE,
+    EASYLVGL_COMPONENT_TABVIEW,
+    EASYLVGL_COMPONENT_TEXTAREA,
+    EASYLVGL_COMPONENT_KEYBOARD
+} easylvgl_component_type_t;
+
+/** TabView 对齐常量 */
+typedef enum {
+    EASYLVGL_TABVIEW_PAD_ALL = 0,
+    EASYLVGL_TABVIEW_PAD_HOR,
+    EASYLVGL_TABVIEW_PAD_VER,
+    EASYLVGL_TABVIEW_PAD_TOP,
+    EASYLVGL_TABVIEW_PAD_BOTTOM,
+    EASYLVGL_TABVIEW_PAD_LEFT,
+    EASYLVGL_TABVIEW_PAD_RIGHT,
+    EASYLVGL_TABVIEW_PAD_MAX
+} easylvgl_tabview_pad_method_t;
+
+/** 事件类型 */
+typedef enum {
+    EASYLVGL_EVENT_CLICKED = 0,
+    EASYLVGL_EVENT_PRESSED,
+    EASYLVGL_EVENT_RELEASED,
+    EASYLVGL_EVENT_VALUE_CHANGED,
+    EASYLVGL_EVENT_ACTION,
+    EASYLVGL_EVENT_READY,
+    EASYLVGL_EVENT_CLOSE,
+    EASYLVGL_EVENT_MAX
+} easylvgl_event_type_t;
+
+/**********************
+ *      TYPEDEFS
+ *********************/
+
+/**
+ * 组件元数据
+ */
+struct easylvgl_component_meta {
+    lv_obj_t *obj;                      /**< LVGL 对象指针 */
+    easylvgl_ctx_t *ctx;                /**< 上下文引用 */
+    
+    // 回调引用(Lua registry)
+    int callback_refs[EASYLVGL_CALLBACK_MAX];  /**< 事件回调引用数组 */
+    
+    // 组件类型
+    uint8_t component_type;
+    
+    // 私有数据
+    void *user_data;
+};
+
+/**
+ * Msgbox 私有数据
+ */
+typedef struct {
+    lv_timer_t *timeout_timer;
+} easylvgl_msgbox_data_t;
+
+/**
+ * Textarea 私有数据
+ */
+typedef struct {
+    lv_obj_t *keyboard;
+} easylvgl_textarea_data_t;
+
+/**
+ * Keyboard 私有数据
+ */
+typedef struct {
+    lv_obj_t *target;
+} easylvgl_keyboard_data_t;
+
+/**********************
+ * GLOBAL PROTOTYPES
+ **********************/
+
+/**
+ * 分配组件元数据
+ * @param ctx 上下文指针
+ * @param obj LVGL 对象指针
+ * @param component_type 组件类型
+ * @return 元数据指针,失败返回 NULL
+ */
+easylvgl_component_meta_t *easylvgl_component_meta_alloc(
+    easylvgl_ctx_t *ctx,
+    lv_obj_t *obj,
+    easylvgl_component_type_t component_type);
+
+/**
+ * 释放组件元数据
+ * @param meta 元数据指针
+ * @pre-condition meta 必须非空
+ * @post-condition 元数据及关联资源已释放
+ */
+void easylvgl_component_meta_free(easylvgl_component_meta_t *meta);
+
+/**
+ * 捕获配置表中的回调函数
+ * @param L Lua 状态
+ * @param idx 配置表在栈中的索引
+ * @param key 回调函数键名(如 "on_click")
+ * @return Lua registry 引用,未找到返回 LUA_NOREF
+ */
+int easylvgl_component_capture_callback(void *L, int idx, const char *key);
+
+/**
+ * 调用 Lua 回调函数
+ * @param meta 组件元数据
+ * @param event_type 事件类型
+ * @param L Lua 状态
+ * @pre-condition meta 必须非空
+ */
+void easylvgl_component_call_callback(
+    easylvgl_component_meta_t *meta,
+    easylvgl_event_type_t event_type,
+    void *L);
+
+/**
+ * 调用 Msgbox Action 回调(额外传递按钮文本)
+ */
+void easylvgl_component_call_action_callback(
+    easylvgl_component_meta_t *meta,
+    const char *action_text);
+
+/**
+ * 绑定组件事件
+ * @param meta 组件元数据
+ * @param event_type 事件类型
+ * @param callback_ref Lua 回调引用
+ * @return 0 成功,<0 失败
+ */
+int easylvgl_component_bind_event(
+    easylvgl_component_meta_t *meta,
+    easylvgl_event_type_t event_type,
+    int callback_ref);
+
+/**
+ * 释放组件所有回调引用
+ * @param meta 组件元数据
+ * @param L Lua 状态
+ */
+void easylvgl_component_release_callbacks(easylvgl_component_meta_t *meta, void *L);
+
+/**
+ * 从配置表读取整数字段
+ * @param L Lua 状态
+ * @param idx 配置表索引
+ * @param key 字段名
+ * @param default_value 默认值
+ * @return 整数值
+ */
+int easylvgl_marshal_integer(void *L, int idx, const char *key, int default_value);
+/**
+ * 获取表字段的长度(仅支持数组)
+ */
+int easylvgl_marshal_table_length(void *L, int idx, const char *key);
+
+/**
+ * 在表字段中获取指定位置的字符串
+ */
+const char *easylvgl_marshal_table_string_at(void *L, int idx, const char *key, int position);
+
+
+/**
+ * 从配置表读取布尔字段
+ * @param L Lua 状态
+ * @param idx 配置表索引
+ * @param key 字段名
+ * @param default_value 默认值
+ * @return 布尔值
+ */
+bool easylvgl_marshal_bool(void *L, int idx, const char *key, bool default_value);
+
+/**
+ * 从配置表读取字符串字段
+ * @param L Lua 状态
+ * @param idx 配置表索引
+ * @param key 字段名
+ * @param default_value 默认值
+ * @return 字符串指针(内部字符串,不需要释放),未找到返回 default_value
+ */
+const char *easylvgl_marshal_string(void *L, int idx, const char *key, const char *default_value);
+
+/**
+ * 从配置表读取父对象
+ * @param L Lua 状态
+ * @param idx 配置表索引
+ * @return LVGL 父对象指针,未指定返回 NULL
+ */
+lv_obj_t *easylvgl_marshal_parent(void *L, int idx);
+
+/**
+ * 从配置表读取点坐标(用于 pivot 等)
+ * @param L Lua 状态
+ * @param idx 配置表索引
+ * @param key 字段名
+ * @param out 输出点坐标
+ * @return true 成功读取,false 未找到或格式错误
+ */
+bool easylvgl_marshal_point(void *L, int idx, const char *key, lv_point_t *out);
+
+/**
+ * 从 LVGL 对象获取元数据
+ * @param obj LVGL 对象指针
+ * @return 元数据指针,未找到返回 NULL
+ */
+easylvgl_component_meta_t *easylvgl_component_meta_get(lv_obj_t *obj);
+
+/**
+ * Button 组件:从配置表创建
+ */
+lv_obj_t *easylvgl_button_create_from_config(void *L, int idx);
+int easylvgl_button_set_text(lv_obj_t *btn, const char *text); //设置按钮文本
+int easylvgl_button_set_on_click(lv_obj_t *btn, int callback_ref); //设置点击回调
+
+/**
+ * Label 组件:从配置表创建
+ */
+lv_obj_t *easylvgl_label_create_from_config(void *L, int idx);
+int easylvgl_label_set_text(lv_obj_t *label, const char *text); //设置标签文本
+const char *easylvgl_label_get_text(lv_obj_t *label); //获取标签文本
+
+/**
+ * Dropdown 组件创建
+ */
+lv_obj_t *easylvgl_dropdown_create_from_config(void *L, int idx);
+int easylvgl_dropdown_set_selected(lv_obj_t *dropdown, int index); //设置下拉框选中项
+int easylvgl_dropdown_get_selected(lv_obj_t *dropdown); //获取下拉框选中项
+int easylvgl_dropdown_set_on_change(lv_obj_t *dropdown, int callback_ref); //设置改变回调
+
+/**
+ * Switch 组件创建
+ */
+lv_obj_t *easylvgl_switch_create_from_config(void *L, int idx);
+int easylvgl_switch_set_state(lv_obj_t *sw, bool checked); //设置开关状态
+bool easylvgl_switch_get_state(lv_obj_t *sw); //获取开关状态
+int easylvgl_switch_set_on_change(lv_obj_t *sw, int callback_ref); //设置改变回调
+
+/**
+ * Container 组件创建
+ */
+lv_obj_t *easylvgl_container_create_from_config(void *L, int idx);
+int easylvgl_container_set_color(lv_obj_t *container, uint32_t color); //设置背景颜色
+
+/**
+ * Table 组件创建
+ */
+lv_obj_t *easylvgl_table_create_from_config(void *L, int idx);
+int easylvgl_table_set_cell_text(lv_obj_t *table, uint16_t row, uint16_t col, const char *text); //设置单元格文本
+int easylvgl_table_set_col_width(lv_obj_t *table, uint16_t col, lv_coord_t width); //调整列宽   
+
+/**
+ * TabView 组件创建
+ */
+lv_obj_t *easylvgl_tabview_create_from_config(void *L, int idx);
+int easylvgl_tabview_set_active(lv_obj_t *tabview, uint32_t idx); //激活某页
+lv_obj_t *easylvgl_tabview_get_content(lv_obj_t *tabview, int idx); //获取某页容器
+void easylvgl_tabview_release_data(easylvgl_component_meta_t *meta); //释放内部页容器数据
+
+/**
+ * Msgbox 组件
+ */
+lv_obj_t *easylvgl_msgbox_create_from_config(void *L, int idx);
+int easylvgl_msgbox_set_on_action(lv_obj_t *msgbox, int callback_ref); //设置消息框按钮回调
+int easylvgl_msgbox_show(lv_obj_t *msgbox); //显示消息框
+int easylvgl_msgbox_hide(lv_obj_t *msgbox); //隐藏消息框
+lv_timer_t *easylvgl_msgbox_release_user_data(easylvgl_component_meta_t *meta); //释放消息框用户数据(定时器等)
+
+/**
+ * Image 组件
+ */
+lv_obj_t *easylvgl_image_create_from_config(void *L, int idx);
+int easylvgl_image_set_src(lv_obj_t *img, const char *src); //设置图片源
+int easylvgl_image_set_zoom(lv_obj_t *img, int zoom); //设置缩放比例
+int easylvgl_image_set_opacity(lv_obj_t *img, int opacity); //设置透明度
+
+/**
+ * Win 组件
+ */
+lv_obj_t *easylvgl_win_create_from_config(void *L, int idx);
+int easylvgl_win_set_title(lv_obj_t *win, const char *title); //设置窗口标题
+int easylvgl_win_add_content(lv_obj_t *win, lv_obj_t *child); //添加子组件到内容容器
+
+/**
+ * Textarea组件
+ */
+lv_obj_t *easylvgl_textarea_create_from_config(void *L, int idx);
+int easylvgl_textarea_set_text(lv_obj_t *textarea, const char *text); //设置文本内容
+const char *easylvgl_textarea_get_text(lv_obj_t *textarea); //获取文本内容
+int easylvgl_textarea_set_cursor(lv_obj_t *textarea, uint32_t pos); //设置光标位置
+int easylvgl_textarea_set_on_text_change(lv_obj_t *textarea, int callback_ref); //设置文本改变回调
+int easylvgl_textarea_attach_keyboard(lv_obj_t *textarea, lv_obj_t *keyboard); //关联键盘
+lv_obj_t *easylvgl_textarea_get_keyboard(lv_obj_t *textarea); //获取关联键盘
+
+/**
+ * Keyboard组件
+ */
+lv_obj_t *easylvgl_keyboard_create_from_config(void *L, int idx);
+int easylvgl_keyboard_set_target(lv_obj_t *keyboard, lv_obj_t *textarea); //设置目标组件
+int easylvgl_keyboard_show(lv_obj_t *keyboard); //显示键盘
+int easylvgl_keyboard_hide(lv_obj_t *keyboard); //隐藏键盘
+int easylvgl_keyboard_set_on_commit(lv_obj_t *keyboard, int callback_ref); //设置提交回调
+int easylvgl_keyboard_set_layout(lv_obj_t *keyboard, const char *layout); //设置键盘布局
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* EASYLVGL_COMPONENT_H */
+

+ 68 - 0
components/easylvgl/inc/luat_easylvgl_conf.h

@@ -0,0 +1,68 @@
+/**
+ * @file luat_easylvgl_conf.h
+ * @summary EasyLVGL 平台相关配置
+ * @description 根据不同的平台(SDL2、BK7258等)配置 LVGL 参数
+ * 
+ * 注意:此文件会被 lv_conf.h 包含,用于覆盖默认配置
+ */
+
+#ifndef LUAT_EASYLVGL_CONF_H
+#define LUAT_EASYLVGL_CONF_H
+
+#include "luat_conf_bsp.h"
+
+/*=================
+ * PLATFORM CONFIGURATION
+ *=================*/
+
+#if defined(LUAT_USE_EASYLVGL_SDL2)
+    /* SDL2 平台配置 */
+    #define LV_USE_OS   LV_OS_NONE  /* SDL2 平台可能不需要 OSAL */
+
+    #define LV_USE_LOG 1
+    #define LV_LOG_LEVEL LV_LOG_LEVEL_INFO
+
+    // 打开png支持
+    #define LV_USE_LODEPNG 1
+    #define LV_MEM_SIZE (256 * 1024U)
+    #define LV_USE_TJPGD 1
+
+    // 默认字体设置
+    #define LV_FONT_FMT_TXT_LARGE 1
+    #define LV_USE_FONT_COMPRESSED 1
+    #define LV_FONT_CUSTOM_DECLARE LV_FONT_DECLARE(lv_font_misans_20) LV_FONT_DECLARE(lv_font_misans_14)
+    
+    // #define LV_FONT_DEFAULT &lv_font_misans_20
+    #define LV_FONT_DEFAULT &lv_font_misans_14
+
+
+
+#elif defined(__BK72XX__)
+    /* BK7258 平台配置:使用 FreeRTOS 以支持 LVGL 多线程渲染 */
+    #define LV_USE_OS   LV_OS_FREERTOS
+    // #define LV_DRAW_SW_DRAW_UNIT_CNT    1   // 开启2个软件渲染单元以并行绘制
+    // #define LV_USE_PARALLEL_DRAW_DEBUG  0    // 开启并行绘制调试
+
+    #define LV_USE_LOG 1
+    /** Set value to one of the following levels of logging detail:
+     *  - LV_LOG_LEVEL_TRACE    Log detailed information.
+     *  - LV_LOG_LEVEL_INFO     Log important events.
+     *  - LV_LOG_LEVEL_WARN     Log if something unwanted happened but didn't cause a problem.
+     *  - LV_LOG_LEVEL_ERROR    Log only critical issues, when system may fail.
+     *  - LV_LOG_LEVEL_USER     Log only custom log messages added by the user.
+     *  - LV_LOG_LEVEL_NONE     Do not log anything. */
+    #define LV_LOG_LEVEL LV_LOG_LEVEL_INFO
+    
+    // 图片解码支持
+    #define LV_USE_LODEPNG 1
+    #define LV_MEM_SIZE (128 * 1024U)
+
+    #define LV_USE_TJPGD 1
+
+#else
+    /* 默认配置(如果未定义平台) */
+    /* 使用 lv_conf.h 中的默认值,这里不需要重新定义 */
+#endif
+
+#endif /* LUAT_EASYLVGL_CONF_H */
+

+ 69 - 0
components/easylvgl/inc/luat_easylvgl_symbol.h

@@ -0,0 +1,69 @@
+#ifndef LUAT_EASYLVGL_SYMBOL_H
+#define LUAT_EASYLVGL_SYMBOL_H
+
+#include "rotable2.h"
+#include "lvgl9/src/font/lv_symbol_def.h"
+
+#define EASYLVGL_SYMBOL_REG \
+    {"EASYLVGL_SYMBOL_AUDIO",        ROREG_STR(LV_SYMBOL_AUDIO           )},\
+    {"EASYLVGL_SYMBOL_VIDEO",        ROREG_STR(LV_SYMBOL_VIDEO           )},\
+    {"EASYLVGL_SYMBOL_LIST",         ROREG_STR(LV_SYMBOL_LIST            )},\
+    {"EASYLVGL_SYMBOL_OK",           ROREG_STR(LV_SYMBOL_OK              )},\
+    {"EASYLVGL_SYMBOL_CLOSE",        ROREG_STR(LV_SYMBOL_CLOSE           )},\
+    {"EASYLVGL_SYMBOL_POWER",        ROREG_STR(LV_SYMBOL_POWER           )},\
+    {"EASYLVGL_SYMBOL_SETTINGS",     ROREG_STR(LV_SYMBOL_SETTINGS        )},\
+    {"EASYLVGL_SYMBOL_HOME",         ROREG_STR(LV_SYMBOL_HOME            )},\
+    {"EASYLVGL_SYMBOL_DOWNLOAD",     ROREG_STR(LV_SYMBOL_DOWNLOAD        )},\
+    {"EASYLVGL_SYMBOL_DRIVE",        ROREG_STR(LV_SYMBOL_DRIVE           )},\
+    {"EASYLVGL_SYMBOL_REFRESH",      ROREG_STR(LV_SYMBOL_REFRESH         )},\
+    {"EASYLVGL_SYMBOL_MUTE",         ROREG_STR(LV_SYMBOL_MUTE            )},\
+    {"EASYLVGL_SYMBOL_VOLUME_MID",   ROREG_STR(LV_SYMBOL_VOLUME_MID      )},\
+    {"EASYLVGL_SYMBOL_VOLUME_MAX",   ROREG_STR(LV_SYMBOL_VOLUME_MAX      )},\
+    {"EASYLVGL_SYMBOL_IMAGE",        ROREG_STR(LV_SYMBOL_IMAGE           )},\
+    {"EASYLVGL_SYMBOL_EDIT",         ROREG_STR(LV_SYMBOL_EDIT            )},\
+    {"EASYLVGL_SYMBOL_PREV",         ROREG_STR(LV_SYMBOL_PREV            )},\
+    {"EASYLVGL_SYMBOL_PLAY",         ROREG_STR(LV_SYMBOL_PLAY            )},\
+    {"EASYLVGL_SYMBOL_PAUSE",        ROREG_STR(LV_SYMBOL_PAUSE           )},\
+    {"EASYLVGL_SYMBOL_STOP",         ROREG_STR(LV_SYMBOL_STOP            )},\
+    {"EASYLVGL_SYMBOL_NEXT",         ROREG_STR(LV_SYMBOL_NEXT            )},\
+    {"EASYLVGL_SYMBOL_EJECT",        ROREG_STR(LV_SYMBOL_EJECT           )},\
+    {"EASYLVGL_SYMBOL_LEFT",         ROREG_STR(LV_SYMBOL_LEFT            )},\
+    {"EASYLVGL_SYMBOL_RIGHT",        ROREG_STR(LV_SYMBOL_RIGHT           )},\
+    {"EASYLVGL_SYMBOL_PLUS",         ROREG_STR(LV_SYMBOL_PLUS            )},\
+    {"EASYLVGL_SYMBOL_MINUS",        ROREG_STR(LV_SYMBOL_MINUS           )},\
+    {"EASYLVGL_SYMBOL_EYE_OPEN",     ROREG_STR(LV_SYMBOL_EYE_OPEN        )},\
+    {"EASYLVGL_SYMBOL_EYE_CLOSE",    ROREG_STR(LV_SYMBOL_EYE_CLOSE       )},\
+    {"EASYLVGL_SYMBOL_WARNING",      ROREG_STR(LV_SYMBOL_WARNING         )},\
+    {"EASYLVGL_SYMBOL_SHUFFLE",      ROREG_STR(LV_SYMBOL_SHUFFLE         )},\
+    {"EASYLVGL_SYMBOL_UP",           ROREG_STR(LV_SYMBOL_UP              )},\
+    {"EASYLVGL_SYMBOL_DOWN",         ROREG_STR(LV_SYMBOL_DOWN            )},\
+    {"EASYLVGL_SYMBOL_LOOP",         ROREG_STR(LV_SYMBOL_LOOP            )},\
+    {"EASYLVGL_SYMBOL_DIRECTORY",    ROREG_STR(LV_SYMBOL_DIRECTORY       )},\
+    {"EASYLVGL_SYMBOL_UPLOAD",       ROREG_STR(LV_SYMBOL_UPLOAD          )},\
+    {"EASYLVGL_SYMBOL_CALL",         ROREG_STR(LV_SYMBOL_CALL            )},\
+    {"EASYLVGL_SYMBOL_CUT",          ROREG_STR(LV_SYMBOL_CUT             )},\
+    {"EASYLVGL_SYMBOL_COPY",         ROREG_STR(LV_SYMBOL_COPY            )},\
+    {"EASYLVGL_SYMBOL_SAVE",         ROREG_STR(LV_SYMBOL_SAVE            )},\
+    {"EASYLVGL_SYMBOL_CHARGE",       ROREG_STR(LV_SYMBOL_CHARGE          )},\
+    {"EASYLVGL_SYMBOL_PASTE",        ROREG_STR(LV_SYMBOL_PASTE           )},\
+    {"EASYLVGL_SYMBOL_BELL",         ROREG_STR(LV_SYMBOL_BELL            )},\
+    {"EASYLVGL_SYMBOL_KEYBOARD",     ROREG_STR(LV_SYMBOL_KEYBOARD        )},\
+    {"EASYLVGL_SYMBOL_GPS",          ROREG_STR(LV_SYMBOL_GPS             )},\
+    {"EASYLVGL_SYMBOL_FILE",         ROREG_STR(LV_SYMBOL_FILE            )},\
+    {"EASYLVGL_SYMBOL_WIFI",         ROREG_STR(LV_SYMBOL_WIFI            )},\
+    {"EASYLVGL_SYMBOL_BATTERY_FULL", ROREG_STR(LV_SYMBOL_BATTERY_FULL    )},\
+    {"EASYLVGL_SYMBOL_BATTERY_3",    ROREG_STR(LV_SYMBOL_BATTERY_3       )},\
+    {"EASYLVGL_SYMBOL_BATTERY_2",    ROREG_STR(LV_SYMBOL_BATTERY_2       )},\
+    {"EASYLVGL_SYMBOL_BATTERY_1",    ROREG_STR(LV_SYMBOL_BATTERY_1       )},\
+    {"EASYLVGL_SYMBOL_BATTERY_EMPTY",ROREG_STR(LV_SYMBOL_BATTERY_EMPTY   )},\
+    {"EASYLVGL_SYMBOL_USB",          ROREG_STR(LV_SYMBOL_USB             )},\
+    {"EASYLVGL_SYMBOL_BLUETOOTH",    ROREG_STR(LV_SYMBOL_BLUETOOTH       )},\
+    {"EASYLVGL_SYMBOL_TRASH",        ROREG_STR(LV_SYMBOL_TRASH           )},\
+    {"EASYLVGL_SYMBOL_BACKSPACE",    ROREG_STR(LV_SYMBOL_BACKSPACE       )},\
+    {"EASYLVGL_SYMBOL_SD_CARD",      ROREG_STR(LV_SYMBOL_SD_CARD         )},\
+    {"EASYLVGL_SYMBOL_NEW_LINE",     ROREG_STR(LV_SYMBOL_NEW_LINE        )},\
+    {"EASYLVGL_SYMBOL_DUMMY",        ROREG_STR(   "\xEF\xA3\xBF"         )},\
+    {"EASYLVGL_SYMBOL_BULLET",       ROREG_STR(LV_SYMBOL_BULLET)}
+
+#endif
+

+ 1503 - 0
components/easylvgl/lvgl9/lv_conf.h

@@ -0,0 +1,1503 @@
+/**
+ * @file lv_conf.h
+ * Configuration file for v9.4.0
+ */
+
+/*
+ * Copy this file as `lv_conf.h`
+ * 1. simply next to `lvgl` folder
+ * 2. or to any other place and
+ *    - define `LV_CONF_INCLUDE_SIMPLE`;
+ *    - add the path as an include path.
+ */
+
+/* clang-format off */
+#if 1 /* Set this to "1" to enable content */
+
+#ifndef LV_CONF_H
+#define LV_CONF_H
+
+/* Include platform-specific configuration */
+#include "luat_easylvgl_conf.h"
+
+/* If you need to include anything here, do it inside the `__ASSEMBLY__` guard */
+#if  0 && defined(__ASSEMBLY__)
+#include "my_include.h"
+#endif
+
+/*====================
+   COLOR SETTINGS
+ *====================*/
+
+/** Color depth: 1 (I1), 8 (L8), 16 (RGB565), 24 (RGB888), 32 (XRGB8888) */
+#define LV_COLOR_DEPTH 16
+
+/*=========================
+   STDLIB WRAPPER SETTINGS
+ *=========================*/
+
+/** Possible values
+ * - LV_STDLIB_BUILTIN:     LVGL's built in implementation
+ * - LV_STDLIB_CLIB:        Standard C functions, like malloc, strlen, etc
+ * - LV_STDLIB_MICROPYTHON: MicroPython implementation
+ * - LV_STDLIB_RTTHREAD:    RT-Thread implementation
+ * - LV_STDLIB_CUSTOM:      Implement the functions externally
+ */
+#define LV_USE_STDLIB_MALLOC    LV_STDLIB_BUILTIN
+
+/** Possible values
+ * - LV_STDLIB_BUILTIN:     LVGL's built in implementation
+ * - LV_STDLIB_CLIB:        Standard C functions, like malloc, strlen, etc
+ * - LV_STDLIB_MICROPYTHON: MicroPython implementation
+ * - LV_STDLIB_RTTHREAD:    RT-Thread implementation
+ * - LV_STDLIB_CUSTOM:      Implement the functions externally
+ */
+#define LV_USE_STDLIB_STRING    LV_STDLIB_BUILTIN
+
+/** Possible values
+ * - LV_STDLIB_BUILTIN:     LVGL's built in implementation
+ * - LV_STDLIB_CLIB:        Standard C functions, like malloc, strlen, etc
+ * - LV_STDLIB_MICROPYTHON: MicroPython implementation
+ * - LV_STDLIB_RTTHREAD:    RT-Thread implementation
+ * - LV_STDLIB_CUSTOM:      Implement the functions externally
+ */
+#define LV_USE_STDLIB_SPRINTF   LV_STDLIB_BUILTIN
+
+#define LV_STDINT_INCLUDE       <stdint.h>
+#define LV_STDDEF_INCLUDE       <stddef.h>
+#define LV_STDBOOL_INCLUDE      <stdbool.h>
+#define LV_INTTYPES_INCLUDE     <inttypes.h>
+#define LV_LIMITS_INCLUDE       <limits.h>
+#define LV_STDARG_INCLUDE       <stdarg.h>
+
+#if LV_USE_STDLIB_MALLOC == LV_STDLIB_BUILTIN
+    /** Size of memory available for `lv_malloc()` in bytes (>= 2kB) */
+    #ifndef LV_MEM_SIZE
+        #define LV_MEM_SIZE (64 * 1024U)          /**< [bytes] */
+    #endif
+
+    /** Size of the memory expand for `lv_malloc()` in bytes */
+    #define LV_MEM_POOL_EXPAND_SIZE 0
+
+    /** Set an address for the memory pool instead of allocating it as a normal array. Can be in external SRAM too. */
+    #define LV_MEM_ADR 0     /**< 0: unused*/
+    /* Instead of an address give a memory allocator that will be called to get a memory pool for LVGL. E.g. my_malloc */
+    #if LV_MEM_ADR == 0
+        #undef LV_MEM_POOL_INCLUDE
+        #undef LV_MEM_POOL_ALLOC
+    #endif
+#endif  /*LV_USE_STDLIB_MALLOC == LV_STDLIB_BUILTIN*/
+
+/*====================
+   HAL SETTINGS
+ *====================*/
+
+/** Default display refresh, input device read and animation step period. */
+#define LV_DEF_REFR_PERIOD  33      /**< [ms] */
+
+/** Default Dots Per Inch. Used to initialize default sizes such as widgets sized, style paddings.
+ * (Not so important, you can adjust it to modify default sizes and spaces.) */
+#define LV_DPI_DEF 130              /**< [px/inch] */
+
+/*=================
+ * OPERATING SYSTEM
+ *=================*/
+/** Select operating system to use. Possible options:
+ * - LV_OS_NONE
+ * - LV_OS_PTHREAD
+ * - LV_OS_FREERTOS
+ * - LV_OS_CMSIS_RTOS2
+ * - LV_OS_RTTHREAD
+ * - LV_OS_WINDOWS
+ * - LV_OS_MQX
+ * - LV_OS_SDL2
+ * - LV_OS_CUSTOM */
+#ifndef LV_USE_OS
+    #define LV_USE_OS   LV_OS_NONE  /* Default: no OS */
+#endif
+
+#if LV_USE_OS == LV_OS_CUSTOM
+    #define LV_OS_CUSTOM_INCLUDE "luat_easylvgl_os.h"
+#endif
+#if LV_USE_OS == LV_OS_FREERTOS
+    /*
+     * Unblocking an RTOS task with a direct notification is 45% faster and uses less RAM
+     * than unblocking a task using an intermediary object such as a binary semaphore.
+     * RTOS task notifications can only be used when there is only one task that can be the recipient of the event.
+     */
+    #define LV_USE_FREERTOS_TASK_NOTIFY 1
+#endif
+
+/*========================
+ * RENDERING CONFIGURATION
+ *========================*/
+
+/** Align stride of all layers and images to this bytes */
+#define LV_DRAW_BUF_STRIDE_ALIGN                1
+
+/** Align start address of draw_buf addresses to this bytes*/
+#define LV_DRAW_BUF_ALIGN                       4
+
+/** Using matrix for transformations.
+ * Requirements:
+ * - `LV_USE_MATRIX = 1`.
+ * - Rendering engine needs to support 3x3 matrix transformations. */
+#define LV_DRAW_TRANSFORM_USE_MATRIX            0
+
+/* If a widget has `style_opa < 255` (not `bg_opa`, `text_opa` etc) or not NORMAL blend mode
+ * it is buffered into a "simple" layer before rendering. The widget can be buffered in smaller chunks.
+ * "Transformed layers" (if `transform_angle/zoom` are set) use larger buffers
+ * and can't be drawn in chunks. */
+
+/** The target buffer size for simple layer chunks. */
+#define LV_DRAW_LAYER_SIMPLE_BUF_SIZE    (24 * 1024)    /**< [bytes]*/
+
+/* Limit the max allocated memory for simple and transformed layers.
+ * It should be at least `LV_DRAW_LAYER_SIMPLE_BUF_SIZE` sized but if transformed layers are also used
+ * it should be enough to store the largest widget too (width x height x 4 area).
+ * Set it to 0 to have no limit. */
+#define LV_DRAW_LAYER_MAX_MEMORY 0  /**< No limit by default [bytes]*/
+
+/** Stack size of drawing thread.
+ * NOTE: If FreeType or ThorVG is enabled, it is recommended to set it to 32KB or more.
+ */
+#define LV_DRAW_THREAD_STACK_SIZE    (8 * 1024)         /**< [bytes]*/
+
+/** Thread priority of the drawing task.
+ *  Higher values mean higher priority.
+ *  Can use values from lv_thread_prio_t enum in lv_os.h: LV_THREAD_PRIO_LOWEST,
+ *  LV_THREAD_PRIO_LOW, LV_THREAD_PRIO_MID, LV_THREAD_PRIO_HIGH, LV_THREAD_PRIO_HIGHEST
+ *  Make sure the priority value aligns with the OS-specific priority levels.
+ *  On systems with limited priority levels (e.g., FreeRTOS), a higher value can improve
+ *  rendering performance but might cause other tasks to starve. */
+#define LV_DRAW_THREAD_PRIO LV_THREAD_PRIO_HIGH
+
+#define LV_USE_DRAW_SW 1
+#if LV_USE_DRAW_SW == 1
+    /*
+     * Selectively disable color format support in order to reduce code size.
+     * NOTE: some features use certain color formats internally, e.g.
+     * - gradients use RGB888
+     * - bitmaps with transparency may use ARGB8888
+     */
+    #define LV_DRAW_SW_SUPPORT_RGB565       1
+    #define LV_DRAW_SW_SUPPORT_RGB565_SWAPPED       1
+    #define LV_DRAW_SW_SUPPORT_RGB565A8     1
+    #define LV_DRAW_SW_SUPPORT_RGB888       1
+    #define LV_DRAW_SW_SUPPORT_XRGB8888     1
+    #define LV_DRAW_SW_SUPPORT_ARGB8888     1
+    #define LV_DRAW_SW_SUPPORT_ARGB8888_PREMULTIPLIED 1
+    #define LV_DRAW_SW_SUPPORT_L8           1
+    #define LV_DRAW_SW_SUPPORT_AL88         1
+    #define LV_DRAW_SW_SUPPORT_A8           1
+    #define LV_DRAW_SW_SUPPORT_I1           1
+
+    /* The threshold of the luminance to consider a pixel as
+     * active in indexed color format */
+    #define LV_DRAW_SW_I1_LUM_THRESHOLD 127
+
+    /** Set number of draw units.
+     *  - > 1 requires operating system to be enabled in `LV_USE_OS`.
+     *  - > 1 means multiple threads will render the screen in parallel. */
+    #ifndef LV_DRAW_SW_DRAW_UNIT_CNT
+        #define LV_DRAW_SW_DRAW_UNIT_CNT    1
+    #endif
+
+    /** Use Arm-2D to accelerate software (sw) rendering. */
+    #define LV_USE_DRAW_ARM2D_SYNC      0
+
+    /** Enable native helium assembly to be compiled. */
+    #define LV_USE_NATIVE_HELIUM_ASM    0
+
+    /**
+     * - 0: Use a simple renderer capable of drawing only simple rectangles with gradient, images, text, and straight lines only.
+     * - 1: Use a complex renderer capable of drawing rounded corners, shadow, skew lines, and arcs too. */
+    #define LV_DRAW_SW_COMPLEX          1
+
+    #if LV_DRAW_SW_COMPLEX == 1
+        /** Allow buffering some shadow calculation.
+         *  LV_DRAW_SW_SHADOW_CACHE_SIZE is the maximum shadow size to buffer, where shadow size is
+         *  `shadow_width + radius`.  Caching has LV_DRAW_SW_SHADOW_CACHE_SIZE^2 RAM cost. */
+        #define LV_DRAW_SW_SHADOW_CACHE_SIZE 0
+
+        /** Set number of maximally-cached circle data.
+         *  The circumference of 1/4 circle are saved for anti-aliasing.
+         *  `radius * 4` bytes are used per circle (the most often used radiuses are saved).
+         *  - 0: disables caching */
+        #define LV_DRAW_SW_CIRCLE_CACHE_SIZE 4
+    #endif
+
+    #define  LV_USE_DRAW_SW_ASM     LV_DRAW_SW_ASM_NONE
+
+    #if LV_USE_DRAW_SW_ASM == LV_DRAW_SW_ASM_CUSTOM
+        #define  LV_DRAW_SW_ASM_CUSTOM_INCLUDE ""
+    #endif
+
+    /** Enable drawing complex gradients in software: linear at an angle, radial or conical */
+    #define LV_USE_DRAW_SW_COMPLEX_GRADIENTS    0
+
+#endif
+
+/*Use TSi's aka (Think Silicon) NemaGFX */
+#define LV_USE_NEMA_GFX 0
+
+#if LV_USE_NEMA_GFX
+    /** Select which NemaGFX HAL to use. Possible options:
+     * - LV_NEMA_HAL_CUSTOM
+     * - LV_NEMA_HAL_STM32 */
+    #define LV_USE_NEMA_HAL LV_NEMA_HAL_CUSTOM
+    #if LV_USE_NEMA_HAL == LV_NEMA_HAL_STM32
+        #define LV_NEMA_STM32_HAL_INCLUDE <stm32u5xx_hal.h>
+    #endif
+
+    /*Enable Vector Graphics Operations. Available only if NemaVG library is present*/
+    #define LV_USE_NEMA_VG 0
+    #if LV_USE_NEMA_VG
+        /*Define application's resolution used for VG related buffer allocation */
+        #define LV_NEMA_GFX_MAX_RESX 800
+        #define LV_NEMA_GFX_MAX_RESY 600
+    #endif
+#endif
+
+/** Use NXP's PXP on iMX RTxxx platforms. */
+#define LV_USE_PXP 0
+
+#if LV_USE_PXP
+    /** Use PXP for drawing.*/
+    #define LV_USE_DRAW_PXP 1
+
+    /** Use PXP to rotate display.*/
+    #define LV_USE_ROTATE_PXP 0
+
+    #if LV_USE_DRAW_PXP && LV_USE_OS
+        /** Use additional draw thread for PXP processing.*/
+        #define LV_USE_PXP_DRAW_THREAD 1
+    #endif
+
+    /** Enable PXP asserts. */
+    #define LV_USE_PXP_ASSERT 0
+#endif
+
+/** Use NXP's G2D on MPU platforms. */
+#define LV_USE_G2D 0
+
+#if LV_USE_G2D
+    /** Use G2D for drawing. **/
+    #define LV_USE_DRAW_G2D 1
+
+    /** Use G2D to rotate display. **/
+    #define LV_USE_ROTATE_G2D 0
+
+    /** Maximum number of buffers that can be stored for G2D draw unit.
+     *  Includes the frame buffers and assets. */
+    #define LV_G2D_HASH_TABLE_SIZE 50
+
+    #if LV_USE_DRAW_G2D && LV_USE_OS
+        /** Use additional draw thread for G2D processing.*/
+        #define LV_USE_G2D_DRAW_THREAD 1
+    #endif
+
+    /** Enable G2D asserts. */
+    #define LV_USE_G2D_ASSERT 0
+#endif
+
+/** Use Renesas Dave2D on RA  platforms. */
+#define LV_USE_DRAW_DAVE2D 0
+
+/** Draw using cached SDL textures*/
+#define LV_USE_DRAW_SDL 0
+
+/** Use VG-Lite GPU. */
+#define LV_USE_DRAW_VG_LITE 0
+#if LV_USE_DRAW_VG_LITE
+    /** Enable VG-Lite custom external 'gpu_init()' function */
+    #define LV_VG_LITE_USE_GPU_INIT 0
+
+    /** Enable VG-Lite assert. */
+    #define LV_VG_LITE_USE_ASSERT 0
+
+    /** VG-Lite flush commit trigger threshold. GPU will try to batch these many draw tasks. */
+    #define LV_VG_LITE_FLUSH_MAX_COUNT 8
+
+    /** Enable border to simulate shadow.
+     *  NOTE: which usually improves performance,
+     *  but does not guarantee the same rendering quality as the software. */
+    #define LV_VG_LITE_USE_BOX_SHADOW 1
+
+    /** VG-Lite gradient maximum cache number.
+     *  @note  The memory usage of a single gradient image is 4K bytes. */
+    #define LV_VG_LITE_GRAD_CACHE_CNT 32
+
+    /** VG-Lite stroke maximum cache number. */
+    #define LV_VG_LITE_STROKE_CACHE_CNT 32
+
+    /** Remove VLC_OP_CLOSE path instruction (Workaround for NXP) **/
+    #define LV_VG_LITE_DISABLE_VLC_OP_CLOSE 0
+
+    /** Disable linear gradient extension for some older versions of drivers. */
+    #define LV_VG_LITE_DISABLE_LINEAR_GRADIENT_EXT 0
+
+    /** Enable usage of the LVGL's built-in vg_lite driver */
+    #define LV_USE_VG_LITE_DRIVER  0
+    #if LV_USE_VG_LITE_DRIVER
+        /** Used to pick the correct GPU series folder valid options are gc255, gc355 and gc555*/
+        #define LV_VG_LITE_HAL_GPU_SERIES gc255
+
+        /** Used to pick the correct GPU revision header it depends on the vendor */
+        #define LV_VG_LITE_HAL_GPU_REVISION 0x40
+
+        /** Base memory address of the GPU IP it depends on SoC,
+         *  default value is for NXP based devices */
+        #define LV_VG_LITE_HAL_GPU_BASE_ADDRESS 0x40240000
+    #endif /*LV_USE_VG_LITE_DRIVER*/
+
+    /** Use ThorVG (a software vector library) as VG-Lite driver to allow testing VGLite on PC
+     *  Requires: LV_USE_THORVG_INTERNAL or LV_USE_THORVG_EXTERNAL */
+    #define LV_USE_VG_LITE_THORVG   0
+    #if LV_USE_VG_LITE_THORVG
+        /** Enable LVGL's blend mode support */
+        #define LV_VG_LITE_THORVG_LVGL_BLEND_SUPPORT 0
+
+        /** Enable YUV color format support */
+        #define LV_VG_LITE_THORVG_YUV_SUPPORT 0
+
+        /** Enable Linear gradient extension support */
+        #define LV_VG_LITE_THORVG_LINEAR_GRADIENT_EXT_SUPPORT 0
+
+        /** Enable alignment on 16 pixels */
+        #define LV_VG_LITE_THORVG_16PIXELS_ALIGN 1
+
+        /** Buffer address alignment */
+        #define LV_VG_LITE_THORVG_BUF_ADDR_ALIGN 64
+
+        /** Enable multi-thread render */
+        #define LV_VG_LITE_THORVG_THREAD_RENDER 0
+    #endif /*LV_USE_VG_LITE_THORVG*/
+#endif
+
+/** Accelerate blends, fills, etc. with STM32 DMA2D */
+#define LV_USE_DRAW_DMA2D 0
+#if LV_USE_DRAW_DMA2D
+    #define LV_DRAW_DMA2D_HAL_INCLUDE "stm32h7xx_hal.h"
+
+    /* if enabled, the user is required to call `lv_draw_dma2d_transfer_complete_interrupt_handler`
+     * upon receiving the DMA2D global interrupt
+     */
+    #define LV_USE_DRAW_DMA2D_INTERRUPT 0
+#endif
+
+/** Draw using cached OpenGLES textures. Requires LV_USE_OPENGLES */
+#define LV_USE_DRAW_OPENGLES 0
+#if LV_USE_DRAW_OPENGLES
+    #define LV_DRAW_OPENGLES_TEXTURE_CACHE_COUNT 64
+#endif
+
+/** Draw using espressif PPA accelerator */
+#define LV_USE_PPA  0
+#if LV_USE_PPA
+    #define LV_USE_PPA_IMG 0
+#endif
+
+/* Use EVE FT81X GPU. */
+#define LV_USE_DRAW_EVE 0
+#if LV_USE_DRAW_EVE
+    /* EVE_GEN value: 2, 3, or 4 */
+    #define LV_DRAW_EVE_EVE_GENERATION 4
+
+    /* The maximum number of bytes to buffer before a single SPI transmission.
+     * Set it to 0 to disable write buffering.
+     */
+    #define LV_DRAW_EVE_WRITE_BUFFER_SIZE 2048
+#endif
+
+/*=======================
+ * FEATURE CONFIGURATION
+ *=======================*/
+
+/*-------------
+ * Logging
+ *-----------*/
+
+/** Enable log module */
+#ifndef LV_USE_LOG
+    #define LV_USE_LOG 0
+#endif
+#if LV_USE_LOG
+    /** Set value to one of the following levels of logging detail:
+     *  - LV_LOG_LEVEL_TRACE    Log detailed information.
+     *  - LV_LOG_LEVEL_INFO     Log important events.
+     *  - LV_LOG_LEVEL_WARN     Log if something unwanted happened but didn't cause a problem.
+     *  - LV_LOG_LEVEL_ERROR    Log only critical issues, when system may fail.
+     *  - LV_LOG_LEVEL_USER     Log only custom log messages added by the user.
+     *  - LV_LOG_LEVEL_NONE     Do not log anything. */
+    #ifndef LV_LOG_LEVEL
+        #define LV_LOG_LEVEL LV_LOG_LEVEL_WARN
+    #endif
+
+    /** - 1: Print log with 'printf';
+     *  - 0: User needs to register a callback with `lv_log_register_print_cb()`. */
+    #define LV_LOG_PRINTF 0
+
+    /** Set callback to print logs.
+     *  E.g `my_print`. The prototype should be `void my_print(lv_log_level_t level, const char * buf)`.
+     *  Can be overwritten by `lv_log_register_print_cb`. */
+    //#define LV_LOG_PRINT_CB
+
+    /** - 1: Enable printing timestamp;
+     *  - 0: Disable printing timestamp. */
+    #define LV_LOG_USE_TIMESTAMP 1
+
+    /** - 1: Print file and line number of the log;
+     *  - 0: Do not print file and line number of the log. */
+    #define LV_LOG_USE_FILE_LINE 1
+
+    /* Enable/disable LV_LOG_TRACE in modules that produces a huge number of logs. */
+    #define LV_LOG_TRACE_MEM        1   /**< Enable/disable trace logs in memory operations. */
+    #define LV_LOG_TRACE_TIMER      1   /**< Enable/disable trace logs in timer operations. */
+    #define LV_LOG_TRACE_INDEV      1   /**< Enable/disable trace logs in input device operations. */
+    #define LV_LOG_TRACE_DISP_REFR  1   /**< Enable/disable trace logs in display re-draw operations. */
+    #define LV_LOG_TRACE_EVENT      1   /**< Enable/disable trace logs in event dispatch logic. */
+    #define LV_LOG_TRACE_OBJ_CREATE 1   /**< Enable/disable trace logs in object creation (core `obj` creation plus every widget). */
+    #define LV_LOG_TRACE_LAYOUT     1   /**< Enable/disable trace logs in flex- and grid-layout operations. */
+    #define LV_LOG_TRACE_ANIM       1   /**< Enable/disable trace logs in animation logic. */
+    #define LV_LOG_TRACE_CACHE      1   /**< Enable/disable trace logs in cache operations. */
+#endif  /*LV_USE_LOG*/
+
+/*-------------
+ * Asserts
+ *-----------*/
+
+/* Enable assertion failures if an operation fails or invalid data is found.
+ * If LV_USE_LOG is enabled, an error message will be printed on failure. */
+#define LV_USE_ASSERT_NULL          1   /**< Check if the parameter is NULL. (Very fast, recommended) */
+#define LV_USE_ASSERT_MALLOC        1   /**< Checks is the memory is successfully allocated or no. (Very fast, recommended) */
+#define LV_USE_ASSERT_STYLE         0   /**< Check if the styles are properly initialized. (Very fast, recommended) */
+#define LV_USE_ASSERT_MEM_INTEGRITY 0   /**< Check the integrity of `lv_mem` after critical operations. (Slow) */
+#define LV_USE_ASSERT_OBJ           0   /**< Check the object's type and existence (e.g. not deleted). (Slow) */
+
+/** Add a custom handler when assert happens e.g. to restart MCU. */
+#define LV_ASSERT_HANDLER_INCLUDE <stdint.h>
+#define LV_ASSERT_HANDLER while(1);     /**< Halt by default */
+
+/*-------------
+ * Debug
+ *-----------*/
+
+/** 1: Draw random colored rectangles over the redrawn areas. */
+#define LV_USE_REFR_DEBUG 0
+
+/** 1: Draw a red overlay for ARGB layers and a green overlay for RGB layers*/
+#define LV_USE_LAYER_DEBUG 0
+
+/** 1: Adds the following behaviors for debugging:
+ *  - Draw overlays with different colors for each draw_unit's tasks.
+ *  - Draw index number of draw unit on white background.
+ *  - For layers, draws index number of draw unit on black background. */
+
+ #ifndef LV_USE_PARALLEL_DRAW_DEBUG
+    #define LV_USE_PARALLEL_DRAW_DEBUG 0
+ #endif
+
+/*-------------
+ * Others
+ *-----------*/
+
+#define LV_ENABLE_GLOBAL_CUSTOM 0
+#if LV_ENABLE_GLOBAL_CUSTOM
+    /** Header to include for custom 'lv_global' function" */
+    #define LV_GLOBAL_CUSTOM_INCLUDE <stdint.h>
+#endif
+
+/** Default cache size in bytes.
+ *  Used by image decoders such as `lv_lodepng` to keep the decoded image in memory.
+ *  If size is not set to 0, the decoder will fail to decode when the cache is full.
+ *  If size is 0, the cache function is not enabled and the decoded memory will be
+ *  released immediately after use. */
+#define LV_CACHE_DEF_SIZE       0
+
+/** Default number of image header cache entries. The cache is used to store the headers of images
+ *  The main logic is like `LV_CACHE_DEF_SIZE` but for image headers. */
+#define LV_IMAGE_HEADER_CACHE_DEF_CNT 0
+
+/** Number of stops allowed per gradient. Increase this to allow more stops.
+ *  This adds (sizeof(lv_color_t) + 1) bytes per additional stop. */
+#define LV_GRADIENT_MAX_STOPS   2
+
+/** Adjust color mix functions rounding. GPUs might calculate color mix (blending) differently.
+ *  - 0:   round down,
+ *  - 64:  round up from x.75,
+ *  - 128: round up from half,
+ *  - 192: round up from x.25,
+ *  - 254: round up */
+#define LV_COLOR_MIX_ROUND_OFS  0
+
+/** Add 2 x 32-bit variables to each `lv_obj_t` to speed up getting style properties */
+#define LV_OBJ_STYLE_CACHE      0
+
+/** Add `id` field to `lv_obj_t` */
+#define LV_USE_OBJ_ID           0
+
+/**  Enable support widget names*/
+#define LV_USE_OBJ_NAME         0
+
+/** Automatically assign an ID when obj is created */
+#define LV_OBJ_ID_AUTO_ASSIGN   LV_USE_OBJ_ID
+
+/** Use builtin obj ID handler functions:
+* - lv_obj_assign_id:       Called when a widget is created. Use a separate counter for each widget class as an ID.
+* - lv_obj_id_compare:      Compare the ID to decide if it matches with a requested value.
+* - lv_obj_stringify_id:    Return string-ified identifier, e.g. "button3".
+* - lv_obj_free_id:         Does nothing, as there is no memory allocation for the ID.
+* When disabled these functions needs to be implemented by the user.*/
+#define LV_USE_OBJ_ID_BUILTIN   1
+
+/** Use obj property set/get API. */
+#define LV_USE_OBJ_PROPERTY 0
+
+/** Enable property name support. */
+#define LV_USE_OBJ_PROPERTY_NAME 1
+
+/* Enable the multi-touch gesture recognition feature */
+/* Gesture recognition requires the use of floats */
+#define LV_USE_GESTURE_RECOGNITION 0
+
+/*=====================
+ *  COMPILER SETTINGS
+ *====================*/
+
+/** For big endian systems set to 1 */
+#define LV_BIG_ENDIAN_SYSTEM 0
+
+/** Define a custom attribute for `lv_tick_inc` function */
+#define LV_ATTRIBUTE_TICK_INC
+
+/** Define a custom attribute for `lv_timer_handler` function */
+#define LV_ATTRIBUTE_TIMER_HANDLER
+
+/** Define a custom attribute for `lv_display_flush_ready` function */
+#define LV_ATTRIBUTE_FLUSH_READY
+
+/** Align VG_LITE buffers on this number of bytes.
+ *  @note  vglite_src_buf_aligned() uses this value to validate alignment of passed buffer pointers. */
+#define LV_ATTRIBUTE_MEM_ALIGN_SIZE 1
+
+/** Will be added where memory needs to be aligned (with -Os data might not be aligned to boundary by default).
+ *  E.g. __attribute__((aligned(4)))*/
+#define LV_ATTRIBUTE_MEM_ALIGN
+
+/** Attribute to mark large constant arrays, for example for font bitmaps */
+#define LV_ATTRIBUTE_LARGE_CONST
+
+/** Compiler prefix for a large array declaration in RAM */
+#define LV_ATTRIBUTE_LARGE_RAM_ARRAY
+
+/** Place performance critical functions into a faster memory (e.g RAM) */
+#define LV_ATTRIBUTE_FAST_MEM
+
+/** Export integer constant to binding. This macro is used with constants in the form of LV_<CONST> that
+ *  should also appear on LVGL binding API such as MicroPython. */
+#define LV_EXPORT_CONST_INT(int_value) struct _silence_gcc_warning  /**< The default value just prevents GCC warning */
+
+/** Prefix all global extern data with this */
+#define LV_ATTRIBUTE_EXTERN_DATA
+
+/** Use `float` as `lv_value_precise_t` */
+#define LV_USE_FLOAT            0
+
+/** Enable matrix support
+ *  - Requires `LV_USE_FLOAT = 1` */
+#define LV_USE_MATRIX           0
+
+/** Include `lvgl_private.h` in `lvgl.h` to access internal data and functions by default */
+#ifndef LV_USE_PRIVATE_API
+    #define LV_USE_PRIVATE_API  0
+#endif
+
+/*==================
+ *   FONT USAGE
+ *===================*/
+
+/* Montserrat fonts with ASCII range and some symbols using bpp = 4
+ * https://fonts.google.com/specimen/Montserrat */
+#define LV_FONT_MONTSERRAT_8  0
+#define LV_FONT_MONTSERRAT_10 0
+#define LV_FONT_MONTSERRAT_12 0
+#define LV_FONT_MONTSERRAT_14 1
+#define LV_FONT_MONTSERRAT_16 0
+#define LV_FONT_MONTSERRAT_18 0
+#define LV_FONT_MONTSERRAT_20 0
+#define LV_FONT_MONTSERRAT_22 0
+#define LV_FONT_MONTSERRAT_24 0
+#define LV_FONT_MONTSERRAT_26 0
+#define LV_FONT_MONTSERRAT_28 0
+#define LV_FONT_MONTSERRAT_30 0
+#define LV_FONT_MONTSERRAT_32 0
+#define LV_FONT_MONTSERRAT_34 0
+#define LV_FONT_MONTSERRAT_36 0
+#define LV_FONT_MONTSERRAT_38 0
+#define LV_FONT_MONTSERRAT_40 0
+#define LV_FONT_MONTSERRAT_42 0
+#define LV_FONT_MONTSERRAT_44 0
+#define LV_FONT_MONTSERRAT_46 0
+#define LV_FONT_MONTSERRAT_48 0
+
+/* Demonstrate special features */
+#define LV_FONT_MONTSERRAT_28_COMPRESSED    0  /**< bpp = 3 */
+#define LV_FONT_DEJAVU_16_PERSIAN_HEBREW    0  /**< Hebrew, Arabic, Persian letters and all their forms */
+#define LV_FONT_SOURCE_HAN_SANS_SC_14_CJK   0  /**< 1338 most common CJK radicals */
+#define LV_FONT_SOURCE_HAN_SANS_SC_16_CJK   0  /**< 1338 most common CJK radicals */
+
+/** Pixel perfect monospaced fonts */
+#define LV_FONT_UNSCII_8  0
+#define LV_FONT_UNSCII_16 0
+
+/** Optionally declare custom fonts here.
+ *
+ *  You can use any of these fonts as the default font too and they will be available
+ *  globally.  Example:
+ *
+ *  @code
+ *  #define LV_FONT_CUSTOM_DECLARE   LV_FONT_DECLARE(my_font_1) LV_FONT_DECLARE(my_font_2)
+ *  @endcode
+ */
+
+#ifndef LV_FONT_CUSTOM_DECLARE
+    #define LV_FONT_CUSTOM_DECLARE
+#endif
+/** Always set a default font */
+#ifndef LV_FONT_DEFAULT
+    #define LV_FONT_DEFAULT &lv_font_montserrat_14
+#endif
+
+/** Enable handling large font and/or fonts with a lot of characters.
+ *  The limit depends on the font size, font face and bpp.
+ *  A compiler error will be triggered if a font needs it. */
+ #ifndef LV_FONT_FMT_TXT_LARGE
+    #define LV_FONT_FMT_TXT_LARGE 0
+ #endif
+
+/** Enables/disables support for compressed fonts. */
+#ifndef LV_USE_FONT_COMPRESSED
+    #define LV_USE_FONT_COMPRESSED 0
+#endif
+
+/** Enable drawing placeholders when glyph dsc is not found. */
+#define LV_USE_FONT_PLACEHOLDER 1
+
+/*=================
+ *  TEXT SETTINGS
+ *=================*/
+
+/**
+ * Select a character encoding for strings.
+ * Your IDE or editor should have the same character encoding.
+ * - LV_TXT_ENC_UTF8
+ * - LV_TXT_ENC_ASCII
+ */
+#define LV_TXT_ENC LV_TXT_ENC_UTF8
+
+/** While rendering text strings, break (wrap) text on these chars. */
+#define LV_TXT_BREAK_CHARS " ,.;:-_)]}"
+
+/** If a word is at least this long, will break wherever "prettiest".
+ *  To disable, set to a value <= 0. */
+#define LV_TXT_LINE_BREAK_LONG_LEN 0
+
+/** Minimum number of characters in a long word to put on a line before a break.
+ *  Depends on LV_TXT_LINE_BREAK_LONG_LEN. */
+#define LV_TXT_LINE_BREAK_LONG_PRE_MIN_LEN 3
+
+/** Minimum number of characters in a long word to put on a line after a break.
+ *  Depends on LV_TXT_LINE_BREAK_LONG_LEN. */
+#define LV_TXT_LINE_BREAK_LONG_POST_MIN_LEN 3
+
+/** Support bidirectional text. Allows mixing Left-to-Right and Right-to-Left text.
+ *  The direction will be processed according to the Unicode Bidirectional Algorithm:
+ *  https://www.w3.org/International/articles/inline-bidi-markup/uba-basics */
+#define LV_USE_BIDI 0
+#if LV_USE_BIDI
+    /*Set the default direction. Supported values:
+    *`LV_BASE_DIR_LTR` Left-to-Right
+    *`LV_BASE_DIR_RTL` Right-to-Left
+    *`LV_BASE_DIR_AUTO` detect text base direction*/
+    #define LV_BIDI_BASE_DIR_DEF LV_BASE_DIR_AUTO
+#endif
+
+/** Enable Arabic/Persian processing
+ *  In these languages characters should be replaced with another form based on their position in the text */
+#define LV_USE_ARABIC_PERSIAN_CHARS 0
+
+/*The control character to use for signaling text recoloring*/
+#define LV_TXT_COLOR_CMD "#"
+
+/*==================
+ * WIDGETS
+ *================*/
+/* Documentation for widgets can be found here: https://docs.lvgl.io/master/details/widgets/index.html . */
+
+/** 1: Causes these widgets to be given default values at creation time.
+ *  - lv_buttonmatrix_t:  Get default maps:  {"Btn1", "Btn2", "Btn3", "\n", "Btn4", "Btn5", ""}, else map not set.
+ *  - lv_checkbox_t    :  String label set to "Check box", else set to empty string.
+ *  - lv_dropdown_t    :  Options set to "Option 1", "Option 2", "Option 3", else no values are set.
+ *  - lv_roller_t      :  Options set to "Option 1", "Option 2", "Option 3", "Option 4", "Option 5", else no values are set.
+ *  - lv_label_t       :  Text set to "Text", else empty string.
+ *  - lv_arclabel_t   :  Text set to "Arced Text", else empty string.
+ * */
+#define LV_WIDGETS_HAS_DEFAULT_VALUE  1
+
+#define LV_USE_ANIMIMG    1
+
+#define LV_USE_ARC        1
+
+#define LV_USE_ARCLABEL  1
+
+#define LV_USE_BAR        1
+
+#define LV_USE_BUTTON        1
+
+#define LV_USE_BUTTONMATRIX  1
+
+#define LV_USE_CALENDAR   1
+#if LV_USE_CALENDAR
+    #define LV_CALENDAR_WEEK_STARTS_MONDAY 0
+    #if LV_CALENDAR_WEEK_STARTS_MONDAY
+        #define LV_CALENDAR_DEFAULT_DAY_NAMES {"Mo", "Tu", "We", "Th", "Fr", "Sa", "Su"}
+    #else
+        #define LV_CALENDAR_DEFAULT_DAY_NAMES {"Su", "Mo", "Tu", "We", "Th", "Fr", "Sa"}
+    #endif
+
+    #define LV_CALENDAR_DEFAULT_MONTH_NAMES {"January", "February", "March",  "April", "May",  "June", "July", "August", "September", "October", "November", "December"}
+    #define LV_USE_CALENDAR_HEADER_ARROW 1
+    #define LV_USE_CALENDAR_HEADER_DROPDOWN 1
+    #define LV_USE_CALENDAR_CHINESE 0
+#endif  /*LV_USE_CALENDAR*/
+
+#define LV_USE_CANVAS     1
+
+#define LV_USE_CHART      1
+
+#define LV_USE_CHECKBOX   1
+
+#define LV_USE_DROPDOWN   1   /**< Requires: lv_label */
+
+#define LV_USE_IMAGE      1   /**< Requires: lv_label */
+
+#define LV_USE_IMAGEBUTTON     1
+
+#define LV_USE_KEYBOARD   1
+
+#define LV_USE_LABEL      1
+#if LV_USE_LABEL
+    #define LV_LABEL_TEXT_SELECTION 1   /**< Enable selecting text of the label */
+    #define LV_LABEL_LONG_TXT_HINT 1    /**< Store some extra info in labels to speed up drawing of very long text */
+    #define LV_LABEL_WAIT_CHAR_COUNT 3  /**< The count of wait chart */
+#endif
+
+#define LV_USE_LED        1
+
+#define LV_USE_LINE       1
+
+#define LV_USE_LIST       1
+
+#define LV_USE_LOTTIE     0  /**< Requires: lv_canvas, thorvg */
+
+#define LV_USE_MENU       1
+
+#define LV_USE_MSGBOX     1
+
+#define LV_USE_ROLLER     1   /**< Requires: lv_label */
+
+#define LV_USE_SCALE      1
+
+#define LV_USE_SLIDER     1   /**< Requires: lv_bar */
+
+#define LV_USE_SPAN       1
+#if LV_USE_SPAN
+    /** A line of text can contain this maximum number of span descriptors. */
+    #define LV_SPAN_SNIPPET_STACK_SIZE 64
+#endif
+
+#define LV_USE_SPINBOX    1
+
+#define LV_USE_SPINNER    1
+
+#define LV_USE_SWITCH     1
+
+#define LV_USE_TABLE      1
+
+#define LV_USE_TABVIEW    1
+
+#define LV_USE_TEXTAREA   1   /**< Requires: lv_label */
+#if LV_USE_TEXTAREA != 0
+    #define LV_TEXTAREA_DEF_PWD_SHOW_TIME 1500    /**< [ms] */
+#endif
+
+#define LV_USE_TILEVIEW   1
+
+#define LV_USE_WIN        1
+
+#define LV_USE_3DTEXTURE  0
+
+/*==================
+ * THEMES
+ *==================*/
+/* Documentation for themes can be found here: https://docs.lvgl.io/master/details/common-widget-features/styles/styles.html#themes . */
+
+/** A simple, impressive and very complete theme */
+#define LV_USE_THEME_DEFAULT 1
+#if LV_USE_THEME_DEFAULT
+    /** 0: Light mode; 1: Dark mode */
+    #define LV_THEME_DEFAULT_DARK 0
+
+    /** 1: Enable grow on press */
+    #define LV_THEME_DEFAULT_GROW 1
+
+    /** Default transition time in ms. */
+    #define LV_THEME_DEFAULT_TRANSITION_TIME 80
+#endif /*LV_USE_THEME_DEFAULT*/
+
+/** A very simple theme that is a good starting point for a custom theme */
+#define LV_USE_THEME_SIMPLE 1
+
+/** A theme designed for monochrome displays */
+#define LV_USE_THEME_MONO 1
+
+/*==================
+ * LAYOUTS
+ *==================*/
+/* Documentation for layouts can be found here: https://docs.lvgl.io/master/details/common-widget-features/layouts/index.html . */
+
+/** A layout similar to Flexbox in CSS. */
+#define LV_USE_FLEX 1
+
+/** A layout similar to Grid in CSS. */
+#define LV_USE_GRID 1
+
+/*====================
+ * 3RD PARTS LIBRARIES
+ *====================*/
+/* Documentation for libraries can be found here: https://docs.lvgl.io/master/details/libs/index.html . */
+
+/* File system interfaces for common APIs */
+
+/** Setting a default driver letter allows skipping the driver prefix in filepaths.
+ *  Documentation about how to use the below driver-identifier letters can be found at
+ *  https://docs.lvgl.io/master/details/main-modules/fs.html#lv-fs-identifier-letters . */
+#define LV_FS_DEFAULT_DRIVER_LETTER '\0'
+
+/** API for fopen, fread, etc. */
+#define LV_USE_FS_STDIO 0
+#if LV_USE_FS_STDIO
+    #define LV_FS_STDIO_LETTER '\0'     /**< Set an upper-case driver-identifier letter for this driver (e.g. 'A'). */
+    #define LV_FS_STDIO_PATH ""         /**< Set the working directory. File/directory paths will be appended to it. */
+    #define LV_FS_STDIO_CACHE_SIZE 0    /**< >0 to cache this number of bytes in lv_fs_read() */
+#endif
+
+/** API for open, read, etc. */
+#define LV_USE_FS_POSIX 0
+#if LV_USE_FS_POSIX
+    #define LV_FS_POSIX_LETTER '\0'     /**< Set an upper-case driver-identifier letter for this driver (e.g. 'A'). */
+    #define LV_FS_POSIX_PATH ""         /**< Set the working directory. File/directory paths will be appended to it. */
+    #define LV_FS_POSIX_CACHE_SIZE 0    /**< >0 to cache this number of bytes in lv_fs_read() */
+#endif
+
+/** API for CreateFile, ReadFile, etc. */
+#define LV_USE_FS_WIN32 0
+#if LV_USE_FS_WIN32
+    #define LV_FS_WIN32_LETTER '\0'     /**< Set an upper-case driver-identifier letter for this driver (e.g. 'A'). */
+    #define LV_FS_WIN32_PATH ""         /**< Set the working directory. File/directory paths will be appended to it. */
+    #define LV_FS_WIN32_CACHE_SIZE 0    /**< >0 to cache this number of bytes in lv_fs_read() */
+#endif
+
+/** API for FATFS (needs to be added separately). Uses f_open, f_read, etc. */
+#define LV_USE_FS_FATFS 0
+#if LV_USE_FS_FATFS
+    #define LV_FS_FATFS_LETTER '\0'     /**< Set an upper-case driver-identifier letter for this driver (e.g. 'A'). */
+    #define LV_FS_FATFS_PATH ""         /**< Set the working directory. File/directory paths will be appended to it. */
+    #define LV_FS_FATFS_CACHE_SIZE 0    /**< >0 to cache this number of bytes in lv_fs_read() */
+#endif
+
+/** API for memory-mapped file access. */
+#define LV_USE_FS_MEMFS 0
+#if LV_USE_FS_MEMFS
+    #define LV_FS_MEMFS_LETTER '\0'     /**< Set an upper-case driver-identifier letter for this driver (e.g. 'A'). */
+#endif
+
+/** API for LittleFs. */
+#define LV_USE_FS_LITTLEFS 0
+#if LV_USE_FS_LITTLEFS
+    #define LV_FS_LITTLEFS_LETTER '\0'  /**< Set an upper-case driver-identifier letter for this driver (e.g. 'A'). */
+    #define LV_FS_LITTLEFS_PATH ""      /**< Set the working directory. File/directory paths will be appended to it. */
+#endif
+
+/** API for Arduino LittleFs. */
+#define LV_USE_FS_ARDUINO_ESP_LITTLEFS 0
+#if LV_USE_FS_ARDUINO_ESP_LITTLEFS
+    #define LV_FS_ARDUINO_ESP_LITTLEFS_LETTER '\0'  /**< Set an upper-case driver-identifier letter for this driver (e.g. 'A'). */
+    #define LV_FS_ARDUINO_ESP_LITTLEFS_PATH ""      /**< Set the working directory. File/directory paths will be appended to it. */
+#endif
+
+/** API for Arduino Sd. */
+#define LV_USE_FS_ARDUINO_SD 0
+#if LV_USE_FS_ARDUINO_SD
+    #define LV_FS_ARDUINO_SD_LETTER '\0'  /**< Set an upper-case driver-identifier letter for this driver (e.g. 'A'). */
+    #define LV_FS_ARDUINO_SD_PATH ""      /**< Set the working directory. File/directory paths will be appended to it. */
+#endif
+
+/** API for UEFI */
+#define LV_USE_FS_UEFI 0
+#if LV_USE_FS_UEFI
+    #define LV_FS_UEFI_LETTER '\0'      /**< Set an upper-case driver-identifier letter for this driver (e.g. 'A'). */
+#endif
+
+#define LV_USE_FS_FROGFS 0
+#if LV_USE_FS_FROGFS
+    #define LV_FS_FROGFS_LETTER '\0'
+#endif
+
+/** LODEPNG decoder library */
+#ifndef LV_USE_LODEPNG
+    #define LV_USE_LODEPNG 0
+#endif
+
+/** PNG decoder(libpng) library */
+#define LV_USE_LIBPNG 0
+
+/** BMP decoder library */
+#define LV_USE_BMP 0
+
+/** JPG + split JPG decoder library.
+ *  Split JPG is a custom format optimized for embedded systems. */
+#ifndef LV_USE_TJPGD
+    #define LV_USE_TJPGD 0
+#endif
+
+/** libjpeg-turbo decoder library.
+ *  - Supports complete JPEG specifications and high-performance JPEG decoding. */
+#define LV_USE_LIBJPEG_TURBO 0
+
+/** GIF decoder library */
+#define LV_USE_GIF 0
+#if LV_USE_GIF
+    /** GIF decoder accelerate */
+    #define LV_GIF_CACHE_DECODE_DATA 0
+#endif
+
+/** GStreamer library */
+#define LV_USE_GSTREAMER 0
+
+/** Decode bin images to RAM */
+#define LV_BIN_DECODER_RAM_LOAD 0
+
+/** RLE decompress library */
+#define LV_USE_RLE 0
+
+/** QR code library */
+#define LV_USE_QRCODE 0
+
+/** Barcode code library */
+#define LV_USE_BARCODE 0
+
+/** FreeType library */
+#define LV_USE_FREETYPE 0
+#if LV_USE_FREETYPE
+    /** Let FreeType use LVGL memory and file porting */
+    #define LV_FREETYPE_USE_LVGL_PORT 0
+
+    /** Cache count of glyphs in FreeType, i.e. number of glyphs that can be cached.
+     *  The higher the value, the more memory will be used. */
+    #define LV_FREETYPE_CACHE_FT_GLYPH_CNT 256
+#endif
+
+/** Built-in TTF decoder */
+#define LV_USE_TINY_TTF 0
+#if LV_USE_TINY_TTF
+    /* Enable loading TTF data from files */
+    #define LV_TINY_TTF_FILE_SUPPORT 0
+    #define LV_TINY_TTF_CACHE_GLYPH_CNT 128
+    #define LV_TINY_TTF_CACHE_KERNING_CNT 256
+#endif
+
+/** Rlottie library */
+#define LV_USE_RLOTTIE 0
+
+/** Requires `LV_USE_3DTEXTURE = 1` */
+#define LV_USE_GLTF  0
+
+/** Enable Vector Graphic APIs
+ *  Requires `LV_USE_MATRIX = 1` */
+#define LV_USE_VECTOR_GRAPHIC  0
+
+/** Enable ThorVG (vector graphics library) from the src/libs folder.
+ *  Requires LV_USE_VECTOR_GRAPHIC */
+#define LV_USE_THORVG_INTERNAL 0
+
+/** Enable ThorVG by assuming that its installed and linked to the project
+ *  Requires LV_USE_VECTOR_GRAPHIC */
+#define LV_USE_THORVG_EXTERNAL 0
+
+/** Use lvgl built-in LZ4 lib */
+#define LV_USE_LZ4_INTERNAL  0
+
+/** Use external LZ4 library */
+#define LV_USE_LZ4_EXTERNAL  0
+
+/*SVG library
+ *  - Requires `LV_USE_VECTOR_GRAPHIC = 1` */
+#define LV_USE_SVG 0
+#define LV_USE_SVG_ANIMATION 0
+#define LV_USE_SVG_DEBUG 0
+
+/** FFmpeg library for image decoding and playing videos.
+ *  Supports all major image formats so do not enable other image decoder with it. */
+#define LV_USE_FFMPEG 0
+#if LV_USE_FFMPEG
+    /** Dump input information to stderr */
+    #define LV_FFMPEG_DUMP_FORMAT 0
+    /** Use lvgl file path in FFmpeg Player widget
+     *  You won't be able to open URLs after enabling this feature.
+     *  Note that FFmpeg image decoder will always use lvgl file system. */
+    #define LV_FFMPEG_PLAYER_USE_LV_FS 0
+#endif
+
+/*==================
+ * OTHERS
+ *==================*/
+/* Documentation for several of the below items can be found here: https://docs.lvgl.io/master/details/auxiliary-modules/index.html . */
+
+/** 1: Enable API to take snapshot for object */
+#define LV_USE_SNAPSHOT 0
+
+/** 1: Enable system monitor component */
+#define LV_USE_SYSMON   0
+#if LV_USE_SYSMON
+    /** Get the idle percentage. E.g. uint32_t my_get_idle(void); */
+    #define LV_SYSMON_GET_IDLE lv_os_get_idle_percent
+    /** 1: Enable usage of lv_os_get_proc_idle_percent.*/
+    #define LV_SYSMON_PROC_IDLE_AVAILABLE 0
+    #if LV_SYSMON_PROC_IDLE_AVAILABLE
+        /** Get the applications idle percentage.
+         * - Requires `LV_USE_OS == LV_OS_PTHREAD` */
+        #define LV_SYSMON_GET_PROC_IDLE lv_os_get_proc_idle_percent
+    #endif
+
+    /** 1: Show CPU usage and FPS count.
+     *  - Requires `LV_USE_SYSMON = 1` */
+    #define LV_USE_PERF_MONITOR 0
+    #if LV_USE_PERF_MONITOR
+        #define LV_USE_PERF_MONITOR_POS LV_ALIGN_BOTTOM_RIGHT
+
+        /** 0: Displays performance data on the screen; 1: Prints performance data using log. */
+        #define LV_USE_PERF_MONITOR_LOG_MODE 0
+    #endif
+
+    /** 1: Show used memory and memory fragmentation.
+     *     - Requires `LV_USE_STDLIB_MALLOC = LV_STDLIB_BUILTIN`
+     *     - Requires `LV_USE_SYSMON = 1`*/
+    #define LV_USE_MEM_MONITOR 0
+    #if LV_USE_MEM_MONITOR
+        #define LV_USE_MEM_MONITOR_POS LV_ALIGN_BOTTOM_LEFT
+    #endif
+#endif /*LV_USE_SYSMON*/
+
+/** 1: Enable runtime performance profiler */
+#define LV_USE_PROFILER 1
+#if LV_USE_PROFILER
+    /** 1: Enable the built-in profiler */
+    #define LV_USE_PROFILER_BUILTIN 1
+    #if LV_USE_PROFILER_BUILTIN
+        /** Default profiler trace buffer size */
+        #define LV_PROFILER_BUILTIN_BUF_SIZE (16 * 1024)     /**< [bytes] */
+        #define LV_PROFILER_BUILTIN_DEFAULT_ENABLE 1
+        #define LV_USE_PROFILER_BUILTIN_POSIX 0 /**< Enable POSIX profiler port */
+    #endif
+
+    /** Header to include for profiler */
+    #define LV_PROFILER_INCLUDE "lvgl9/src/misc/lv_profiler_builtin.h"
+
+    /** Profiler start point function */
+    #define LV_PROFILER_BEGIN    LV_PROFILER_BUILTIN_BEGIN
+
+    /** Profiler end point function */
+    #define LV_PROFILER_END      LV_PROFILER_BUILTIN_END
+
+    /** Profiler start point function with custom tag */
+    #define LV_PROFILER_BEGIN_TAG LV_PROFILER_BUILTIN_BEGIN_TAG
+
+    /** Profiler end point function with custom tag */
+    #define LV_PROFILER_END_TAG   LV_PROFILER_BUILTIN_END_TAG
+
+    /*Enable layout profiler*/
+    #define LV_PROFILER_LAYOUT 0
+
+    /*Enable disp refr profiler*/
+    #define LV_PROFILER_REFR 0
+
+    /*Enable draw profiler*/
+    #define LV_PROFILER_DRAW 0
+
+    /*Enable indev profiler*/
+    #define LV_PROFILER_INDEV 0
+
+    /*Enable decoder profiler*/
+    #define LV_PROFILER_DECODER 1
+
+    /*Enable font profiler*/
+    #define LV_PROFILER_FONT 0
+
+    /*Enable fs profiler*/
+    #define LV_PROFILER_FS 0
+
+    /*Enable style profiler*/
+    #define LV_PROFILER_STYLE 0
+
+    /*Enable timer profiler*/
+    #define LV_PROFILER_TIMER 0
+
+    /*Enable cache profiler*/
+    #define LV_PROFILER_CACHE 0
+
+    /*Enable event profiler*/
+    #define LV_PROFILER_EVENT 0
+#endif
+
+/** 1: Enable Monkey test */
+#define LV_USE_MONKEY 0
+
+/** 1: Enable grid navigation */
+#define LV_USE_GRIDNAV 0
+
+/** 1: Enable `lv_obj` fragment logic */
+#define LV_USE_FRAGMENT 0
+
+/** 1: Support using images as font in label or span widgets */
+#define LV_USE_IMGFONT 0
+
+/** 1: Enable an observer pattern implementation */
+#define LV_USE_OBSERVER 1
+
+/** 1: Enable Pinyin input method
+ *  - Requires: lv_keyboard */
+#define LV_USE_IME_PINYIN 0
+#if LV_USE_IME_PINYIN
+    /** 1: Use default thesaurus.
+     *  @note  If you do not use the default thesaurus, be sure to use `lv_ime_pinyin` after setting the thesaurus. */
+    #define LV_IME_PINYIN_USE_DEFAULT_DICT 1
+    /** Set maximum number of candidate panels that can be displayed.
+     *  @note  This needs to be adjusted according to size of screen. */
+    #define LV_IME_PINYIN_CAND_TEXT_NUM 6
+
+    /** Use 9-key input (k9). */
+    #define LV_IME_PINYIN_USE_K9_MODE      1
+    #if LV_IME_PINYIN_USE_K9_MODE == 1
+        #define LV_IME_PINYIN_K9_CAND_TEXT_NUM 3
+    #endif /*LV_IME_PINYIN_USE_K9_MODE*/
+#endif
+
+/** 1: Enable file explorer.
+ *  - Requires: lv_table */
+#define LV_USE_FILE_EXPLORER                     0
+#if LV_USE_FILE_EXPLORER
+    /** Maximum length of path */
+    #define LV_FILE_EXPLORER_PATH_MAX_LEN        (128)
+    /** Quick access bar, 1:use, 0:do not use.
+     *  - Requires: lv_list */
+    #define LV_FILE_EXPLORER_QUICK_ACCESS        1
+#endif
+
+/** 1: Enable Font manager */
+#define LV_USE_FONT_MANAGER                     0
+#if LV_USE_FONT_MANAGER
+
+/**Font manager name max length*/
+#define LV_FONT_MANAGER_NAME_MAX_LEN            32
+
+#endif
+
+/** Enable emulated input devices, time emulation, and screenshot compares. */
+#define LV_USE_TEST 0
+#if LV_USE_TEST
+
+/** Enable `lv_test_screenshot_compare`.
+ * Requires lodepng and a few MB of extra RAM. */
+#define LV_USE_TEST_SCREENSHOT_COMPARE 0
+#endif /*LV_USE_TEST*/
+
+/** Enable loading XML UIs runtime */
+#define LV_USE_XML    0
+
+/** 1: Enable text translation support */
+#define LV_USE_TRANSLATION 0
+
+/*1: Enable color filter style*/
+#define LV_USE_COLOR_FILTER     0
+
+/*==================
+ * DEVICES
+ *==================*/
+
+/** Use SDL to open window on PC and handle mouse and keyboard. */
+#define LV_USE_SDL              0
+#if LV_USE_SDL
+    #define LV_SDL_INCLUDE_PATH     <SDL2/SDL.h>
+    #define LV_SDL_RENDER_MODE      LV_DISPLAY_RENDER_MODE_DIRECT   /**< LV_DISPLAY_RENDER_MODE_DIRECT is recommended for best performance */
+    #define LV_SDL_BUF_COUNT        1    /**< 1 or 2 */
+    #define LV_SDL_ACCELERATED      1    /**< 1: Use hardware acceleration*/
+    #define LV_SDL_FULLSCREEN       0    /**< 1: Make the window full screen by default */
+    #define LV_SDL_DIRECT_EXIT      1    /**< 1: Exit the application when all SDL windows are closed */
+    #define LV_SDL_MOUSEWHEEL_MODE  LV_SDL_MOUSEWHEEL_MODE_ENCODER  /*LV_SDL_MOUSEWHEEL_MODE_ENCODER/CROWN*/
+#endif
+
+/** Use X11 to open window on Linux desktop and handle mouse and keyboard */
+#define LV_USE_X11              0
+#if LV_USE_X11
+    #define LV_X11_DIRECT_EXIT         1  /**< Exit application when all X11 windows have been closed */
+    #define LV_X11_DOUBLE_BUFFER       1  /**< Use double buffers for rendering */
+    /* Select only 1 of the following render modes (LV_X11_RENDER_MODE_PARTIAL preferred!). */
+    #define LV_X11_RENDER_MODE_PARTIAL 1  /**< Partial render mode (preferred) */
+    #define LV_X11_RENDER_MODE_DIRECT  0  /**< Direct render mode */
+    #define LV_X11_RENDER_MODE_FULL    0  /**< Full render mode */
+#endif
+
+/** Use Wayland to open a window and handle input on Linux or BSD desktops */
+#define LV_USE_WAYLAND          0
+#if LV_USE_WAYLAND
+    #define LV_WAYLAND_BUF_COUNT            1    /**< Use 1 for single buffer with partial render mode or 2 for double buffer with full render mode*/
+    #define LV_WAYLAND_USE_DMABUF           0    /**< Use DMA buffers for frame buffers. Requires LV_DRAW_USE_G2D */
+    #define LV_WAYLAND_RENDER_MODE          LV_DISPLAY_RENDER_MODE_PARTIAL   /**< DMABUF supports LV_DISPLAY_RENDER_MODE_FULL and LV_DISPLAY_RENDER_MODE_DIRECT*/
+                                                                             /**< When LV_WAYLAND_USE_DMABUF is disabled, only LV_DISPLAY_RENDER_MODE_PARTIAL is supported*/
+    #define LV_WAYLAND_WINDOW_DECORATIONS   0    /**< Draw client side window decorations only necessary on Mutter/GNOME. Not supported using DMABUF*/
+#endif
+
+/** Driver for /dev/fb */
+#define LV_USE_LINUX_FBDEV      0
+#if LV_USE_LINUX_FBDEV
+    #define LV_LINUX_FBDEV_BSD           0
+    #define LV_LINUX_FBDEV_RENDER_MODE   LV_DISPLAY_RENDER_MODE_PARTIAL
+    #define LV_LINUX_FBDEV_BUFFER_COUNT  0
+    #define LV_LINUX_FBDEV_BUFFER_SIZE   60
+    #define LV_LINUX_FBDEV_MMAP          1
+#endif
+
+/** Use Nuttx to open window and handle touchscreen */
+#define LV_USE_NUTTX    0
+
+#if LV_USE_NUTTX
+    #define LV_USE_NUTTX_INDEPENDENT_IMAGE_HEAP 0
+
+    /** Use independent image heap for default draw buffer */
+    #define LV_NUTTX_DEFAULT_DRAW_BUF_USE_INDEPENDENT_IMAGE_HEAP    0
+
+    #define LV_USE_NUTTX_LIBUV    0
+
+    /** Use Nuttx custom init API to open window and handle touchscreen */
+    #define LV_USE_NUTTX_CUSTOM_INIT    0
+
+    /** Driver for /dev/lcd */
+    #define LV_USE_NUTTX_LCD      0
+    #if LV_USE_NUTTX_LCD
+        #define LV_NUTTX_LCD_BUFFER_COUNT    0
+        #define LV_NUTTX_LCD_BUFFER_SIZE     60
+    #endif
+
+    /** Driver for /dev/input */
+    #define LV_USE_NUTTX_TOUCHSCREEN    0
+
+    /** Touchscreen cursor size in pixels(<=0: disable cursor) */
+    #define LV_NUTTX_TOUCHSCREEN_CURSOR_SIZE    0
+
+    /** Driver for /dev/mouse */
+    #define LV_USE_NUTTX_MOUSE    0
+
+    /** Mouse movement step (pixels) */
+    #define LV_USE_NUTTX_MOUSE_MOVE_STEP    1
+
+    /*NuttX trace file and its path*/
+    #define LV_USE_NUTTX_TRACE_FILE 0
+    #if LV_USE_NUTTX_TRACE_FILE
+        #define LV_NUTTX_TRACE_FILE_PATH "/data/lvgl-trace.log"
+    #endif
+
+#endif
+
+/** Driver for /dev/dri/card */
+#define LV_USE_LINUX_DRM        0
+
+#if LV_USE_LINUX_DRM
+
+    /* Use the MESA GBM library to allocate DMA buffers that can be
+     * shared across sub-systems and libraries using the Linux DMA-BUF API.
+     * The GBM library aims to provide a platform independent memory management system
+     * it supports the major GPU vendors - This option requires linking with libgbm */
+    #define LV_USE_LINUX_DRM_GBM_BUFFERS 0
+
+    #define LV_LINUX_DRM_USE_EGL     0
+#endif
+
+/** Interface for TFT_eSPI */
+#define LV_USE_TFT_ESPI         0
+
+/** Interface for Lovyan_GFX */
+#define LV_USE_LOVYAN_GFX         0
+
+#if LV_USE_LOVYAN_GFX
+    #define LV_LGFX_USER_INCLUDE "lv_lgfx_user.hpp"
+
+#endif /*LV_USE_LOVYAN_GFX*/
+
+/** Driver for evdev input devices */
+#define LV_USE_EVDEV    0
+
+/** Driver for libinput input devices */
+#define LV_USE_LIBINPUT    0
+
+#if LV_USE_LIBINPUT
+    #define LV_LIBINPUT_BSD    0
+
+    /** Full keyboard support */
+    #define LV_LIBINPUT_XKB             0
+    #if LV_LIBINPUT_XKB
+        /** "setxkbmap -query" can help find the right values for your keyboard */
+        #define LV_LIBINPUT_XKB_KEY_MAP { .rules = NULL, .model = "pc101", .layout = "us", .variant = NULL, .options = NULL }
+    #endif
+#endif
+
+/* Drivers for LCD devices connected via SPI/parallel port */
+#define LV_USE_ST7735        0
+#define LV_USE_ST7789        0
+#define LV_USE_ST7796        0
+#define LV_USE_ILI9341       0
+#define LV_USE_FT81X         0
+#define LV_USE_NV3007        0
+
+#if (LV_USE_ST7735 | LV_USE_ST7789 | LV_USE_ST7796 | LV_USE_ILI9341 | LV_USE_NV3007)
+    #define LV_USE_GENERIC_MIPI 1
+#else
+    #define LV_USE_GENERIC_MIPI 0
+#endif
+
+/** Driver for Renesas GLCD */
+#define LV_USE_RENESAS_GLCDC    0
+
+/** Driver for ST LTDC */
+#define LV_USE_ST_LTDC    0
+#if LV_USE_ST_LTDC
+    /* Only used for partial. */
+    #define LV_ST_LTDC_USE_DMA2D_FLUSH 0
+#endif
+
+/** Driver for NXP ELCDIF */
+#define LV_USE_NXP_ELCDIF   0
+
+/** LVGL Windows backend */
+#define LV_USE_WINDOWS    0
+
+/** LVGL UEFI backend */
+#define LV_USE_UEFI 0
+#if LV_USE_UEFI
+    #define LV_USE_UEFI_INCLUDE "myefi.h"   /**< Header that hides the actual framework (EDK2, gnu-efi, ...) */
+    #define LV_UEFI_USE_MEMORY_SERVICES 0   /**< Use the memory functions from the boot services table */
+#endif
+
+/** Use a generic OpenGL driver that can be used to embed in other applications or used with GLFW/EGL */
+#define LV_USE_OPENGLES   0
+#if LV_USE_OPENGLES
+    #define LV_USE_OPENGLES_DEBUG        1    /**< Enable or disable debug for opengles */
+#endif
+
+/** Use GLFW to open window on PC and handle mouse and keyboard. Requires*/
+#define LV_USE_GLFW   0
+
+
+/** QNX Screen display and input drivers */
+#define LV_USE_QNX              0
+#if LV_USE_QNX
+    #define LV_QNX_BUF_COUNT        1    /**< 1 or 2 */
+#endif
+
+/*=====================
+* BUILD OPTIONS
+*======================*/
+
+/** Enable examples to be built with the library. */
+#define LV_BUILD_EXAMPLES 1
+
+/** Build the demos */
+#define LV_BUILD_DEMOS 1
+
+/*===================
+ * DEMO USAGE
+ ====================*/
+
+#if LV_BUILD_DEMOS
+    /** Show some widgets. This might be required to increase `LV_MEM_SIZE`. */
+    #define LV_USE_DEMO_WIDGETS 0
+
+    /** Demonstrate usage of encoder and keyboard. */
+    #define LV_USE_DEMO_KEYPAD_AND_ENCODER 0
+
+    /** Benchmark your system */
+    #define LV_USE_DEMO_BENCHMARK 0
+
+    #if LV_USE_DEMO_BENCHMARK
+        /** Use fonts where bitmaps are aligned 16 byte and has Nx16 byte stride */
+        #define LV_DEMO_BENCHMARK_ALIGNED_FONTS 0
+    #endif
+
+    /** Render test for each primitive.
+     *  - Requires at least 480x272 display. */
+    #define LV_USE_DEMO_RENDER 0
+
+    /** Stress test for LVGL */
+    #define LV_USE_DEMO_STRESS 0
+
+    /** Music player demo */
+    #define LV_USE_DEMO_MUSIC 0
+    #if LV_USE_DEMO_MUSIC
+        #define LV_DEMO_MUSIC_SQUARE    0
+        #define LV_DEMO_MUSIC_LANDSCAPE 0
+        #define LV_DEMO_MUSIC_ROUND     0
+        #define LV_DEMO_MUSIC_LARGE     0
+        #define LV_DEMO_MUSIC_AUTO_PLAY 0
+    #endif
+
+    /** Vector graphic demo */
+    #define LV_USE_DEMO_VECTOR_GRAPHIC  0
+
+    /** GLTF demo */
+    #define LV_USE_DEMO_GLTF            0
+
+    /*---------------------------
+     * Demos from lvgl/lv_demos
+      ---------------------------*/
+
+    /** Flex layout demo */
+    #define LV_USE_DEMO_FLEX_LAYOUT     0
+
+    /** Smart-phone like multi-language demo */
+    #define LV_USE_DEMO_MULTILANG       0
+
+    /** Widget transformation demo */
+    #define LV_USE_DEMO_TRANSFORM       0
+
+    /** Demonstrate scroll settings */
+    #define LV_USE_DEMO_SCROLL          0
+
+    /*E-bike demo with Lottie animations (if LV_USE_LOTTIE is enabled)*/
+    #define LV_USE_DEMO_EBIKE           0
+    #if LV_USE_DEMO_EBIKE
+        #define LV_DEMO_EBIKE_PORTRAIT  0    /*0: for 480x270..480x320, 1: for 480x800..720x1280*/
+    #endif
+
+    /** High-resolution demo */
+    #define LV_USE_DEMO_HIGH_RES        0
+
+    /* Smart watch demo */
+    #define LV_USE_DEMO_SMARTWATCH      0
+#endif /* LV_BUILD_DEMOS */
+
+/*--END OF LV_CONF_H--*/
+
+#endif /*LV_CONF_H*/
+
+#endif /*End of "Content enable"*/

+ 17 - 0
components/easylvgl/lvgl9/lv_version.h

@@ -0,0 +1,17 @@
+/**
+ * @file lv_version.h
+ * The current version of LVGL
+ */
+
+#ifndef LV_VERSION_H
+#define LV_VERSION_H
+
+#define LVGL_VERSION_MAJOR 9
+#define LVGL_VERSION_MINOR 4
+#define LVGL_VERSION_PATCH 0
+#define LVGL_VERSION_INFO ""
+
+#endif /* LV_VERSION_H */
+
+
+

+ 218 - 0
components/easylvgl/lvgl9/lvgl.h

@@ -0,0 +1,218 @@
+/**
+ * @file lvgl.h
+ * Include all LVGL related headers
+ */
+
+#ifndef LVGL_H
+#define LVGL_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/***************************
+ * CURRENT VERSION OF LVGL
+ ***************************/
+#include "lv_version.h"
+
+/*********************
+ *      INCLUDES
+ *********************/
+#include "src/lv_init.h"
+
+#include "src/stdlib/lv_mem.h"
+#include "src/stdlib/lv_string.h"
+#include "src/stdlib/lv_sprintf.h"
+
+#include "src/misc/lv_log.h"
+#include "src/misc/lv_timer.h"
+#include "src/misc/lv_math.h"
+#include "src/misc/lv_array.h"
+#include "src/misc/lv_async.h"
+#include "src/misc/lv_anim_timeline.h"
+#include "src/misc/lv_profiler_builtin.h"
+#include "src/misc/lv_rb.h"
+#include "src/misc/lv_utils.h"
+#include "src/misc/lv_iter.h"
+#include "src/misc/lv_circle_buf.h"
+#include "src/misc/lv_tree.h"
+
+#include "src/osal/lv_os.h"
+
+#include "src/tick/lv_tick.h"
+
+#include "src/core/lv_obj.h"
+#include "src/core/lv_group.h"
+#include "src/indev/lv_indev.h"
+#include "src/indev/lv_indev_gesture.h"
+#include "src/core/lv_refr.h"
+#include "src/display/lv_display.h"
+
+#include "src/font/lv_font.h"
+#include "src/font/lv_binfont_loader.h"
+#include "src/font/lv_font_fmt_txt.h"
+
+#include "src/widgets/animimage/lv_animimage.h"
+#include "src/widgets/arc/lv_arc.h"
+#include "src/widgets/arclabel/lv_arclabel.h"
+#include "src/widgets/bar/lv_bar.h"
+#include "src/widgets/button/lv_button.h"
+#include "src/widgets/buttonmatrix/lv_buttonmatrix.h"
+#include "src/widgets/calendar/lv_calendar.h"
+#include "src/widgets/canvas/lv_canvas.h"
+#include "src/widgets/chart/lv_chart.h"
+#include "src/widgets/checkbox/lv_checkbox.h"
+#include "src/widgets/dropdown/lv_dropdown.h"
+#include "src/widgets/image/lv_image.h"
+#include "src/widgets/imagebutton/lv_imagebutton.h"
+#include "src/widgets/keyboard/lv_keyboard.h"
+#include "src/widgets/label/lv_label.h"
+#include "src/widgets/led/lv_led.h"
+#include "src/widgets/line/lv_line.h"
+#include "src/widgets/list/lv_list.h"
+#include "src/widgets/lottie/lv_lottie.h"
+#include "src/widgets/menu/lv_menu.h"
+#include "src/widgets/msgbox/lv_msgbox.h"
+#include "src/widgets/roller/lv_roller.h"
+#include "src/widgets/scale/lv_scale.h"
+#include "src/widgets/slider/lv_slider.h"
+#include "src/widgets/span/lv_span.h"
+#include "src/widgets/spinbox/lv_spinbox.h"
+#include "src/widgets/spinner/lv_spinner.h"
+#include "src/widgets/switch/lv_switch.h"
+#include "src/widgets/table/lv_table.h"
+#include "src/widgets/tabview/lv_tabview.h"
+#include "src/widgets/textarea/lv_textarea.h"
+#include "src/widgets/tileview/lv_tileview.h"
+#include "src/widgets/win/lv_win.h"
+#include "src/widgets/3dtexture/lv_3dtexture.h"
+
+#include "src/others/snapshot/lv_snapshot.h"
+#include "src/others/sysmon/lv_sysmon.h"
+#include "src/others/monkey/lv_monkey.h"
+#include "src/others/gridnav/lv_gridnav.h"
+#include "src/others/fragment/lv_fragment.h"
+#include "src/others/imgfont/lv_imgfont.h"
+#include "src/others/observer/lv_observer.h"
+#include "src/others/ime/lv_ime_pinyin.h"
+#include "src/others/file_explorer/lv_file_explorer.h"
+#include "src/others/font_manager/lv_font_manager.h"
+#include "src/others/translation/lv_translation.h"
+#include "src/others/xml/lv_xml.h"
+#include "src/others/test/lv_test.h"
+
+#include "src/libs/barcode/lv_barcode.h"
+#include "src/libs/bin_decoder/lv_bin_decoder.h"
+#include "src/libs/bmp/lv_bmp.h"
+#include "src/libs/rle/lv_rle.h"
+#include "src/libs/fsdrv/lv_fsdrv.h"
+#include "src/libs/lodepng/lv_lodepng.h"
+#include "src/libs/libpng/lv_libpng.h"
+#include "src/libs/gltf/gltf_data/lv_gltf_model.h"
+#include "src/libs/gltf/gltf_view/lv_gltf.h"
+#include "src/libs/gif/lv_gif.h"
+#include "src/libs/gstreamer/lv_gstreamer.h"
+#include "src/libs/qrcode/lv_qrcode.h"
+#include "src/libs/tjpgd/lv_tjpgd.h"
+#include "src/libs/libjpeg_turbo/lv_libjpeg_turbo.h"
+#include "src/libs/freetype/lv_freetype.h"
+#include "src/libs/rlottie/lv_rlottie.h"
+#include "src/libs/ffmpeg/lv_ffmpeg.h"
+#include "src/libs/tiny_ttf/lv_tiny_ttf.h"
+#include "src/libs/svg/lv_svg.h"
+#include "src/libs/svg/lv_svg_render.h"
+
+#include "src/layouts/lv_layout.h"
+
+#include "src/draw/lv_draw_buf.h"
+#include "src/draw/lv_draw_vector.h"
+#include "src/draw/sw/lv_draw_sw_utils.h"
+#include "src/draw/eve/lv_draw_eve_target.h"
+
+#include "src/themes/lv_theme.h"
+
+#include "src/drivers/lv_drivers.h"
+
+/* Define LV_DISABLE_API_MAPPING using a compiler option 
+ * to make sure your application is not using deprecated names */
+#ifndef LV_DISABLE_API_MAPPING
+    #include "src/lv_api_map_v8.h"
+    #include "src/lv_api_map_v9_0.h"
+    #include "src/lv_api_map_v9_1.h"
+    #include "src/lv_api_map_v9_2.h"
+    #include "src/lv_api_map_v9_3.h"
+#endif /*LV_DISABLE_API_MAPPING*/
+
+#if LV_USE_PRIVATE_API
+#include "src/lvgl_private.h"
+#endif
+
+
+/*********************
+ *      DEFINES
+ *********************/
+
+/**********************
+ *      TYPEDEFS
+ **********************/
+
+/**********************
+ * GLOBAL PROTOTYPES
+ **********************/
+
+/**********************
+ *      MACROS
+ **********************/
+
+/** Gives 1 if the x.y.z version is supported in the current version
+ * Usage:
+ *
+ * - Require v6
+ * #if LV_VERSION_CHECK(6,0,0)
+ *   new_func_in_v6();
+ * #endif
+ *
+ *
+ * - Require at least v5.3
+ * #if LV_VERSION_CHECK(5,3,0)
+ *   new_feature_from_v5_3();
+ * #endif
+ *
+ *
+ * - Require v5.3.2 bugfixes
+ * #if LV_VERSION_CHECK(5,3,2)
+ *   bugfix_in_v5_3_2();
+ * #endif
+ *
+ */
+#define LV_VERSION_CHECK(x,y,z) (x == LVGL_VERSION_MAJOR && (y < LVGL_VERSION_MINOR || (y == LVGL_VERSION_MINOR && z <= LVGL_VERSION_PATCH)))
+
+/**
+ * Wrapper functions for VERSION macros
+ */
+
+static inline int lv_version_major(void)
+{
+    return LVGL_VERSION_MAJOR;
+}
+
+static inline int lv_version_minor(void)
+{
+    return LVGL_VERSION_MINOR;
+}
+
+static inline int lv_version_patch(void)
+{
+    return LVGL_VERSION_PATCH;
+}
+
+static inline const char * lv_version_info(void)
+{
+    return LVGL_VERSION_INFO;
+}
+
+#ifdef __cplusplus
+} /*extern "C"*/
+#endif
+
+#endif /*LVGL_H*/

+ 318 - 0
components/easylvgl/lvgl9/src/core/lv_global.h

@@ -0,0 +1,318 @@
+/**
+ * @file lv_global.h
+ *
+ */
+
+#ifndef LV_GLOBAL_H
+#define LV_GLOBAL_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/*********************
+ *      INCLUDES
+ *********************/
+#include "../lv_conf_internal.h"
+
+#include "../misc/lv_types.h"
+#include "../draw/lv_draw.h"
+#if LV_USE_DRAW_SW
+#include "../draw/sw/lv_draw_sw.h"
+#endif
+#include "../misc/lv_anim.h"
+#include "../misc/lv_area.h"
+#include "../misc/lv_color_op.h"
+#include "../misc/lv_ll.h"
+#include "../misc/lv_log.h"
+#include "../misc/lv_style.h"
+#include "../misc/lv_timer.h"
+#include "../osal/lv_os_private.h"
+#include "../others/sysmon/lv_sysmon.h"
+#include "../stdlib/builtin/lv_tlsf.h"
+
+#if LV_USE_FONT_COMPRESSED
+#include "../font/lv_font_fmt_txt_private.h"
+#endif
+
+#include "../tick/lv_tick.h"
+#include "../layouts/lv_layout.h"
+
+#include "../misc/lv_types.h"
+
+#include "../misc/lv_timer_private.h"
+#include "../misc/lv_anim_private.h"
+#include "../tick/lv_tick_private.h"
+#include "../draw/lv_draw_buf_private.h"
+#include "../draw/lv_draw_private.h"
+#include "../draw/sw/lv_draw_sw_private.h"
+#include "../draw/sw/lv_draw_sw_mask_private.h"
+#include "../stdlib/builtin/lv_tlsf_private.h"
+#include "../others/sysmon/lv_sysmon_private.h"
+#include "../others/test/lv_test_private.h"
+#include "../layouts/lv_layout_private.h"
+
+/*********************
+ *      DEFINES
+ *********************/
+#define ZERO_MEM_SENTINEL  0xa1b2c3d4
+
+/**********************
+ *      TYPEDEFS
+ **********************/
+
+#if LV_USE_SPAN != 0
+struct _snippet_stack;
+#endif
+
+#if LV_USE_FREETYPE
+struct _lv_freetype_context_t;
+#endif
+
+#if LV_USE_PROFILER && LV_USE_PROFILER_BUILTIN
+struct _lv_profiler_builtin_ctx_t;
+#endif
+
+#if LV_USE_NUTTX
+struct _lv_nuttx_ctx_t;
+#endif
+
+typedef struct _lv_global_t {
+    /**
+     * User data for the LVGL library. Move from the bottom of the struct
+     * to avoid breaking the ABI. E.g., if the user data is used by a
+     * closed-source library, this can help to avoid re-compiling the library
+     * when the lvgl-related configs are changed.
+     */
+    void * user_data;
+
+    bool inited;
+    bool deinit_in_progress;     /**< Can be used e.g. in the LV_EVENT_DELETE to deinit the drivers too */
+
+    lv_ll_t disp_ll;
+    lv_display_t * disp_refresh;
+    lv_display_t * disp_default;
+
+    lv_ll_t style_trans_ll;
+    bool style_refresh;
+    uint32_t style_custom_table_size;
+    uint32_t style_last_custom_prop_id;
+    uint8_t * style_custom_prop_flag_lookup_table;
+
+    lv_ll_t group_ll;
+    lv_group_t * group_default;
+
+    lv_ll_t indev_ll;
+    lv_indev_t * indev_active;
+    lv_obj_t * indev_obj_active;
+
+    uint32_t layout_count;
+    lv_layout_dsc_t * layout_list;
+    bool layout_update_mutex;
+
+    uint32_t memory_zero;
+    uint32_t math_rand_seed;
+
+    lv_event_t * event_header;
+    uint32_t event_last_register_id;
+
+    lv_timer_state_t timer_state;
+    lv_anim_state_t anim_state;
+    lv_tick_state_t tick_state;
+
+    lv_draw_buf_handlers_t draw_buf_handlers;
+    lv_draw_buf_handlers_t font_draw_buf_handlers;
+    lv_draw_buf_handlers_t image_cache_draw_buf_handlers;  /**< Ensure that all assigned draw buffers
+                                                            * can be managed by image cache. */
+
+    lv_ll_t img_decoder_ll;
+#if LV_USE_OS != LV_OS_NONE
+    lv_mutex_t img_decoder_info_lock;
+    lv_mutex_t img_decoder_open_lock;
+#endif
+
+    lv_cache_t * img_cache;
+    lv_cache_t * img_header_cache;
+
+    lv_draw_global_info_t draw_info;
+    lv_ll_t draw_sw_blend_handler_ll;
+#if defined(LV_DRAW_SW_SHADOW_CACHE_SIZE) && LV_DRAW_SW_SHADOW_CACHE_SIZE > 0
+    lv_draw_sw_shadow_cache_t sw_shadow_cache;
+#endif
+#if LV_DRAW_SW_COMPLEX
+    lv_draw_sw_mask_radius_circle_dsc_arr_t sw_circle_cache;
+#endif
+
+#if LV_USE_LOG
+    lv_log_print_g_cb_t custom_log_print_cb;
+#endif
+
+#if LV_USE_LOG && LV_LOG_USE_TIMESTAMP
+    uint32_t log_last_log_time;
+#endif
+
+#if LV_USE_THEME_SIMPLE
+    void * theme_simple;
+#endif
+
+#if LV_USE_THEME_DEFAULT
+    void * theme_default;
+#endif
+
+#if LV_USE_THEME_MONO
+    void * theme_mono;
+#endif
+
+#if LV_USE_STDLIB_MALLOC == LV_STDLIB_BUILTIN
+    lv_tlsf_state_t tlsf_state;
+#endif
+
+    lv_ll_t fsdrv_ll;
+#if LV_USE_FS_STDIO != '\0'
+    lv_fs_drv_t stdio_fs_drv;
+#endif
+#if LV_USE_FS_POSIX
+    lv_fs_drv_t posix_fs_drv;
+#endif
+
+#if LV_USE_FS_FATFS
+    lv_fs_drv_t fatfs_fs_drv;
+#endif
+
+#if LV_USE_FS_WIN32 != '\0'
+    lv_fs_drv_t win32_fs_drv;
+#endif
+
+#if LV_USE_FS_UEFI
+    lv_fs_drv_t uefi_fs_drv;
+#endif
+
+#if LV_USE_FS_LITTLEFS
+    lv_fs_drv_t littlefs_fs_drv;
+#endif
+
+#if LV_USE_FS_ARDUINO_ESP_LITTLEFS
+    lv_fs_drv_t arduino_esp_littlefs_fs_drv;
+#endif
+
+#if LV_USE_FS_ARDUINO_SD
+    lv_fs_drv_t arduino_sd_fs_drv;
+#endif
+
+#if LV_USE_FS_FROGFS
+    lv_fs_drv_t frogfs_fs_drv;
+#endif
+
+#if LV_USE_FREETYPE
+    struct _lv_freetype_context_t * ft_context;
+#endif
+
+#if LV_USE_FONT_COMPRESSED
+    lv_font_fmt_rle_t font_fmt_rle;
+#endif
+
+#if LV_USE_SPAN != 0
+    struct _snippet_stack * span_snippet_stack;
+#endif
+
+#if LV_USE_PROFILER && LV_USE_PROFILER_BUILTIN
+    struct _lv_profiler_builtin_ctx_t * profiler_context;
+#endif
+
+
+#if LV_USE_FILE_EXPLORER
+    lv_style_t file_explorer_quick_access_style;
+    size_t file_explorer_count;
+#endif
+
+#if LV_USE_MEM_MONITOR
+    lv_sysmon_backend_data_t sysmon_mem;
+#endif
+
+#if LV_USE_IME_PINYIN != 0
+    size_t ime_cand_len;
+#endif
+
+#if LV_USE_OBJ_ID_BUILTIN
+    void * objid_array;
+    uint32_t objid_count;
+#endif
+
+#if LV_USE_TEST
+    lv_test_state_t test_state;
+#endif
+
+#if LV_USE_TRANSLATION
+    lv_ll_t translation_packs_ll;
+    const char * translation_selected_lang;
+#endif
+
+#if LV_USE_NUTTX
+    struct _lv_nuttx_ctx_t * nuttx_ctx;
+#endif
+
+#if LV_USE_OS != LV_OS_NONE
+    lv_mutex_t lv_general_mutex;
+#endif
+
+#if defined(__linux__)
+    lv_linux_proc_stat_t linux_last_proc_stat;
+#if LV_SYSMON_PROC_IDLE_AVAILABLE
+    uint64_t linux_last_self_proc_time_ticks;
+    lv_linux_proc_stat_t linux_last_system_total_ticks_stat;
+#endif
+#endif
+
+#if LV_USE_OS == LV_OS_FREERTOS
+    uint32_t freertos_idle_time_sum;
+    uint32_t freertos_non_idle_time_sum;
+    uint32_t freertos_task_switch_timestamp;
+    bool freertos_idle_task_running;
+#endif
+
+#if LV_USE_EVDEV
+    lv_evdev_discovery_t * evdev_discovery;
+#endif
+
+#if LV_USE_XML
+    const char * xml_path_prefix;
+    uint32_t lv_event_xml_store_timeline;
+    lv_ll_t xml_loads;
+#endif
+
+#if LV_USE_DRAW_EVE
+    lv_draw_eve_unit_t * draw_eve_unit;
+#endif
+} lv_global_t;
+
+/**********************
+ *      MACROS
+ **********************/
+
+#if LV_ENABLE_GLOBAL_CUSTOM
+#include LV_GLOBAL_CUSTOM_INCLUDE
+
+#ifndef LV_GLOBAL_CUSTOM
+#define LV_GLOBAL_CUSTOM() lv_global_default()
+#endif
+#define LV_GLOBAL_DEFAULT() LV_GLOBAL_CUSTOM()
+#else
+LV_ATTRIBUTE_EXTERN_DATA extern lv_global_t lv_global;
+#define LV_GLOBAL_DEFAULT() (&lv_global)
+#endif
+
+/**********************
+ * GLOBAL PROTOTYPES
+ **********************/
+#if LV_ENABLE_GLOBAL_CUSTOM
+/**
+ * Get the default global object for current thread
+ * @return  pointer to the default global object
+ */
+lv_global_t * lv_global_default(void);
+#endif
+#ifdef __cplusplus
+} /*extern "C"*/
+#endif
+
+#endif /*LV_GLOBAL_H*/

+ 530 - 0
components/easylvgl/lvgl9/src/core/lv_group.c

@@ -0,0 +1,530 @@
+/**
+ * @file lv_group.c
+ *
+ */
+
+/*********************
+ *      INCLUDES
+ *********************/
+#include "lv_group_private.h"
+#include "../core/lv_obj_private.h"
+#include "../core/lv_global.h"
+#include "../indev/lv_indev.h"
+#include "../misc/lv_types.h"
+
+/*********************
+ *      DEFINES
+ *********************/
+#define default_group LV_GLOBAL_DEFAULT()->group_default
+#define group_ll_p &(LV_GLOBAL_DEFAULT()->group_ll)
+
+/**********************
+ *      TYPEDEFS
+ **********************/
+
+/**********************
+ *  STATIC PROTOTYPES
+ **********************/
+static bool focus_next_core(lv_group_t * group, void * (*begin)(const lv_ll_t *),
+                            void * (*move)(const lv_ll_t *, const void *));
+static void lv_group_refocus(lv_group_t * g);
+static lv_indev_t * get_indev(const lv_group_t * g);
+
+/**********************
+ *  STATIC VARIABLES
+ **********************/
+
+/**********************
+ *      MACROS
+ **********************/
+
+/**********************
+ *   GLOBAL FUNCTIONS
+ **********************/
+
+void lv_group_init(void)
+{
+    lv_ll_init(group_ll_p, sizeof(lv_group_t));
+}
+
+void lv_group_deinit(void)
+{
+    lv_ll_clear(group_ll_p);
+}
+
+lv_group_t * lv_group_create(void)
+{
+    lv_group_t * group = lv_ll_ins_head(group_ll_p);
+    LV_ASSERT_MALLOC(group);
+    if(group == NULL) return NULL;
+    lv_ll_init(&group->obj_ll, sizeof(lv_obj_t *));
+
+    group->obj_focus      = NULL;
+    group->frozen         = 0;
+    group->focus_cb       = NULL;
+    group->edge_cb        = NULL;
+    group->editing        = 0;
+    group->refocus_policy = LV_GROUP_REFOCUS_POLICY_PREV;
+    group->wrap           = 1;
+    group->user_data      = NULL;
+
+    return group;
+}
+
+void lv_group_delete(lv_group_t * group)
+{
+    /*Defocus the currently focused object*/
+    LV_ASSERT_NULL(group);
+    if(group->obj_focus != NULL) {
+        lv_obj_send_event(*group->obj_focus, LV_EVENT_DEFOCUSED, get_indev(group));
+        lv_obj_invalidate(*group->obj_focus);
+    }
+
+    /*Remove the objects from the group*/
+    lv_obj_t ** obj;
+    LV_LL_READ(&group->obj_ll, obj) {
+        if((*obj)->spec_attr)(*obj)->spec_attr->group_p = NULL;
+    }
+
+    /*Remove the group from any indev devices */
+    lv_indev_t * indev = lv_indev_get_next(NULL);
+    while(indev) {
+        if(lv_indev_get_group(indev) == group) {
+            lv_indev_set_group(indev, NULL);
+        }
+        indev = lv_indev_get_next(indev);
+    }
+
+    /*If the group is the default group, set the default group as NULL*/
+    if(group == lv_group_get_default()) lv_group_set_default(NULL);
+
+    lv_ll_clear(&(group->obj_ll));
+    lv_ll_remove(group_ll_p, group);
+    lv_free(group);
+}
+
+void lv_group_set_default(lv_group_t * group)
+{
+    default_group = group;
+}
+
+lv_group_t * lv_group_get_default(void)
+{
+    return default_group;
+}
+
+void lv_group_add_obj(lv_group_t * group, lv_obj_t * obj)
+{
+    if(group == NULL) return;
+
+    LV_LOG_TRACE("begin");
+
+    /*Be sure the object is removed from its current group*/
+    lv_group_remove_obj(obj);
+
+    if(obj->spec_attr == NULL) lv_obj_allocate_spec_attr(obj);
+    obj->spec_attr->group_p = group;
+
+    lv_obj_t ** next = lv_ll_ins_tail(&group->obj_ll);
+    LV_ASSERT_MALLOC(next);
+    if(next == NULL) return;
+    *next = obj;
+
+    /*If the head and the tail is equal then there is only one object in the linked list.
+     *In this case automatically activate it*/
+    if(lv_ll_get_head(&group->obj_ll) == next) {
+        lv_group_refocus(group);
+    }
+
+    LV_LOG_TRACE("finished");
+}
+
+void lv_group_swap_obj(lv_obj_t * obj1, lv_obj_t * obj2)
+{
+    lv_group_t * g1 = lv_obj_get_group(obj1);
+    lv_group_t * g2 = lv_obj_get_group(obj2);
+    if(g1 != g2) return;
+    if(g1 == NULL) return;
+
+    /*Do not add the object twice*/
+    lv_obj_t ** obj_i;
+    LV_LL_READ(&g1->obj_ll, obj_i) {
+        if((*obj_i) == obj1)(*obj_i) = obj2;
+        else if((*obj_i) == obj2)(*obj_i) = obj1;
+    }
+
+    lv_obj_t * focused = lv_group_get_focused(g1);
+    if(focused == obj1) lv_group_focus_obj(obj2);
+    else if(focused == obj2) lv_group_focus_obj(obj1);
+
+}
+
+void lv_group_remove_obj(lv_obj_t * obj)
+{
+    lv_group_t * g = lv_obj_get_group(obj);
+    if(g == NULL) return;
+
+    LV_LOG_TRACE("begin");
+
+    /*Focus on the next object*/
+    if(g->obj_focus && *g->obj_focus == obj) {
+        if(g->frozen) g->frozen = 0;
+
+        /*If this is the only object in the group then focus to nothing.*/
+        if(lv_ll_get_head(&g->obj_ll) == g->obj_focus && lv_ll_get_tail(&g->obj_ll) == g->obj_focus) {
+            lv_obj_send_event(*g->obj_focus, LV_EVENT_DEFOCUSED, get_indev(g));
+        }
+        /*If there more objects in the group then focus to the next/prev object*/
+        else {
+            lv_group_refocus(g);
+        }
+    }
+
+    /*If the focuses object is still the same then it was the only object in the group but it will
+     *be deleted. Set the `obj_focus` to NULL to get back to the initial state of the group with
+     *zero objects*/
+    if(g->obj_focus && *g->obj_focus == obj) {
+        g->obj_focus = NULL;
+    }
+
+    /*Search the object and remove it from its group*/
+    lv_obj_t ** i;
+    LV_LL_READ(&g->obj_ll, i) {
+        if(*i == obj) {
+            lv_ll_remove(&g->obj_ll, i);
+            lv_free(i);
+            if(obj->spec_attr) obj->spec_attr->group_p = NULL;
+            break;
+        }
+    }
+    LV_LOG_TRACE("finished");
+}
+
+void lv_group_remove_all_objs(lv_group_t * group)
+{
+    LV_ASSERT_NULL(group);
+
+    /*Defocus the currently focused object*/
+    if(group->obj_focus != NULL) {
+        lv_obj_send_event(*group->obj_focus, LV_EVENT_DEFOCUSED, get_indev(group));
+        lv_obj_invalidate(*group->obj_focus);
+        group->obj_focus = NULL;
+    }
+
+    /*Remove the objects from the group*/
+    lv_obj_t ** obj;
+    LV_LL_READ(&group->obj_ll, obj) {
+        if((*obj)->spec_attr)(*obj)->spec_attr->group_p = NULL;
+    }
+
+    lv_ll_clear(&(group->obj_ll));
+}
+
+void lv_group_focus_obj(lv_obj_t * obj)
+{
+    if(obj == NULL) return;
+    lv_group_t * g = lv_obj_get_group(obj);
+    if(g == NULL) return;
+
+    if(g->frozen != 0) return;
+
+    /*On defocus edit mode must be leaved*/
+    lv_group_set_editing(g, false);
+
+    lv_obj_t ** i;
+    LV_LL_READ(&g->obj_ll, i) {
+        if(*i == obj) {
+            if(g->obj_focus != NULL && obj != *g->obj_focus) {  /*Do not defocus if the same object needs to be focused again*/
+                lv_result_t res = lv_obj_send_event(*g->obj_focus, LV_EVENT_DEFOCUSED, get_indev(g));
+                if(res != LV_RESULT_OK) return;
+                lv_obj_invalidate(*g->obj_focus);
+            }
+
+            g->obj_focus = i;
+
+            if(g->obj_focus != NULL) {
+                if(g->focus_cb) g->focus_cb(g);
+                lv_result_t res = lv_obj_send_event(*g->obj_focus, LV_EVENT_FOCUSED, get_indev(g));
+                if(res != LV_RESULT_OK) return;
+                lv_obj_invalidate(*g->obj_focus);
+            }
+            break;
+        }
+    }
+}
+
+void lv_group_focus_next(lv_group_t * group)
+{
+    LV_ASSERT_NULL(group);
+
+    bool focus_changed = focus_next_core(group, lv_ll_get_head, lv_ll_get_next);
+    if(group->edge_cb) {
+        if(!focus_changed)
+            group->edge_cb(group, true);
+    }
+}
+
+void lv_group_focus_prev(lv_group_t * group)
+{
+    LV_ASSERT_NULL(group);
+
+    bool focus_changed = focus_next_core(group, lv_ll_get_tail, lv_ll_get_prev);
+    if(group->edge_cb) {
+        if(!focus_changed)
+            group->edge_cb(group, false);
+    }
+}
+
+void lv_group_focus_freeze(lv_group_t * group, bool en)
+{
+    LV_ASSERT_NULL(group);
+
+    if(en == false) group->frozen = 0;
+    else group->frozen = 1;
+}
+
+lv_result_t lv_group_send_data(lv_group_t * group, uint32_t c)
+{
+    LV_ASSERT_NULL(group);
+
+    lv_obj_t * act = lv_group_get_focused(group);
+    if(act == NULL) return LV_RESULT_OK;
+
+    if(lv_obj_has_state(act, LV_STATE_DISABLED)) return LV_RESULT_OK;
+
+    return lv_obj_send_event(act, LV_EVENT_KEY, &c);
+}
+
+void lv_group_set_focus_cb(lv_group_t * group, lv_group_focus_cb_t focus_cb)
+{
+    if(group == NULL) return;
+
+    group->focus_cb = focus_cb;
+}
+
+void lv_group_set_edge_cb(lv_group_t * group, lv_group_edge_cb_t edge_cb)
+{
+    LV_ASSERT_NULL(group);
+
+    group->edge_cb = edge_cb;
+}
+
+void lv_group_set_editing(lv_group_t * group, bool edit)
+{
+    LV_ASSERT_NULL(group);
+    uint8_t en_val = edit ? 1 : 0;
+
+    if(en_val == group->editing) return; /*Do not set the same mode again*/
+
+    group->editing     = en_val;
+    lv_obj_t * focused = lv_group_get_focused(group);
+
+    if(focused) {
+        lv_result_t res = lv_obj_send_event(*group->obj_focus, LV_EVENT_FOCUSED, get_indev(group));
+        if(res != LV_RESULT_OK) return;
+
+        lv_obj_invalidate(focused);
+    }
+}
+
+void lv_group_set_refocus_policy(lv_group_t * group, lv_group_refocus_policy_t policy)
+{
+    LV_ASSERT_NULL(group);
+    group->refocus_policy = policy & 0x01;
+}
+
+void lv_group_set_wrap(lv_group_t * group, bool en)
+{
+    LV_ASSERT_NULL(group);
+    group->wrap = en ? 1 : 0;
+}
+
+lv_obj_t * lv_group_get_focused(const lv_group_t * group)
+{
+    if(!group) return NULL;
+    if(group->obj_focus == NULL) return NULL;
+
+    return *group->obj_focus;
+}
+
+lv_group_focus_cb_t lv_group_get_focus_cb(const lv_group_t * group)
+{
+    if(!group) return NULL;
+    return group->focus_cb;
+}
+
+lv_group_edge_cb_t lv_group_get_edge_cb(const lv_group_t * group)
+{
+    if(!group) return NULL;
+    return group->edge_cb;
+}
+
+bool lv_group_get_editing(const lv_group_t * group)
+{
+    if(!group) return false;
+    return group->editing;
+}
+
+bool lv_group_get_wrap(lv_group_t * group)
+{
+    if(!group) return false;
+    return group->wrap;
+}
+
+uint32_t lv_group_get_obj_count(lv_group_t * group)
+{
+    LV_ASSERT_NULL(group);
+    return lv_ll_get_len(&group->obj_ll);
+}
+
+lv_obj_t * lv_group_get_obj_by_index(lv_group_t * group, uint32_t index)
+{
+    uint32_t len = 0;
+    lv_obj_t ** obj;
+
+    LV_LL_READ(&group->obj_ll, obj) {
+        if(len == index) {
+            return *obj;
+        }
+        len++;
+    }
+    return NULL;
+}
+
+uint32_t lv_group_get_count(void)
+{
+    return lv_ll_get_len(group_ll_p);
+}
+
+lv_group_t  * lv_group_by_index(uint32_t index)
+{
+    uint32_t len = 0;
+    lv_group_t * group;
+
+    LV_LL_READ_BACK(group_ll_p, group) {
+        if(len == index) {
+            return group;
+        }
+        len++;
+    }
+
+    return NULL;
+}
+/**********************
+ *   STATIC FUNCTIONS
+ **********************/
+
+static void lv_group_refocus(lv_group_t * g)
+{
+    /*Refocus must temporarily allow wrapping to work correctly*/
+    uint8_t temp_wrap = g->wrap;
+    g->wrap           = 1;
+
+    if(g->refocus_policy == LV_GROUP_REFOCUS_POLICY_NEXT)
+        lv_group_focus_next(g);
+    else if(g->refocus_policy == LV_GROUP_REFOCUS_POLICY_PREV)
+        lv_group_focus_prev(g);
+    /*Restore wrap property*/
+    g->wrap = temp_wrap;
+}
+
+static bool focus_next_core(lv_group_t * group, void * (*begin)(const lv_ll_t *),
+                            void * (*move)(const lv_ll_t *, const void *))
+{
+    bool focus_changed = false;
+    if(group->frozen) return focus_changed;
+
+    lv_obj_t ** obj_next     = group->obj_focus;
+    lv_obj_t ** obj_sentinel = NULL;
+    bool can_move            = true;
+    bool can_begin           = true;
+
+    for(;;) {
+        if(obj_next == NULL) {
+            if(group->wrap || obj_sentinel == NULL) {
+                if(!can_begin) return focus_changed;
+                obj_next  = begin(&group->obj_ll);
+                can_move  = false;
+                can_begin = false;
+            }
+            else {
+                /*Currently focused object is the last/first in the group, keep it that way*/
+                return focus_changed;
+            }
+        }
+
+        if(obj_sentinel == NULL) {
+            obj_sentinel = obj_next;
+            if(obj_sentinel == NULL) return focus_changed; /*Group is empty*/
+        }
+
+        if(can_move) {
+            obj_next = move(&group->obj_ll, obj_next);
+
+            /*Give up if we walked the entire list and haven't found another visible object*/
+            if(obj_next == obj_sentinel) return focus_changed;
+        }
+
+        can_move = true;
+
+        if(obj_next == NULL) continue;
+        if(lv_obj_get_state(*obj_next) & LV_STATE_DISABLED) continue;
+
+        /*Hidden objects don't receive focus.
+         *If any parent is hidden, the object is also hidden)*/
+        lv_obj_t * parent = *obj_next;
+        while(parent) {
+            if(lv_obj_has_flag(parent, LV_OBJ_FLAG_HIDDEN)) break;
+            parent = lv_obj_get_parent(parent);
+        }
+
+        if(parent && lv_obj_has_flag(parent, LV_OBJ_FLAG_HIDDEN)) continue;
+
+        /*If we got her a good candidate is found*/
+        break;
+    }
+
+    if(obj_next == group->obj_focus) return focus_changed; /*There's only one visible object and it's already focused*/
+
+    if(group->obj_focus) {
+        lv_result_t res = lv_obj_send_event(*group->obj_focus, LV_EVENT_DEFOCUSED, get_indev(group));
+        if(res != LV_RESULT_OK) return focus_changed;
+        lv_obj_invalidate(*group->obj_focus);
+    }
+
+    group->obj_focus = obj_next;
+
+    lv_result_t res = lv_obj_send_event(*group->obj_focus, LV_EVENT_FOCUSED, get_indev(group));
+    if(res != LV_RESULT_OK) return focus_changed;
+
+    lv_obj_invalidate(*group->obj_focus);
+
+    if(group->focus_cb) group->focus_cb(group);
+    focus_changed = true;
+    return focus_changed;
+}
+
+/**
+ * Find an indev preferably with POINTER type (because it's the most generic) that uses the given group.
+ * In other words, find an indev, that is related to the given group.
+ * In the worst case simply return the latest indev
+ * @param g     a group the find in the indevs
+ * @return      the suggested indev
+ */
+static lv_indev_t * get_indev(const lv_group_t * g)
+{
+    lv_indev_t * indev_guess = NULL;
+    lv_indev_t * indev = lv_indev_get_next(NULL);
+
+    while(indev) {
+        lv_indev_type_t indev_type = lv_indev_get_type(indev);
+        /*Prefer POINTER*/
+        if(indev_type == LV_INDEV_TYPE_POINTER) return indev;
+        if(lv_indev_get_group(indev) == g) {
+            indev_guess = indev;
+        }
+        indev = lv_indev_get_next(indev);
+    }
+
+    return indev_guess;
+}

+ 248 - 0
components/easylvgl/lvgl9/src/core/lv_group.h

@@ -0,0 +1,248 @@
+/**
+ * @file lv_group.h
+ *
+ */
+
+#ifndef LV_GROUP_H
+#define LV_GROUP_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/*********************
+ *      INCLUDES
+ *********************/
+#include "../lv_conf_internal.h"
+
+#include "../misc/lv_types.h"
+#include "../misc/lv_ll.h"
+
+/*********************
+ *      DEFINES
+ *********************/
+/** Predefined keys to control which Widget has focus via lv_group_send(group, c) */
+typedef enum {
+    LV_KEY_UP        = 17,  /*0x11*/
+    LV_KEY_DOWN      = 18,  /*0x12*/
+    LV_KEY_RIGHT     = 19,  /*0x13*/
+    LV_KEY_LEFT      = 20,  /*0x14*/
+    LV_KEY_ESC       = 27,  /*0x1B*/
+    LV_KEY_DEL       = 127, /*0x7F*/
+    LV_KEY_BACKSPACE = 8,   /*0x08*/
+    LV_KEY_ENTER     = 10,  /*0x0A, '\n'*/
+    LV_KEY_NEXT      = 9,   /*0x09, '\t'*/
+    LV_KEY_PREV      = 11,  /*0x0B, '*/
+    LV_KEY_HOME      = 2,   /*0x02, STX*/
+    LV_KEY_END       = 3,   /*0x03, ETX*/
+} lv_key_t;
+
+/**********************
+ *      TYPEDEFS
+ **********************/
+
+typedef void (*lv_group_focus_cb_t)(lv_group_t *);
+typedef void (*lv_group_edge_cb_t)(lv_group_t *, bool);
+
+typedef enum {
+    LV_GROUP_REFOCUS_POLICY_NEXT = 0,
+    LV_GROUP_REFOCUS_POLICY_PREV = 1
+} lv_group_refocus_policy_t;
+
+/**********************
+ * GLOBAL PROTOTYPES
+ **********************/
+
+/**
+ * Create new Widget group.
+ * @return          pointer to the new Widget group
+ */
+lv_group_t * lv_group_create(void);
+
+/**
+ * Delete group object.
+ * @param group     pointer to a group
+ */
+void lv_group_delete(lv_group_t * group);
+
+/**
+ * Set default group. New Widgets will be added to this group if it's enabled in
+ * their class with `add_to_def_group = true`.
+ * @param group     pointer to a group (can be `NULL`)
+ */
+void lv_group_set_default(lv_group_t * group);
+
+/**
+ * Get default group.
+ * @return          pointer to the default group
+ */
+lv_group_t * lv_group_get_default(void);
+
+/**
+ * Add an Widget to group.
+ * @param group     pointer to a group
+ * @param obj       pointer to a Widget to add
+ */
+void lv_group_add_obj(lv_group_t * group, lv_obj_t * obj);
+
+/**
+ * Swap 2 Widgets in group.  Widgets must be in the same group.
+ * @param obj1  pointer to a Widget
+ * @param obj2  pointer to another Widget
+ */
+void lv_group_swap_obj(lv_obj_t * obj1, lv_obj_t * obj2);
+
+/**
+ * Remove a Widget from its group.
+ * @param obj       pointer to Widget to remove
+ */
+void lv_group_remove_obj(lv_obj_t * obj);
+
+/**
+ * Remove all Widgets from a group.
+ * @param group     pointer to a group
+ */
+void lv_group_remove_all_objs(lv_group_t * group);
+
+/**
+ * Focus on a Widget (defocus the current).
+ * @param obj       pointer to Widget to focus on
+ */
+void lv_group_focus_obj(lv_obj_t * obj);
+
+/**
+ * Focus on next Widget in a group (defocus the current).
+ * @param group     pointer to a group
+ */
+void lv_group_focus_next(lv_group_t * group);
+
+/**
+ * Focus on previous Widget in a group (defocus the current).
+ * @param group     pointer to a group
+ */
+void lv_group_focus_prev(lv_group_t * group);
+
+/**
+ * Do not allow changing focus from current Widget.
+ * @param group     pointer to a group
+ * @param en        true: freeze, false: release freezing (normal mode)
+ */
+void lv_group_focus_freeze(lv_group_t * group, bool en);
+
+/**
+ * Send a control character to Widget that has focus in a group.
+ * @param group     pointer to a group
+ * @param c         a character (use LV_KEY_.. to navigate)
+ * @return          result of Widget with focus in group.
+ */
+lv_result_t lv_group_send_data(lv_group_t * group, uint32_t c);
+
+/**
+ * Set a function for a group which will be called when a new Widget has focus.
+ * @param group         pointer to a group
+ * @param focus_cb      the call back function or NULL if unused
+ */
+void lv_group_set_focus_cb(lv_group_t * group, lv_group_focus_cb_t focus_cb);
+
+/**
+ * Set a function for a group which will be called when a focus edge is reached
+ * @param group         pointer to a group
+ * @param edge_cb      the call back function or NULL if unused
+ */
+void lv_group_set_edge_cb(lv_group_t * group, lv_group_edge_cb_t edge_cb);
+
+/**
+ * Set whether the next or previous Widget in a group gets focus when Widget that has
+ * focus is deleted.
+ * @param group         pointer to a group
+ * @param policy        new refocus policy enum
+ */
+void lv_group_set_refocus_policy(lv_group_t * group, lv_group_refocus_policy_t policy);
+
+/**
+ * Manually set the current mode (edit or navigate).
+ * @param group         pointer to group
+ * @param edit          true: edit mode; false: navigate mode
+ */
+void lv_group_set_editing(lv_group_t * group, bool edit);
+
+/**
+ * Set whether moving focus to next/previous Widget will allow wrapping from
+ * first->last or last->first Widget.
+ * @param group         pointer to group
+ * @param               en true: wrapping enabled; false: wrapping disabled
+ */
+void lv_group_set_wrap(lv_group_t * group, bool en);
+
+/**
+ * Get Widget that has focus, or NULL if there isn't one.
+ * @param group         pointer to a group
+ * @return              pointer to Widget with focus
+ */
+lv_obj_t * lv_group_get_focused(const lv_group_t * group);
+
+/**
+ * Get focus callback function of a group.
+ * @param group pointer to a group
+ * @return the call back function or NULL if not set
+ */
+lv_group_focus_cb_t lv_group_get_focus_cb(const lv_group_t * group);
+
+/**
+ * Get edge callback function of a group.
+ * @param group pointer to a group
+ * @return the call back function or NULL if not set
+ */
+lv_group_edge_cb_t lv_group_get_edge_cb(const lv_group_t * group);
+
+/**
+ * Get current mode (edit or navigate).
+ * @param group         pointer to group
+ * @return              true: edit mode; false: navigate mode
+ */
+bool lv_group_get_editing(const lv_group_t * group);
+
+/**
+ * Get whether moving focus to next/previous Widget will allow wrapping from
+ * first->last or last->first Widget.
+ * @param group         pointer to group
+ */
+bool lv_group_get_wrap(lv_group_t * group);
+
+/**
+ * Get number of Widgets in group.
+ * @param group         pointer to a group
+ * @return              number of Widgets in the group
+ */
+uint32_t lv_group_get_obj_count(lv_group_t * group);
+
+/**
+ * Get nth Widget within group.
+ * @param group         pointer to a group
+ * @param index         index of Widget within the group
+ * @return              pointer to Widget
+ */
+lv_obj_t * lv_group_get_obj_by_index(lv_group_t * group, uint32_t index);
+
+/**
+ * Get the number of groups.
+ * @return              number of groups
+ */
+uint32_t lv_group_get_count(void);
+
+/**
+ * Get a group by its index.
+ * @param index         index of the group
+ * @return              pointer to the group
+ */
+lv_group_t  * lv_group_by_index(uint32_t index);
+
+/**********************
+ *      MACROS
+ **********************/
+
+#ifdef __cplusplus
+} /*extern "C"*/
+#endif
+
+#endif /*LV_GROUP_H*/

+ 75 - 0
components/easylvgl/lvgl9/src/core/lv_group_private.h

@@ -0,0 +1,75 @@
+/**
+ * @file lv_group_private.h
+ *
+ */
+
+#ifndef LV_GROUP_PRIVATE_H
+#define LV_GROUP_PRIVATE_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/*********************
+ *      INCLUDES
+ *********************/
+
+#include "lv_group.h"
+
+/*********************
+ *      DEFINES
+ *********************/
+
+/**********************
+ *      TYPEDEFS
+ **********************/
+
+/**
+ * Groups can be used to logically hold objects so that they can be individually focused.
+ * They are NOT for laying out objects on a screen (try layouts for that).
+ */
+struct _lv_group_t {
+    lv_ll_t obj_ll;        /**< Linked list to store the objects in the group*/
+    lv_obj_t ** obj_focus; /**< The object in focus*/
+
+    lv_group_focus_cb_t focus_cb;              /**< A function to call when a new object is focused (optional)*/
+    lv_group_edge_cb_t  edge_cb;               /**< A function to call when an edge is reached, no more focus
+                                                    targets are available in this direction (to allow edge feedback
+                                                    like a sound or a scroll bounce) */
+
+    void * user_data;
+
+    uint8_t frozen : 1;         /**< 1: can't focus to new object*/
+    uint8_t editing : 1;        /**< 1: Edit mode, 0: Navigate mode*/
+    uint8_t refocus_policy : 1; /**< 1: Focus prev if focused on deletion. 0: Focus next if focused on
+                                   deletion.*/
+    uint8_t wrap : 1;           /**< 1: Focus next/prev can wrap at end of list. 0: Focus next/prev stops at end
+                                   of list.*/
+};
+
+
+/**********************
+ * GLOBAL PROTOTYPES
+ **********************/
+
+/**
+ * Init the group module
+ * @remarks Internal function, do not call directly.
+ */
+void lv_group_init(void);
+
+/**
+ * Deinit the group module
+ * @remarks Internal function, do not call directly.
+ */
+void lv_group_deinit(void);
+
+/**********************
+ *      MACROS
+ **********************/
+
+#ifdef __cplusplus
+} /*extern "C"*/
+#endif
+
+#endif /*LV_GROUP_PRIVATE_H*/

+ 1229 - 0
components/easylvgl/lvgl9/src/core/lv_obj.c

@@ -0,0 +1,1229 @@
+/**
+ * @file lv_obj.c
+ *
+ */
+
+/*********************
+ *      INCLUDES
+ *********************/
+#include "lv_obj_private.h"
+#include "../misc/lv_event_private.h"
+#include "../misc/lv_area_private.h"
+#include "lv_obj_style_private.h"
+#include "lv_obj_event_private.h"
+#include "lv_obj_class_private.h"
+#include "../indev/lv_indev.h"
+#include "../indev/lv_indev_private.h"
+#include "lv_refr.h"
+#include "lv_group.h"
+#include "../display/lv_display.h"
+#include "../display/lv_display_private.h"
+#include "../themes/lv_theme.h"
+#include "../misc/lv_assert.h"
+#include "../misc/lv_math.h"
+#include "../misc/lv_log.h"
+#include "../misc/lv_types.h"
+#include "../misc/lv_anim_timeline.h"
+#include "../tick/lv_tick.h"
+#include "../stdlib/lv_string.h"
+#include "lv_obj_draw_private.h"
+
+/*********************
+ *      DEFINES
+ *********************/
+#define MY_CLASS (&lv_obj_class)
+#define LV_OBJ_DEF_WIDTH    (LV_DPX(100))
+#define LV_OBJ_DEF_HEIGHT   (LV_DPX(50))
+#define STYLE_TRANSITION_MAX 32
+
+/**********************
+ *      TYPEDEFS
+ **********************/
+
+typedef struct {
+    lv_screen_load_anim_t anim_type;
+    uint32_t duration;
+    uint32_t delay;
+    union {
+        lv_obj_t * screen;
+        lv_screen_create_cb_t create_cb;
+    } target;
+} screen_load_anim_dsc_t;
+
+typedef struct {
+    lv_anim_timeline_t * at;
+    uint32_t delay;
+    bool reverse;
+} timeline_play_dsc_t;
+
+/**********************
+ *  STATIC PROTOTYPES
+ **********************/
+static void lv_obj_constructor(const lv_obj_class_t * class_p, lv_obj_t * obj);
+static void lv_obj_destructor(const lv_obj_class_t * class_p, lv_obj_t * obj);
+static void lv_obj_draw(lv_event_t * e);
+static void lv_obj_event(const lv_obj_class_t * class_p, lv_event_t * e);
+static void draw_scrollbar(lv_obj_t * obj, lv_layer_t * layer);
+static lv_result_t scrollbar_init_draw_dsc(lv_obj_t * obj, lv_draw_rect_dsc_t * dsc);
+static bool obj_valid_child(const lv_obj_t * parent, const lv_obj_t * obj_to_find);
+static void update_obj_state(lv_obj_t * obj, lv_state_t new_state);
+static void lv_obj_children_add_state(lv_obj_t * obj, lv_state_t state);
+static void lv_obj_children_remove_state(lv_obj_t * obj, lv_state_t state);
+static void null_on_delete_cb(lv_event_t * e);
+static void screen_load_on_trigger_event_cb(lv_event_t * e);
+static void screen_create_on_trigger_event_cb(lv_event_t * e);
+static void play_timeline_on_trigger_event_cb(lv_event_t * e);
+static void delete_on_screen_unloaded_event_cb(lv_event_t * e);
+
+#if LV_USE_OBJ_PROPERTY
+    static lv_result_t lv_obj_set_any(lv_obj_t *, lv_prop_id_t, const lv_property_t *);
+    static lv_result_t lv_obj_get_any(const lv_obj_t *, lv_prop_id_t, lv_property_t *);
+#endif
+
+/**********************
+ *  STATIC VARIABLES
+ **********************/
+#if LV_USE_OBJ_PROPERTY
+static const lv_property_ops_t properties[] = {
+    {
+        .id = LV_PROPERTY_OBJ_PARENT,
+        .setter = lv_obj_set_parent,
+        .getter = lv_obj_get_parent,
+    },
+    {
+        .id = LV_PROPERTY_OBJ_X,
+        .setter = lv_obj_set_x,
+        .getter = lv_obj_get_x,
+    },
+    {
+        .id = LV_PROPERTY_OBJ_Y,
+        .setter = lv_obj_set_y,
+        .getter = lv_obj_get_y,
+    },
+    {
+        .id = LV_PROPERTY_OBJ_W,
+        .setter = lv_obj_set_width,
+        .getter = lv_obj_get_width,
+    },
+    {
+        .id = LV_PROPERTY_OBJ_H,
+        .setter = lv_obj_set_height,
+        .getter = lv_obj_get_height,
+    },
+    {
+        .id = LV_PROPERTY_OBJ_CONTENT_WIDTH,
+        .setter = lv_obj_set_content_width,
+        .getter = lv_obj_get_content_width,
+    },
+    {
+        .id = LV_PROPERTY_OBJ_CONTENT_HEIGHT,
+        .setter = lv_obj_set_content_height,
+        .getter = lv_obj_get_content_height,
+    },
+    {
+        .id = LV_PROPERTY_OBJ_LAYOUT,
+        .setter = lv_obj_set_layout,
+    },
+    {
+        .id = LV_PROPERTY_OBJ_ALIGN,
+        .setter = lv_obj_set_align,
+    },
+    {
+        .id = LV_PROPERTY_OBJ_SCROLLBAR_MODE,
+        .setter = lv_obj_set_scrollbar_mode,
+        .getter = lv_obj_get_scrollbar_mode,
+    },
+    {
+        .id = LV_PROPERTY_OBJ_SCROLL_DIR,
+        .setter = lv_obj_set_scroll_dir,
+        .getter = lv_obj_get_scroll_dir,
+    },
+    {
+        .id = LV_PROPERTY_OBJ_SCROLL_SNAP_X,
+        .setter = lv_obj_set_scroll_snap_x,
+        .getter = lv_obj_get_scroll_snap_x,
+    },
+    {
+        .id = LV_PROPERTY_OBJ_SCROLL_SNAP_Y,
+        .setter = lv_obj_set_scroll_snap_y,
+        .getter = lv_obj_get_scroll_snap_y,
+    },
+    {
+        .id = LV_PROPERTY_OBJ_SCROLL_TOP,
+        .getter = lv_obj_get_scroll_top,
+    },
+    {
+        .id = LV_PROPERTY_OBJ_SCROLL_BOTTOM,
+        .getter = lv_obj_get_scroll_bottom,
+    },
+    {
+        .id = LV_PROPERTY_OBJ_SCROLL_LEFT,
+        .getter = lv_obj_get_scroll_left,
+    },
+    {
+        .id = LV_PROPERTY_OBJ_SCROLL_RIGHT,
+        .getter = lv_obj_get_scroll_right,
+    },
+    {
+        .id = LV_PROPERTY_OBJ_SCROLL_END,
+        .getter = lv_obj_get_scroll_end,
+    },
+    {
+        .id = LV_PROPERTY_OBJ_EXT_DRAW_SIZE,
+        .getter = lv_obj_get_ext_draw_size,
+    },
+    {
+        .id = LV_PROPERTY_OBJ_EVENT_COUNT,
+        .getter = lv_obj_get_event_count,
+    },
+    {
+        .id = LV_PROPERTY_OBJ_SCREEN,
+        .getter = lv_obj_get_screen,
+    },
+    {
+        .id = LV_PROPERTY_OBJ_DISPLAY,
+        .getter = lv_obj_get_display,
+    },
+    {
+        .id = LV_PROPERTY_OBJ_CHILD_COUNT,
+        .getter = lv_obj_get_child_count,
+    },
+    {
+        .id = LV_PROPERTY_OBJ_INDEX,
+        .getter = lv_obj_get_index,
+    },
+    {
+        .id = LV_PROPERTY_ID_ANY,
+        .setter = lv_obj_set_any,
+        .getter = lv_obj_get_any,
+    }
+};
+#endif
+
+const lv_obj_class_t lv_obj_class = {
+    .constructor_cb = lv_obj_constructor,
+    .destructor_cb = lv_obj_destructor,
+    .event_cb = lv_obj_event,
+    .width_def = LV_DPI_DEF,
+    .height_def = LV_DPI_DEF,
+    .editable = LV_OBJ_CLASS_EDITABLE_FALSE,
+    .group_def = LV_OBJ_CLASS_GROUP_DEF_FALSE,
+    .instance_size = (sizeof(lv_obj_t)),
+    .base_class = NULL,
+    .name = "lv_obj",
+#if LV_USE_OBJ_PROPERTY
+    .prop_index_start = LV_PROPERTY_OBJ_START,
+    .prop_index_end = LV_PROPERTY_OBJ_END,
+    .properties = properties,
+    .properties_count = sizeof(properties) / sizeof(properties[0]),
+
+#if LV_USE_OBJ_PROPERTY_NAME
+    .property_names = lv_obj_property_names,
+    .names_count = sizeof(lv_obj_property_names) / sizeof(lv_property_name_t),
+#endif
+
+#endif
+};
+
+/**********************
+ *      MACROS
+ **********************/
+
+/**********************
+ *   GLOBAL FUNCTIONS
+ **********************/
+
+lv_obj_t * lv_obj_create(lv_obj_t * parent)
+{
+    LV_LOG_INFO("begin");
+    lv_obj_t * obj = lv_obj_class_create_obj(MY_CLASS, parent);
+    LV_ASSERT_NULL(obj);
+    if(obj == NULL) return NULL;
+    lv_obj_class_init_obj(obj);
+    return obj;
+}
+
+/*=====================
+ * Setter functions
+ *====================*/
+
+/*-----------------
+ * Attribute set
+ *----------------*/
+
+void lv_obj_add_flag(lv_obj_t * obj, lv_obj_flag_t f)
+{
+    LV_ASSERT_OBJ(obj, MY_CLASS);
+    if(lv_obj_has_flag(obj, f)) /*Check if all flags are set*/
+        return;
+
+    bool was_on_layout = lv_obj_is_layout_positioned(obj);
+
+    /* We must invalidate the area occupied by the object before we hide it as calls to invalidate hidden objects are ignored */
+    if(f & LV_OBJ_FLAG_HIDDEN) lv_obj_invalidate(obj);
+
+    obj->flags |= f;
+
+    if(f & LV_OBJ_FLAG_HIDDEN) {
+        if(lv_obj_has_state(obj, LV_STATE_FOCUSED)) {
+            lv_group_t * group = lv_obj_get_group(obj);
+            if(group != NULL) {
+                lv_group_focus_next(group);
+                lv_obj_t * next_obj = lv_group_get_focused(group);
+                if(next_obj != NULL) {
+                    lv_obj_invalidate(next_obj);
+                }
+            }
+        }
+    }
+
+    if((was_on_layout != lv_obj_is_layout_positioned(obj)) || (f & (LV_OBJ_FLAG_LAYOUT_1 |  LV_OBJ_FLAG_LAYOUT_2))) {
+        lv_obj_mark_layout_as_dirty(lv_obj_get_parent(obj));
+        lv_obj_mark_layout_as_dirty(obj);
+    }
+
+    if(f & LV_OBJ_FLAG_SCROLLABLE) {
+        lv_area_t hor_area, ver_area;
+        lv_obj_get_scrollbar_area(obj, &hor_area, &ver_area);
+        lv_obj_invalidate_area(obj, &hor_area);
+        lv_obj_invalidate_area(obj, &ver_area);
+    }
+}
+
+void lv_obj_remove_flag(lv_obj_t * obj, lv_obj_flag_t f)
+{
+    LV_ASSERT_OBJ(obj, MY_CLASS);
+    if(!lv_obj_has_flag_any(obj, f))
+        return;
+
+    bool was_on_layout = lv_obj_is_layout_positioned(obj);
+    if(f & LV_OBJ_FLAG_SCROLLABLE) {
+        lv_area_t hor_area, ver_area;
+        lv_obj_get_scrollbar_area(obj, &hor_area, &ver_area);
+        lv_obj_invalidate_area(obj, &hor_area);
+        lv_obj_invalidate_area(obj, &ver_area);
+    }
+
+    obj->flags &= (~f);
+
+    if(f & LV_OBJ_FLAG_HIDDEN) {
+        lv_obj_invalidate(obj);
+        lv_obj_mark_layout_as_dirty(lv_obj_get_parent(obj));
+        lv_obj_mark_layout_as_dirty(obj);
+    }
+
+    if((was_on_layout != lv_obj_is_layout_positioned(obj)) || (f & (LV_OBJ_FLAG_LAYOUT_1 |  LV_OBJ_FLAG_LAYOUT_2))) {
+        lv_obj_mark_layout_as_dirty(lv_obj_get_parent(obj));
+    }
+
+}
+
+void lv_obj_set_flag(lv_obj_t * obj, lv_obj_flag_t f, bool v)
+{
+    if(v) lv_obj_add_flag(obj, f);
+    else lv_obj_remove_flag(obj, f);
+}
+
+void lv_obj_add_state(lv_obj_t * obj, lv_state_t state)
+{
+    LV_ASSERT_OBJ(obj, MY_CLASS);
+
+    lv_state_t new_state = obj->state | state;
+    if(obj->state != new_state) {
+        update_obj_state(obj, new_state);
+        if(lv_obj_has_flag(obj, LV_OBJ_FLAG_STATE_TRICKLE)) {
+            lv_obj_children_add_state(obj, state);
+        }
+    }
+}
+
+void lv_obj_remove_state(lv_obj_t * obj, lv_state_t state)
+{
+    LV_ASSERT_OBJ(obj, MY_CLASS);
+
+    lv_state_t new_state = obj->state & (~state);
+    if(obj->state != new_state) {
+        update_obj_state(obj, new_state);
+        if(lv_obj_has_flag(obj, LV_OBJ_FLAG_STATE_TRICKLE)) {
+            lv_obj_children_remove_state(obj, state);
+        }
+    }
+}
+
+void lv_obj_set_state(lv_obj_t * obj, lv_state_t state, bool v)
+{
+    if(v) lv_obj_add_state(obj, state);
+    else lv_obj_remove_state(obj, state);
+}
+
+/*=======================
+ * Getter functions
+ *======================*/
+
+bool lv_obj_has_flag(const lv_obj_t * obj, lv_obj_flag_t f)
+{
+    LV_ASSERT_OBJ(obj, MY_CLASS);
+
+    return (obj->flags & f)  == f;
+}
+
+bool lv_obj_has_flag_any(const lv_obj_t * obj, lv_obj_flag_t f)
+{
+    LV_ASSERT_OBJ(obj, MY_CLASS);
+
+    return !!(obj->flags & f);
+}
+
+lv_state_t lv_obj_get_state(const lv_obj_t * obj)
+{
+    LV_ASSERT_OBJ(obj, MY_CLASS);
+
+    return obj->state;
+}
+
+bool lv_obj_has_state(const lv_obj_t * obj, lv_state_t state)
+{
+    LV_ASSERT_OBJ(obj, MY_CLASS);
+
+    return !!(obj->state & state);
+}
+
+lv_group_t * lv_obj_get_group(const lv_obj_t * obj)
+{
+    LV_ASSERT_OBJ(obj, MY_CLASS);
+
+    if(obj->spec_attr) return obj->spec_attr->group_p;
+    else return NULL;
+}
+
+/*-------------------
+ * OTHER FUNCTIONS
+ *------------------*/
+
+void lv_obj_allocate_spec_attr(lv_obj_t * obj)
+{
+    LV_ASSERT_OBJ(obj, MY_CLASS);
+
+    if(obj->spec_attr == NULL) {
+        obj->spec_attr = lv_malloc_zeroed(sizeof(lv_obj_spec_attr_t));
+        LV_ASSERT_MALLOC(obj->spec_attr);
+        if(obj->spec_attr == NULL) return;
+
+        obj->spec_attr->scroll_dir = LV_DIR_ALL;
+        obj->spec_attr->scrollbar_mode = LV_SCROLLBAR_MODE_AUTO;
+    }
+}
+
+bool lv_obj_check_type(const lv_obj_t * obj, const lv_obj_class_t * class_p)
+{
+    if(obj == NULL) return false;
+    return obj->class_p == class_p;
+}
+
+bool lv_obj_has_class(const lv_obj_t * obj, const lv_obj_class_t * class_p)
+{
+    const lv_obj_class_t * obj_class = obj->class_p;
+    while(obj_class) {
+        if(obj_class == class_p) return true;
+        obj_class = obj_class->base_class;
+    }
+
+    return false;
+}
+
+const lv_obj_class_t * lv_obj_get_class(const lv_obj_t * obj)
+{
+    return obj->class_p;
+}
+
+bool lv_obj_is_valid(const lv_obj_t * obj)
+{
+    lv_display_t * disp = lv_display_get_next(NULL);
+    while(disp) {
+        uint32_t i;
+        for(i = 0; i < disp->screen_cnt; i++) {
+            if(disp->screens[i] == obj) return true;
+            bool found = obj_valid_child(disp->screens[i], obj);
+            if(found) return true;
+        }
+
+        disp = lv_display_get_next(disp);
+    }
+
+    return false;
+}
+
+void lv_obj_null_on_delete(lv_obj_t ** obj_ptr)
+{
+    lv_obj_add_event_cb(*obj_ptr, null_on_delete_cb, LV_EVENT_DELETE, obj_ptr);
+}
+
+#if LV_USE_OBJ_ID
+void * lv_obj_get_id(const lv_obj_t * obj)
+{
+    LV_ASSERT_NULL(obj);
+    return obj->id;
+}
+
+lv_obj_t * lv_obj_find_by_id(const lv_obj_t * obj, const void * id)
+{
+    LV_LOG_WARN("DEPRECATED: IDs are used only to print the widget trees. To find a widget use obj_name");
+
+    if(obj == NULL) obj = lv_display_get_screen_active(NULL);
+    if(obj == NULL) return NULL;
+
+    uint32_t i;
+    uint32_t child_cnt = lv_obj_get_child_count(obj);
+    for(i = 0; i < child_cnt; i++) {
+        lv_obj_t * child = obj->spec_attr->children[i];
+        if(lv_obj_id_compare(child->id, id) == 0) return child;
+    }
+
+    /*Search children*/
+    for(i = 0; i < child_cnt; i++) {
+        lv_obj_t * child = obj->spec_attr->children[i];
+        lv_obj_t * found = lv_obj_find_by_id(child, id);
+        if(found != NULL) return found;
+    }
+
+    return NULL;
+}
+#endif
+
+void lv_obj_add_screen_load_event(lv_obj_t * obj, lv_event_code_t trigger, lv_obj_t * screen,
+                                  lv_screen_load_anim_t anim_type, uint32_t duration, uint32_t delay)
+{
+    if(screen == NULL) {
+        LV_LOG_WARN("`screen` is NULL, can't load a non existing screens");
+        return;
+    }
+
+    screen_load_anim_dsc_t * dsc = lv_malloc(sizeof(screen_load_anim_dsc_t));
+    LV_ASSERT_MALLOC(dsc);
+    lv_memzero(dsc, sizeof(screen_load_anim_dsc_t));
+    dsc->anim_type = anim_type;
+    dsc->duration = duration;
+    dsc->delay = delay;
+    dsc->target.screen = screen;
+
+    lv_obj_add_event_cb(obj, screen_load_on_trigger_event_cb, trigger, dsc);
+    lv_obj_add_event_cb(obj, lv_event_free_user_data_cb, LV_EVENT_DELETE, dsc);
+}
+
+void lv_obj_add_screen_create_event(lv_obj_t * obj, lv_event_code_t trigger, lv_screen_create_cb_t screen_create_cb,
+                                    lv_screen_load_anim_t anim_type, uint32_t duration, uint32_t delay)
+{
+    screen_load_anim_dsc_t * dsc = lv_malloc(sizeof(screen_load_anim_dsc_t));
+    LV_ASSERT_MALLOC(dsc);
+    lv_memzero(dsc, sizeof(screen_load_anim_dsc_t));
+    dsc->anim_type = anim_type;
+    dsc->duration = duration;
+    dsc->delay = delay;
+    dsc->target.create_cb = screen_create_cb;
+
+    lv_obj_add_event_cb(obj, screen_create_on_trigger_event_cb, trigger, dsc);
+    lv_obj_add_event_cb(obj, lv_event_free_user_data_cb, LV_EVENT_DELETE, dsc);
+}
+
+void lv_obj_add_play_timeline_event(lv_obj_t * obj, lv_event_code_t trigger, lv_anim_timeline_t * at, uint32_t delay,
+                                    bool reverse)
+{
+    timeline_play_dsc_t * dsc = lv_malloc(sizeof(timeline_play_dsc_t));
+    LV_ASSERT_MALLOC(dsc);
+    lv_memzero(dsc, sizeof(timeline_play_dsc_t));
+    dsc->at = at;
+    dsc->delay = delay;
+    dsc->reverse = reverse;
+
+    lv_obj_add_event_cb(obj, play_timeline_on_trigger_event_cb, trigger, dsc);
+    lv_obj_add_event_cb(obj, lv_event_free_user_data_cb, LV_EVENT_DELETE, dsc);
+}
+
+void lv_obj_set_user_data(lv_obj_t * obj, void * user_data)
+{
+    obj->user_data = user_data;
+}
+
+void * lv_obj_get_user_data(lv_obj_t * obj)
+{
+    return obj->user_data;
+}
+
+/**********************
+ *   STATIC FUNCTIONS
+ **********************/
+
+static void lv_obj_constructor(const lv_obj_class_t * class_p, lv_obj_t * obj)
+{
+    LV_UNUSED(class_p);
+    LV_TRACE_OBJ_CREATE("begin");
+
+    lv_obj_t * parent = obj->parent;
+    if(parent) {
+        int32_t sl = lv_obj_get_scroll_left(parent);
+        int32_t st = lv_obj_get_scroll_top(parent);
+
+        obj->coords.y1 = parent->coords.y1 + lv_obj_get_style_pad_top(parent, LV_PART_MAIN) - st;
+        obj->coords.y2 = obj->coords.y1 - 1;
+        obj->coords.x1  = parent->coords.x1 + lv_obj_get_style_pad_left(parent, LV_PART_MAIN) - sl;
+        obj->coords.x2  = obj->coords.x1 - 1;
+    }
+
+    /*Set attributes*/
+    obj->flags = LV_OBJ_FLAG_CLICKABLE;
+    obj->flags |= LV_OBJ_FLAG_SNAPPABLE;
+    if(parent) obj->flags |= LV_OBJ_FLAG_PRESS_LOCK;
+    if(parent) obj->flags |= LV_OBJ_FLAG_SCROLL_CHAIN;
+    obj->flags |= LV_OBJ_FLAG_CLICK_FOCUSABLE;
+    obj->flags |= LV_OBJ_FLAG_SCROLLABLE;
+    obj->flags |= LV_OBJ_FLAG_SCROLL_ELASTIC;
+    obj->flags |= LV_OBJ_FLAG_SCROLL_MOMENTUM;
+    obj->flags |= LV_OBJ_FLAG_SCROLL_WITH_ARROW;
+    if(parent) obj->flags |= LV_OBJ_FLAG_GESTURE_BUBBLE;
+
+#if LV_OBJ_ID_AUTO_ASSIGN
+    lv_obj_assign_id(class_p, obj);
+#endif
+
+    LV_TRACE_OBJ_CREATE("finished");
+}
+
+static void lv_obj_destructor(const lv_obj_class_t * class_p, lv_obj_t * obj)
+{
+    LV_UNUSED(class_p);
+
+    lv_event_mark_deleted(obj);
+
+    /*Remove all style*/
+    lv_obj_enable_style_refresh(false); /*No need to refresh the style because the object will be deleted*/
+    lv_obj_remove_style_all(obj);
+    lv_obj_enable_style_refresh(true);
+
+    /*Remove the animations from this object*/
+    lv_anim_delete(obj, NULL);
+
+    /*Delete from the group*/
+    lv_group_t * group = lv_obj_get_group(obj);
+    if(group) lv_group_remove_obj(obj);
+
+    if(obj->spec_attr) {
+        if(obj->spec_attr->children) {
+            lv_free(obj->spec_attr->children);
+            obj->spec_attr->children = NULL;
+        }
+
+        lv_event_remove_all(&obj->spec_attr->event_list);
+#if LV_USE_OBJ_NAME
+        if(obj->spec_attr->name && !obj->spec_attr->name_static) {
+            lv_free((void *)obj->spec_attr->name);
+        }
+#endif
+
+#if LV_DRAW_TRANSFORM_USE_MATRIX
+        if(obj->spec_attr->matrix) {
+            lv_free(obj->spec_attr->matrix);
+            obj->spec_attr->matrix = NULL;
+        }
+#endif
+
+        lv_free(obj->spec_attr);
+        obj->spec_attr = NULL;
+    }
+
+#if LV_OBJ_ID_AUTO_ASSIGN
+    lv_obj_free_id(obj);
+#endif
+}
+
+static void lv_obj_draw(lv_event_t * e)
+{
+    lv_event_code_t code = lv_event_get_code(e);
+    lv_obj_t * obj = lv_event_get_current_target(e);
+    if(code == LV_EVENT_COVER_CHECK) {
+        lv_cover_check_info_t * info = lv_event_get_param(e);
+        if(info->res == LV_COVER_RES_MASKED) return;
+        if(lv_obj_get_style_clip_corner(obj, LV_PART_MAIN)) {
+            info->res = LV_COVER_RES_MASKED;
+            return;
+        }
+
+        /*Most trivial test. Is the mask fully IN the object? If no it surely doesn't cover it*/
+        int32_t r = lv_obj_get_style_radius(obj, LV_PART_MAIN);
+        int32_t w = lv_obj_get_style_transform_width(obj, LV_PART_MAIN);
+        int32_t h = lv_obj_get_style_transform_height(obj, LV_PART_MAIN);
+        lv_area_t coords;
+        lv_area_copy(&coords, &obj->coords);
+        lv_area_increase(&coords, w, h);
+
+        if(lv_area_is_in(info->area, &coords, r) == false) {
+            info->res = LV_COVER_RES_NOT_COVER;
+            return;
+        }
+
+        if(lv_obj_get_style_bg_opa(obj, LV_PART_MAIN) < LV_OPA_MAX) {
+            info->res = LV_COVER_RES_NOT_COVER;
+            return;
+        }
+
+        if(lv_obj_get_style_opa(obj, LV_PART_MAIN) < LV_OPA_MAX) {
+            info->res = LV_COVER_RES_NOT_COVER;
+            return;
+        }
+
+        if(lv_obj_get_style_bg_grad_dir(obj, LV_PART_MAIN) != LV_GRAD_DIR_NONE) {
+            if(lv_obj_get_style_bg_grad_opa(obj, LV_PART_MAIN) < LV_OPA_MAX ||
+               lv_obj_get_style_bg_main_opa(obj, LV_PART_MAIN) < LV_OPA_MAX) {
+                info->res = LV_COVER_RES_NOT_COVER;
+                return;
+            }
+        }
+        const lv_grad_dsc_t * grad_dsc = lv_obj_get_style_bg_grad(obj, LV_PART_MAIN);
+        if(grad_dsc) {
+            uint32_t i;
+            for(i = 0; i < grad_dsc->stops_count; i++) {
+                if(grad_dsc->stops[i].opa < LV_OPA_MAX) {
+                    info->res = LV_COVER_RES_NOT_COVER;
+                    return;
+                }
+            }
+        }
+        info->res = LV_COVER_RES_COVER;
+    }
+    else if(code == LV_EVENT_DRAW_MAIN) {
+        lv_layer_t * layer = lv_event_get_layer(e);
+        lv_draw_rect_dsc_t draw_dsc;
+        lv_draw_rect_dsc_init(&draw_dsc);
+        draw_dsc.base.layer = layer;
+
+        lv_obj_init_draw_rect_dsc(obj, LV_PART_MAIN, &draw_dsc);
+        /*If the border is drawn later disable loading its properties*/
+        if(lv_obj_get_style_border_post(obj, LV_PART_MAIN)) {
+            draw_dsc.border_post = 1;
+        }
+
+        int32_t w = lv_obj_get_style_transform_width(obj, LV_PART_MAIN);
+        int32_t h = lv_obj_get_style_transform_height(obj, LV_PART_MAIN);
+        lv_area_t coords;
+        lv_area_copy(&coords, &obj->coords);
+        lv_area_increase(&coords, w, h);
+
+        lv_draw_rect(layer, &draw_dsc, &coords);
+    }
+    else if(code == LV_EVENT_DRAW_POST) {
+        lv_layer_t * layer = lv_event_get_layer(e);
+        draw_scrollbar(obj, layer);
+
+        /*If the border is drawn later disable loading other properties*/
+        if(lv_obj_get_style_border_width(obj, LV_PART_MAIN) &&
+           lv_obj_get_style_border_post(obj, LV_PART_MAIN)) {
+            lv_draw_rect_dsc_t draw_dsc;
+            lv_draw_rect_dsc_init(&draw_dsc);
+            draw_dsc.bg_opa = LV_OPA_TRANSP;
+            draw_dsc.bg_image_opa = LV_OPA_TRANSP;
+            draw_dsc.outline_opa = LV_OPA_TRANSP;
+            draw_dsc.shadow_opa = LV_OPA_TRANSP;
+            draw_dsc.base.layer = layer;
+            lv_obj_init_draw_rect_dsc(obj, LV_PART_MAIN, &draw_dsc);
+
+            int32_t w = lv_obj_get_style_transform_width(obj, LV_PART_MAIN);
+            int32_t h = lv_obj_get_style_transform_height(obj, LV_PART_MAIN);
+            lv_area_t coords;
+            lv_area_copy(&coords, &obj->coords);
+            lv_area_increase(&coords, w, h);
+
+            lv_draw_rect(layer, &draw_dsc, &coords);
+        }
+    }
+}
+
+static void draw_scrollbar(lv_obj_t * obj, lv_layer_t * layer)
+{
+
+    lv_area_t hor_area;
+    lv_area_t ver_area;
+    lv_obj_get_scrollbar_area(obj, &hor_area, &ver_area);
+
+    if(lv_area_get_size(&hor_area) <= 0 && lv_area_get_size(&ver_area) <= 0) return;
+
+    lv_draw_rect_dsc_t draw_dsc;
+    lv_result_t sb_res = scrollbar_init_draw_dsc(obj, &draw_dsc);
+    if(sb_res != LV_RESULT_OK) return;
+
+    if(lv_area_get_size(&hor_area) > 0) {
+        draw_dsc.base.id1 = 0;
+        lv_draw_rect(layer, &draw_dsc, &hor_area);
+    }
+    if(lv_area_get_size(&ver_area) > 0) {
+        draw_dsc.base.id1 = 1;
+        lv_draw_rect(layer, &draw_dsc, &ver_area);
+    }
+}
+
+/**
+ * Initialize the draw descriptor for the scrollbar
+ * @param obj pointer to an object
+ * @param dsc the draw descriptor to initialize
+ * @return LV_RESULT_OK: the scrollbar is visible; LV_RESULT_INVALID: the scrollbar is not visible
+ */
+static lv_result_t scrollbar_init_draw_dsc(lv_obj_t * obj, lv_draw_rect_dsc_t * dsc)
+{
+    lv_draw_rect_dsc_init(dsc);
+    dsc->bg_opa = lv_obj_get_style_bg_opa(obj, LV_PART_SCROLLBAR);
+    if(dsc->bg_opa > LV_OPA_MIN) {
+        dsc->bg_color = lv_obj_get_style_bg_color(obj, LV_PART_SCROLLBAR);
+    }
+
+    dsc->border_opa = lv_obj_get_style_border_opa(obj, LV_PART_SCROLLBAR);
+    if(dsc->border_opa > LV_OPA_MIN) {
+        dsc->border_width = lv_obj_get_style_border_width(obj, LV_PART_SCROLLBAR);
+        if(dsc->border_width > 0) {
+            dsc->border_color = lv_obj_get_style_border_color(obj, LV_PART_SCROLLBAR);
+        }
+        else {
+            dsc->border_opa = LV_OPA_TRANSP;
+        }
+    }
+
+    dsc->shadow_opa = lv_obj_get_style_shadow_opa(obj, LV_PART_SCROLLBAR);
+    if(dsc->shadow_opa > LV_OPA_MIN) {
+        dsc->shadow_width = lv_obj_get_style_shadow_width(obj, LV_PART_SCROLLBAR);
+        if(dsc->shadow_width > 0) {
+            dsc->shadow_spread = lv_obj_get_style_shadow_spread(obj, LV_PART_SCROLLBAR);
+            dsc->shadow_color = lv_obj_get_style_shadow_color(obj, LV_PART_SCROLLBAR);
+        }
+        else {
+            dsc->shadow_opa = LV_OPA_TRANSP;
+        }
+    }
+
+    lv_opa_t opa = lv_obj_get_style_opa_recursive(obj, LV_PART_SCROLLBAR);
+    if(opa < LV_OPA_MAX) {
+        lv_opa_t v = LV_OPA_MIX2(dsc->bg_opa, opa);
+        dsc->bg_opa = v;
+        dsc->border_opa = v;
+        dsc->shadow_opa = v;
+    }
+
+    if(dsc->bg_opa != LV_OPA_TRANSP || dsc->border_opa != LV_OPA_TRANSP || dsc->shadow_opa != LV_OPA_TRANSP) {
+        dsc->radius = lv_obj_get_style_radius(obj, LV_PART_SCROLLBAR);
+        return LV_RESULT_OK;
+    }
+    else {
+        return LV_RESULT_INVALID;
+    }
+}
+
+static void lv_obj_event(const lv_obj_class_t * class_p, lv_event_t * e)
+{
+    LV_UNUSED(class_p);
+
+    lv_event_code_t code = lv_event_get_code(e);
+    lv_obj_t * obj = lv_event_get_current_target(e);
+    if(code == LV_EVENT_PRESSED) {
+        lv_obj_add_state(obj, LV_STATE_PRESSED);
+    }
+    else if(code == LV_EVENT_RELEASED) {
+        lv_obj_remove_state(obj, LV_STATE_PRESSED);
+        void * param = lv_event_get_param(e);
+        /*Go the checked state if enabled*/
+        if(lv_indev_get_scroll_obj(param) == NULL && lv_obj_has_flag(obj, LV_OBJ_FLAG_CHECKABLE)) {
+            if(!(lv_obj_get_state(obj) & LV_STATE_CHECKED)) lv_obj_add_state(obj, LV_STATE_CHECKED);
+            else lv_obj_remove_state(obj, LV_STATE_CHECKED);
+
+            lv_result_t res = lv_obj_send_event(obj, LV_EVENT_VALUE_CHANGED, NULL);
+            if(res != LV_RESULT_OK) return;
+        }
+    }
+    else if(code == LV_EVENT_PRESS_LOST) {
+        lv_obj_remove_state(obj, LV_STATE_PRESSED);
+    }
+    else if(code == LV_EVENT_STYLE_CHANGED) {
+        uint32_t child_cnt = lv_obj_get_child_count(obj);
+        for(uint32_t i = 0; i < child_cnt; i++) {
+            lv_obj_t * child = obj->spec_attr->children[i];
+            lv_obj_mark_layout_as_dirty(child);
+        }
+    }
+    else if(code == LV_EVENT_KEY) {
+        if(lv_obj_has_flag(obj, LV_OBJ_FLAG_CHECKABLE)) {
+            uint32_t c = lv_event_get_key(e);
+            if(c == LV_KEY_RIGHT || c == LV_KEY_UP) {
+                lv_obj_add_state(obj, LV_STATE_CHECKED);
+            }
+            else if(c == LV_KEY_LEFT || c == LV_KEY_DOWN) {
+                lv_obj_remove_state(obj, LV_STATE_CHECKED);
+            }
+
+            /*With Enter LV_EVENT_RELEASED will send VALUE_CHANGE event*/
+            if(c != LV_KEY_ENTER) {
+                lv_result_t res = lv_obj_send_event(obj, LV_EVENT_VALUE_CHANGED, NULL);
+                if(res != LV_RESULT_OK) return;
+            }
+        }
+        else if(lv_obj_has_flag(obj, LV_OBJ_FLAG_SCROLLABLE | LV_OBJ_FLAG_SCROLL_WITH_ARROW) && !lv_obj_is_editable(obj)) {
+            /*scroll by keypad or encoder*/
+            lv_anim_enable_t anim_enable = LV_ANIM_OFF;
+            int32_t sl = lv_obj_get_scroll_left(obj);
+            int32_t sr = lv_obj_get_scroll_right(obj);
+            uint32_t c = lv_event_get_key(e);
+            if(c == LV_KEY_DOWN) {
+                /*use scroll_to_x/y functions to enforce scroll limits*/
+                lv_obj_scroll_to_y(obj, lv_obj_get_scroll_y(obj) + lv_obj_get_height(obj) / 4, anim_enable);
+            }
+            else if(c == LV_KEY_UP) {
+                lv_obj_scroll_to_y(obj, lv_obj_get_scroll_y(obj) - lv_obj_get_height(obj) / 4, anim_enable);
+            }
+            else if(c == LV_KEY_RIGHT) {
+                /*If the object can't be scrolled horizontally then scroll it vertically*/
+                if(!((lv_obj_get_scroll_dir(obj) & LV_DIR_HOR) && (sl > 0 || sr > 0)))
+                    lv_obj_scroll_to_y(obj, lv_obj_get_scroll_y(obj) + lv_obj_get_height(obj) / 4, anim_enable);
+                else
+                    lv_obj_scroll_to_x(obj, lv_obj_get_scroll_x(obj) + lv_obj_get_width(obj) / 4, anim_enable);
+            }
+            else if(c == LV_KEY_LEFT) {
+                /*If the object can't be scrolled horizontally then scroll it vertically*/
+                if(!((lv_obj_get_scroll_dir(obj) & LV_DIR_HOR) && (sl > 0 || sr > 0)))
+                    lv_obj_scroll_to_y(obj, lv_obj_get_scroll_y(obj) - lv_obj_get_height(obj) / 4, anim_enable);
+                else
+                    lv_obj_scroll_to_x(obj, lv_obj_get_scroll_x(obj) - lv_obj_get_width(obj) / 4, anim_enable);
+            }
+        }
+    }
+    else if(code == LV_EVENT_FOCUSED) {
+        if(lv_obj_has_flag(obj, LV_OBJ_FLAG_SCROLL_ON_FOCUS)) {
+            lv_obj_scroll_to_view_recursive(obj, LV_ANIM_ON);
+        }
+
+        bool editing = false;
+        editing = lv_group_get_editing(lv_obj_get_group(obj));
+        lv_state_t state = LV_STATE_FOCUSED;
+
+        /* Use the indev for then indev handler.
+         * But if the obj was focused manually it returns NULL so try to
+         * use the indev from the event*/
+        lv_indev_t * indev = lv_indev_active();
+        if(indev == NULL) indev = lv_event_get_indev(e);
+
+        lv_indev_type_t indev_type = lv_indev_get_type(indev);
+        if(indev_type == LV_INDEV_TYPE_KEYPAD || indev_type == LV_INDEV_TYPE_ENCODER) state |= LV_STATE_FOCUS_KEY;
+        if(editing) {
+            state |= LV_STATE_EDITED;
+            lv_obj_add_state(obj, state);
+        }
+        else {
+            lv_obj_add_state(obj, state);
+            lv_obj_remove_state(obj, LV_STATE_EDITED);
+        }
+    }
+    else if(code == LV_EVENT_SCROLL_BEGIN) {
+        lv_obj_add_state(obj, LV_STATE_SCROLLED);
+    }
+    else if(code == LV_EVENT_SCROLL_END) {
+        lv_obj_remove_state(obj, LV_STATE_SCROLLED);
+        if(lv_obj_get_scrollbar_mode(obj) == LV_SCROLLBAR_MODE_ACTIVE) {
+            lv_area_t hor_area, ver_area;
+            lv_obj_get_scrollbar_area(obj, &hor_area, &ver_area);
+            lv_obj_invalidate_area(obj, &hor_area);
+            lv_obj_invalidate_area(obj, &ver_area);
+        }
+    }
+    else if(code == LV_EVENT_DEFOCUSED) {
+        lv_obj_remove_state(obj, LV_STATE_FOCUSED | LV_STATE_EDITED | LV_STATE_FOCUS_KEY);
+    }
+    else if(code == LV_EVENT_SIZE_CHANGED) {
+        int32_t align = lv_obj_get_style_align(obj, LV_PART_MAIN);
+        uint16_t layout = lv_obj_get_style_layout(obj, LV_PART_MAIN);
+        if(layout || align) {
+            lv_obj_mark_layout_as_dirty(obj);
+        }
+
+        uint32_t i;
+        uint32_t child_cnt = lv_obj_get_child_count(obj);
+        for(i = 0; i < child_cnt; i++) {
+            lv_obj_t * child = obj->spec_attr->children[i];
+            lv_obj_mark_layout_as_dirty(child);
+        }
+    }
+    else if(code == LV_EVENT_CHILD_CHANGED) {
+        int32_t w = lv_obj_get_style_width(obj, LV_PART_MAIN);
+        int32_t h = lv_obj_get_style_height(obj, LV_PART_MAIN);
+        int32_t align = lv_obj_get_style_align(obj, LV_PART_MAIN);
+        uint16_t layout = lv_obj_get_style_layout(obj, LV_PART_MAIN);
+        if(layout || align || w == LV_SIZE_CONTENT || h == LV_SIZE_CONTENT) {
+            lv_obj_mark_layout_as_dirty(obj);
+        }
+    }
+    else if(code == LV_EVENT_CHILD_DELETED) {
+        obj->readjust_scroll_after_layout = 1;
+        lv_obj_mark_layout_as_dirty(obj);
+    }
+    else if(code == LV_EVENT_REFR_EXT_DRAW_SIZE) {
+        int32_t d = lv_obj_calculate_ext_draw_size(obj, LV_PART_MAIN);
+        lv_event_set_ext_draw_size(e, d);
+    }
+    else if(code == LV_EVENT_DRAW_MAIN || code == LV_EVENT_DRAW_POST || code == LV_EVENT_COVER_CHECK) {
+        lv_obj_draw(e);
+    }
+    else if(code == LV_EVENT_INDEV_RESET) {
+        lv_obj_remove_state(obj, LV_STATE_PRESSED);
+        lv_obj_remove_state(obj, LV_STATE_SCROLLED);
+    }
+    else if(code == LV_EVENT_HOVER_OVER) {
+        lv_obj_add_state(obj, LV_STATE_HOVERED);
+    }
+    else if(code == LV_EVENT_HOVER_LEAVE) {
+        lv_obj_remove_state(obj, LV_STATE_HOVERED);
+    }
+}
+
+/**
+ * Set the state (fully overwrite) of an object.
+ * If specified in the styles, transition animations will be started from the previous state to the current.
+ * @param obj       pointer to an object
+ * @param state     the new state
+ */
+static void update_obj_state(lv_obj_t * obj, lv_state_t new_state)
+{
+    if(obj->state == new_state) return;
+
+    LV_ASSERT_OBJ(obj, MY_CLASS);
+
+    lv_state_t prev_state = obj->state;
+
+    lv_style_state_cmp_t cmp_res = lv_obj_style_state_compare(obj, prev_state, new_state);
+    /*If there is no difference in styles there is nothing else to do*/
+    if(cmp_res == LV_STYLE_STATE_CMP_SAME) {
+        obj->state = new_state;
+        return;
+    }
+
+    /*Invalidate the object in their current state*/
+    lv_obj_invalidate(obj);
+
+    obj->state = new_state;
+    lv_obj_update_layer_type(obj);
+    lv_obj_style_transition_dsc_t * ts = lv_malloc_zeroed(sizeof(lv_obj_style_transition_dsc_t) * STYLE_TRANSITION_MAX);
+    uint32_t tsi = 0;
+    uint32_t i;
+    for(i = 0; i < obj->style_cnt && tsi < STYLE_TRANSITION_MAX; i++) {
+        lv_obj_style_t * obj_style = &obj->styles[i];
+        lv_state_t state_act = lv_obj_style_get_selector_state(obj->styles[i].selector);
+        lv_part_t part_act = lv_obj_style_get_selector_part(obj->styles[i].selector);
+        if(state_act & (~new_state)) continue; /*Skip unrelated styles*/
+        if(obj_style->is_trans) continue;
+
+        lv_style_value_t v;
+        if(lv_style_get_prop_inlined(obj_style->style, LV_STYLE_TRANSITION, &v) != LV_STYLE_RES_FOUND) continue;
+        const lv_style_transition_dsc_t * tr = v.ptr;
+
+        /*Add the props to the set if not added yet or added but with smaller weight*/
+        uint32_t j;
+        for(j = 0; tr->props[j] != 0 && tsi < STYLE_TRANSITION_MAX; j++) {
+            uint32_t t;
+            for(t = 0; t < tsi; t++) {
+                lv_style_selector_t selector = ts[t].selector;
+                lv_state_t state_ts = lv_obj_style_get_selector_state(selector);
+                lv_part_t part_ts = lv_obj_style_get_selector_part(selector);
+                if(ts[t].prop == tr->props[j] && part_ts == part_act && state_ts >= state_act) break;
+            }
+
+            /*If not found  add it*/
+            if(t == tsi) {
+                ts[tsi].time = tr->time;
+                ts[tsi].delay = tr->delay;
+                ts[tsi].path_cb = tr->path_xcb;
+                ts[tsi].prop = tr->props[j];
+                ts[tsi].user_data = tr->user_data;
+                ts[tsi].selector = obj_style->selector;
+                tsi++;
+            }
+        }
+    }
+
+    for(i = 0; i < tsi; i++) {
+        lv_part_t part_act = lv_obj_style_get_selector_part(ts[i].selector);
+        lv_obj_style_create_transition(obj, part_act, prev_state, new_state, &ts[i]);
+    }
+
+    lv_free(ts);
+
+    if(cmp_res == LV_STYLE_STATE_CMP_DIFF_REDRAW) {
+        /*Invalidation is not enough, e.g. layer type needs to be updated too*/
+        lv_obj_refresh_style(obj, LV_PART_ANY, LV_STYLE_PROP_ANY);
+    }
+    else if(cmp_res == LV_STYLE_STATE_CMP_DIFF_LAYOUT) {
+        lv_obj_refresh_style(obj, LV_PART_ANY, LV_STYLE_PROP_ANY);
+    }
+    else if(cmp_res == LV_STYLE_STATE_CMP_DIFF_DRAW_PAD) {
+        lv_obj_invalidate(obj);
+        lv_obj_refresh_ext_draw_size(obj);
+    }
+}
+
+/**
+ * Apply the state to the children of the object
+ * @param obj pointer to an object
+ * @param state the state to apply
+ */
+static void lv_obj_children_add_state(lv_obj_t * obj, lv_state_t state)
+{
+    uint32_t child_count = lv_obj_get_child_count(obj);
+
+    for(uint32_t i = 0; i < child_count; i++) {
+        lv_obj_t * child = lv_obj_get_child(obj, i);
+        if(child) {
+            lv_obj_add_state(child, state);
+        }
+    }
+}
+
+/**
+ * Remove the state from the children of the object
+ * @param obj pointer to an object
+ * @param state the state to remove
+ */
+static void lv_obj_children_remove_state(lv_obj_t * obj, lv_state_t state)
+{
+    uint32_t child_count = lv_obj_get_child_count(obj);
+
+    for(uint32_t i = 0; i < child_count; i++) {
+        lv_obj_t * child = lv_obj_get_child(obj, i);
+        if(child) {
+            lv_obj_remove_state(child, state);
+        }
+    }
+}
+
+static bool obj_valid_child(const lv_obj_t * parent, const lv_obj_t * obj_to_find)
+{
+    /*Check all children of `parent`*/
+    uint32_t child_cnt = 0;
+    if(parent->spec_attr) child_cnt = parent->spec_attr->child_cnt;
+    uint32_t i;
+    for(i = 0; i < child_cnt; i++) {
+        lv_obj_t * child = parent->spec_attr->children[i];
+        if(child == obj_to_find) {
+            return true;
+        }
+
+        /*Check the children*/
+        bool found = obj_valid_child(child, obj_to_find);
+        if(found) {
+            return true;
+        }
+    }
+    return false;
+}
+
+static void null_on_delete_cb(lv_event_t * e)
+{
+    lv_obj_t ** obj_ptr = lv_event_get_user_data(e);
+    *obj_ptr = NULL;
+}
+
+static void screen_load_on_trigger_event_cb(lv_event_t * e)
+{
+    screen_load_anim_dsc_t * dsc = lv_event_get_user_data(e);
+    LV_ASSERT_NULL(dsc);
+    lv_screen_load_anim(dsc->target.screen, dsc->anim_type, dsc->duration, dsc->delay, false);
+}
+
+static void screen_create_on_trigger_event_cb(lv_event_t * e)
+{
+    screen_load_anim_dsc_t * dsc = lv_event_get_user_data(e);
+    LV_ASSERT_NULL(dsc);
+
+    lv_obj_t * screen = dsc->target.create_cb();
+    lv_screen_load_anim(screen, dsc->anim_type, dsc->duration, dsc->delay, false);
+    lv_obj_add_event_cb(screen, delete_on_screen_unloaded_event_cb, LV_EVENT_SCREEN_UNLOADED, NULL);
+}
+
+static void play_timeline_on_trigger_event_cb(lv_event_t * e)
+{
+    timeline_play_dsc_t * dsc = lv_event_get_user_data(e);
+    LV_ASSERT_NULL(dsc);
+
+    /*Reset the progress only if the animation was finished*/
+    uint16_t progress = lv_anim_timeline_get_progress(dsc->at);
+    if(dsc->reverse) {
+        if(progress == 0) {
+            lv_anim_timeline_set_progress(dsc->at, LV_ANIM_TIMELINE_PROGRESS_MAX);
+        }
+
+        if(lv_anim_timeline_get_progress(dsc->at) == LV_ANIM_TIMELINE_PROGRESS_MAX) {
+            lv_anim_timeline_set_delay(dsc->at, dsc->delay);
+        }
+
+        lv_anim_timeline_set_reverse(dsc->at, true);
+    }
+    else {
+        if(progress == LV_ANIM_TIMELINE_PROGRESS_MAX) {
+            lv_anim_timeline_set_progress(dsc->at, 0);
+        }
+
+        if(lv_anim_timeline_get_progress(dsc->at) == 0) {
+            lv_anim_timeline_set_delay(dsc->at, dsc->delay);
+        }
+
+        lv_anim_timeline_set_reverse(dsc->at, false);
+    }
+    lv_anim_timeline_start(dsc->at);
+}
+
+
+static void delete_on_screen_unloaded_event_cb(lv_event_t * e)
+{
+    lv_obj_delete(lv_event_get_target_obj(e));
+}
+
+#if LV_USE_OBJ_PROPERTY
+static lv_result_t lv_obj_set_any(lv_obj_t * obj, lv_prop_id_t id, const lv_property_t * prop)
+{
+    LV_ASSERT_OBJ(obj, MY_CLASS);
+
+    if(id >= LV_PROPERTY_OBJ_FLAG_START && id <= LV_PROPERTY_OBJ_FLAG_END) {
+        lv_obj_flag_t flag = 1L << (id - LV_PROPERTY_OBJ_FLAG_START);
+        if(prop->num) lv_obj_add_flag(obj, flag);
+        else lv_obj_remove_flag(obj, flag);
+        return LV_RESULT_OK;
+    }
+    else if(id >= LV_PROPERTY_OBJ_STATE_START && id <= LV_PROPERTY_OBJ_STATE_END) {
+        lv_state_t state = 1L << (id - LV_PROPERTY_OBJ_STATE_START);
+        if(id == LV_PROPERTY_OBJ_STATE_ANY) {
+            state = LV_STATE_ANY;
+        }
+
+        if(prop->num) lv_obj_add_state(obj, state);
+        else lv_obj_remove_state(obj, state);
+        return LV_RESULT_OK;
+    }
+    else {
+        return LV_RESULT_INVALID;
+    }
+}
+
+static lv_result_t lv_obj_get_any(const lv_obj_t * obj, lv_prop_id_t id, lv_property_t * prop)
+{
+    LV_ASSERT_OBJ(obj, MY_CLASS);
+    if(id >= LV_PROPERTY_OBJ_FLAG_START && id <= LV_PROPERTY_OBJ_FLAG_END) {
+        lv_obj_flag_t flag = 1L << (id - LV_PROPERTY_OBJ_FLAG_START);
+        prop->id = id;
+        prop->num = obj->flags & flag;
+        return LV_RESULT_OK;
+    }
+    else if(id >= LV_PROPERTY_OBJ_STATE_START && id <= LV_PROPERTY_OBJ_STATE_END) {
+        prop->id = id;
+        if(id == LV_PROPERTY_OBJ_STATE_ANY) {
+            prop->num = obj->state;
+        }
+        else {
+            lv_obj_flag_t flag = 1L << (id - LV_PROPERTY_OBJ_STATE_START);
+            prop->num = obj->state & flag;
+        }
+        return LV_RESULT_OK;
+    }
+    else {
+        return LV_RESULT_INVALID;
+    }
+}
+#endif

+ 486 - 0
components/easylvgl/lvgl9/src/core/lv_obj.h

@@ -0,0 +1,486 @@
+/**
+ * @file lv_obj.h
+ *
+ */
+
+#ifndef LV_OBJ_H
+#define LV_OBJ_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/*********************
+ *      INCLUDES
+ *********************/
+#include "../lv_conf_internal.h"
+
+#include "../misc/lv_types.h"
+#include "../misc/lv_style.h"
+#include "../misc/lv_area.h"
+#include "../misc/lv_color.h"
+#include "../misc/lv_assert.h"
+
+#include "lv_obj_tree.h"
+#include "lv_obj_pos.h"
+#include "lv_obj_scroll.h"
+#include "lv_obj_style.h"
+#include "lv_obj_draw.h"
+#include "lv_obj_class.h"
+#include "lv_obj_event.h"
+#include "lv_obj_property.h"
+#include "lv_group.h"
+
+/*********************
+ *      DEFINES
+ *********************/
+
+/**********************
+ *      TYPEDEFS
+ **********************/
+/**
+ * On/Off features controlling the object's behavior.
+ * OR-ed values are possible
+ *
+ * Note: update obj flags corresponding properties below
+ * whenever add/remove flags or change bit definition of flags.
+ */
+typedef enum {
+    LV_OBJ_FLAG_HIDDEN          = (1L << 0),  /**< Make the object hidden. (Like it wasn't there at all)*/
+    LV_OBJ_FLAG_CLICKABLE       = (1L << 1),  /**< Make the object clickable by the input devices*/
+    LV_OBJ_FLAG_CLICK_FOCUSABLE = (1L << 2),  /**< Add focused state to the object when clicked*/
+    LV_OBJ_FLAG_CHECKABLE       = (1L << 3),  /**< Toggle checked state when the object is clicked*/
+    LV_OBJ_FLAG_SCROLLABLE      = (1L << 4),  /**< Make the object scrollable*/
+    LV_OBJ_FLAG_SCROLL_ELASTIC  = (1L << 5),  /**< Allow scrolling inside but with slower speed*/
+    LV_OBJ_FLAG_SCROLL_MOMENTUM = (1L << 6),  /**< Make the object scroll further when "thrown"*/
+    LV_OBJ_FLAG_SCROLL_ONE      = (1L << 7),  /**< Allow scrolling only one snappable children*/
+    LV_OBJ_FLAG_SCROLL_CHAIN_HOR = (1L << 8), /**< Allow propagating the horizontal scroll to a parent*/
+    LV_OBJ_FLAG_SCROLL_CHAIN_VER = (1L << 9), /**< Allow propagating the vertical scroll to a parent*/
+    LV_OBJ_FLAG_SCROLL_CHAIN     = (LV_OBJ_FLAG_SCROLL_CHAIN_HOR | LV_OBJ_FLAG_SCROLL_CHAIN_VER),
+    LV_OBJ_FLAG_SCROLL_ON_FOCUS = (1L << 10),  /**< Automatically scroll object to make it visible when focused*/
+    LV_OBJ_FLAG_SCROLL_WITH_ARROW  = (1L << 11), /**< Allow scrolling the focused object with arrow keys*/
+    LV_OBJ_FLAG_SNAPPABLE       = (1L << 12), /**< If scroll snap is enabled on the parent it can snap to this object*/
+    LV_OBJ_FLAG_PRESS_LOCK      = (1L << 13), /**< Keep the object pressed even if the press slid from the object*/
+    LV_OBJ_FLAG_EVENT_BUBBLE    = (1L << 14), /**< Propagate the events to the parent too*/
+    LV_OBJ_FLAG_GESTURE_BUBBLE  = (1L << 15), /**< Propagate the gestures to the parent*/
+    LV_OBJ_FLAG_ADV_HITTEST     = (1L << 16), /**< Allow performing more accurate hit (click) test. E.g. consider rounded corners.*/
+    LV_OBJ_FLAG_IGNORE_LAYOUT   = (1L << 17), /**< Make the object not positioned by the layouts*/
+    LV_OBJ_FLAG_FLOATING        = (1L << 18), /**< Do not scroll the object when the parent scrolls and ignore layout*/
+    LV_OBJ_FLAG_SEND_DRAW_TASK_EVENTS = (1L << 19), /**< Send `LV_EVENT_DRAW_TASK_ADDED` events*/
+    LV_OBJ_FLAG_OVERFLOW_VISIBLE = (1L << 20),/**< Do not clip the children to the parent's ext draw size*/
+    LV_OBJ_FLAG_EVENT_TRICKLE   = (1L << 21), /**< Propagate the events to the children too*/
+    LV_OBJ_FLAG_STATE_TRICKLE   = (1L << 22), /**< Propagate the states to the children too*/
+
+    LV_OBJ_FLAG_LAYOUT_1        = (1L << 23), /**< Custom flag, free to use by layouts*/
+    LV_OBJ_FLAG_LAYOUT_2        = (1L << 24), /**< Custom flag, free to use by layouts*/
+#if LV_USE_FLEX
+    LV_OBJ_FLAG_FLEX_IN_NEW_TRACK = LV_OBJ_FLAG_LAYOUT_1,     /**< Start a new flex track on this item*/
+#endif
+
+    LV_OBJ_FLAG_WIDGET_1        = (1L << 25), /**< Custom flag, free to use by widget*/
+    LV_OBJ_FLAG_WIDGET_2        = (1L << 26), /**< Custom flag, free to use by widget*/
+    LV_OBJ_FLAG_USER_1          = (1L << 27), /**< Custom flag, free to use by user*/
+    LV_OBJ_FLAG_USER_2          = (1L << 28), /**< Custom flag, free to use by user*/
+    LV_OBJ_FLAG_USER_3          = (1L << 29), /**< Custom flag, free to use by user*/
+    LV_OBJ_FLAG_USER_4          = (1L << 30), /**< Custom flag, free to use by user*/
+} lv_obj_flag_t;
+
+#if LV_USE_OBJ_PROPERTY
+enum _lv_signed_prop_id_t {
+    /*OBJ flag properties */
+    LV_PROPERTY_ID(OBJ, FLAG_START,                 LV_PROPERTY_TYPE_INT,       0),
+    LV_PROPERTY_ID(OBJ, FLAG_HIDDEN,                LV_PROPERTY_TYPE_INT,       0),
+    LV_PROPERTY_ID(OBJ, FLAG_CLICKABLE,             LV_PROPERTY_TYPE_INT,       1),
+    LV_PROPERTY_ID(OBJ, FLAG_CLICK_FOCUSABLE,       LV_PROPERTY_TYPE_INT,       2),
+    LV_PROPERTY_ID(OBJ, FLAG_CHECKABLE,             LV_PROPERTY_TYPE_INT,       3),
+    LV_PROPERTY_ID(OBJ, FLAG_SCROLLABLE,            LV_PROPERTY_TYPE_INT,       4),
+    LV_PROPERTY_ID(OBJ, FLAG_SCROLL_ELASTIC,        LV_PROPERTY_TYPE_INT,       5),
+    LV_PROPERTY_ID(OBJ, FLAG_SCROLL_MOMENTUM,       LV_PROPERTY_TYPE_INT,       6),
+    LV_PROPERTY_ID(OBJ, FLAG_SCROLL_ONE,            LV_PROPERTY_TYPE_INT,       7),
+    LV_PROPERTY_ID(OBJ, FLAG_SCROLL_CHAIN_HOR,      LV_PROPERTY_TYPE_INT,       8),
+    LV_PROPERTY_ID(OBJ, FLAG_SCROLL_CHAIN_VER,      LV_PROPERTY_TYPE_INT,       9),
+    LV_PROPERTY_ID(OBJ, FLAG_SCROLL_ON_FOCUS,       LV_PROPERTY_TYPE_INT,       10),
+    LV_PROPERTY_ID(OBJ, FLAG_SCROLL_WITH_ARROW,     LV_PROPERTY_TYPE_INT,       11),
+    LV_PROPERTY_ID(OBJ, FLAG_SNAPPABLE,             LV_PROPERTY_TYPE_INT,       12),
+    LV_PROPERTY_ID(OBJ, FLAG_PRESS_LOCK,            LV_PROPERTY_TYPE_INT,       13),
+    LV_PROPERTY_ID(OBJ, FLAG_EVENT_BUBBLE,          LV_PROPERTY_TYPE_INT,       14),
+    LV_PROPERTY_ID(OBJ, FLAG_GESTURE_BUBBLE,        LV_PROPERTY_TYPE_INT,       15),
+    LV_PROPERTY_ID(OBJ, FLAG_ADV_HITTEST,           LV_PROPERTY_TYPE_INT,       16),
+    LV_PROPERTY_ID(OBJ, FLAG_IGNORE_LAYOUT,         LV_PROPERTY_TYPE_INT,       17),
+    LV_PROPERTY_ID(OBJ, FLAG_FLOATING,              LV_PROPERTY_TYPE_INT,       18),
+    LV_PROPERTY_ID(OBJ, FLAG_SEND_DRAW_TASK_EVENTS, LV_PROPERTY_TYPE_INT,       19),
+    LV_PROPERTY_ID(OBJ, FLAG_OVERFLOW_VISIBLE,      LV_PROPERTY_TYPE_INT,       20),
+    LV_PROPERTY_ID(OBJ, FLAG_EVENT_TRICKLE,         LV_PROPERTY_TYPE_INT,       21),
+    LV_PROPERTY_ID(OBJ, FLAG_STATE_TRICKLE,         LV_PROPERTY_TYPE_INT,       22),
+    LV_PROPERTY_ID(OBJ, FLAG_LAYOUT_1,              LV_PROPERTY_TYPE_INT,       23),
+    LV_PROPERTY_ID(OBJ, FLAG_LAYOUT_2,              LV_PROPERTY_TYPE_INT,       24),
+    LV_PROPERTY_ID(OBJ, FLAG_FLEX_IN_NEW_TRACK,     LV_PROPERTY_TYPE_INT,       23), /*Mapped to FLAG_LAYOUT_1*/
+    LV_PROPERTY_ID(OBJ, FLAG_WIDGET_1,              LV_PROPERTY_TYPE_INT,       25),
+    LV_PROPERTY_ID(OBJ, FLAG_WIDGET_2,              LV_PROPERTY_TYPE_INT,       26),
+    LV_PROPERTY_ID(OBJ, FLAG_USER_1,                LV_PROPERTY_TYPE_INT,       27),
+    LV_PROPERTY_ID(OBJ, FLAG_USER_2,                LV_PROPERTY_TYPE_INT,       28),
+    LV_PROPERTY_ID(OBJ, FLAG_USER_3,                LV_PROPERTY_TYPE_INT,       29),
+    LV_PROPERTY_ID(OBJ, FLAG_USER_4,                LV_PROPERTY_TYPE_INT,       30),
+    LV_PROPERTY_ID(OBJ, FLAG_END,                   LV_PROPERTY_TYPE_INT,       30),
+
+    LV_PROPERTY_ID(OBJ, STATE_START,                LV_PROPERTY_TYPE_INT,       31),
+    LV_PROPERTY_ID(OBJ, STATE_CHECKED,              LV_PROPERTY_TYPE_INT,       31),
+    LV_PROPERTY_ID(OBJ, STATE_FOCUSED,              LV_PROPERTY_TYPE_INT,       32),
+    LV_PROPERTY_ID(OBJ, STATE_FOCUS_KEY,            LV_PROPERTY_TYPE_INT,       33),
+    LV_PROPERTY_ID(OBJ, STATE_EDITED,               LV_PROPERTY_TYPE_INT,       34),
+    LV_PROPERTY_ID(OBJ, STATE_HOVERED,              LV_PROPERTY_TYPE_INT,       35),
+    LV_PROPERTY_ID(OBJ, STATE_PRESSED,              LV_PROPERTY_TYPE_INT,       36),
+    LV_PROPERTY_ID(OBJ, STATE_SCROLLED,             LV_PROPERTY_TYPE_INT,       37),
+    LV_PROPERTY_ID(OBJ, STATE_DISABLED,             LV_PROPERTY_TYPE_INT,       38),
+    /*not used bit8-bit11*/
+    LV_PROPERTY_ID(OBJ, STATE_USER_1,               LV_PROPERTY_TYPE_INT,       43),
+    LV_PROPERTY_ID(OBJ, STATE_USER_2,               LV_PROPERTY_TYPE_INT,       44),
+    LV_PROPERTY_ID(OBJ, STATE_USER_3,               LV_PROPERTY_TYPE_INT,       45),
+    LV_PROPERTY_ID(OBJ, STATE_USER_4,               LV_PROPERTY_TYPE_INT,       46),
+    LV_PROPERTY_ID(OBJ, STATE_ANY,                  LV_PROPERTY_TYPE_INT,       47),
+    LV_PROPERTY_ID(OBJ, STATE_END,                  LV_PROPERTY_TYPE_INT,       47),
+
+    /*OBJ normal properties*/
+    LV_PROPERTY_ID(OBJ, PARENT,                     LV_PROPERTY_TYPE_OBJ,       48),
+    LV_PROPERTY_ID(OBJ, X,                          LV_PROPERTY_TYPE_INT,       49),
+    LV_PROPERTY_ID(OBJ, Y,                          LV_PROPERTY_TYPE_INT,       50),
+    LV_PROPERTY_ID(OBJ, W,                          LV_PROPERTY_TYPE_INT,       51),
+    LV_PROPERTY_ID(OBJ, H,                          LV_PROPERTY_TYPE_INT,       52),
+    LV_PROPERTY_ID(OBJ, CONTENT_WIDTH,              LV_PROPERTY_TYPE_INT,       53),
+    LV_PROPERTY_ID(OBJ, CONTENT_HEIGHT,             LV_PROPERTY_TYPE_INT,       54),
+    LV_PROPERTY_ID(OBJ, LAYOUT,                     LV_PROPERTY_TYPE_INT,       55),
+    LV_PROPERTY_ID(OBJ, ALIGN,                      LV_PROPERTY_TYPE_INT,       56),
+    LV_PROPERTY_ID(OBJ, SCROLLBAR_MODE,             LV_PROPERTY_TYPE_INT,       57),
+    LV_PROPERTY_ID(OBJ, SCROLL_DIR,                 LV_PROPERTY_TYPE_INT,       58),
+    LV_PROPERTY_ID(OBJ, SCROLL_SNAP_X,              LV_PROPERTY_TYPE_INT,       59),
+    LV_PROPERTY_ID(OBJ, SCROLL_SNAP_Y,              LV_PROPERTY_TYPE_INT,       60),
+    LV_PROPERTY_ID(OBJ, SCROLL_X,                   LV_PROPERTY_TYPE_INT,       61),
+    LV_PROPERTY_ID(OBJ, SCROLL_Y,                   LV_PROPERTY_TYPE_INT,       62),
+    LV_PROPERTY_ID(OBJ, SCROLL_TOP,                 LV_PROPERTY_TYPE_INT,       63),
+    LV_PROPERTY_ID(OBJ, SCROLL_BOTTOM,              LV_PROPERTY_TYPE_INT,       64),
+    LV_PROPERTY_ID(OBJ, SCROLL_LEFT,                LV_PROPERTY_TYPE_INT,       65),
+    LV_PROPERTY_ID(OBJ, SCROLL_RIGHT,               LV_PROPERTY_TYPE_INT,       66),
+    LV_PROPERTY_ID(OBJ, SCROLL_END,                 LV_PROPERTY_TYPE_INT,       67),
+    LV_PROPERTY_ID(OBJ, EXT_DRAW_SIZE,              LV_PROPERTY_TYPE_INT,       68),
+    LV_PROPERTY_ID(OBJ, EVENT_COUNT,                LV_PROPERTY_TYPE_INT,       69),
+    LV_PROPERTY_ID(OBJ, SCREEN,                     LV_PROPERTY_TYPE_OBJ,       70),
+    LV_PROPERTY_ID(OBJ, DISPLAY,                    LV_PROPERTY_TYPE_POINTER,   71),
+    LV_PROPERTY_ID(OBJ, CHILD_COUNT,                LV_PROPERTY_TYPE_INT,       72),
+    LV_PROPERTY_ID(OBJ, INDEX,                      LV_PROPERTY_TYPE_INT,       73),
+
+    LV_PROPERTY_OBJ_END,
+};
+#endif
+
+/**
+ * Make the base object's class publicly available.
+ */
+LV_ATTRIBUTE_EXTERN_DATA extern const lv_obj_class_t lv_obj_class;
+
+/**********************
+ * GLOBAL PROTOTYPES
+ **********************/
+
+/**
+ * Create a base object (a rectangle)
+ * @param parent    pointer to a parent object. If NULL then a screen will be created.
+ * @return          pointer to the new object
+ */
+lv_obj_t * lv_obj_create(lv_obj_t * parent);
+
+/*=====================
+ * Setter functions
+ *====================*/
+
+/**
+ * Set one or more flags
+ * @param obj   pointer to an object
+ * @param f     OR-ed values from `lv_obj_flag_t` to set.
+ */
+void lv_obj_add_flag(lv_obj_t * obj, lv_obj_flag_t f);
+
+/**
+ * Remove one or more flags
+ * @param obj   pointer to an object
+ * @param f     OR-ed values from `lv_obj_flag_t` to clear.
+ */
+void lv_obj_remove_flag(lv_obj_t * obj, lv_obj_flag_t f);
+
+/**
+ * Set add or remove one or more flags.
+ * @param obj   pointer to an object
+ * @param f     OR-ed values from `lv_obj_flag_t` to update.
+ * @param v     true: add the flags; false: remove the flags
+ */
+void lv_obj_set_flag(lv_obj_t * obj, lv_obj_flag_t f, bool v);
+
+/**
+ * Add one or more states to the object. The other state bits will remain unchanged.
+ * If specified in the styles, transition animation will be started from the previous state to the current.
+ * @param obj       pointer to an object
+ * @param state     the states to add. E.g `LV_STATE_PRESSED | LV_STATE_FOCUSED`
+ */
+void lv_obj_add_state(lv_obj_t * obj, lv_state_t state);
+
+/**
+ * Remove one or more states to the object. The other state bits will remain unchanged.
+ * If specified in the styles, transition animation will be started from the previous state to the current.
+ * @param obj       pointer to an object
+ * @param state     the states to add. E.g `LV_STATE_PRESSED | LV_STATE_FOCUSED`
+ */
+void lv_obj_remove_state(lv_obj_t * obj, lv_state_t state);
+
+/**
+ * Add or remove one or more states to the object. The other state bits will remain unchanged.
+ * @param obj       pointer to an object
+ * @param state     the states to add. E.g `LV_STATE_PRESSED | LV_STATE_FOCUSED`
+ * @param v         true: add the states; false: remove the states
+ */
+void lv_obj_set_state(lv_obj_t * obj, lv_state_t state, bool v);
+
+/**
+ * Set the user_data field of the object
+ * @param obj   pointer to an object
+ * @param user_data   pointer to the new user_data.
+ */
+void lv_obj_set_user_data(lv_obj_t * obj, void * user_data);
+
+/*=======================
+ * Getter functions
+ *======================*/
+
+/**
+ * Check if a given flag or all the given flags are set on an object.
+ * @param obj   pointer to an object
+ * @param f     the flag(s) to check (OR-ed values can be used)
+ * @return      true: all flags are set; false: not all flags are set
+ */
+bool lv_obj_has_flag(const lv_obj_t * obj, lv_obj_flag_t f);
+
+/**
+ * Check if a given flag or any of the flags are set on an object.
+ * @param obj   pointer to an object
+ * @param f     the flag(s) to check (OR-ed values can be used)
+ * @return      true: at least one flag is set; false: none of the flags are set
+ */
+bool lv_obj_has_flag_any(const lv_obj_t * obj, lv_obj_flag_t f);
+
+/**
+ * Get the state of an object
+ * @param obj   pointer to an object
+ * @return      the state (OR-ed values from `lv_state_t`)
+ */
+lv_state_t lv_obj_get_state(const lv_obj_t * obj);
+
+/**
+ * Check if the object is in a given state or not.
+ * @param obj       pointer to an object
+ * @param state     a state or combination of states to check
+ * @return          true: `obj` is in `state`; false: `obj` is not in `state`
+ */
+bool lv_obj_has_state(const lv_obj_t * obj, lv_state_t state);
+
+/**
+ * Get the group of the object
+ * @param       obj pointer to an object
+ * @return      the pointer to group of the object
+ */
+lv_group_t * lv_obj_get_group(const lv_obj_t * obj);
+
+/**
+ * Get the user_data field of the object
+ * @param obj   pointer to an object
+ * @return      the pointer to the user_data of the object
+ */
+void * lv_obj_get_user_data(lv_obj_t * obj);
+
+/*=======================
+ * Other functions
+ *======================*/
+
+/**
+ * Allocate special data for an object if not allocated yet.
+ * @param obj   pointer to an object
+ */
+void lv_obj_allocate_spec_attr(lv_obj_t * obj);
+
+/**
+ * Check the type of obj.
+ * @param obj       pointer to an object
+ * @param class_p   a class to check (e.g. `lv_slider_class`)
+ * @return          true: `class_p` is the `obj` class.
+ */
+bool lv_obj_check_type(const lv_obj_t * obj, const lv_obj_class_t * class_p);
+
+/**
+ * Check if any object has a given class (type).
+ * It checks the ancestor classes too.
+ * @param obj       pointer to an object
+ * @param class_p   a class to check (e.g. `lv_slider_class`)
+ * @return          true: `obj` has the given class
+ */
+bool lv_obj_has_class(const lv_obj_t * obj, const lv_obj_class_t * class_p);
+
+/**
+ * Get the class (type) of the object
+ * @param obj   pointer to an object
+ * @return      the class (type) of the object
+ */
+const lv_obj_class_t * lv_obj_get_class(const lv_obj_t * obj);
+
+/**
+ * Check if any object is still "alive".
+ * @param obj       pointer to an object
+ * @return          true: valid
+ */
+bool lv_obj_is_valid(const lv_obj_t * obj);
+
+/**
+ * Utility to set an object reference to NULL when it gets deleted.
+ * The reference should be in a location that will not become invalid
+ * during the object's lifetime, i.e. static or allocated.
+ * @param obj_ptr   a pointer to a pointer to an object
+ */
+void lv_obj_null_on_delete(lv_obj_t ** obj_ptr);
+
+/**
+ * Add an event handler to a widget that will load a screen on a trigger.
+ * @param obj           pointer to widget which should load the screen
+ * @param trigger       an event code, e.g. `LV_EVENT_CLICKED`
+ * @param screen        the screen to load (must be a valid widget)
+ * @param anim_type     element of `lv_screen_load_anim_t` the screen load animation
+ * @param duration      duration of the animation in milliseconds
+ * @param delay         delay before the screen load in milliseconds
+ */
+void lv_obj_add_screen_load_event(lv_obj_t * obj, lv_event_code_t trigger, lv_obj_t * screen,
+                                  lv_screen_load_anim_t anim_type, uint32_t duration, uint32_t delay);
+
+/**
+ * Add an event handler to a widget that will create a screen on a trigger.
+ * The created screen will be deleted when it's unloaded
+ * @param obj               pointer to widget which should load the screen
+ * @param trigger           an event code, e.g. `LV_EVENT_CLICKED`
+ * @param screen_create_cb  a callback to create the screen, e.g. `lv_obj_t * myscreen_create(void)`
+ * @param anim_type         element of `lv_screen_load_anim_t` the screen load animation
+ * @param duration          duration of the animation in milliseconds
+ * @param delay             delay before the screen load in milliseconds
+ */
+void lv_obj_add_screen_create_event(lv_obj_t * obj, lv_event_code_t trigger, lv_screen_create_cb_t screen_create_cb,
+                                    lv_screen_load_anim_t anim_type, uint32_t duration, uint32_t delay);
+
+
+/**
+ * Play a timeline animation on a trigger
+ * @param obj               pointer to widget which should trigger playing the animation
+ * @param trigger           an event code, e.g. `LV_EVENT_CLICKED`
+ * @param at                pointer to an animation timeline
+ * @param delay             wait time before starting the animation
+ * @param reverse           true: play in reverse
+ */
+void lv_obj_add_play_timeline_event(lv_obj_t * obj, lv_event_code_t trigger, lv_anim_timeline_t * at, uint32_t delay,
+                                    bool reverse);
+
+#if LV_USE_OBJ_ID
+/**
+ * Set an id for an object.
+ * @param obj   pointer to an object
+ * @param id    the id of the object
+ */
+void lv_obj_set_id(lv_obj_t * obj, void * id);
+
+/**
+ * Get the id of an object.
+ * @param obj   pointer to an object
+ * @return      the id of the object
+ */
+void * lv_obj_get_id(const lv_obj_t * obj);
+
+/**
+ * DEPRECATED IDs are used only to print the widget trees.
+ * To find a widget use `lv_obj_find_by_name`
+ *
+ * Get the child object by its id.
+ * It will check children and grandchildren recursively.
+ * Function `lv_obj_id_compare` is used to matched obj id with given id.
+ *
+ * @param obj       pointer to an object
+ * @param id        the id of the child object
+ * @return          pointer to the child object or NULL if not found
+ */
+lv_obj_t * lv_obj_find_by_id(const lv_obj_t * obj, const void * id);
+
+/**
+ * Assign id to object if not previously assigned.
+ * This function gets called automatically when LV_OBJ_ID_AUTO_ASSIGN is enabled.
+ *
+ * Set `LV_USE_OBJ_ID_BUILTIN` to use the builtin method to generate object ID.
+ * Otherwise, these functions including `lv_obj_[set|assign|free|stringify]_id` and
+ * `lv_obj_id_compare`should be implemented externally.
+ *
+ * @param class_p   the class this obj belongs to. Note obj->class_p is the class currently being constructed.
+ * @param obj   pointer to an object
+ */
+void lv_obj_assign_id(const lv_obj_class_t * class_p, lv_obj_t * obj);
+
+/**
+ * Free resources allocated by `lv_obj_assign_id` or `lv_obj_set_id`.
+ * This function is also called automatically when object is deleted.
+ * @param obj   pointer to an object
+ */
+void lv_obj_free_id(lv_obj_t * obj);
+
+/**
+ * Compare two obj id, return 0 if they are equal.
+ *
+ * Set `LV_USE_OBJ_ID_BUILTIN` to use the builtin method for compare.
+ * Otherwise, it must be implemented externally.
+ *
+ * @param id1: the first id
+ * @param id2: the second id
+ * @return     0 if they are equal, non-zero otherwise.
+ */
+int lv_obj_id_compare(const void * id1, const void * id2);
+
+/**
+ * Format an object's id into a string.
+ * @param obj   pointer to an object
+ * @param buf   buffer to write the string into
+ * @param len   length of the buffer
+ */
+const char * lv_obj_stringify_id(lv_obj_t * obj, char * buf, uint32_t len);
+
+#if LV_USE_OBJ_ID_BUILTIN
+/**
+ * Free resources used by builtin ID generator.
+ */
+void lv_objid_builtin_destroy(void);
+#endif
+
+#endif /*LV_USE_OBJ_ID*/
+
+/**********************
+ *      MACROS
+ **********************/
+
+#if LV_USE_ASSERT_OBJ
+#  define LV_ASSERT_OBJ(obj_p, obj_class)                                                               \
+    do {                                                                                                \
+        LV_ASSERT_MSG(obj_p != NULL, "The object is NULL");                                             \
+        LV_ASSERT_MSG(lv_obj_has_class(obj_p, obj_class) == true, "Incompatible object type.");         \
+        LV_ASSERT_MSG(lv_obj_is_valid(obj_p)  == true, "The object is invalid, deleted or corrupted?"); \
+    } while(0)
+# else
+#  define LV_ASSERT_OBJ(obj_p, obj_class) LV_ASSERT_NULL(obj_p)
+#endif
+
+#if LV_USE_LOG && LV_LOG_TRACE_OBJ_CREATE
+#  define LV_TRACE_OBJ_CREATE(...) LV_LOG_TRACE(__VA_ARGS__)
+#else
+#  define LV_TRACE_OBJ_CREATE(...)
+#endif
+
+#ifdef __cplusplus
+} /*extern "C"*/
+#endif
+
+#endif /*LV_OBJ_H*/

+ 204 - 0
components/easylvgl/lvgl9/src/core/lv_obj_class.c

@@ -0,0 +1,204 @@
+/**
+ * @file lv_obj_class.c
+ *
+ */
+
+/*********************
+ *      INCLUDES
+ *********************/
+#include "lv_obj_class_private.h"
+#include "lv_obj_private.h"
+#include "../themes/lv_theme.h"
+#include "../display/lv_display.h"
+#include "../display/lv_display_private.h"
+#include "../stdlib/lv_string.h"
+
+/*********************
+ *      DEFINES
+ *********************/
+#define MY_CLASS (&lv_obj_class)
+
+/**********************
+ *      TYPEDEFS
+ **********************/
+
+/**********************
+ *  GLOBAL PROTOTYPES
+ **********************/
+
+/**********************
+ *  STATIC PROTOTYPES
+ **********************/
+static void lv_obj_construct(const lv_obj_class_t * class_p, lv_obj_t * obj);
+static uint32_t get_instance_size(const lv_obj_class_t * class_p);
+
+/**********************
+ *  STATIC VARIABLES
+ **********************/
+
+/**********************
+ *      MACROS
+ **********************/
+
+/**********************
+ *   GLOBAL FUNCTIONS
+ **********************/
+
+lv_obj_t * lv_obj_class_create_obj(const lv_obj_class_t * class_p, lv_obj_t * parent)
+{
+    LV_TRACE_OBJ_CREATE("Creating object with %p class on %p parent", (void *)class_p, (void *)parent);
+    uint32_t s = get_instance_size(class_p);
+    lv_obj_t * obj = lv_malloc_zeroed(s);
+    if(obj == NULL) return NULL;
+    obj->class_p = class_p;
+    obj->parent = parent;
+
+    /*Create a screen*/
+    if(parent == NULL) {
+        LV_TRACE_OBJ_CREATE("creating a screen");
+        lv_display_t * disp = lv_display_get_default();
+        if(!disp) {
+            LV_LOG_WARN("No display created yet. No place to assign the new screen");
+            lv_free(obj);
+            return NULL;
+        }
+
+        if(disp->screens == NULL) {
+            disp->screen_cnt = 0;
+        }
+
+        lv_obj_t ** screens = lv_realloc(disp->screens, sizeof(lv_obj_t *) * (disp->screen_cnt + 1));
+        LV_ASSERT_MALLOC(screens);
+        if(screens == NULL) {
+            lv_free(obj);
+            return NULL;
+        }
+
+        disp->screen_cnt++;
+        disp->screens = screens;
+        disp->screens[disp->screen_cnt - 1] = obj;
+
+        /*Set coordinates to full screen size*/
+        obj->coords.x1 = 0;
+        obj->coords.y1 = 0;
+        obj->coords.x2 = lv_display_get_horizontal_resolution(NULL) - 1;
+        obj->coords.y2 = lv_display_get_vertical_resolution(NULL) - 1;
+    }
+    /*Create a normal object*/
+    else {
+        LV_TRACE_OBJ_CREATE("creating normal object");
+        LV_ASSERT_OBJ(parent, MY_CLASS);
+        if(parent->spec_attr == NULL) {
+            lv_obj_allocate_spec_attr(parent);
+        }
+
+        parent->spec_attr->child_cnt++;
+        parent->spec_attr->children = lv_realloc(parent->spec_attr->children,
+                                                 sizeof(lv_obj_t *) * parent->spec_attr->child_cnt);
+        parent->spec_attr->children[parent->spec_attr->child_cnt - 1] = obj;
+    }
+
+    return obj;
+}
+
+void lv_obj_class_init_obj(lv_obj_t * obj)
+{
+    if(obj == NULL) return;
+
+    lv_obj_mark_layout_as_dirty(obj);
+    lv_obj_enable_style_refresh(false);
+
+    lv_theme_apply(obj);
+    lv_obj_construct(obj->class_p, obj);
+
+    lv_obj_enable_style_refresh(true);
+    lv_obj_refresh_style(obj, LV_PART_ANY, LV_STYLE_PROP_ANY);
+
+    lv_obj_refresh_self_size(obj);
+
+    lv_group_t * def_group = lv_group_get_default();
+    if(def_group && lv_obj_is_group_def(obj)) {
+        lv_group_add_obj(def_group, obj);
+    }
+
+    lv_obj_t * parent = lv_obj_get_parent(obj);
+    if(parent) {
+        /*Call the ancestor's event handler to the parent to notify it about the new child.
+         *Also triggers layout update*/
+        lv_obj_send_event(parent, LV_EVENT_CHILD_CHANGED, obj);
+        lv_obj_send_event(parent, LV_EVENT_CHILD_CREATED, obj);
+
+        /*Invalidate the area if not screen created*/
+        lv_obj_invalidate(obj);
+    }
+}
+
+void lv_obj_destruct(lv_obj_t * obj)
+{
+    if(obj->class_p->destructor_cb) obj->class_p->destructor_cb(obj->class_p, obj);
+
+    if(obj->class_p->base_class) {
+        /*Don't let the descendant methods run during destructing the ancestor type*/
+        obj->class_p = obj->class_p->base_class;
+
+        /*Call the base class's destructor too*/
+        lv_obj_destruct(obj);
+    }
+}
+
+bool lv_obj_is_editable(lv_obj_t * obj)
+{
+    const lv_obj_class_t * class_p = obj->class_p;
+
+    /*Find a base in which editable is set*/
+    while(class_p && class_p->editable == LV_OBJ_CLASS_EDITABLE_INHERIT) class_p = class_p->base_class;
+
+    if(class_p == NULL) return false;
+
+    return class_p->editable == LV_OBJ_CLASS_EDITABLE_TRUE;
+}
+
+bool lv_obj_is_group_def(lv_obj_t * obj)
+{
+    const lv_obj_class_t * class_p = obj->class_p;
+
+    /*Find a base in which group_def is set*/
+    while(class_p && class_p->group_def == LV_OBJ_CLASS_GROUP_DEF_INHERIT) class_p = class_p->base_class;
+
+    if(class_p == NULL) return false;
+
+    return class_p->group_def == LV_OBJ_CLASS_GROUP_DEF_TRUE;
+}
+
+/**********************
+ *   STATIC FUNCTIONS
+ **********************/
+
+static void lv_obj_construct(const lv_obj_class_t * class_p, lv_obj_t * obj)
+{
+    if(obj->class_p->base_class) {
+        const lv_obj_class_t * original_class_p = obj->class_p;
+
+        /*Don't let the descendant methods run during constructing the ancestor type*/
+        obj->class_p = obj->class_p->base_class;
+
+        /*Construct the base first*/
+        lv_obj_construct(class_p, obj);
+
+        /*Restore the original class*/
+        obj->class_p = original_class_p;
+    }
+
+    if(obj->class_p->constructor_cb) obj->class_p->constructor_cb(class_p, obj);
+}
+
+static uint32_t get_instance_size(const lv_obj_class_t * class_p)
+{
+    /*Find a base in which instance size is set*/
+    const lv_obj_class_t * base = class_p;
+    while(base && base->instance_size == 0) base = base->base_class;
+
+    if(base == NULL) return 0;  /*Never happens: set at least in `lv_obj` class*/
+
+    return base->instance_size;
+}

+ 72 - 0
components/easylvgl/lvgl9/src/core/lv_obj_class.h

@@ -0,0 +1,72 @@
+/**
+ * @file lv_obj_class.h
+ *
+ */
+
+#ifndef LV_OBJ_CLASS_H
+#define LV_OBJ_CLASS_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/*********************
+ *      INCLUDES
+ *********************/
+#include "../misc/lv_types.h"
+#include "../misc/lv_area.h"
+#include "lv_obj_property.h"
+
+/*********************
+ *      DEFINES
+ *********************/
+
+/**********************
+ *      TYPEDEFS
+ **********************/
+
+typedef enum {
+    LV_OBJ_CLASS_EDITABLE_INHERIT,      /**< Check the base class. Must have 0 value to let zero initialized class inherit*/
+    LV_OBJ_CLASS_EDITABLE_TRUE,
+    LV_OBJ_CLASS_EDITABLE_FALSE,
+} lv_obj_class_editable_t;
+
+typedef enum {
+    LV_OBJ_CLASS_GROUP_DEF_INHERIT,      /**< Check the base class. Must have 0 value to let zero initialized class inherit*/
+    LV_OBJ_CLASS_GROUP_DEF_TRUE,
+    LV_OBJ_CLASS_GROUP_DEF_FALSE,
+} lv_obj_class_group_def_t;
+
+typedef enum {
+    LV_OBJ_CLASS_THEME_INHERITABLE_FALSE,    /**< Do not inherit theme from base class. */
+    LV_OBJ_CLASS_THEME_INHERITABLE_TRUE,
+} lv_obj_class_theme_inheritable_t;
+
+typedef void (*lv_obj_class_event_cb_t)(lv_obj_class_t * class_p, lv_event_t * e);
+/**********************
+ * GLOBAL PROTOTYPES
+ **********************/
+
+/**
+ * Create an object form a class descriptor
+ * @param class_p   pointer to a class
+ * @param parent    pointer to an object where the new object should be created
+ * @return          pointer to the created object
+ */
+lv_obj_t * lv_obj_class_create_obj(const lv_obj_class_t * class_p, lv_obj_t * parent);
+
+void lv_obj_class_init_obj(lv_obj_t * obj);
+
+bool lv_obj_is_editable(lv_obj_t * obj);
+
+bool lv_obj_is_group_def(lv_obj_t * obj);
+
+/**********************
+ *      MACROS
+ **********************/
+
+#ifdef __cplusplus
+} /*extern "C"*/
+#endif
+
+#endif /*LV_OBJ_CLASS_H*/

+ 78 - 0
components/easylvgl/lvgl9/src/core/lv_obj_class_private.h

@@ -0,0 +1,78 @@
+/**
+ * @file lv_obj_class_private.h
+ *
+ */
+
+#ifndef LV_OBJ_CLASS_PRIVATE_H
+#define LV_OBJ_CLASS_PRIVATE_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/*********************
+ *      INCLUDES
+ *********************/
+
+#include "lv_obj_class.h"
+
+/*********************
+ *      DEFINES
+ *********************/
+
+/**********************
+ *      TYPEDEFS
+ **********************/
+
+/**
+ * Describe the common methods of every object.
+ * Similar to a C++ class.
+ */
+struct _lv_obj_class_t {
+    const lv_obj_class_t * base_class;
+    /** class_p is the final class while obj->class_p is the class currently being [de]constructed. */
+    void (*constructor_cb)(const lv_obj_class_t * class_p, lv_obj_t * obj);
+    void (*destructor_cb)(const lv_obj_class_t * class_p, lv_obj_t * obj);
+
+    /** class_p is the class in which event is being processed. */
+    void (*event_cb)(const lv_obj_class_t * class_p, lv_event_t * e);  /**< Widget type specific event function*/
+
+#if LV_USE_OBJ_PROPERTY
+    uint32_t prop_index_start;
+    uint32_t prop_index_end;
+    const lv_property_ops_t * properties;
+    uint32_t properties_count;
+
+#if LV_USE_OBJ_PROPERTY_NAME
+    /* An array of property ID and name */
+    const lv_property_name_t * property_names;
+    uint32_t names_count;
+#endif
+#endif
+
+    void * user_data;
+    const char * name;
+    int32_t width_def;
+    int32_t height_def;
+    uint32_t editable : 2;             /**< Value from ::lv_obj_class_editable_t*/
+    uint32_t group_def : 2;            /**< Value from ::lv_obj_class_group_def_t*/
+    uint32_t instance_size : 16;
+    uint32_t theme_inheritable : 1;    /**< Value from ::lv_obj_class_theme_inheritable_t*/
+};
+
+
+/**********************
+ * GLOBAL PROTOTYPES
+ **********************/
+
+void lv_obj_destruct(lv_obj_t * obj);
+
+/**********************
+ *      MACROS
+ **********************/
+
+#ifdef __cplusplus
+} /*extern "C"*/
+#endif
+
+#endif /*LV_OBJ_CLASS_PRIVATE_H*/

+ 458 - 0
components/easylvgl/lvgl9/src/core/lv_obj_draw.c

@@ -0,0 +1,458 @@
+/**
+ * @file lv_obj_draw.c
+ *
+ */
+
+/*********************
+ *      INCLUDES
+ *********************/
+#include "lv_obj_draw_private.h"
+#include "lv_obj_private.h"
+#include "lv_obj_style.h"
+#include "../display/lv_display.h"
+#include "../indev/lv_indev.h"
+#include "../stdlib/lv_string.h"
+#include "../draw/lv_draw_arc.h"
+
+/*********************
+ *      DEFINES
+ *********************/
+#define MY_CLASS (&lv_obj_class)
+
+/**********************
+ *      TYPEDEFS
+ **********************/
+
+/**********************
+ *  STATIC PROTOTYPES
+ **********************/
+
+static inline lv_opa_t get_layer_opa(const lv_obj_t * obj, lv_part_t part, const lv_draw_dsc_base_t * base_dsc);
+static lv_color_t normal_apply_layer_recolor(const lv_obj_t * obj, lv_part_t part,  const lv_draw_dsc_base_t * base_dsc,
+                                             lv_color_t color);
+static lv_color32_t image_apply_layer_recolor(const lv_obj_t * obj, lv_part_t part,
+                                              const lv_draw_dsc_base_t * base_dsc, lv_color_t color, lv_opa_t opa);
+
+/**********************
+ *  STATIC VARIABLES
+ **********************/
+
+/**********************
+ *      MACROS
+ **********************/
+
+/**********************
+ *   GLOBAL FUNCTIONS
+ **********************/
+
+void lv_obj_init_draw_rect_dsc(lv_obj_t * obj, lv_part_t part, lv_draw_rect_dsc_t * draw_dsc)
+{
+    LV_PROFILER_DRAW_BEGIN;
+    draw_dsc->base.obj = obj;
+    draw_dsc->base.part = part;
+
+    lv_opa_t opa = get_layer_opa(obj, part, &draw_dsc->base);
+    if(part != LV_PART_MAIN) {
+        if(opa <= LV_OPA_MIN) {
+            draw_dsc->bg_opa = LV_OPA_TRANSP;
+            draw_dsc->bg_image_opa = LV_OPA_TRANSP;
+            draw_dsc->border_opa = LV_OPA_TRANSP;
+            draw_dsc->outline_opa = LV_OPA_TRANSP;
+            draw_dsc->shadow_opa = LV_OPA_TRANSP;
+            LV_PROFILER_DRAW_END;
+            return;
+        }
+    }
+
+    draw_dsc->radius = lv_obj_get_style_radius(obj, part);
+
+    if(draw_dsc->bg_opa != LV_OPA_TRANSP) {
+        draw_dsc->bg_opa = lv_obj_get_style_bg_opa(obj, part);
+        if(draw_dsc->bg_opa > LV_OPA_MIN) {
+            lv_color_t bg_color = lv_obj_get_style_bg_color_filtered(obj, part);
+            draw_dsc->bg_color = normal_apply_layer_recolor(obj, part, &draw_dsc->base, bg_color);
+            const lv_grad_dsc_t * grad = lv_obj_get_style_bg_grad(obj, part);
+            if(grad && grad->dir != LV_GRAD_DIR_NONE) {
+                lv_memcpy(&draw_dsc->bg_grad, grad, sizeof(*grad));
+            }
+            else {
+                draw_dsc->bg_grad.dir = lv_obj_get_style_bg_grad_dir(obj, part);
+                if(draw_dsc->bg_grad.dir != LV_GRAD_DIR_NONE) {
+                    draw_dsc->bg_grad.stops[0].color = draw_dsc->bg_color;
+                    lv_color_t bg_grad_color = lv_obj_get_style_bg_grad_color_filtered(obj, part);
+                    draw_dsc->bg_grad.stops[1].color = normal_apply_layer_recolor(obj, part, &draw_dsc->base, bg_grad_color);
+                    draw_dsc->bg_grad.stops[0].frac = lv_obj_get_style_bg_main_stop(obj, part);
+                    draw_dsc->bg_grad.stops[1].frac = lv_obj_get_style_bg_grad_stop(obj, part);
+                    draw_dsc->bg_grad.stops[0].opa = lv_obj_get_style_bg_main_opa(obj, part);
+                    draw_dsc->bg_grad.stops[1].opa = lv_obj_get_style_bg_grad_opa(obj, part);
+                }
+            }
+        }
+    }
+
+    if(draw_dsc->border_opa != LV_OPA_TRANSP) {
+        draw_dsc->border_width = lv_obj_get_style_border_width(obj, part);
+        if(draw_dsc->border_width) {
+            draw_dsc->border_opa = lv_obj_get_style_border_opa(obj, part);
+            if(draw_dsc->border_opa > LV_OPA_MIN) {
+                draw_dsc->border_side = lv_obj_get_style_border_side(obj, part);
+                lv_color_t border_color = lv_obj_get_style_border_color_filtered(obj, part);
+                draw_dsc->border_color = normal_apply_layer_recolor(obj, part, &draw_dsc->base, border_color);
+            }
+        }
+    }
+
+    if(draw_dsc->outline_opa != LV_OPA_TRANSP) {
+        draw_dsc->outline_width = lv_obj_get_style_outline_width(obj, part);
+        if(draw_dsc->outline_width) {
+            draw_dsc->outline_opa = lv_obj_get_style_outline_opa(obj, part);
+            if(draw_dsc->outline_opa > LV_OPA_MIN) {
+                draw_dsc->outline_pad = lv_obj_get_style_outline_pad(obj, part);
+                lv_color_t outline_color = lv_obj_get_style_outline_color_filtered(obj, part);
+                draw_dsc->outline_color = normal_apply_layer_recolor(obj, part, &draw_dsc->base, outline_color);
+            }
+        }
+    }
+
+    if(draw_dsc->bg_image_opa != LV_OPA_TRANSP) {
+        draw_dsc->bg_image_src = lv_obj_get_style_bg_image_src(obj, part);
+        if(draw_dsc->bg_image_src) {
+            draw_dsc->bg_image_opa = lv_obj_get_style_bg_image_opa(obj, part);
+            if(draw_dsc->bg_image_opa > LV_OPA_MIN) {
+                if(lv_image_src_get_type(draw_dsc->bg_image_src) == LV_IMAGE_SRC_SYMBOL) {
+                    draw_dsc->bg_image_symbol_font = lv_obj_get_style_text_font(obj, part);
+                    lv_color_t text_color = lv_obj_get_style_text_color_filtered(obj, part);
+                    draw_dsc->bg_image_recolor = normal_apply_layer_recolor(obj, part, &draw_dsc->base, text_color);
+                }
+                else {
+                    lv_color_t bg_image_recolor = lv_obj_get_style_bg_image_recolor_filtered(obj, part);
+                    lv_opa_t bg_image_recolor_opa = lv_obj_get_style_bg_image_recolor_opa(obj, part);
+                    lv_color32_t result = image_apply_layer_recolor(obj, part, &draw_dsc->base, bg_image_recolor, bg_image_recolor_opa);
+                    draw_dsc->bg_image_recolor_opa = result.alpha;
+                    draw_dsc->bg_image_recolor = lv_color_make(result.red, result.green, result.blue);
+                    draw_dsc->bg_image_tiled = lv_obj_get_style_bg_image_tiled(obj, part);
+                }
+
+                draw_dsc->bg_image_colorkey = lv_obj_get_style_image_colorkey(obj, part);
+            }
+        }
+    }
+
+    if(draw_dsc->shadow_opa) {
+        draw_dsc->shadow_width = lv_obj_get_style_shadow_width(obj, part);
+        if(draw_dsc->shadow_width) {
+            if(draw_dsc->shadow_opa > LV_OPA_MIN) {
+                draw_dsc->shadow_opa = lv_obj_get_style_shadow_opa(obj, part);
+                if(draw_dsc->shadow_opa > LV_OPA_MIN) {
+                    draw_dsc->shadow_offset_x = lv_obj_get_style_shadow_offset_x(obj, part);
+                    draw_dsc->shadow_offset_y = lv_obj_get_style_shadow_offset_y(obj, part);
+                    draw_dsc->shadow_spread = lv_obj_get_style_shadow_spread(obj, part);
+                    lv_color_t shadow_color = lv_obj_get_style_shadow_color_filtered(obj, part);
+                    draw_dsc->shadow_color = normal_apply_layer_recolor(obj, part, &draw_dsc->base, shadow_color);
+                }
+            }
+        }
+    }
+
+    if(opa < LV_OPA_MAX) {
+        draw_dsc->bg_opa = LV_OPA_MIX2(draw_dsc->bg_opa, opa);
+        draw_dsc->bg_image_opa = LV_OPA_MIX2(draw_dsc->bg_image_opa, opa);
+        draw_dsc->border_opa = LV_OPA_MIX2(draw_dsc->border_opa, opa);
+        draw_dsc->shadow_opa = LV_OPA_MIX2(draw_dsc->shadow_opa, opa);
+        draw_dsc->outline_opa = LV_OPA_MIX2(draw_dsc->outline_opa, opa);
+    }
+
+    LV_PROFILER_DRAW_END;
+}
+
+void lv_obj_init_draw_label_dsc(lv_obj_t * obj, lv_part_t part, lv_draw_label_dsc_t * draw_dsc)
+{
+    LV_PROFILER_DRAW_BEGIN;
+    draw_dsc->base.obj = obj;
+    draw_dsc->base.part = part;
+
+    draw_dsc->opa = lv_obj_get_style_text_opa(obj, part);
+    if(draw_dsc->opa <= LV_OPA_MIN) {
+        LV_PROFILER_DRAW_END;
+        return;
+    }
+
+    lv_opa_t opa = get_layer_opa(obj, part, &draw_dsc->base);
+    if(opa < LV_OPA_MAX) {
+        draw_dsc->opa = LV_OPA_MIX2(draw_dsc->opa, opa);
+    }
+    if(draw_dsc->opa <= LV_OPA_MIN) {
+        LV_PROFILER_DRAW_END;
+        return;
+    }
+
+    lv_color_t text_color = lv_obj_get_style_text_color_filtered(obj, part);
+    draw_dsc->color = normal_apply_layer_recolor(obj, part, &draw_dsc->base, text_color);
+    draw_dsc->letter_space = lv_obj_get_style_text_letter_space(obj, part);
+    draw_dsc->line_space = lv_obj_get_style_text_line_space(obj, part);
+    draw_dsc->decor = lv_obj_get_style_text_decor(obj, part);
+
+    draw_dsc->font = lv_obj_get_style_text_font(obj, part);
+
+#if LV_USE_BIDI
+    draw_dsc->bidi_dir = lv_obj_get_style_base_dir(obj, LV_PART_MAIN);
+#endif
+
+    draw_dsc->align = lv_obj_get_style_text_align(obj, part);
+
+    LV_PROFILER_DRAW_END;
+}
+
+void lv_obj_init_draw_image_dsc(lv_obj_t * obj, lv_part_t part, lv_draw_image_dsc_t * draw_dsc)
+{
+    LV_PROFILER_DRAW_BEGIN;
+    draw_dsc->base.obj = obj;
+    draw_dsc->base.part = part;
+
+    draw_dsc->opa = lv_obj_get_style_image_opa(obj, part);
+    if(draw_dsc->opa <= LV_OPA_MIN) {
+        LV_PROFILER_DRAW_END;
+        return;
+    }
+
+    lv_opa_t opa = get_layer_opa(obj, part, &draw_dsc->base);
+    if(opa < LV_OPA_MAX) {
+        draw_dsc->opa = LV_OPA_MIX2(draw_dsc->opa, opa);
+    }
+    if(draw_dsc->opa <= LV_OPA_MIN) {
+        LV_PROFILER_DRAW_END;
+        return;
+    }
+
+    draw_dsc->rotation = 0;
+    draw_dsc->scale_x = LV_SCALE_NONE;
+    draw_dsc->scale_y = LV_SCALE_NONE;
+    draw_dsc->pivot.x = lv_area_get_width(&obj->coords) / 2;
+    draw_dsc->pivot.y = lv_area_get_height(&obj->coords) / 2;
+
+    lv_color_t recolor = lv_obj_get_style_image_recolor_filtered(obj, part);
+    lv_opa_t recolor_opa = lv_obj_get_style_image_recolor_opa(obj, part);
+    lv_color32_t result = image_apply_layer_recolor(obj, part, &draw_dsc->base, recolor, recolor_opa);
+    draw_dsc->recolor_opa = result.alpha;
+    draw_dsc->recolor = lv_color_make(result.red, result.green, result.blue);
+
+    draw_dsc->colorkey = lv_obj_get_style_image_colorkey(obj, part);
+
+    if(part != LV_PART_MAIN) draw_dsc->blend_mode = lv_obj_get_style_blend_mode(obj, part);
+
+    LV_PROFILER_DRAW_END;
+}
+
+void lv_obj_init_draw_line_dsc(lv_obj_t * obj, lv_part_t part, lv_draw_line_dsc_t * draw_dsc)
+{
+    LV_PROFILER_DRAW_BEGIN;
+    draw_dsc->base.obj = obj;
+    draw_dsc->base.part = part;
+
+    draw_dsc->opa = lv_obj_get_style_line_opa(obj, part);
+    if(draw_dsc->opa <= LV_OPA_MIN) {
+        LV_PROFILER_DRAW_END;
+        return;
+    }
+
+    lv_opa_t opa = get_layer_opa(obj, part, &draw_dsc->base);
+    if(opa < LV_OPA_MAX) {
+        draw_dsc->opa = LV_OPA_MIX2(draw_dsc->opa, opa);
+    }
+    if(draw_dsc->opa <= LV_OPA_MIN) {
+        LV_PROFILER_DRAW_END;
+        return;
+    }
+
+    draw_dsc->width = lv_obj_get_style_line_width(obj, part);
+    if(draw_dsc->width == 0) {
+        LV_PROFILER_DRAW_END;
+        return;
+    }
+
+    lv_color_t line_color = lv_obj_get_style_line_color_filtered(obj, part);
+    draw_dsc->color = normal_apply_layer_recolor(obj, part, &draw_dsc->base, line_color);
+
+    draw_dsc->dash_width = lv_obj_get_style_line_dash_width(obj, part);
+    if(draw_dsc->dash_width) {
+        draw_dsc->dash_gap = lv_obj_get_style_line_dash_gap(obj, part);
+    }
+
+    draw_dsc->round_start = lv_obj_get_style_line_rounded(obj, part);
+    draw_dsc->round_end = draw_dsc->round_start;
+
+    LV_PROFILER_DRAW_END;
+}
+
+void lv_obj_init_draw_arc_dsc(lv_obj_t * obj, lv_part_t part, lv_draw_arc_dsc_t * draw_dsc)
+{
+    LV_PROFILER_DRAW_BEGIN;
+    draw_dsc->base.obj = obj;
+    draw_dsc->base.part = part;
+
+    draw_dsc->width = lv_obj_get_style_arc_width(obj, part);
+    if(draw_dsc->width == 0) {
+        LV_PROFILER_DRAW_END;
+        return;
+    }
+
+    draw_dsc->opa = lv_obj_get_style_arc_opa(obj, part);
+    if(draw_dsc->opa <= LV_OPA_MIN) {
+        LV_PROFILER_DRAW_END;
+        return;
+    }
+
+    lv_opa_t opa = get_layer_opa(obj, part, &draw_dsc->base);
+    if(opa < LV_OPA_MAX) {
+        draw_dsc->opa = LV_OPA_MIX2(draw_dsc->opa, opa);
+    }
+    if(draw_dsc->opa <= LV_OPA_MIN) {
+        LV_PROFILER_DRAW_END;
+        return;
+    }
+
+    lv_color_t arc_color = lv_obj_get_style_arc_color_filtered(obj, part);
+    draw_dsc->color = normal_apply_layer_recolor(obj, part, &draw_dsc->base, arc_color);
+    draw_dsc->img_src = lv_obj_get_style_arc_image_src(obj, part);
+
+    draw_dsc->rounded = lv_obj_get_style_arc_rounded(obj, part);
+    LV_PROFILER_DRAW_END;
+}
+
+int32_t lv_obj_calculate_ext_draw_size(lv_obj_t * obj, lv_part_t part)
+{
+    LV_PROFILER_DRAW_BEGIN;
+    int32_t s = 0;
+
+    int32_t sh_width = lv_obj_get_style_shadow_width(obj, part);
+    if(sh_width) {
+        lv_opa_t sh_opa = lv_obj_get_style_shadow_opa(obj, part);
+        if(sh_opa > LV_OPA_MIN) {
+            sh_width = sh_width / 2 + 1;    /*The blur adds only half width*/
+            sh_width += lv_obj_get_style_shadow_spread(obj, part);
+            int32_t sh_ofs_x = lv_obj_get_style_shadow_offset_x(obj, part);
+            int32_t sh_ofs_y = lv_obj_get_style_shadow_offset_y(obj, part);
+            sh_width += LV_MAX(LV_ABS(sh_ofs_x), LV_ABS(sh_ofs_y));
+            s = LV_MAX(s, sh_width);
+        }
+    }
+
+    int32_t outline_width = lv_obj_get_style_outline_width(obj, part);
+    if(outline_width) {
+        lv_opa_t outline_opa = lv_obj_get_style_outline_opa(obj, part);
+        if(outline_opa > LV_OPA_MIN) {
+            int32_t outline_pad = lv_obj_get_style_outline_pad(obj, part);
+            s = LV_MAX(s, outline_pad + outline_width);
+        }
+    }
+
+    int32_t w = lv_obj_get_style_transform_width(obj, part);
+    int32_t h = lv_obj_get_style_transform_height(obj, part);
+    int32_t wh = LV_MAX(w, h);
+    if(wh > 0) s += wh;
+
+    LV_PROFILER_DRAW_END;
+    return s;
+}
+
+void lv_obj_refresh_ext_draw_size(lv_obj_t * obj)
+{
+    LV_PROFILER_DRAW_BEGIN;
+    LV_ASSERT_OBJ(obj, MY_CLASS);
+
+    int32_t s_old = lv_obj_get_ext_draw_size(obj);
+    int32_t s_new = 0;
+    lv_obj_send_event(obj, LV_EVENT_REFR_EXT_DRAW_SIZE, &s_new);
+
+    /*Store the result if the special attrs already allocated*/
+    if(obj->spec_attr) {
+        obj->spec_attr->ext_draw_size = s_new;
+    }
+    /*Allocate spec. attrs. only if the result is not zero.
+     *Zero is the default value if the spec. attr. are not defined.*/
+    else if(s_new != 0) {
+        lv_obj_allocate_spec_attr(obj);
+        obj->spec_attr->ext_draw_size = s_new;
+    }
+
+    if(s_new != s_old) lv_obj_invalidate(obj);
+    LV_PROFILER_DRAW_END;
+}
+
+int32_t lv_obj_get_ext_draw_size(const lv_obj_t * obj)
+{
+    if(obj->spec_attr) return obj->spec_attr->ext_draw_size;
+    else return 0;
+}
+
+lv_layer_type_t lv_obj_get_layer_type(const lv_obj_t * obj)
+{
+
+    if(obj->spec_attr) return (lv_layer_type_t)obj->spec_attr->layer_type;
+    else return LV_LAYER_TYPE_NONE;
+}
+
+/**********************
+ *   STATIC FUNCTIONS
+ **********************/
+
+static inline lv_opa_t get_layer_opa(const lv_obj_t * obj, lv_part_t part, const lv_draw_dsc_base_t * base_dsc)
+{
+    if(base_dsc->layer) {
+        /* Accessing the layer opa directly is faster than using get style opa recursive */
+        if(part == LV_PART_MAIN) {
+            return base_dsc->layer->opa;
+        }
+
+        return LV_OPA_MIX2(base_dsc->layer->opa, lv_obj_get_style_opa(obj, part));
+    }
+
+    /* fallback to old recursive style opa */
+    return lv_obj_get_style_opa_recursive(obj, part);
+}
+
+
+static lv_color_t normal_apply_layer_recolor(const lv_obj_t * obj, lv_part_t part,  const lv_draw_dsc_base_t * base_dsc,
+                                             lv_color_t color)
+{
+    lv_color32_t recolor;
+
+    if(base_dsc->layer) {
+        recolor = base_dsc->layer->recolor;
+        if(part != LV_PART_MAIN) {
+            recolor = lv_obj_style_apply_recolor(obj, part, recolor);
+        }
+    }
+    else {
+        recolor = lv_obj_get_style_recolor_recursive(obj, part);
+    }
+
+    return lv_color_mix(lv_color_make(recolor.red, recolor.green, recolor.blue), color, recolor.alpha);
+}
+
+
+static lv_color32_t image_apply_layer_recolor(const lv_obj_t * obj, lv_part_t part,
+                                              const lv_draw_dsc_base_t * base_dsc, lv_color_t color, lv_opa_t opa)
+{
+    lv_color32_t recolor;
+
+    if(base_dsc->layer) {
+        recolor = base_dsc->layer->recolor;
+        if(part != LV_PART_MAIN) {
+            recolor = lv_obj_style_apply_recolor(obj, part, recolor);
+        }
+    }
+    else {
+        recolor = lv_obj_get_style_recolor_recursive(obj, part);
+    }
+
+    if(opa > LV_OPA_TRANSP && recolor.alpha > LV_OPA_TRANSP) {
+        return lv_color_over32(recolor, lv_color_to_32(color, opa));
+    }
+    else if(recolor.alpha > LV_OPA_TRANSP) {
+        return recolor;
+    }
+    else {
+        return lv_color_to_32(color, opa);
+    }
+}

+ 129 - 0
components/easylvgl/lvgl9/src/core/lv_obj_draw.h

@@ -0,0 +1,129 @@
+/**
+ * @file lv_obj_draw.h
+ *
+ */
+
+#ifndef LV_OBJ_DRAW_H
+#define LV_OBJ_DRAW_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/*********************
+ *      INCLUDES
+ *********************/
+#include "../misc/lv_types.h"
+#include "../draw/lv_draw_rect.h"
+#include "../draw/lv_draw_label.h"
+#include "../draw/lv_draw_image.h"
+#include "../draw/lv_draw_line.h"
+#include "../draw/lv_draw_arc.h"
+#include "../draw/lv_draw_triangle.h"
+#include "lv_obj_style.h"
+
+/*********************
+ *      DEFINES
+ *********************/
+
+/**********************
+ *      TYPEDEFS
+ **********************/
+
+/** Store the type of layer required to render a widget.*/
+typedef enum {
+    /**No layer is needed. */
+    LV_LAYER_TYPE_NONE,
+
+    /**Simple layer means that the layer can be rendered in chunks.
+     * For example with opa_layered = 140 it's possible to render only 10 lines
+     * from the layer. When it's ready go to the next 10 lines.
+     * It avoids large memory allocations for the layer buffer.
+     * The buffer size for a chunk can be set by `LV_DRAW_LAYER_SIMPLE_BUF_SIZE` in lv_conf.h.*/
+    LV_LAYER_TYPE_SIMPLE,
+
+    /**The widget is transformed and cannot be rendered in chunks.
+     * It's because - due to the transformations -  pixel outside of
+     * a given area will also contribute to the final image.
+     * In this case there is no limitation on the buffer size.
+     * LVGL will allocate as large buffer as needed to render the transformed area.*/
+    LV_LAYER_TYPE_TRANSFORM,
+} lv_layer_type_t;
+
+/**********************
+ * GLOBAL PROTOTYPES
+ **********************/
+
+/**
+ * Initialize a rectangle draw descriptor from an object's styles in its current state
+ * @param obj       pointer to an object
+ * @param part      part of the object, e.g. `LV_PART_MAIN`, `LV_PART_SCROLLBAR`, `LV_PART_KNOB`, etc
+ * @param draw_dsc  the descriptor to initialize.
+ *                  If an `..._opa` field is set to `LV_OPA_TRANSP` the related properties won't be initialized.
+ *                  Should be initialized with `lv_draw_rect_dsc_init(draw_dsc)`.
+ * @note Only the relevant fields will be set.
+ *       E.g. if `border width == 0` the other border properties won't be evaluated.
+ */
+void lv_obj_init_draw_rect_dsc(lv_obj_t * obj, lv_part_t part, lv_draw_rect_dsc_t * draw_dsc);
+
+/**
+ * Initialize a label draw descriptor from an object's styles in its current state
+ * @param obj       pointer to an object
+ * @param part      part of the object, e.g. `LV_PART_MAIN`, `LV_PART_SCROLLBAR`, `LV_PART_KNOB`, etc
+ * @param draw_dsc  the descriptor to initialize.
+ *                  If the `opa` field is set to or the property is equal to `LV_OPA_TRANSP` the rest won't be initialized.
+ *                  Should be initialized with `lv_draw_label_dsc_init(draw_dsc)`.
+ */
+void lv_obj_init_draw_label_dsc(lv_obj_t * obj, lv_part_t part, lv_draw_label_dsc_t * draw_dsc);
+
+/**
+ * Initialize an image draw descriptor from an object's styles in its current state
+ * @param obj       pointer to an object
+ * @param part      part of the object, e.g. `LV_PART_MAIN`, `LV_PART_SCROLLBAR`, `LV_PART_KNOB`, etc
+ * @param draw_dsc  the descriptor to initialize.
+ *                  Should be initialized with `lv_draw_image_dsc_init(draw_dsc)`.
+ */
+void lv_obj_init_draw_image_dsc(lv_obj_t * obj, lv_part_t part, lv_draw_image_dsc_t * draw_dsc);
+
+/**
+ * Initialize a line draw descriptor from an object's styles in its current state
+ * @param obj pointer to an object
+ * @param part      part of the object, e.g. `LV_PART_MAIN`, `LV_PART_SCROLLBAR`, `LV_PART_KNOB`, etc
+ * @param draw_dsc  the descriptor to initialize.
+ *                  Should be initialized with `lv_draw_line_dsc_init(draw_dsc)`.
+ */
+void lv_obj_init_draw_line_dsc(lv_obj_t * obj, lv_part_t part, lv_draw_line_dsc_t * draw_dsc);
+
+/**
+ * Initialize an arc draw descriptor from an object's styles in its current state
+ * @param obj       pointer to an object
+ * @param part      part of the object, e.g. `LV_PART_MAIN`, `LV_PART_SCROLLBAR`, `LV_PART_KNOB`, etc
+ * @param draw_dsc  the descriptor to initialize.
+ *                  Should be initialized with `lv_draw_arc_dsc_init(draw_dsc)`.
+ */
+void lv_obj_init_draw_arc_dsc(lv_obj_t * obj, lv_part_t part, lv_draw_arc_dsc_t * draw_dsc);
+
+/**
+ * Get the required extra size (around the object's part) to draw shadow, outline, value etc.
+ * @param obj       pointer to an object
+ * @param part      part of the object
+ * @return          the extra size required around the object
+ */
+int32_t lv_obj_calculate_ext_draw_size(lv_obj_t * obj, lv_part_t part);
+
+/**
+ * Send a 'LV_EVENT_REFR_EXT_DRAW_SIZE' Call the ancestor's event handler to the object to refresh the value of the extended draw size.
+ * The result will be saved in `obj`.
+ * @param obj       pointer to an object
+ */
+void lv_obj_refresh_ext_draw_size(lv_obj_t * obj);
+
+/**********************
+ *      MACROS
+ **********************/
+
+#ifdef __cplusplus
+} /*extern "C"*/
+#endif
+
+#endif /*LV_OBJ_DRAW_H*/

+ 48 - 0
components/easylvgl/lvgl9/src/core/lv_obj_draw_private.h

@@ -0,0 +1,48 @@
+/**
+ * @file lv_obj_draw_private.h
+ *
+ */
+
+#ifndef LV_OBJ_DRAW_PRIVATE_H
+#define LV_OBJ_DRAW_PRIVATE_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/*********************
+ *      INCLUDES
+ *********************/
+
+#include "lv_obj_draw.h"
+
+/*********************
+ *      DEFINES
+ *********************/
+
+/**********************
+ *      TYPEDEFS
+ **********************/
+
+/**********************
+ * GLOBAL PROTOTYPES
+ **********************/
+
+/**
+ * Get the extended draw area of an object.
+ * @param obj       pointer to an object
+ * @return          the size extended draw area around the real coordinates
+ */
+int32_t lv_obj_get_ext_draw_size(const lv_obj_t * obj);
+
+lv_layer_type_t lv_obj_get_layer_type(const lv_obj_t * obj);
+
+/**********************
+ *      MACROS
+ **********************/
+
+#ifdef __cplusplus
+} /*extern "C"*/
+#endif
+
+#endif /*LV_OBJ_DRAW_PRIVATE_H*/

+ 480 - 0
components/easylvgl/lvgl9/src/core/lv_obj_event.c

@@ -0,0 +1,480 @@
+/**
+ * @file lv_obj_event.c
+ *
+ */
+
+/*********************
+ *      INCLUDES
+ *********************/
+#include "../misc/lv_event_private.h"
+#include "lv_obj_event_private.h"
+#include "lv_obj_class_private.h"
+#include "lv_obj_private.h"
+#include "../indev/lv_indev.h"
+#include "../indev/lv_indev_private.h"
+
+/*********************
+ *      DEFINES
+ *********************/
+#define MY_CLASS (&lv_obj_class)
+
+/**********************
+ *      TYPEDEFS
+ **********************/
+
+/**********************
+ *  STATIC PROTOTYPES
+ **********************/
+static lv_result_t event_send_core(lv_event_t * e);
+static bool event_is_bubbled(lv_event_t * e);
+static bool event_is_trickled(lv_event_t * e);
+
+/**********************
+ *  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
+ **********************/
+
+lv_result_t lv_obj_send_event(lv_obj_t * obj, lv_event_code_t event_code, void * param)
+{
+    if(obj == NULL) return LV_RESULT_OK;
+
+    LV_ASSERT_OBJ(obj, MY_CLASS);
+
+    lv_event_t e;
+    e.current_target = obj;
+    e.original_target = obj;
+    e.code = event_code;
+    e.user_data = NULL;
+    e.param = param;
+    e.deleted = 0;
+    e.stop_bubbling = 0;
+    e.stop_processing = 0;
+    e.stop_trickling = 0;
+
+    lv_event_push(&e);
+
+    /*Send the event*/
+    lv_result_t res = event_send_core(&e);
+
+    /*Remove this element from the list*/
+    lv_event_pop(&e);
+
+    return res;
+}
+
+lv_result_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 = ((lv_obj_t *)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_RESULT_OK;
+    if(base->event_cb == NULL) return LV_RESULT_OK;
+
+    /*Call the actual event callback*/
+    e->user_data = NULL;
+    LV_PROFILER_EVENT_BEGIN_TAG(lv_event_code_get_name(e->code));
+    base->event_cb(base, e);
+    LV_PROFILER_EVENT_END_TAG(lv_event_code_get_name(e->code));
+
+    lv_result_t res = LV_RESULT_OK;
+    /*Stop if the object is deleted*/
+    if(e->deleted) res = LV_RESULT_INVALID;
+
+    return res;
+}
+
+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);
+
+    return lv_event_add(&obj->spec_attr->event_list, event_cb, filter, user_data);
+}
+
+uint32_t lv_obj_get_event_count(lv_obj_t * obj)
+{
+    LV_ASSERT_NULL(obj);
+    if(obj->spec_attr == NULL) return 0;
+    return lv_event_get_count(&obj->spec_attr->event_list);
+}
+
+lv_event_dsc_t * lv_obj_get_event_dsc(lv_obj_t * obj, uint32_t index)
+{
+    LV_ASSERT_NULL(obj);
+    if(obj->spec_attr == NULL) return NULL;
+    return lv_event_get_dsc(&obj->spec_attr->event_list, index);
+}
+
+bool lv_obj_remove_event(lv_obj_t * obj, uint32_t index)
+{
+    LV_ASSERT_NULL(obj);
+    if(obj->spec_attr == NULL) return false;
+    return lv_event_remove(&obj->spec_attr->event_list, index);
+}
+
+bool lv_obj_remove_event_dsc(lv_obj_t * obj, lv_event_dsc_t * dsc)
+{
+    LV_ASSERT_NULL(obj);
+    LV_ASSERT_NULL(dsc);
+    if(obj->spec_attr == NULL) return false;
+    return lv_event_remove_dsc(&obj->spec_attr->event_list, dsc);
+}
+
+uint32_t lv_obj_remove_event_cb(lv_obj_t * obj, lv_event_cb_t event_cb)
+{
+    LV_ASSERT_NULL(obj);
+
+    uint32_t event_cnt = lv_obj_get_event_count(obj);
+    uint32_t removed_count = 0;
+    int32_t i;
+
+    if(event_cnt == 0) return 0;
+
+    for(i = event_cnt - 1; i >= 0; i--) {
+        lv_event_dsc_t * dsc = lv_obj_get_event_dsc(obj, i);
+        if(dsc && dsc->cb == event_cb) {
+            lv_obj_remove_event(obj, i);
+            removed_count++;
+        }
+    }
+
+    return removed_count;
+}
+
+uint32_t lv_obj_remove_event_cb_with_user_data(lv_obj_t * obj, lv_event_cb_t event_cb, void * user_data)
+{
+    LV_ASSERT_NULL(obj);
+
+    uint32_t event_cnt = lv_obj_get_event_count(obj);
+    uint32_t removed_count = 0;
+    int32_t i;
+
+    if(event_cnt == 0) return 0;
+
+    for(i = event_cnt - 1; i >= 0; i--) {
+        lv_event_dsc_t * dsc = lv_obj_get_event_dsc(obj, i);
+        if(dsc && (event_cb == NULL || dsc->cb == event_cb) && dsc->user_data == user_data) {
+            lv_obj_remove_event(obj, i);
+            removed_count ++;
+        }
+    }
+
+    return removed_count;
+}
+
+lv_obj_t * lv_event_get_current_target_obj(lv_event_t * e)
+{
+    return lv_event_get_current_target(e);
+}
+
+lv_obj_t * lv_event_get_target_obj(lv_event_t * e)
+{
+    return lv_event_get_target(e);
+}
+
+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 ||
+       e->code == LV_EVENT_HOVER_OVER ||
+       e->code == LV_EVENT_HOVER_LEAVE) {
+        return lv_event_get_param(e);
+    }
+    else {
+        LV_LOG_WARN("Not interpreted with this event code");
+        return NULL;
+    }
+}
+
+lv_layer_t * lv_event_get_layer(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;
+    }
+}
+
+int32_t lv_event_get_rotary_diff(lv_event_t * e)
+{
+    if(e->code == LV_EVENT_ROTARY) {
+        int32_t * r = lv_event_get_param(e);
+        if(r) return *r;
+        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 NULL;
+    }
+}
+
+void lv_event_set_ext_draw_size(lv_event_t * e, int32_t size)
+{
+    if(e->code == LV_EVENT_REFR_EXT_DRAW_SIZE) {
+        int32_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");
+    }
+}
+
+lv_draw_task_t * lv_event_get_draw_task(lv_event_t * e)
+{
+    if(e->code == LV_EVENT_DRAW_TASK_ADDED) {
+        return lv_event_get_param(e);
+    }
+    else {
+        LV_LOG_WARN("Not interpreted with this event code");
+        return NULL;
+    }
+
+}
+
+/**********************
+ *   STATIC FUNCTIONS
+ **********************/
+
+static lv_result_t event_send_core(lv_event_t * e)
+{
+    LV_TRACE_EVENT("Sending event %d to %p with %p param", e->code, (void *)e->original_target, e->param);
+
+    lv_indev_t * indev_act = lv_indev_active();
+    if(indev_act) {
+        if(e->stop_processing) return LV_RESULT_OK;
+        if(e->deleted) return LV_RESULT_INVALID;
+    }
+
+    lv_obj_t * target = e->current_target;
+    lv_result_t res = LV_RESULT_OK;
+    lv_event_list_t * list = target->spec_attr ?  &target->spec_attr->event_list : NULL;
+
+    res = lv_event_send(list, e, true);
+    if(res != LV_RESULT_OK || e->stop_processing) return res;
+
+    res = lv_obj_event_base(NULL, e);
+    if(res != LV_RESULT_OK || e->stop_processing) return res;
+
+    res = lv_event_send(list, e, false);
+    if(res != LV_RESULT_OK || e->stop_processing) return res;
+
+    lv_obj_t * parent = lv_obj_get_parent(e->current_target);
+    if(parent && event_is_bubbled(e)) {
+        e->current_target = parent;
+        res = event_send_core(e);
+    }
+    if(res != LV_RESULT_OK) return res;
+
+    /*Trickle down to children if enabled*/
+    if(event_is_trickled(e)) {
+        uint32_t child_count = lv_obj_get_child_count(target);
+
+        /* we don't want the event to bubble up again when trickling down */
+        e->stop_bubbling = 1;
+
+        for(uint32_t i = 0; i < child_count && res == LV_RESULT_OK && !e->stop_processing; i++) {
+            lv_obj_t * child = lv_obj_get_child(target, i);
+            if(child) {
+                e->current_target = child;
+                res = event_send_core(e);
+                if(res != LV_RESULT_OK) {
+                    LV_LOG_WARN("Trickle down event %d to child %p failed", e->code, (void *)child);
+                    break;
+                }
+            }
+        }
+    }
+
+    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_TASK_ADDED:
+        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;
+    }
+}
+
+static bool event_is_trickled(lv_event_t * e)
+{
+    if(e->stop_trickling) return false;
+
+    /*Check other codes only if trickle is enabled*/
+    if(lv_obj_has_flag(e->current_target, LV_OBJ_FLAG_EVENT_TRICKLE) == 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_TASK_ADDED:
+        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;
+    }
+}

+ 204 - 0
components/easylvgl/lvgl9/src/core/lv_obj_event.h

@@ -0,0 +1,204 @@
+/**
+ * @file lv_obj_event.h
+ *
+ */
+
+#ifndef LV_OBJ_EVENT_H
+#define LV_OBJ_EVENT_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/*********************
+ *      INCLUDES
+ *********************/
+#include "../misc/lv_types.h"
+#include "../misc/lv_event.h"
+#include "../indev/lv_indev.h"
+
+/*********************
+ *      DEFINES
+ *********************/
+
+/**********************
+ *      TYPEDEFS
+ **********************/
+
+/** Cover check results.*/
+typedef enum {
+    LV_COVER_RES_COVER      = 0,
+    LV_COVER_RES_NOT_COVER  = 1,
+    LV_COVER_RES_MASKED     = 2,
+} lv_cover_res_t;
+
+/**********************
+ * GLOBAL PROTOTYPES
+ **********************/
+
+/**
+ * Send an event to the object
+ * @param obj           pointer to an object
+ * @param event_code    the type of the event from `lv_event_t`
+ * @param param         arbitrary data depending on the widget type and the event. (Usually `NULL`)
+ * @return LV_RESULT_OK: `obj` was not deleted in the event; LV_RESULT_INVALID: `obj` was deleted in the event_code
+ */
+lv_result_t lv_obj_send_event(lv_obj_t * obj, lv_event_code_t event_code, void * param);
+
+/**
+ * Used by the widgets internally to call the ancestor widget types's event handler
+ * @param class_p   pointer to the class of the widget (NOT the ancestor class)
+ * @param e         pointer to the event descriptor
+ * @return          LV_RESULT_OK: the target object was not deleted in the event; LV_RESULT_INVALID: it was deleted in the event_code
+ */
+lv_result_t lv_obj_event_base(const lv_obj_class_t * class_p, lv_event_t * e);
+
+/**
+ * Get the current target of the event. It's the object which event handler being called.
+ * If the event is not bubbled it's the same as "original" target.
+ * @param e     pointer to the event descriptor
+ * @return      the target of the event_code
+ */
+lv_obj_t * lv_event_get_current_target_obj(lv_event_t * e);
+
+/**
+ * Get the object originally targeted by the event. It's the same even if the event is bubbled.
+ * @param e     pointer to the event descriptor
+ * @return      pointer to the original target of the event_code
+ */
+lv_obj_t * lv_event_get_target_obj(lv_event_t * e);
+
+/**
+ * Add an event handler function for an object.
+ * Used by the user to react on event which happens with the object.
+ * An object can have multiple event handler. They will be called in the same order as they were added.
+ * @param obj       pointer to an object
+ * @param filter    an event code (e.g. `LV_EVENT_CLICKED`) on which the event should be called. `LV_EVENT_ALL` can be used to receive all the events.
+ * @param event_cb  the new event function
+ * @param           user_data custom data will be available in `event_cb`
+ * @return          handler to the event. It can be used in `lv_obj_remove_event_dsc`.
+ */
+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);
+
+uint32_t lv_obj_get_event_count(lv_obj_t * obj);
+
+lv_event_dsc_t * lv_obj_get_event_dsc(lv_obj_t * obj, uint32_t index);
+
+bool lv_obj_remove_event(lv_obj_t * obj, uint32_t index);
+
+bool lv_obj_remove_event_dsc(lv_obj_t * obj, lv_event_dsc_t * dsc);
+
+/**
+ * Remove an event_cb from an object
+ * @param obj           pointer to a obj
+ * @param event_cb      the event_cb of the event to remove
+ * @return              the count of the event removed
+ */
+uint32_t lv_obj_remove_event_cb(lv_obj_t * obj, lv_event_cb_t event_cb);
+
+/**
+ * Remove an event_cb with user_data
+ * @param obj           pointer to a obj
+ * @param event_cb      the event_cb of the event to remove
+ * @param user_data     user_data
+ * @return              the count of the event removed
+ */
+uint32_t lv_obj_remove_event_cb_with_user_data(lv_obj_t * obj, lv_event_cb_t event_cb, void * user_data);
+
+/**
+ * Get the input device passed as parameter to indev related events.
+ * @param e     pointer to an event
+ * @return      the indev that triggered the event or NULL if called on a not indev related event
+ */
+lv_indev_t * lv_event_get_indev(lv_event_t * e);
+
+/**
+ * Get the draw context which should be the first parameter of the draw functions.
+ * Namely: `LV_EVENT_DRAW_MAIN/POST`, `LV_EVENT_DRAW_MAIN/POST_BEGIN`, `LV_EVENT_DRAW_MAIN/POST_END`
+ * @param e     pointer to an event
+ * @return      pointer to a draw context or NULL if called on an unrelated event
+ */
+lv_layer_t * lv_event_get_layer(lv_event_t * e);
+
+/**
+ * Get the old area of the object before its size was changed. Can be used in `LV_EVENT_SIZE_CHANGED`
+ * @param e     pointer to an event
+ * @return      the old absolute area of the object or NULL if called on an unrelated event
+ */
+const lv_area_t * lv_event_get_old_size(lv_event_t * e);
+
+/**
+ * Get the key passed as parameter to an event. Can be used in `LV_EVENT_KEY`
+ * @param e     pointer to an event
+ * @return      the triggering key or NULL if called on an unrelated event
+ */
+uint32_t lv_event_get_key(lv_event_t * e);
+
+/**
+ * Get the signed rotary encoder diff. passed as parameter to an event. Can be used in `LV_EVENT_ROTARY`
+ * @param e     pointer to an event
+ * @return      the triggering key or NULL if called on an unrelated event
+ */
+int32_t lv_event_get_rotary_diff(lv_event_t * e);
+
+/**
+ * Get the animation descriptor of a scrolling. Can be used in `LV_EVENT_SCROLL_BEGIN`
+ * @param e     pointer to an event
+ * @return      the animation that will scroll the object. (can be modified as required)
+ */
+lv_anim_t * lv_event_get_scroll_anim(lv_event_t * e);
+
+/**
+ * Set the new extra draw size. Can be used in `LV_EVENT_REFR_EXT_DRAW_SIZE`
+ * @param e     pointer to an event
+ * @param size  The new extra draw size
+ */
+void lv_event_set_ext_draw_size(lv_event_t * e, int32_t size);
+
+/**
+ * Get a pointer to an `lv_point_t` variable in which the self size should be saved (width in `point->x` and height `point->y`).
+ * Can be used in `LV_EVENT_GET_SELF_SIZE`
+ * @param e     pointer to an event
+ * @return      pointer to `lv_point_t` or NULL if called on an unrelated event
+ */
+lv_point_t * lv_event_get_self_size_info(lv_event_t * e);
+
+/**
+ * Get a pointer to an `lv_hit_test_info_t` variable in which the hit test result should be saved. Can be used in `LV_EVENT_HIT_TEST`
+ * @param e     pointer to an event
+ * @return      pointer to `lv_hit_test_info_t` or NULL if called on an unrelated event
+ */
+lv_hit_test_info_t * lv_event_get_hit_test_info(lv_event_t * e);
+
+/**
+ * Get a pointer to an area which should be examined whether the object fully covers it or not.
+ * Can be used in `LV_EVENT_HIT_TEST`
+ * @param e     pointer to an event
+ * @return      an area with absolute coordinates to check
+ */
+const lv_area_t * lv_event_get_cover_area(lv_event_t * e);
+
+/**
+ * Set the result of cover checking. Can be used in `LV_EVENT_COVER_CHECK`
+ * @param e     pointer to an event
+ * @param res   an element of ::lv_cover_check_info_t
+ */
+void lv_event_set_cover_res(lv_event_t * e, lv_cover_res_t res);
+
+/**
+ * Get the draw task which was just added.
+ * Can be used in `LV_EVENT_DRAW_TASK_ADDED event`
+ * @param e     pointer to an event
+ * @return      the added draw task
+ */
+lv_draw_task_t * lv_event_get_draw_task(lv_event_t * e);
+
+/**********************
+ *      MACROS
+ **********************/
+
+#ifdef __cplusplus
+} /*extern "C"*/
+#endif
+
+#endif /*LV_OBJ_EVENT_H*/

+ 62 - 0
components/easylvgl/lvgl9/src/core/lv_obj_event_private.h

@@ -0,0 +1,62 @@
+/**
+ * @file lv_obj_event_private.h
+ *
+ */
+
+#ifndef LV_OBJ_EVENT_PRIVATE_H
+#define LV_OBJ_EVENT_PRIVATE_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/*********************
+ *      INCLUDES
+ *********************/
+
+#include "lv_obj_event.h"
+
+/*********************
+ *      DEFINES
+ *********************/
+
+/**********************
+ *      TYPEDEFS
+ **********************/
+
+/**
+ * Used as the event parameter of ::LV_EVENT_HIT_TEST to check if an `point` can click the object or not.
+ * `res` should be set like this:
+ *   - If already set to `false` another event wants that point non clickable. If you want to respect it leave it as `false` or set `true` to overwrite it.
+ *   - If already set `true` and `point` shouldn't be clickable set to `false`
+ *   - If already set to `true` you agree that `point` can click the object leave it as `true`
+ */
+struct _lv_hit_test_info_t {
+    const lv_point_t * point;   /**< A point relative to screen to check if it can click the object or not*/
+    bool res;                   /**< true: `point` can click the object; false: it cannot*/
+};
+
+/**
+ * Used as the event parameter of ::LV_EVENT_COVER_CHECK to check if an area is covered by the object or not.
+ * In the event use `const lv_area_t * area = lv_event_get_cover_area(e)` to get the area to check
+ * and `lv_event_set_cover_res(e, res)` to set the result.
+ */
+struct _lv_cover_check_info_t {
+    lv_cover_res_t res;
+    const lv_area_t * area;
+};
+
+
+/**********************
+ * GLOBAL PROTOTYPES
+ **********************/
+
+/**********************
+ *      MACROS
+ **********************/
+
+#ifdef __cplusplus
+} /*extern "C"*/
+#endif
+
+#endif /*LV_OBJ_EVENT_PRIVATE_H*/

+ 122 - 0
components/easylvgl/lvgl9/src/core/lv_obj_id_builtin.c

@@ -0,0 +1,122 @@
+/**
+ * @file lv_obj_id_builtin.c
+ *
+ */
+
+/*********************
+ *      INCLUDES
+ *********************/
+#include "lv_obj_class_private.h"
+#include "lv_obj_private.h"
+#include "lv_global.h"
+#include "../osal/lv_os_private.h"
+#include "../stdlib/lv_sprintf.h"
+
+/*********************
+ *      DEFINES
+ *********************/
+
+/**********************
+ *      TYPEDEFS
+ **********************/
+
+/**********************
+ *  STATIC PROTOTYPES
+ **********************/
+
+typedef struct _class_info_t {
+    const lv_obj_class_t * class_p;
+    uint32_t obj_count;
+} class_info_t;
+
+/**********************
+ *  STATIC VARIABLES
+ **********************/
+
+/**********************
+ *      MACROS
+ **********************/
+
+/**********************
+ *   GLOBAL FUNCTIONS
+ **********************/
+
+#if LV_USE_OBJ_ID && LV_USE_OBJ_ID_BUILTIN
+
+void lv_obj_assign_id(const lv_obj_class_t * class_p, lv_obj_t * obj)
+{
+    LV_ASSERT(obj && class_p);
+
+    uint32_t i;
+    uint32_t id = 0;
+    lv_global_t * global = LV_GLOBAL_DEFAULT();
+    class_info_t * info = NULL;
+
+    if(obj == NULL || class_p == NULL) return;
+    if(global == NULL) return;
+
+    obj->id = NULL;
+
+    for(i = 0; i < global->objid_count; i ++) {
+        info = ((class_info_t *)global->objid_array) + i;
+        if(class_p == info->class_p) break;
+    }
+
+    /*Resize array*/
+    if(i == global->objid_count) {
+        void * array = lv_realloc(global->objid_array, sizeof(class_info_t) * (global->objid_count + 1));
+        LV_ASSERT_MALLOC(array);
+        if(array == NULL) return;
+        global->objid_array = array;
+        global->objid_count ++;
+        info = ((class_info_t *)global->objid_array) + i;
+        info->obj_count = 0;
+        info->class_p = class_p;
+    }
+
+    id = ++info->obj_count;
+
+    obj->id = (void *)(lv_uintptr_t)id;
+}
+
+void lv_obj_set_id(lv_obj_t * obj, void * id)
+{
+    LV_ASSERT_NULL(obj);
+    if(obj->id) lv_obj_free_id(obj);
+    obj->id = id;
+}
+
+void lv_obj_free_id(lv_obj_t * obj)
+{
+    LV_UNUSED(obj);
+    obj->id = NULL;
+}
+
+const char * lv_obj_stringify_id(lv_obj_t * obj, char * buf, uint32_t len)
+{
+    const char * name;
+    if(obj == NULL || obj->class_p == NULL) return NULL;
+    if(buf == NULL) return NULL;
+
+    name = obj->class_p->name;
+    if(name == NULL) name = "nameless";
+
+    lv_snprintf(buf, len, "%s%" LV_PRIu32 "", name, (uint32_t)(lv_uintptr_t)obj->id);
+    return buf;
+}
+
+void lv_objid_builtin_destroy(void)
+{
+    lv_global_t * global = LV_GLOBAL_DEFAULT();
+    if(global == NULL) return;
+
+    lv_free(global->objid_array);
+    global->objid_count = 0;
+}
+
+int lv_obj_id_compare(const void * id1, const void * id2)
+{
+    return id1 == id2 ? 0 : 1;
+}
+
+#endif /*LV_USE_OBJ_ID_BUILTIN*/

+ 1318 - 0
components/easylvgl/lvgl9/src/core/lv_obj_pos.c

@@ -0,0 +1,1318 @@
+/**
+ * @file lv_obj_pos.c
+ *
+ */
+
+/*********************
+ *      INCLUDES
+ *********************/
+#include "../misc/lv_area_private.h"
+#include "../layouts/lv_layout_private.h"
+#include "lv_obj_event_private.h"
+#include "lv_obj_draw_private.h"
+#include "lv_obj_style_private.h"
+#include "lv_obj_private.h"
+#include "../display/lv_display.h"
+#include "../display/lv_display_private.h"
+#include "lv_refr_private.h"
+#include "../core/lv_global.h"
+
+/*********************
+ *      DEFINES
+ *********************/
+#define MY_CLASS (&lv_obj_class)
+#define update_layout_mutex LV_GLOBAL_DEFAULT()->layout_update_mutex
+
+/**********************
+ *      TYPEDEFS
+ **********************/
+
+/**********************
+ *  STATIC PROTOTYPES
+ **********************/
+static int32_t calc_content_width(lv_obj_t * obj);
+static int32_t calc_content_height(lv_obj_t * obj);
+static void layout_update_core(lv_obj_t * obj);
+static void transform_point_array(const lv_obj_t * obj, lv_point_t * p, size_t p_count, bool inv);
+static bool is_transformed(const lv_obj_t * obj);
+
+/**********************
+ *  STATIC VARIABLES
+ **********************/
+
+/**********************
+ *      MACROS
+ **********************/
+
+/**********************
+ *   GLOBAL FUNCTIONS
+ **********************/
+
+void lv_obj_set_pos(lv_obj_t * obj, int32_t x, int32_t y)
+{
+    LV_ASSERT_OBJ(obj, MY_CLASS);
+
+    lv_obj_set_x(obj, x);
+    lv_obj_set_y(obj, y);
+}
+
+void lv_obj_set_x(lv_obj_t * obj, int32_t x)
+{
+    LV_ASSERT_OBJ(obj, MY_CLASS);
+
+    lv_style_res_t res_x;
+    lv_style_value_t v_x;
+
+    res_x = lv_obj_get_local_style_prop(obj, LV_STYLE_X, &v_x, 0);
+
+    if((res_x == LV_STYLE_RES_FOUND && v_x.num != x) || res_x == LV_STYLE_RES_NOT_FOUND) {
+        lv_obj_set_style_x(obj, x, 0);
+    }
+}
+
+void lv_obj_set_y(lv_obj_t * obj, int32_t y)
+{
+    LV_ASSERT_OBJ(obj, MY_CLASS);
+
+    lv_style_res_t res_y;
+    lv_style_value_t v_y;
+
+    res_y = lv_obj_get_local_style_prop(obj, LV_STYLE_Y, &v_y, 0);
+
+    if((res_y == LV_STYLE_RES_FOUND && v_y.num != y) || res_y == LV_STYLE_RES_NOT_FOUND) {
+        lv_obj_set_style_y(obj, y, 0);
+    }
+}
+
+static int32_t calc_dynamic_width(lv_obj_t * obj, int32_t width, int32_t * const content_width)
+{
+    if(width == LV_SIZE_CONTENT) {
+        if(*content_width < 0) {
+            *content_width = calc_content_width(obj);
+        }
+        width = *content_width;
+    }
+    else if(LV_COORD_IS_PCT(width)) {
+        lv_obj_t * parent = lv_obj_get_parent(obj);
+        if(parent->w_layout == 0 && lv_obj_get_style_width(parent, 0) == LV_SIZE_CONTENT) {
+            /*If parent has content size and the child has pct size
+             *a circular dependency will occur. To solve it keep child size at zero */
+            width = lv_obj_get_style_space_left(obj, 0) + lv_obj_get_style_space_right(obj, 0);
+        }
+        else {
+            int32_t parent_w = lv_obj_get_content_width(parent);
+            width = (LV_COORD_GET_PCT(width) * parent_w) / 100;
+            width -= lv_obj_get_style_margin_left(obj, LV_PART_MAIN) + lv_obj_get_style_margin_right(obj, LV_PART_MAIN);
+        }
+    }
+    return width;
+}
+
+static int32_t calc_dynamic_height(lv_obj_t * obj, int32_t height, int32_t * const content_height)
+{
+    if(height == LV_SIZE_CONTENT) {
+        if(*content_height < 0) {
+            *content_height = calc_content_height(obj);
+        }
+        height = *content_height;
+    }
+    else if(LV_COORD_IS_PCT(height)) {
+        lv_obj_t * parent = lv_obj_get_parent(obj);
+        if(parent->h_layout == 0 && lv_obj_get_style_height(parent, 0) == LV_SIZE_CONTENT) {
+            /*If parent has content size and the child has pct size
+             *a circular dependency will occur. To solve it keep child size at zero */
+            height = lv_obj_get_style_space_top(obj, 0) + lv_obj_get_style_space_bottom(obj, 0);
+        }
+
+        else {
+            int32_t parent_h = lv_obj_get_content_height(parent);
+            height = (LV_COORD_GET_PCT(height) * parent_h) / 100;
+            height -=
+                lv_obj_get_style_margin_top(obj, LV_PART_MAIN) + lv_obj_get_style_margin_bottom(obj, LV_PART_MAIN);
+        }
+    }
+    return height;
+}
+
+bool lv_obj_refr_size(lv_obj_t * obj)
+{
+    LV_ASSERT_OBJ(obj, MY_CLASS);
+
+    /*If the width or height is set by a layout do not modify them*/
+    if(obj->w_layout && obj->h_layout) return false;
+
+    lv_obj_t * parent = lv_obj_get_parent(obj);
+    if(parent == NULL) return false;
+
+    int32_t w;
+    if(obj->w_layout) {
+        w = lv_obj_get_width(obj);
+    }
+    else {
+        int32_t content_width = -1;
+        w = calc_dynamic_width(obj, lv_obj_get_style_width(obj, LV_PART_MAIN), &content_width);
+        int32_t minw = calc_dynamic_width(obj, lv_obj_get_style_min_width(obj, LV_PART_MAIN), &content_width);
+        int32_t maxw = calc_dynamic_width(obj, lv_obj_get_style_max_width(obj, LV_PART_MAIN), &content_width);
+        w = LV_CLAMP(minw, w, maxw);
+    }
+
+    int32_t h;
+    if(obj->h_layout) {
+        h = lv_obj_get_height(obj);
+    }
+    else {
+        int32_t content_height = -1;
+        h = calc_dynamic_height(obj, lv_obj_get_style_height(obj, LV_PART_MAIN), &content_height);
+        int32_t minh = calc_dynamic_height(obj, lv_obj_get_style_min_height(obj, LV_PART_MAIN), &content_height);
+        int32_t maxh = calc_dynamic_height(obj, lv_obj_get_style_max_height(obj, LV_PART_MAIN), &content_height);
+        h = LV_CLAMP(minh, h, maxh);
+    }
+
+    /*Do nothing if the size is not changed*/
+    /*It is very important else recursive resizing can occur without size change*/
+    if(lv_obj_get_width(obj) == w && lv_obj_get_height(obj) == h)
+        return false;
+
+    /*Invalidate the original area*/
+    lv_obj_invalidate(obj);
+
+    /*Save the original coordinates*/
+    lv_area_t ori;
+    lv_obj_get_coords(obj, &ori);
+
+    /*Check if the object inside the parent or not*/
+    lv_area_t parent_fit_area;
+    lv_obj_get_content_coords(parent, &parent_fit_area);
+
+    /*If the object is already out of the parent and its position is changes
+     *surely the scrollbars also changes so invalidate them*/
+    bool on1 = lv_area_is_in(&ori, &parent_fit_area, 0);
+    if(!on1)
+        lv_obj_scrollbar_invalidate(parent);
+
+    /*Set the length and height
+     *Be sure the content is not scrolled in an invalid position on the new size*/
+    obj->coords.y2 = obj->coords.y1 + h - 1;
+    if(lv_obj_get_style_base_dir(obj, LV_PART_MAIN) == LV_BASE_DIR_RTL) {
+        obj->coords.x1 = obj->coords.x2 - w + 1;
+    }
+    else {
+        obj->coords.x2 = obj->coords.x1 + w - 1;
+    }
+
+    /*Call the ancestor's event handler to the object with its new coordinates*/
+    lv_obj_send_event(obj, LV_EVENT_SIZE_CHANGED, &ori);
+
+    /*Call the ancestor's event handler to the parent too*/
+    lv_obj_send_event(parent, LV_EVENT_CHILD_CHANGED, obj);
+
+    /*Invalidate the new area*/
+    lv_obj_invalidate(obj);
+
+    obj->readjust_scroll_after_layout = 1;
+
+    /*If the object was out of the parent invalidate the new scrollbar area too.
+     *If it wasn't out of the parent but out now, also invalidate the scrollbars*/
+    bool on2 = lv_area_is_in(&obj->coords, &parent_fit_area, 0);
+    if(on1 || (!on1 && on2))
+        lv_obj_scrollbar_invalidate(parent);
+
+    lv_obj_refresh_ext_draw_size(obj);
+
+    return true;
+}
+
+void lv_obj_set_size(lv_obj_t * obj, int32_t w, int32_t h)
+{
+    LV_ASSERT_OBJ(obj, MY_CLASS);
+
+    lv_obj_set_width(obj, w);
+    lv_obj_set_height(obj, h);
+}
+
+void lv_obj_set_width(lv_obj_t * obj, int32_t w)
+{
+    LV_ASSERT_OBJ(obj, MY_CLASS);
+    lv_style_res_t res_w;
+    lv_style_value_t v_w;
+
+    res_w = lv_obj_get_local_style_prop(obj, LV_STYLE_WIDTH, &v_w, 0);
+
+    if((res_w == LV_STYLE_RES_FOUND && v_w.num != w) || res_w == LV_STYLE_RES_NOT_FOUND) {
+        lv_obj_set_style_width(obj, w, 0);
+    }
+}
+
+void lv_obj_set_height(lv_obj_t * obj, int32_t h)
+{
+    LV_ASSERT_OBJ(obj, MY_CLASS);
+    lv_style_res_t res_h;
+    lv_style_value_t v_h;
+
+    res_h = lv_obj_get_local_style_prop(obj, LV_STYLE_HEIGHT, &v_h, 0);
+
+    if((res_h == LV_STYLE_RES_FOUND && v_h.num != h) || res_h == LV_STYLE_RES_NOT_FOUND) {
+        lv_obj_set_style_height(obj, h, 0);
+    }
+}
+
+void lv_obj_set_content_width(lv_obj_t * obj, int32_t w)
+{
+    int32_t left = lv_obj_get_style_space_left(obj, LV_PART_MAIN);
+    int32_t right = lv_obj_get_style_space_right(obj, LV_PART_MAIN);
+    lv_obj_set_width(obj, w + left + right);
+}
+
+void lv_obj_set_content_height(lv_obj_t * obj, int32_t h)
+{
+    int32_t top = lv_obj_get_style_space_top(obj, LV_PART_MAIN);
+    int32_t bottom = lv_obj_get_style_space_bottom(obj, LV_PART_MAIN);
+    lv_obj_set_height(obj, h + top + bottom);
+}
+
+void lv_obj_set_layout(lv_obj_t * obj, uint32_t layout)
+{
+    LV_ASSERT_OBJ(obj, MY_CLASS);
+
+    lv_obj_set_style_layout(obj, layout, 0);
+
+    lv_obj_mark_layout_as_dirty(obj);
+}
+
+bool lv_obj_is_layout_positioned(const lv_obj_t * obj)
+{
+    if(lv_obj_has_flag_any(obj, LV_OBJ_FLAG_HIDDEN | LV_OBJ_FLAG_IGNORE_LAYOUT | LV_OBJ_FLAG_FLOATING)) return false;
+
+    lv_obj_t * parent = lv_obj_get_parent(obj);
+    if(parent == NULL) return false;
+
+    uint32_t layout = lv_obj_get_style_layout(parent, LV_PART_MAIN);
+    if(layout) return true;
+    else return false;
+}
+
+void lv_obj_mark_layout_as_dirty(lv_obj_t * obj)
+{
+    obj->layout_inv = 1;
+
+    /*Mark the screen as dirty too to mark that there is something to do on this screen*/
+    lv_obj_t * scr = lv_obj_get_screen(obj);
+    scr->scr_layout_inv = 1;
+
+    /*Make the display refreshing*/
+    lv_display_t * disp = lv_obj_get_display(scr);
+    lv_display_send_event(disp, LV_EVENT_REFR_REQUEST, NULL);
+}
+
+void lv_obj_update_layout(const lv_obj_t * obj)
+{
+    if(update_layout_mutex) {
+        LV_LOG_TRACE("Already running, returning");
+        return;
+    }
+    LV_PROFILER_LAYOUT_BEGIN;
+    update_layout_mutex = true;
+
+    lv_obj_t * scr = lv_obj_get_screen(obj);
+    /*Repeat until there are no more layout invalidations*/
+    while(scr->scr_layout_inv) {
+        LV_LOG_TRACE("Layout update begin");
+        scr->scr_layout_inv = 0;
+        layout_update_core(scr);
+        LV_LOG_TRACE("Layout update end");
+    }
+
+    update_layout_mutex = false;
+    LV_PROFILER_LAYOUT_END;
+}
+
+void lv_obj_set_align(lv_obj_t * obj, lv_align_t align)
+{
+    lv_obj_set_style_align(obj, align, 0);
+}
+
+void lv_obj_align(lv_obj_t * obj, lv_align_t align, int32_t x_ofs, int32_t y_ofs)
+{
+    lv_obj_set_style_align(obj, align, 0);
+    lv_obj_set_pos(obj, x_ofs, y_ofs);
+}
+
+void lv_obj_align_to(lv_obj_t * obj, const lv_obj_t * base, lv_align_t align, int32_t x_ofs, int32_t y_ofs)
+{
+    LV_ASSERT_OBJ(obj, MY_CLASS);
+
+    lv_obj_update_layout(obj);
+    if(base == NULL) base = lv_obj_get_parent(obj);
+
+    LV_ASSERT_OBJ(base, MY_CLASS);
+
+    int32_t x = 0;
+    int32_t y = 0;
+
+    lv_obj_t * parent = lv_obj_get_parent(obj);
+
+    LV_ASSERT_OBJ(parent, MY_CLASS);
+
+    int32_t pleft = lv_obj_get_style_space_left(parent, LV_PART_MAIN);
+    int32_t ptop = lv_obj_get_style_space_top(parent, LV_PART_MAIN);
+
+    int32_t bleft = lv_obj_get_style_space_left(base, LV_PART_MAIN);
+    int32_t btop = lv_obj_get_style_space_top(base, LV_PART_MAIN);
+
+    if(align == LV_ALIGN_DEFAULT) {
+        if(lv_obj_get_style_base_dir(base, LV_PART_MAIN) == LV_BASE_DIR_RTL) align = LV_ALIGN_TOP_RIGHT;
+        else align = LV_ALIGN_TOP_LEFT;
+    }
+
+    switch(align) {
+        case LV_ALIGN_CENTER:
+            x = lv_obj_get_content_width(base) / 2 - lv_obj_get_width(obj) / 2 + bleft;
+            y = lv_obj_get_content_height(base) / 2 - lv_obj_get_height(obj) / 2 + btop;
+            break;
+
+        case LV_ALIGN_TOP_LEFT:
+            x = bleft;
+            y = btop;
+            break;
+
+        case LV_ALIGN_TOP_MID:
+            x = lv_obj_get_content_width(base) / 2 - lv_obj_get_width(obj) / 2 + bleft;
+            y = btop;
+            break;
+
+        case LV_ALIGN_TOP_RIGHT:
+            x = lv_obj_get_content_width(base) - lv_obj_get_width(obj) + bleft;
+            y = btop;
+            break;
+
+        case LV_ALIGN_BOTTOM_LEFT:
+            x = bleft;
+            y = lv_obj_get_content_height(base) - lv_obj_get_height(obj) + btop;
+            break;
+        case LV_ALIGN_BOTTOM_MID:
+            x = lv_obj_get_content_width(base) / 2 - lv_obj_get_width(obj) / 2 + bleft;
+            y = lv_obj_get_content_height(base) - lv_obj_get_height(obj) + btop;
+            break;
+
+        case LV_ALIGN_BOTTOM_RIGHT:
+            x = lv_obj_get_content_width(base) - lv_obj_get_width(obj) + bleft;
+            y = lv_obj_get_content_height(base) - lv_obj_get_height(obj) + btop;
+            break;
+
+        case LV_ALIGN_LEFT_MID:
+            x = bleft;
+            y = lv_obj_get_content_height(base) / 2 - lv_obj_get_height(obj) / 2 + btop;
+            break;
+
+        case LV_ALIGN_RIGHT_MID:
+            x = lv_obj_get_content_width(base) - lv_obj_get_width(obj) + bleft;
+            y = lv_obj_get_content_height(base) / 2 - lv_obj_get_height(obj) / 2 + btop;
+            break;
+
+        case LV_ALIGN_OUT_TOP_LEFT:
+            x = 0;
+            y = -lv_obj_get_height(obj);
+            break;
+
+        case LV_ALIGN_OUT_TOP_MID:
+            x = lv_obj_get_width(base) / 2 - lv_obj_get_width(obj) / 2;
+            y = -lv_obj_get_height(obj);
+            break;
+
+        case LV_ALIGN_OUT_TOP_RIGHT:
+            x = lv_obj_get_width(base) - lv_obj_get_width(obj);
+            y = -lv_obj_get_height(obj);
+            break;
+
+        case LV_ALIGN_OUT_BOTTOM_LEFT:
+            x = 0;
+            y = lv_obj_get_height(base);
+            break;
+
+        case LV_ALIGN_OUT_BOTTOM_MID:
+            x = lv_obj_get_width(base) / 2 - lv_obj_get_width(obj) / 2;
+            y = lv_obj_get_height(base);
+            break;
+
+        case LV_ALIGN_OUT_BOTTOM_RIGHT:
+            x = lv_obj_get_width(base) - lv_obj_get_width(obj);
+            y = lv_obj_get_height(base);
+            break;
+
+        case LV_ALIGN_OUT_LEFT_TOP:
+            x = -lv_obj_get_width(obj);
+            y = 0;
+            break;
+
+        case LV_ALIGN_OUT_LEFT_MID:
+            x = -lv_obj_get_width(obj);
+            y = lv_obj_get_height(base) / 2 - lv_obj_get_height(obj) / 2;
+            break;
+
+        case LV_ALIGN_OUT_LEFT_BOTTOM:
+            x = -lv_obj_get_width(obj);
+            y = lv_obj_get_height(base) - lv_obj_get_height(obj);
+            break;
+
+        case LV_ALIGN_OUT_RIGHT_TOP:
+            x = lv_obj_get_width(base);
+            y = 0;
+            break;
+
+        case LV_ALIGN_OUT_RIGHT_MID:
+            x = lv_obj_get_width(base);
+            y = lv_obj_get_height(base) / 2 - lv_obj_get_height(obj) / 2;
+            break;
+
+        case LV_ALIGN_OUT_RIGHT_BOTTOM:
+            x = lv_obj_get_width(base);
+            y = lv_obj_get_height(base) - lv_obj_get_height(obj);
+            break;
+
+        case LV_ALIGN_DEFAULT:
+            break;
+    }
+
+    if(LV_COORD_IS_PCT(x_ofs)) x_ofs = (lv_obj_get_width(base) * LV_COORD_GET_PCT(x_ofs)) / 100;
+    if(LV_COORD_IS_PCT(y_ofs)) y_ofs = (lv_obj_get_height(base) * LV_COORD_GET_PCT(y_ofs)) / 100;
+    if(lv_obj_get_style_base_dir(parent, LV_PART_MAIN) == LV_BASE_DIR_RTL) {
+        x += x_ofs + base->coords.x1 - parent->coords.x1 + lv_obj_get_scroll_right(parent) - pleft;
+    }
+    else {
+        x += x_ofs + base->coords.x1 - parent->coords.x1 + lv_obj_get_scroll_left(parent) - pleft;
+    }
+    y += y_ofs + base->coords.y1 - parent->coords.y1 + lv_obj_get_scroll_top(parent) - ptop;
+    lv_obj_set_style_align(obj, LV_ALIGN_TOP_LEFT, 0);
+    lv_obj_set_pos(obj, x, y);
+
+}
+
+void lv_obj_get_coords(const lv_obj_t * obj, lv_area_t * coords)
+{
+    LV_ASSERT_OBJ(obj, MY_CLASS);
+
+    lv_area_copy(coords, &obj->coords);
+}
+
+int32_t lv_obj_get_x(const lv_obj_t * obj)
+{
+    LV_ASSERT_OBJ(obj, MY_CLASS);
+
+    int32_t rel_x;
+    lv_obj_t * parent = lv_obj_get_parent(obj);
+    if(parent) {
+        rel_x  = obj->coords.x1 - parent->coords.x1;
+        rel_x += lv_obj_get_scroll_x(parent);
+        rel_x -= lv_obj_get_style_space_left(parent, LV_PART_MAIN);
+    }
+    else {
+        rel_x = obj->coords.x1;
+    }
+    return rel_x;
+}
+
+int32_t lv_obj_get_x2(const lv_obj_t * obj)
+{
+    LV_ASSERT_OBJ(obj, MY_CLASS);
+
+    return lv_obj_get_x(obj) + lv_obj_get_width(obj);
+}
+
+int32_t lv_obj_get_y(const lv_obj_t * obj)
+{
+    LV_ASSERT_OBJ(obj, MY_CLASS);
+
+    int32_t rel_y;
+    lv_obj_t * parent = lv_obj_get_parent(obj);
+    if(parent) {
+        rel_y = obj->coords.y1 - parent->coords.y1;
+        rel_y += lv_obj_get_scroll_y(parent);
+        rel_y -= lv_obj_get_style_space_top(parent, LV_PART_MAIN);
+    }
+    else {
+        rel_y = obj->coords.y1;
+    }
+    return rel_y;
+}
+
+int32_t lv_obj_get_y2(const lv_obj_t * obj)
+{
+    LV_ASSERT_OBJ(obj, MY_CLASS);
+
+    return lv_obj_get_y(obj) + lv_obj_get_height(obj);
+}
+
+int32_t lv_obj_get_x_aligned(const lv_obj_t * obj)
+{
+    return lv_obj_get_style_x(obj, LV_PART_MAIN);
+}
+
+int32_t lv_obj_get_y_aligned(const lv_obj_t * obj)
+{
+    return lv_obj_get_style_y(obj, LV_PART_MAIN);
+}
+
+int32_t lv_obj_get_width(const lv_obj_t * obj)
+{
+    LV_ASSERT_OBJ(obj, MY_CLASS);
+
+    return lv_area_get_width(&obj->coords);
+}
+
+int32_t lv_obj_get_height(const lv_obj_t * obj)
+{
+    LV_ASSERT_OBJ(obj, MY_CLASS);
+
+    return lv_area_get_height(&obj->coords);
+}
+
+int32_t lv_obj_get_content_width(const lv_obj_t * obj)
+{
+    LV_ASSERT_OBJ(obj, MY_CLASS);
+
+    int32_t left = lv_obj_get_style_space_left(obj, LV_PART_MAIN);
+    int32_t right = lv_obj_get_style_space_right(obj, LV_PART_MAIN);
+
+    return lv_obj_get_width(obj) - left - right;
+}
+
+int32_t lv_obj_get_content_height(const lv_obj_t * obj)
+{
+    LV_ASSERT_OBJ(obj, MY_CLASS);
+
+    int32_t top = lv_obj_get_style_space_top(obj, LV_PART_MAIN);
+    int32_t bottom = lv_obj_get_style_space_bottom(obj, LV_PART_MAIN);
+
+    return lv_obj_get_height(obj) - top - bottom;
+}
+
+void lv_obj_get_content_coords(const lv_obj_t * obj, lv_area_t * area)
+{
+    LV_ASSERT_OBJ(obj, MY_CLASS);
+
+    lv_obj_get_coords(obj, area);
+    area->x1 += lv_obj_get_style_space_left(obj, LV_PART_MAIN);
+    area->x2 -= lv_obj_get_style_space_right(obj, LV_PART_MAIN);
+    area->y1 += lv_obj_get_style_space_top(obj, LV_PART_MAIN);
+    area->y2 -= lv_obj_get_style_space_bottom(obj, LV_PART_MAIN);
+
+}
+
+int32_t lv_obj_get_self_width(const lv_obj_t * obj)
+{
+    lv_point_t p = {0, LV_COORD_MIN};
+    lv_obj_send_event((lv_obj_t *)obj, LV_EVENT_GET_SELF_SIZE, &p);
+    return p.x;
+}
+
+int32_t lv_obj_get_self_height(const lv_obj_t * obj)
+{
+    lv_point_t p = {LV_COORD_MIN, 0};
+    lv_obj_send_event((lv_obj_t *)obj, LV_EVENT_GET_SELF_SIZE, &p);
+    return p.y;
+}
+
+bool lv_obj_refresh_self_size(lv_obj_t * obj)
+{
+    int32_t w_set = lv_obj_get_style_width(obj, LV_PART_MAIN);
+    int32_t h_set = lv_obj_get_style_height(obj, LV_PART_MAIN);
+    if(w_set != LV_SIZE_CONTENT && h_set != LV_SIZE_CONTENT) return false;
+
+    lv_obj_mark_layout_as_dirty(obj);
+    return true;
+}
+
+void lv_obj_refr_pos(lv_obj_t * obj)
+{
+    if(lv_obj_is_layout_positioned(obj)) return;
+
+    lv_obj_t * parent = lv_obj_get_parent(obj);
+    int32_t x = lv_obj_get_style_x(obj, LV_PART_MAIN);
+    int32_t y = lv_obj_get_style_y(obj, LV_PART_MAIN);
+
+    if(parent == NULL) {
+        lv_obj_move_to(obj, x, y);
+        return;
+    }
+
+    /*Handle percentage value*/
+    int32_t pw = lv_obj_get_content_width(parent);
+    int32_t ph = lv_obj_get_content_height(parent);
+    if(LV_COORD_IS_PCT(x)) {
+        if(lv_obj_get_style_width(parent, LV_PART_MAIN) == LV_SIZE_CONTENT) x = 0; /*Avoid circular dependency*/
+        else x = (pw * LV_COORD_GET_PCT(x)) / 100;
+    }
+
+    if(LV_COORD_IS_PCT(y)) {
+        if(lv_obj_get_style_height(parent, LV_PART_MAIN) == LV_SIZE_CONTENT) y = 0; /*Avoid circular dependency*/
+        y = (ph * LV_COORD_GET_PCT(y)) / 100;
+    }
+
+    /*Handle percentage value of translate*/
+    int32_t tr_x = lv_obj_get_style_translate_x(obj, LV_PART_MAIN);
+    int32_t tr_y = lv_obj_get_style_translate_y(obj, LV_PART_MAIN);
+    int32_t w = lv_obj_get_width(obj);
+    int32_t h = lv_obj_get_height(obj);
+    if(LV_COORD_IS_PCT(tr_x)) tr_x = (w * LV_COORD_GET_PCT(tr_x)) / 100;
+    if(LV_COORD_IS_PCT(tr_y)) tr_y = (h * LV_COORD_GET_PCT(tr_y)) / 100;
+
+    /*Use the translation*/
+    x += tr_x;
+    y += tr_y;
+
+    lv_align_t align = lv_obj_get_style_align(obj, LV_PART_MAIN);
+
+    if(align == LV_ALIGN_DEFAULT) {
+        if(lv_obj_get_style_base_dir(parent, LV_PART_MAIN) == LV_BASE_DIR_RTL) align = LV_ALIGN_TOP_RIGHT;
+        else align = LV_ALIGN_TOP_LEFT;
+    }
+
+    switch(align) {
+        case LV_ALIGN_TOP_LEFT:
+            break;
+        case LV_ALIGN_TOP_MID:
+            x += pw / 2 - w / 2;
+            break;
+        case LV_ALIGN_TOP_RIGHT:
+            x += pw - w;
+            break;
+        case LV_ALIGN_LEFT_MID:
+            y += ph / 2 - h / 2;
+            break;
+        case LV_ALIGN_BOTTOM_LEFT:
+            y += ph - h;
+            break;
+        case LV_ALIGN_BOTTOM_MID:
+            x += pw / 2 - w / 2;
+            y += ph - h;
+            break;
+        case LV_ALIGN_BOTTOM_RIGHT:
+            x += pw - w;
+            y += ph - h;
+            break;
+        case LV_ALIGN_RIGHT_MID:
+            x += pw - w;
+            y += ph / 2 - h / 2;
+            break;
+        case LV_ALIGN_CENTER:
+            x += pw / 2 - w / 2;
+            y += ph / 2 - h / 2;
+            break;
+        default:
+            break;
+    }
+
+    lv_obj_move_to(obj, x, y);
+}
+
+void lv_obj_move_to(lv_obj_t * obj, int32_t x, int32_t y)
+{
+    /*Convert x and y to absolute coordinates*/
+    lv_obj_t * parent = obj->parent;
+
+    if(parent) {
+        if(lv_obj_has_flag(obj, LV_OBJ_FLAG_FLOATING)) {
+            x += parent->coords.x1;
+            y += parent->coords.y1;
+        }
+        else {
+            x += parent->coords.x1 - lv_obj_get_scroll_x(parent);
+            y += parent->coords.y1 - lv_obj_get_scroll_y(parent);
+        }
+
+        x += lv_obj_get_style_space_left(parent, LV_PART_MAIN);
+        y += lv_obj_get_style_space_top(parent, LV_PART_MAIN);
+    }
+
+    /*Calculate and set the movement*/
+    lv_point_t diff;
+    diff.x = x - obj->coords.x1;
+    diff.y = y - obj->coords.y1;
+
+    /*Do nothing if the position is not changed*/
+    /*It is very important else recursive positioning can
+     *occur without position change*/
+    if(diff.x == 0 && diff.y == 0) return;
+
+    /*Invalidate the original area*/
+    lv_obj_invalidate(obj);
+
+    /*Save the original coordinates*/
+    lv_area_t ori;
+    lv_obj_get_coords(obj, &ori);
+
+    /*Check if the object inside the parent or not*/
+    lv_area_t parent_fit_area;
+    bool on1 = false;
+    if(parent) {
+        lv_obj_get_content_coords(parent, &parent_fit_area);
+
+        /*If the object is already out of the parent and its position is changes
+         *surely the scrollbars also changes so invalidate them*/
+        on1 = lv_area_is_in(&ori, &parent_fit_area, 0);
+        if(!on1) lv_obj_scrollbar_invalidate(parent);
+    }
+
+    obj->coords.x1 += diff.x;
+    obj->coords.y1 += diff.y;
+    obj->coords.x2 += diff.x;
+    obj->coords.y2 += diff.y;
+
+    lv_obj_move_children_by(obj, diff.x, diff.y, false);
+
+    /*Call the ancestor's event handler to the parent too*/
+    if(parent) lv_obj_send_event(parent, LV_EVENT_CHILD_CHANGED, obj);
+
+    /*Invalidate the new area*/
+    lv_obj_invalidate(obj);
+
+    /*If the object was out of the parent invalidate the new scrollbar area too.
+     *If it wasn't out of the parent but out now, also invalidate the scrollbars*/
+    if(parent) {
+        bool on2 = lv_area_is_in(&obj->coords, &parent_fit_area, 0);
+        if(on1 || (!on1 && on2)) lv_obj_scrollbar_invalidate(parent);
+    }
+}
+
+void lv_obj_move_children_by(lv_obj_t * obj, int32_t x_diff, int32_t y_diff, bool ignore_floating)
+{
+    uint32_t i;
+    uint32_t child_cnt = lv_obj_get_child_count(obj);
+    for(i = 0; i < child_cnt; i++) {
+        lv_obj_t * child = obj->spec_attr->children[i];
+        if(ignore_floating && lv_obj_has_flag(child, LV_OBJ_FLAG_FLOATING)) continue;
+        child->coords.x1 += x_diff;
+        child->coords.y1 += y_diff;
+        child->coords.x2 += x_diff;
+        child->coords.y2 += y_diff;
+
+        lv_obj_move_children_by(child, x_diff, y_diff, false);
+    }
+}
+
+void lv_obj_transform_point(const lv_obj_t * obj, lv_point_t * p, lv_obj_point_transform_flag_t flags)
+{
+    lv_obj_transform_point_array(obj, p, 1, flags);
+}
+
+void lv_obj_transform_point_array(const lv_obj_t * obj, lv_point_t points[], size_t count,
+                                  lv_obj_point_transform_flag_t flags)
+{
+    if(obj) {
+        lv_layer_type_t layer_type = lv_obj_get_layer_type(obj);
+        bool do_tranf = layer_type == LV_LAYER_TYPE_TRANSFORM;
+        bool recursive = flags & LV_OBJ_POINT_TRANSFORM_FLAG_RECURSIVE;
+        bool inverse = flags & LV_OBJ_POINT_TRANSFORM_FLAG_INVERSE;
+        if(inverse) {
+            if(recursive) lv_obj_transform_point_array(lv_obj_get_parent(obj), points, count, flags);
+            if(do_tranf) transform_point_array(obj, points, count, inverse);
+        }
+        else {
+            if(do_tranf) transform_point_array(obj, points, count, inverse);
+            if(recursive) lv_obj_transform_point_array(lv_obj_get_parent(obj), points, count, flags);
+        }
+    }
+}
+
+void lv_obj_get_transformed_area(const lv_obj_t * obj, lv_area_t * area, lv_obj_point_transform_flag_t flags)
+{
+    lv_point_t p[4] = {
+        {area->x1, area->y1},
+        {area->x1, area->y2 + 1},
+        {area->x2 + 1, area->y1},
+        {area->x2 + 1, area->y2 + 1},
+    };
+
+    lv_obj_transform_point_array(obj, p, 4, flags);
+
+    area->x1 = LV_MIN4(p[0].x, p[1].x, p[2].x, p[3].x);
+    area->x2 = LV_MAX4(p[0].x, p[1].x, p[2].x, p[3].x);
+    area->y1 = LV_MIN4(p[0].y, p[1].y, p[2].y, p[3].y);
+    area->y2 = LV_MAX4(p[0].y, p[1].y, p[2].y, p[3].y);
+}
+
+void lv_obj_invalidate_area(const lv_obj_t * obj, const lv_area_t * area)
+{
+    LV_ASSERT_OBJ(obj, MY_CLASS);
+
+    lv_display_t * disp   = lv_obj_get_display(obj);
+    if(!lv_display_is_invalidation_enabled(disp)) return;
+
+    lv_area_t area_tmp;
+    lv_area_copy(&area_tmp, area);
+
+    if(!lv_obj_area_is_visible(obj, &area_tmp)) return;
+#if LV_DRAW_TRANSFORM_USE_MATRIX
+    /**
+     * When using the global matrix, the vertex coordinates of clip_area lose precision after transformation,
+     * which can be solved by expanding the redrawing area.
+     */
+    lv_area_increase(&area_tmp, 5, 5);
+#else
+    if(obj->spec_attr && obj->spec_attr->layer_type == LV_LAYER_TYPE_TRANSFORM) {
+        /*Make the area slightly larger to avoid rounding errors.
+         *5 is an empirical value*/
+        lv_area_increase(&area_tmp, 5, 5);
+    }
+#endif
+
+    lv_inv_area(lv_obj_get_display(obj),  &area_tmp);
+}
+
+void lv_obj_invalidate(const lv_obj_t * obj)
+{
+    LV_ASSERT_OBJ(obj, MY_CLASS);
+
+    /*Truncate the area to the object*/
+    lv_area_t obj_coords;
+    int32_t ext_size = lv_obj_get_ext_draw_size(obj);
+    lv_area_copy(&obj_coords, &obj->coords);
+    obj_coords.x1 -= ext_size;
+    obj_coords.y1 -= ext_size;
+    obj_coords.x2 += ext_size;
+    obj_coords.y2 += ext_size;
+
+    lv_obj_invalidate_area(obj, &obj_coords);
+}
+
+bool lv_obj_area_is_visible(const lv_obj_t * obj, lv_area_t * area)
+{
+    if(lv_obj_has_flag(obj, LV_OBJ_FLAG_HIDDEN)) return false;
+
+    /*Invalidate the object only if it belongs to the current or previous or one of the layers'*/
+    lv_obj_t * obj_scr = lv_obj_get_screen(obj);
+    lv_display_t * disp   = lv_obj_get_display(obj_scr);
+    if(obj_scr != lv_display_get_screen_active(disp) &&
+       obj_scr != lv_display_get_screen_prev(disp) &&
+       obj_scr != lv_display_get_layer_bottom(disp) &&
+       obj_scr != lv_display_get_layer_top(disp) &&
+       obj_scr != lv_display_get_layer_sys(disp)) {
+        return false;
+    }
+
+    /*Truncate the area to the object*/
+    lv_area_t obj_coords;
+    int32_t ext_size = lv_obj_get_ext_draw_size(obj);
+    lv_area_copy(&obj_coords, &obj->coords);
+    lv_area_increase(&obj_coords, ext_size, ext_size);
+
+    /*The area is not on the object*/
+    if(!lv_area_intersect(area, area, &obj_coords)) return false;
+
+    if(is_transformed(obj)) {
+        lv_obj_get_transformed_area(obj, area, LV_OBJ_POINT_TRANSFORM_FLAG_RECURSIVE);
+    }
+
+    /*Truncate recursively to the parents*/
+    lv_obj_t * parent = lv_obj_get_parent(obj);
+    while(parent != NULL) {
+        /*If the parent is hidden then the child is hidden and won't be drawn*/
+        if(lv_obj_has_flag(parent, LV_OBJ_FLAG_HIDDEN)) return false;
+
+        /*Truncate to the parent and if no common parts break*/
+        lv_area_t parent_coords = parent->coords;
+        if(lv_obj_has_flag(parent, LV_OBJ_FLAG_OVERFLOW_VISIBLE)) {
+            int32_t parent_ext_size = lv_obj_get_ext_draw_size(parent);
+            lv_area_increase(&parent_coords, parent_ext_size, parent_ext_size);
+        }
+
+        if(is_transformed(parent)) {
+            lv_obj_get_transformed_area(parent, &parent_coords, LV_OBJ_POINT_TRANSFORM_FLAG_RECURSIVE);
+        }
+        if(!lv_area_intersect(area, area, &parent_coords)) return false;
+
+        parent = lv_obj_get_parent(parent);
+    }
+
+    return true;
+}
+
+bool lv_obj_is_visible(const lv_obj_t * obj)
+{
+    LV_ASSERT_OBJ(obj, MY_CLASS);
+
+    lv_area_t obj_coords;
+    int32_t ext_size = lv_obj_get_ext_draw_size(obj);
+    lv_area_copy(&obj_coords, &obj->coords);
+    obj_coords.x1 -= ext_size;
+    obj_coords.y1 -= ext_size;
+    obj_coords.x2 += ext_size;
+    obj_coords.y2 += ext_size;
+
+    return lv_obj_area_is_visible(obj, &obj_coords);
+}
+
+void lv_obj_set_ext_click_area(lv_obj_t * obj, int32_t size)
+{
+    LV_ASSERT_OBJ(obj, MY_CLASS);
+
+    lv_obj_allocate_spec_attr(obj);
+    obj->spec_attr->ext_click_pad = size;
+}
+
+void lv_obj_get_click_area(const lv_obj_t * obj, lv_area_t * area)
+{
+    lv_area_copy(area, &obj->coords);
+    if(obj->spec_attr) {
+        lv_area_increase(area, obj->spec_attr->ext_click_pad, obj->spec_attr->ext_click_pad);
+    }
+}
+
+bool lv_obj_hit_test(lv_obj_t * obj, const lv_point_t * point)
+{
+    if(!lv_obj_has_flag(obj, LV_OBJ_FLAG_CLICKABLE)) return false;
+
+    lv_area_t a;
+    lv_obj_get_click_area(obj, &a);
+    bool res = lv_area_is_point_on(&a, point, 0);
+    if(res == false) return false;
+
+    if(lv_obj_has_flag(obj, LV_OBJ_FLAG_ADV_HITTEST)) {
+        lv_hit_test_info_t hit_info;
+        hit_info.point = point;
+        hit_info.res = true;
+        lv_obj_send_event(obj, LV_EVENT_HIT_TEST, &hit_info);
+        return hit_info.res;
+    }
+
+    return res;
+}
+
+int32_t lv_clamp_width(int32_t width, int32_t min_width, int32_t max_width, int32_t ref_width)
+{
+    if(LV_COORD_IS_PCT(min_width)) min_width = (ref_width * LV_COORD_GET_PCT(min_width)) / 100;
+    if(LV_COORD_IS_PCT(max_width)) max_width = (ref_width * LV_COORD_GET_PCT(max_width)) / 100;
+    return LV_CLAMP(min_width, width, max_width);
+}
+
+int32_t lv_clamp_height(int32_t height, int32_t min_height, int32_t max_height, int32_t ref_height)
+{
+    if(LV_COORD_IS_PCT(min_height)) min_height = (ref_height * LV_COORD_GET_PCT(min_height)) / 100;
+    if(LV_COORD_IS_PCT(max_height)) max_height = (ref_height * LV_COORD_GET_PCT(max_height)) / 100;
+    return LV_CLAMP(min_height, height, max_height);
+}
+
+void lv_obj_center(lv_obj_t * obj)
+{
+    lv_obj_align(obj, LV_ALIGN_CENTER, 0, 0);
+}
+
+void lv_obj_set_transform(lv_obj_t * obj, const lv_matrix_t * matrix)
+{
+#if LV_DRAW_TRANSFORM_USE_MATRIX
+    LV_ASSERT_OBJ(obj, MY_CLASS);
+
+    if(!matrix) {
+        lv_obj_reset_transform(obj);
+        return;
+    }
+
+    lv_obj_allocate_spec_attr(obj);
+    if(!obj->spec_attr->matrix) {
+        obj->spec_attr->matrix = lv_malloc(sizeof(lv_matrix_t));
+        LV_ASSERT_MALLOC(obj->spec_attr->matrix);
+    }
+
+    /* Invalidate the old area */
+    lv_obj_invalidate(obj);
+
+    /* Copy the matrix */
+    *obj->spec_attr->matrix = *matrix;
+
+    /* Matrix is set. Update the layer type */
+    lv_obj_update_layer_type(obj);
+
+    /* Invalidate the new area */
+    lv_obj_invalidate(obj);
+#else
+    LV_UNUSED(obj);
+    LV_UNUSED(matrix);
+    LV_LOG_WARN("Transform matrix is not used because LV_DRAW_TRANSFORM_USE_MATRIX is disabled");
+#endif
+}
+
+void lv_obj_reset_transform(lv_obj_t * obj)
+{
+#if LV_DRAW_TRANSFORM_USE_MATRIX
+    LV_ASSERT_OBJ(obj, MY_CLASS);
+    if(!obj->spec_attr) {
+        return;
+    }
+
+    if(!obj->spec_attr->matrix) {
+        return;
+    }
+
+    /* Invalidate the old area */
+    lv_obj_invalidate(obj);
+
+    /* Free the matrix */
+    lv_free(obj->spec_attr->matrix);
+    obj->spec_attr->matrix = NULL;
+
+    /* Matrix is cleared. Update the layer type */
+    lv_obj_update_layer_type(obj);
+
+    /* Invalidate the new area */
+    lv_obj_invalidate(obj);
+#else
+    LV_UNUSED(obj);
+#endif
+}
+
+const lv_matrix_t * lv_obj_get_transform(const lv_obj_t * obj)
+{
+#if LV_DRAW_TRANSFORM_USE_MATRIX
+    LV_ASSERT_OBJ(obj, MY_CLASS);
+    if(obj->spec_attr) {
+        return obj->spec_attr->matrix;
+    }
+#else
+    LV_UNUSED(obj);
+#endif
+    return NULL;
+}
+
+/**********************
+ *   STATIC FUNCTIONS
+ **********************/
+
+static bool is_transformed(const lv_obj_t * obj)
+{
+    while(obj) {
+        if(obj->spec_attr && obj->spec_attr->layer_type == LV_LAYER_TYPE_TRANSFORM) return true;
+        obj = obj->parent;
+    }
+    return false;
+}
+
+static int32_t calc_content_width(lv_obj_t * obj)
+{
+    int32_t scroll_x_tmp = lv_obj_get_scroll_x(obj);
+    if(obj->spec_attr) obj->spec_attr->scroll.x = 0;
+
+    int32_t space_right = lv_obj_get_style_space_right(obj, LV_PART_MAIN);
+    int32_t space_left = lv_obj_get_style_space_left(obj, LV_PART_MAIN);
+
+    int32_t self_w;
+    self_w = lv_obj_get_self_width(obj) + space_left + space_right;
+
+    int32_t child_res = LV_COORD_MIN;
+    uint32_t i;
+    uint32_t child_cnt = lv_obj_get_child_count(obj);
+    /*With RTL find the left most coordinate*/
+    if(lv_obj_get_style_base_dir(obj, LV_PART_MAIN) == LV_BASE_DIR_RTL) {
+        for(i = 0; i < child_cnt; i++) {
+            int32_t child_res_tmp = LV_COORD_MIN;
+            lv_obj_t * child = obj->spec_attr->children[i];
+            if(lv_obj_has_flag_any(child,  LV_OBJ_FLAG_HIDDEN | LV_OBJ_FLAG_FLOATING)) continue;
+
+            if(!lv_obj_is_layout_positioned(child)) {
+                lv_align_t align = lv_obj_get_style_align(child, LV_PART_MAIN);
+                switch(align) {
+                    case LV_ALIGN_DEFAULT:
+                    case LV_ALIGN_TOP_RIGHT:
+                    case LV_ALIGN_BOTTOM_RIGHT:
+                    case LV_ALIGN_RIGHT_MID:
+                        /*Normal right aligns. Other are ignored due to possible circular dependencies*/
+                        child_res_tmp = obj->coords.x2 - child->coords.x1 + 1;
+                        break;
+                    default:
+                        /* Consider other cases only if x=0 and use the width of the object.
+                         * With x!=0 circular dependency could occur. */
+                        if(lv_obj_get_style_x(child, LV_PART_MAIN) == 0) {
+                            child_res_tmp = lv_area_get_width(&child->coords) + space_right;
+                            child_res_tmp += lv_obj_get_style_margin_left(child, LV_PART_MAIN);
+                        }
+                        break;
+                }
+            }
+            else {
+                child_res_tmp = obj->coords.x2 - child->coords.x1 + 1;
+            }
+            child_res = LV_MAX(child_res, child_res_tmp + lv_obj_get_style_margin_left(child, LV_PART_MAIN));
+        }
+        if(child_res != LV_COORD_MIN) {
+            child_res += space_left;
+        }
+    }
+    /*Else find the right most coordinate*/
+    else {
+        for(i = 0; i < child_cnt; i++) {
+            int32_t child_res_tmp = LV_COORD_MIN;
+            lv_obj_t * child = obj->spec_attr->children[i];
+            if(lv_obj_has_flag_any(child,  LV_OBJ_FLAG_HIDDEN | LV_OBJ_FLAG_FLOATING)) continue;
+
+            if(!lv_obj_is_layout_positioned(child)) {
+                lv_align_t align = lv_obj_get_style_align(child, LV_PART_MAIN);
+                switch(align) {
+                    case LV_ALIGN_DEFAULT:
+                    case LV_ALIGN_TOP_LEFT:
+                    case LV_ALIGN_BOTTOM_LEFT:
+                    case LV_ALIGN_LEFT_MID:
+                        /*Normal left aligns.*/
+                        child_res_tmp = child->coords.x2 - obj->coords.x1 + 1;
+                        break;
+                    default:
+                        /* Consider other cases only if x=0 and use the width of the object.
+                         * With x!=0 circular dependency could occur. */
+                        if(lv_obj_get_style_x(child, LV_PART_MAIN) == 0) {
+                            child_res_tmp = lv_area_get_width(&child->coords) + space_left;
+                            child_res_tmp += lv_obj_get_style_margin_right(child, LV_PART_MAIN);
+                        }
+                        break;
+                }
+            }
+            else {
+                child_res_tmp = child->coords.x2 - obj->coords.x1 + 1;
+            }
+
+            child_res = LV_MAX(child_res, child_res_tmp + lv_obj_get_style_margin_right(child, LV_PART_MAIN));
+        }
+
+        if(child_res != LV_COORD_MIN) {
+            child_res += space_right;
+        }
+    }
+
+    if(obj->spec_attr) obj->spec_attr->scroll.x = -scroll_x_tmp;
+
+    if(child_res == LV_COORD_MIN) return self_w;
+    return LV_MAX(child_res, self_w);
+}
+
+static int32_t calc_content_height(lv_obj_t * obj)
+{
+    int32_t scroll_y_tmp = lv_obj_get_scroll_y(obj);
+    if(obj->spec_attr) obj->spec_attr->scroll.y = 0;
+
+    int32_t space_top = lv_obj_get_style_space_top(obj, LV_PART_MAIN);
+    int32_t space_bottom = lv_obj_get_style_space_bottom(obj, LV_PART_MAIN);
+
+    int32_t self_h;
+    self_h = lv_obj_get_self_height(obj) + space_top + space_bottom;
+
+    int32_t child_res = LV_COORD_MIN;
+    uint32_t i;
+    uint32_t child_cnt = lv_obj_get_child_count(obj);
+    for(i = 0; i < child_cnt; i++) {
+        int32_t child_res_tmp = LV_COORD_MIN;
+        lv_obj_t * child = obj->spec_attr->children[i];
+        if(lv_obj_has_flag_any(child,  LV_OBJ_FLAG_HIDDEN | LV_OBJ_FLAG_FLOATING)) continue;
+
+        if(!lv_obj_is_layout_positioned(child)) {
+            lv_align_t align = lv_obj_get_style_align(child, LV_PART_MAIN);
+            switch(align) {
+                case LV_ALIGN_DEFAULT:
+                case LV_ALIGN_TOP_RIGHT:
+                case LV_ALIGN_TOP_MID:
+                case LV_ALIGN_TOP_LEFT:
+                    /*Normal top aligns. */
+                    child_res_tmp = child->coords.y2 - obj->coords.y1 + 1;
+                    break;
+                default:
+                    /* Consider other cases only if y=0 and use the height of the object.
+                     * With y!=0 circular dependency could occur. */
+                    if(lv_obj_get_style_y(child, LV_PART_MAIN) == 0) {
+                        child_res_tmp = lv_area_get_height(&child->coords) + space_top;
+                        child_res_tmp += lv_obj_get_style_margin_top(child, LV_PART_MAIN);
+                    }
+                    break;
+            }
+        }
+        else {
+            child_res_tmp = child->coords.y2 - obj->coords.y1 + 1;
+        }
+
+        child_res = LV_MAX(child_res, child_res_tmp + lv_obj_get_style_margin_bottom(child, LV_PART_MAIN));
+    }
+
+    if(obj->spec_attr) obj->spec_attr->scroll.y = -scroll_y_tmp;
+
+    if(child_res == LV_COORD_MIN) return self_h;
+    return LV_MAX(self_h, child_res + space_bottom);
+}
+
+static void layout_update_core(lv_obj_t * obj)
+{
+    uint32_t i;
+    uint32_t child_cnt = lv_obj_get_child_count(obj);
+    for(i = 0; i < child_cnt; i++) {
+        lv_obj_t * child = obj->spec_attr->children[i];
+        layout_update_core(child);
+    }
+
+    if(obj->layout_inv) {
+        obj->layout_inv = 0;
+        lv_obj_refr_size(obj);
+        lv_obj_refr_pos(obj);
+
+        if(child_cnt > 0) {
+            lv_layout_apply(obj);
+        }
+    }
+
+    if(obj->readjust_scroll_after_layout) {
+        obj->readjust_scroll_after_layout = 0;
+        lv_obj_readjust_scroll(obj, LV_ANIM_OFF);
+    }
+}
+
+static void transform_point_array(const lv_obj_t * obj, lv_point_t * p, size_t p_count, bool inv)
+{
+#if LV_DRAW_TRANSFORM_USE_MATRIX
+    const lv_matrix_t * obj_matrix = lv_obj_get_transform(obj);
+    if(obj_matrix) {
+        lv_matrix_t m;
+        lv_matrix_identity(&m);
+        lv_matrix_translate(&m, obj->coords.x1, obj->coords.y1);
+        lv_matrix_multiply(&m, obj_matrix);
+        lv_matrix_translate(&m, -obj->coords.x1, -obj->coords.y1);
+
+        if(inv) {
+            lv_matrix_t inv_m;
+            lv_matrix_inverse(&inv_m, &m);
+            m = inv_m;
+        }
+
+        for(size_t i = 0; i < p_count; i++) {
+            lv_point_precise_t p_precise = lv_point_to_precise(&p[i]);
+            lv_point_precise_t res = lv_matrix_transform_precise_point(&m, &p_precise);
+            p[i] = lv_point_from_precise(&res);
+        }
+
+        return;
+    }
+#endif /* LV_DRAW_TRANSFORM_USE_MATRIX */
+
+    int32_t angle = lv_obj_get_style_transform_rotation(obj, LV_PART_MAIN);
+    int32_t scale_x = lv_obj_get_style_transform_scale_x_safe(obj, LV_PART_MAIN);
+    int32_t scale_y = lv_obj_get_style_transform_scale_y_safe(obj, LV_PART_MAIN);
+    if(scale_x == 0) scale_x = 1;
+    if(scale_y == 0) scale_y = 1;
+
+    if(angle == 0 && scale_x == LV_SCALE_NONE && scale_y == LV_SCALE_NONE) return;
+
+    lv_point_t pivot = {
+        .x = lv_obj_get_style_transform_pivot_x(obj, LV_PART_MAIN),
+        .y = lv_obj_get_style_transform_pivot_y(obj, LV_PART_MAIN)
+    };
+
+    if(LV_COORD_IS_PCT(pivot.x)) {
+        pivot.x = (LV_COORD_GET_PCT(pivot.x) * lv_area_get_width(&obj->coords)) / 100;
+    }
+    if(LV_COORD_IS_PCT(pivot.y)) {
+        pivot.y = (LV_COORD_GET_PCT(pivot.y) * lv_area_get_height(&obj->coords)) / 100;
+    }
+
+    pivot.x = obj->coords.x1 + pivot.x;
+    pivot.y = obj->coords.y1 + pivot.y;
+
+    if(inv) {
+        angle = -angle;
+        scale_x = (256 * 256 + scale_x - 1) / scale_x;
+        scale_y = (256 * 256 + scale_y - 1) / scale_y;
+    }
+
+    lv_point_array_transform(p, p_count, angle, scale_x, scale_y, &pivot, !inv);
+}

+ 473 - 0
components/easylvgl/lvgl9/src/core/lv_obj_pos.h

@@ -0,0 +1,473 @@
+/**
+ * @file lv_obj_pos.h
+ *
+ */
+
+#ifndef LV_OBJ_POS_H
+#define LV_OBJ_POS_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/*********************
+ *      INCLUDES
+ *********************/
+#include "../misc/lv_area.h"
+
+/*********************
+ *      DEFINES
+ *********************/
+
+/**********************
+ *      TYPEDEFS
+ **********************/
+
+typedef enum {
+    /** No flags */
+    LV_OBJ_POINT_TRANSFORM_FLAG_NONE = 0x00,
+
+    /** Consider the transformation properties of the parents too */
+    LV_OBJ_POINT_TRANSFORM_FLAG_RECURSIVE = 0x01,
+
+    /** Execute the inverse of the transformation (-angle and 1/zoom) */
+    LV_OBJ_POINT_TRANSFORM_FLAG_INVERSE = 0x02,
+
+    /** Both inverse and recursive*/
+    LV_OBJ_POINT_TRANSFORM_FLAG_INVERSE_RECURSIVE = 0x03,
+} lv_obj_point_transform_flag_t;
+
+/**********************
+ * GLOBAL PROTOTYPES
+ **********************/
+
+/**
+ * Set the position of an object relative to the set alignment.
+ * @param obj       pointer to an object
+ * @param x         new x coordinate
+ * @param y         new y coordinate
+ * @note            With default alignment it's the distance from the top left corner
+ * @note            E.g. LV_ALIGN_CENTER alignment it's the offset from the center of the parent
+ * @note            The position is interpreted on the content area of the parent
+ * @note            The values can be set in pixel or in percentage of parent size with `lv_pct(v)`
+ */
+void lv_obj_set_pos(lv_obj_t * obj, int32_t x, int32_t y);
+
+/**
+ * Set the x coordinate of an object
+ * @param obj       pointer to an object
+ * @param x         new x coordinate
+ * @note            With default alignment it's the distance from the top left corner
+ * @note            E.g. LV_ALIGN_CENTER alignment it's the offset from the center of the parent
+ * @note            The position is interpreted on the content area of the parent
+ * @note            The values can be set in pixel or in percentage of parent size with `lv_pct(v)`
+ */
+void lv_obj_set_x(lv_obj_t * obj, int32_t x);
+
+/**
+ * Set the y coordinate of an object
+ * @param obj       pointer to an object
+ * @param y         new y coordinate
+ * @note            With default alignment it's the distance from the top left corner
+ * @note            E.g. LV_ALIGN_CENTER alignment it's the offset from the center of the parent
+ * @note            The position is interpreted on the content area of the parent
+ * @note            The values can be set in pixel or in percentage of parent size with `lv_pct(v)`
+ */
+void lv_obj_set_y(lv_obj_t * obj, int32_t y);
+
+/**
+ * Set the size of an object.
+ * @param obj       pointer to an object
+ * @param w         the new width
+ * @param h         the new height
+ * @note            possible values are:
+ *                  pixel               simple set the size accordingly
+ *                  LV_SIZE_CONTENT     set the size to involve all children in the given direction
+ *                  lv_pct(x)           to set size in percentage of the parent's content area size (the size without paddings).
+ *                                      x should be in [0..1000]% range
+ */
+void lv_obj_set_size(lv_obj_t * obj, int32_t w, int32_t h);
+
+/**
+ * Recalculate the size of the object
+ * @param obj       pointer to an object
+ * @return          true: the size has been changed
+ */
+bool lv_obj_refr_size(lv_obj_t * obj);
+
+/**
+ * Set the width of an object
+ * @param obj       pointer to an object
+ * @param w         the new width
+ * @note            possible values are:
+ *                  pixel               simple set the size accordingly
+ *                  LV_SIZE_CONTENT     set the size to involve all children in the given direction
+ *                  lv_pct(x)           to set size in percentage of the parent's content area size (the size without paddings).
+ *                                      x should be in [0..1000]% range
+ */
+void lv_obj_set_width(lv_obj_t * obj, int32_t w);
+
+/**
+ * Set the height of an object
+ * @param obj       pointer to an object
+ * @param h         the new height
+ * @note            possible values are:
+ *                  pixel               simple set the size accordingly
+ *                  LV_SIZE_CONTENT     set the size to involve all children in the given direction
+ *                  lv_pct(x)           to set size in percentage of the parent's content area size (the size without paddings).
+ *                                      x should be in [0..1000]% range
+ */
+void lv_obj_set_height(lv_obj_t * obj, int32_t h);
+
+/**
+ * Set the width reduced by the left and right padding and the border width.
+ * @param obj       pointer to an object
+ * @param w         the width without paddings in pixels
+ */
+void lv_obj_set_content_width(lv_obj_t * obj, int32_t w);
+
+/**
+ * Set the height reduced by the top and bottom padding and the border width.
+ * @param obj       pointer to an object
+ * @param h         the height without paddings in pixels
+ */
+void lv_obj_set_content_height(lv_obj_t * obj, int32_t h);
+
+/**
+ * Set a layout for an object
+ * @param obj       pointer to an object
+ * @param layout    pointer to a layout descriptor to set
+ */
+void lv_obj_set_layout(lv_obj_t * obj, uint32_t layout);
+
+/**
+ * Test whether the and object is positioned by a layout or not
+ * @param obj       pointer to an object to test
+ * @return true:    positioned by a layout; false: not positioned by a layout
+ */
+bool lv_obj_is_layout_positioned(const lv_obj_t * obj);
+
+/**
+ * Mark the object for layout update.
+ * @param obj      pointer to an object whose children need to be updated
+ */
+void lv_obj_mark_layout_as_dirty(lv_obj_t * obj);
+
+/**
+ * Update the layout of an object.
+ * @param obj      pointer to an object whose position and size needs to be updated
+ */
+void lv_obj_update_layout(const lv_obj_t * obj);
+
+/**
+ * Change the alignment of an object.
+ * @param obj       pointer to an object to align
+ * @param align     type of alignment (see 'lv_align_t' enum) `LV_ALIGN_OUT_...` can't be used.
+ */
+void lv_obj_set_align(lv_obj_t * obj, lv_align_t align);
+
+/**
+ * Change the alignment of an object and set new coordinates.
+ * Equivalent to:
+ * lv_obj_set_align(obj, align);
+ * lv_obj_set_pos(obj, x_ofs, y_ofs);
+ * @param obj       pointer to an object to align
+ * @param align     type of alignment (see 'lv_align_t' enum) `LV_ALIGN_OUT_...` can't be used.
+ * @param x_ofs     x coordinate offset after alignment
+ * @param y_ofs     y coordinate offset after alignment
+ */
+void lv_obj_align(lv_obj_t * obj, lv_align_t align, int32_t x_ofs, int32_t y_ofs);
+
+/**
+ * Align an object to another object.
+ * @param obj       pointer to an object to align
+ * @param base      pointer to another object (if NULL `obj`s parent is used). 'obj' will be aligned to it.
+ * @param align     type of alignment (see 'lv_align_t' enum)
+ * @param x_ofs     x coordinate offset after alignment
+ * @param y_ofs     y coordinate offset after alignment
+ * @note            if the position or size of `base` changes `obj` needs to be aligned manually again
+ */
+void lv_obj_align_to(lv_obj_t * obj, const lv_obj_t * base, lv_align_t align, int32_t x_ofs,
+                     int32_t y_ofs);
+
+/**
+ * Align an object to the center on its parent.
+ * @param obj       pointer to an object to align
+ * @note            if the parent size changes `obj` needs to be aligned manually again
+ */
+void lv_obj_center(lv_obj_t * obj);
+
+/**
+ * Set the transform matrix of an object
+ * @param obj       pointer to an object
+ * @param matrix    pointer to a matrix to set
+ * @note `LV_DRAW_TRANSFORM_USE_MATRIX` needs to be enabled.
+ */
+void lv_obj_set_transform(lv_obj_t * obj, const lv_matrix_t * matrix);
+
+/**
+ * Reset the transform matrix of an object to identity matrix
+ * @param obj       pointer to an object
+ * @note `LV_DRAW_TRANSFORM_USE_MATRIX` needs to be enabled.
+ */
+void lv_obj_reset_transform(lv_obj_t * obj);
+
+/**
+ * Copy the coordinates of an object to an area
+ * @param obj       pointer to an object
+ * @param coords    pointer to an area to store the coordinates
+ */
+void lv_obj_get_coords(const lv_obj_t * obj, lv_area_t * coords);
+
+/**
+ * Get the x coordinate of object.
+ * @param obj       pointer to an object
+ * @return          distance of `obj` from the left side of its parent plus the parent's left padding
+ * @note            The position of the object is recalculated only on the next redraw. To force coordinate recalculation
+ *                  call `lv_obj_update_layout(obj)`.
+ * @note            Zero return value means the object is on the left padding of the parent, and not on the left edge.
+ * @note            Scrolling of the parent doesn't change the returned value.
+ * @note            The returned value is always the distance from the parent even if `obj` is positioned by a layout.
+ */
+int32_t lv_obj_get_x(const lv_obj_t * obj);
+
+/**
+ * Get the x2 coordinate of object.
+ * @param obj       pointer to an object
+ * @return          distance of `obj` from the right side of its parent plus the parent's right padding
+ * @note            The position of the object is recalculated only on the next redraw. To force coordinate recalculation
+ *                  call `lv_obj_update_layout(obj)`.
+ * @note            Zero return value means the object is on the right padding of the parent, and not on the right edge.
+ * @note            Scrolling of the parent doesn't change the returned value.
+ * @note            The returned value is always the distance from the parent even if `obj` is positioned by a layout.
+ */
+int32_t lv_obj_get_x2(const lv_obj_t * obj);
+
+/**
+ * Get the y coordinate of object.
+ * @param obj       pointer to an object
+ * @return          distance of `obj` from the top side of its parent plus the parent's top padding
+ * @note            The position of the object is recalculated only on the next redraw. To force coordinate recalculation
+ *                  call `lv_obj_update_layout(obj)`.
+ * @note            Zero return value means the object is on the top padding of the parent, and not on the top edge.
+ * @note            Scrolling of the parent doesn't change the returned value.
+ * @note            The returned value is always the distance from the parent even if `obj` is positioned by a layout.
+ */
+int32_t lv_obj_get_y(const lv_obj_t * obj);
+
+/**
+ * Get the y2 coordinate of object.
+ * @param obj       pointer to an object
+ * @return          distance of `obj` from the bottom side of its parent plus the parent's bottom padding
+ * @note            The position of the object is recalculated only on the next redraw. To force coordinate recalculation
+ *                  call `lv_obj_update_layout(obj)`.
+ * @note            Zero return value means the object is on the bottom padding of the parent, and not on the bottom edge.
+ * @note            Scrolling of the parent doesn't change the returned value.
+ * @note            The returned value is always the distance from the parent even if `obj` is positioned by a layout.
+ */
+int32_t lv_obj_get_y2(const lv_obj_t * obj);
+
+/**
+ * Get the actually set x coordinate of object, i.e. the offset from the set alignment
+ * @param obj       pointer to an object
+ * @return          the set x coordinate
+ */
+int32_t lv_obj_get_x_aligned(const lv_obj_t * obj);
+
+/**
+ * Get the actually set y coordinate of object, i.e. the offset from the set alignment
+ * @param obj       pointer to an object
+ * @return          the set y coordinate
+ */
+int32_t lv_obj_get_y_aligned(const lv_obj_t * obj);
+
+/**
+ * Get the width of an object
+ * @param obj       pointer to an object
+ * @note            The position of the object is recalculated only on the next redraw. To force coordinate recalculation
+ *                  call `lv_obj_update_layout(obj)`.
+ * @return          the width in pixels
+ */
+int32_t lv_obj_get_width(const lv_obj_t * obj);
+
+/**
+ * Get the height of an object
+ * @param obj       pointer to an object
+ * @note            The position of the object is recalculated only on the next redraw. To force coordinate recalculation
+ *                  call `lv_obj_update_layout(obj)`.
+ * @return          the height in pixels
+ */
+int32_t lv_obj_get_height(const lv_obj_t * obj);
+
+/**
+ * Get the width reduced by the left and right padding and the border width.
+ * @param obj       pointer to an object
+ * @note            The position of the object is recalculated only on the next redraw. To force coordinate recalculation
+ *                  call `lv_obj_update_layout(obj)`.
+ * @return          the width which still fits into its parent without causing overflow (making the parent scrollable)
+ */
+int32_t lv_obj_get_content_width(const lv_obj_t * obj);
+
+/**
+ * Get the height reduced by the top and bottom padding and the border width.
+ * @param obj       pointer to an object
+ * @note            The position of the object is recalculated only on the next redraw. To force coordinate recalculation
+ *                  call `lv_obj_update_layout(obj)`.
+ * @return          the height which still fits into the parent without causing overflow (making the parent scrollable)
+ */
+int32_t lv_obj_get_content_height(const lv_obj_t * obj);
+
+/**
+ * Get the area reduced by the paddings and the border width.
+ * @param obj       pointer to an object
+ * @note            The position of the object is recalculated only on the next redraw. To force coordinate recalculation
+ *                  call `lv_obj_update_layout(obj)`.
+ * @param area      the area which still fits into the parent without causing overflow (making the parent scrollable)
+ */
+void lv_obj_get_content_coords(const lv_obj_t * obj, lv_area_t * area);
+
+/**
+ * Get the width occupied by the "parts" of the widget. E.g. the width of all columns of a table.
+ * @param obj       pointer to an objects
+ * @return          the width of the virtually drawn content
+ * @note            This size independent from the real size of the widget.
+ *                  It just tells how large the internal ("virtual") content is.
+ */
+int32_t lv_obj_get_self_width(const lv_obj_t * obj);
+
+/**
+ * Get the height occupied by the "parts" of the widget. E.g. the height of all rows of a table.
+ * @param obj       pointer to an objects
+ * @return          the width of the virtually drawn content
+ * @note            This size independent from the real size of the widget.
+ *                  It just tells how large the internal ("virtual") content is.
+ */
+int32_t lv_obj_get_self_height(const lv_obj_t * obj);
+
+/**
+ * Handle if the size of the internal ("virtual") content of an object has changed.
+ * @param obj       pointer to an object
+ * @return          false: nothing happened; true: refresh happened
+ */
+bool lv_obj_refresh_self_size(lv_obj_t * obj);
+
+void lv_obj_refr_pos(lv_obj_t * obj);
+
+void lv_obj_move_to(lv_obj_t * obj, int32_t x, int32_t y);
+
+void lv_obj_move_children_by(lv_obj_t * obj, int32_t x_diff, int32_t y_diff, bool ignore_floating);
+
+/**
+ * Get the transform matrix of an object
+ * @param obj       pointer to an object
+ * @return          pointer to the transform matrix or NULL if not set
+ */
+const lv_matrix_t * lv_obj_get_transform(const lv_obj_t * obj);
+
+/**
+ * Transform a point using the angle and zoom style properties of an object
+ * @param obj           pointer to an object whose style properties should be used
+ * @param p             a point to transform, the result will be written back here too
+ * @param flags         OR-ed valued of :cpp:enum:`lv_obj_point_transform_flag_t`
+ */
+void lv_obj_transform_point(const lv_obj_t * obj, lv_point_t * p, lv_obj_point_transform_flag_t flags);
+
+/**
+ * Transform an array of points using the angle and zoom style properties of an object
+ * @param obj           pointer to an object whose style properties should be used
+ * @param points        the array of points to transform, the result will be written back here too
+ * @param count         number of points in the array
+ * @param flags         OR-ed valued of :cpp:enum:`lv_obj_point_transform_flag_t`
+ */
+void lv_obj_transform_point_array(const lv_obj_t * obj, lv_point_t points[], size_t count,
+                                  lv_obj_point_transform_flag_t flags);
+
+/**
+ * Transform an area using the angle and zoom style properties of an object
+ * @param obj           pointer to an object whose style properties should be used
+ * @param area          an area to transform, the result will be written back here too
+ * @param flags         OR-ed valued of :cpp:enum:`lv_obj_point_transform_flag_t`
+ */
+void lv_obj_get_transformed_area(const lv_obj_t * obj, lv_area_t * area, lv_obj_point_transform_flag_t flags);
+
+/**
+ * Mark an area of an object as invalid.
+ * The area will be truncated to the object's area and marked for redraw.
+ * @param obj       pointer to an object
+ * @param           area the area to redraw
+ */
+void lv_obj_invalidate_area(const lv_obj_t * obj, const lv_area_t * area);
+
+/**
+ * Mark the object as invalid to redrawn its area
+ * @param obj       pointer to an object
+ */
+void lv_obj_invalidate(const lv_obj_t * obj);
+
+/**
+ * Tell whether an area of an object is visible (even partially) now or not
+ * @param obj       pointer to an object
+ * @param area      the are to check. The visible part of the area will be written back here.
+ * @return true     visible; false not visible (hidden, out of parent, on other screen, etc)
+ */
+bool lv_obj_area_is_visible(const lv_obj_t * obj, lv_area_t * area);
+
+/**
+ * Tell whether an object is visible (even partially) now or not
+ * @param obj       pointer to an object
+ * @return      true: visible; false not visible (hidden, out of parent, on other screen, etc)
+ */
+bool lv_obj_is_visible(const lv_obj_t * obj);
+
+/**
+ * Set the size of an extended clickable area
+ * @param obj       pointer to an object
+ * @param size      extended clickable area in all 4 directions [px]
+ */
+void lv_obj_set_ext_click_area(lv_obj_t * obj, int32_t size);
+
+/**
+ * Get the an area where to object can be clicked.
+ * It's the object's normal area plus the extended click area.
+ * @param obj       pointer to an object
+ * @param area      store the result area here
+ */
+void lv_obj_get_click_area(const lv_obj_t * obj, lv_area_t * area);
+
+/**
+ * Hit-test an object given a particular point in screen space.
+ * @param obj       object to hit-test
+ * @param point     screen-space point (absolute coordinate)
+ * @return          true: if the object is considered under the point
+ */
+bool lv_obj_hit_test(lv_obj_t * obj, const lv_point_t * point);
+
+/**
+ * Clamp a width between min and max width. If the min/max width is in percentage value use the ref_width
+ * @param width         width to clamp
+ * @param min_width     the minimal width
+ * @param max_width     the maximal width
+ * @param ref_width     the reference width used when min/max width is in percentage
+ * @return              the clamped width
+ */
+int32_t lv_clamp_width(int32_t width, int32_t min_width, int32_t max_width, int32_t ref_width);
+
+/**
+ * Clamp a height between min and max height. If the min/max height is in percentage value use the ref_height
+ * @param height         height to clamp
+ * @param min_height     the minimal height
+ * @param max_height     the maximal height
+ * @param ref_height     the reference height used when min/max height is in percentage
+ * @return              the clamped height
+ */
+int32_t lv_clamp_height(int32_t height, int32_t min_height, int32_t max_height, int32_t ref_height);
+
+/**********************
+ *      MACROS
+ **********************/
+
+#ifdef __cplusplus
+} /*extern "C"*/
+#endif
+
+#endif /*LV_OBJ_POS_H*/

+ 93 - 0
components/easylvgl/lvgl9/src/core/lv_obj_private.h

@@ -0,0 +1,93 @@
+/**
+ * @file lv_obj_private.h
+ *
+ */
+
+#ifndef LV_OBJ_PRIVATE_H
+#define LV_OBJ_PRIVATE_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/*********************
+ *      INCLUDES
+ *********************/
+
+#include "lv_obj.h"
+
+/*********************
+ *      DEFINES
+ *********************/
+
+/**********************
+ *      TYPEDEFS
+ **********************/
+
+/**
+ * Special, rarely used attributes.
+ * They are allocated automatically if any elements is set.
+ */
+struct _lv_obj_spec_attr_t {
+    lv_obj_t ** children;           /**< Store the pointer of the children in an array.*/
+    lv_group_t * group_p;
+#if LV_DRAW_TRANSFORM_USE_MATRIX
+    lv_matrix_t * matrix;           /**< The transform matrix*/
+#endif
+    lv_event_list_t event_list;
+#if LV_USE_OBJ_NAME
+    const char * name;              /**< Pointer to the name */
+#endif
+    lv_point_t scroll;              /**< The current X/Y scroll offset*/
+
+    int32_t ext_click_pad;          /**< Extra click padding in all direction*/
+    int32_t ext_draw_size;          /**< EXTend the size in every direction for drawing.*/
+
+    uint16_t child_cnt;             /**< Number of children*/
+    uint16_t scrollbar_mode : 2;    /**< How to display scrollbars, see `lv_scrollbar_mode_t`*/
+    uint16_t scroll_snap_x : 2;     /**< Where to align the snappable children horizontally, see `lv_scroll_snap_t`*/
+    uint16_t scroll_snap_y : 2;     /**< Where to align the snappable children vertically*/
+    uint16_t scroll_dir : 4;        /**< The allowed scroll direction(s), see `lv_dir_t`*/
+    uint16_t layer_type : 2;        /**< Cache the layer type here. Element of lv_intermediate_layer_type_t */
+    uint16_t name_static : 1;        /**< 1: `name` was not dynamically allocated */
+};
+
+struct _lv_obj_t {
+    const lv_obj_class_t * class_p;
+    lv_obj_t * parent;
+    lv_obj_spec_attr_t * spec_attr;
+    lv_obj_style_t * styles;
+#if LV_OBJ_STYLE_CACHE
+    uint32_t style_main_prop_is_set;
+    uint32_t style_other_prop_is_set;
+#endif
+    void * user_data;
+#if LV_USE_OBJ_ID
+    void * id;
+#endif
+    lv_area_t coords;
+    lv_obj_flag_t flags;
+    uint16_t state;
+    uint16_t layout_inv : 1;
+    uint16_t readjust_scroll_after_layout : 1;
+    uint16_t scr_layout_inv : 1;
+    uint16_t skip_trans : 1;
+    uint16_t style_cnt  : 6;
+    uint16_t h_layout   : 1;
+    uint16_t w_layout   : 1;
+    uint16_t is_deleting : 1;
+};
+
+/**********************
+ * GLOBAL PROTOTYPES
+ **********************/
+
+/**********************
+ *      MACROS
+ **********************/
+
+#ifdef __cplusplus
+} /*extern "C"*/
+#endif
+
+#endif /*LV_OBJ_PRIVATE_H*/

+ 339 - 0
components/easylvgl/lvgl9/src/core/lv_obj_property.c

@@ -0,0 +1,339 @@
+/**
+ * @file lv_obj_property.c
+ *
+ */
+
+/*********************
+ *      INCLUDES
+ *********************/
+#include "lv_obj_private.h"
+#include "../core/lv_obj.h"
+#include "../stdlib/lv_string.h"
+#include "../misc/lv_utils.h"
+#include "lv_obj_property.h"
+#include "lv_obj_class_private.h"
+
+#if LV_USE_OBJ_PROPERTY
+
+/*********************
+ *      DEFINES
+ *********************/
+
+#define HANDLE_PROPERTY_TYPE(type, field) \
+    if(!set) { \
+        value->field = ((lv_property_get_##type##_t)(prop->getter))(obj); \
+    } else { \
+        switch(LV_PROPERTY_ID_TYPE2(prop->id)) { \
+            case LV_PROPERTY_ID_INVALID: \
+                ((lv_property_set_##type##_t)(prop->setter))(obj, value->field); \
+                break; \
+            case LV_PROPERTY_TYPE_INT: \
+                ((lv_property_set_##type##_integer_t)(prop->setter))(obj, value->arg1.field, value->arg2.num); \
+                break; \
+            case LV_PROPERTY_TYPE_BOOL: \
+                ((lv_property_set_##type##_boolean_t)(prop->setter))(obj, value->arg1.field, value->arg2.enable); \
+                break; \
+            case LV_PROPERTY_TYPE_PRECISE: \
+                ((lv_property_set_##type##_precise_t)(prop->setter))(obj, value->arg1.field, value->arg2.precise); \
+                break; \
+            case LV_PROPERTY_TYPE_COLOR: \
+                ((lv_property_set_##type##_color_t)(prop->setter))(obj, value->arg1.field, value->arg2.color); \
+                break; \
+            case LV_PROPERTY_TYPE_POINTER: \
+            case LV_PROPERTY_TYPE_IMGSRC: \
+            case LV_PROPERTY_TYPE_TEXT: \
+            case LV_PROPERTY_TYPE_OBJ: \
+            case LV_PROPERTY_TYPE_DISPLAY: \
+            case LV_PROPERTY_TYPE_FONT: \
+                ((lv_property_set_##type##_pointer_t)(prop->setter))(obj, value->arg1.field, value->arg2.ptr); \
+                break; \
+        } \
+    }
+
+
+/**********************
+ *      TYPEDEFS
+ **********************/
+
+typedef int32_t integer;
+typedef bool boolean;
+typedef lv_value_precise_t precise;
+typedef lv_color_t color;
+typedef const void * pointer;
+
+#define DEFINE_PROPERTY_SETTER_TYPES(type) \
+    typedef void (*lv_property_set_##type##_t)(lv_obj_t *, type); \
+    typedef void (*lv_property_set_##type##_integer_t)(lv_obj_t *, type, int32_t); \
+    typedef void (*lv_property_set_##type##_boolean_t)(lv_obj_t *, type, bool); \
+    typedef void (*lv_property_set_##type##_precise_t)(lv_obj_t *, type, lv_value_precise_t); \
+    typedef void (*lv_property_set_##type##_color_t)(lv_obj_t *, type, lv_color_t); \
+    typedef void (*lv_property_set_##type##_pointer_t)(lv_obj_t *, type, const void *)
+
+DEFINE_PROPERTY_SETTER_TYPES(integer);
+DEFINE_PROPERTY_SETTER_TYPES(boolean);
+DEFINE_PROPERTY_SETTER_TYPES(precise);
+DEFINE_PROPERTY_SETTER_TYPES(color);
+DEFINE_PROPERTY_SETTER_TYPES(pointer);
+
+typedef void (*lv_property_set_point_t)(lv_obj_t *, lv_point_t *);
+typedef lv_result_t (*lv_property_setter_t)(lv_obj_t *, lv_prop_id_t, const lv_property_t *);
+
+typedef integer(*lv_property_get_integer_t)(const lv_obj_t *);
+typedef bool (*lv_property_get_boolean_t)(const lv_obj_t *);
+typedef lv_value_precise_t (*lv_property_get_precise_t)(const lv_obj_t *);
+typedef lv_color_t (*lv_property_get_color_t)(const lv_obj_t *);
+typedef void * (*lv_property_get_pointer_t)(const lv_obj_t *);
+typedef lv_point_t (*lv_property_get_point_t)(lv_obj_t *);
+
+typedef lv_result_t (*lv_property_getter_t)(const lv_obj_t *, lv_prop_id_t, lv_property_t *);
+
+/**********************
+ *  STATIC PROTOTYPES
+ **********************/
+
+static lv_result_t obj_property(lv_obj_t * obj, lv_prop_id_t id, lv_property_t * value, bool set);
+static int property_name_compare(const void * ref, const void * element);
+
+/**********************
+ *  STATIC VARIABLES
+ **********************/
+
+/**********************
+ *      MACROS
+ **********************/
+
+/**********************
+ *   GLOBAL FUNCTIONS
+ **********************/
+
+lv_result_t lv_obj_set_property(lv_obj_t * obj, const lv_property_t * value)
+{
+    LV_ASSERT(obj && value);
+
+    uint32_t index = LV_PROPERTY_ID_INDEX(value->id);
+    if(value->id == LV_PROPERTY_ID_INVALID || index > LV_PROPERTY_ID_ANY) {
+        LV_LOG_WARN("Invalid property id set to %p", obj);
+        return LV_RESULT_INVALID;
+    }
+
+    if(index < LV_PROPERTY_ID_START) {
+        lv_obj_set_local_style_prop(obj, index, value->style, value->selector);
+        return LV_RESULT_OK;
+    }
+
+    return obj_property(obj, value->id, (lv_property_t *)value, true);
+}
+
+lv_result_t lv_obj_set_properties(lv_obj_t * obj, const lv_property_t * value, uint32_t count)
+{
+    for(uint32_t i = 0; i < count; i++) {
+        lv_result_t result = lv_obj_set_property(obj, &value[i]);
+        if(result != LV_RESULT_OK) {
+            return result;
+        }
+    }
+
+    return LV_RESULT_OK;
+}
+
+lv_property_t lv_obj_get_property(lv_obj_t * obj, lv_prop_id_t id)
+{
+    lv_result_t result;
+    lv_property_t value = { 0 };
+
+    uint32_t index = LV_PROPERTY_ID_INDEX(id);
+    if(id == LV_PROPERTY_ID_INVALID || index > LV_PROPERTY_ID_ANY) {
+        LV_LOG_WARN("Invalid property id to get from %p", obj);
+        value.id = LV_PROPERTY_ID_INVALID;
+        value.num = 0;
+        return value;
+    }
+
+    if(index < LV_PROPERTY_ID_START) {
+        lv_obj_get_local_style_prop(obj, index, &value.style, 0);
+        value.id = id;
+        value.selector = 0;
+        return value;
+    }
+
+    result = obj_property(obj, id, &value, false);
+    if(result != LV_RESULT_OK)
+        value.id = LV_PROPERTY_ID_INVALID;
+
+    return value;
+}
+
+lv_property_t lv_obj_get_style_property(lv_obj_t * obj, lv_prop_id_t id, uint32_t selector)
+{
+    lv_property_t value;
+    uint32_t index = LV_PROPERTY_ID_INDEX(id);
+
+    if(index == LV_PROPERTY_ID_INVALID || index >= LV_PROPERTY_ID_START) {
+        LV_LOG_WARN("invalid style property id 0x%" LV_PRIx32, id);
+        value.id = LV_PROPERTY_ID_INVALID;
+        value.num = 0;
+        return value;
+    }
+
+    lv_obj_get_local_style_prop(obj, id, &value.style, selector);
+    value.id = id;
+    value.selector = selector;
+    return value;
+}
+
+lv_prop_id_t lv_style_property_get_id(const char * name)
+{
+#if LV_USE_OBJ_PROPERTY_NAME
+    lv_property_name_t * found;
+    /*Check style property*/
+    found = lv_utils_bsearch(name, lv_style_property_names, sizeof(lv_style_property_names) / sizeof(lv_property_name_t),
+                             sizeof(lv_property_name_t), property_name_compare);
+    if(found) return found->id;
+#else
+    LV_UNUSED(name);
+#endif
+    return LV_PROPERTY_ID_INVALID;
+}
+
+lv_prop_id_t lv_obj_class_property_get_id(const lv_obj_class_t * clz, const char * name)
+{
+#if LV_USE_OBJ_PROPERTY_NAME
+    const lv_property_name_t * names;
+    lv_property_name_t * found;
+
+    names = clz->property_names;
+    if(names == NULL) {
+        /* try base class*/
+        return LV_PROPERTY_ID_INVALID;
+    }
+
+    found = lv_utils_bsearch(name, names, clz->names_count, sizeof(lv_property_name_t), property_name_compare);
+    if(found) return found->id;
+#else
+    LV_UNUSED(obj);
+    LV_UNUSED(name);
+    LV_UNUSED(property_name_compare);
+#endif
+    return LV_PROPERTY_ID_INVALID;
+}
+
+lv_prop_id_t lv_obj_property_get_id(const lv_obj_t * obj, const char * name)
+{
+#if LV_USE_OBJ_PROPERTY_NAME
+    const lv_obj_class_t * clz;
+    lv_prop_id_t id;
+
+    for(clz = obj->class_p; clz; clz = clz->base_class) {
+        id = lv_obj_class_property_get_id(clz, name);
+        if(id != LV_PROPERTY_ID_INVALID) return id;
+    }
+
+    /*Check style property*/
+    id = lv_style_property_get_id(name);
+    if(id != LV_PROPERTY_ID_INVALID) return id;
+#else
+    LV_UNUSED(obj);
+    LV_UNUSED(name);
+    LV_UNUSED(property_name_compare);
+#endif
+    return LV_PROPERTY_ID_INVALID;
+}
+
+/**********************
+ *  STATIC FUNCTIONS
+ **********************/
+
+static lv_result_t obj_property(lv_obj_t * obj, lv_prop_id_t id, lv_property_t * value, bool set)
+{
+    const lv_property_ops_t * properties;
+    const lv_property_ops_t * prop;
+
+    const lv_obj_class_t * clz;
+    uint32_t index = LV_PROPERTY_ID_INDEX(id);
+
+    for(clz = obj->class_p ; clz; clz = clz->base_class) {
+        properties = clz->properties;
+        if(properties == NULL) {
+            /* try base class*/
+            continue;
+        }
+
+        if(id != LV_PROPERTY_ID_ANY && (index < clz->prop_index_start || index > clz->prop_index_end)) {
+            /* try base class*/
+            continue;
+        }
+
+        /*Check if there's setter available for this class*/
+        for(uint32_t i = 0; i < clz->properties_count; i++) {
+            prop = &properties[i];
+
+            /*pass id and value directly to widget's property method*/
+            if(prop->id == LV_PROPERTY_ID_ANY) {
+                value->id = prop->id;
+                if(set) return ((lv_property_setter_t)prop->setter)(obj, id, value);
+                else return ((lv_property_getter_t)prop->getter)(obj, id, value);
+            }
+
+            /*Not this id, check next*/
+            if(prop->id != id)
+                continue;
+
+            /*id matched but we got null pointer to functions*/
+            if(set ? prop->setter == NULL : prop->getter == NULL) {
+                LV_LOG_WARN("NULL %s provided, id: 0x%" LV_PRIx32, set ? "setter" : "getter", id);
+                return LV_RESULT_INVALID;
+            }
+
+            /*Update value id if it's a read*/
+            if(!set) value->id = prop->id;
+
+            switch(LV_PROPERTY_ID_TYPE(prop->id)) {
+                case LV_PROPERTY_TYPE_INT:
+                    HANDLE_PROPERTY_TYPE(integer, num);
+                    break;
+                case LV_PROPERTY_TYPE_BOOL:
+                    HANDLE_PROPERTY_TYPE(boolean, enable);
+                    break;
+                case LV_PROPERTY_TYPE_PRECISE:
+                    HANDLE_PROPERTY_TYPE(precise, precise);
+                    break;
+                case LV_PROPERTY_TYPE_COLOR:
+                    HANDLE_PROPERTY_TYPE(color, color);
+                    break;
+                case LV_PROPERTY_TYPE_POINTER:
+                case LV_PROPERTY_TYPE_IMGSRC:
+                case LV_PROPERTY_TYPE_TEXT:
+                case LV_PROPERTY_TYPE_OBJ:
+                case LV_PROPERTY_TYPE_DISPLAY:
+                case LV_PROPERTY_TYPE_FONT:
+                    HANDLE_PROPERTY_TYPE(pointer, ptr);
+                    break;
+                case LV_PROPERTY_TYPE_POINT: {
+                        lv_point_t * point = &value->point;
+                        if(set)((lv_property_set_point_t)(prop->setter))(obj, point);
+                        else *point = ((lv_property_get_point_t)(prop->getter))(obj);
+                        break;
+                    }
+                default: {
+                        LV_LOG_WARN("Unknown property id: 0x%08" LV_PRIx32, prop->id);
+                        return LV_RESULT_INVALID;
+                    }
+            }
+
+            return LV_RESULT_OK;
+        }
+
+        /*If no setter found, try base class then*/
+    }
+
+    LV_LOG_WARN("Unknown property id: 0x%08" LV_PRIx32, id);
+    return LV_RESULT_INVALID;
+}
+
+static int property_name_compare(const void * ref, const void * element)
+{
+    const lv_property_name_t * prop = element;
+    return lv_strcmp(ref, prop->name);
+}
+
+#endif /*LV_USE_OBJ_PROPERTY*/

+ 246 - 0
components/easylvgl/lvgl9/src/core/lv_obj_property.h

@@ -0,0 +1,246 @@
+/**
+ * @file lv_obj_property.h
+ *
+ */
+
+#ifndef LV_OBJ_PROPERTY_H
+#define LV_OBJ_PROPERTY_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/*********************
+ *      INCLUDES
+ *********************/
+#include "../misc/lv_types.h"
+#include "../misc/lv_style.h"
+
+#if LV_USE_OBJ_PROPERTY
+
+/*********************
+ *      DEFINES
+ *********************/
+
+/*All possible property value types*/
+#define LV_PROPERTY_TYPE_INVALID        0   /*Use default 0 as invalid to detect program outliers*/
+#define LV_PROPERTY_TYPE_INT            1   /*int32_t type*/
+#define LV_PROPERTY_TYPE_PRECISE        2   /*lv_value_precise_t, int32_t or float depending on LV_USE_FLOAT*/
+#define LV_PROPERTY_TYPE_COLOR          3   /*ARGB8888 type*/
+#define LV_PROPERTY_TYPE_POINT          4   /*lv_point_t */
+#define LV_PROPERTY_TYPE_POINTER        5   /*void * pointer*/
+#define LV_PROPERTY_TYPE_IMGSRC         6   /*Special pointer for image*/
+#define LV_PROPERTY_TYPE_TEXT           7   /*Special pointer of char* */
+#define LV_PROPERTY_TYPE_OBJ            8   /*Special pointer of lv_obj_t* */
+#define LV_PROPERTY_TYPE_DISPLAY        9   /*Special pointer of lv_display_t* */
+#define LV_PROPERTY_TYPE_FONT           10  /*Special pointer of lv_font_t* */
+#define LV_PROPERTY_TYPE_BOOL           11  /*int32_t type*/
+
+#define LV_PROPERTY_TYPE_SHIFT          28
+#define LV_PROPERTY_TYPE2_SHIFT         24
+
+/* Example:
+ * LV_PROPERTY_ID(OBJ, FLAG_CLICKABLE, LV_PROPERTY_TYPE_INT, 1),
+ * produces
+ * LV_PROPERTY_OBJ_FLAG_CLICKABLE = (LV_PROPERTY_OBJ_START + (1)) | ((LV_PROPERTY_TYPE_INT) << LV_PROPERTY_TYPE_SHIFT)
+ */
+#define LV_PROPERTY_ID(clz, name, type, index)          LV_PROPERTY_## clz ##_##name = (LV_PROPERTY_## clz ##_START + ((int)index)) | ((type) << LV_PROPERTY_TYPE_SHIFT)
+
+/* Example:
+ * LV_PROPERTY_ID2(SLIDER, VALUE, LV_PROPERTY_TYPE_INT, LV_PROPERTY_TYPE_BOOL, 0)
+ * produces
+ * LV_PROPERTY_SLIDER_VALUE = (LV_PROPERTY_SLIDER_START + (0)) | ((LV_PROPERTY_TYPE_INT) << LV_PROPERTY_TYPE_SHIFT) | ((LV_PROPERTY_TYPE_BOOL) << LV_PROPERTY_TYPE2_SHIFT)
+ */
+#define LV_PROPERTY_ID2(clz, name, type, type2, index)  LV_PROPERTY_ID(clz, name, type, index) | ((type2) << LV_PROPERTY_TYPE2_SHIFT)
+
+#define LV_PROPERTY_ID_TYPE(id) ((id) >> LV_PROPERTY_TYPE_SHIFT)
+#define LV_PROPERTY_ID_TYPE2(id) ((id) >> LV_PROPERTY_TYPE_SHIFT)
+#define LV_PROPERTY_ID_INDEX(id) ((id) & 0xfffffff)
+
+/*Set properties from an array of lv_property_t*/
+#define LV_OBJ_SET_PROPERTY_ARRAY(obj, array) lv_obj_set_properties(obj, array, sizeof(array)/sizeof(array[0]))
+
+
+/**********************
+ *      TYPEDEFS
+ **********************/
+
+/**
+ * Group of predefined widget ID start value.
+ */
+enum _lv_prop_id_range_boundary_t {
+    LV_PROPERTY_ID_INVALID      = 0,
+
+    /*ID 0x01 to 0xff are style ID, check lv_style_prop_t*/
+    LV_PROPERTY_STYLE_START     = 0x00,
+
+    LV_PROPERTY_ID_START        = 0x0100, /*ID smaller than 0xff is style ID*/
+    /*Define the property ID for every widget here. */
+    LV_PROPERTY_OBJ_START       = 0x0100, /* lv_obj.c */
+    LV_PROPERTY_IMAGE_START     = 0x0200, /* lv_image.c */
+    LV_PROPERTY_LABEL_START     = 0x0300, /* lv_label.c */
+    LV_PROPERTY_KEYBOARD_START  = 0x0400, /* lv_keyboard.c */
+    LV_PROPERTY_TEXTAREA_START  = 0x0500, /* lv_textarea.c */
+    LV_PROPERTY_ROLLER_START    = 0x0600, /* lv_roller.c */
+    LV_PROPERTY_DROPDOWN_START  = 0x0700, /* lv_dropdown.c */
+    LV_PROPERTY_SLIDER_START    = 0x0800, /* lv_slider.c */
+    LV_PROPERTY_ANIMIMAGE_START = 0x0900, /* lv_animimage.c */
+
+    /*Special ID, use it to extend ID and make sure it's unique and compile time determinant*/
+    LV_PROPERTY_ID_BUILTIN_LAST = 0xffff, /*ID of 0x10000 ~ 0xfffffff is reserved for user*/
+
+    LV_PROPERTY_ID_ANY          = 0x7ffffffe, /*Special ID used by lvgl to intercept all setter/getter call.*/
+};
+
+struct _lv_property_name_t {
+    const char * name;
+    lv_prop_id_t id;
+};
+
+typedef struct {
+    lv_prop_id_t id;
+    union {
+        int32_t num;                /**< Signed integer number (enums or "normal" numbers)*/
+        uint32_t num_u;             /**< Unsigned integer number (opacity, Booleans)  */
+        bool enable;                /**< Booleans */
+        const void * ptr;           /**< Constant pointers  (font, cone text, etc.) */
+        lv_color_t color;           /**< Colors */
+        lv_value_precise_t precise; /**< float or int for precise value */
+        lv_point_t point;           /**< Point, contains two int32_t */
+
+        struct {
+            /**
+             * Note that place struct member `style` at first place is intended.
+             * `style` shares same memory with `num`, `ptr`, `color`.
+             * So we set the style value directly without using `prop.style.num`.
+             *
+             * E.g.
+             *
+             * static const lv_property_t obj_pos_x = {
+             *      .id = LV_PROPERTY_STYLE_X,
+             *      .num = 123,
+             *      .selector = LV_STATE_PRESSED,
+             * }
+             *
+             * instead of:
+             * static const lv_property_t obj_pos_x = {
+             *      .id = LV_PROPERTY_STYLE_X,
+             *      .style.num = 123, // note this line.
+             *      .selector = LV_STATE_PRESSED,
+             * }
+             */
+            lv_style_value_t style; /**< Make sure it's the first element in struct. */
+            uint32_t selector;      /**< Style selector, lv_part_t | lv_state_t */
+        };
+
+        /**
+         * For some properties like slider range, it contains two simple (4-byte) values
+         * so we can use `arg1.num` and `arg2.num` to set the argument.
+         */
+        struct {
+            union {
+                int32_t num;
+                uint32_t num_u;
+                bool enable;
+                const void * ptr;
+                lv_color_t color;
+                lv_value_precise_t precise;
+            } arg1, arg2;
+        };
+    };
+} lv_property_t;
+
+typedef struct {
+    lv_prop_id_t id;
+
+    void * setter;      /**< Callback used to set property. */
+    void * getter;      /**< Callback used to get property. */
+} lv_property_ops_t;
+
+/**********************
+ * GLOBAL PROTOTYPES
+ **********************/
+
+/*=====================
+ * Setter functions
+ *====================*/
+
+/**
+ * Set Widget property.
+ * @param obj       pointer to Widget
+ * @param value     property value to set
+ * @return          return LV_RESULT_OK if call succeeded
+ */
+lv_result_t lv_obj_set_property(lv_obj_t * obj, const lv_property_t * value);
+
+/**
+ * Set multiple Widget properties. Helper `LV_OBJ_SET_PROPERTY_ARRAY` can be used for constant property array.
+ * @param obj       pointer to Widget
+ * @param value     property value array to set
+ * @param count     number of array elements
+ * @return          return LV_RESULT_OK if call succeeded
+ */
+lv_result_t lv_obj_set_properties(lv_obj_t * obj, const lv_property_t * value, uint32_t count);
+
+/*=====================
+ * Getter functions
+ *====================*/
+
+/**
+ * Read property value from Widget.
+ * If id is a style property.  Style selector is 0 by default.
+ * @param obj       pointer to Widget
+ * @param id        ID of property to read
+ * @return          return property value read. The returned property ID is set to `LV_PROPERTY_ID_INVALID` if read failed.
+ */
+lv_property_t lv_obj_get_property(lv_obj_t * obj, lv_prop_id_t id);
+
+/**
+ * Read style property value from Widget
+ * @param obj       pointer to Widget
+ * @param id        ID of style property
+ * @param selector  selector for style property
+ * @return          return property value read. The returned property ID is set to `LV_PROPERTY_ID_INVALID` if read failed.
+ */
+lv_property_t lv_obj_get_style_property(lv_obj_t * obj, lv_prop_id_t id, uint32_t selector);
+
+/**
+ * Get property ID by recursively searching for name in Widget's class hierarchy, and
+ * if still not found, then search style properties.
+ * Requires to enabling `LV_USE_OBJ_PROPERTY_NAME`.
+ * @param obj       pointer to Widget whose class and base-class hierarchy are to be searched.
+ * @param name      property name
+ * @return          property ID found or `LV_PROPERTY_ID_INVALID` if not found.
+ */
+lv_prop_id_t lv_obj_property_get_id(const lv_obj_t * obj, const char * name);
+
+/**
+ * Get property ID by doing a non-recursive search for name directly in Widget class properties.
+ * Requires enabling `LV_USE_OBJ_PROPERTY_NAME`.
+ * @param clz       pointer to Widget class that has specified property.
+ * @param name      property name
+ * @return          property ID found or `LV_PROPERTY_ID_INVALID` if not found.
+ */
+lv_prop_id_t lv_obj_class_property_get_id(const lv_obj_class_t * clz, const char * name);
+
+/**
+ * Get style property ID by name. Requires enabling `LV_USE_OBJ_PROPERTY_NAME`.
+ * @param name      property name
+ * @return          property ID found or `LV_PROPERTY_ID_INVALID` if not found.
+ */
+lv_prop_id_t lv_style_property_get_id(const char * name);
+
+/**********************
+ *      MACROS
+ **********************/
+
+#include "../widgets/property/lv_obj_property_names.h"
+#include "../widgets/property/lv_style_properties.h"
+
+#endif /*LV_USE_OBJ_PROPERTY*/
+
+#ifdef __cplusplus
+} /*extern "C"*/
+#endif
+
+#endif /*LV_OBJ_PROPERTY_H*/

+ 813 - 0
components/easylvgl/lvgl9/src/core/lv_obj_scroll.c

@@ -0,0 +1,813 @@
+/**
+ * @file lv_obj_scroll.c
+ *
+ */
+
+/*********************
+ *      INCLUDES
+ *********************/
+#include "lv_obj_scroll_private.h"
+#include "../misc/lv_anim_private.h"
+#include "lv_obj_private.h"
+#include "../indev/lv_indev.h"
+#include "../indev/lv_indev_scroll.h"
+#include "../display/lv_display.h"
+#include "../misc/lv_area.h"
+
+/*********************
+ *      DEFINES
+ *********************/
+#define MY_CLASS (&lv_obj_class)
+#define SCROLL_ANIM_TIME_MIN    200    /*ms*/
+#define SCROLL_ANIM_TIME_MAX    400    /*ms*/
+#define SCROLLBAR_MIN_SIZE      (LV_DPX(10))
+
+/**********************
+ *      TYPEDEFS
+ **********************/
+
+/**********************
+ *  GLOBAL PROTOTYPES
+ **********************/
+
+/**********************
+ *  STATIC PROTOTYPES
+ **********************/
+static void scroll_x_anim(void * obj, int32_t v);
+static void scroll_y_anim(void * obj, int32_t v);
+static void scroll_end_cb(lv_anim_t * a);
+static void scroll_area_into_view(const lv_area_t * area, lv_obj_t * child, lv_point_t * scroll_value,
+                                  lv_anim_enable_t anim_en);
+
+/**********************
+ *  STATIC VARIABLES
+ **********************/
+
+/**********************
+ *      MACROS
+ **********************/
+
+/**********************
+ *   GLOBAL FUNCTIONS
+ **********************/
+
+/*=====================
+ * Setter functions
+ *====================*/
+
+void lv_obj_set_scrollbar_mode(lv_obj_t * obj, lv_scrollbar_mode_t mode)
+{
+    LV_ASSERT_OBJ(obj, MY_CLASS);
+
+    lv_obj_allocate_spec_attr(obj);
+
+    if(obj->spec_attr->scrollbar_mode == mode) return;
+    obj->spec_attr->scrollbar_mode = mode;
+    lv_obj_invalidate(obj);
+}
+
+void lv_obj_set_scroll_dir(lv_obj_t * obj, lv_dir_t dir)
+{
+    lv_obj_allocate_spec_attr(obj);
+
+    if(dir != obj->spec_attr->scroll_dir) {
+        obj->spec_attr->scroll_dir = dir;
+    }
+}
+
+void lv_obj_set_scroll_snap_x(lv_obj_t * obj, lv_scroll_snap_t align)
+{
+    lv_obj_allocate_spec_attr(obj);
+    obj->spec_attr->scroll_snap_x = align;
+}
+
+void lv_obj_set_scroll_snap_y(lv_obj_t * obj, lv_scroll_snap_t align)
+{
+    lv_obj_allocate_spec_attr(obj);
+    obj->spec_attr->scroll_snap_y = align;
+}
+
+/*=====================
+ * Getter functions
+ *====================*/
+
+lv_scrollbar_mode_t lv_obj_get_scrollbar_mode(const lv_obj_t * obj)
+{
+    if(obj->spec_attr) return (lv_scrollbar_mode_t) obj->spec_attr->scrollbar_mode;
+    else return LV_SCROLLBAR_MODE_AUTO;
+}
+
+lv_dir_t lv_obj_get_scroll_dir(const lv_obj_t * obj)
+{
+    if(obj->spec_attr) return (lv_dir_t) obj->spec_attr->scroll_dir;
+    else return LV_DIR_ALL;
+}
+
+lv_scroll_snap_t lv_obj_get_scroll_snap_x(const lv_obj_t * obj)
+{
+    if(obj->spec_attr) return (lv_scroll_snap_t) obj->spec_attr->scroll_snap_x;
+    else return LV_SCROLL_SNAP_NONE;
+}
+
+lv_scroll_snap_t lv_obj_get_scroll_snap_y(const lv_obj_t * obj)
+{
+    if(obj->spec_attr) return (lv_scroll_snap_t) obj->spec_attr->scroll_snap_y;
+    else return LV_SCROLL_SNAP_NONE;
+}
+
+int32_t lv_obj_get_scroll_x(const lv_obj_t * obj)
+{
+    if(obj->spec_attr == NULL) return 0;
+    return -obj->spec_attr->scroll.x;
+}
+
+int32_t lv_obj_get_scroll_y(const lv_obj_t * obj)
+{
+    if(obj->spec_attr == NULL) return 0;
+    return -obj->spec_attr->scroll.y;
+}
+
+int32_t lv_obj_get_scroll_top(const lv_obj_t * obj)
+{
+    if(obj->spec_attr == NULL) return 0;
+    return -obj->spec_attr->scroll.y;
+}
+
+int32_t lv_obj_get_scroll_bottom(const lv_obj_t * obj)
+{
+    LV_ASSERT_OBJ(obj, MY_CLASS);
+
+    int32_t child_res = LV_COORD_MIN;
+    uint32_t i;
+    uint32_t child_cnt = lv_obj_get_child_count(obj);
+    for(i = 0; i < child_cnt; i++) {
+        const lv_obj_t * child = obj->spec_attr->children[i];
+        if(lv_obj_has_flag_any(child,  LV_OBJ_FLAG_HIDDEN | LV_OBJ_FLAG_FLOATING)) continue;
+
+        int32_t tmp_y = child->coords.y2 + lv_obj_get_style_margin_bottom(child, LV_PART_MAIN);
+        child_res = LV_MAX(child_res, tmp_y);
+    }
+
+    int32_t space_top = lv_obj_get_style_space_top(obj, LV_PART_MAIN);
+    int32_t space_bottom = lv_obj_get_style_space_bottom(obj, LV_PART_MAIN);
+
+    if(child_res != LV_COORD_MIN) {
+        child_res -= (obj->coords.y2 - space_bottom);
+    }
+
+    int32_t self_h = lv_obj_get_self_height(obj);
+    self_h = self_h - (lv_obj_get_height(obj) - space_top - space_bottom);
+    self_h -= lv_obj_get_scroll_y(obj);
+    return LV_MAX(child_res, self_h);
+}
+
+int32_t lv_obj_get_scroll_left(const lv_obj_t * obj)
+{
+    LV_ASSERT_OBJ(obj, MY_CLASS);
+
+    /*Normally can't scroll the object out on the left.
+     *So simply use the current scroll position as "left size"*/
+    if(lv_obj_get_style_base_dir(obj, LV_PART_MAIN) != LV_BASE_DIR_RTL) {
+        if(obj->spec_attr == NULL) return 0;
+        return -obj->spec_attr->scroll.x;
+    }
+
+    /*With RTL base direction scrolling the left is normal so find the left most coordinate*/
+    int32_t space_right = lv_obj_get_style_space_right(obj, LV_PART_MAIN);
+    int32_t space_left = lv_obj_get_style_space_left(obj, LV_PART_MAIN);
+
+    int32_t child_res = 0;
+
+    uint32_t i;
+    int32_t x1 = LV_COORD_MAX;
+    uint32_t child_cnt = lv_obj_get_child_count(obj);
+    for(i = 0; i < child_cnt; i++) {
+        const lv_obj_t * child = obj->spec_attr->children[i];
+        if(lv_obj_has_flag_any(child,  LV_OBJ_FLAG_HIDDEN | LV_OBJ_FLAG_FLOATING)) continue;
+
+        int32_t tmp_x = child->coords.x1 - lv_obj_get_style_margin_left(child, LV_PART_MAIN);
+        x1 = LV_MIN(x1, tmp_x);
+    }
+
+    if(x1 != LV_COORD_MAX) {
+        child_res = x1;
+        child_res = (obj->coords.x1 + space_left) - child_res;
+    }
+    else {
+        child_res = LV_COORD_MIN;
+    }
+
+    int32_t self_w = lv_obj_get_self_width(obj);
+    self_w = self_w - (lv_obj_get_width(obj) - space_right - space_left);
+    self_w += lv_obj_get_scroll_x(obj);
+
+    return LV_MAX(child_res, self_w);
+}
+
+int32_t lv_obj_get_scroll_right(const lv_obj_t * obj)
+{
+    LV_ASSERT_OBJ(obj, MY_CLASS);
+
+    /*With RTL base dir can't scroll to the object out on the right.
+     *So simply use the current scroll position as "right size"*/
+    if(lv_obj_get_style_base_dir(obj, LV_PART_MAIN) == LV_BASE_DIR_RTL) {
+        if(obj->spec_attr == NULL) return 0;
+        return obj->spec_attr->scroll.x;
+    }
+
+    /*With other base direction (LTR) scrolling to the right is normal so find the right most coordinate*/
+    int32_t child_res = LV_COORD_MIN;
+    uint32_t i;
+    uint32_t child_cnt = lv_obj_get_child_count(obj);
+    for(i = 0; i < child_cnt; i++) {
+        const lv_obj_t * child = obj->spec_attr->children[i];
+        if(lv_obj_has_flag_any(child,  LV_OBJ_FLAG_HIDDEN | LV_OBJ_FLAG_FLOATING)) continue;
+
+        int32_t tmp_x = child->coords.x2 + lv_obj_get_style_margin_right(child, LV_PART_MAIN);
+        child_res = LV_MAX(child_res, tmp_x);
+    }
+
+    int32_t space_right = lv_obj_get_style_space_right(obj, LV_PART_MAIN);
+    int32_t space_left = lv_obj_get_style_space_left(obj, LV_PART_MAIN);
+
+    if(child_res != LV_COORD_MIN) {
+        child_res -= (obj->coords.x2 - space_right);
+    }
+
+    int32_t self_w;
+    self_w = lv_obj_get_self_width(obj);
+    self_w = self_w - (lv_obj_get_width(obj) - space_right - space_left);
+    self_w -= lv_obj_get_scroll_x(obj);
+    return LV_MAX(child_res, self_w);
+}
+
+void lv_obj_get_scroll_end(lv_obj_t * obj, lv_point_t * end)
+{
+    lv_anim_t * a;
+    a = lv_anim_get(obj, scroll_x_anim);
+    end->x = a ? -a->end_value : lv_obj_get_scroll_x(obj);
+
+    a = lv_anim_get(obj, scroll_y_anim);
+    end->y = a ? -a->end_value : lv_obj_get_scroll_y(obj);
+}
+
+/*=====================
+ * Other functions
+ *====================*/
+
+void lv_obj_scroll_by_bounded(lv_obj_t * obj, int32_t dx, int32_t dy, lv_anim_enable_t anim_en)
+{
+    if(dx == 0 && dy == 0) return;
+
+    /*We need to know the final sizes for bound check*/
+    lv_obj_update_layout(obj);
+
+    /*Don't let scroll more than naturally possible by the size of the content*/
+    int32_t x_current = -lv_obj_get_scroll_x(obj);
+    int32_t x_bounded = x_current + dx;
+
+    if(lv_obj_get_style_base_dir(obj, LV_PART_MAIN) != LV_BASE_DIR_RTL) {
+        if(x_bounded > 0) x_bounded = 0;
+        if(x_bounded < 0) {
+            int32_t  scroll_max = lv_obj_get_scroll_left(obj) + lv_obj_get_scroll_right(obj);
+            if(scroll_max < 0) scroll_max = 0;
+
+            if(x_bounded < -scroll_max) x_bounded = -scroll_max;
+        }
+    }
+    else {
+        if(x_bounded < 0) x_bounded = 0;
+        if(x_bounded > 0) {
+            int32_t  scroll_max = lv_obj_get_scroll_left(obj) + lv_obj_get_scroll_right(obj);
+            if(scroll_max < 0) scroll_max = 0;
+
+            if(x_bounded > scroll_max) x_bounded = scroll_max;
+        }
+    }
+
+    /*Don't let scroll more than naturally possible by the size of the content*/
+    int32_t y_current = -lv_obj_get_scroll_y(obj);
+    int32_t y_bounded = y_current + dy;
+
+    if(y_bounded > 0) y_bounded = 0;
+    if(y_bounded < 0) {
+        int32_t  scroll_max = lv_obj_get_scroll_top(obj) + lv_obj_get_scroll_bottom(obj);
+        if(scroll_max < 0) scroll_max = 0;
+        if(y_bounded < -scroll_max) y_bounded = -scroll_max;
+    }
+
+    dx = x_bounded - x_current;
+    dy = y_bounded - y_current;
+    if(dx || dy) {
+        lv_obj_scroll_by(obj, dx, dy, anim_en);
+    }
+}
+
+void lv_obj_scroll_by(lv_obj_t * obj, int32_t dx, int32_t dy, lv_anim_enable_t anim_en)
+{
+    if(dx == 0 && dy == 0) return;
+    if(anim_en) {
+        lv_display_t * d = lv_obj_get_display(obj);
+        lv_anim_t a;
+        lv_anim_init(&a);
+        lv_anim_set_var(&a, obj);
+        lv_anim_set_deleted_cb(&a, scroll_end_cb);
+
+        if(dx) {
+            uint32_t t = lv_anim_speed_clamped((lv_display_get_horizontal_resolution(d)) >> 1, SCROLL_ANIM_TIME_MIN,
+                                               SCROLL_ANIM_TIME_MAX);
+            lv_anim_set_duration(&a, t);
+            int32_t sx = lv_obj_get_scroll_x(obj);
+            lv_anim_set_values(&a, -sx, -sx + dx);
+            lv_anim_set_exec_cb(&a, scroll_x_anim);
+            lv_anim_set_path_cb(&a, lv_anim_path_ease_out);
+
+            lv_result_t res;
+            res = lv_obj_send_event(obj, LV_EVENT_SCROLL_BEGIN, &a);
+            if(res != LV_RESULT_OK) return;
+            lv_anim_start(&a);
+        }
+
+        if(dy) {
+            uint32_t t = lv_anim_speed_clamped((lv_display_get_vertical_resolution(d)) >> 1, SCROLL_ANIM_TIME_MIN,
+                                               SCROLL_ANIM_TIME_MAX);
+            lv_anim_set_duration(&a, t);
+            int32_t sy = lv_obj_get_scroll_y(obj);
+            lv_anim_set_values(&a, -sy, -sy + dy);
+            lv_anim_set_exec_cb(&a,  scroll_y_anim);
+            lv_anim_set_path_cb(&a, lv_anim_path_ease_out);
+
+            lv_result_t res;
+            res = lv_obj_send_event(obj, LV_EVENT_SCROLL_BEGIN, &a);
+            if(res != LV_RESULT_OK) return;
+            lv_anim_start(&a);
+        }
+    }
+    else {
+        /*Remove pending animations*/
+        lv_anim_delete(obj, scroll_y_anim);
+        lv_anim_delete(obj, scroll_x_anim);
+
+        lv_result_t res;
+        res = lv_obj_send_event(obj, LV_EVENT_SCROLL_BEGIN, NULL);
+        if(res != LV_RESULT_OK) return;
+
+        res = lv_obj_scroll_by_raw(obj, dx, dy);
+        if(res != LV_RESULT_OK) return;
+
+        res = lv_obj_send_event(obj, LV_EVENT_SCROLL_END, NULL);
+        if(res != LV_RESULT_OK) return;
+    }
+}
+
+void lv_obj_scroll_to(lv_obj_t * obj, int32_t x, int32_t y, lv_anim_enable_t anim_en)
+{
+    lv_obj_scroll_to_x(obj, x, anim_en);
+    lv_obj_scroll_to_y(obj, y, anim_en);
+}
+
+void lv_obj_scroll_to_x(lv_obj_t * obj, int32_t x, lv_anim_enable_t anim_en)
+{
+    lv_anim_delete(obj, scroll_x_anim);
+
+    int32_t scroll_x = lv_obj_get_scroll_x(obj);
+    int32_t diff = -x + scroll_x;
+
+    lv_obj_scroll_by_bounded(obj, diff, 0, anim_en);
+}
+
+void lv_obj_scroll_to_y(lv_obj_t * obj, int32_t y, lv_anim_enable_t anim_en)
+{
+    lv_anim_delete(obj, scroll_y_anim);
+
+    int32_t scroll_y = lv_obj_get_scroll_y(obj);
+    int32_t diff = -y + scroll_y;
+
+    lv_obj_scroll_by_bounded(obj, 0, diff, anim_en);
+}
+
+void lv_obj_scroll_to_view(lv_obj_t * obj, lv_anim_enable_t anim_en)
+{
+    /*Be sure the screens layout is correct*/
+    lv_obj_update_layout(obj);
+
+    lv_point_t p = {0, 0};
+    scroll_area_into_view(&obj->coords, obj, &p, anim_en);
+}
+
+void lv_obj_scroll_to_view_recursive(lv_obj_t * obj, lv_anim_enable_t anim_en)
+{
+    /*Be sure the screens layout is correct*/
+    lv_obj_update_layout(obj);
+
+    lv_point_t p = {0, 0};
+    lv_obj_t * child = obj;
+    lv_obj_t * parent = lv_obj_get_parent(child);
+    while(parent) {
+        scroll_area_into_view(&obj->coords, child, &p, anim_en);
+        child = parent;
+        parent = lv_obj_get_parent(parent);
+    }
+}
+
+lv_result_t lv_obj_scroll_by_raw(lv_obj_t * obj, int32_t x, int32_t y)
+{
+    if(x == 0 && y == 0) return LV_RESULT_OK;
+
+    lv_obj_allocate_spec_attr(obj);
+
+    obj->spec_attr->scroll.x += x;
+    obj->spec_attr->scroll.y += y;
+
+    lv_obj_move_children_by(obj, x, y, true);
+    lv_result_t res = lv_obj_send_event(obj, LV_EVENT_SCROLL, NULL);
+    if(res != LV_RESULT_OK) return res;
+    lv_obj_invalidate(obj);
+    return LV_RESULT_OK;
+}
+
+bool lv_obj_is_scrolling(const lv_obj_t * obj)
+{
+    lv_indev_t * indev = lv_indev_get_next(NULL);
+    while(indev) {
+        if(lv_indev_get_scroll_obj(indev) == obj) return true;
+        indev = lv_indev_get_next(indev);
+    }
+
+    if(lv_anim_get((lv_obj_t *)obj, scroll_x_anim) != NULL ||
+       lv_anim_get((lv_obj_t *)obj, scroll_y_anim) != NULL) {
+        return true;
+    }
+
+    return false;
+}
+
+void lv_obj_stop_scroll_anim(const lv_obj_t * obj)
+{
+    lv_anim_delete((lv_obj_t *)obj, scroll_y_anim);
+    lv_anim_delete((lv_obj_t *)obj, scroll_x_anim);
+}
+
+void lv_obj_update_snap(lv_obj_t * obj, lv_anim_enable_t anim_en)
+{
+    lv_obj_update_layout(obj);
+    lv_point_t p;
+    lv_indev_scroll_get_snap_dist(obj, &p);
+    if(p.x == LV_COORD_MAX || p.x == LV_COORD_MIN) p.x = 0;
+    if(p.y == LV_COORD_MAX || p.y == LV_COORD_MIN) p.y = 0;
+    lv_obj_scroll_by(obj, p.x, p.y, anim_en);
+}
+
+void lv_obj_get_scrollbar_area(lv_obj_t * obj, lv_area_t * hor_area, lv_area_t * ver_area)
+{
+    lv_area_set(hor_area, 0, 0, -1, -1);
+    lv_area_set(ver_area, 0, 0, -1, -1);
+
+    if(lv_obj_has_flag(obj, LV_OBJ_FLAG_SCROLLABLE) == false) return;
+
+    lv_scrollbar_mode_t sm = lv_obj_get_scrollbar_mode(obj);
+    if(sm == LV_SCROLLBAR_MODE_OFF)  return;
+
+    /*If there is no indev scrolling this object but `mode==active` return*/
+    lv_indev_t * indev = lv_indev_get_next(NULL);
+    if(sm == LV_SCROLLBAR_MODE_ACTIVE) {
+        while(indev) {
+            if(lv_indev_get_scroll_obj(indev) == obj) break;
+            indev = lv_indev_get_next(indev);
+        }
+        if(indev == NULL)  return;
+    }
+
+    int32_t st = lv_obj_get_scroll_top(obj);
+    int32_t sb = lv_obj_get_scroll_bottom(obj);
+    int32_t sl = lv_obj_get_scroll_left(obj);
+    int32_t sr = lv_obj_get_scroll_right(obj);
+
+    lv_dir_t dir = lv_obj_get_scroll_dir(obj);
+
+    bool ver_draw = false;
+    if((dir & LV_DIR_VER) &&
+       ((sm == LV_SCROLLBAR_MODE_ON) ||
+        (sm == LV_SCROLLBAR_MODE_AUTO && (st > 0 || sb > 0)) ||
+        (sm == LV_SCROLLBAR_MODE_ACTIVE && lv_indev_get_scroll_dir(indev) == LV_DIR_VER))) {
+        ver_draw = true;
+    }
+
+    bool hor_draw = false;
+    if((dir & LV_DIR_HOR) &&
+       ((sm == LV_SCROLLBAR_MODE_ON) ||
+        (sm == LV_SCROLLBAR_MODE_AUTO && (sl > 0 || sr > 0)) ||
+        (sm == LV_SCROLLBAR_MODE_ACTIVE && lv_indev_get_scroll_dir(indev) == LV_DIR_HOR))) {
+        hor_draw = true;
+    }
+
+    if(!hor_draw && !ver_draw) return;
+
+    bool rtl = lv_obj_get_style_base_dir(obj, LV_PART_SCROLLBAR) == LV_BASE_DIR_RTL;
+
+    int32_t top_space = lv_obj_get_style_pad_top(obj, LV_PART_SCROLLBAR);
+    int32_t bottom_space = lv_obj_get_style_pad_bottom(obj, LV_PART_SCROLLBAR);
+    int32_t left_space = lv_obj_get_style_pad_left(obj, LV_PART_SCROLLBAR);
+    int32_t right_space = lv_obj_get_style_pad_right(obj, LV_PART_SCROLLBAR);
+    int32_t thickness = lv_obj_get_style_width(obj, LV_PART_SCROLLBAR);
+    int32_t length = lv_obj_get_style_length(obj, LV_PART_SCROLLBAR);
+
+    int32_t obj_h = lv_obj_get_height(obj);
+    int32_t obj_w = lv_obj_get_width(obj);
+
+    /*Space required for the vertical and horizontal scrollbars*/
+    int32_t ver_reg_space = ver_draw ? thickness : 0;
+    int32_t hor_req_space = hor_draw ? thickness : 0;
+    int32_t rem;
+
+    if(lv_obj_get_style_bg_opa(obj, LV_PART_SCROLLBAR) <= LV_OPA_MIN &&
+       lv_obj_get_style_border_opa(obj, LV_PART_SCROLLBAR) <= LV_OPA_MIN) {
+        return;
+    }
+
+    /*Draw vertical scrollbar if the mode is ON or can be scrolled in this direction*/
+    int32_t content_h = obj_h + st + sb;
+    if(ver_draw && content_h) {
+        ver_area->y1 = obj->coords.y1;
+        ver_area->y2 = obj->coords.y2;
+        if(rtl) {
+            ver_area->x1 = obj->coords.x1 + left_space;
+            ver_area->x2 = ver_area->x1 + thickness - 1;
+        }
+        else {
+            ver_area->x2 = obj->coords.x2 - right_space;
+            ver_area->x1 = ver_area->x2 - thickness + 1;
+        }
+
+        int32_t sb_h = ((obj_h - top_space - bottom_space - hor_req_space) * obj_h) / content_h;
+        sb_h = LV_MAX(length > 0 ? length : sb_h, SCROLLBAR_MIN_SIZE); /*Style-defined size, calculated size, or minimum size*/
+        sb_h = LV_MIN(sb_h, obj_h); /*Limit scrollbar length to parent height*/
+        rem = (obj_h - top_space - bottom_space - hor_req_space) -
+              sb_h;  /*Remaining size from the scrollbar track that is not the scrollbar itself*/
+        int32_t scroll_h = content_h - obj_h; /*The size of the content which can be really scrolled*/
+        if(scroll_h <= 0) {
+            ver_area->y1 = obj->coords.y1 + top_space;
+            ver_area->y2 = obj->coords.y2 - bottom_space - hor_req_space - 1;
+        }
+        else {
+            int32_t sb_y = (rem * sb) / scroll_h;
+            sb_y = rem - sb_y;
+
+            ver_area->y1 = obj->coords.y1 + sb_y + top_space;
+            ver_area->y2 = ver_area->y1 + sb_h - 1;
+            if(ver_area->y1 < obj->coords.y1 + top_space) {
+                ver_area->y1 = obj->coords.y1 + top_space;
+                if(ver_area->y1 + SCROLLBAR_MIN_SIZE > ver_area->y2) {
+                    ver_area->y2 = ver_area->y1 + SCROLLBAR_MIN_SIZE;
+                }
+            }
+            if(ver_area->y2 > obj->coords.y2 - hor_req_space - bottom_space) {
+                ver_area->y2 = obj->coords.y2 - hor_req_space - bottom_space;
+                if(ver_area->y2 - SCROLLBAR_MIN_SIZE < ver_area->y1) {
+                    ver_area->y1 = ver_area->y2 - SCROLLBAR_MIN_SIZE;
+                }
+            }
+        }
+    }
+
+    /*Draw horizontal scrollbar if the mode is ON or can be scrolled in this direction*/
+    int32_t content_w = obj_w + sl + sr;
+    if(hor_draw && content_w) {
+        hor_area->y2 = obj->coords.y2 - bottom_space;
+        hor_area->y1 = hor_area->y2 - thickness + 1;
+        hor_area->x1 = obj->coords.x1;
+        hor_area->x2 = obj->coords.x2;
+
+        int32_t sb_w = ((obj_w - left_space - right_space - ver_reg_space) * obj_w) / content_w;
+        sb_w = LV_MAX(length > 0 ? length : sb_w, SCROLLBAR_MIN_SIZE); /*Style-defined size, calculated size, or minimum size*/
+        sb_w = LV_MIN(sb_w, obj_w); /*Limit scrollbar length to parent width*/
+        rem = (obj_w - left_space - right_space - ver_reg_space) -
+              sb_w;  /*Remaining size from the scrollbar track that is not the scrollbar itself*/
+        int32_t scroll_w = content_w - obj_w; /*The size of the content which can be really scrolled*/
+        if(scroll_w <= 0) {
+            if(rtl) {
+                hor_area->x1 = obj->coords.x1 + left_space + ver_reg_space - 1;
+                hor_area->x2 = obj->coords.x2 - right_space;
+            }
+            else {
+                hor_area->x1 = obj->coords.x1 + left_space;
+                hor_area->x2 = obj->coords.x2 - right_space - ver_reg_space - 1;
+            }
+        }
+        else {
+            int32_t sb_x = (rem * sr) / scroll_w;
+            sb_x = rem - sb_x;
+
+            if(rtl) {
+                hor_area->x1 = obj->coords.x1 + sb_x + left_space + ver_reg_space;
+                hor_area->x2 = hor_area->x1 + sb_w - 1;
+                if(hor_area->x1 < obj->coords.x1 + left_space + ver_reg_space) {
+                    hor_area->x1 = obj->coords.x1 + left_space + ver_reg_space;
+                    if(hor_area->x1 + SCROLLBAR_MIN_SIZE > hor_area->x2) {
+                        hor_area->x2 = hor_area->x1 + SCROLLBAR_MIN_SIZE;
+                    }
+                }
+                if(hor_area->x2 > obj->coords.x2 - right_space) {
+                    hor_area->x2 = obj->coords.x2 - right_space;
+                    if(hor_area->x2 - SCROLLBAR_MIN_SIZE < hor_area->x1) {
+                        hor_area->x1 = hor_area->x2 - SCROLLBAR_MIN_SIZE;
+                    }
+                }
+            }
+            else {
+                hor_area->x1 = obj->coords.x1 + sb_x + left_space;
+                hor_area->x2 = hor_area->x1 + sb_w - 1;
+                if(hor_area->x1 < obj->coords.x1 + left_space) {
+                    hor_area->x1 = obj->coords.x1 + left_space;
+                    if(hor_area->x1 + SCROLLBAR_MIN_SIZE > hor_area->x2) {
+                        hor_area->x2 = hor_area->x1 + SCROLLBAR_MIN_SIZE;
+                    }
+                }
+                if(hor_area->x2 > obj->coords.x2 - ver_reg_space - right_space) {
+                    hor_area->x2 = obj->coords.x2 - ver_reg_space - right_space;
+                    if(hor_area->x2 - SCROLLBAR_MIN_SIZE < hor_area->x1) {
+                        hor_area->x1 = hor_area->x2 - SCROLLBAR_MIN_SIZE;
+                    }
+                }
+            }
+        }
+    }
+}
+
+void lv_obj_scrollbar_invalidate(lv_obj_t * obj)
+{
+    lv_area_t hor_area;
+    lv_area_t ver_area;
+    lv_obj_get_scrollbar_area(obj, &hor_area, &ver_area);
+
+    if(lv_area_get_size(&hor_area) <= 0 && lv_area_get_size(&ver_area) <= 0) return;
+
+    if(lv_area_get_size(&hor_area) > 0) lv_obj_invalidate_area(obj, &hor_area);
+    if(lv_area_get_size(&ver_area) > 0) lv_obj_invalidate_area(obj, &ver_area);
+}
+
+void lv_obj_readjust_scroll(lv_obj_t * obj, lv_anim_enable_t anim_en)
+{
+    /*Be sure the bottom side is not remains scrolled in*/
+    /*With snapping the content can't be scrolled in*/
+    if(lv_obj_get_scroll_snap_y(obj) == LV_SCROLL_SNAP_NONE) {
+        int32_t st = lv_obj_get_scroll_top(obj);
+        int32_t sb = lv_obj_get_scroll_bottom(obj);
+        if(sb < 0 && st > 0) {
+            sb = LV_MIN(st, -sb);
+            lv_obj_scroll_by(obj, 0, sb, anim_en);
+        }
+    }
+
+    if(lv_obj_get_scroll_snap_x(obj) == LV_SCROLL_SNAP_NONE) {
+        int32_t sl = lv_obj_get_scroll_left(obj);
+        int32_t sr = lv_obj_get_scroll_right(obj);
+        if(lv_obj_get_style_base_dir(obj, LV_PART_MAIN) != LV_BASE_DIR_RTL) {
+            /*Be sure the left side is not remains scrolled in*/
+            if(sr < 0 && sl > 0) {
+                sr = LV_MIN(sl, -sr);
+                lv_obj_scroll_by(obj, sr, 0, anim_en);
+            }
+        }
+        else {
+            /*Be sure the right side is not remains scrolled in*/
+            if(sl < 0 && sr > 0) {
+                sr = LV_MIN(sr, -sl);
+                lv_obj_scroll_by(obj, sl, 0, anim_en);
+            }
+        }
+    }
+}
+
+/**********************
+ *   STATIC FUNCTIONS
+ **********************/
+
+static void scroll_x_anim(void * obj, int32_t v)
+{
+    lv_obj_scroll_by_raw(obj, v + lv_obj_get_scroll_x(obj), 0);
+}
+
+static void scroll_y_anim(void * obj, int32_t v)
+{
+    lv_obj_scroll_by_raw(obj, 0, v + lv_obj_get_scroll_y(obj));
+}
+
+static void scroll_end_cb(lv_anim_t * a)
+{
+    /*Do not sent END event if there wasn't a BEGIN*/
+    if(a->start_cb_called) lv_obj_send_event(a->var, LV_EVENT_SCROLL_END, NULL);
+}
+
+static void scroll_area_into_view(const lv_area_t * area, lv_obj_t * child, lv_point_t * scroll_value,
+                                  lv_anim_enable_t anim_en)
+{
+    lv_obj_t * parent = lv_obj_get_parent(child);
+    if(!lv_obj_has_flag(parent, LV_OBJ_FLAG_SCROLLABLE)) return;
+
+    lv_dir_t scroll_dir = lv_obj_get_scroll_dir(parent);
+    int32_t snap_goal = 0;
+    int32_t act = 0;
+    const lv_area_t * area_tmp;
+
+    int32_t y_scroll = 0;
+    lv_scroll_snap_t snap_y = lv_obj_get_scroll_snap_y(parent);
+    if(snap_y != LV_SCROLL_SNAP_NONE) area_tmp = &child->coords;
+    else area_tmp = area;
+
+    int32_t stop = lv_obj_get_style_space_top(parent, LV_PART_MAIN);
+    int32_t sbottom = lv_obj_get_style_space_bottom(parent, LV_PART_MAIN);
+    int32_t top_diff = parent->coords.y1 + stop - area_tmp->y1 - scroll_value->y;
+    int32_t bottom_diff = -(parent->coords.y2 - sbottom - area_tmp->y2 - scroll_value->y);
+    int32_t parent_h = lv_obj_get_height(parent) - stop - sbottom;
+    if((top_diff >= 0 && bottom_diff >= 0)) y_scroll = 0;
+    else if(top_diff > 0) {
+        y_scroll = top_diff;
+        /*Do not let scrolling in*/
+        int32_t st = lv_obj_get_scroll_top(parent);
+        if(st - y_scroll < 0) y_scroll = 0;
+    }
+    else if(bottom_diff > 0) {
+        y_scroll = -bottom_diff;
+        /*Do not let scrolling in*/
+        int32_t sb = lv_obj_get_scroll_bottom(parent);
+        if(sb + y_scroll < 0) y_scroll = 0;
+    }
+
+    switch(snap_y) {
+        case LV_SCROLL_SNAP_START:
+            snap_goal = parent->coords.y1 + stop;
+            act = area_tmp->y1 + y_scroll;
+            y_scroll += snap_goal - act;
+            break;
+        case LV_SCROLL_SNAP_END:
+            snap_goal = parent->coords.y2 - sbottom;
+            act = area_tmp->y2 + y_scroll;
+            y_scroll += snap_goal - act;
+            break;
+        case LV_SCROLL_SNAP_CENTER:
+            snap_goal = parent->coords.y1 + stop + parent_h / 2;
+            act = lv_area_get_height(area_tmp) / 2 + area_tmp->y1 + y_scroll;
+            y_scroll += snap_goal - act;
+            break;
+        case LV_SCROLL_SNAP_NONE:
+            break;
+    }
+
+    int32_t x_scroll = 0;
+    lv_scroll_snap_t snap_x = lv_obj_get_scroll_snap_x(parent);
+    if(snap_x != LV_SCROLL_SNAP_NONE) area_tmp = &child->coords;
+    else area_tmp = area;
+
+    int32_t sleft = lv_obj_get_style_space_left(parent, LV_PART_MAIN);
+    int32_t sright = lv_obj_get_style_space_right(parent, LV_PART_MAIN);
+    int32_t left_diff = parent->coords.x1 + sleft - area_tmp->x1 - scroll_value->x;
+    int32_t right_diff = -(parent->coords.x2 - sright - area_tmp->x2 - scroll_value->x);
+    if((left_diff >= 0 && right_diff >= 0)) x_scroll = 0;
+    else if(left_diff > 0) {
+        x_scroll = left_diff;
+        /*Do not let scrolling in*/
+        int32_t sl = lv_obj_get_scroll_left(parent);
+        if(sl - x_scroll < 0) x_scroll = 0;
+    }
+    else if(right_diff > 0) {
+        x_scroll = -right_diff;
+        /*Do not let scrolling in*/
+        int32_t sr = lv_obj_get_scroll_right(parent);
+        if(sr + x_scroll < 0) x_scroll = 0;
+    }
+
+    int32_t parent_w = lv_obj_get_width(parent) - sleft - sright;
+    switch(snap_x) {
+        case LV_SCROLL_SNAP_START:
+            snap_goal = parent->coords.x1 + sleft;
+            act = area_tmp->x1 + x_scroll;
+            x_scroll += snap_goal - act;
+            break;
+        case LV_SCROLL_SNAP_END:
+            snap_goal = parent->coords.x2 - sright;
+            act = area_tmp->x2 + x_scroll;
+            x_scroll += snap_goal - act;
+            break;
+        case LV_SCROLL_SNAP_CENTER:
+            snap_goal = parent->coords.x1 + sleft + parent_w / 2;
+            act = lv_area_get_width(area_tmp) / 2 + area_tmp->x1 + x_scroll;
+            x_scroll += snap_goal - act;
+            break;
+        case LV_SCROLL_SNAP_NONE:
+            break;
+    }
+
+    /*Remove any pending scroll animations.*/
+    lv_anim_delete(parent, scroll_y_anim);
+    lv_anim_delete(parent, scroll_x_anim);
+
+    if((scroll_dir & LV_DIR_LEFT) == 0 && x_scroll < 0) x_scroll = 0;
+    if((scroll_dir & LV_DIR_RIGHT) == 0 && x_scroll > 0) x_scroll = 0;
+    if((scroll_dir & LV_DIR_TOP) == 0 && y_scroll < 0) y_scroll = 0;
+    if((scroll_dir & LV_DIR_BOTTOM) == 0 && y_scroll > 0) y_scroll = 0;
+
+    scroll_value->x += anim_en ? x_scroll : 0;
+    scroll_value->y += anim_en ? y_scroll : 0;
+    lv_obj_scroll_by(parent, x_scroll, y_scroll, anim_en);
+}

+ 301 - 0
components/easylvgl/lvgl9/src/core/lv_obj_scroll.h

@@ -0,0 +1,301 @@
+/**
+ * @file lv_obj_scroll.h
+ *
+ */
+
+#ifndef LV_OBJ_SCROLL_H
+#define LV_OBJ_SCROLL_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/*********************
+ *      INCLUDES
+ *********************/
+#include "../misc/lv_area.h"
+#include "../misc/lv_anim.h"
+#include "../misc/lv_types.h"
+
+/*********************
+ *      DEFINES
+ *********************/
+
+/**********************
+ *      TYPEDEFS
+ **********************/
+
+/*Can't include lv_obj.h because it includes this header file*/
+
+/** Scrollbar modes: shows when should the scrollbars be visible*/
+typedef enum {
+    LV_SCROLLBAR_MODE_OFF,      /**< Never show scrollbars*/
+    LV_SCROLLBAR_MODE_ON,       /**< Always show scrollbars*/
+    LV_SCROLLBAR_MODE_ACTIVE,   /**< Show scroll bars when Widget is being scrolled*/
+    LV_SCROLLBAR_MODE_AUTO,     /**< Show scroll bars when the content is large enough to be scrolled*/
+} lv_scrollbar_mode_t;
+
+/** Scroll span align options. Tells where to align the snappable children when scroll stops.*/
+typedef enum {
+    LV_SCROLL_SNAP_NONE,    /**< Do not align, leave where it is*/
+    LV_SCROLL_SNAP_START,   /**< Align to the left/top*/
+    LV_SCROLL_SNAP_END,     /**< Align to the right/bottom*/
+    LV_SCROLL_SNAP_CENTER   /**< Align to the center*/
+} lv_scroll_snap_t;
+
+/**********************
+ * GLOBAL PROTOTYPES
+ **********************/
+
+/*=====================
+ * Setter functions
+ *====================*/
+
+/**
+ * Set how the scrollbars should behave.
+ * @param obj       pointer to Widget
+ * @param mode      LV_SCROLL_MODE_ON/OFF/AUTO/ACTIVE
+ */
+void lv_obj_set_scrollbar_mode(lv_obj_t * obj, lv_scrollbar_mode_t mode);
+
+/**
+ * Set direction Widget can be scrolled
+ * @param obj       pointer to Widget
+ * @param dir       one or more bit-wise OR-ed values of `lv_dir_t` enumeration
+ */
+void lv_obj_set_scroll_dir(lv_obj_t * obj, lv_dir_t dir);
+
+/**
+ * Set where to snap the children when scrolling ends horizontally
+ * @param obj       pointer to Widget
+ * @param align     value from `lv_scroll_snap_t` enumeration
+ */
+void lv_obj_set_scroll_snap_x(lv_obj_t * obj, lv_scroll_snap_t align);
+
+/**
+ * Set where to snap the children when scrolling ends vertically
+ * @param obj       pointer to Widget
+ * @param align     value from `lv_scroll_snap_t` enumeration
+ */
+void lv_obj_set_scroll_snap_y(lv_obj_t * obj, lv_scroll_snap_t align);
+
+/*=====================
+ * Getter functions
+ *====================*/
+
+/**
+ * Get the current scroll mode (when to hide the scrollbars)
+ * @param obj       pointer to Widget
+ * @return          the current scroll mode from `lv_scrollbar_mode_t`
+ */
+lv_scrollbar_mode_t lv_obj_get_scrollbar_mode(const lv_obj_t * obj);
+
+/**
+ * Get directions Widget can be scrolled (set with `lv_obj_set_scroll_dir()`)
+ * @param obj       pointer to Widget
+ * @return          current scroll direction bit(s)
+ */
+lv_dir_t lv_obj_get_scroll_dir(const lv_obj_t * obj);
+
+/**
+ * Get where to snap child Widgets when horizontal scrolling ends.
+ * @param obj       pointer to Widget
+ * @return          current snap value from `lv_scroll_snap_t`
+ */
+lv_scroll_snap_t lv_obj_get_scroll_snap_x(const lv_obj_t * obj);
+
+/**
+ * Get where to snap child Widgets when vertical scrolling ends.
+ * @param  obj      pointer to Widget
+ * @return          current snap value from `lv_scroll_snap_t`
+ */
+lv_scroll_snap_t lv_obj_get_scroll_snap_y(const lv_obj_t * obj);
+
+/**
+ * Get current X scroll position.  Identical to `lv_obj_get_scroll_left()`.
+ * @param obj       pointer to scrollable container Widget
+ * @return          current scroll position from left edge
+ *                      - If Widget is not scrolled return 0.
+ *                      - If scrolled return > 0.
+ *                      - If scrolled inside (elastic scroll) return < 0.
+ */
+int32_t lv_obj_get_scroll_x(const lv_obj_t * obj);
+
+/**
+ * Get current Y scroll position.  Identical to `lv_obj_get_scroll_top()`.
+ * @param obj       pointer to scrollable container Widget
+ * @return          current scroll position from top edge
+ *                      - If Widget is not scrolled return 0.
+ *                      - If scrolled return > 0.
+ *                      - If scrolled inside (elastic scroll) return < 0.
+ */
+int32_t lv_obj_get_scroll_y(const lv_obj_t * obj);
+
+/**
+ * Number of pixels a scrollable container Widget can be scrolled down
+ * before its top edge appears.  When LV_OBJ_FLAG_SCROLL_ELASTIC flag
+ * is set in Widget, this value can go negative while Widget is being
+ * dragged below its normal top-edge boundary.
+ * @param obj       pointer to scrollable container Widget
+ * @return          pixels Widget can be scrolled down before its top edge appears
+ */
+int32_t lv_obj_get_scroll_top(const lv_obj_t * obj);
+
+/**
+ * Number of pixels a scrollable container Widget can be scrolled up
+ * before its bottom edge appears.  When LV_OBJ_FLAG_SCROLL_ELASTIC flag
+ * is set in Widget, this value can go negative while Widget is being
+ * dragged above its normal bottom-edge boundary.
+ * @param obj       pointer to scrollable container Widget
+ * @return          pixels Widget can be scrolled up before its bottom edge appears
+ */
+int32_t lv_obj_get_scroll_bottom(const lv_obj_t * obj);
+
+/**
+ * Number of pixels a scrollable container Widget can be scrolled right
+ * before its left edge appears.  When LV_OBJ_FLAG_SCROLL_ELASTIC flag
+ * is set in Widget, this value can go negative while Widget is being
+ * dragged farther right than its normal left-edge boundary.
+ * @param obj       pointer to scrollable container Widget
+ * @return          pixels Widget can be scrolled right before its left edge appears
+ */
+int32_t lv_obj_get_scroll_left(const lv_obj_t * obj);
+
+/**
+ * Number of pixels a scrollable container Widget can be scrolled left
+ * before its right edge appears.  When LV_OBJ_FLAG_SCROLL_ELASTIC flag
+ * is set in Widget, this value can go negative while Widget is being
+ * dragged farther left than its normal right-edge boundary.
+ * @param obj       pointer to scrollable container Widget
+ * @return          pixels Widget can be scrolled left before its right edge appears
+ */
+int32_t lv_obj_get_scroll_right(const lv_obj_t * obj);
+
+/**
+ * Get the X and Y coordinates where the scrolling will end for Widget if a scrolling animation is in progress.
+ * If no scrolling animation, give the current `x` or `y` scroll position.
+ * @param obj       pointer to scrollable Widget
+ * @param end       pointer to `lv_point_t` object in which to store result
+ */
+void lv_obj_get_scroll_end(lv_obj_t * obj, lv_point_t * end);
+
+/*=====================
+ * Other functions
+ *====================*/
+
+/**
+ * Scroll by given amount of pixels.
+ * @param obj       pointer to scrollable Widget to scroll
+ * @param dx        pixels to scroll horizontally
+ * @param dy        pixels to scroll vertically
+ * @param anim_en   LV_ANIM_ON: scroll with animation; LV_ANIM_OFF: scroll immediately
+ * @note            > 0 value means scroll right/bottom (show the more content on the right/bottom)
+ * @note            e.g. dy = -20 means scroll down 20 px
+ */
+void lv_obj_scroll_by(lv_obj_t * obj, int32_t dx, int32_t dy, lv_anim_enable_t anim_en);
+
+/**
+ * Scroll by given amount of pixels.
+ * `dx` and `dy` will be limited internally to allow scrolling only on the content area.
+ * @param obj       pointer to scrollable Widget to scroll
+ * @param dx        pixels to scroll horizontally
+ * @param dy        pixels to scroll vertically
+ * @param anim_en   LV_ANIM_ON: scroll with animation; LV_ANIM_OFF: scroll immediately
+ * @note            e.g. dy = -20 means scroll down 20 px
+ */
+void lv_obj_scroll_by_bounded(lv_obj_t * obj, int32_t dx, int32_t dy, lv_anim_enable_t anim_en);
+
+/**
+ * Scroll to given coordinate on Widget.
+ * `x` and `y` will be limited internally to allow scrolling only on the content area.
+ * @param obj       pointer to scrollable Widget to scroll
+ * @param x         pixels to scroll horizontally
+ * @param y         pixels to scroll vertically
+ * @param anim_en   LV_ANIM_ON: scroll with animation; LV_ANIM_OFF: scroll immediately
+ */
+void lv_obj_scroll_to(lv_obj_t * obj, int32_t x, int32_t y, lv_anim_enable_t anim_en);
+
+/**
+ * Scroll to X coordinate on Widget.
+ * `x` will be limited internally to allow scrolling only on the content area.
+ * @param obj       pointer to scrollable Widget to scroll
+ * @param x         pixels to scroll horizontally
+ * @param anim_en   LV_ANIM_ON: scroll with animation; LV_ANIM_OFF: scroll immediately
+ */
+void lv_obj_scroll_to_x(lv_obj_t * obj, int32_t x, lv_anim_enable_t anim_en);
+
+/**
+ * Scroll to Y coordinate on Widget.
+ * `y` will be limited internally to allow scrolling only on the content area.
+ * @param obj       pointer to scrollable Widget to scroll
+ * @param y         pixels to scroll vertically
+ * @param anim_en   LV_ANIM_ON: scroll with animation; LV_ANIM_OFF: scroll immediately
+ */
+void lv_obj_scroll_to_y(lv_obj_t * obj, int32_t y, lv_anim_enable_t anim_en);
+
+/**
+ * Scroll `obj`'s parent Widget until `obj` becomes visible.
+ * @param obj       pointer to Widget to scroll into view
+ * @param anim_en   LV_ANIM_ON: scroll with animation; LV_ANIM_OFF: scroll immediately
+ */
+void lv_obj_scroll_to_view(lv_obj_t * obj, lv_anim_enable_t anim_en);
+
+/**
+ * Scroll `obj`'s parent Widgets recursively until `obj` becomes visible.
+ * Widget will be scrolled into view even it has nested scrollable parents.
+ * @param obj       pointer to Widget to scroll into view
+ * @param anim_en   LV_ANIM_ON: scroll with animation; LV_ANIM_OFF: scroll immediately
+ */
+void lv_obj_scroll_to_view_recursive(lv_obj_t * obj, lv_anim_enable_t anim_en);
+
+/**
+ * Tell whether Widget is being scrolled or not at this moment
+ * @param obj   pointer to Widget
+ * @return      true: `obj` is being scrolled
+ */
+bool lv_obj_is_scrolling(const lv_obj_t * obj);
+
+/**
+ * Stop scrolling the current object
+ *
+ * @param obj The object being scrolled
+ */
+void lv_obj_stop_scroll_anim(const lv_obj_t * obj);
+
+/**
+ * Check children of `obj` and scroll `obj` to fulfill scroll_snap settings.
+ * @param obj       Widget whose children need to be checked and snapped
+ * @param anim_en   LV_ANIM_ON/OFF
+ */
+void lv_obj_update_snap(lv_obj_t * obj, lv_anim_enable_t anim_en);
+
+/**
+ * Get the area of the scrollbars
+ * @param obj   pointer to Widget
+ * @param hor   pointer to store the area of the horizontal scrollbar
+ * @param ver   pointer to store the area of the vertical  scrollbar
+ */
+void lv_obj_get_scrollbar_area(lv_obj_t * obj, lv_area_t * hor, lv_area_t * ver);
+
+/**
+ * Invalidate the area of the scrollbars
+ * @param obj       pointer to Widget
+ */
+void lv_obj_scrollbar_invalidate(lv_obj_t * obj);
+
+/**
+ * Checks if the content is scrolled "in" and adjusts it to a normal position.
+ * @param obj       pointer to Widget
+ * @param anim_en   LV_ANIM_ON/OFF
+ */
+void lv_obj_readjust_scroll(lv_obj_t * obj, lv_anim_enable_t anim_en);
+
+/**********************
+ *      MACROS
+ **********************/
+
+#ifdef __cplusplus
+} /*extern "C"*/
+#endif
+
+#endif /*LV_OBJ_SCROLL_H*/

+ 50 - 0
components/easylvgl/lvgl9/src/core/lv_obj_scroll_private.h

@@ -0,0 +1,50 @@
+/**
+ * @file lv_obj_scroll_private.h
+ *
+ */
+
+#ifndef LV_OBJ_SCROLL_PRIVATE_H
+#define LV_OBJ_SCROLL_PRIVATE_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/*********************
+ *      INCLUDES
+ *********************/
+
+#include "lv_obj_scroll.h"
+
+/*********************
+ *      DEFINES
+ *********************/
+
+/**********************
+ *      TYPEDEFS
+ **********************/
+
+/**********************
+ * GLOBAL PROTOTYPES
+ **********************/
+
+/**
+ * Low level function to scroll by given x and y coordinates.
+ * `LV_EVENT_SCROLL` is sent.
+ * @param obj       pointer to an object to scroll
+ * @param x         pixels to scroll horizontally
+ * @param y         pixels to scroll vertically
+ * @return          `LV_RESULT_INVALID`: to object was deleted in `LV_EVENT_SCROLL`;
+ *                  `LV_RESULT_OK`: if the object is still valid
+ */
+lv_result_t lv_obj_scroll_by_raw(lv_obj_t * obj, int32_t x, int32_t y);
+
+/**********************
+ *      MACROS
+ **********************/
+
+#ifdef __cplusplus
+} /*extern "C"*/
+#endif
+
+#endif /*LV_OBJ_SCROLL_PRIVATE_H*/

+ 1219 - 0
components/easylvgl/lvgl9/src/core/lv_obj_style.c

@@ -0,0 +1,1219 @@
+/**
+ * @file lv_obj_style.c
+ *
+ */
+
+/*********************
+ *      INCLUDES
+ *********************/
+#include "lv_obj_private.h"
+#include "../misc/lv_anim_private.h"
+#include "lv_obj_style_private.h"
+#include "lv_obj_class_private.h"
+#include "../display/lv_display.h"
+#include "../display/lv_display_private.h"
+#include "../misc/lv_color.h"
+#include "../stdlib/lv_string.h"
+#include "../core/lv_global.h"
+/*********************
+ *      DEFINES
+ *********************/
+#define MY_CLASS (&lv_obj_class)
+#define style_refr LV_GLOBAL_DEFAULT()->style_refresh
+#define style_trans_ll_p &(LV_GLOBAL_DEFAULT()->style_trans_ll)
+#define _style_custom_prop_flag_lookup_table LV_GLOBAL_DEFAULT()->style_custom_prop_flag_lookup_table
+#define STYLE_PROP_SHIFTED(prop) ((uint32_t)1 << ((prop) >> 3))
+
+/**********************
+ *      TYPEDEFS
+ **********************/
+
+typedef struct {
+    lv_obj_t * obj;
+    lv_style_prop_t prop;
+    lv_style_selector_t selector;
+    lv_style_value_t start_value;
+    lv_style_value_t end_value;
+} trans_t;
+
+/**********************
+ *  GLOBAL PROTOTYPES
+ **********************/
+
+/**********************
+ *  STATIC PROTOTYPES
+ **********************/
+static lv_style_t * get_local_style(lv_obj_t * obj, lv_style_selector_t selector);
+static lv_obj_style_t * get_trans_style(lv_obj_t * obj, lv_style_selector_t selector);
+static lv_style_res_t get_prop_core(const lv_obj_t * obj, lv_style_selector_t selector, lv_style_prop_t prop,
+                                    lv_style_value_t * v);
+static void report_style_change_core(void * style, lv_obj_t * obj);
+static void refresh_children_style(lv_obj_t * obj);
+static bool trans_delete(lv_obj_t * obj, lv_part_t part, lv_style_prop_t prop, trans_t * tr_limit);
+static void trans_anim_cb(void * _tr, int32_t v);
+static void trans_anim_start_cb(lv_anim_t * a);
+static void trans_anim_completed_cb(lv_anim_t * a);
+static lv_layer_type_t calculate_layer_type(lv_obj_t * obj);
+static void full_cache_refresh(lv_obj_t * obj, lv_part_t part);
+static void fade_anim_cb(void * obj, int32_t v);
+static void fade_in_anim_completed(lv_anim_t * a);
+static bool style_has_flag(const lv_style_t * style, uint32_t flag);
+static lv_style_res_t get_selector_style_prop(const lv_obj_t * obj, lv_style_selector_t selector, lv_style_prop_t prop,
+                                              lv_style_value_t * value_act);
+
+/**********************
+ *  STATIC VARIABLES
+ **********************/
+
+/**********************
+ *      MACROS
+ **********************/
+
+/**********************
+ *   GLOBAL FUNCTIONS
+ **********************/
+
+void lv_obj_style_init(void)
+{
+    lv_ll_init(style_trans_ll_p, sizeof(trans_t));
+}
+
+void lv_obj_style_deinit(void)
+{
+    lv_ll_clear(style_trans_ll_p);
+    if(_style_custom_prop_flag_lookup_table != NULL) {
+        lv_free(_style_custom_prop_flag_lookup_table);
+        _style_custom_prop_flag_lookup_table = NULL;
+    }
+}
+
+void lv_obj_add_style(lv_obj_t * obj, const lv_style_t * style, lv_style_selector_t selector)
+{
+    LV_ASSERT(obj->style_cnt < 63);
+
+    trans_delete(obj, selector, LV_STYLE_PROP_ANY, NULL);
+
+    lv_part_t part = lv_obj_style_get_selector_part(selector);
+
+    if(style && part == LV_PART_MAIN && style_has_flag(style, LV_STYLE_PROP_FLAG_TRANSFORM)) {
+        lv_obj_invalidate(obj);
+    }
+
+    /*Try removing the style first to be sure it won't be added twice*/
+    lv_obj_remove_style(obj, style, selector);
+
+    uint32_t i;
+    /*Go after the transition and local styles*/
+    for(i = 0; i < obj->style_cnt; i++) {
+        if(obj->styles[i].is_trans) continue;
+        if(obj->styles[i].is_local) continue;
+        break;
+    }
+
+    /*Now `i` is at the first normal style. Insert the new style before this*/
+
+    /*Allocate space for the new style and shift the rest of the style to the end*/
+    obj->style_cnt++;
+    LV_ASSERT(obj->style_cnt != 0);
+    obj->styles = lv_realloc(obj->styles, obj->style_cnt * sizeof(lv_obj_style_t));
+    LV_ASSERT_MALLOC(obj->styles);
+
+    uint32_t j;
+    for(j = obj->style_cnt - 1; j > i ; j--) {
+        obj->styles[j] = obj->styles[j - 1];
+    }
+
+    lv_memzero(&obj->styles[i], sizeof(lv_obj_style_t));
+    obj->styles[i].style = style;
+    obj->styles[i].selector = selector;
+
+#if LV_OBJ_STYLE_CACHE
+    uint32_t * prop_is_set = part == LV_PART_MAIN ? &obj->style_main_prop_is_set : &obj->style_other_prop_is_set;
+    if(lv_style_is_const(style)) {
+        lv_style_const_prop_t * props = style->values_and_props;
+        for(i = 0; props[i].prop != LV_STYLE_PROP_INV; i++) {
+            (*prop_is_set) |= STYLE_PROP_SHIFTED(props[i].prop);
+        }
+    }
+    else {
+        lv_style_prop_t * props = (lv_style_prop_t *)style->values_and_props + style->prop_cnt * sizeof(lv_style_value_t);
+        for(i = 0; i < style->prop_cnt; i++) {
+            (*prop_is_set) |= STYLE_PROP_SHIFTED(props[i]);
+        }
+    }
+#endif
+
+    lv_obj_refresh_style(obj, selector, LV_STYLE_PROP_ANY);
+}
+
+bool lv_obj_replace_style(lv_obj_t * obj, const lv_style_t * old_style, const lv_style_t * new_style,
+                          lv_style_selector_t selector)
+{
+    lv_state_t state = lv_obj_style_get_selector_state(selector);
+    lv_part_t part = lv_obj_style_get_selector_part(selector);
+
+    /*All objects must exist*/
+    if(!obj || !old_style || !new_style || (old_style == new_style)) {
+        return false;
+    }
+
+    /*Similar to lv_obj_add_style, delete transition*/
+    trans_delete(obj, selector, LV_STYLE_PROP_ANY, NULL);
+
+    bool replaced = false;
+    uint32_t i;
+    for(i = 0; i < obj->style_cnt; i++) {
+        lv_state_t state_act = lv_obj_style_get_selector_state(obj->styles[i].selector);
+        lv_part_t part_act = lv_obj_style_get_selector_part(obj->styles[i].selector);
+
+        /*Skip local styles and transitions*/
+        if(obj->styles[i].is_local || obj->styles[i].is_trans) {
+            continue;
+        }
+
+        /*Skip non-matching styles*/
+        if((state != LV_STATE_ANY && state_act != state) ||
+           (part != LV_PART_ANY && part_act != part) ||
+           (old_style != obj->styles[i].style)) {
+            continue;
+        }
+
+        lv_memzero(&obj->styles[i], sizeof(lv_obj_style_t));
+        obj->styles[i].style = new_style;
+        obj->styles[i].selector = selector;
+
+        replaced = true;
+        /*Don't break and continue replacing other occurrences*/
+    }
+    if(replaced) {
+        full_cache_refresh(obj, part);
+        lv_obj_refresh_style(obj, part, LV_STYLE_PROP_ANY);
+    }
+    return replaced;
+}
+
+void lv_obj_remove_style(lv_obj_t * obj, const lv_style_t * style, lv_style_selector_t selector)
+{
+    lv_state_t state = lv_obj_style_get_selector_state(selector);
+    lv_part_t part = lv_obj_style_get_selector_part(selector);
+    lv_style_prop_t prop = LV_STYLE_PROP_ANY;
+    if(style && style->prop_cnt == 0) prop = LV_STYLE_PROP_INV;
+
+    if(style && part == LV_PART_MAIN && style_has_flag(style, LV_STYLE_PROP_FLAG_TRANSFORM)) {
+        lv_obj_invalidate(obj);
+    }
+
+    uint32_t i = 0;
+    bool deleted = false;
+    while(i <  obj->style_cnt) {
+        lv_state_t state_act = lv_obj_style_get_selector_state(obj->styles[i].selector);
+        lv_part_t part_act = lv_obj_style_get_selector_part(obj->styles[i].selector);
+        if((state != LV_STATE_ANY && state_act != state) ||
+           (part != LV_PART_ANY && part_act != part) ||
+           (style != NULL && style != obj->styles[i].style)) {
+            i++;
+            continue;
+        }
+
+        if(obj->styles[i].is_trans) {
+            trans_delete(obj, part, LV_STYLE_PROP_ANY, NULL);
+        }
+
+        if(obj->styles[i].is_local || obj->styles[i].is_trans) {
+            if(obj->styles[i].style) lv_style_reset((lv_style_t *)obj->styles[i].style);
+            lv_free((lv_style_t *)obj->styles[i].style);
+            obj->styles[i].style = NULL;
+        }
+
+        /*Shift the styles after `i` by one*/
+        uint32_t j;
+        for(j = i; j < (uint32_t)obj->style_cnt - 1 ; j++) {
+            obj->styles[j] = obj->styles[j + 1];
+        }
+
+        obj->style_cnt--;
+        obj->styles = lv_realloc(obj->styles, obj->style_cnt * sizeof(lv_obj_style_t));
+
+        deleted = true;
+        /*The style from the current `i` index is removed, so `i` points to the next style.
+         *Therefore it doesn't needs to be incremented*/
+    }
+
+    if(deleted && prop != LV_STYLE_PROP_INV) {
+        full_cache_refresh(obj, part);
+        lv_obj_refresh_style(obj, part, prop);
+    }
+}
+
+void lv_obj_remove_style_all(lv_obj_t * obj)
+{
+    lv_obj_remove_style(obj, NULL, LV_PART_ANY | LV_STATE_ANY);
+}
+
+void lv_obj_report_style_change(lv_style_t * style)
+{
+    if(!style_refr) return;
+    lv_display_t * d = lv_display_get_next(NULL);
+
+    while(d) {
+        uint32_t i;
+        for(i = 0; i < d->screen_cnt; i++) {
+            report_style_change_core(style, d->screens[i]);
+        }
+        d = lv_display_get_next(d);
+    }
+}
+
+void lv_obj_refresh_style(lv_obj_t * obj, lv_part_t part, lv_style_prop_t prop)
+{
+    LV_ASSERT_OBJ(obj, MY_CLASS);
+
+    if(!style_refr) return;
+
+    LV_PROFILER_STYLE_BEGIN;
+
+    lv_obj_invalidate(obj);
+
+    bool is_layout_refr = lv_style_prop_has_flag(prop, LV_STYLE_PROP_FLAG_LAYOUT_UPDATE);
+    bool is_ext_draw = lv_style_prop_has_flag(prop, LV_STYLE_PROP_FLAG_EXT_DRAW_UPDATE);
+    bool is_inheritable = lv_style_prop_has_flag(prop, LV_STYLE_PROP_FLAG_INHERITABLE);
+    bool is_layer_refr = lv_style_prop_has_flag(prop, LV_STYLE_PROP_FLAG_LAYER_UPDATE);
+
+    if(is_layout_refr) {
+        if(part == LV_PART_ANY ||
+           part == LV_PART_MAIN ||
+           lv_obj_get_style_height(obj, LV_PART_MAIN) == LV_SIZE_CONTENT ||
+           lv_obj_get_style_width(obj, LV_PART_MAIN) == LV_SIZE_CONTENT) {
+            lv_obj_send_event(obj, LV_EVENT_STYLE_CHANGED, NULL);
+            lv_obj_mark_layout_as_dirty(obj);
+        }
+    }
+    if((part == LV_PART_ANY || part == LV_PART_MAIN) && (prop == LV_STYLE_PROP_ANY || is_layout_refr)) {
+        lv_obj_t * parent = lv_obj_get_parent(obj);
+        if(parent) lv_obj_mark_layout_as_dirty(parent);
+    }
+
+    /*Cache the layer type*/
+    if((part == LV_PART_ANY || part == LV_PART_MAIN) && is_layer_refr) {
+        lv_obj_update_layer_type(obj);
+    }
+
+    if(prop == LV_STYLE_PROP_ANY || is_ext_draw) {
+        lv_obj_refresh_ext_draw_size(obj);
+    }
+    lv_obj_invalidate(obj);
+
+    if(prop == LV_STYLE_PROP_ANY || (is_inheritable && (is_ext_draw || is_layout_refr))) {
+        if(part != LV_PART_SCROLLBAR) {
+            refresh_children_style(obj);
+        }
+    }
+
+    LV_PROFILER_STYLE_END;
+}
+
+void lv_obj_style_set_disabled(lv_obj_t * obj, const lv_style_t * style, lv_style_selector_t selector, bool dis)
+{
+    uint32_t i;
+    for(i = 0; i < obj->style_cnt; i++) {
+        if(obj->styles[i].style == style && obj->styles[i].selector == selector) {
+            if(dis == obj->styles[i].is_disabled) {
+                return; /*Already in the right state*/
+            }
+            obj->styles[i].is_disabled = dis;
+            full_cache_refresh(obj, lv_obj_style_get_selector_part(selector));
+            lv_obj_refresh_style(obj, selector, LV_STYLE_PROP_ANY);
+            return;
+        }
+    }
+    LV_LOG_WARN("%p style was not found on %p widget with %6" LV_PRIx32 " selector", (void *)style, (void *)obj, selector);
+}
+
+bool lv_obj_style_get_disabled(lv_obj_t * obj, const lv_style_t * style, lv_style_selector_t selector)
+{
+    uint32_t i;
+    for(i = 0; i < obj->style_cnt; i++) {
+        if(obj->styles[i].style == style && obj->styles[i].selector == selector) {
+            return obj->styles[i].is_disabled;
+        }
+    }
+
+    LV_LOG_WARN("%p style was not found on %p widget with %6" LV_PRIx32 " selector", (void *)style, (void *)obj, selector);
+    return false;
+}
+
+
+void lv_obj_enable_style_refresh(bool en)
+{
+    style_refr = en;
+}
+
+lv_style_value_t lv_obj_get_style_prop(const lv_obj_t * obj, lv_part_t part, lv_style_prop_t prop)
+{
+    LV_ASSERT_NULL(obj)
+
+    lv_style_selector_t selector = part | obj->state;
+    lv_style_value_t value_act = { .ptr = NULL };
+    lv_style_res_t found;
+
+    found = get_selector_style_prop(obj, selector, prop, &value_act);
+    if(found == LV_STYLE_RES_FOUND) return value_act;
+
+    return lv_style_prop_get_default(prop);
+}
+
+bool lv_obj_has_style_prop(const lv_obj_t * obj, lv_style_selector_t selector, lv_style_prop_t prop)
+{
+    LV_ASSERT_NULL(obj)
+
+    lv_style_value_t value_act = { .ptr = NULL };
+    lv_style_res_t found;
+
+    found = get_selector_style_prop(obj, selector, prop, &value_act);
+    if(found == LV_STYLE_RES_FOUND) return true;
+
+    return false;
+}
+
+void lv_obj_set_local_style_prop(lv_obj_t * obj, lv_style_prop_t prop, lv_style_value_t value,
+                                 lv_style_selector_t selector)
+{
+    LV_PROFILER_STYLE_BEGIN;
+
+    /*Stop running transitions with this property */
+    trans_delete(obj, lv_obj_style_get_selector_part(selector), prop, NULL);
+
+    lv_style_t * style = get_local_style(obj, selector);
+    if(selector == LV_PART_MAIN && lv_style_prop_has_flag(prop, LV_STYLE_PROP_FLAG_TRANSFORM)) {
+        lv_obj_invalidate(obj);
+    }
+
+    lv_style_set_prop(style, prop, value);
+
+#if LV_OBJ_STYLE_CACHE
+    uint32_t prop_shifted = STYLE_PROP_SHIFTED(prop);
+    if(lv_obj_style_get_selector_part(selector) == LV_PART_MAIN) {
+        obj->style_main_prop_is_set |= prop_shifted;
+    }
+    else {
+        obj->style_other_prop_is_set |= prop_shifted;
+    }
+#endif
+
+    lv_obj_refresh_style(obj, selector, prop);
+    LV_PROFILER_STYLE_END;
+}
+
+lv_style_res_t lv_obj_get_local_style_prop(lv_obj_t * obj, lv_style_prop_t prop, lv_style_value_t * value,
+                                           lv_style_selector_t selector)
+{
+    uint32_t i;
+    for(i = 0; i < obj->style_cnt; i++) {
+        if(obj->styles[i].is_local &&
+           obj->styles[i].selector == selector) {
+            return lv_style_get_prop(obj->styles[i].style, prop, value);
+        }
+    }
+
+    return LV_STYLE_RES_NOT_FOUND;
+}
+
+bool lv_obj_remove_local_style_prop(lv_obj_t * obj, lv_style_prop_t prop, lv_style_selector_t selector)
+{
+    LV_ASSERT_OBJ(obj, MY_CLASS);
+
+    uint32_t i;
+    /*Find the style*/
+    for(i = 0; i < obj->style_cnt; i++) {
+        if(obj->styles[i].is_local &&
+           obj->styles[i].selector == selector) {
+            break;
+        }
+    }
+
+    /*The style is not found*/
+    if(i == obj->style_cnt) return false;
+
+    lv_result_t res = lv_style_remove_prop((lv_style_t *)obj->styles[i].style, prop);
+    if(res == LV_RESULT_OK) {
+        full_cache_refresh(obj, lv_obj_style_get_selector_part(selector));
+        lv_obj_refresh_style(obj, selector, prop);
+    }
+
+    return res;
+}
+
+void lv_obj_style_create_transition(lv_obj_t * obj, lv_part_t part, lv_state_t prev_state, lv_state_t new_state,
+                                    const lv_obj_style_transition_dsc_t * tr_dsc)
+{
+    trans_t * tr;
+
+    /*Get the previous and current values*/
+    obj->skip_trans = 1;
+    obj->state = prev_state;
+    lv_style_value_t v1 = lv_obj_get_style_prop(obj, part, tr_dsc->prop);
+    obj->state = new_state;
+    lv_style_value_t v2 = lv_obj_get_style_prop(obj, part, tr_dsc->prop);
+    obj->skip_trans = 0;
+
+    if(v1.ptr == v2.ptr && v1.num == v2.num && lv_color_eq(v1.color, v2.color))  return;
+    obj->state = prev_state;
+    v1 = lv_obj_get_style_prop(obj, part, tr_dsc->prop);
+    obj->state = new_state;
+
+    lv_obj_style_t * style_trans = get_trans_style(obj, part);
+    lv_style_set_prop((lv_style_t *)style_trans->style, tr_dsc->prop, v1);  /*Be sure `trans_style` has a valid value*/
+    lv_obj_refresh_style(obj, tr_dsc->selector, tr_dsc->prop);
+
+    if(tr_dsc->prop == LV_STYLE_RADIUS) {
+        if(v1.num == LV_RADIUS_CIRCLE || v2.num == LV_RADIUS_CIRCLE) {
+            int32_t whalf = lv_obj_get_width(obj) / 2;
+            int32_t hhalf = lv_obj_get_height(obj) / 2;
+            if(v1.num == LV_RADIUS_CIRCLE) v1.num = LV_MIN(whalf + 1, hhalf + 1);
+            if(v2.num == LV_RADIUS_CIRCLE) v2.num = LV_MIN(whalf + 1, hhalf + 1);
+        }
+    }
+
+    tr = lv_ll_ins_head(style_trans_ll_p);
+    LV_ASSERT_MALLOC(tr);
+    if(tr == NULL) return;
+    tr->start_value = v1;
+    tr->end_value = v2;
+    tr->obj = obj;
+    tr->prop = tr_dsc->prop;
+    tr->selector = part;
+
+    lv_anim_t a;
+    lv_anim_init(&a);
+    lv_anim_set_var(&a, tr);
+    lv_anim_set_exec_cb(&a, trans_anim_cb);
+    lv_anim_set_start_cb(&a, trans_anim_start_cb);
+    lv_anim_set_completed_cb(&a, trans_anim_completed_cb);
+    lv_anim_set_values(&a, 0x00, 0xFF);
+    lv_anim_set_duration(&a, tr_dsc->time);
+    lv_anim_set_delay(&a, tr_dsc->delay);
+    lv_anim_set_path_cb(&a, tr_dsc->path_cb);
+    lv_anim_set_early_apply(&a, false);
+    lv_anim_set_user_data(&a, tr_dsc->user_data);
+    lv_anim_start(&a);
+}
+
+lv_style_value_t lv_obj_style_apply_color_filter(const lv_obj_t * obj, lv_part_t part, lv_style_value_t v)
+{
+#if LV_USE_COLOR_FILTER
+    if(obj == NULL) return v;
+    const lv_color_filter_dsc_t * f = lv_obj_get_style_color_filter_dsc(obj, part);
+    if(f && f->filter_cb) {
+        lv_opa_t f_opa = lv_obj_get_style_color_filter_opa(obj, part);
+        if(f_opa != 0) v.color = f->filter_cb(f, v.color, f_opa);
+    }
+#else
+    LV_UNUSED(obj);
+    LV_UNUSED(part);
+    LV_UNUSED(v);
+#endif
+    return v;
+}
+
+lv_style_state_cmp_t lv_obj_style_state_compare(lv_obj_t * obj, lv_state_t state1, lv_state_t state2)
+{
+    lv_style_state_cmp_t res = LV_STYLE_STATE_CMP_SAME;
+
+    /*Are there any new styles for the new state?*/
+    uint32_t i;
+    for(i = 0; i < obj->style_cnt; i++) {
+        if(obj->styles[i].is_trans) continue;
+
+        lv_state_t state_act = lv_obj_style_get_selector_state(obj->styles[i].selector);
+        /*The style is valid for a state but not the other*/
+        bool valid1 = state_act & (~state1) ? false : true;
+        bool valid2 = state_act & (~state2) ? false : true;
+        if(valid1 != valid2) {
+            const lv_style_t * style = obj->styles[i].style;
+            lv_style_value_t v;
+            /*If there is layout difference on the main part, return immediately. There is no more serious difference*/
+            bool layout_diff = false;
+            if(lv_style_get_prop(style, LV_STYLE_PAD_TOP, &v))layout_diff = true;
+            else if(lv_style_get_prop(style, LV_STYLE_PAD_BOTTOM, &v)) layout_diff = true;
+            else if(lv_style_get_prop(style, LV_STYLE_PAD_LEFT, &v)) layout_diff = true;
+            else if(lv_style_get_prop(style, LV_STYLE_PAD_RIGHT, &v)) layout_diff = true;
+            else if(lv_style_get_prop(style, LV_STYLE_PAD_COLUMN, &v)) layout_diff = true;
+            else if(lv_style_get_prop(style, LV_STYLE_PAD_ROW, &v)) layout_diff = true;
+            else if(lv_style_get_prop(style, LV_STYLE_LAYOUT, &v)) layout_diff = true;
+            else if(lv_style_get_prop(style, LV_STYLE_TRANSLATE_X, &v)) layout_diff = true;
+            else if(lv_style_get_prop(style, LV_STYLE_TRANSLATE_Y, &v)) layout_diff = true;
+            else if(lv_style_get_prop(style, LV_STYLE_WIDTH, &v)) layout_diff = true;
+            else if(lv_style_get_prop(style, LV_STYLE_HEIGHT, &v)) layout_diff = true;
+            else if(lv_style_get_prop(style, LV_STYLE_MIN_WIDTH, &v)) layout_diff = true;
+            else if(lv_style_get_prop(style, LV_STYLE_MAX_WIDTH, &v)) layout_diff = true;
+            else if(lv_style_get_prop(style, LV_STYLE_MIN_HEIGHT, &v)) layout_diff = true;
+            else if(lv_style_get_prop(style, LV_STYLE_MAX_HEIGHT, &v)) layout_diff = true;
+            else if(lv_style_get_prop(style, LV_STYLE_BORDER_WIDTH, &v)) layout_diff = true;
+
+            if(layout_diff) {
+                return LV_STYLE_STATE_CMP_DIFF_LAYOUT;
+            }
+
+            /*Check for draw pad changes*/
+            if(lv_style_get_prop(style, LV_STYLE_TRANSFORM_WIDTH, &v)) res = LV_STYLE_STATE_CMP_DIFF_DRAW_PAD;
+            else if(lv_style_get_prop(style, LV_STYLE_TRANSFORM_HEIGHT, &v)) res = LV_STYLE_STATE_CMP_DIFF_DRAW_PAD;
+            else if(lv_style_get_prop(style, LV_STYLE_TRANSFORM_ROTATION, &v)) res = LV_STYLE_STATE_CMP_DIFF_DRAW_PAD;
+            else if(lv_style_get_prop(style, LV_STYLE_TRANSFORM_SCALE_X, &v)) res = LV_STYLE_STATE_CMP_DIFF_DRAW_PAD;
+            else if(lv_style_get_prop(style, LV_STYLE_TRANSFORM_SCALE_Y, &v)) res = LV_STYLE_STATE_CMP_DIFF_DRAW_PAD;
+            else if(lv_style_get_prop(style, LV_STYLE_OUTLINE_OPA, &v)) res = LV_STYLE_STATE_CMP_DIFF_DRAW_PAD;
+            else if(lv_style_get_prop(style, LV_STYLE_OUTLINE_PAD, &v)) res = LV_STYLE_STATE_CMP_DIFF_DRAW_PAD;
+            else if(lv_style_get_prop(style, LV_STYLE_OUTLINE_WIDTH, &v)) res = LV_STYLE_STATE_CMP_DIFF_DRAW_PAD;
+            else if(lv_style_get_prop(style, LV_STYLE_SHADOW_WIDTH, &v)) res = LV_STYLE_STATE_CMP_DIFF_DRAW_PAD;
+            else if(lv_style_get_prop(style, LV_STYLE_SHADOW_OPA, &v)) res = LV_STYLE_STATE_CMP_DIFF_DRAW_PAD;
+            else if(lv_style_get_prop(style, LV_STYLE_SHADOW_OFFSET_X, &v)) res = LV_STYLE_STATE_CMP_DIFF_DRAW_PAD;
+            else if(lv_style_get_prop(style, LV_STYLE_SHADOW_OFFSET_Y, &v)) res = LV_STYLE_STATE_CMP_DIFF_DRAW_PAD;
+            else if(lv_style_get_prop(style, LV_STYLE_SHADOW_SPREAD, &v)) res = LV_STYLE_STATE_CMP_DIFF_DRAW_PAD;
+            else if(lv_style_get_prop(style, LV_STYLE_LINE_WIDTH, &v)) res = LV_STYLE_STATE_CMP_DIFF_DRAW_PAD;
+            else if(res == LV_STYLE_STATE_CMP_SAME) res = LV_STYLE_STATE_CMP_DIFF_REDRAW;
+        }
+    }
+
+    return res;
+}
+
+void lv_obj_fade_in(lv_obj_t * obj, uint32_t time, uint32_t delay)
+{
+    lv_anim_t a;
+    lv_anim_init(&a);
+    lv_anim_set_var(&a, obj);
+    lv_anim_set_values(&a, 0, LV_OPA_COVER);
+    lv_anim_set_exec_cb(&a, fade_anim_cb);
+    lv_anim_set_completed_cb(&a, fade_in_anim_completed);
+    lv_anim_set_duration(&a, time);
+    lv_anim_set_delay(&a, delay);
+    lv_anim_start(&a);
+}
+
+void lv_obj_fade_out(lv_obj_t * obj, uint32_t time, uint32_t delay)
+{
+    lv_anim_t a;
+    lv_anim_init(&a);
+    lv_anim_set_var(&a, obj);
+    lv_anim_set_values(&a, lv_obj_get_style_opa(obj, LV_PART_MAIN), LV_OPA_TRANSP);
+    lv_anim_set_exec_cb(&a, fade_anim_cb);
+    lv_anim_set_duration(&a, time);
+    lv_anim_set_delay(&a, delay);
+    lv_anim_start(&a);
+}
+
+lv_text_align_t lv_obj_calculate_style_text_align(const lv_obj_t * obj, lv_part_t part, const char * txt)
+{
+    lv_text_align_t align = lv_obj_get_style_text_align(obj, part);
+    lv_base_dir_t base_dir = lv_obj_get_style_base_dir(obj, part);
+    lv_bidi_calculate_align(&align, &base_dir, txt);
+    return align;
+}
+
+lv_opa_t lv_obj_get_style_opa_recursive(const lv_obj_t * obj, lv_part_t part)
+{
+    LV_PROFILER_STYLE_BEGIN;
+    lv_opa_t opa_obj = lv_obj_get_style_opa(obj, part);
+    if(opa_obj <= LV_OPA_MIN) {
+        LV_PROFILER_STYLE_END;
+        return LV_OPA_TRANSP;
+    }
+
+    lv_opa_t opa_final = LV_OPA_COVER;
+    if(opa_obj < LV_OPA_MAX) {
+        opa_final = LV_OPA_MIX2(opa_final, opa_obj);
+    }
+
+    if(part != LV_PART_MAIN) {
+        part = LV_PART_MAIN;
+    }
+    else {
+        obj = lv_obj_get_parent(obj);
+    }
+
+    while(obj) {
+        opa_obj = lv_obj_get_style_opa(obj, part);
+        if(opa_obj <= LV_OPA_MIN) {
+            LV_PROFILER_STYLE_END;
+            return LV_OPA_TRANSP;
+        }
+        if(opa_obj < LV_OPA_MAX) {
+            opa_final = LV_OPA_MIX2(opa_final, opa_obj);
+        }
+
+        obj = lv_obj_get_parent(obj);
+    }
+
+    if(opa_final <= LV_OPA_MIN) {
+        LV_PROFILER_STYLE_END;
+        return LV_OPA_TRANSP;
+    }
+
+    if(opa_final >= LV_OPA_MAX) {
+        LV_PROFILER_STYLE_END;
+        return LV_OPA_COVER;
+    }
+
+    LV_PROFILER_STYLE_END;
+    return opa_final;
+}
+
+void lv_obj_update_layer_type(lv_obj_t * obj)
+{
+    lv_layer_type_t layer_type = calculate_layer_type(obj);
+    if(obj->spec_attr) obj->spec_attr->layer_type = layer_type;
+    else if(layer_type != LV_LAYER_TYPE_NONE) {
+        lv_obj_allocate_spec_attr(obj);
+        obj->spec_attr->layer_type = layer_type;
+    }
+}
+
+lv_color32_t lv_obj_style_apply_recolor(const lv_obj_t * obj, lv_part_t part, lv_color32_t color)
+{
+    lv_opa_t opa = lv_obj_get_style_recolor_opa(obj, part);
+    if(opa > LV_OPA_TRANSP) {
+        lv_color_t recolor = lv_obj_get_style_recolor(obj, part);
+        color = lv_color_over32(color, lv_color_to_32(recolor, opa));
+    }
+
+    return color;
+}
+
+lv_color32_t lv_obj_get_style_recolor_recursive(const lv_obj_t * obj, lv_part_t part)
+{
+    lv_color32_t result;
+
+    lv_color_t color = lv_obj_get_style_recolor(obj, part);
+    lv_opa_t opa = lv_obj_get_style_recolor_opa(obj, part);
+
+    result = lv_color_to_32(color, opa);
+
+    if(part != LV_PART_MAIN) {
+        part = LV_PART_MAIN;
+    }
+    else {
+        obj = lv_obj_get_parent(obj);
+    }
+
+    while(obj) {
+        result = lv_obj_style_apply_recolor(obj, part, result);
+        obj = lv_obj_get_parent(obj);
+    }
+
+    return result;
+}
+
+/**********************
+ *   STATIC FUNCTIONS
+ **********************/
+
+/**
+ * Get the local style of an object for a given part and for a given state.
+ * If the local style for the part-state pair doesn't exist allocate and return it.
+ * @param obj pointer to an object
+ * @param selector OR-ed value of parts and state for which the style should be get
+ * @return pointer to the local style
+ */
+static lv_style_t * get_local_style(lv_obj_t * obj, lv_style_selector_t selector)
+{
+    uint32_t i;
+    for(i = 0; i < obj->style_cnt; i++) {
+        if(obj->styles[i].is_local &&
+           obj->styles[i].selector == selector) {
+            return (lv_style_t *)obj->styles[i].style;
+        }
+    }
+
+    obj->style_cnt++;
+    LV_ASSERT(obj->style_cnt != 0);
+    obj->styles = lv_realloc(obj->styles, obj->style_cnt * sizeof(lv_obj_style_t));
+    LV_ASSERT_MALLOC(obj->styles);
+
+    for(i = obj->style_cnt - 1; i > 0 ; i--) {
+        /*Copy only normal styles (not local and transition).
+         *The new local style will be added as the last local style*/
+        if(obj->styles[i - 1].is_local || obj->styles[i - 1].is_trans) break;
+        obj->styles[i] = obj->styles[i - 1];
+    }
+
+    lv_memzero(&obj->styles[i], sizeof(lv_obj_style_t));
+    obj->styles[i].style = lv_malloc_zeroed(sizeof(lv_style_t));
+    lv_style_init((lv_style_t *)obj->styles[i].style);
+
+    obj->styles[i].is_local = 1;
+    obj->styles[i].selector = selector;
+    return (lv_style_t *)obj->styles[i].style;
+}
+
+/**
+ * Get the transition style of an object for a given part and for a given state.
+ * If the transition style for the part-state pair doesn't exist allocate and return it.
+ * @param obj   pointer to an object
+ * @param selector OR-ed value of parts and state for which the style should be get
+ * @return pointer to the transition style
+ */
+static lv_obj_style_t * get_trans_style(lv_obj_t * obj,  lv_style_selector_t selector)
+{
+    uint32_t i;
+    for(i = 0; i < obj->style_cnt; i++) {
+        if(obj->styles[i].is_trans && obj->styles[i].selector == selector) break;
+    }
+
+    /*Already have a transition style for it*/
+    if(i != obj->style_cnt) return &obj->styles[i];
+
+    obj->style_cnt++;
+    LV_ASSERT(obj->style_cnt != 0);
+    obj->styles = lv_realloc(obj->styles, obj->style_cnt * sizeof(lv_obj_style_t));
+
+    for(i = obj->style_cnt - 1; i > 0 ; i--) {
+        obj->styles[i] = obj->styles[i - 1];
+    }
+
+    lv_memzero(&obj->styles[0], sizeof(lv_obj_style_t));
+    obj->styles[0].style = lv_malloc(sizeof(lv_style_t));
+    lv_style_init((lv_style_t *)obj->styles[0].style);
+
+    obj->styles[0].is_trans = 1;
+    obj->styles[0].selector = selector;
+    return &obj->styles[0];
+}
+
+static lv_style_res_t get_prop_core(const lv_obj_t * obj, lv_style_selector_t selector, lv_style_prop_t prop,
+                                    lv_style_value_t * v)
+{
+
+    const uint32_t group = (uint32_t)1 << lv_style_get_prop_group(prop);
+    const lv_part_t part = lv_obj_style_get_selector_part(selector);
+    const lv_state_t state = lv_obj_style_get_selector_state(selector);
+    const lv_state_t state_inv = ~state;
+    const bool skip_trans = (const bool) obj->skip_trans;
+    int32_t weight = -1;
+    lv_style_res_t found;
+    uint32_t i;
+    for(i = 0; i < obj->style_cnt; i++) {
+        lv_obj_style_t * obj_style = &obj->styles[i];
+        if(obj_style->is_trans == false) break;
+        if(skip_trans) continue;
+        if(obj_style->is_disabled) continue;
+
+        lv_part_t part_act = lv_obj_style_get_selector_part(obj->styles[i].selector);
+
+        if(part_act != part) continue;
+        if((obj_style->style->has_group & group) == 0) continue;
+        found = lv_style_get_prop_inlined(obj_style->style, prop, v);
+        if(found == LV_STYLE_RES_FOUND) {
+            return LV_STYLE_RES_FOUND;
+        }
+    }
+
+    for(; i < obj->style_cnt; i++) {
+        if((obj->styles[i].style->has_group & group) == 0) continue;
+        if(obj->styles[i].is_disabled) continue;
+        lv_obj_style_t * obj_style = &obj->styles[i];
+        lv_part_t part_act = lv_obj_style_get_selector_part(obj->styles[i].selector);
+        if(part_act != part) continue;
+
+        /*Be sure the style not specifies other state than the requested.
+         *E.g. For HOVER+PRESS object state, HOVER style only is OK, but HOVER+FOCUS style is not*/
+        lv_state_t state_act = lv_obj_style_get_selector_state(obj->styles[i].selector);
+        if((state_act & state_inv)) continue;
+
+        /*Check only better candidates*/
+        if((int32_t)state_act <= weight) continue;
+
+        found = lv_style_get_prop_inlined(obj_style->style, prop, v);
+        if(found == LV_STYLE_RES_FOUND) {
+            if(state_act == state) {
+                return LV_STYLE_RES_FOUND;
+            }
+            weight = state_act;
+        }
+    }
+
+    if(weight >= 0) return LV_STYLE_RES_FOUND;
+    else return LV_STYLE_RES_NOT_FOUND;
+}
+
+/**
+ * Refresh the style of all children of an object. (Called recursively)
+ * @param style refresh objects only with this
+ * @param obj pointer to an object
+ */
+static void report_style_change_core(void * style, lv_obj_t * obj)
+{
+    uint32_t i;
+    for(i = 0; i < obj->style_cnt; i++) {
+        if(style == NULL || obj->styles[i].style == style) {
+            full_cache_refresh(obj, lv_obj_style_get_selector_part(obj->styles[i].selector));
+            lv_obj_refresh_style(obj, LV_PART_ANY, LV_STYLE_PROP_ANY);
+            break;
+        }
+    }
+
+    uint32_t child_cnt = lv_obj_get_child_count(obj);
+    for(i = 0; i < child_cnt; i++) {
+        report_style_change_core(style, obj->spec_attr->children[i]);
+    }
+}
+
+/**
+ * Recursively refresh the style of the children. Go deeper until a not NULL style is found
+ * because the NULL styles are inherited from the parent
+ * @param obj pointer to an object
+ */
+static void refresh_children_style(lv_obj_t * obj)
+{
+    uint32_t i;
+    uint32_t child_cnt = lv_obj_get_child_count(obj);
+    for(i = 0; i < child_cnt; i++) {
+        lv_obj_t * child = obj->spec_attr->children[i];
+        lv_obj_invalidate(child);
+        lv_obj_send_event(child, LV_EVENT_STYLE_CHANGED, NULL);
+        lv_obj_invalidate(child);
+
+        refresh_children_style(child); /*Check children too*/
+    }
+}
+
+/**
+ * Remove the transition from object's part's property.
+ * - Remove the transition from `lv_obj_style_trans_ll` and free it
+ * - Delete pending transitions
+ * @param obj pointer to an object which transition(s) should be removed
+ * @param part a part of object or 0xFF to remove from all parts
+ * @param prop a property or 0xFF to remove all properties
+ * @param tr_limit delete transitions only "older" than this. `NULL` if not used
+ */
+static bool trans_delete(lv_obj_t * obj, lv_part_t part, lv_style_prop_t prop, trans_t * tr_limit)
+{
+    trans_t * tr;
+    trans_t * tr_prev;
+    bool removed = false;
+    tr = lv_ll_get_tail(style_trans_ll_p);
+    while(tr != NULL) {
+        if(tr == tr_limit) break;
+
+        /*'tr' might be deleted, so get the next object while 'tr' is valid*/
+        tr_prev = lv_ll_get_prev(style_trans_ll_p, tr);
+
+        if(tr->obj == obj && (part == tr->selector || part == LV_PART_ANY) && (prop == tr->prop || prop == LV_STYLE_PROP_ANY)) {
+            /*Remove any transitioned properties from the trans. style
+             *to allow changing it by normal styles*/
+            uint32_t i;
+            for(i = 0; i < obj->style_cnt; i++) {
+                if(obj->styles[i].is_trans && (part == LV_PART_ANY || obj->styles[i].selector == part)) {
+                    lv_style_remove_prop((lv_style_t *)obj->styles[i].style, tr->prop);
+                }
+            }
+
+            /*Free the transition descriptor too*/
+            lv_anim_delete(tr, NULL);
+            lv_ll_remove(style_trans_ll_p, tr);
+            lv_free(tr);
+            removed = true;
+
+        }
+        tr = tr_prev;
+    }
+    return removed;
+}
+
+static void trans_anim_cb(void * _tr, int32_t v)
+{
+    trans_t * tr = _tr;
+    lv_obj_t * obj = tr->obj;
+
+    uint32_t i;
+    for(i = 0; i < obj->style_cnt; i++) {
+        if(obj->styles[i].is_trans == 0 || obj->styles[i].selector != tr->selector) continue;
+
+        lv_style_value_t value_final = {0};
+        switch(tr->prop) {
+
+            case LV_STYLE_BORDER_SIDE:
+            case LV_STYLE_BORDER_POST:
+            case LV_STYLE_BLEND_MODE:
+                if(v < 255) value_final.num = tr->start_value.num;
+                else value_final.num = tr->end_value.num;
+                break;
+            case LV_STYLE_TRANSITION:
+            case LV_STYLE_TEXT_FONT:
+                if(v < 255) value_final.ptr = tr->start_value.ptr;
+                else value_final.ptr = tr->end_value.ptr;
+                break;
+            case LV_STYLE_COLOR_FILTER_DSC:
+                if(tr->start_value.ptr == NULL) value_final.ptr = tr->end_value.ptr;
+                else if(tr->end_value.ptr == NULL) value_final.ptr = tr->start_value.ptr;
+                else if(v < 128) value_final.ptr = tr->start_value.ptr;
+                else value_final.ptr = tr->end_value.ptr;
+                break;
+            case LV_STYLE_RECOLOR:
+            case LV_STYLE_BG_COLOR:
+            case LV_STYLE_BG_GRAD_COLOR:
+            case LV_STYLE_BORDER_COLOR:
+            case LV_STYLE_TEXT_COLOR:
+            case LV_STYLE_SHADOW_COLOR:
+            case LV_STYLE_OUTLINE_COLOR:
+            case LV_STYLE_IMAGE_RECOLOR:
+                if(v <= 0) value_final.color = tr->start_value.color;
+                else if(v >= 255) value_final.color = tr->end_value.color;
+                else value_final.color = lv_color_mix(tr->end_value.color, tr->start_value.color, v);
+                break;
+
+            default:
+                if(v == 0) value_final.num = tr->start_value.num;
+                else if(v == 255) value_final.num = tr->end_value.num;
+                else value_final.num = tr->start_value.num + ((int32_t)((int32_t)(tr->end_value.num - tr->start_value.num) * v) >> 8);
+                break;
+        }
+
+        lv_style_value_t old_value = {0};
+        bool refr = true;
+        if(lv_style_get_prop(obj->styles[i].style, tr->prop, &old_value)) {
+            if(value_final.ptr == old_value.ptr && lv_color_eq(value_final.color, old_value.color) &&
+               value_final.num == old_value.num) {
+                refr = false;
+            }
+        }
+        lv_style_set_prop((lv_style_t *)obj->styles[i].style, tr->prop, value_final);
+        if(refr) lv_obj_refresh_style(tr->obj, tr->selector, tr->prop);
+        break;
+
+    }
+
+}
+
+static void trans_anim_start_cb(lv_anim_t * a)
+{
+    trans_t * tr = a->var;
+
+    lv_part_t part = lv_obj_style_get_selector_part(tr->selector);
+    tr->start_value = lv_obj_get_style_prop(tr->obj, part, tr->prop);
+
+    /*Init prop to an invalid values to be sure `trans_del` won't delete this added `tr`*/
+    lv_style_prop_t prop_tmp = tr->prop;
+    tr->prop = LV_STYLE_PROP_INV;
+
+    /*Delete the related transitions if any*/
+    trans_delete(tr->obj, part, prop_tmp, tr);
+
+    tr->prop = prop_tmp;
+
+    lv_obj_style_t * style_trans = get_trans_style(tr->obj, tr->selector);
+    /*Be sure `trans_style` has a valid value*/
+    lv_style_set_prop((lv_style_t *)style_trans->style, tr->prop, tr->start_value);
+    lv_obj_refresh_style(tr->obj, tr->selector, tr->prop);
+
+}
+
+static void trans_anim_completed_cb(lv_anim_t * a)
+{
+    trans_t * tr = a->var;
+    lv_obj_t * obj = tr->obj;
+    lv_style_prop_t prop = tr->prop;
+
+    /*Remove the transitioned property from trans. style
+     *if there no more transitions for this property
+     *It allows changing it by normal styles*/
+    bool running = false;
+    trans_t * tr_i;
+    LV_LL_READ(style_trans_ll_p, tr_i) {
+        if(tr_i != tr && tr_i->obj == tr->obj && tr_i->selector == tr->selector && tr_i->prop == tr->prop) {
+            running = true;
+            break;
+        }
+    }
+
+    if(!running) {
+        uint32_t i;
+        for(i = 0; i < obj->style_cnt; i++) {
+            if(obj->styles[i].is_trans && obj->styles[i].selector == tr->selector) {
+                lv_ll_remove(style_trans_ll_p, tr);
+                lv_free(tr);
+
+                lv_obj_style_t * obj_style = &obj->styles[i];
+                lv_style_remove_prop((lv_style_t *)obj_style->style, prop);
+
+                if(lv_style_is_empty(obj->styles[i].style)) {
+                    lv_obj_remove_style(obj, (lv_style_t *)obj_style->style, obj_style->selector);
+
+                }
+                break;
+            }
+        }
+    }
+}
+
+static lv_layer_type_t calculate_layer_type(lv_obj_t * obj)
+{
+#if LV_DRAW_TRANSFORM_USE_MATRIX
+    if(lv_obj_get_transform(obj) != NULL) return LV_LAYER_TYPE_TRANSFORM;
+#endif
+    if(lv_obj_get_style_transform_rotation(obj, LV_PART_MAIN) != 0) return LV_LAYER_TYPE_TRANSFORM;
+    if(lv_obj_get_style_transform_scale_x(obj, LV_PART_MAIN) != 256) return LV_LAYER_TYPE_TRANSFORM;
+    if(lv_obj_get_style_transform_scale_y(obj, LV_PART_MAIN) != 256) return LV_LAYER_TYPE_TRANSFORM;
+    if(lv_obj_get_style_transform_skew_x(obj, LV_PART_MAIN) != 0) return LV_LAYER_TYPE_TRANSFORM;
+    if(lv_obj_get_style_transform_skew_y(obj, LV_PART_MAIN) != 0) return LV_LAYER_TYPE_TRANSFORM;
+    if(lv_obj_get_style_opa_layered(obj, LV_PART_MAIN) != LV_OPA_COVER) return LV_LAYER_TYPE_SIMPLE;
+    if(lv_obj_get_style_bitmap_mask_src(obj, LV_PART_MAIN) != NULL) return LV_LAYER_TYPE_SIMPLE;
+    if(lv_obj_get_style_blend_mode(obj, LV_PART_MAIN) != LV_BLEND_MODE_NORMAL) return LV_LAYER_TYPE_SIMPLE;
+    return LV_LAYER_TYPE_NONE;
+}
+
+static void full_cache_refresh(lv_obj_t * obj, lv_part_t part)
+{
+#if LV_OBJ_STYLE_CACHE
+    uint32_t i;
+    if(part == LV_PART_MAIN || part == LV_PART_ANY) {
+        obj->style_main_prop_is_set = 0;
+        for(i = 0; i < obj->style_cnt; i++) {
+            if(lv_obj_style_get_selector_part(obj->styles[i].selector) != LV_PART_MAIN) continue;
+            if(obj->styles[i].is_disabled) continue;
+            lv_style_t * style = (lv_style_t *)obj->styles[i].style;
+            uint32_t j;
+            if(lv_style_is_const(style)) {
+                lv_style_const_prop_t * props = style->values_and_props;
+                for(j = 0; props[j].prop != LV_STYLE_PROP_INV; j++) {
+                    obj->style_main_prop_is_set |= STYLE_PROP_SHIFTED(props[j].prop);
+                }
+            }
+            else {
+                lv_style_prop_t * props = (lv_style_prop_t *)style->values_and_props + style->prop_cnt * sizeof(lv_style_value_t);
+                for(j = 0; j < style->prop_cnt; j++) {
+                    obj->style_main_prop_is_set |= STYLE_PROP_SHIFTED(props[j]);
+                }
+            }
+        }
+    }
+    if(part != LV_PART_MAIN || part == LV_PART_ANY) {
+        obj->style_other_prop_is_set = 0;
+        for(i = 0; i < obj->style_cnt; i++) {
+            if(lv_obj_style_get_selector_part(obj->styles[i].selector) == LV_PART_MAIN) continue;
+            if(obj->styles[i].is_disabled) continue;
+
+            lv_style_t * style = (lv_style_t *)obj->styles[i].style;
+            uint32_t j;
+            if(lv_style_is_const(style)) {
+                lv_style_const_prop_t * props = style->values_and_props;
+                for(j = 0; props[j].prop != LV_STYLE_PROP_INV; j++) {
+                    obj->style_other_prop_is_set |= STYLE_PROP_SHIFTED(props[j].prop);
+                }
+            }
+            else {
+                lv_style_prop_t * props = (lv_style_prop_t *)style->values_and_props + style->prop_cnt * sizeof(lv_style_value_t);
+                for(j = 0; j < style->prop_cnt; j++) {
+                    obj->style_other_prop_is_set |= STYLE_PROP_SHIFTED(props[j]);
+                }
+            }
+        }
+    }
+#else
+    LV_UNUSED(obj);
+    LV_UNUSED(part);
+#endif
+}
+
+static void fade_anim_cb(void * obj, int32_t v)
+{
+    lv_obj_set_style_opa(obj, v, 0);
+}
+
+static void fade_in_anim_completed(lv_anim_t * a)
+{
+    lv_obj_remove_local_style_prop(a->var, LV_STYLE_OPA, 0);
+}
+
+static bool style_has_flag(const lv_style_t * style, uint32_t flag)
+{
+    if(lv_style_is_const(style)) {
+        lv_style_const_prop_t * props = style->values_and_props;
+        uint32_t i;
+        for(i = 0; props[i].prop != LV_STYLE_PROP_INV; i++) {
+            if(lv_style_prop_has_flag(props[i].prop, flag)) {
+                return true;
+            }
+        }
+    }
+    else {
+        lv_style_prop_t * props = (lv_style_prop_t *)style->values_and_props + style->prop_cnt * sizeof(lv_style_value_t);
+        uint32_t i;
+        for(i = 0; i < style->prop_cnt; i++) {
+            if(lv_style_prop_has_flag(props[i], flag)) {
+                return true;
+            }
+        }
+    }
+    return false;
+}
+
+static lv_style_res_t get_selector_style_prop(const lv_obj_t * obj, lv_style_selector_t selector, lv_style_prop_t prop,
+                                              lv_style_value_t * value_act)
+{
+    lv_style_res_t found;
+    lv_part_t part = lv_obj_style_get_selector_part(selector);
+
+    /*The happy path*/
+#if LV_OBJ_STYLE_CACHE
+    const uint32_t prop_shifted = STYLE_PROP_SHIFTED(prop);
+    if((part == LV_PART_MAIN ? obj->style_main_prop_is_set : obj->style_other_prop_is_set) & prop_shifted)
+#endif
+    {
+        found = get_prop_core(obj, selector, prop, value_act);
+        if(found == LV_STYLE_RES_FOUND) return LV_STYLE_RES_FOUND;
+    }
+
+    extern const uint8_t lv_style_builtin_prop_flag_lookup_table[];
+    bool inheritable = false;
+    if(prop < LV_STYLE_NUM_BUILT_IN_PROPS) {
+        inheritable = lv_style_builtin_prop_flag_lookup_table[prop] & LV_STYLE_PROP_FLAG_INHERITABLE;
+    }
+    else {
+        if(_style_custom_prop_flag_lookup_table != NULL) {
+            inheritable = _style_custom_prop_flag_lookup_table[prop - LV_STYLE_NUM_BUILT_IN_PROPS] &
+                          LV_STYLE_PROP_FLAG_INHERITABLE;
+        }
+    }
+
+    if(inheritable) {
+        /*If not found, check the `MAIN` style first, if already on the MAIN part go to the parent*/
+        if(part != LV_PART_MAIN) part = LV_PART_MAIN;
+        else obj = obj->parent;
+
+        while(obj) {
+#if LV_OBJ_STYLE_CACHE
+            if(obj->style_main_prop_is_set & prop_shifted)
+#endif
+            {
+                selector = part | obj->state;
+                found = get_prop_core(obj, selector, prop, value_act);
+                if(found == LV_STYLE_RES_FOUND) return LV_STYLE_RES_FOUND;
+            }
+            /*Check the parent too.*/
+            obj = obj->parent;
+        }
+    }
+    else {
+        /*Get the width and height from the class.
+                * WIDTH and HEIGHT are not inherited so add them in the `else` to skip checking them for inherited properties */
+        if(part == LV_PART_MAIN && (prop == LV_STYLE_WIDTH || prop == LV_STYLE_HEIGHT)) {
+            const lv_obj_class_t * cls = obj->class_p;
+            while(cls) {
+                if(prop == LV_STYLE_WIDTH) {
+                    if(cls->width_def != 0)  {
+                        value_act->num = cls->width_def;
+                        return LV_STYLE_RES_FOUND;
+                    }
+                }
+                else {
+                    if(cls->height_def != 0) {
+                        value_act->num = cls->height_def;
+                        return LV_STYLE_RES_FOUND;
+                    }
+                }
+                cls = cls->base_class;
+            }
+        }
+    }
+
+    return LV_STYLE_RES_NOT_FOUND;
+}

+ 405 - 0
components/easylvgl/lvgl9/src/core/lv_obj_style.h

@@ -0,0 +1,405 @@
+/**
+ * @file lv_obj_style.h
+ *
+ */
+
+#ifndef LV_OBJ_STYLE_H
+#define LV_OBJ_STYLE_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/*********************
+ *      INCLUDES
+ *********************/
+#include "../misc/lv_bidi.h"
+#include "../misc/lv_style.h"
+#include "../misc/lv_types.h"
+
+/*********************
+ *      DEFINES
+ *********************/
+
+/**********************
+ *      TYPEDEFS
+ **********************/
+
+/**
+ * Possible states of a widget.
+ * OR-ed values are possible
+ */
+typedef enum {
+    LV_STATE_DEFAULT     = 0x0000,
+    LV_STATE_CHECKED     = 0x0001,
+    LV_STATE_FOCUSED     = 0x0002,
+    LV_STATE_FOCUS_KEY   = 0x0004,
+    LV_STATE_EDITED      = 0x0008,
+    LV_STATE_HOVERED     = 0x0010,
+    LV_STATE_PRESSED     = 0x0020,
+    LV_STATE_SCROLLED    = 0x0040,
+    LV_STATE_DISABLED    = 0x0080,
+    LV_STATE_USER_1      = 0x1000,
+    LV_STATE_USER_2      = 0x2000,
+    LV_STATE_USER_3      = 0x4000,
+    LV_STATE_USER_4      = 0x8000,
+
+    LV_STATE_ANY         = 0xFFFF,  /**< Special value can be used in some functions to target all states*/
+} lv_state_t;
+
+/**
+ * The possible parts of widgets.
+ * The parts can be considered as the internal building block of the widgets.
+ * E.g. slider = background + indicator + knob
+ * Not all parts are used by every widget
+ */
+
+typedef enum {
+    LV_PART_MAIN         = 0x000000,  /**< A background like rectangle*/
+    LV_PART_SCROLLBAR    = 0x010000,  /**< The scrollbar(s)*/
+    LV_PART_INDICATOR    = 0x020000,  /**< Indicator, e.g. for slider, bar, switch, or the tick box of the checkbox*/
+    LV_PART_KNOB         = 0x030000,  /**< Like handle to grab to adjust the value*/
+    LV_PART_SELECTED     = 0x040000,  /**< Indicate the currently selected option or section*/
+    LV_PART_ITEMS        = 0x050000,  /**< Used if the widget has multiple similar elements (e.g. table cells)*/
+    LV_PART_CURSOR       = 0x060000,  /**< Mark a specific place e.g. for text area's cursor or on a chart*/
+
+    LV_PART_CUSTOM_FIRST = 0x080000,  /**< Extension point for custom widgets*/
+
+    LV_PART_ANY          = 0x0F0000,  /**< Special value can be used in some functions to target all parts*/
+} lv_part_t;
+
+typedef enum {
+    LV_STYLE_STATE_CMP_SAME,           /**< The style properties in the 2 states are identical */
+    LV_STYLE_STATE_CMP_DIFF_REDRAW,    /**< The differences can be shown with a simple redraw */
+    LV_STYLE_STATE_CMP_DIFF_DRAW_PAD,  /**< The differences can be shown with a simple redraw */
+    LV_STYLE_STATE_CMP_DIFF_LAYOUT,    /**< The differences can be shown with a simple redraw */
+} lv_style_state_cmp_t;
+
+/**
+ * A joint type for `lv_part_t` and `lv_state_t`. Example values
+ * - `0`: means `LV_PART_MAIN | LV_STATE_DEFAULT`
+ * - `LV_STATE_PRSSED`
+ * - `LV_PART_KNOB`
+ * - `LV_PART_KNOB | LV_STATE_PRESSED | LV_STATE_CHECKED`
+ */
+typedef uint32_t lv_style_selector_t;
+
+/**********************
+ * GLOBAL PROTOTYPES
+ **********************/
+
+/**
+ * Add a style to an object.
+ * @param obj       pointer to an object
+ * @param style     pointer to a style to add
+ * @param selector  OR-ed value of parts and state to which the style should be added
+ *
+ * Examples:
+ * @code
+ * lv_obj_add_style(btn, &style_btn, 0); //Default button style
+ *
+ * lv_obj_add_style(btn, &btn_red, LV_STATE_PRESSED); //Overwrite only some colors to red when pressed
+ * @endcode
+ */
+void lv_obj_add_style(lv_obj_t * obj, const lv_style_t * style, lv_style_selector_t selector);
+
+/**
+ * Replaces a style of an object, preserving the order of the style stack (local styles and transitions are ignored).
+ * @param obj           pointer to an object
+ * @param old_style     pointer to a style to replace.
+ * @param new_style     pointer to a style to replace the old style with.
+ * @param selector      OR-ed values of states and a part to replace only styles with matching selectors. LV_STATE_ANY and LV_PART_ANY can be used
+ *
+ * Examples:
+ * @code
+ * lv_obj_replace_style(obj, &yellow_style, &blue_style, LV_PART_ANY | LV_STATE_ANY); //Replace a specific style
+ *
+ * lv_obj_replace_style(obj, &yellow_style, &blue_style, LV_PART_MAIN | LV_STATE_PRESSED); //Replace a specific style assigned to the main part when it is pressed
+ * @endcode
+ */
+bool lv_obj_replace_style(lv_obj_t * obj, const lv_style_t * old_style, const lv_style_t * new_style,
+                          lv_style_selector_t selector);
+
+/**
+ * Remove a style from an object.
+ * @param obj       pointer to an object
+ * @param style     pointer to a style to remove. Can be NULL to check only the selector
+ * @param selector  OR-ed values of states and a part to remove only styles with matching selectors. LV_STATE_ANY and LV_PART_ANY can be used
+ *
+ * Examples:
+ * @code
+ * lv_obj_remove_style(obj, &style, LV_PART_ANY | LV_STATE_ANY); //Remove a specific style
+ *
+ * lv_obj_remove_style(obj, NULL, LV_PART_MAIN | LV_STATE_ANY); //Remove all styles from the main part
+ *
+ * lv_obj_remove_style(obj, NULL, LV_PART_ANY | LV_STATE_ANY); //Remove all styles
+ * @endcode
+ */
+void lv_obj_remove_style(lv_obj_t * obj, const lv_style_t * style, lv_style_selector_t selector);
+
+/**
+ * Remove all styles from an object
+ * @param obj       pointer to an object
+ */
+void lv_obj_remove_style_all(lv_obj_t * obj);
+
+/**
+ * Notify all object if a style is modified
+ * @param style     pointer to a style. Only the objects with this style will be notified
+ *                  (NULL to notify all objects)
+ */
+void lv_obj_report_style_change(lv_style_t * style);
+
+/**
+ * Notify an object and its children about its style is modified.
+ * @param obj       pointer to an object
+ * @param part      the part whose style was changed. E.g. `LV_PART_ANY`, `LV_PART_MAIN`
+ * @param prop      `LV_STYLE_PROP_ANY` or an `LV_STYLE_...` property.
+ *                  It is used to optimize what needs to be refreshed.
+ *                  `LV_STYLE_PROP_INV` to perform only a style cache update
+ */
+void lv_obj_refresh_style(lv_obj_t * obj, lv_part_t part, lv_style_prop_t prop);
+
+/**
+ * Temporary disable a style for a selector. It will look like is the style wasn't added
+ * @param obj       pointer to an object
+ * @param style     pointer to a style
+ * @param selector  the selector of a style (e.g. LV_STATE_PRESSED | LV_PART_KNOB)
+ * @param dis       true: disable the style, false: enable the style
+ */
+void lv_obj_style_set_disabled(lv_obj_t * obj, const lv_style_t * style, lv_style_selector_t selector, bool dis);
+
+/**
+ * Get if a given style is disabled on an object.
+ * @param obj       pointer to an object
+ * @param style     pointer to a style
+ * @param selector  the selector of a style (e.g. LV_STATE_PRESSED | LV_PART_KNOB)
+ * @return          true: disable the style, false: enable the style
+ */
+bool lv_obj_style_get_disabled(lv_obj_t * obj, const lv_style_t * style, lv_style_selector_t selector);
+
+/**
+ * Enable or disable automatic style refreshing when a new style is added/removed to/from an object
+ * or any other style change happens.
+ * @param en        true: enable refreshing; false: disable refreshing
+ */
+void lv_obj_enable_style_refresh(bool en);
+
+/**
+ * Get the value of a style property. The current state of the object will be considered.
+ * Inherited properties will be inherited.
+ * If a property is not set a default value will be returned.
+ * @param obj       pointer to an object
+ * @param part      a part from which the property should be get
+ * @param prop      the property to get
+ * @return          the value of the property.
+ *                  Should be read from the correct field of the `lv_style_value_t` according to the type of the property.
+ */
+lv_style_value_t lv_obj_get_style_prop(const lv_obj_t * obj, lv_part_t part, lv_style_prop_t prop);
+
+/**
+ * Check if an object has a specified style property for a given style selector.
+ * @param obj       pointer to an object
+ * @param selector  the style selector to be checked, defining the scope of the style to be examined.
+ * @param prop      the property to be checked.
+ * @return          true if the object has the specified selector and property, false otherwise.
+ */
+bool lv_obj_has_style_prop(const lv_obj_t * obj, lv_style_selector_t selector, lv_style_prop_t prop);
+
+/**
+ * Set local style property on an object's part and state.
+ * @param obj       pointer to an object
+ * @param prop      the property
+ * @param value     value of the property. The correct element should be set according to the type of the property
+ * @param selector  OR-ed value of parts and state for which the style should be set
+ */
+void lv_obj_set_local_style_prop(lv_obj_t * obj, lv_style_prop_t prop, lv_style_value_t value,
+                                 lv_style_selector_t selector);
+
+lv_style_res_t lv_obj_get_local_style_prop(lv_obj_t * obj, lv_style_prop_t prop, lv_style_value_t * value,
+                                           lv_style_selector_t selector);
+
+/**
+ * Remove a local style property from a part of an object with a given state.
+ * @param obj       pointer to an object
+ * @param prop      a style property to remove.
+ * @param selector  OR-ed value of parts and state for which the style should be removed
+ * @return true     the property was found and removed; false: the property was not found
+ */
+bool lv_obj_remove_local_style_prop(lv_obj_t * obj, lv_style_prop_t prop, lv_style_selector_t selector);
+
+/**
+ * Used internally for color filtering
+ */
+lv_style_value_t lv_obj_style_apply_color_filter(const lv_obj_t * obj, lv_part_t part, lv_style_value_t v);
+
+/**
+ * Fade in an an object and all its children.
+ * @param obj       the object to fade in
+ * @param time      time of fade
+ * @param delay     delay to start the animation
+ */
+void lv_obj_fade_in(lv_obj_t * obj, uint32_t time, uint32_t delay);
+
+/**
+ * Fade out an an object and all its children.
+ * @param obj       the object to fade out
+ * @param time      time of fade
+ * @param delay     delay to start the animation
+ */
+void lv_obj_fade_out(lv_obj_t * obj, uint32_t time, uint32_t delay);
+
+static inline lv_state_t lv_obj_style_get_selector_state(lv_style_selector_t selector)
+{
+    return (lv_state_t)(selector & 0xFFFF);
+}
+
+static inline lv_part_t lv_obj_style_get_selector_part(lv_style_selector_t selector)
+{
+    return (lv_part_t)(selector & 0xFF0000);
+}
+
+#include "lv_obj_style_gen.h"
+
+static inline void lv_obj_set_style_pad_all(lv_obj_t * obj, int32_t value, lv_style_selector_t selector)
+{
+    lv_obj_set_style_pad_left(obj, value, selector);
+    lv_obj_set_style_pad_right(obj, value, selector);
+    lv_obj_set_style_pad_top(obj, value, selector);
+    lv_obj_set_style_pad_bottom(obj, value, selector);
+}
+
+static inline void lv_obj_set_style_pad_hor(lv_obj_t * obj, int32_t value, lv_style_selector_t selector)
+{
+    lv_obj_set_style_pad_left(obj, value, selector);
+    lv_obj_set_style_pad_right(obj, value, selector);
+}
+
+static inline void lv_obj_set_style_pad_ver(lv_obj_t * obj, int32_t value, lv_style_selector_t selector)
+{
+    lv_obj_set_style_pad_top(obj, value, selector);
+    lv_obj_set_style_pad_bottom(obj, value, selector);
+}
+
+static inline void lv_obj_set_style_margin_all(lv_obj_t * obj, int32_t value, lv_style_selector_t selector)
+{
+    lv_obj_set_style_margin_left(obj, value, selector);
+    lv_obj_set_style_margin_right(obj, value, selector);
+    lv_obj_set_style_margin_top(obj, value, selector);
+    lv_obj_set_style_margin_bottom(obj, value, selector);
+}
+
+static inline void lv_obj_set_style_margin_hor(lv_obj_t * obj, int32_t value, lv_style_selector_t selector)
+{
+    lv_obj_set_style_margin_left(obj, value, selector);
+    lv_obj_set_style_margin_right(obj, value, selector);
+}
+
+static inline void lv_obj_set_style_margin_ver(lv_obj_t * obj, int32_t value, lv_style_selector_t selector)
+{
+    lv_obj_set_style_margin_top(obj, value, selector);
+    lv_obj_set_style_margin_bottom(obj, value, selector);
+}
+
+static inline void lv_obj_set_style_pad_gap(lv_obj_t * obj, int32_t value, lv_style_selector_t selector)
+{
+    lv_obj_set_style_pad_row(obj, value, selector);
+    lv_obj_set_style_pad_column(obj, value, selector);
+}
+
+static inline void lv_obj_set_style_size(lv_obj_t * obj, int32_t width, int32_t height,
+                                         lv_style_selector_t selector)
+{
+    lv_obj_set_style_width(obj, width, selector);
+    lv_obj_set_style_height(obj, height, selector);
+}
+
+static inline void lv_obj_set_style_transform_scale(lv_obj_t * obj, int32_t value,
+                                                    lv_style_selector_t selector)
+{
+    lv_obj_set_style_transform_scale_x(obj, value, selector);
+    lv_obj_set_style_transform_scale_y(obj, value, selector);
+}
+
+static inline int32_t lv_obj_get_style_space_left(const lv_obj_t * obj, lv_part_t part)
+{
+    int32_t padding = lv_obj_get_style_pad_left(obj, part);
+    int32_t border_width = lv_obj_get_style_border_width(obj, part);
+    lv_border_side_t border_side = lv_obj_get_style_border_side(obj, part);
+    return (border_side & LV_BORDER_SIDE_LEFT) ? padding + border_width : padding;
+}
+
+static inline int32_t lv_obj_get_style_space_right(const lv_obj_t * obj, lv_part_t part)
+{
+    int32_t padding = lv_obj_get_style_pad_right(obj, part);
+    int32_t border_width = lv_obj_get_style_border_width(obj, part);
+    lv_border_side_t border_side = lv_obj_get_style_border_side(obj, part);
+    return (border_side & LV_BORDER_SIDE_RIGHT) ? padding + border_width : padding;
+}
+
+static inline int32_t lv_obj_get_style_space_top(const lv_obj_t * obj, lv_part_t part)
+{
+    int32_t padding = lv_obj_get_style_pad_top(obj, part);
+    int32_t border_width = lv_obj_get_style_border_width(obj, part);
+    lv_border_side_t border_side = lv_obj_get_style_border_side(obj, part);
+    return (border_side & LV_BORDER_SIDE_TOP) ? padding + border_width : padding;
+}
+
+static inline int32_t lv_obj_get_style_space_bottom(const lv_obj_t * obj, lv_part_t part)
+{
+    int32_t padding = lv_obj_get_style_pad_bottom(obj, part);
+    int32_t border_width = lv_obj_get_style_border_width(obj, part);
+    lv_border_side_t border_side = lv_obj_get_style_border_side(obj, part);
+    return (border_side & LV_BORDER_SIDE_BOTTOM) ? padding + border_width : padding;
+}
+
+lv_text_align_t lv_obj_calculate_style_text_align(const lv_obj_t * obj, lv_part_t part, const char * txt);
+
+static inline int32_t lv_obj_get_style_transform_scale_x_safe(const lv_obj_t * obj, lv_part_t part)
+{
+    int32_t scale = lv_obj_get_style_transform_scale_x(obj, part);
+    return scale > 0 ? scale : 1;
+}
+
+static inline int32_t lv_obj_get_style_transform_scale_y_safe(const lv_obj_t * obj, lv_part_t part)
+{
+    int32_t scale = lv_obj_get_style_transform_scale_y(obj, part);
+    return scale > 0 ? scale : 1;
+}
+
+/**
+ * Get the `opa` style property from all parents and multiply and `>> 8` them.
+ * @param obj       the object whose opacity should be get
+ * @param part      the part whose opacity should be get. Non-MAIN parts will consider the `opa` of the MAIN part too
+ * @return          the final opacity considering the parents' opacity too
+ */
+lv_opa_t lv_obj_get_style_opa_recursive(const lv_obj_t * obj, lv_part_t part);
+
+
+/**
+ * Apply recolor effect to the input color based on the object's style properties.
+ * @param obj       the target object containing recolor style properties
+ * @param part      the part to retrieve recolor styles.
+ * @param color     the original color to be modified
+ * @return          the blended color after applying recolor and opacity
+ */
+lv_color32_t lv_obj_style_apply_recolor(const lv_obj_t * obj, lv_part_t part, lv_color32_t color);
+
+/**
+ * Get the `recolor` style property from all parents and blend them recursively.
+ * @param obj       the object whose recolor value should be retrieved
+ * @param part      the target part to check. Non-MAIN parts will also consider
+ *                  the `recolor` value from the MAIN part during calculation
+ * @return          the final blended recolor value combining all parent's recolor values
+ */
+lv_color32_t lv_obj_get_style_recolor_recursive(const lv_obj_t * obj, lv_part_t part);
+
+/**********************
+ *      MACROS
+ **********************/
+
+#ifdef __cplusplus
+} /*extern "C"*/
+#endif
+
+#endif /*LV_OBJ_STYLE_H*/

+ 969 - 0
components/easylvgl/lvgl9/src/core/lv_obj_style_gen.c

@@ -0,0 +1,969 @@
+
+/*
+ **********************************************************************
+ *                            DO NOT EDIT
+ * This file is automatically generated by "style_api_gen.py"
+ **********************************************************************
+ */
+
+
+#include "lv_obj.h"
+
+
+void lv_obj_set_style_width(lv_obj_t * obj, int32_t value, lv_style_selector_t selector)
+{
+    lv_style_value_t v = {
+        .num = (int32_t)value
+    };
+    lv_obj_set_local_style_prop(obj, LV_STYLE_WIDTH, v, selector);
+}
+
+void lv_obj_set_style_min_width(lv_obj_t * obj, int32_t value, lv_style_selector_t selector)
+{
+    lv_style_value_t v = {
+        .num = (int32_t)value
+    };
+    lv_obj_set_local_style_prop(obj, LV_STYLE_MIN_WIDTH, v, selector);
+}
+
+void lv_obj_set_style_max_width(lv_obj_t * obj, int32_t value, lv_style_selector_t selector)
+{
+    lv_style_value_t v = {
+        .num = (int32_t)value
+    };
+    lv_obj_set_local_style_prop(obj, LV_STYLE_MAX_WIDTH, v, selector);
+}
+
+void lv_obj_set_style_height(lv_obj_t * obj, int32_t value, lv_style_selector_t selector)
+{
+    lv_style_value_t v = {
+        .num = (int32_t)value
+    };
+    lv_obj_set_local_style_prop(obj, LV_STYLE_HEIGHT, v, selector);
+}
+
+void lv_obj_set_style_min_height(lv_obj_t * obj, int32_t value, lv_style_selector_t selector)
+{
+    lv_style_value_t v = {
+        .num = (int32_t)value
+    };
+    lv_obj_set_local_style_prop(obj, LV_STYLE_MIN_HEIGHT, v, selector);
+}
+
+void lv_obj_set_style_max_height(lv_obj_t * obj, int32_t value, lv_style_selector_t selector)
+{
+    lv_style_value_t v = {
+        .num = (int32_t)value
+    };
+    lv_obj_set_local_style_prop(obj, LV_STYLE_MAX_HEIGHT, v, selector);
+}
+
+void lv_obj_set_style_length(lv_obj_t * obj, int32_t value, lv_style_selector_t selector)
+{
+    lv_style_value_t v = {
+        .num = (int32_t)value
+    };
+    lv_obj_set_local_style_prop(obj, LV_STYLE_LENGTH, v, selector);
+}
+
+void lv_obj_set_style_x(lv_obj_t * obj, int32_t value, lv_style_selector_t selector)
+{
+    lv_style_value_t v = {
+        .num = (int32_t)value
+    };
+    lv_obj_set_local_style_prop(obj, LV_STYLE_X, v, selector);
+}
+
+void lv_obj_set_style_y(lv_obj_t * obj, int32_t value, lv_style_selector_t selector)
+{
+    lv_style_value_t v = {
+        .num = (int32_t)value
+    };
+    lv_obj_set_local_style_prop(obj, LV_STYLE_Y, v, selector);
+}
+
+void lv_obj_set_style_align(lv_obj_t * obj, lv_align_t value, lv_style_selector_t selector)
+{
+    lv_style_value_t v = {
+        .num = (int32_t)value
+    };
+    lv_obj_set_local_style_prop(obj, LV_STYLE_ALIGN, v, selector);
+}
+
+void lv_obj_set_style_transform_width(lv_obj_t * obj, int32_t value, lv_style_selector_t selector)
+{
+    lv_style_value_t v = {
+        .num = (int32_t)value
+    };
+    lv_obj_set_local_style_prop(obj, LV_STYLE_TRANSFORM_WIDTH, v, selector);
+}
+
+void lv_obj_set_style_transform_height(lv_obj_t * obj, int32_t value, lv_style_selector_t selector)
+{
+    lv_style_value_t v = {
+        .num = (int32_t)value
+    };
+    lv_obj_set_local_style_prop(obj, LV_STYLE_TRANSFORM_HEIGHT, v, selector);
+}
+
+void lv_obj_set_style_translate_x(lv_obj_t * obj, int32_t value, lv_style_selector_t selector)
+{
+    lv_style_value_t v = {
+        .num = (int32_t)value
+    };
+    lv_obj_set_local_style_prop(obj, LV_STYLE_TRANSLATE_X, v, selector);
+}
+
+void lv_obj_set_style_translate_y(lv_obj_t * obj, int32_t value, lv_style_selector_t selector)
+{
+    lv_style_value_t v = {
+        .num = (int32_t)value
+    };
+    lv_obj_set_local_style_prop(obj, LV_STYLE_TRANSLATE_Y, v, selector);
+}
+
+void lv_obj_set_style_translate_radial(lv_obj_t * obj, int32_t value, lv_style_selector_t selector)
+{
+    lv_style_value_t v = {
+        .num = (int32_t)value
+    };
+    lv_obj_set_local_style_prop(obj, LV_STYLE_TRANSLATE_RADIAL, v, selector);
+}
+
+void lv_obj_set_style_transform_scale_x(lv_obj_t * obj, int32_t value, lv_style_selector_t selector)
+{
+    lv_style_value_t v = {
+        .num = (int32_t)value
+    };
+    lv_obj_set_local_style_prop(obj, LV_STYLE_TRANSFORM_SCALE_X, v, selector);
+}
+
+void lv_obj_set_style_transform_scale_y(lv_obj_t * obj, int32_t value, lv_style_selector_t selector)
+{
+    lv_style_value_t v = {
+        .num = (int32_t)value
+    };
+    lv_obj_set_local_style_prop(obj, LV_STYLE_TRANSFORM_SCALE_Y, v, selector);
+}
+
+void lv_obj_set_style_transform_rotation(lv_obj_t * obj, int32_t value, lv_style_selector_t selector)
+{
+    lv_style_value_t v = {
+        .num = (int32_t)value
+    };
+    lv_obj_set_local_style_prop(obj, LV_STYLE_TRANSFORM_ROTATION, v, selector);
+}
+
+void lv_obj_set_style_transform_pivot_x(lv_obj_t * obj, int32_t value, lv_style_selector_t selector)
+{
+    lv_style_value_t v = {
+        .num = (int32_t)value
+    };
+    lv_obj_set_local_style_prop(obj, LV_STYLE_TRANSFORM_PIVOT_X, v, selector);
+}
+
+void lv_obj_set_style_transform_pivot_y(lv_obj_t * obj, int32_t value, lv_style_selector_t selector)
+{
+    lv_style_value_t v = {
+        .num = (int32_t)value
+    };
+    lv_obj_set_local_style_prop(obj, LV_STYLE_TRANSFORM_PIVOT_Y, v, selector);
+}
+
+void lv_obj_set_style_transform_skew_x(lv_obj_t * obj, int32_t value, lv_style_selector_t selector)
+{
+    lv_style_value_t v = {
+        .num = (int32_t)value
+    };
+    lv_obj_set_local_style_prop(obj, LV_STYLE_TRANSFORM_SKEW_X, v, selector);
+}
+
+void lv_obj_set_style_transform_skew_y(lv_obj_t * obj, int32_t value, lv_style_selector_t selector)
+{
+    lv_style_value_t v = {
+        .num = (int32_t)value
+    };
+    lv_obj_set_local_style_prop(obj, LV_STYLE_TRANSFORM_SKEW_Y, v, selector);
+}
+
+void lv_obj_set_style_pad_top(lv_obj_t * obj, int32_t value, lv_style_selector_t selector)
+{
+    lv_style_value_t v = {
+        .num = (int32_t)value
+    };
+    lv_obj_set_local_style_prop(obj, LV_STYLE_PAD_TOP, v, selector);
+}
+
+void lv_obj_set_style_pad_bottom(lv_obj_t * obj, int32_t value, lv_style_selector_t selector)
+{
+    lv_style_value_t v = {
+        .num = (int32_t)value
+    };
+    lv_obj_set_local_style_prop(obj, LV_STYLE_PAD_BOTTOM, v, selector);
+}
+
+void lv_obj_set_style_pad_left(lv_obj_t * obj, int32_t value, lv_style_selector_t selector)
+{
+    lv_style_value_t v = {
+        .num = (int32_t)value
+    };
+    lv_obj_set_local_style_prop(obj, LV_STYLE_PAD_LEFT, v, selector);
+}
+
+void lv_obj_set_style_pad_right(lv_obj_t * obj, int32_t value, lv_style_selector_t selector)
+{
+    lv_style_value_t v = {
+        .num = (int32_t)value
+    };
+    lv_obj_set_local_style_prop(obj, LV_STYLE_PAD_RIGHT, v, selector);
+}
+
+void lv_obj_set_style_pad_row(lv_obj_t * obj, int32_t value, lv_style_selector_t selector)
+{
+    lv_style_value_t v = {
+        .num = (int32_t)value
+    };
+    lv_obj_set_local_style_prop(obj, LV_STYLE_PAD_ROW, v, selector);
+}
+
+void lv_obj_set_style_pad_column(lv_obj_t * obj, int32_t value, lv_style_selector_t selector)
+{
+    lv_style_value_t v = {
+        .num = (int32_t)value
+    };
+    lv_obj_set_local_style_prop(obj, LV_STYLE_PAD_COLUMN, v, selector);
+}
+
+void lv_obj_set_style_pad_radial(lv_obj_t * obj, int32_t value, lv_style_selector_t selector)
+{
+    lv_style_value_t v = {
+        .num = (int32_t)value
+    };
+    lv_obj_set_local_style_prop(obj, LV_STYLE_PAD_RADIAL, v, selector);
+}
+
+void lv_obj_set_style_margin_top(lv_obj_t * obj, int32_t value, lv_style_selector_t selector)
+{
+    lv_style_value_t v = {
+        .num = (int32_t)value
+    };
+    lv_obj_set_local_style_prop(obj, LV_STYLE_MARGIN_TOP, v, selector);
+}
+
+void lv_obj_set_style_margin_bottom(lv_obj_t * obj, int32_t value, lv_style_selector_t selector)
+{
+    lv_style_value_t v = {
+        .num = (int32_t)value
+    };
+    lv_obj_set_local_style_prop(obj, LV_STYLE_MARGIN_BOTTOM, v, selector);
+}
+
+void lv_obj_set_style_margin_left(lv_obj_t * obj, int32_t value, lv_style_selector_t selector)
+{
+    lv_style_value_t v = {
+        .num = (int32_t)value
+    };
+    lv_obj_set_local_style_prop(obj, LV_STYLE_MARGIN_LEFT, v, selector);
+}
+
+void lv_obj_set_style_margin_right(lv_obj_t * obj, int32_t value, lv_style_selector_t selector)
+{
+    lv_style_value_t v = {
+        .num = (int32_t)value
+    };
+    lv_obj_set_local_style_prop(obj, LV_STYLE_MARGIN_RIGHT, v, selector);
+}
+
+void lv_obj_set_style_bg_color(lv_obj_t * obj, lv_color_t value, lv_style_selector_t selector)
+{
+    lv_style_value_t v = {
+        .color = value
+    };
+    lv_obj_set_local_style_prop(obj, LV_STYLE_BG_COLOR, v, selector);
+}
+
+void lv_obj_set_style_bg_opa(lv_obj_t * obj, lv_opa_t value, lv_style_selector_t selector)
+{
+    lv_style_value_t v = {
+        .num = (int32_t)value
+    };
+    lv_obj_set_local_style_prop(obj, LV_STYLE_BG_OPA, v, selector);
+}
+
+void lv_obj_set_style_bg_grad_color(lv_obj_t * obj, lv_color_t value, lv_style_selector_t selector)
+{
+    lv_style_value_t v = {
+        .color = value
+    };
+    lv_obj_set_local_style_prop(obj, LV_STYLE_BG_GRAD_COLOR, v, selector);
+}
+
+void lv_obj_set_style_bg_grad_dir(lv_obj_t * obj, lv_grad_dir_t value, lv_style_selector_t selector)
+{
+    lv_style_value_t v = {
+        .num = (int32_t)value
+    };
+    lv_obj_set_local_style_prop(obj, LV_STYLE_BG_GRAD_DIR, v, selector);
+}
+
+void lv_obj_set_style_bg_main_stop(lv_obj_t * obj, int32_t value, lv_style_selector_t selector)
+{
+    lv_style_value_t v = {
+        .num = (int32_t)value
+    };
+    lv_obj_set_local_style_prop(obj, LV_STYLE_BG_MAIN_STOP, v, selector);
+}
+
+void lv_obj_set_style_bg_grad_stop(lv_obj_t * obj, int32_t value, lv_style_selector_t selector)
+{
+    lv_style_value_t v = {
+        .num = (int32_t)value
+    };
+    lv_obj_set_local_style_prop(obj, LV_STYLE_BG_GRAD_STOP, v, selector);
+}
+
+void lv_obj_set_style_bg_main_opa(lv_obj_t * obj, lv_opa_t value, lv_style_selector_t selector)
+{
+    lv_style_value_t v = {
+        .num = (int32_t)value
+    };
+    lv_obj_set_local_style_prop(obj, LV_STYLE_BG_MAIN_OPA, v, selector);
+}
+
+void lv_obj_set_style_bg_grad_opa(lv_obj_t * obj, lv_opa_t value, lv_style_selector_t selector)
+{
+    lv_style_value_t v = {
+        .num = (int32_t)value
+    };
+    lv_obj_set_local_style_prop(obj, LV_STYLE_BG_GRAD_OPA, v, selector);
+}
+
+void lv_obj_set_style_bg_grad(lv_obj_t * obj, const lv_grad_dsc_t * value, lv_style_selector_t selector)
+{
+    lv_style_value_t v = {
+        .ptr = value
+    };
+    lv_obj_set_local_style_prop(obj, LV_STYLE_BG_GRAD, v, selector);
+}
+
+void lv_obj_set_style_bg_image_src(lv_obj_t * obj, const void * value, lv_style_selector_t selector)
+{
+    lv_style_value_t v = {
+        .ptr = value
+    };
+    lv_obj_set_local_style_prop(obj, LV_STYLE_BG_IMAGE_SRC, v, selector);
+}
+
+void lv_obj_set_style_bg_image_opa(lv_obj_t * obj, lv_opa_t value, lv_style_selector_t selector)
+{
+    lv_style_value_t v = {
+        .num = (int32_t)value
+    };
+    lv_obj_set_local_style_prop(obj, LV_STYLE_BG_IMAGE_OPA, v, selector);
+}
+
+void lv_obj_set_style_bg_image_recolor(lv_obj_t * obj, lv_color_t value, lv_style_selector_t selector)
+{
+    lv_style_value_t v = {
+        .color = value
+    };
+    lv_obj_set_local_style_prop(obj, LV_STYLE_BG_IMAGE_RECOLOR, v, selector);
+}
+
+void lv_obj_set_style_bg_image_recolor_opa(lv_obj_t * obj, lv_opa_t value, lv_style_selector_t selector)
+{
+    lv_style_value_t v = {
+        .num = (int32_t)value
+    };
+    lv_obj_set_local_style_prop(obj, LV_STYLE_BG_IMAGE_RECOLOR_OPA, v, selector);
+}
+
+void lv_obj_set_style_bg_image_tiled(lv_obj_t * obj, bool value, lv_style_selector_t selector)
+{
+    lv_style_value_t v = {
+        .num = (int32_t)value
+    };
+    lv_obj_set_local_style_prop(obj, LV_STYLE_BG_IMAGE_TILED, v, selector);
+}
+
+void lv_obj_set_style_border_color(lv_obj_t * obj, lv_color_t value, lv_style_selector_t selector)
+{
+    lv_style_value_t v = {
+        .color = value
+    };
+    lv_obj_set_local_style_prop(obj, LV_STYLE_BORDER_COLOR, v, selector);
+}
+
+void lv_obj_set_style_border_opa(lv_obj_t * obj, lv_opa_t value, lv_style_selector_t selector)
+{
+    lv_style_value_t v = {
+        .num = (int32_t)value
+    };
+    lv_obj_set_local_style_prop(obj, LV_STYLE_BORDER_OPA, v, selector);
+}
+
+void lv_obj_set_style_border_width(lv_obj_t * obj, int32_t value, lv_style_selector_t selector)
+{
+    lv_style_value_t v = {
+        .num = (int32_t)value
+    };
+    lv_obj_set_local_style_prop(obj, LV_STYLE_BORDER_WIDTH, v, selector);
+}
+
+void lv_obj_set_style_border_side(lv_obj_t * obj, lv_border_side_t value, lv_style_selector_t selector)
+{
+    lv_style_value_t v = {
+        .num = (int32_t)value
+    };
+    lv_obj_set_local_style_prop(obj, LV_STYLE_BORDER_SIDE, v, selector);
+}
+
+void lv_obj_set_style_border_post(lv_obj_t * obj, bool value, lv_style_selector_t selector)
+{
+    lv_style_value_t v = {
+        .num = (int32_t)value
+    };
+    lv_obj_set_local_style_prop(obj, LV_STYLE_BORDER_POST, v, selector);
+}
+
+void lv_obj_set_style_outline_width(lv_obj_t * obj, int32_t value, lv_style_selector_t selector)
+{
+    lv_style_value_t v = {
+        .num = (int32_t)value
+    };
+    lv_obj_set_local_style_prop(obj, LV_STYLE_OUTLINE_WIDTH, v, selector);
+}
+
+void lv_obj_set_style_outline_color(lv_obj_t * obj, lv_color_t value, lv_style_selector_t selector)
+{
+    lv_style_value_t v = {
+        .color = value
+    };
+    lv_obj_set_local_style_prop(obj, LV_STYLE_OUTLINE_COLOR, v, selector);
+}
+
+void lv_obj_set_style_outline_opa(lv_obj_t * obj, lv_opa_t value, lv_style_selector_t selector)
+{
+    lv_style_value_t v = {
+        .num = (int32_t)value
+    };
+    lv_obj_set_local_style_prop(obj, LV_STYLE_OUTLINE_OPA, v, selector);
+}
+
+void lv_obj_set_style_outline_pad(lv_obj_t * obj, int32_t value, lv_style_selector_t selector)
+{
+    lv_style_value_t v = {
+        .num = (int32_t)value
+    };
+    lv_obj_set_local_style_prop(obj, LV_STYLE_OUTLINE_PAD, v, selector);
+}
+
+void lv_obj_set_style_shadow_width(lv_obj_t * obj, int32_t value, lv_style_selector_t selector)
+{
+    lv_style_value_t v = {
+        .num = (int32_t)value
+    };
+    lv_obj_set_local_style_prop(obj, LV_STYLE_SHADOW_WIDTH, v, selector);
+}
+
+void lv_obj_set_style_shadow_offset_x(lv_obj_t * obj, int32_t value, lv_style_selector_t selector)
+{
+    lv_style_value_t v = {
+        .num = (int32_t)value
+    };
+    lv_obj_set_local_style_prop(obj, LV_STYLE_SHADOW_OFFSET_X, v, selector);
+}
+
+void lv_obj_set_style_shadow_offset_y(lv_obj_t * obj, int32_t value, lv_style_selector_t selector)
+{
+    lv_style_value_t v = {
+        .num = (int32_t)value
+    };
+    lv_obj_set_local_style_prop(obj, LV_STYLE_SHADOW_OFFSET_Y, v, selector);
+}
+
+void lv_obj_set_style_shadow_spread(lv_obj_t * obj, int32_t value, lv_style_selector_t selector)
+{
+    lv_style_value_t v = {
+        .num = (int32_t)value
+    };
+    lv_obj_set_local_style_prop(obj, LV_STYLE_SHADOW_SPREAD, v, selector);
+}
+
+void lv_obj_set_style_shadow_color(lv_obj_t * obj, lv_color_t value, lv_style_selector_t selector)
+{
+    lv_style_value_t v = {
+        .color = value
+    };
+    lv_obj_set_local_style_prop(obj, LV_STYLE_SHADOW_COLOR, v, selector);
+}
+
+void lv_obj_set_style_shadow_opa(lv_obj_t * obj, lv_opa_t value, lv_style_selector_t selector)
+{
+    lv_style_value_t v = {
+        .num = (int32_t)value
+    };
+    lv_obj_set_local_style_prop(obj, LV_STYLE_SHADOW_OPA, v, selector);
+}
+
+void lv_obj_set_style_image_opa(lv_obj_t * obj, lv_opa_t value, lv_style_selector_t selector)
+{
+    lv_style_value_t v = {
+        .num = (int32_t)value
+    };
+    lv_obj_set_local_style_prop(obj, LV_STYLE_IMAGE_OPA, v, selector);
+}
+
+void lv_obj_set_style_image_recolor(lv_obj_t * obj, lv_color_t value, lv_style_selector_t selector)
+{
+    lv_style_value_t v = {
+        .color = value
+    };
+    lv_obj_set_local_style_prop(obj, LV_STYLE_IMAGE_RECOLOR, v, selector);
+}
+
+void lv_obj_set_style_image_recolor_opa(lv_obj_t * obj, lv_opa_t value, lv_style_selector_t selector)
+{
+    lv_style_value_t v = {
+        .num = (int32_t)value
+    };
+    lv_obj_set_local_style_prop(obj, LV_STYLE_IMAGE_RECOLOR_OPA, v, selector);
+}
+
+void lv_obj_set_style_image_colorkey(lv_obj_t * obj, const lv_image_colorkey_t * value, lv_style_selector_t selector)
+{
+    lv_style_value_t v = {
+        .ptr = value
+    };
+    lv_obj_set_local_style_prop(obj, LV_STYLE_IMAGE_COLORKEY, v, selector);
+}
+
+void lv_obj_set_style_line_width(lv_obj_t * obj, int32_t value, lv_style_selector_t selector)
+{
+    lv_style_value_t v = {
+        .num = (int32_t)value
+    };
+    lv_obj_set_local_style_prop(obj, LV_STYLE_LINE_WIDTH, v, selector);
+}
+
+void lv_obj_set_style_line_dash_width(lv_obj_t * obj, int32_t value, lv_style_selector_t selector)
+{
+    lv_style_value_t v = {
+        .num = (int32_t)value
+    };
+    lv_obj_set_local_style_prop(obj, LV_STYLE_LINE_DASH_WIDTH, v, selector);
+}
+
+void lv_obj_set_style_line_dash_gap(lv_obj_t * obj, int32_t value, lv_style_selector_t selector)
+{
+    lv_style_value_t v = {
+        .num = (int32_t)value
+    };
+    lv_obj_set_local_style_prop(obj, LV_STYLE_LINE_DASH_GAP, v, selector);
+}
+
+void lv_obj_set_style_line_rounded(lv_obj_t * obj, bool value, lv_style_selector_t selector)
+{
+    lv_style_value_t v = {
+        .num = (int32_t)value
+    };
+    lv_obj_set_local_style_prop(obj, LV_STYLE_LINE_ROUNDED, v, selector);
+}
+
+void lv_obj_set_style_line_color(lv_obj_t * obj, lv_color_t value, lv_style_selector_t selector)
+{
+    lv_style_value_t v = {
+        .color = value
+    };
+    lv_obj_set_local_style_prop(obj, LV_STYLE_LINE_COLOR, v, selector);
+}
+
+void lv_obj_set_style_line_opa(lv_obj_t * obj, lv_opa_t value, lv_style_selector_t selector)
+{
+    lv_style_value_t v = {
+        .num = (int32_t)value
+    };
+    lv_obj_set_local_style_prop(obj, LV_STYLE_LINE_OPA, v, selector);
+}
+
+void lv_obj_set_style_arc_width(lv_obj_t * obj, int32_t value, lv_style_selector_t selector)
+{
+    lv_style_value_t v = {
+        .num = (int32_t)value
+    };
+    lv_obj_set_local_style_prop(obj, LV_STYLE_ARC_WIDTH, v, selector);
+}
+
+void lv_obj_set_style_arc_rounded(lv_obj_t * obj, bool value, lv_style_selector_t selector)
+{
+    lv_style_value_t v = {
+        .num = (int32_t)value
+    };
+    lv_obj_set_local_style_prop(obj, LV_STYLE_ARC_ROUNDED, v, selector);
+}
+
+void lv_obj_set_style_arc_color(lv_obj_t * obj, lv_color_t value, lv_style_selector_t selector)
+{
+    lv_style_value_t v = {
+        .color = value
+    };
+    lv_obj_set_local_style_prop(obj, LV_STYLE_ARC_COLOR, v, selector);
+}
+
+void lv_obj_set_style_arc_opa(lv_obj_t * obj, lv_opa_t value, lv_style_selector_t selector)
+{
+    lv_style_value_t v = {
+        .num = (int32_t)value
+    };
+    lv_obj_set_local_style_prop(obj, LV_STYLE_ARC_OPA, v, selector);
+}
+
+void lv_obj_set_style_arc_image_src(lv_obj_t * obj, const void * value, lv_style_selector_t selector)
+{
+    lv_style_value_t v = {
+        .ptr = value
+    };
+    lv_obj_set_local_style_prop(obj, LV_STYLE_ARC_IMAGE_SRC, v, selector);
+}
+
+void lv_obj_set_style_text_color(lv_obj_t * obj, lv_color_t value, lv_style_selector_t selector)
+{
+    lv_style_value_t v = {
+        .color = value
+    };
+    lv_obj_set_local_style_prop(obj, LV_STYLE_TEXT_COLOR, v, selector);
+}
+
+void lv_obj_set_style_text_opa(lv_obj_t * obj, lv_opa_t value, lv_style_selector_t selector)
+{
+    lv_style_value_t v = {
+        .num = (int32_t)value
+    };
+    lv_obj_set_local_style_prop(obj, LV_STYLE_TEXT_OPA, v, selector);
+}
+
+void lv_obj_set_style_text_font(lv_obj_t * obj, const lv_font_t * value, lv_style_selector_t selector)
+{
+    lv_style_value_t v = {
+        .ptr = value
+    };
+    lv_obj_set_local_style_prop(obj, LV_STYLE_TEXT_FONT, v, selector);
+}
+
+void lv_obj_set_style_text_letter_space(lv_obj_t * obj, int32_t value, lv_style_selector_t selector)
+{
+    lv_style_value_t v = {
+        .num = (int32_t)value
+    };
+    lv_obj_set_local_style_prop(obj, LV_STYLE_TEXT_LETTER_SPACE, v, selector);
+}
+
+void lv_obj_set_style_text_line_space(lv_obj_t * obj, int32_t value, lv_style_selector_t selector)
+{
+    lv_style_value_t v = {
+        .num = (int32_t)value
+    };
+    lv_obj_set_local_style_prop(obj, LV_STYLE_TEXT_LINE_SPACE, v, selector);
+}
+
+void lv_obj_set_style_text_decor(lv_obj_t * obj, lv_text_decor_t value, lv_style_selector_t selector)
+{
+    lv_style_value_t v = {
+        .num = (int32_t)value
+    };
+    lv_obj_set_local_style_prop(obj, LV_STYLE_TEXT_DECOR, v, selector);
+}
+
+void lv_obj_set_style_text_align(lv_obj_t * obj, lv_text_align_t value, lv_style_selector_t selector)
+{
+    lv_style_value_t v = {
+        .num = (int32_t)value
+    };
+    lv_obj_set_local_style_prop(obj, LV_STYLE_TEXT_ALIGN, v, selector);
+}
+
+void lv_obj_set_style_text_outline_stroke_color(lv_obj_t * obj, lv_color_t value, lv_style_selector_t selector)
+{
+    lv_style_value_t v = {
+        .color = value
+    };
+    lv_obj_set_local_style_prop(obj, LV_STYLE_TEXT_OUTLINE_STROKE_COLOR, v, selector);
+}
+
+void lv_obj_set_style_text_outline_stroke_width(lv_obj_t * obj, int32_t value, lv_style_selector_t selector)
+{
+    lv_style_value_t v = {
+        .num = (int32_t)value
+    };
+    lv_obj_set_local_style_prop(obj, LV_STYLE_TEXT_OUTLINE_STROKE_WIDTH, v, selector);
+}
+
+void lv_obj_set_style_text_outline_stroke_opa(lv_obj_t * obj, lv_opa_t value, lv_style_selector_t selector)
+{
+    lv_style_value_t v = {
+        .num = (int32_t)value
+    };
+    lv_obj_set_local_style_prop(obj, LV_STYLE_TEXT_OUTLINE_STROKE_OPA, v, selector);
+}
+
+void lv_obj_set_style_radius(lv_obj_t * obj, int32_t value, lv_style_selector_t selector)
+{
+    lv_style_value_t v = {
+        .num = (int32_t)value
+    };
+    lv_obj_set_local_style_prop(obj, LV_STYLE_RADIUS, v, selector);
+}
+
+void lv_obj_set_style_radial_offset(lv_obj_t * obj, int32_t value, lv_style_selector_t selector)
+{
+    lv_style_value_t v = {
+        .num = (int32_t)value
+    };
+    lv_obj_set_local_style_prop(obj, LV_STYLE_RADIAL_OFFSET, v, selector);
+}
+
+void lv_obj_set_style_clip_corner(lv_obj_t * obj, bool value, lv_style_selector_t selector)
+{
+    lv_style_value_t v = {
+        .num = (int32_t)value
+    };
+    lv_obj_set_local_style_prop(obj, LV_STYLE_CLIP_CORNER, v, selector);
+}
+
+void lv_obj_set_style_opa(lv_obj_t * obj, lv_opa_t value, lv_style_selector_t selector)
+{
+    lv_style_value_t v = {
+        .num = (int32_t)value
+    };
+    lv_obj_set_local_style_prop(obj, LV_STYLE_OPA, v, selector);
+}
+
+void lv_obj_set_style_opa_layered(lv_obj_t * obj, lv_opa_t value, lv_style_selector_t selector)
+{
+    lv_style_value_t v = {
+        .num = (int32_t)value
+    };
+    lv_obj_set_local_style_prop(obj, LV_STYLE_OPA_LAYERED, v, selector);
+}
+
+void lv_obj_set_style_color_filter_dsc(lv_obj_t * obj, const lv_color_filter_dsc_t * value, lv_style_selector_t selector)
+{
+    lv_style_value_t v = {
+        .ptr = value
+    };
+    lv_obj_set_local_style_prop(obj, LV_STYLE_COLOR_FILTER_DSC, v, selector);
+}
+
+void lv_obj_set_style_color_filter_opa(lv_obj_t * obj, lv_opa_t value, lv_style_selector_t selector)
+{
+    lv_style_value_t v = {
+        .num = (int32_t)value
+    };
+    lv_obj_set_local_style_prop(obj, LV_STYLE_COLOR_FILTER_OPA, v, selector);
+}
+
+void lv_obj_set_style_recolor(lv_obj_t * obj, lv_color_t value, lv_style_selector_t selector)
+{
+    lv_style_value_t v = {
+        .color = value
+    };
+    lv_obj_set_local_style_prop(obj, LV_STYLE_RECOLOR, v, selector);
+}
+
+void lv_obj_set_style_recolor_opa(lv_obj_t * obj, lv_opa_t value, lv_style_selector_t selector)
+{
+    lv_style_value_t v = {
+        .num = (int32_t)value
+    };
+    lv_obj_set_local_style_prop(obj, LV_STYLE_RECOLOR_OPA, v, selector);
+}
+
+void lv_obj_set_style_anim(lv_obj_t * obj, const lv_anim_t * value, lv_style_selector_t selector)
+{
+    lv_style_value_t v = {
+        .ptr = value
+    };
+    lv_obj_set_local_style_prop(obj, LV_STYLE_ANIM, v, selector);
+}
+
+void lv_obj_set_style_anim_duration(lv_obj_t * obj, uint32_t value, lv_style_selector_t selector)
+{
+    lv_style_value_t v = {
+        .num = (int32_t)value
+    };
+    lv_obj_set_local_style_prop(obj, LV_STYLE_ANIM_DURATION, v, selector);
+}
+
+void lv_obj_set_style_transition(lv_obj_t * obj, const lv_style_transition_dsc_t * value, lv_style_selector_t selector)
+{
+    lv_style_value_t v = {
+        .ptr = value
+    };
+    lv_obj_set_local_style_prop(obj, LV_STYLE_TRANSITION, v, selector);
+}
+
+void lv_obj_set_style_blend_mode(lv_obj_t * obj, lv_blend_mode_t value, lv_style_selector_t selector)
+{
+    lv_style_value_t v = {
+        .num = (int32_t)value
+    };
+    lv_obj_set_local_style_prop(obj, LV_STYLE_BLEND_MODE, v, selector);
+}
+
+void lv_obj_set_style_layout(lv_obj_t * obj, uint16_t value, lv_style_selector_t selector)
+{
+    lv_style_value_t v = {
+        .num = (int32_t)value
+    };
+    lv_obj_set_local_style_prop(obj, LV_STYLE_LAYOUT, v, selector);
+}
+
+void lv_obj_set_style_base_dir(lv_obj_t * obj, lv_base_dir_t value, lv_style_selector_t selector)
+{
+    lv_style_value_t v = {
+        .num = (int32_t)value
+    };
+    lv_obj_set_local_style_prop(obj, LV_STYLE_BASE_DIR, v, selector);
+}
+
+void lv_obj_set_style_bitmap_mask_src(lv_obj_t * obj, const void * value, lv_style_selector_t selector)
+{
+    lv_style_value_t v = {
+        .ptr = value
+    };
+    lv_obj_set_local_style_prop(obj, LV_STYLE_BITMAP_MASK_SRC, v, selector);
+}
+
+void lv_obj_set_style_rotary_sensitivity(lv_obj_t * obj, uint32_t value, lv_style_selector_t selector)
+{
+    lv_style_value_t v = {
+        .num = (int32_t)value
+    };
+    lv_obj_set_local_style_prop(obj, LV_STYLE_ROTARY_SENSITIVITY, v, selector);
+}
+#if LV_USE_FLEX
+
+void lv_obj_set_style_flex_flow(lv_obj_t * obj, lv_flex_flow_t value, lv_style_selector_t selector)
+{
+    lv_style_value_t v = {
+        .num = (int32_t)value
+    };
+    lv_obj_set_local_style_prop(obj, LV_STYLE_FLEX_FLOW, v, selector);
+}
+
+void lv_obj_set_style_flex_main_place(lv_obj_t * obj, lv_flex_align_t value, lv_style_selector_t selector)
+{
+    lv_style_value_t v = {
+        .num = (int32_t)value
+    };
+    lv_obj_set_local_style_prop(obj, LV_STYLE_FLEX_MAIN_PLACE, v, selector);
+}
+
+void lv_obj_set_style_flex_cross_place(lv_obj_t * obj, lv_flex_align_t value, lv_style_selector_t selector)
+{
+    lv_style_value_t v = {
+        .num = (int32_t)value
+    };
+    lv_obj_set_local_style_prop(obj, LV_STYLE_FLEX_CROSS_PLACE, v, selector);
+}
+
+void lv_obj_set_style_flex_track_place(lv_obj_t * obj, lv_flex_align_t value, lv_style_selector_t selector)
+{
+    lv_style_value_t v = {
+        .num = (int32_t)value
+    };
+    lv_obj_set_local_style_prop(obj, LV_STYLE_FLEX_TRACK_PLACE, v, selector);
+}
+
+void lv_obj_set_style_flex_grow(lv_obj_t * obj, uint8_t value, lv_style_selector_t selector)
+{
+    lv_style_value_t v = {
+        .num = (int32_t)value
+    };
+    lv_obj_set_local_style_prop(obj, LV_STYLE_FLEX_GROW, v, selector);
+}
+#endif /*LV_USE_FLEX*/
+
+#if LV_USE_GRID
+
+void lv_obj_set_style_grid_column_dsc_array(lv_obj_t * obj, const int32_t * value, lv_style_selector_t selector)
+{
+    lv_style_value_t v = {
+        .ptr = value
+    };
+    lv_obj_set_local_style_prop(obj, LV_STYLE_GRID_COLUMN_DSC_ARRAY, v, selector);
+}
+
+void lv_obj_set_style_grid_column_align(lv_obj_t * obj, lv_grid_align_t value, lv_style_selector_t selector)
+{
+    lv_style_value_t v = {
+        .num = (int32_t)value
+    };
+    lv_obj_set_local_style_prop(obj, LV_STYLE_GRID_COLUMN_ALIGN, v, selector);
+}
+
+void lv_obj_set_style_grid_row_dsc_array(lv_obj_t * obj, const int32_t * value, lv_style_selector_t selector)
+{
+    lv_style_value_t v = {
+        .ptr = value
+    };
+    lv_obj_set_local_style_prop(obj, LV_STYLE_GRID_ROW_DSC_ARRAY, v, selector);
+}
+
+void lv_obj_set_style_grid_row_align(lv_obj_t * obj, lv_grid_align_t value, lv_style_selector_t selector)
+{
+    lv_style_value_t v = {
+        .num = (int32_t)value
+    };
+    lv_obj_set_local_style_prop(obj, LV_STYLE_GRID_ROW_ALIGN, v, selector);
+}
+
+void lv_obj_set_style_grid_cell_column_pos(lv_obj_t * obj, int32_t value, lv_style_selector_t selector)
+{
+    lv_style_value_t v = {
+        .num = (int32_t)value
+    };
+    lv_obj_set_local_style_prop(obj, LV_STYLE_GRID_CELL_COLUMN_POS, v, selector);
+}
+
+void lv_obj_set_style_grid_cell_x_align(lv_obj_t * obj, lv_grid_align_t value, lv_style_selector_t selector)
+{
+    lv_style_value_t v = {
+        .num = (int32_t)value
+    };
+    lv_obj_set_local_style_prop(obj, LV_STYLE_GRID_CELL_X_ALIGN, v, selector);
+}
+
+void lv_obj_set_style_grid_cell_column_span(lv_obj_t * obj, int32_t value, lv_style_selector_t selector)
+{
+    lv_style_value_t v = {
+        .num = (int32_t)value
+    };
+    lv_obj_set_local_style_prop(obj, LV_STYLE_GRID_CELL_COLUMN_SPAN, v, selector);
+}
+
+void lv_obj_set_style_grid_cell_row_pos(lv_obj_t * obj, int32_t value, lv_style_selector_t selector)
+{
+    lv_style_value_t v = {
+        .num = (int32_t)value
+    };
+    lv_obj_set_local_style_prop(obj, LV_STYLE_GRID_CELL_ROW_POS, v, selector);
+}
+
+void lv_obj_set_style_grid_cell_y_align(lv_obj_t * obj, lv_grid_align_t value, lv_style_selector_t selector)
+{
+    lv_style_value_t v = {
+        .num = (int32_t)value
+    };
+    lv_obj_set_local_style_prop(obj, LV_STYLE_GRID_CELL_Y_ALIGN, v, selector);
+}
+
+void lv_obj_set_style_grid_cell_row_span(lv_obj_t * obj, int32_t value, lv_style_selector_t selector)
+{
+    lv_style_value_t v = {
+        .num = (int32_t)value
+    };
+    lv_obj_set_local_style_prop(obj, LV_STYLE_GRID_CELL_ROW_SPAN, v, selector);
+}
+#endif /*LV_USE_GRID*/
+

+ 938 - 0
components/easylvgl/lvgl9/src/core/lv_obj_style_gen.h

@@ -0,0 +1,938 @@
+
+/*
+ **********************************************************************
+ *                            DO NOT EDIT
+ * This file is automatically generated by "style_api_gen.py"
+ **********************************************************************
+ */
+
+
+#ifndef LV_OBJ_STYLE_GEN_H
+#define LV_OBJ_STYLE_GEN_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include "../misc/lv_area.h"
+#include "../misc/lv_style.h"
+#include "../core/lv_obj_style.h"
+#include "../misc/lv_types.h"
+
+static inline int32_t lv_obj_get_style_width(const lv_obj_t * obj, lv_part_t part)
+{
+    lv_style_value_t v = lv_obj_get_style_prop(obj, part, LV_STYLE_WIDTH);
+    return (int32_t)v.num;
+}
+
+static inline int32_t lv_obj_get_style_min_width(const lv_obj_t * obj, lv_part_t part)
+{
+    lv_style_value_t v = lv_obj_get_style_prop(obj, part, LV_STYLE_MIN_WIDTH);
+    return (int32_t)v.num;
+}
+
+static inline int32_t lv_obj_get_style_max_width(const lv_obj_t * obj, lv_part_t part)
+{
+    lv_style_value_t v = lv_obj_get_style_prop(obj, part, LV_STYLE_MAX_WIDTH);
+    return (int32_t)v.num;
+}
+
+static inline int32_t lv_obj_get_style_height(const lv_obj_t * obj, lv_part_t part)
+{
+    lv_style_value_t v = lv_obj_get_style_prop(obj, part, LV_STYLE_HEIGHT);
+    return (int32_t)v.num;
+}
+
+static inline int32_t lv_obj_get_style_min_height(const lv_obj_t * obj, lv_part_t part)
+{
+    lv_style_value_t v = lv_obj_get_style_prop(obj, part, LV_STYLE_MIN_HEIGHT);
+    return (int32_t)v.num;
+}
+
+static inline int32_t lv_obj_get_style_max_height(const lv_obj_t * obj, lv_part_t part)
+{
+    lv_style_value_t v = lv_obj_get_style_prop(obj, part, LV_STYLE_MAX_HEIGHT);
+    return (int32_t)v.num;
+}
+
+static inline int32_t lv_obj_get_style_length(const lv_obj_t * obj, lv_part_t part)
+{
+    lv_style_value_t v = lv_obj_get_style_prop(obj, part, LV_STYLE_LENGTH);
+    return (int32_t)v.num;
+}
+
+static inline int32_t lv_obj_get_style_x(const lv_obj_t * obj, lv_part_t part)
+{
+    lv_style_value_t v = lv_obj_get_style_prop(obj, part, LV_STYLE_X);
+    return (int32_t)v.num;
+}
+
+static inline int32_t lv_obj_get_style_y(const lv_obj_t * obj, lv_part_t part)
+{
+    lv_style_value_t v = lv_obj_get_style_prop(obj, part, LV_STYLE_Y);
+    return (int32_t)v.num;
+}
+
+static inline lv_align_t lv_obj_get_style_align(const lv_obj_t * obj, lv_part_t part)
+{
+    lv_style_value_t v = lv_obj_get_style_prop(obj, part, LV_STYLE_ALIGN);
+    return (lv_align_t)v.num;
+}
+
+static inline int32_t lv_obj_get_style_transform_width(const lv_obj_t * obj, lv_part_t part)
+{
+    lv_style_value_t v = lv_obj_get_style_prop(obj, part, LV_STYLE_TRANSFORM_WIDTH);
+    return (int32_t)v.num;
+}
+
+static inline int32_t lv_obj_get_style_transform_height(const lv_obj_t * obj, lv_part_t part)
+{
+    lv_style_value_t v = lv_obj_get_style_prop(obj, part, LV_STYLE_TRANSFORM_HEIGHT);
+    return (int32_t)v.num;
+}
+
+static inline int32_t lv_obj_get_style_translate_x(const lv_obj_t * obj, lv_part_t part)
+{
+    lv_style_value_t v = lv_obj_get_style_prop(obj, part, LV_STYLE_TRANSLATE_X);
+    return (int32_t)v.num;
+}
+
+static inline int32_t lv_obj_get_style_translate_y(const lv_obj_t * obj, lv_part_t part)
+{
+    lv_style_value_t v = lv_obj_get_style_prop(obj, part, LV_STYLE_TRANSLATE_Y);
+    return (int32_t)v.num;
+}
+
+static inline int32_t lv_obj_get_style_translate_radial(const lv_obj_t * obj, lv_part_t part)
+{
+    lv_style_value_t v = lv_obj_get_style_prop(obj, part, LV_STYLE_TRANSLATE_RADIAL);
+    return (int32_t)v.num;
+}
+
+static inline int32_t lv_obj_get_style_transform_scale_x(const lv_obj_t * obj, lv_part_t part)
+{
+    lv_style_value_t v = lv_obj_get_style_prop(obj, part, LV_STYLE_TRANSFORM_SCALE_X);
+    return (int32_t)v.num;
+}
+
+static inline int32_t lv_obj_get_style_transform_scale_y(const lv_obj_t * obj, lv_part_t part)
+{
+    lv_style_value_t v = lv_obj_get_style_prop(obj, part, LV_STYLE_TRANSFORM_SCALE_Y);
+    return (int32_t)v.num;
+}
+
+static inline int32_t lv_obj_get_style_transform_rotation(const lv_obj_t * obj, lv_part_t part)
+{
+    lv_style_value_t v = lv_obj_get_style_prop(obj, part, LV_STYLE_TRANSFORM_ROTATION);
+    return (int32_t)v.num;
+}
+
+static inline int32_t lv_obj_get_style_transform_pivot_x(const lv_obj_t * obj, lv_part_t part)
+{
+    lv_style_value_t v = lv_obj_get_style_prop(obj, part, LV_STYLE_TRANSFORM_PIVOT_X);
+    return (int32_t)v.num;
+}
+
+static inline int32_t lv_obj_get_style_transform_pivot_y(const lv_obj_t * obj, lv_part_t part)
+{
+    lv_style_value_t v = lv_obj_get_style_prop(obj, part, LV_STYLE_TRANSFORM_PIVOT_Y);
+    return (int32_t)v.num;
+}
+
+static inline int32_t lv_obj_get_style_transform_skew_x(const lv_obj_t * obj, lv_part_t part)
+{
+    lv_style_value_t v = lv_obj_get_style_prop(obj, part, LV_STYLE_TRANSFORM_SKEW_X);
+    return (int32_t)v.num;
+}
+
+static inline int32_t lv_obj_get_style_transform_skew_y(const lv_obj_t * obj, lv_part_t part)
+{
+    lv_style_value_t v = lv_obj_get_style_prop(obj, part, LV_STYLE_TRANSFORM_SKEW_Y);
+    return (int32_t)v.num;
+}
+
+static inline int32_t lv_obj_get_style_pad_top(const lv_obj_t * obj, lv_part_t part)
+{
+    lv_style_value_t v = lv_obj_get_style_prop(obj, part, LV_STYLE_PAD_TOP);
+    return (int32_t)v.num;
+}
+
+static inline int32_t lv_obj_get_style_pad_bottom(const lv_obj_t * obj, lv_part_t part)
+{
+    lv_style_value_t v = lv_obj_get_style_prop(obj, part, LV_STYLE_PAD_BOTTOM);
+    return (int32_t)v.num;
+}
+
+static inline int32_t lv_obj_get_style_pad_left(const lv_obj_t * obj, lv_part_t part)
+{
+    lv_style_value_t v = lv_obj_get_style_prop(obj, part, LV_STYLE_PAD_LEFT);
+    return (int32_t)v.num;
+}
+
+static inline int32_t lv_obj_get_style_pad_right(const lv_obj_t * obj, lv_part_t part)
+{
+    lv_style_value_t v = lv_obj_get_style_prop(obj, part, LV_STYLE_PAD_RIGHT);
+    return (int32_t)v.num;
+}
+
+static inline int32_t lv_obj_get_style_pad_row(const lv_obj_t * obj, lv_part_t part)
+{
+    lv_style_value_t v = lv_obj_get_style_prop(obj, part, LV_STYLE_PAD_ROW);
+    return (int32_t)v.num;
+}
+
+static inline int32_t lv_obj_get_style_pad_column(const lv_obj_t * obj, lv_part_t part)
+{
+    lv_style_value_t v = lv_obj_get_style_prop(obj, part, LV_STYLE_PAD_COLUMN);
+    return (int32_t)v.num;
+}
+
+static inline int32_t lv_obj_get_style_pad_radial(const lv_obj_t * obj, lv_part_t part)
+{
+    lv_style_value_t v = lv_obj_get_style_prop(obj, part, LV_STYLE_PAD_RADIAL);
+    return (int32_t)v.num;
+}
+
+static inline int32_t lv_obj_get_style_margin_top(const lv_obj_t * obj, lv_part_t part)
+{
+    lv_style_value_t v = lv_obj_get_style_prop(obj, part, LV_STYLE_MARGIN_TOP);
+    return (int32_t)v.num;
+}
+
+static inline int32_t lv_obj_get_style_margin_bottom(const lv_obj_t * obj, lv_part_t part)
+{
+    lv_style_value_t v = lv_obj_get_style_prop(obj, part, LV_STYLE_MARGIN_BOTTOM);
+    return (int32_t)v.num;
+}
+
+static inline int32_t lv_obj_get_style_margin_left(const lv_obj_t * obj, lv_part_t part)
+{
+    lv_style_value_t v = lv_obj_get_style_prop(obj, part, LV_STYLE_MARGIN_LEFT);
+    return (int32_t)v.num;
+}
+
+static inline int32_t lv_obj_get_style_margin_right(const lv_obj_t * obj, lv_part_t part)
+{
+    lv_style_value_t v = lv_obj_get_style_prop(obj, part, LV_STYLE_MARGIN_RIGHT);
+    return (int32_t)v.num;
+}
+
+static inline lv_color_t lv_obj_get_style_bg_color(const lv_obj_t * obj, lv_part_t part)
+{
+    lv_style_value_t v = lv_obj_get_style_prop(obj, part, LV_STYLE_BG_COLOR);
+    return v.color;
+}
+
+static inline lv_color_t lv_obj_get_style_bg_color_filtered(const lv_obj_t * obj, lv_part_t part)
+{
+    lv_style_value_t v = lv_obj_style_apply_color_filter(obj, part, lv_obj_get_style_prop(obj, part, LV_STYLE_BG_COLOR));
+    return v.color;
+}
+
+static inline lv_opa_t lv_obj_get_style_bg_opa(const lv_obj_t * obj, lv_part_t part)
+{
+    lv_style_value_t v = lv_obj_get_style_prop(obj, part, LV_STYLE_BG_OPA);
+    return (lv_opa_t)v.num;
+}
+
+static inline lv_color_t lv_obj_get_style_bg_grad_color(const lv_obj_t * obj, lv_part_t part)
+{
+    lv_style_value_t v = lv_obj_get_style_prop(obj, part, LV_STYLE_BG_GRAD_COLOR);
+    return v.color;
+}
+
+static inline lv_color_t lv_obj_get_style_bg_grad_color_filtered(const lv_obj_t * obj, lv_part_t part)
+{
+    lv_style_value_t v = lv_obj_style_apply_color_filter(obj, part, lv_obj_get_style_prop(obj, part, LV_STYLE_BG_GRAD_COLOR));
+    return v.color;
+}
+
+static inline lv_grad_dir_t lv_obj_get_style_bg_grad_dir(const lv_obj_t * obj, lv_part_t part)
+{
+    lv_style_value_t v = lv_obj_get_style_prop(obj, part, LV_STYLE_BG_GRAD_DIR);
+    return (lv_grad_dir_t)v.num;
+}
+
+static inline int32_t lv_obj_get_style_bg_main_stop(const lv_obj_t * obj, lv_part_t part)
+{
+    lv_style_value_t v = lv_obj_get_style_prop(obj, part, LV_STYLE_BG_MAIN_STOP);
+    return (int32_t)v.num;
+}
+
+static inline int32_t lv_obj_get_style_bg_grad_stop(const lv_obj_t * obj, lv_part_t part)
+{
+    lv_style_value_t v = lv_obj_get_style_prop(obj, part, LV_STYLE_BG_GRAD_STOP);
+    return (int32_t)v.num;
+}
+
+static inline lv_opa_t lv_obj_get_style_bg_main_opa(const lv_obj_t * obj, lv_part_t part)
+{
+    lv_style_value_t v = lv_obj_get_style_prop(obj, part, LV_STYLE_BG_MAIN_OPA);
+    return (lv_opa_t)v.num;
+}
+
+static inline lv_opa_t lv_obj_get_style_bg_grad_opa(const lv_obj_t * obj, lv_part_t part)
+{
+    lv_style_value_t v = lv_obj_get_style_prop(obj, part, LV_STYLE_BG_GRAD_OPA);
+    return (lv_opa_t)v.num;
+}
+
+static inline const lv_grad_dsc_t * lv_obj_get_style_bg_grad(const lv_obj_t * obj, lv_part_t part)
+{
+    lv_style_value_t v = lv_obj_get_style_prop(obj, part, LV_STYLE_BG_GRAD);
+    return (const lv_grad_dsc_t *)v.ptr;
+}
+
+static inline const void * lv_obj_get_style_bg_image_src(const lv_obj_t * obj, lv_part_t part)
+{
+    lv_style_value_t v = lv_obj_get_style_prop(obj, part, LV_STYLE_BG_IMAGE_SRC);
+    return (const void *)v.ptr;
+}
+
+static inline lv_opa_t lv_obj_get_style_bg_image_opa(const lv_obj_t * obj, lv_part_t part)
+{
+    lv_style_value_t v = lv_obj_get_style_prop(obj, part, LV_STYLE_BG_IMAGE_OPA);
+    return (lv_opa_t)v.num;
+}
+
+static inline lv_color_t lv_obj_get_style_bg_image_recolor(const lv_obj_t * obj, lv_part_t part)
+{
+    lv_style_value_t v = lv_obj_get_style_prop(obj, part, LV_STYLE_BG_IMAGE_RECOLOR);
+    return v.color;
+}
+
+static inline lv_color_t lv_obj_get_style_bg_image_recolor_filtered(const lv_obj_t * obj, lv_part_t part)
+{
+    lv_style_value_t v = lv_obj_style_apply_color_filter(obj, part, lv_obj_get_style_prop(obj, part, LV_STYLE_BG_IMAGE_RECOLOR));
+    return v.color;
+}
+
+static inline lv_opa_t lv_obj_get_style_bg_image_recolor_opa(const lv_obj_t * obj, lv_part_t part)
+{
+    lv_style_value_t v = lv_obj_get_style_prop(obj, part, LV_STYLE_BG_IMAGE_RECOLOR_OPA);
+    return (lv_opa_t)v.num;
+}
+
+static inline bool lv_obj_get_style_bg_image_tiled(const lv_obj_t * obj, lv_part_t part)
+{
+    lv_style_value_t v = lv_obj_get_style_prop(obj, part, LV_STYLE_BG_IMAGE_TILED);
+    return (bool)v.num;
+}
+
+static inline lv_color_t lv_obj_get_style_border_color(const lv_obj_t * obj, lv_part_t part)
+{
+    lv_style_value_t v = lv_obj_get_style_prop(obj, part, LV_STYLE_BORDER_COLOR);
+    return v.color;
+}
+
+static inline lv_color_t lv_obj_get_style_border_color_filtered(const lv_obj_t * obj, lv_part_t part)
+{
+    lv_style_value_t v = lv_obj_style_apply_color_filter(obj, part, lv_obj_get_style_prop(obj, part, LV_STYLE_BORDER_COLOR));
+    return v.color;
+}
+
+static inline lv_opa_t lv_obj_get_style_border_opa(const lv_obj_t * obj, lv_part_t part)
+{
+    lv_style_value_t v = lv_obj_get_style_prop(obj, part, LV_STYLE_BORDER_OPA);
+    return (lv_opa_t)v.num;
+}
+
+static inline int32_t lv_obj_get_style_border_width(const lv_obj_t * obj, lv_part_t part)
+{
+    lv_style_value_t v = lv_obj_get_style_prop(obj, part, LV_STYLE_BORDER_WIDTH);
+    return (int32_t)v.num;
+}
+
+static inline lv_border_side_t lv_obj_get_style_border_side(const lv_obj_t * obj, lv_part_t part)
+{
+    lv_style_value_t v = lv_obj_get_style_prop(obj, part, LV_STYLE_BORDER_SIDE);
+    return (lv_border_side_t)v.num;
+}
+
+static inline bool lv_obj_get_style_border_post(const lv_obj_t * obj, lv_part_t part)
+{
+    lv_style_value_t v = lv_obj_get_style_prop(obj, part, LV_STYLE_BORDER_POST);
+    return (bool)v.num;
+}
+
+static inline int32_t lv_obj_get_style_outline_width(const lv_obj_t * obj, lv_part_t part)
+{
+    lv_style_value_t v = lv_obj_get_style_prop(obj, part, LV_STYLE_OUTLINE_WIDTH);
+    return (int32_t)v.num;
+}
+
+static inline lv_color_t lv_obj_get_style_outline_color(const lv_obj_t * obj, lv_part_t part)
+{
+    lv_style_value_t v = lv_obj_get_style_prop(obj, part, LV_STYLE_OUTLINE_COLOR);
+    return v.color;
+}
+
+static inline lv_color_t lv_obj_get_style_outline_color_filtered(const lv_obj_t * obj, lv_part_t part)
+{
+    lv_style_value_t v = lv_obj_style_apply_color_filter(obj, part, lv_obj_get_style_prop(obj, part, LV_STYLE_OUTLINE_COLOR));
+    return v.color;
+}
+
+static inline lv_opa_t lv_obj_get_style_outline_opa(const lv_obj_t * obj, lv_part_t part)
+{
+    lv_style_value_t v = lv_obj_get_style_prop(obj, part, LV_STYLE_OUTLINE_OPA);
+    return (lv_opa_t)v.num;
+}
+
+static inline int32_t lv_obj_get_style_outline_pad(const lv_obj_t * obj, lv_part_t part)
+{
+    lv_style_value_t v = lv_obj_get_style_prop(obj, part, LV_STYLE_OUTLINE_PAD);
+    return (int32_t)v.num;
+}
+
+static inline int32_t lv_obj_get_style_shadow_width(const lv_obj_t * obj, lv_part_t part)
+{
+    lv_style_value_t v = lv_obj_get_style_prop(obj, part, LV_STYLE_SHADOW_WIDTH);
+    return (int32_t)v.num;
+}
+
+static inline int32_t lv_obj_get_style_shadow_offset_x(const lv_obj_t * obj, lv_part_t part)
+{
+    lv_style_value_t v = lv_obj_get_style_prop(obj, part, LV_STYLE_SHADOW_OFFSET_X);
+    return (int32_t)v.num;
+}
+
+static inline int32_t lv_obj_get_style_shadow_offset_y(const lv_obj_t * obj, lv_part_t part)
+{
+    lv_style_value_t v = lv_obj_get_style_prop(obj, part, LV_STYLE_SHADOW_OFFSET_Y);
+    return (int32_t)v.num;
+}
+
+static inline int32_t lv_obj_get_style_shadow_spread(const lv_obj_t * obj, lv_part_t part)
+{
+    lv_style_value_t v = lv_obj_get_style_prop(obj, part, LV_STYLE_SHADOW_SPREAD);
+    return (int32_t)v.num;
+}
+
+static inline lv_color_t lv_obj_get_style_shadow_color(const lv_obj_t * obj, lv_part_t part)
+{
+    lv_style_value_t v = lv_obj_get_style_prop(obj, part, LV_STYLE_SHADOW_COLOR);
+    return v.color;
+}
+
+static inline lv_color_t lv_obj_get_style_shadow_color_filtered(const lv_obj_t * obj, lv_part_t part)
+{
+    lv_style_value_t v = lv_obj_style_apply_color_filter(obj, part, lv_obj_get_style_prop(obj, part, LV_STYLE_SHADOW_COLOR));
+    return v.color;
+}
+
+static inline lv_opa_t lv_obj_get_style_shadow_opa(const lv_obj_t * obj, lv_part_t part)
+{
+    lv_style_value_t v = lv_obj_get_style_prop(obj, part, LV_STYLE_SHADOW_OPA);
+    return (lv_opa_t)v.num;
+}
+
+static inline lv_opa_t lv_obj_get_style_image_opa(const lv_obj_t * obj, lv_part_t part)
+{
+    lv_style_value_t v = lv_obj_get_style_prop(obj, part, LV_STYLE_IMAGE_OPA);
+    return (lv_opa_t)v.num;
+}
+
+static inline lv_color_t lv_obj_get_style_image_recolor(const lv_obj_t * obj, lv_part_t part)
+{
+    lv_style_value_t v = lv_obj_get_style_prop(obj, part, LV_STYLE_IMAGE_RECOLOR);
+    return v.color;
+}
+
+static inline lv_color_t lv_obj_get_style_image_recolor_filtered(const lv_obj_t * obj, lv_part_t part)
+{
+    lv_style_value_t v = lv_obj_style_apply_color_filter(obj, part, lv_obj_get_style_prop(obj, part, LV_STYLE_IMAGE_RECOLOR));
+    return v.color;
+}
+
+static inline lv_opa_t lv_obj_get_style_image_recolor_opa(const lv_obj_t * obj, lv_part_t part)
+{
+    lv_style_value_t v = lv_obj_get_style_prop(obj, part, LV_STYLE_IMAGE_RECOLOR_OPA);
+    return (lv_opa_t)v.num;
+}
+
+static inline const lv_image_colorkey_t * lv_obj_get_style_image_colorkey(const lv_obj_t * obj, lv_part_t part)
+{
+    lv_style_value_t v = lv_obj_get_style_prop(obj, part, LV_STYLE_IMAGE_COLORKEY);
+    return (const lv_image_colorkey_t *)v.ptr;
+}
+
+static inline int32_t lv_obj_get_style_line_width(const lv_obj_t * obj, lv_part_t part)
+{
+    lv_style_value_t v = lv_obj_get_style_prop(obj, part, LV_STYLE_LINE_WIDTH);
+    return (int32_t)v.num;
+}
+
+static inline int32_t lv_obj_get_style_line_dash_width(const lv_obj_t * obj, lv_part_t part)
+{
+    lv_style_value_t v = lv_obj_get_style_prop(obj, part, LV_STYLE_LINE_DASH_WIDTH);
+    return (int32_t)v.num;
+}
+
+static inline int32_t lv_obj_get_style_line_dash_gap(const lv_obj_t * obj, lv_part_t part)
+{
+    lv_style_value_t v = lv_obj_get_style_prop(obj, part, LV_STYLE_LINE_DASH_GAP);
+    return (int32_t)v.num;
+}
+
+static inline bool lv_obj_get_style_line_rounded(const lv_obj_t * obj, lv_part_t part)
+{
+    lv_style_value_t v = lv_obj_get_style_prop(obj, part, LV_STYLE_LINE_ROUNDED);
+    return (bool)v.num;
+}
+
+static inline lv_color_t lv_obj_get_style_line_color(const lv_obj_t * obj, lv_part_t part)
+{
+    lv_style_value_t v = lv_obj_get_style_prop(obj, part, LV_STYLE_LINE_COLOR);
+    return v.color;
+}
+
+static inline lv_color_t lv_obj_get_style_line_color_filtered(const lv_obj_t * obj, lv_part_t part)
+{
+    lv_style_value_t v = lv_obj_style_apply_color_filter(obj, part, lv_obj_get_style_prop(obj, part, LV_STYLE_LINE_COLOR));
+    return v.color;
+}
+
+static inline lv_opa_t lv_obj_get_style_line_opa(const lv_obj_t * obj, lv_part_t part)
+{
+    lv_style_value_t v = lv_obj_get_style_prop(obj, part, LV_STYLE_LINE_OPA);
+    return (lv_opa_t)v.num;
+}
+
+static inline int32_t lv_obj_get_style_arc_width(const lv_obj_t * obj, lv_part_t part)
+{
+    lv_style_value_t v = lv_obj_get_style_prop(obj, part, LV_STYLE_ARC_WIDTH);
+    return (int32_t)v.num;
+}
+
+static inline bool lv_obj_get_style_arc_rounded(const lv_obj_t * obj, lv_part_t part)
+{
+    lv_style_value_t v = lv_obj_get_style_prop(obj, part, LV_STYLE_ARC_ROUNDED);
+    return (bool)v.num;
+}
+
+static inline lv_color_t lv_obj_get_style_arc_color(const lv_obj_t * obj, lv_part_t part)
+{
+    lv_style_value_t v = lv_obj_get_style_prop(obj, part, LV_STYLE_ARC_COLOR);
+    return v.color;
+}
+
+static inline lv_color_t lv_obj_get_style_arc_color_filtered(const lv_obj_t * obj, lv_part_t part)
+{
+    lv_style_value_t v = lv_obj_style_apply_color_filter(obj, part, lv_obj_get_style_prop(obj, part, LV_STYLE_ARC_COLOR));
+    return v.color;
+}
+
+static inline lv_opa_t lv_obj_get_style_arc_opa(const lv_obj_t * obj, lv_part_t part)
+{
+    lv_style_value_t v = lv_obj_get_style_prop(obj, part, LV_STYLE_ARC_OPA);
+    return (lv_opa_t)v.num;
+}
+
+static inline const void * lv_obj_get_style_arc_image_src(const lv_obj_t * obj, lv_part_t part)
+{
+    lv_style_value_t v = lv_obj_get_style_prop(obj, part, LV_STYLE_ARC_IMAGE_SRC);
+    return (const void *)v.ptr;
+}
+
+static inline lv_color_t lv_obj_get_style_text_color(const lv_obj_t * obj, lv_part_t part)
+{
+    lv_style_value_t v = lv_obj_get_style_prop(obj, part, LV_STYLE_TEXT_COLOR);
+    return v.color;
+}
+
+static inline lv_color_t lv_obj_get_style_text_color_filtered(const lv_obj_t * obj, lv_part_t part)
+{
+    lv_style_value_t v = lv_obj_style_apply_color_filter(obj, part, lv_obj_get_style_prop(obj, part, LV_STYLE_TEXT_COLOR));
+    return v.color;
+}
+
+static inline lv_opa_t lv_obj_get_style_text_opa(const lv_obj_t * obj, lv_part_t part)
+{
+    lv_style_value_t v = lv_obj_get_style_prop(obj, part, LV_STYLE_TEXT_OPA);
+    return (lv_opa_t)v.num;
+}
+
+static inline const lv_font_t * lv_obj_get_style_text_font(const lv_obj_t * obj, lv_part_t part)
+{
+    lv_style_value_t v = lv_obj_get_style_prop(obj, part, LV_STYLE_TEXT_FONT);
+    return (const lv_font_t *)v.ptr;
+}
+
+static inline int32_t lv_obj_get_style_text_letter_space(const lv_obj_t * obj, lv_part_t part)
+{
+    lv_style_value_t v = lv_obj_get_style_prop(obj, part, LV_STYLE_TEXT_LETTER_SPACE);
+    return (int32_t)v.num;
+}
+
+static inline int32_t lv_obj_get_style_text_line_space(const lv_obj_t * obj, lv_part_t part)
+{
+    lv_style_value_t v = lv_obj_get_style_prop(obj, part, LV_STYLE_TEXT_LINE_SPACE);
+    return (int32_t)v.num;
+}
+
+static inline lv_text_decor_t lv_obj_get_style_text_decor(const lv_obj_t * obj, lv_part_t part)
+{
+    lv_style_value_t v = lv_obj_get_style_prop(obj, part, LV_STYLE_TEXT_DECOR);
+    return (lv_text_decor_t)v.num;
+}
+
+static inline lv_text_align_t lv_obj_get_style_text_align(const lv_obj_t * obj, lv_part_t part)
+{
+    lv_style_value_t v = lv_obj_get_style_prop(obj, part, LV_STYLE_TEXT_ALIGN);
+    return (lv_text_align_t)v.num;
+}
+
+static inline lv_color_t lv_obj_get_style_text_outline_stroke_color(const lv_obj_t * obj, lv_part_t part)
+{
+    lv_style_value_t v = lv_obj_get_style_prop(obj, part, LV_STYLE_TEXT_OUTLINE_STROKE_COLOR);
+    return v.color;
+}
+
+static inline lv_color_t lv_obj_get_style_text_outline_stroke_color_filtered(const lv_obj_t * obj, lv_part_t part)
+{
+    lv_style_value_t v = lv_obj_style_apply_color_filter(obj, part, lv_obj_get_style_prop(obj, part, LV_STYLE_TEXT_OUTLINE_STROKE_COLOR));
+    return v.color;
+}
+
+static inline int32_t lv_obj_get_style_text_outline_stroke_width(const lv_obj_t * obj, lv_part_t part)
+{
+    lv_style_value_t v = lv_obj_get_style_prop(obj, part, LV_STYLE_TEXT_OUTLINE_STROKE_WIDTH);
+    return (int32_t)v.num;
+}
+
+static inline lv_opa_t lv_obj_get_style_text_outline_stroke_opa(const lv_obj_t * obj, lv_part_t part)
+{
+    lv_style_value_t v = lv_obj_get_style_prop(obj, part, LV_STYLE_TEXT_OUTLINE_STROKE_OPA);
+    return (lv_opa_t)v.num;
+}
+
+static inline int32_t lv_obj_get_style_radius(const lv_obj_t * obj, lv_part_t part)
+{
+    lv_style_value_t v = lv_obj_get_style_prop(obj, part, LV_STYLE_RADIUS);
+    return (int32_t)v.num;
+}
+
+static inline int32_t lv_obj_get_style_radial_offset(const lv_obj_t * obj, lv_part_t part)
+{
+    lv_style_value_t v = lv_obj_get_style_prop(obj, part, LV_STYLE_RADIAL_OFFSET);
+    return (int32_t)v.num;
+}
+
+static inline bool lv_obj_get_style_clip_corner(const lv_obj_t * obj, lv_part_t part)
+{
+    lv_style_value_t v = lv_obj_get_style_prop(obj, part, LV_STYLE_CLIP_CORNER);
+    return (bool)v.num;
+}
+
+static inline lv_opa_t lv_obj_get_style_opa(const lv_obj_t * obj, lv_part_t part)
+{
+    lv_style_value_t v = lv_obj_get_style_prop(obj, part, LV_STYLE_OPA);
+    return (lv_opa_t)v.num;
+}
+
+static inline lv_opa_t lv_obj_get_style_opa_layered(const lv_obj_t * obj, lv_part_t part)
+{
+    lv_style_value_t v = lv_obj_get_style_prop(obj, part, LV_STYLE_OPA_LAYERED);
+    return (lv_opa_t)v.num;
+}
+
+static inline const lv_color_filter_dsc_t * lv_obj_get_style_color_filter_dsc(const lv_obj_t * obj, lv_part_t part)
+{
+    lv_style_value_t v = lv_obj_get_style_prop(obj, part, LV_STYLE_COLOR_FILTER_DSC);
+    return (const lv_color_filter_dsc_t *)v.ptr;
+}
+
+static inline lv_opa_t lv_obj_get_style_color_filter_opa(const lv_obj_t * obj, lv_part_t part)
+{
+    lv_style_value_t v = lv_obj_get_style_prop(obj, part, LV_STYLE_COLOR_FILTER_OPA);
+    return (lv_opa_t)v.num;
+}
+
+static inline lv_color_t lv_obj_get_style_recolor(const lv_obj_t * obj, lv_part_t part)
+{
+    lv_style_value_t v = lv_obj_get_style_prop(obj, part, LV_STYLE_RECOLOR);
+    return v.color;
+}
+
+static inline lv_opa_t lv_obj_get_style_recolor_opa(const lv_obj_t * obj, lv_part_t part)
+{
+    lv_style_value_t v = lv_obj_get_style_prop(obj, part, LV_STYLE_RECOLOR_OPA);
+    return (lv_opa_t)v.num;
+}
+
+static inline const lv_anim_t * lv_obj_get_style_anim(const lv_obj_t * obj, lv_part_t part)
+{
+    lv_style_value_t v = lv_obj_get_style_prop(obj, part, LV_STYLE_ANIM);
+    return (const lv_anim_t *)v.ptr;
+}
+
+static inline uint32_t lv_obj_get_style_anim_duration(const lv_obj_t * obj, lv_part_t part)
+{
+    lv_style_value_t v = lv_obj_get_style_prop(obj, part, LV_STYLE_ANIM_DURATION);
+    return (uint32_t)v.num;
+}
+
+static inline const lv_style_transition_dsc_t * lv_obj_get_style_transition(const lv_obj_t * obj, lv_part_t part)
+{
+    lv_style_value_t v = lv_obj_get_style_prop(obj, part, LV_STYLE_TRANSITION);
+    return (const lv_style_transition_dsc_t *)v.ptr;
+}
+
+static inline lv_blend_mode_t lv_obj_get_style_blend_mode(const lv_obj_t * obj, lv_part_t part)
+{
+    lv_style_value_t v = lv_obj_get_style_prop(obj, part, LV_STYLE_BLEND_MODE);
+    return (lv_blend_mode_t)v.num;
+}
+
+static inline uint16_t lv_obj_get_style_layout(const lv_obj_t * obj, lv_part_t part)
+{
+    lv_style_value_t v = lv_obj_get_style_prop(obj, part, LV_STYLE_LAYOUT);
+    return (uint16_t)v.num;
+}
+
+static inline lv_base_dir_t lv_obj_get_style_base_dir(const lv_obj_t * obj, lv_part_t part)
+{
+    lv_style_value_t v = lv_obj_get_style_prop(obj, part, LV_STYLE_BASE_DIR);
+    return (lv_base_dir_t)v.num;
+}
+
+static inline const void * lv_obj_get_style_bitmap_mask_src(const lv_obj_t * obj, lv_part_t part)
+{
+    lv_style_value_t v = lv_obj_get_style_prop(obj, part, LV_STYLE_BITMAP_MASK_SRC);
+    return (const void *)v.ptr;
+}
+
+static inline uint32_t lv_obj_get_style_rotary_sensitivity(const lv_obj_t * obj, lv_part_t part)
+{
+    lv_style_value_t v = lv_obj_get_style_prop(obj, part, LV_STYLE_ROTARY_SENSITIVITY);
+    return (uint32_t)v.num;
+}
+
+#if LV_USE_FLEX
+static inline lv_flex_flow_t lv_obj_get_style_flex_flow(const lv_obj_t * obj, lv_part_t part)
+{
+    lv_style_value_t v = lv_obj_get_style_prop(obj, part, LV_STYLE_FLEX_FLOW);
+    return (lv_flex_flow_t)v.num;
+}
+
+static inline lv_flex_align_t lv_obj_get_style_flex_main_place(const lv_obj_t * obj, lv_part_t part)
+{
+    lv_style_value_t v = lv_obj_get_style_prop(obj, part, LV_STYLE_FLEX_MAIN_PLACE);
+    return (lv_flex_align_t)v.num;
+}
+
+static inline lv_flex_align_t lv_obj_get_style_flex_cross_place(const lv_obj_t * obj, lv_part_t part)
+{
+    lv_style_value_t v = lv_obj_get_style_prop(obj, part, LV_STYLE_FLEX_CROSS_PLACE);
+    return (lv_flex_align_t)v.num;
+}
+
+static inline lv_flex_align_t lv_obj_get_style_flex_track_place(const lv_obj_t * obj, lv_part_t part)
+{
+    lv_style_value_t v = lv_obj_get_style_prop(obj, part, LV_STYLE_FLEX_TRACK_PLACE);
+    return (lv_flex_align_t)v.num;
+}
+
+static inline uint8_t lv_obj_get_style_flex_grow(const lv_obj_t * obj, lv_part_t part)
+{
+    lv_style_value_t v = lv_obj_get_style_prop(obj, part, LV_STYLE_FLEX_GROW);
+    return (uint8_t)v.num;
+}
+
+#endif /*LV_USE_FLEX*/
+
+#if LV_USE_GRID
+static inline const int32_t * lv_obj_get_style_grid_column_dsc_array(const lv_obj_t * obj, lv_part_t part)
+{
+    lv_style_value_t v = lv_obj_get_style_prop(obj, part, LV_STYLE_GRID_COLUMN_DSC_ARRAY);
+    return (const int32_t *)v.ptr;
+}
+
+static inline lv_grid_align_t lv_obj_get_style_grid_column_align(const lv_obj_t * obj, lv_part_t part)
+{
+    lv_style_value_t v = lv_obj_get_style_prop(obj, part, LV_STYLE_GRID_COLUMN_ALIGN);
+    return (lv_grid_align_t)v.num;
+}
+
+static inline const int32_t * lv_obj_get_style_grid_row_dsc_array(const lv_obj_t * obj, lv_part_t part)
+{
+    lv_style_value_t v = lv_obj_get_style_prop(obj, part, LV_STYLE_GRID_ROW_DSC_ARRAY);
+    return (const int32_t *)v.ptr;
+}
+
+static inline lv_grid_align_t lv_obj_get_style_grid_row_align(const lv_obj_t * obj, lv_part_t part)
+{
+    lv_style_value_t v = lv_obj_get_style_prop(obj, part, LV_STYLE_GRID_ROW_ALIGN);
+    return (lv_grid_align_t)v.num;
+}
+
+static inline int32_t lv_obj_get_style_grid_cell_column_pos(const lv_obj_t * obj, lv_part_t part)
+{
+    lv_style_value_t v = lv_obj_get_style_prop(obj, part, LV_STYLE_GRID_CELL_COLUMN_POS);
+    return (int32_t)v.num;
+}
+
+static inline lv_grid_align_t lv_obj_get_style_grid_cell_x_align(const lv_obj_t * obj, lv_part_t part)
+{
+    lv_style_value_t v = lv_obj_get_style_prop(obj, part, LV_STYLE_GRID_CELL_X_ALIGN);
+    return (lv_grid_align_t)v.num;
+}
+
+static inline int32_t lv_obj_get_style_grid_cell_column_span(const lv_obj_t * obj, lv_part_t part)
+{
+    lv_style_value_t v = lv_obj_get_style_prop(obj, part, LV_STYLE_GRID_CELL_COLUMN_SPAN);
+    return (int32_t)v.num;
+}
+
+static inline int32_t lv_obj_get_style_grid_cell_row_pos(const lv_obj_t * obj, lv_part_t part)
+{
+    lv_style_value_t v = lv_obj_get_style_prop(obj, part, LV_STYLE_GRID_CELL_ROW_POS);
+    return (int32_t)v.num;
+}
+
+static inline lv_grid_align_t lv_obj_get_style_grid_cell_y_align(const lv_obj_t * obj, lv_part_t part)
+{
+    lv_style_value_t v = lv_obj_get_style_prop(obj, part, LV_STYLE_GRID_CELL_Y_ALIGN);
+    return (lv_grid_align_t)v.num;
+}
+
+static inline int32_t lv_obj_get_style_grid_cell_row_span(const lv_obj_t * obj, lv_part_t part)
+{
+    lv_style_value_t v = lv_obj_get_style_prop(obj, part, LV_STYLE_GRID_CELL_ROW_SPAN);
+    return (int32_t)v.num;
+}
+
+#endif /*LV_USE_GRID*/
+
+void lv_obj_set_style_width(lv_obj_t * obj, int32_t value, lv_style_selector_t selector);
+void lv_obj_set_style_min_width(lv_obj_t * obj, int32_t value, lv_style_selector_t selector);
+void lv_obj_set_style_max_width(lv_obj_t * obj, int32_t value, lv_style_selector_t selector);
+void lv_obj_set_style_height(lv_obj_t * obj, int32_t value, lv_style_selector_t selector);
+void lv_obj_set_style_min_height(lv_obj_t * obj, int32_t value, lv_style_selector_t selector);
+void lv_obj_set_style_max_height(lv_obj_t * obj, int32_t value, lv_style_selector_t selector);
+void lv_obj_set_style_length(lv_obj_t * obj, int32_t value, lv_style_selector_t selector);
+void lv_obj_set_style_x(lv_obj_t * obj, int32_t value, lv_style_selector_t selector);
+void lv_obj_set_style_y(lv_obj_t * obj, int32_t value, lv_style_selector_t selector);
+void lv_obj_set_style_align(lv_obj_t * obj, lv_align_t value, lv_style_selector_t selector);
+void lv_obj_set_style_transform_width(lv_obj_t * obj, int32_t value, lv_style_selector_t selector);
+void lv_obj_set_style_transform_height(lv_obj_t * obj, int32_t value, lv_style_selector_t selector);
+void lv_obj_set_style_translate_x(lv_obj_t * obj, int32_t value, lv_style_selector_t selector);
+void lv_obj_set_style_translate_y(lv_obj_t * obj, int32_t value, lv_style_selector_t selector);
+void lv_obj_set_style_translate_radial(lv_obj_t * obj, int32_t value, lv_style_selector_t selector);
+void lv_obj_set_style_transform_scale_x(lv_obj_t * obj, int32_t value, lv_style_selector_t selector);
+void lv_obj_set_style_transform_scale_y(lv_obj_t * obj, int32_t value, lv_style_selector_t selector);
+void lv_obj_set_style_transform_rotation(lv_obj_t * obj, int32_t value, lv_style_selector_t selector);
+void lv_obj_set_style_transform_pivot_x(lv_obj_t * obj, int32_t value, lv_style_selector_t selector);
+void lv_obj_set_style_transform_pivot_y(lv_obj_t * obj, int32_t value, lv_style_selector_t selector);
+void lv_obj_set_style_transform_skew_x(lv_obj_t * obj, int32_t value, lv_style_selector_t selector);
+void lv_obj_set_style_transform_skew_y(lv_obj_t * obj, int32_t value, lv_style_selector_t selector);
+void lv_obj_set_style_pad_top(lv_obj_t * obj, int32_t value, lv_style_selector_t selector);
+void lv_obj_set_style_pad_bottom(lv_obj_t * obj, int32_t value, lv_style_selector_t selector);
+void lv_obj_set_style_pad_left(lv_obj_t * obj, int32_t value, lv_style_selector_t selector);
+void lv_obj_set_style_pad_right(lv_obj_t * obj, int32_t value, lv_style_selector_t selector);
+void lv_obj_set_style_pad_row(lv_obj_t * obj, int32_t value, lv_style_selector_t selector);
+void lv_obj_set_style_pad_column(lv_obj_t * obj, int32_t value, lv_style_selector_t selector);
+void lv_obj_set_style_pad_radial(lv_obj_t * obj, int32_t value, lv_style_selector_t selector);
+void lv_obj_set_style_margin_top(lv_obj_t * obj, int32_t value, lv_style_selector_t selector);
+void lv_obj_set_style_margin_bottom(lv_obj_t * obj, int32_t value, lv_style_selector_t selector);
+void lv_obj_set_style_margin_left(lv_obj_t * obj, int32_t value, lv_style_selector_t selector);
+void lv_obj_set_style_margin_right(lv_obj_t * obj, int32_t value, lv_style_selector_t selector);
+void lv_obj_set_style_bg_color(lv_obj_t * obj, lv_color_t value, lv_style_selector_t selector);
+void lv_obj_set_style_bg_opa(lv_obj_t * obj, lv_opa_t value, lv_style_selector_t selector);
+void lv_obj_set_style_bg_grad_color(lv_obj_t * obj, lv_color_t value, lv_style_selector_t selector);
+void lv_obj_set_style_bg_grad_dir(lv_obj_t * obj, lv_grad_dir_t value, lv_style_selector_t selector);
+void lv_obj_set_style_bg_main_stop(lv_obj_t * obj, int32_t value, lv_style_selector_t selector);
+void lv_obj_set_style_bg_grad_stop(lv_obj_t * obj, int32_t value, lv_style_selector_t selector);
+void lv_obj_set_style_bg_main_opa(lv_obj_t * obj, lv_opa_t value, lv_style_selector_t selector);
+void lv_obj_set_style_bg_grad_opa(lv_obj_t * obj, lv_opa_t value, lv_style_selector_t selector);
+void lv_obj_set_style_bg_grad(lv_obj_t * obj, const lv_grad_dsc_t * value, lv_style_selector_t selector);
+void lv_obj_set_style_bg_image_src(lv_obj_t * obj, const void * value, lv_style_selector_t selector);
+void lv_obj_set_style_bg_image_opa(lv_obj_t * obj, lv_opa_t value, lv_style_selector_t selector);
+void lv_obj_set_style_bg_image_recolor(lv_obj_t * obj, lv_color_t value, lv_style_selector_t selector);
+void lv_obj_set_style_bg_image_recolor_opa(lv_obj_t * obj, lv_opa_t value, lv_style_selector_t selector);
+void lv_obj_set_style_bg_image_tiled(lv_obj_t * obj, bool value, lv_style_selector_t selector);
+void lv_obj_set_style_border_color(lv_obj_t * obj, lv_color_t value, lv_style_selector_t selector);
+void lv_obj_set_style_border_opa(lv_obj_t * obj, lv_opa_t value, lv_style_selector_t selector);
+void lv_obj_set_style_border_width(lv_obj_t * obj, int32_t value, lv_style_selector_t selector);
+void lv_obj_set_style_border_side(lv_obj_t * obj, lv_border_side_t value, lv_style_selector_t selector);
+void lv_obj_set_style_border_post(lv_obj_t * obj, bool value, lv_style_selector_t selector);
+void lv_obj_set_style_outline_width(lv_obj_t * obj, int32_t value, lv_style_selector_t selector);
+void lv_obj_set_style_outline_color(lv_obj_t * obj, lv_color_t value, lv_style_selector_t selector);
+void lv_obj_set_style_outline_opa(lv_obj_t * obj, lv_opa_t value, lv_style_selector_t selector);
+void lv_obj_set_style_outline_pad(lv_obj_t * obj, int32_t value, lv_style_selector_t selector);
+void lv_obj_set_style_shadow_width(lv_obj_t * obj, int32_t value, lv_style_selector_t selector);
+void lv_obj_set_style_shadow_offset_x(lv_obj_t * obj, int32_t value, lv_style_selector_t selector);
+void lv_obj_set_style_shadow_offset_y(lv_obj_t * obj, int32_t value, lv_style_selector_t selector);
+void lv_obj_set_style_shadow_spread(lv_obj_t * obj, int32_t value, lv_style_selector_t selector);
+void lv_obj_set_style_shadow_color(lv_obj_t * obj, lv_color_t value, lv_style_selector_t selector);
+void lv_obj_set_style_shadow_opa(lv_obj_t * obj, lv_opa_t value, lv_style_selector_t selector);
+void lv_obj_set_style_image_opa(lv_obj_t * obj, lv_opa_t value, lv_style_selector_t selector);
+void lv_obj_set_style_image_recolor(lv_obj_t * obj, lv_color_t value, lv_style_selector_t selector);
+void lv_obj_set_style_image_recolor_opa(lv_obj_t * obj, lv_opa_t value, lv_style_selector_t selector);
+void lv_obj_set_style_image_colorkey(lv_obj_t * obj, const lv_image_colorkey_t * value, lv_style_selector_t selector);
+void lv_obj_set_style_line_width(lv_obj_t * obj, int32_t value, lv_style_selector_t selector);
+void lv_obj_set_style_line_dash_width(lv_obj_t * obj, int32_t value, lv_style_selector_t selector);
+void lv_obj_set_style_line_dash_gap(lv_obj_t * obj, int32_t value, lv_style_selector_t selector);
+void lv_obj_set_style_line_rounded(lv_obj_t * obj, bool value, lv_style_selector_t selector);
+void lv_obj_set_style_line_color(lv_obj_t * obj, lv_color_t value, lv_style_selector_t selector);
+void lv_obj_set_style_line_opa(lv_obj_t * obj, lv_opa_t value, lv_style_selector_t selector);
+void lv_obj_set_style_arc_width(lv_obj_t * obj, int32_t value, lv_style_selector_t selector);
+void lv_obj_set_style_arc_rounded(lv_obj_t * obj, bool value, lv_style_selector_t selector);
+void lv_obj_set_style_arc_color(lv_obj_t * obj, lv_color_t value, lv_style_selector_t selector);
+void lv_obj_set_style_arc_opa(lv_obj_t * obj, lv_opa_t value, lv_style_selector_t selector);
+void lv_obj_set_style_arc_image_src(lv_obj_t * obj, const void * value, lv_style_selector_t selector);
+void lv_obj_set_style_text_color(lv_obj_t * obj, lv_color_t value, lv_style_selector_t selector);
+void lv_obj_set_style_text_opa(lv_obj_t * obj, lv_opa_t value, lv_style_selector_t selector);
+void lv_obj_set_style_text_font(lv_obj_t * obj, const lv_font_t * value, lv_style_selector_t selector);
+void lv_obj_set_style_text_letter_space(lv_obj_t * obj, int32_t value, lv_style_selector_t selector);
+void lv_obj_set_style_text_line_space(lv_obj_t * obj, int32_t value, lv_style_selector_t selector);
+void lv_obj_set_style_text_decor(lv_obj_t * obj, lv_text_decor_t value, lv_style_selector_t selector);
+void lv_obj_set_style_text_align(lv_obj_t * obj, lv_text_align_t value, lv_style_selector_t selector);
+void lv_obj_set_style_text_outline_stroke_color(lv_obj_t * obj, lv_color_t value, lv_style_selector_t selector);
+void lv_obj_set_style_text_outline_stroke_width(lv_obj_t * obj, int32_t value, lv_style_selector_t selector);
+void lv_obj_set_style_text_outline_stroke_opa(lv_obj_t * obj, lv_opa_t value, lv_style_selector_t selector);
+void lv_obj_set_style_radius(lv_obj_t * obj, int32_t value, lv_style_selector_t selector);
+void lv_obj_set_style_radial_offset(lv_obj_t * obj, int32_t value, lv_style_selector_t selector);
+void lv_obj_set_style_clip_corner(lv_obj_t * obj, bool value, lv_style_selector_t selector);
+void lv_obj_set_style_opa(lv_obj_t * obj, lv_opa_t value, lv_style_selector_t selector);
+void lv_obj_set_style_opa_layered(lv_obj_t * obj, lv_opa_t value, lv_style_selector_t selector);
+void lv_obj_set_style_color_filter_dsc(lv_obj_t * obj, const lv_color_filter_dsc_t * value, lv_style_selector_t selector);
+void lv_obj_set_style_color_filter_opa(lv_obj_t * obj, lv_opa_t value, lv_style_selector_t selector);
+void lv_obj_set_style_recolor(lv_obj_t * obj, lv_color_t value, lv_style_selector_t selector);
+void lv_obj_set_style_recolor_opa(lv_obj_t * obj, lv_opa_t value, lv_style_selector_t selector);
+void lv_obj_set_style_anim(lv_obj_t * obj, const lv_anim_t * value, lv_style_selector_t selector);
+void lv_obj_set_style_anim_duration(lv_obj_t * obj, uint32_t value, lv_style_selector_t selector);
+void lv_obj_set_style_transition(lv_obj_t * obj, const lv_style_transition_dsc_t * value, lv_style_selector_t selector);
+void lv_obj_set_style_blend_mode(lv_obj_t * obj, lv_blend_mode_t value, lv_style_selector_t selector);
+void lv_obj_set_style_layout(lv_obj_t * obj, uint16_t value, lv_style_selector_t selector);
+void lv_obj_set_style_base_dir(lv_obj_t * obj, lv_base_dir_t value, lv_style_selector_t selector);
+void lv_obj_set_style_bitmap_mask_src(lv_obj_t * obj, const void * value, lv_style_selector_t selector);
+void lv_obj_set_style_rotary_sensitivity(lv_obj_t * obj, uint32_t value, lv_style_selector_t selector);
+#if LV_USE_FLEX
+void lv_obj_set_style_flex_flow(lv_obj_t * obj, lv_flex_flow_t value, lv_style_selector_t selector);
+void lv_obj_set_style_flex_main_place(lv_obj_t * obj, lv_flex_align_t value, lv_style_selector_t selector);
+void lv_obj_set_style_flex_cross_place(lv_obj_t * obj, lv_flex_align_t value, lv_style_selector_t selector);
+void lv_obj_set_style_flex_track_place(lv_obj_t * obj, lv_flex_align_t value, lv_style_selector_t selector);
+void lv_obj_set_style_flex_grow(lv_obj_t * obj, uint8_t value, lv_style_selector_t selector);
+#endif /*LV_USE_FLEX*/
+
+#if LV_USE_GRID
+void lv_obj_set_style_grid_column_dsc_array(lv_obj_t * obj, const int32_t * value, lv_style_selector_t selector);
+void lv_obj_set_style_grid_column_align(lv_obj_t * obj, lv_grid_align_t value, lv_style_selector_t selector);
+void lv_obj_set_style_grid_row_dsc_array(lv_obj_t * obj, const int32_t * value, lv_style_selector_t selector);
+void lv_obj_set_style_grid_row_align(lv_obj_t * obj, lv_grid_align_t value, lv_style_selector_t selector);
+void lv_obj_set_style_grid_cell_column_pos(lv_obj_t * obj, int32_t value, lv_style_selector_t selector);
+void lv_obj_set_style_grid_cell_x_align(lv_obj_t * obj, lv_grid_align_t value, lv_style_selector_t selector);
+void lv_obj_set_style_grid_cell_column_span(lv_obj_t * obj, int32_t value, lv_style_selector_t selector);
+void lv_obj_set_style_grid_cell_row_pos(lv_obj_t * obj, int32_t value, lv_style_selector_t selector);
+void lv_obj_set_style_grid_cell_y_align(lv_obj_t * obj, lv_grid_align_t value, lv_style_selector_t selector);
+void lv_obj_set_style_grid_cell_row_span(lv_obj_t * obj, int32_t value, lv_style_selector_t selector);
+#endif /*LV_USE_GRID*/
+
+
+#ifdef __cplusplus
+} /* extern "C" */
+#endif
+
+#endif /* LV_OBJ_STYLE_GEN_H */

+ 96 - 0
components/easylvgl/lvgl9/src/core/lv_obj_style_private.h

@@ -0,0 +1,96 @@
+/**
+ * @file lv_obj_style_private.h
+ *
+ */
+
+#ifndef LV_OBJ_STYLE_PRIVATE_H
+#define LV_OBJ_STYLE_PRIVATE_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/*********************
+ *      INCLUDES
+ *********************/
+
+#include "lv_obj_style.h"
+
+/*********************
+ *      DEFINES
+ *********************/
+
+/**********************
+ *      TYPEDEFS
+ **********************/
+
+struct _lv_obj_style_t {
+    const lv_style_t * style;
+    uint32_t selector : 24;
+    uint32_t is_local : 1;
+    uint32_t is_trans : 1;
+    uint32_t is_disabled : 1;
+};
+
+struct _lv_obj_style_transition_dsc_t {
+    uint16_t time;
+    uint16_t delay;
+    lv_style_selector_t selector;
+    lv_style_prop_t prop;
+    lv_anim_path_cb_t path_cb;
+    void * user_data;
+};
+
+
+/**********************
+ * GLOBAL PROTOTYPES
+ **********************/
+
+/**
+ * Initialize the object related style manager module.
+ * Called by LVGL in `lv_init()`
+ */
+void lv_obj_style_init(void);
+
+/**
+ * Deinitialize the object related style manager module.
+ * Called by LVGL in `lv_deinit()`
+ */
+void lv_obj_style_deinit(void);
+
+/**
+ * Used internally to create a style transition
+ * @param obj
+ * @param part
+ * @param prev_state
+ * @param new_state
+ * @param tr
+ */
+void lv_obj_style_create_transition(lv_obj_t * obj, lv_part_t part, lv_state_t prev_state,
+                                    lv_state_t new_state, const lv_obj_style_transition_dsc_t * tr);
+
+/**
+ * Used internally to compare the appearance of an object in 2 states
+ * @param obj
+ * @param state1
+ * @param state2
+ * @return
+ */
+lv_style_state_cmp_t lv_obj_style_state_compare(lv_obj_t * obj, lv_state_t state1, lv_state_t state2);
+
+/**
+ * Update the layer type of a widget bayed on its current styles.
+ * The result will be stored in `obj->spec_attr->layer_type`
+ * @param obj       the object whose layer should be updated
+ */
+void lv_obj_update_layer_type(lv_obj_t * obj);
+
+/**********************
+ *      MACROS
+ **********************/
+
+#ifdef __cplusplus
+} /*extern "C"*/
+#endif
+
+#endif /*LV_OBJ_STYLE_PRIVATE_H*/

+ 845 - 0
components/easylvgl/lvgl9/src/core/lv_obj_tree.c

@@ -0,0 +1,845 @@
+/**
+ * @file lv_obj_tree.c
+ *
+ */
+
+/*********************
+ *      INCLUDES
+ *********************/
+#include "lv_obj_private.h"
+#include "lv_obj_class_private.h"
+#include "../indev/lv_indev.h"
+#include "../indev/lv_indev_private.h"
+#include "../display/lv_display.h"
+#include "../display/lv_display_private.h"
+#include "../misc/lv_anim_private.h"
+#include "../misc/lv_async.h"
+#include "../core/lv_global.h"
+
+/*********************
+ *      DEFINES
+ *********************/
+#define MY_CLASS (&lv_obj_class)
+#define disp_ll_p &(LV_GLOBAL_DEFAULT()->disp_ll)
+
+#define OBJ_DUMP_STRING_LEN 128
+#define LV_OBJ_NAME_MAX_LEN 128
+
+/**********************
+ *      TYPEDEFS
+ **********************/
+
+/**********************
+ *  STATIC PROTOTYPES
+ **********************/
+static void lv_obj_delete_async_cb(void * obj);
+static void obj_delete_core(lv_obj_t * obj);
+static lv_obj_tree_walk_res_t walk_core(lv_obj_t * obj, lv_obj_tree_walk_cb_t cb, void * user_data);
+static void dump_tree_core(lv_obj_t * obj, int32_t depth);
+static lv_obj_t * lv_obj_get_first_not_deleting_child(lv_obj_t * obj);
+#if LV_USE_OBJ_NAME
+    static lv_obj_t * find_by_name_direct(const lv_obj_t * parent, const char * name, size_t len);
+#endif /*LV_USE_OBJ_NAME*/
+
+/**********************
+ *  STATIC VARIABLES
+ **********************/
+
+/**********************
+ *      MACROS
+ **********************/
+
+/**********************
+ *   GLOBAL FUNCTIONS
+ **********************/
+
+void lv_obj_delete(lv_obj_t * obj)
+{
+    if(obj->is_deleting)
+        return;
+
+    LV_LOG_TRACE("begin (delete %p)", (void *)obj);
+    LV_ASSERT_OBJ(obj, MY_CLASS);
+    lv_obj_invalidate(obj);
+
+    lv_obj_t * par = lv_obj_get_parent(obj);
+
+    lv_display_t * disp = NULL;
+    bool act_screen_del = false;
+    if(par == NULL) {
+        disp = lv_obj_get_display(obj);
+        if(!disp) return;   /*Shouldn't happen*/
+        if(disp->act_scr == obj) act_screen_del = true;
+    }
+
+    obj_delete_core(obj);
+
+    /*Call the ancestor's event handler to the parent to notify it about the child delete*/
+    if(par && !par->is_deleting) {
+        lv_obj_scrollbar_invalidate(par);
+        lv_obj_send_event(par, LV_EVENT_CHILD_CHANGED, NULL);
+        lv_obj_send_event(par, LV_EVENT_CHILD_DELETED, NULL);
+    }
+
+    /*Handle if the active screen was deleted*/
+    if(act_screen_del) {
+        LV_LOG_WARN("the active screen was deleted");
+        disp->act_scr = NULL;
+    }
+
+    LV_ASSERT_MEM_INTEGRITY();
+    LV_LOG_TRACE("finished (delete %p)", (void *)obj);
+}
+
+void lv_obj_clean(lv_obj_t * obj)
+{
+    LV_LOG_TRACE("begin (clean %p)", (void *)obj);
+    LV_ASSERT_OBJ(obj, MY_CLASS);
+
+    lv_obj_invalidate(obj);
+
+    uint32_t cnt = lv_obj_get_child_count(obj);
+    lv_obj_t * child = lv_obj_get_first_not_deleting_child(obj);
+    while(child) {
+        obj_delete_core(child);
+        child = lv_obj_get_first_not_deleting_child(obj);
+    }
+    /*Just to remove scroll animations if any*/
+    lv_obj_scroll_to(obj, 0, 0, LV_ANIM_OFF);
+    if(obj->spec_attr) {
+        obj->spec_attr->scroll.x = 0;
+        obj->spec_attr->scroll.y = 0;
+    }
+
+    if(lv_obj_get_child_count(obj) < cnt) {
+        lv_obj_send_event(obj, LV_EVENT_CHILD_CHANGED, NULL);
+        lv_obj_send_event(obj, LV_EVENT_CHILD_DELETED, NULL);
+    }
+
+    LV_ASSERT_MEM_INTEGRITY();
+
+    LV_LOG_TRACE("finished (clean %p)", (void *)obj);
+}
+
+void lv_obj_delete_delayed(lv_obj_t * obj, uint32_t delay_ms)
+{
+    lv_anim_t a;
+    lv_anim_init(&a);
+    lv_anim_set_var(&a, obj);
+    lv_anim_set_exec_cb(&a, NULL);
+    lv_anim_set_duration(&a, 1);
+    lv_anim_set_delay(&a, delay_ms);
+    lv_anim_set_completed_cb(&a, lv_obj_delete_anim_completed_cb);
+    lv_anim_start(&a);
+}
+
+void lv_obj_delete_anim_completed_cb(lv_anim_t * a)
+{
+    lv_obj_delete(a->var);
+}
+
+void lv_obj_delete_async(lv_obj_t * obj)
+{
+    LV_ASSERT_OBJ(obj, MY_CLASS);
+    lv_async_call(lv_obj_delete_async_cb, obj);
+}
+
+void lv_obj_set_parent(lv_obj_t * obj, lv_obj_t * parent)
+{
+    LV_ASSERT_OBJ(obj, MY_CLASS);
+    LV_ASSERT_OBJ(parent, MY_CLASS);
+
+    if(obj->parent == NULL) {
+        LV_LOG_WARN("Can't set the parent of a screen");
+        return;
+    }
+
+    if(parent == NULL) {
+        LV_LOG_WARN("Can't set parent == NULL to an object");
+        return;
+    }
+
+    if(parent == obj->parent) {
+        return;
+    }
+
+    lv_obj_invalidate(obj);
+
+    lv_obj_allocate_spec_attr(parent);
+
+    lv_obj_t * old_parent = obj->parent;
+    /*Remove the object from the old parent's child list*/
+    int32_t i;
+    for(i = lv_obj_get_index(obj); i <= (int32_t)lv_obj_get_child_count(old_parent) - 2; i++) {
+        old_parent->spec_attr->children[i] = old_parent->spec_attr->children[i + 1];
+    }
+    old_parent->spec_attr->child_cnt--;
+    if(old_parent->spec_attr->child_cnt) {
+        old_parent->spec_attr->children = lv_realloc(old_parent->spec_attr->children,
+                                                     old_parent->spec_attr->child_cnt * (sizeof(lv_obj_t *)));
+    }
+    else {
+        lv_free(old_parent->spec_attr->children);
+        old_parent->spec_attr->children = NULL;
+    }
+
+    /*Add the child to the new parent as the last (newest child)*/
+    parent->spec_attr->child_cnt++;
+    parent->spec_attr->children = lv_realloc(parent->spec_attr->children,
+                                             parent->spec_attr->child_cnt * (sizeof(lv_obj_t *)));
+    parent->spec_attr->children[lv_obj_get_child_count(parent) - 1] = obj;
+
+    obj->parent = parent;
+
+    /*Notify the original parent because one of its children is lost*/
+    lv_obj_scrollbar_invalidate(old_parent);
+    lv_obj_send_event(old_parent, LV_EVENT_CHILD_CHANGED, obj);
+    lv_obj_send_event(old_parent, LV_EVENT_CHILD_DELETED, NULL);
+
+    /*Notify the new parent about the child*/
+    lv_obj_send_event(parent, LV_EVENT_CHILD_CHANGED, obj);
+    lv_obj_send_event(parent, LV_EVENT_CHILD_CREATED, NULL);
+
+    lv_obj_mark_layout_as_dirty(obj);
+
+    lv_obj_invalidate(obj);
+}
+
+void lv_obj_move_to_index(lv_obj_t * obj, int32_t index)
+{
+    LV_ASSERT_OBJ(obj, MY_CLASS);
+
+    /* Check parent validity */
+    lv_obj_t * parent = lv_obj_get_parent(obj);
+    if(!parent) {
+        LV_LOG_WARN("parent is NULL");
+        return;
+    }
+
+    const uint32_t parent_child_count = lv_obj_get_child_count(parent);
+    /* old_index only can be 0 or greater, this point cannot be reached if the parent is not null */
+    const int32_t old_index = lv_obj_get_index(obj);
+    LV_ASSERT(0 <= old_index);
+
+    if(index < 0) {
+        index += parent_child_count;
+    }
+
+    /* Index was negative and the absolute value is greater than parent child count */
+    if((index < 0)
+       /* Index is same or bigger than parent child count */
+       || (index >= (int32_t) parent_child_count)
+       /* If both previous and new index are the same */
+       || (index == old_index)) {
+
+        return;
+    }
+
+    int32_t i = old_index;
+    if(index < old_index) {
+        while(i > index)  {
+            parent->spec_attr->children[i] = parent->spec_attr->children[i - 1];
+            i--;
+        }
+    }
+    else {
+        while(i < index) {
+            parent->spec_attr->children[i] = parent->spec_attr->children[i + 1];
+            i++;
+        }
+    }
+
+    parent->spec_attr->children[index] = obj;
+    lv_obj_send_event(parent, LV_EVENT_CHILD_CHANGED, NULL);
+    lv_obj_invalidate(parent);
+}
+
+void lv_obj_swap(lv_obj_t * obj1, lv_obj_t * obj2)
+{
+    LV_ASSERT_OBJ(obj1, MY_CLASS);
+    LV_ASSERT_OBJ(obj2, MY_CLASS);
+
+    lv_obj_t * parent = lv_obj_get_parent(obj1);
+    lv_obj_t * parent2 = lv_obj_get_parent(obj2);
+
+    uint_fast32_t index1 = lv_obj_get_index(obj1);
+    uint_fast32_t index2 = lv_obj_get_index(obj2);
+
+    lv_obj_send_event(parent2, LV_EVENT_CHILD_DELETED, obj2);
+    lv_obj_send_event(parent, LV_EVENT_CHILD_DELETED, obj1);
+
+    parent->spec_attr->children[index1] = obj2;
+    obj2->parent = parent;
+
+    parent2->spec_attr->children[index2] = obj1;
+    obj1->parent = parent2;
+
+    lv_obj_send_event(parent, LV_EVENT_CHILD_CHANGED, obj2);
+    lv_obj_send_event(parent, LV_EVENT_CHILD_CREATED, obj2);
+    lv_obj_send_event(parent2, LV_EVENT_CHILD_CHANGED, obj1);
+    lv_obj_send_event(parent2, LV_EVENT_CHILD_CREATED, obj1);
+
+    lv_obj_invalidate(parent);
+
+    if(parent != parent2) {
+        lv_obj_invalidate(parent2);
+    }
+    lv_group_swap_obj(obj1, obj2);
+}
+
+lv_obj_t * lv_obj_get_screen(const lv_obj_t * obj)
+{
+    LV_ASSERT_OBJ(obj, MY_CLASS);
+
+    const lv_obj_t * par = obj;
+    const lv_obj_t * act_par;
+
+    do {
+        act_par = par;
+        par = lv_obj_get_parent(act_par);
+    } while(par != NULL);
+
+    return (lv_obj_t *)act_par;
+}
+
+lv_display_t * lv_obj_get_display(const lv_obj_t * obj)
+{
+    LV_ASSERT_OBJ(obj, MY_CLASS);
+
+    const lv_obj_t * scr;
+
+    if(obj->parent == NULL) scr = obj;  /*`obj` is a screen*/
+    else scr = lv_obj_get_screen(obj);  /*get the screen of `obj`*/
+
+    lv_display_t * d;
+    lv_ll_t * disp_head = disp_ll_p;
+    LV_LL_READ(disp_head, d) {
+        uint32_t i;
+        for(i = 0; i < d->screen_cnt; i++) {
+            if(d->screens[i] == scr) return d;
+        }
+    }
+
+    LV_LOG_WARN("No screen found");
+    return NULL;
+}
+
+lv_obj_t * lv_obj_get_parent(const lv_obj_t * obj)
+{
+    if(obj == NULL) return NULL;
+    LV_ASSERT_OBJ(obj, MY_CLASS);
+
+    return obj->parent;
+}
+
+lv_obj_t * lv_obj_get_child(const lv_obj_t * obj, int32_t idx)
+{
+    LV_ASSERT_OBJ(obj, MY_CLASS);
+
+    if(obj->spec_attr == NULL) return NULL;
+
+    uint32_t idu;
+    if(idx < 0) {
+        idx = obj->spec_attr->child_cnt + idx;
+        if(idx < 0) return NULL;
+        idu = (uint32_t) idx;
+    }
+    else {
+        idu = idx;
+    }
+
+    if(idu >= obj->spec_attr->child_cnt) return NULL;
+    else return obj->spec_attr->children[idx];
+}
+
+lv_obj_t * lv_obj_get_child_by_type(const lv_obj_t * obj, int32_t idx, const lv_obj_class_t * class_p)
+{
+    LV_ASSERT_OBJ(obj, MY_CLASS);
+
+    if(obj->spec_attr == NULL) return NULL;
+
+    int32_t i;
+    int32_t cnt = (int32_t)obj->spec_attr->child_cnt;
+    if(idx >= 0) {
+        for(i = 0; i < cnt; i++) {
+            if(obj->spec_attr->children[i]->class_p == class_p) {
+                if(idx == 0) return obj->spec_attr->children[i];
+                else idx--;
+            }
+        }
+    }
+    else {
+        idx++;   /*-1 means the first child*/
+        for(i = cnt - 1; i >= 0; i--) {
+            if(obj->spec_attr->children[i]->class_p == class_p) {
+                if(idx == 0) return obj->spec_attr->children[i];
+                else idx++;
+            }
+        }
+    }
+    return NULL;
+}
+
+lv_obj_t * lv_obj_get_sibling(const lv_obj_t * obj, int32_t idx)
+{
+    lv_obj_t * parent = lv_obj_get_parent(obj);
+    int32_t sibling_idx = (int32_t)lv_obj_get_index(obj) + idx;
+    if(sibling_idx < 0) return NULL;
+
+    return lv_obj_get_child(parent, sibling_idx);
+}
+
+lv_obj_t * lv_obj_get_sibling_by_type(const lv_obj_t * obj, int32_t idx, const lv_obj_class_t * class_p)
+{
+    LV_ASSERT_OBJ(obj, MY_CLASS);
+
+    lv_obj_t * parent = lv_obj_get_parent(obj);
+    int32_t sibling_idx = (int32_t)lv_obj_get_index_by_type(obj, class_p) + idx;
+    if(sibling_idx < 0) return NULL;
+
+    return lv_obj_get_child_by_type(parent, sibling_idx, class_p);
+}
+
+uint32_t lv_obj_get_child_count(const lv_obj_t * obj)
+{
+    LV_ASSERT_OBJ(obj, MY_CLASS);
+    if(obj->spec_attr == NULL) return 0;
+    return obj->spec_attr->child_cnt;
+}
+
+uint32_t lv_obj_get_child_count_by_type(const lv_obj_t * obj, const lv_obj_class_t * class_p)
+{
+    LV_ASSERT_OBJ(obj, MY_CLASS);
+    if(obj->spec_attr == NULL) return 0;
+
+    uint32_t i;
+    uint32_t cnt = 0;
+    for(i = 0; i < obj->spec_attr->child_cnt; i++) {
+        if(obj->spec_attr->children[i]->class_p == class_p) cnt++;
+    }
+    return cnt;
+}
+
+#if LV_USE_OBJ_NAME
+
+void lv_obj_set_name(lv_obj_t * obj, const char * name)
+{
+    LV_ASSERT_OBJ(obj, MY_CLASS);
+
+    lv_obj_allocate_spec_attr(obj);
+
+    if(!obj->spec_attr->name_static && obj->spec_attr->name) lv_free((void *)obj->spec_attr->name);
+
+    if(name == NULL) {
+        obj->spec_attr->name = NULL;
+        obj->spec_attr->name_static = 1;
+    }
+    else {
+        obj->spec_attr->name = lv_strdup(name);
+        obj->spec_attr->name_static = 0;
+    }
+}
+
+void lv_obj_set_name_static(lv_obj_t * obj, const char * name)
+{
+    LV_ASSERT_OBJ(obj, MY_CLASS);
+
+    lv_obj_allocate_spec_attr(obj);
+
+    if(!obj->spec_attr->name_static && obj->spec_attr->name) lv_free((void *)obj->spec_attr->name);
+
+    obj->spec_attr->name = name;
+    obj->spec_attr->name_static = 1;
+}
+
+const char * lv_obj_get_name(const lv_obj_t * obj)
+{
+    LV_ASSERT_OBJ(obj, MY_CLASS);
+
+    if(obj->spec_attr == NULL) return NULL;
+    else return obj->spec_attr->name;
+}
+
+void lv_obj_get_name_resolved(const lv_obj_t * obj, char buf[], size_t buf_size)
+{
+    LV_ASSERT_OBJ(obj, MY_CLASS);
+
+    const char * name = lv_obj_get_name(obj);
+    /*Use a default name which auto-indexing*/
+    char name_buf[LV_OBJ_NAME_MAX_LEN];
+    if(name == NULL) {
+        lv_snprintf(name_buf, sizeof(name_buf), "%s_#", obj->class_p->name);
+        name = name_buf;
+    }
+
+    size_t name_len = lv_strlen(name);
+    lv_obj_t * parent = lv_obj_get_parent(obj);
+
+    /*If the last character is # automatically index the children with the same name start*/
+    if(parent && name_len > 0 && name[name_len - 1] == '#') {
+        uint32_t child_cnt = lv_obj_get_child_count(parent);
+        uint32_t cnt = 0;
+        uint32_t i;
+        for(i = 0; i < child_cnt; i++) {
+            lv_obj_t * child = lv_obj_get_child(parent, i);
+            /*All siblings older siblings are checked, craft the name of this widget*/
+            if(child == obj) {
+                char num_buf[8];
+                size_t num_len;
+                num_len = lv_snprintf(num_buf, sizeof(num_buf), "%d", cnt);
+                /*Is there enough space for the name and the index?*/
+                if(buf_size > name_len + num_len) {
+                    /*E.g. buf = "some_name_", so trim the # from the end*/
+                    lv_strncpy(buf, name, name_len - 1);
+                    lv_strcpy(&buf[name_len - 1], num_buf);
+                }
+                else {
+                    /*Use the name as it is as a fallback*/
+                    lv_strlcpy(buf, obj->spec_attr->name, buf_size);
+                }
+                break;
+            }
+            /*Check the older siblings. IF they start with the same name count them*/
+            else {
+                const char * child_name = lv_obj_get_name(child);
+                if(child_name == NULL) {
+                    /*If the name we are looking for start with the child's class name
+                     *increment the index. E.g. <class_name>_#*/
+                    size_t class_name_len = lv_strlen(child->class_p->name);
+                    if(name_len > 3 && class_name_len == name_len - 2 &&
+                       lv_strncmp(child->class_p->name, name, class_name_len) == 0) {
+                        cnt++;
+                    }
+                }
+                /*The name is set, check if it's e.g. <some_name>#*/
+                else {
+                    if(lv_strcmp(child->spec_attr->name, name) == 0) {
+                        cnt++;
+                    }
+                }
+            }
+        }
+    }
+    else {
+        /*Just use the set name*/
+        lv_strlcpy(buf, obj->spec_attr->name, buf_size);
+    }
+}
+
+lv_obj_t * lv_obj_get_child_by_name(const lv_obj_t * parent, const char * path)
+{
+    LV_ASSERT_OBJ(parent, MY_CLASS);
+
+    if(parent == NULL || parent->spec_attr == NULL || path == NULL) return NULL;
+
+    while(*path) {
+        const char * segment = path;
+        uint32_t len = 0;
+
+        /* Calculate the length of the current segment */
+        while(path[len] && path[len] != '/')
+            len++;
+
+        /* Look for a child whose resolved name exactly matches the segment */
+        lv_obj_t * child = find_by_name_direct(parent, segment, len);
+        if(!child) return NULL; /*Segment not found*/
+
+        /* Advance to the next segment */
+        path += len;
+        if(*path == '/') path++; /* Skip the '/' */
+
+        /* If there is no further segment, we've found the target child */
+        if(*path == '\0') return child;
+
+        parent = child;
+    }
+
+    return NULL;
+
+}
+
+lv_obj_t * lv_obj_find_by_name(const lv_obj_t * parent, const char * name)
+{
+    if(parent == NULL) parent = lv_display_get_screen_active(NULL);
+    if(parent == NULL) return NULL;
+
+    lv_obj_t * child = find_by_name_direct(parent, name, UINT16_MAX);
+    if(child) return child;
+
+    /*Search children recursively*/
+    uint32_t child_cnt = lv_obj_get_child_count(parent);
+    uint32_t i;
+    for(i = 0; i < child_cnt; i++) {
+        child = parent->spec_attr->children[i];
+        lv_obj_t * found = lv_obj_find_by_name(child, name);
+        if(found != NULL) return found;
+    }
+
+    return NULL;
+}
+
+#endif /*LV_USE_OBJ_NAME*/
+
+int32_t lv_obj_get_index(const lv_obj_t * obj)
+{
+    LV_ASSERT_OBJ(obj, MY_CLASS);
+
+    lv_obj_t * parent = lv_obj_get_parent(obj);
+    if(parent == NULL) return -1;
+
+    int32_t i = 0;
+    for(i = 0; i < parent->spec_attr->child_cnt; i++) {
+        if(parent->spec_attr->children[i] == obj) return i;
+    }
+
+    /*Shouldn't reach this point*/
+    LV_ASSERT(0);
+    return -1;
+}
+
+int32_t lv_obj_get_index_by_type(const lv_obj_t * obj, const lv_obj_class_t * class_p)
+{
+    LV_ASSERT_OBJ(obj, MY_CLASS);
+
+    lv_obj_t * parent = lv_obj_get_parent(obj);
+    if(parent == NULL) return 0xFFFFFFFF;
+
+    uint32_t i = 0;
+    uint32_t idx = 0;
+    for(i = 0; i < parent->spec_attr->child_cnt; i++) {
+        lv_obj_t * child = parent->spec_attr->children[i];
+        if(child->class_p == class_p) {
+            if(child == obj) return idx;
+            idx++;
+        }
+    }
+
+    /*Can happen if there was no children with the given type*/
+    return -1;
+}
+
+void lv_obj_tree_walk(lv_obj_t * start_obj, lv_obj_tree_walk_cb_t cb, void * user_data)
+{
+    walk_core(start_obj, cb, user_data);
+}
+
+void lv_obj_dump_tree(lv_obj_t * start_obj)
+{
+    if(start_obj == NULL) {
+        lv_display_t * disp = lv_display_get_next(NULL);
+        while(disp) {
+            uint32_t i;
+            for(i = 0; i < disp->screen_cnt; i++) {
+                dump_tree_core(disp->screens[i], 0);
+            }
+            disp = lv_display_get_next(disp);
+        }
+    }
+    else {
+        dump_tree_core(start_obj, 0);
+    }
+}
+
+/**********************
+ *   STATIC FUNCTIONS
+ **********************/
+
+static void lv_obj_delete_async_cb(void * obj)
+{
+    LV_ASSERT_OBJ(obj, MY_CLASS);
+
+    lv_obj_delete(obj);
+}
+
+static void obj_indev_reset(lv_indev_t * indev, lv_obj_t * obj)
+{
+    /* If the input device is already in the release state,
+     * there is no need to wait for the input device to be released
+     */
+    if(lv_indev_get_state(indev) != LV_INDEV_STATE_RELEASED) {
+        /*Wait for release to avoid accidentally triggering other obj to be clicked*/
+        lv_indev_wait_release(indev);
+    }
+
+    /*Reset the input device*/
+    lv_indev_reset(indev, obj);
+}
+
+static void obj_delete_core(lv_obj_t * obj)
+{
+    if(obj->is_deleting)
+        return;
+
+    obj->is_deleting = true;
+
+    /*Let the user free the resources used in `LV_EVENT_DELETE`*/
+    lv_result_t res = lv_obj_send_event(obj, LV_EVENT_DELETE, NULL);
+    if(res == LV_RESULT_INVALID) {
+        obj->is_deleting = false;
+        return;
+    }
+
+    /*Clean registered event_cb*/
+    if(obj->spec_attr) lv_event_remove_all(&(obj->spec_attr->event_list));
+
+    /*Recursively delete the children*/
+    lv_obj_t * child = lv_obj_get_child(obj, 0);
+    while(child) {
+        obj_delete_core(child);
+        child = lv_obj_get_child(obj, 0);
+    }
+
+    lv_group_t * group = lv_obj_get_group(obj);
+
+    /*Reset all input devices if the object to delete is used*/
+    lv_indev_t * indev = lv_indev_get_next(NULL);
+    while(indev) {
+        lv_indev_type_t indev_type = lv_indev_get_type(indev);
+        if(indev_type == LV_INDEV_TYPE_POINTER || indev_type == LV_INDEV_TYPE_BUTTON) {
+            if(indev->pointer.act_obj == obj || indev->pointer.last_obj == obj || indev->pointer.scroll_obj == obj) {
+                obj_indev_reset(indev, obj);
+            }
+            if(indev->pointer.last_pressed == obj) {
+                indev->pointer.last_pressed = NULL;
+            }
+            if(indev->pointer.last_hovered == obj) {
+                indev->pointer.last_hovered = NULL;
+            }
+        }
+
+        if(indev->group == group && obj == lv_indev_get_active_obj()) {
+            obj_indev_reset(indev, obj);
+        }
+        indev = lv_indev_get_next(indev);
+    }
+
+    /*Delete all pending async del-s*/
+    lv_result_t async_cancel_res = LV_RESULT_OK;
+    while(async_cancel_res == LV_RESULT_OK) {
+        async_cancel_res = lv_async_call_cancel(lv_obj_delete_async_cb, obj);
+    }
+
+    /*All children deleted. Now clean up the object specific data*/
+    lv_obj_destruct(obj);
+
+    /*Remove the screen for the screen list*/
+    if(obj->parent == NULL) {
+        lv_display_t * disp = lv_obj_get_display(obj);
+        uint32_t i;
+        /*Find the screen in the list*/
+        for(i = 0; i < disp->screen_cnt; i++) {
+            if(disp->screens[i] == obj) break;
+        }
+
+        uint32_t id = i;
+        for(i = id; i < disp->screen_cnt - 1; i++) {
+            disp->screens[i] = disp->screens[i + 1];
+        }
+        disp->screen_cnt--;
+        disp->screens = lv_realloc(disp->screens, disp->screen_cnt * sizeof(lv_obj_t *));
+    }
+    /*Remove the object from the child list of its parent*/
+    else {
+        int32_t id = lv_obj_get_index(obj);
+        uint16_t i;
+        for(i = id; i < obj->parent->spec_attr->child_cnt - 1; i++) {
+            obj->parent->spec_attr->children[i] = obj->parent->spec_attr->children[i + 1];
+        }
+        obj->parent->spec_attr->child_cnt--;
+        obj->parent->spec_attr->children = lv_realloc(obj->parent->spec_attr->children,
+                                                      obj->parent->spec_attr->child_cnt * sizeof(lv_obj_t *));
+    }
+
+    /*Free the object itself*/
+    lv_free(obj);
+}
+
+static lv_obj_tree_walk_res_t walk_core(lv_obj_t * obj, lv_obj_tree_walk_cb_t cb, void * user_data)
+{
+    lv_obj_tree_walk_res_t res = LV_OBJ_TREE_WALK_NEXT;
+
+    if(obj == NULL) {
+        lv_display_t * disp = lv_display_get_next(NULL);
+        while(disp) {
+            uint32_t i;
+            for(i = 0; i < disp->screen_cnt; i++) {
+                walk_core(disp->screens[i], cb, user_data);
+            }
+            disp = lv_display_get_next(disp);
+        }
+        return LV_OBJ_TREE_WALK_END;    /*The value doesn't matter as it wasn't called recursively*/
+    }
+
+    res = cb(obj, user_data);
+
+    if(res == LV_OBJ_TREE_WALK_END) return LV_OBJ_TREE_WALK_END;
+
+    if(res != LV_OBJ_TREE_WALK_SKIP_CHILDREN) {
+        uint32_t i;
+        for(i = 0; i < lv_obj_get_child_count(obj); i++) {
+            res = walk_core(lv_obj_get_child(obj, i), cb, user_data);
+            if(res == LV_OBJ_TREE_WALK_END) return LV_OBJ_TREE_WALK_END;
+        }
+    }
+    return LV_OBJ_TREE_WALK_NEXT;
+}
+
+static void dump_tree_core(lv_obj_t * obj, int32_t depth)
+{
+#if LV_USE_LOG
+    const char * id;
+
+#if LV_USE_OBJ_ID
+    char buf[OBJ_DUMP_STRING_LEN];
+    id = lv_obj_stringify_id(obj, buf, sizeof(buf));
+    if(id == NULL) id = "obj0";
+#else
+    id = "obj0";
+#endif
+
+    /*id of `obj0` is an invalid id for builtin id*/
+    LV_LOG_USER("%*sobj:%p, id:%s;", (int)(2 * depth), "", (void *)obj, id);
+#endif /*LV_USE_LOG*/
+
+    if(obj && obj->spec_attr && obj->spec_attr->child_cnt) {
+        for(uint32_t i = 0; i < obj->spec_attr->child_cnt; i++) {
+            dump_tree_core(lv_obj_get_child(obj, i), depth + 1);
+        }
+    }
+}
+
+static lv_obj_t * lv_obj_get_first_not_deleting_child(lv_obj_t * obj)
+{
+    LV_ASSERT_OBJ(obj, MY_CLASS);
+
+    if(obj->spec_attr == NULL) return NULL;
+
+    int32_t i;
+    int32_t cnt = (int32_t)obj->spec_attr->child_cnt;
+    for(i = 0; i < cnt; i++) {
+        if(!obj->spec_attr->children[i]->is_deleting) {
+            return obj->spec_attr->children[i];
+        }
+    }
+
+    return NULL;
+}
+
+#if LV_USE_OBJ_NAME
+
+static lv_obj_t * find_by_name_direct(const lv_obj_t * parent, const char * name, size_t len)
+{
+    uint32_t i;
+    uint32_t child_cnt = lv_obj_get_child_count(parent);
+    for(i = 0; i < child_cnt; i++) {
+        lv_obj_t * child = parent->spec_attr->children[i];
+
+        char child_name_resolved[LV_OBJ_NAME_MAX_LEN];
+        lv_obj_get_name_resolved(child, child_name_resolved, sizeof(child_name_resolved));
+        if(lv_strncmp(child_name_resolved, name, len) == 0) return child;
+    }
+
+    return NULL;
+}
+
+#endif /*LV_USE_OBJ_NAME*/

+ 321 - 0
components/easylvgl/lvgl9/src/core/lv_obj_tree.h

@@ -0,0 +1,321 @@
+/**
+ * @file lv_obj_tree.h
+ *
+ */
+
+#ifndef LV_OBJ_TREE_H
+#define LV_OBJ_TREE_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/*********************
+ *      INCLUDES
+ *********************/
+#include "../misc/lv_types.h"
+#include "../misc/lv_anim.h"
+#include "../display/lv_display.h"
+
+/*********************
+ *      DEFINES
+ *********************/
+
+/**********************
+ *      TYPEDEFS
+ **********************/
+
+typedef enum {
+    LV_OBJ_TREE_WALK_NEXT,
+    LV_OBJ_TREE_WALK_SKIP_CHILDREN,
+    LV_OBJ_TREE_WALK_END,
+} lv_obj_tree_walk_res_t;
+
+typedef lv_obj_tree_walk_res_t (*lv_obj_tree_walk_cb_t)(lv_obj_t *, void *);
+
+/**********************
+ * GLOBAL PROTOTYPES
+ **********************/
+
+/**
+ * Delete an object and all of its children.
+ * Also remove the objects from their group and remove all animations (if any).
+ * Send `LV_EVENT_DELETE` to deleted objects.
+ * @param obj       pointer to an object
+ */
+void lv_obj_delete(lv_obj_t * obj);
+
+/**
+ * Delete all children of an object.
+ * Also remove the objects from their group and remove all animations (if any).
+ * Send `LV_EVENT_DELETE` to deleted objects.
+ * @param obj       pointer to an object
+ */
+void lv_obj_clean(lv_obj_t * obj);
+
+/**
+ * Delete an object after some delay
+ * @param obj       pointer to an object
+ * @param delay_ms  time to wait before delete in milliseconds
+ */
+void lv_obj_delete_delayed(lv_obj_t * obj, uint32_t delay_ms);
+
+/**
+ * A function to be easily used in animation ready callback to delete an object when the animation is ready
+ * @param a         pointer to the animation
+ */
+void lv_obj_delete_anim_completed_cb(lv_anim_t * a);
+
+/**
+ * Helper function for asynchronously deleting objects.
+ * Useful for cases where you can't delete an object directly in an `LV_EVENT_DELETE` handler (i.e. parent).
+ * @param obj       object to delete
+ * @see lv_async_call
+ */
+void lv_obj_delete_async(lv_obj_t * obj);
+
+/**
+ * Move the parent of an object. The relative coordinates will be kept.
+ *
+ * @param obj       pointer to an object whose parent needs to be changed
+ * @param parent pointer to the new parent
+ */
+void lv_obj_set_parent(lv_obj_t * obj, lv_obj_t * parent);
+
+/**
+ * Swap the positions of two objects.
+ * When used in listboxes, it can be used to sort the listbox items.
+ * @param obj1  pointer to the first object
+ * @param obj2  pointer to the second object
+ */
+void lv_obj_swap(lv_obj_t * obj1, lv_obj_t * obj2);
+
+/**
+ * moves the object to the given index in its parent.
+ * When used in listboxes, it can be used to sort the listbox items.
+ * @param obj  pointer to the object to be moved.
+ * @param index  new index in parent. -1 to count from the back
+ * @note to move to the background: lv_obj_move_to_index(obj, 0)
+ * @note to move forward (up): lv_obj_move_to_index(obj, lv_obj_get_index(obj) - 1)
+ */
+void lv_obj_move_to_index(lv_obj_t * obj, int32_t index);
+
+/**
+ * Get the screen of an object
+ * @param obj       pointer to an object
+ * @return          pointer to the object's screen
+ */
+lv_obj_t * lv_obj_get_screen(const lv_obj_t * obj);
+
+/**
+ * Get the display of the object
+ * @param obj       pointer to an object
+ * @return          pointer to the object's display
+ */
+lv_display_t * lv_obj_get_display(const lv_obj_t * obj);
+
+/**
+ * Get the parent of an object
+ * @param obj       pointer to an object
+ * @return          the parent of the object. (NULL if `obj` was a screen)
+ */
+lv_obj_t * lv_obj_get_parent(const lv_obj_t * obj);
+
+/**
+ * Get the child of an object by the child's index.
+ * @param obj       pointer to an object whose child should be get
+ * @param idx       the index of the child.
+ *                  0: the oldest (firstly created) child
+ *                  1: the second oldest
+ *                  child count-1: the youngest
+ *                  -1: the youngest
+ *                  -2: the second youngest
+ * @return          pointer to the child or NULL if the index was invalid
+ */
+lv_obj_t * lv_obj_get_child(const lv_obj_t * obj, int32_t idx);
+
+/**
+ * Get the child of an object by the child's index. Consider the children only with a given type.
+ * @param obj       pointer to an object whose child should be get
+ * @param idx       the index of the child.
+ *                  0: the oldest (firstly created) child
+ *                  1: the second oldest
+ *                  child count-1: the youngest
+ *                  -1: the youngest
+ *                  -2: the second youngest
+ * @param class_p   the type of the children to check
+ * @return          pointer to the child or NULL if the index was invalid
+ */
+lv_obj_t * lv_obj_get_child_by_type(const lv_obj_t * obj, int32_t idx,
+                                    const lv_obj_class_t * class_p);
+
+/**
+ * Return a sibling of an object
+ * @param obj       pointer to an object whose sibling should be get
+ * @param idx       0: `obj` itself
+ *                  -1: the first older sibling
+ *                  -2: the next older sibling
+ *                  1: the first younger sibling
+ *                  2: the next younger sibling
+ *                  etc
+ * @return          pointer to the requested sibling  or NULL if there is no such sibling
+ */
+lv_obj_t * lv_obj_get_sibling(const lv_obj_t * obj, int32_t idx);
+
+/**
+ * Return a sibling of an object. Consider the siblings only with a given type.
+ * @param obj       pointer to an object whose sibling should be get
+ * @param idx       0: `obj` itself
+ *                  -1: the first older sibling
+ *                  -2: the next older sibling
+ *                  1: the first younger sibling
+ *                  2: the next younger sibling
+ *                  etc
+ * @param class_p   the type of the children to check
+ * @return          pointer to the requested sibling  or NULL if there is no such sibling
+ */
+lv_obj_t * lv_obj_get_sibling_by_type(const lv_obj_t * obj, int32_t idx,
+                                      const lv_obj_class_t * class_p);
+
+/**
+ * Get the number of children
+ * @param obj       pointer to an object
+ * @return          the number of children
+ */
+uint32_t lv_obj_get_child_count(const lv_obj_t * obj);
+
+/**
+ * Get the number of children having a given type.
+ * @param obj       pointer to an object
+ * @param class_p   the type of the children to check
+ * @return          the number of children
+ */
+
+uint32_t lv_obj_get_child_count_by_type(const lv_obj_t * obj, const lv_obj_class_t * class_p);
+
+#if LV_USE_OBJ_NAME
+
+/**
+ * Set a name for a widget. The name will be allocated and freed when the
+ * widget is deleted or a new name is set.
+ * @param obj       pointer to an object
+ * @param name      the name to set. If set to `NULL` the default "<widget_type>_#"
+ *                  name will be used.
+ * @note If the name ends with a `#`, older siblings with the same name
+ * will be counted, and the `#` will be replaced by the index of the
+ * given widget. For example, creating multiple widgets with the name
+ * "mybtn_#" will result in resolved names like "mybtn_0", "mybtn_1",
+ * "mybtn_2", etc.  The name is resolved when `lv_obj_get_name_resolved`
+ * is called, so the result reflects the currently existing widgets at
+ * that time.
+ */
+void lv_obj_set_name(lv_obj_t * obj, const char * name);
+
+/**
+ * Set a name for a widget. Only a pointer will be saved.
+ * @param obj       pointer to an object
+ * @param name      the name to set. If set to `NULL` the default "<widget_type>_#"
+ *                  name will be used.
+ * @note If the name ends with a `#`, older siblings with the same name
+ * will be counted, and the `#` will be replaced by the index of the
+ * given widget. For example, creating multiple widgets with the name
+ * "mybtn_#" will result in resolved names like "mybtn_0", "mybtn_1",
+ * "mybtn_2", etc.  The name is resolved when `lv_obj_get_name_resolved`
+ * is called, so the result reflects the currently existing widgets at
+ * that time.
+ */
+void lv_obj_set_name_static(lv_obj_t * obj, const char * name);
+
+/**
+ * Get the set name as it was set.
+ * @param obj       pointer to an object
+ * @return          get the set name or NULL if it wasn't set yet
+ */
+const char * lv_obj_get_name(const lv_obj_t * obj);
+
+/**
+ * Get the set name or craft a name automatically.
+ * @param obj       pointer to an object
+ * @param buf       buffer to store the name
+ * @param buf_size  the size of the buffer in bytes
+ * @note If the name ends with a `#`, older siblings with the same name
+ * will be counted, and the `#` will be replaced by the index of the
+ * given widget. For example, creating multiple widgets with the name
+ * "mybtn_#" will result in resolved names like "mybtn_0", "mybtn_1",
+ * "mybtn_2", etc.  The name is resolved when `lv_obj_get_name_resolved`
+ * is called, so the result reflects the currently existing widgets at
+ * that time.
+ */
+void lv_obj_get_name_resolved(const lv_obj_t * obj, char buf[], size_t buf_size);
+
+/**
+ * Find a child with a given name on a parent. This child doesn't have to be the
+ * direct child of the parent. First direct children of the parent will be checked,
+ * and the direct children of the first child, etc. (Breadth-first search).
+ *
+ * If the name of a widget was not set a name like "lv_button_1" will
+ * be created for it using `lv_obj_get_name_resolved`.
+ *
+ * @param parent        the widget where the search should start
+ * @return              the found widget or NULL if not found.
+ */
+lv_obj_t * lv_obj_find_by_name(const lv_obj_t * parent, const char * name);
+
+/**
+ * Get an object by name. The name can be a path too, for example
+ * "main_container/lv_button_1/label".
+ * In this case the first part of the name-path should be the direct child of the parent,
+ * the second part, should the direct child of first one, etc.
+ *
+ * If the name of a widget was not set a name like "lv_button_1" will
+ * be created for it using `lv_obj_get_name_resolved`.
+ *
+ * @param parent        the widget where the search should start
+ * @return              the found widget or NULL if not found.
+ */
+lv_obj_t * lv_obj_get_child_by_name(const lv_obj_t * parent, const char * name_path);
+
+#endif /*LV_USE_OBJ_NAME*/
+
+/**
+ * Get the index of a child.
+ * @param obj       pointer to an object
+ * @return          the child index of the object.
+ *                  E.g. 0: the oldest (firstly created child).
+ *                  (-1 if child could not be found or no parent exists)
+ */
+int32_t lv_obj_get_index(const lv_obj_t * obj);
+
+/**
+ * Get the index of a child. Consider the children only with a given type.
+ * @param obj       pointer to an object
+ * @param class_p   the type of the children to check
+ * @return          the child index of the object.
+ *                  E.g. 0: the oldest (firstly created child with the given class).
+ *                  (-1 if child could not be found or no parent exists)
+ */
+int32_t lv_obj_get_index_by_type(const lv_obj_t * obj, const lv_obj_class_t * class_p);
+
+/**
+ * Iterate through all children of any object.
+ * @param start_obj     start integrating from this object
+ * @param cb            call this callback on the objects
+ * @param user_data     pointer to any user related data (will be passed to `cb`)
+ */
+void lv_obj_tree_walk(lv_obj_t * start_obj, lv_obj_tree_walk_cb_t cb, void * user_data);
+
+/**
+ * Iterate through all children of any object and print their ID.
+ * @param start_obj     start integrating from this object
+ */
+void lv_obj_dump_tree(lv_obj_t * start_obj);
+
+/**********************
+ *      MACROS
+ **********************/
+
+#ifdef __cplusplus
+} /*extern "C"*/
+#endif
+
+#endif /*LV_OBJ_TREE_H*/

+ 1450 - 0
components/easylvgl/lvgl9/src/core/lv_refr.c

@@ -0,0 +1,1450 @@
+/**
+ * @file lv_refr.c
+ *
+ */
+
+/*********************
+ *      INCLUDES
+ *********************/
+#include "lv_refr_private.h"
+#include "lv_obj_draw_private.h"
+#include "../misc/lv_area_private.h"
+#include "../draw/sw/lv_draw_sw_mask_private.h"
+#include "../draw/lv_draw_mask_private.h"
+#include "lv_obj_private.h"
+#include "lv_obj_event_private.h"
+#include "../display/lv_display.h"
+#include "../display/lv_display_private.h"
+#include "../tick/lv_tick.h"
+#include "../misc/lv_timer_private.h"
+#include "../misc/lv_math.h"
+#include "../misc/lv_profiler.h"
+#include "../misc/lv_types.h"
+#include "../draw/lv_draw_private.h"
+#include "../font/lv_font_fmt_txt.h"
+#include "../stdlib/lv_string.h"
+#include "lv_global.h"
+
+/*********************
+ *      DEFINES
+ *********************/
+
+/*Display being refreshed*/
+#define disp_refr LV_GLOBAL_DEFAULT()->disp_refresh
+
+/**********************
+ *      TYPEDEFS
+ **********************/
+
+/**********************
+ *  STATIC PROTOTYPES
+ **********************/
+static void lv_refr_join_area(void);
+static void refr_invalid_areas(void);
+static void refr_sync_areas(void);
+static void refr_area(const lv_area_t * area_p, int32_t y_offset);
+static void refr_configured_layer(lv_layer_t * layer);
+static void refr_obj_and_children(lv_layer_t * layer, lv_obj_t * top_obj);
+static uint32_t get_max_row(lv_display_t * disp, int32_t area_w, int32_t area_h);
+static void draw_buf_flush(lv_display_t * disp);
+static void call_flush_cb(lv_display_t * disp, const lv_area_t * area, uint8_t * px_map);
+static void wait_for_flushing(lv_display_t * disp);
+static lv_result_t layer_get_area(lv_layer_t * layer, lv_obj_t * obj, lv_layer_type_t layer_type,
+                                  lv_area_t * layer_area_out, lv_area_t * obj_draw_size_out);
+static bool alpha_test_area_on_obj(lv_obj_t * obj, const lv_area_t * area);
+#if LV_DRAW_TRANSFORM_USE_MATRIX
+    static bool refr_check_obj_clip_overflow(lv_layer_t * layer, lv_obj_t * obj);
+    static void refr_obj_matrix(lv_layer_t * layer, lv_obj_t * obj);
+#endif
+
+/**********************
+ *  STATIC VARIABLES
+ **********************/
+
+/**********************
+ *      MACROS
+ **********************/
+#if LV_USE_LOG && LV_LOG_TRACE_DISP_REFR
+    #define LV_TRACE_REFR(...) LV_LOG_TRACE(__VA_ARGS__)
+#else
+    #define LV_TRACE_REFR(...)
+#endif
+
+/**********************
+ *   GLOBAL FUNCTIONS
+ **********************/
+
+/**
+ * Initialize the screen refresh subsystem
+ */
+void lv_refr_init(void)
+{
+}
+
+void lv_refr_deinit(void)
+{
+}
+
+void lv_refr_now(lv_display_t * disp)
+{
+    lv_anim_refr_now();
+
+    if(disp) {
+        if(disp->refr_timer) lv_display_refr_timer(disp->refr_timer);
+    }
+    else {
+        lv_display_t * d;
+        d = lv_display_get_next(NULL);
+        while(d) {
+            if(d->refr_timer) lv_display_refr_timer(d->refr_timer);
+            d = lv_display_get_next(d);
+        }
+    }
+}
+
+void lv_obj_redraw(lv_layer_t * layer, lv_obj_t * obj)
+{
+    LV_PROFILER_REFR_BEGIN;
+    lv_area_t clip_area_ori = layer->_clip_area;
+    lv_area_t clip_coords_for_obj;
+
+    /*Truncate the clip area to `obj size + ext size` area*/
+    lv_area_t obj_coords_ext;
+    lv_obj_get_coords(obj, &obj_coords_ext);
+    int32_t ext_draw_size = lv_obj_get_ext_draw_size(obj);
+    lv_area_increase(&obj_coords_ext, ext_draw_size, ext_draw_size);
+
+    if(!lv_area_intersect(&clip_coords_for_obj, &clip_area_ori, &obj_coords_ext)) {
+        LV_PROFILER_REFR_END;
+        return;
+    }
+    /*If the object is visible on the current clip area*/
+    layer->_clip_area = clip_coords_for_obj;
+
+    lv_obj_send_event(obj, LV_EVENT_DRAW_MAIN_BEGIN, layer);
+    lv_obj_send_event(obj, LV_EVENT_DRAW_MAIN, layer);
+    lv_obj_send_event(obj, LV_EVENT_DRAW_MAIN_END, layer);
+#if LV_USE_REFR_DEBUG
+    lv_color_t debug_color = lv_color_make(lv_rand(0, 0xFF), lv_rand(0, 0xFF), lv_rand(0, 0xFF));
+    lv_draw_rect_dsc_t draw_dsc;
+    lv_draw_rect_dsc_init(&draw_dsc);
+    draw_dsc.bg_color = debug_color;
+    draw_dsc.bg_opa = LV_OPA_20;
+    draw_dsc.border_width = 1;
+    draw_dsc.border_opa = LV_OPA_30;
+    draw_dsc.border_color = debug_color;
+    lv_draw_rect(layer, &draw_dsc, &obj_coords_ext);
+#endif
+
+    const lv_area_t * obj_coords;
+    if(lv_obj_has_flag(obj, LV_OBJ_FLAG_OVERFLOW_VISIBLE)) {
+        obj_coords = &obj_coords_ext;
+    }
+    else {
+        obj_coords = &obj->coords;
+    }
+    lv_area_t clip_coords_for_children;
+    bool refr_children = true;
+    if(!lv_area_intersect(&clip_coords_for_children, &layer->_clip_area, obj_coords) || layer->opa <= LV_OPA_MIN) {
+        refr_children = false;
+    }
+
+    if(refr_children) {
+        uint32_t i;
+        uint32_t child_cnt = lv_obj_get_child_count(obj);
+        if(child_cnt == 0) {
+            /*If the object was visible on the clip area call the post draw events too*/
+            /*If all the children are redrawn make 'post draw' draw*/
+            lv_obj_send_event(obj, LV_EVENT_DRAW_POST_BEGIN, layer);
+            lv_obj_send_event(obj, LV_EVENT_DRAW_POST, layer);
+            lv_obj_send_event(obj, LV_EVENT_DRAW_POST_END, layer);
+        }
+        else {
+            layer->_clip_area = clip_coords_for_children;
+            bool clip_corner = lv_obj_get_style_clip_corner(obj, LV_PART_MAIN);
+
+            int32_t radius = 0;
+            if(clip_corner) {
+                radius = lv_obj_get_style_radius(obj, LV_PART_MAIN);
+                if(radius == 0) clip_corner = false;
+            }
+
+            if(clip_corner == false) {
+                for(i = 0; i < child_cnt; i++) {
+                    lv_obj_t * child = obj->spec_attr->children[i];
+                    lv_obj_refr(layer, child);
+                }
+
+                /*If the object was visible on the clip area call the post draw events too*/
+                /*If all the children are redrawn make 'post draw' draw*/
+                lv_obj_send_event(obj, LV_EVENT_DRAW_POST_BEGIN, layer);
+                lv_obj_send_event(obj, LV_EVENT_DRAW_POST, layer);
+                lv_obj_send_event(obj, LV_EVENT_DRAW_POST_END, layer);
+            }
+            else {
+                lv_layer_t * layer_children;
+                lv_draw_mask_rect_dsc_t mask_draw_dsc;
+                lv_draw_mask_rect_dsc_init(&mask_draw_dsc);
+                mask_draw_dsc.radius = radius;
+                mask_draw_dsc.area = obj->coords;
+
+                lv_draw_image_dsc_t img_draw_dsc;
+                lv_draw_image_dsc_init(&img_draw_dsc);
+
+                int32_t short_side = LV_MIN(lv_area_get_width(&obj->coords), lv_area_get_height(&obj->coords));
+                int32_t rout = LV_MIN(radius, short_side >> 1);
+
+                lv_area_t bottom = obj->coords;
+                bottom.y1 = bottom.y2 - rout + 1;
+                if(lv_area_intersect(&bottom, &bottom, &layer->_clip_area)) {
+                    layer_children = lv_draw_layer_create(layer, LV_COLOR_FORMAT_ARGB8888, &bottom);
+
+                    for(i = 0; i < child_cnt; i++) {
+                        lv_obj_t * child = obj->spec_attr->children[i];
+                        lv_obj_refr(layer_children, child);
+                    }
+
+                    /*If all the children are redrawn send 'post draw' draw*/
+                    lv_obj_send_event(obj, LV_EVENT_DRAW_POST_BEGIN, layer_children);
+                    lv_obj_send_event(obj, LV_EVENT_DRAW_POST, layer_children);
+                    lv_obj_send_event(obj, LV_EVENT_DRAW_POST_END, layer_children);
+
+                    lv_draw_mask_rect(layer_children, &mask_draw_dsc);
+
+                    img_draw_dsc.src = layer_children;
+                    lv_draw_layer(layer, &img_draw_dsc, &bottom);
+                }
+
+                lv_area_t top = obj->coords;
+                top.y2 = top.y1 + rout - 1;
+                if(lv_area_intersect(&top, &top, &layer->_clip_area)) {
+                    layer_children = lv_draw_layer_create(layer, LV_COLOR_FORMAT_ARGB8888, &top);
+
+                    for(i = 0; i < child_cnt; i++) {
+                        lv_obj_t * child = obj->spec_attr->children[i];
+                        lv_obj_refr(layer_children, child);
+                    }
+
+                    /*If all the children are redrawn send 'post draw' draw*/
+                    lv_obj_send_event(obj, LV_EVENT_DRAW_POST_BEGIN, layer_children);
+                    lv_obj_send_event(obj, LV_EVENT_DRAW_POST, layer_children);
+                    lv_obj_send_event(obj, LV_EVENT_DRAW_POST_END, layer_children);
+
+                    lv_draw_mask_rect(layer_children, &mask_draw_dsc);
+
+                    img_draw_dsc.src = layer_children;
+                    lv_draw_layer(layer, &img_draw_dsc, &top);
+
+                }
+
+                lv_area_t mid = obj->coords;
+                mid.y1 += rout;
+                mid.y2 -= rout;
+                if(lv_area_intersect(&mid, &mid, &layer->_clip_area)) {
+                    layer->_clip_area = mid;
+                    for(i = 0; i < child_cnt; i++) {
+                        lv_obj_t * child = obj->spec_attr->children[i];
+                        lv_obj_refr(layer, child);
+                    }
+
+                    /*If all the children are redrawn make 'post draw' draw*/
+                    lv_obj_send_event(obj, LV_EVENT_DRAW_POST_BEGIN, layer);
+                    lv_obj_send_event(obj, LV_EVENT_DRAW_POST, layer);
+                    lv_obj_send_event(obj, LV_EVENT_DRAW_POST_END, layer);
+
+                }
+
+            }
+        }
+    }
+
+    layer->_clip_area = clip_area_ori;
+    LV_PROFILER_REFR_END;
+}
+
+void lv_inv_area(lv_display_t * disp, const lv_area_t * area_p)
+{
+    if(!disp) disp = lv_display_get_default();
+    if(!disp) return;
+    if(!lv_display_is_invalidation_enabled(disp)) return;
+
+    /**
+     * There are two reasons for this issue:
+     *  1.LVGL API is being used across threads, such as modifying widget properties in another thread
+     *    or within an interrupt handler during the main thread rendering process.
+     *  2.User-customized widget modify widget properties/styles again within the DRAW event.
+     *
+     * Therefore, ensure that LVGL is used in a single-threaded manner, or refer to
+     * documentation: https://docs.lvgl.io/master/porting/os.html for proper locking mechanisms.
+     * Additionally, ensure that only drawing-related tasks are performed within the DRAW event,
+     * and move widget property/style modifications to other events.
+     */
+    LV_ASSERT_MSG(!disp->rendering_in_progress, "Invalidate area is not allowed during rendering.");
+
+    /*Clear the invalidate buffer if the parameter is NULL*/
+    if(area_p == NULL) {
+        disp->inv_p = 0;
+        return;
+    }
+
+    lv_area_t scr_area;
+    scr_area.x1 = 0;
+    scr_area.y1 = 0;
+    scr_area.x2 = lv_display_get_horizontal_resolution(disp) - 1;
+    scr_area.y2 = lv_display_get_vertical_resolution(disp) - 1;
+
+    lv_area_t com_area;
+    bool suc;
+
+    suc = lv_area_intersect(&com_area, area_p, &scr_area);
+    if(suc == false)  return; /*Out of the screen*/
+
+    if(disp->color_format == LV_COLOR_FORMAT_I1) {
+        /*Make sure that the X coordinates start and end on byte boundary.
+         *E.g. convert 11;27 to 8;31*/
+        com_area.x1 &= ~0x7; /*Round down: Nx8*/
+        com_area.x2 |= 0x7;    /*Round up: Nx8 - 1*/
+    }
+
+    /*If there were at least 1 invalid area in full refresh mode, redraw the whole screen*/
+    if(disp->render_mode == LV_DISPLAY_RENDER_MODE_FULL) {
+        disp->inv_areas[0] = scr_area;
+        disp->inv_p = 1;
+        lv_display_send_event(disp, LV_EVENT_REFR_REQUEST, NULL);
+        return;
+    }
+
+    lv_result_t res = lv_display_send_event(disp, LV_EVENT_INVALIDATE_AREA, &com_area);
+    if(res != LV_RESULT_OK) return;
+
+    /*Save only if this area is not in one of the saved areas*/
+    uint16_t i;
+    for(i = 0; i < disp->inv_p; i++) {
+        if(lv_area_is_in(&com_area, &disp->inv_areas[i], 0) != false) return;
+    }
+
+    /*Save the area*/
+    lv_area_t * tmp_area_p = &com_area;
+    if(disp->inv_p >= LV_INV_BUF_SIZE) { /*If no place for the area add the screen*/
+        disp->inv_p = 0;
+        tmp_area_p = &scr_area;
+    }
+    lv_area_copy(&disp->inv_areas[disp->inv_p], tmp_area_p);
+    disp->inv_p++;
+
+    lv_display_send_event(disp, LV_EVENT_REFR_REQUEST, NULL);
+}
+
+/**
+ * Get the display which is being refreshed
+ * @return the display being refreshed
+ */
+lv_display_t * lv_refr_get_disp_refreshing(void)
+{
+    return disp_refr;
+}
+
+/**
+ * Get the display which is being refreshed
+ * @return the display being refreshed
+ */
+void lv_refr_set_disp_refreshing(lv_display_t * disp)
+{
+    disp_refr = disp;
+}
+
+void lv_display_refr_timer(lv_timer_t * tmr)
+{
+    LV_PROFILER_REFR_BEGIN;
+    LV_TRACE_REFR("begin");
+
+    if(tmr) {
+        disp_refr = tmr->user_data;
+        /* Ensure the timer does not run again automatically.
+         * This is done before refreshing in case refreshing invalidates something else.
+         * However if the performance monitor is enabled keep the timer running to count the FPS.*/
+#if !LV_USE_PERF_MONITOR
+        lv_timer_pause(tmr);
+#endif
+    }
+    else {
+        disp_refr = lv_display_get_default();
+    }
+
+    if(disp_refr == NULL) {
+        LV_LOG_WARN("No display registered");
+        LV_PROFILER_REFR_END;
+        return;
+    }
+
+    lv_draw_buf_t * buf_act = disp_refr->buf_act;
+    if(!(buf_act && buf_act->data && buf_act->data_size)) {
+        LV_LOG_WARN("No draw buffer");
+        LV_PROFILER_REFR_END;
+        return;
+    }
+
+    lv_result_t res = lv_display_send_event(disp_refr, LV_EVENT_REFR_START, NULL);
+    if(res == LV_RESULT_INVALID) {
+        LV_TRACE_REFR("deleted");
+        LV_PROFILER_REFR_END;
+        return;
+    }
+
+    /*Refresh the screen's layout if required*/
+    LV_PROFILER_LAYOUT_BEGIN_TAG("layout");
+    lv_obj_update_layout(disp_refr->act_scr);
+    if(disp_refr->prev_scr) lv_obj_update_layout(disp_refr->prev_scr);
+
+    lv_obj_update_layout(disp_refr->bottom_layer);
+    lv_obj_update_layout(disp_refr->top_layer);
+    lv_obj_update_layout(disp_refr->sys_layer);
+    LV_PROFILER_LAYOUT_END_TAG("layout");
+
+    /*Do nothing if there is no active screen*/
+    if(disp_refr->act_scr == NULL) {
+        disp_refr->inv_p = 0;
+        LV_LOG_WARN("there is no active screen");
+        goto refr_finish;
+    }
+
+    lv_refr_join_area();
+    refr_sync_areas();
+    refr_invalid_areas();
+
+    if(disp_refr->inv_p == 0) goto refr_finish;
+    /*In double buffered direct mode save the updated areas.
+     *They will be used on the next call to synchronize the buffers.*/
+    if(lv_display_is_double_buffered(disp_refr) && disp_refr->render_mode == LV_DISPLAY_RENDER_MODE_DIRECT) {
+        uint32_t i;
+        for(i = 0; i < disp_refr->inv_p; i++) {
+            if(disp_refr->inv_area_joined[i])
+                continue;
+
+            lv_area_t * sync_area = lv_ll_ins_tail(&disp_refr->sync_areas);
+            *sync_area = disp_refr->inv_areas[i];
+        }
+    }
+
+    lv_memzero(disp_refr->inv_areas, sizeof(disp_refr->inv_areas));
+    lv_memzero(disp_refr->inv_area_joined, sizeof(disp_refr->inv_area_joined));
+    disp_refr->inv_p = 0;
+
+refr_finish:
+
+#if LV_DRAW_SW_COMPLEX == 1
+    lv_draw_sw_mask_cleanup();
+#endif
+
+    lv_display_send_event(disp_refr, LV_EVENT_REFR_READY, NULL);
+
+    LV_TRACE_REFR("finished");
+    LV_PROFILER_REFR_END;
+}
+
+/**
+ * Search the most top object which fully covers an area
+ * @param area_p pointer to an area
+ * @param obj the first object to start the searching (typically a screen)
+ * @return
+ */
+lv_obj_t * lv_refr_get_top_obj(const lv_area_t * area_p, lv_obj_t * obj)
+{
+    lv_obj_t * found_p = NULL;
+
+    if(lv_area_is_in(area_p, &obj->coords, 0) == false) return NULL;
+    if(lv_obj_has_flag(obj, LV_OBJ_FLAG_HIDDEN)) return NULL;
+    if(lv_obj_get_layer_type(obj) != LV_LAYER_TYPE_NONE) return NULL;
+    if(lv_obj_get_style_opa(obj, LV_PART_MAIN) < LV_OPA_MAX) return NULL;
+
+    /*If this object is fully cover the draw area then check the children too*/
+    lv_cover_check_info_t info;
+    info.res = LV_COVER_RES_COVER;
+    info.area = area_p;
+    lv_obj_send_event(obj, LV_EVENT_COVER_CHECK, &info);
+    if(info.res == LV_COVER_RES_MASKED) return NULL;
+
+    int32_t i;
+    int32_t child_cnt = lv_obj_get_child_count(obj);
+    for(i = child_cnt - 1; i >= 0; i--) {
+        lv_obj_t * child = obj->spec_attr->children[i];
+        found_p = lv_refr_get_top_obj(area_p, child);
+
+        /*If a children is ok then break*/
+        if(found_p != NULL) {
+            break;
+        }
+    }
+
+    /*If no better children use this object*/
+    if(found_p == NULL && info.res == LV_COVER_RES_COVER) {
+        found_p = obj;
+    }
+
+    return found_p;
+}
+
+
+void lv_obj_refr(lv_layer_t * layer, lv_obj_t * obj)
+{
+    LV_ASSERT_NULL(layer);
+    LV_ASSERT_NULL(obj);
+    if(lv_obj_has_flag(obj, LV_OBJ_FLAG_HIDDEN)) return;
+
+    /*If `opa_layered != LV_OPA_COVER` draw the widget on a new layer and blend that layer with the given opacity.*/
+    const lv_opa_t opa_layered = lv_obj_get_style_opa_layered(obj, LV_PART_MAIN);
+    if(opa_layered <= LV_OPA_MIN) return;
+
+    const lv_opa_t layer_opa_ori = layer->opa;
+    const lv_color32_t layer_recolor = layer->recolor;
+
+    /*Normal `opa` (not layered) will just scale down `bg_opa`, `text_opa`, etc, in the upcoming drawings.*/
+    const lv_opa_t opa_main = lv_obj_get_style_opa(obj, LV_PART_MAIN);
+    if(opa_main < LV_OPA_MAX) {
+        layer->opa = LV_OPA_MIX2(layer_opa_ori, opa_main);
+    }
+
+    layer->recolor = lv_obj_style_apply_recolor(obj, LV_PART_MAIN, layer->recolor);
+
+    lv_layer_type_t layer_type = lv_obj_get_layer_type(obj);
+    if(layer_type == LV_LAYER_TYPE_NONE) {
+        lv_obj_redraw(layer, obj);
+    }
+#if LV_DRAW_TRANSFORM_USE_MATRIX
+    /*If the layer opa is full then use the matrix transform*/
+    else if(opa_layered >= LV_OPA_MAX && !refr_check_obj_clip_overflow(layer, obj)) {
+        refr_obj_matrix(layer, obj);
+    }
+#endif /* LV_DRAW_TRANSFORM_USE_MATRIX */
+    else {
+        lv_area_t layer_area_full;
+        lv_area_t obj_draw_size;
+        lv_result_t res = layer_get_area(layer, obj, layer_type, &layer_area_full, &obj_draw_size);
+        if(res != LV_RESULT_OK) return;
+
+        /*Simple layers can be subdivided into smaller layers*/
+        uint32_t max_rgb_row_height = lv_area_get_height(&layer_area_full);
+        uint32_t max_argb_row_height = lv_area_get_height(&layer_area_full);
+        if(layer_type == LV_LAYER_TYPE_SIMPLE) {
+            int32_t w = lv_area_get_width(&layer_area_full);
+            uint8_t px_size = lv_color_format_get_size(disp_refr->color_format);
+            max_rgb_row_height = LV_DRAW_LAYER_SIMPLE_BUF_SIZE / w / px_size;
+            max_argb_row_height = LV_DRAW_LAYER_SIMPLE_BUF_SIZE / w / sizeof(lv_color32_t);
+        }
+
+        lv_area_t layer_area_act;
+        layer_area_act.x1 = layer_area_full.x1;
+        layer_area_act.x2 = layer_area_full.x2;
+        layer_area_act.y1 = layer_area_full.y1;
+        layer_area_act.y2 = layer_area_full.y1;
+
+        while(layer_area_act.y2 < layer_area_full.y2) {
+            /* Test with an RGB layer size (which is larger than the ARGB layer size)
+             * If it really doesn't need alpha use it. Else switch to the ARGB size*/
+            layer_area_act.y2 = layer_area_act.y1 + max_rgb_row_height - 1;
+            if(layer_area_act.y2 > layer_area_full.y2) layer_area_act.y2 = layer_area_full.y2;
+
+            const void * bitmap_mask_src = lv_obj_get_style_bitmap_mask_src(obj, LV_PART_MAIN);
+            bool area_need_alpha = bitmap_mask_src || alpha_test_area_on_obj(obj, &layer_area_act);
+
+            if(area_need_alpha) {
+                layer_area_act.y2 = layer_area_act.y1 + max_argb_row_height - 1;
+                if(layer_area_act.y2 > layer_area_full.y2) layer_area_act.y2 = layer_area_full.y2;
+            }
+
+            lv_layer_t * new_layer = lv_draw_layer_create(layer,
+                                                          area_need_alpha ? LV_COLOR_FORMAT_ARGB8888 : LV_COLOR_FORMAT_NATIVE, &layer_area_act);
+            lv_obj_redraw(new_layer, obj);
+
+            lv_point_t pivot = {
+                .x = lv_obj_get_style_transform_pivot_x(obj, LV_PART_MAIN),
+                .y = lv_obj_get_style_transform_pivot_y(obj, LV_PART_MAIN)
+            };
+
+            if(LV_COORD_IS_PCT(pivot.x)) {
+                pivot.x = (LV_COORD_GET_PCT(pivot.x) * lv_area_get_width(&obj->coords)) / 100;
+            }
+            if(LV_COORD_IS_PCT(pivot.y)) {
+                pivot.y = (LV_COORD_GET_PCT(pivot.y) * lv_area_get_height(&obj->coords)) / 100;
+            }
+
+            lv_draw_image_dsc_t layer_draw_dsc;
+            lv_draw_image_dsc_init(&layer_draw_dsc);
+            layer_draw_dsc.pivot.x = obj->coords.x1 + pivot.x - new_layer->buf_area.x1;
+            layer_draw_dsc.pivot.y = obj->coords.y1 + pivot.y - new_layer->buf_area.y1;
+
+            layer_draw_dsc.opa = opa_layered;
+            layer_draw_dsc.rotation = lv_obj_get_style_transform_rotation(obj, LV_PART_MAIN);
+            while(layer_draw_dsc.rotation > 3600) layer_draw_dsc.rotation -= 3600;
+            while(layer_draw_dsc.rotation < 0) layer_draw_dsc.rotation += 3600;
+            layer_draw_dsc.scale_x = lv_obj_get_style_transform_scale_x(obj, LV_PART_MAIN);
+            layer_draw_dsc.scale_y = lv_obj_get_style_transform_scale_y(obj, LV_PART_MAIN);
+            layer_draw_dsc.skew_x = lv_obj_get_style_transform_skew_x(obj, LV_PART_MAIN);
+            layer_draw_dsc.skew_y = lv_obj_get_style_transform_skew_y(obj, LV_PART_MAIN);
+            layer_draw_dsc.blend_mode = lv_obj_get_style_blend_mode(obj, LV_PART_MAIN);
+            layer_draw_dsc.antialias = disp_refr->antialiasing;
+            layer_draw_dsc.bitmap_mask_src = bitmap_mask_src;
+            layer_draw_dsc.image_area = obj_draw_size;
+            layer_draw_dsc.src = new_layer;
+
+            lv_draw_layer(layer, &layer_draw_dsc, &layer_area_act);
+
+            layer_area_act.y1 = layer_area_act.y2 + 1;
+        }
+    }
+
+    /* Restore the original layer opa and recolor */
+    layer->opa = layer_opa_ori;
+    layer->recolor = layer_recolor;
+}
+
+/**********************
+ *   STATIC FUNCTIONS
+ **********************/
+
+/**
+ * Join the areas which has got common parts
+ */
+static void lv_refr_join_area(void)
+{
+    LV_PROFILER_REFR_BEGIN;
+    uint32_t join_from;
+    uint32_t join_in;
+    lv_area_t joined_area;
+    for(join_in = 0; join_in < disp_refr->inv_p; join_in++) {
+        if(disp_refr->inv_area_joined[join_in] != 0) continue;
+
+        /*Check all areas to join them in 'join_in'*/
+        for(join_from = 0; join_from < disp_refr->inv_p; join_from++) {
+            /*Handle only unjoined areas and ignore itself*/
+            if(disp_refr->inv_area_joined[join_from] != 0 || join_in == join_from) {
+                continue;
+            }
+
+            /*Check if the areas are on each other*/
+            if(lv_area_is_on(&disp_refr->inv_areas[join_in], &disp_refr->inv_areas[join_from]) == false) {
+                continue;
+            }
+
+            lv_area_join(&joined_area, &disp_refr->inv_areas[join_in], &disp_refr->inv_areas[join_from]);
+
+            /*Join two area only if the joined area size is smaller*/
+            if(lv_area_get_size(&joined_area) < (lv_area_get_size(&disp_refr->inv_areas[join_in]) +
+                                                 lv_area_get_size(&disp_refr->inv_areas[join_from]))) {
+                lv_area_copy(&disp_refr->inv_areas[join_in], &joined_area);
+
+                /*Mark 'join_form' is joined into 'join_in'*/
+                disp_refr->inv_area_joined[join_from] = 1;
+            }
+        }
+    }
+    LV_PROFILER_REFR_END;
+}
+
+/**
+ * Refresh the sync areas
+ */
+static void refr_sync_areas(void)
+{
+    /*Do not sync if not direct or double buffered*/
+    if(disp_refr->render_mode != LV_DISPLAY_RENDER_MODE_DIRECT) return;
+
+    /*Do not sync if not double buffered*/
+    if(!lv_display_is_double_buffered(disp_refr)) return;
+
+    /*Do not sync if no sync areas*/
+    if(lv_ll_is_empty(&disp_refr->sync_areas)) return;
+
+    LV_PROFILER_REFR_BEGIN;
+    /*With double buffered direct mode synchronize the rendered areas to the other buffer*/
+    /*We need to wait for ready here to not mess up the active screen*/
+    wait_for_flushing(disp_refr);
+
+    /*The buffers are already swapped.
+     *So the active buffer is the off screen buffer where LVGL will render*/
+    lv_draw_buf_t * off_screen = disp_refr->buf_act;
+    /*Triple buffer sync buffer for off-screen2 updates.*/
+    lv_draw_buf_t * off_screen2;
+    lv_draw_buf_t * on_screen;
+
+    if(disp_refr->buf_act == disp_refr->buf_1) {
+        off_screen2 = disp_refr->buf_2;
+        on_screen = disp_refr->buf_3 ? disp_refr->buf_3 : disp_refr->buf_2;
+    }
+    else if(disp_refr->buf_act == disp_refr->buf_2) {
+        off_screen2 = disp_refr->buf_3 ? disp_refr->buf_3 : disp_refr->buf_1;
+        on_screen = disp_refr->buf_1;
+    }
+    else {
+        off_screen2 = disp_refr->buf_1;
+        on_screen = disp_refr->buf_2;
+    }
+
+    uint32_t hor_res = lv_display_get_horizontal_resolution(disp_refr);
+    uint32_t ver_res = lv_display_get_vertical_resolution(disp_refr);
+
+    /*Iterate through invalidated areas to see if sync area should be copied*/
+    uint16_t i;
+    int8_t j;
+    lv_area_t res[4] = {0};
+    int8_t res_c;
+    lv_area_t * sync_area, * new_area, * next_area;
+    for(i = 0; i < disp_refr->inv_p; i++) {
+        /*Skip joined areas*/
+        if(disp_refr->inv_area_joined[i]) continue;
+
+        /*Iterate over sync areas*/
+        sync_area = lv_ll_get_head(&disp_refr->sync_areas);
+        while(sync_area != NULL) {
+            /*Get next sync area*/
+            next_area = lv_ll_get_next(&disp_refr->sync_areas, sync_area);
+
+            /*Remove intersect of redraw area from sync area and get remaining areas*/
+            res_c = lv_area_diff(res, sync_area, &disp_refr->inv_areas[i]);
+
+            /*New sub areas created after removing intersect*/
+            if(res_c != -1) {
+                /*Replace old sync area with new areas*/
+                for(j = 0; j < res_c; j++) {
+                    new_area = lv_ll_ins_prev(&disp_refr->sync_areas, sync_area);
+                    *new_area = res[j];
+                }
+                lv_ll_remove(&disp_refr->sync_areas, sync_area);
+                lv_free(sync_area);
+            }
+
+            /*Move on to next sync area*/
+            sync_area = next_area;
+        }
+    }
+
+    lv_area_t disp_area = {0, 0, (int32_t)hor_res - 1, (int32_t)ver_res - 1};
+    /*Copy sync areas (if any remaining)*/
+    for(sync_area = lv_ll_get_head(&disp_refr->sync_areas); sync_area != NULL;
+        sync_area = lv_ll_get_next(&disp_refr->sync_areas, sync_area)) {
+        /**
+         * @todo Resize SDL window will trigger crash because of sync_area is larger than disp_area
+         */
+        if(!lv_area_intersect(sync_area, sync_area, &disp_area)) {
+            continue;
+        }
+#if LV_DRAW_TRANSFORM_USE_MATRIX
+        if(lv_display_get_matrix_rotation(disp_refr)) {
+            lv_display_rotate_area(disp_refr, sync_area);
+        }
+#endif
+        lv_draw_buf_copy(off_screen, sync_area, on_screen, sync_area);
+        if(off_screen2 != on_screen)
+            lv_draw_buf_copy(off_screen2, sync_area, on_screen, sync_area);
+    }
+
+    /*Clear sync areas*/
+    lv_ll_clear(&disp_refr->sync_areas);
+    LV_PROFILER_REFR_END;
+}
+
+/**
+ * Refresh the joined areas
+ */
+static void refr_invalid_areas(void)
+{
+    if(disp_refr->inv_p == 0) return;
+    LV_PROFILER_REFR_BEGIN;
+
+    /*Notify the display driven rendering has started*/
+    lv_display_send_event(disp_refr, LV_EVENT_RENDER_START, NULL);
+
+    /*Find the last area which will be drawn*/
+    int32_t i;
+    int32_t last_i = 0;
+    for(i = disp_refr->inv_p - 1; i >= 0; i--) {
+        if(disp_refr->inv_area_joined[i] == 0) {
+            last_i = i;
+            break;
+        }
+    }
+
+    disp_refr->last_area = 0;
+    disp_refr->last_part = 0;
+    disp_refr->rendering_in_progress = true;
+
+    for(i = 0; i < (int32_t)disp_refr->inv_p; i++) {
+        /*Refresh the unjoined areas*/
+        if(disp_refr->inv_area_joined[i]) continue;
+
+        if(i == last_i) disp_refr->last_area = 1;
+        disp_refr->last_part = 0;
+
+        lv_area_t inv_a = disp_refr->inv_areas[i];
+        if(disp_refr->render_mode == LV_DISPLAY_RENDER_MODE_PARTIAL) {
+            /*Calculate the max row num*/
+            int32_t w = lv_area_get_width(&inv_a);
+            int32_t h = lv_area_get_height(&inv_a);
+
+            int32_t max_row = get_max_row(disp_refr, w, h);
+
+            int32_t row;
+            int32_t row_last = 0;
+            lv_area_t sub_area;
+            sub_area.x1 = inv_a.x1;
+            sub_area.x2 = inv_a.x2;
+            int32_t y_off = 0;
+            for(row = inv_a.y1; row + max_row - 1 <= inv_a.y2; row += max_row) {
+                /*Calc. the next y coordinates of draw_buf*/
+                sub_area.y1 = row;
+                sub_area.y2 = row + max_row - 1;
+                if(sub_area.y2 > inv_a.y2) sub_area.y2 = inv_a.y2;
+                row_last = sub_area.y2;
+                if(inv_a.y2 == row_last) disp_refr->last_part = 1;
+                refr_area(&sub_area, y_off);
+                y_off += lv_area_get_height(&sub_area);
+                draw_buf_flush(disp_refr);
+            }
+
+            /*If the last y coordinates are not handled yet ...*/
+            if(inv_a.y2 != row_last) {
+                /*Calc. the next y coordinates of draw_buf*/
+                sub_area.y1 = row;
+                sub_area.y2 = inv_a.y2;
+                disp_refr->last_part = 1;
+                refr_area(&sub_area, y_off);
+                y_off += lv_area_get_height(&sub_area);
+                draw_buf_flush(disp_refr);
+            }
+        }
+        else if(disp_refr->render_mode == LV_DISPLAY_RENDER_MODE_FULL ||
+                disp_refr->render_mode == LV_DISPLAY_RENDER_MODE_DIRECT) {
+            disp_refr->last_part = 1;
+            refr_area(&disp_refr->inv_areas[i], 0);
+            draw_buf_flush(disp_refr);
+        }
+    }
+
+    lv_display_send_event(disp_refr, LV_EVENT_RENDER_READY, NULL);
+    disp_refr->rendering_in_progress = false;
+    LV_PROFILER_REFR_END;
+}
+
+/**
+ * Reshape the draw buffer if required
+ * @param layer  pointer to a layer which will be drawn
+ */
+static void layer_reshape_draw_buf(lv_layer_t * layer, uint32_t stride)
+{
+    lv_draw_buf_t * ret = lv_draw_buf_reshape(
+                              layer->draw_buf,
+                              layer->color_format,
+                              lv_area_get_width(&layer->buf_area),
+                              lv_area_get_height(&layer->buf_area),
+                              stride);
+    LV_UNUSED(ret);
+    LV_ASSERT_NULL(ret);
+}
+
+/**
+ * Refresh an area if there is Virtual Display Buffer
+ * @param area_p  pointer to an area to refresh
+ */
+static void refr_area(const lv_area_t * area_p, int32_t y_offset)
+{
+    LV_PROFILER_REFR_BEGIN;
+    lv_layer_t * layer = disp_refr->layer_head;
+    layer->draw_buf = disp_refr->buf_act;
+    layer->_clip_area = *area_p;
+    layer->phy_clip_area = *area_p;
+    layer->partial_y_offset = y_offset;
+
+    if(disp_refr->render_mode == LV_DISPLAY_RENDER_MODE_PARTIAL) {
+        /*In partial mode render this area to the buffer*/
+        layer->buf_area = *area_p;
+        layer_reshape_draw_buf(layer, LV_STRIDE_AUTO);
+    }
+    else if(disp_refr->render_mode == LV_DISPLAY_RENDER_MODE_DIRECT ||
+            disp_refr->render_mode == LV_DISPLAY_RENDER_MODE_FULL) {
+        /*In direct mode and full mode the buffer area is always the whole screen, not considering rotation*/
+        layer->buf_area.x1 = 0;
+        layer->buf_area.y1 = 0;
+        if(lv_display_get_matrix_rotation(disp_refr)) {
+            layer->buf_area.x2 = lv_display_get_original_horizontal_resolution(disp_refr) - 1;
+            layer->buf_area.y2 = lv_display_get_original_vertical_resolution(disp_refr) - 1;
+        }
+        else {
+            layer->buf_area.x2 = lv_display_get_horizontal_resolution(disp_refr) - 1;
+            layer->buf_area.y2 = lv_display_get_vertical_resolution(disp_refr) - 1;
+        }
+        layer_reshape_draw_buf(layer, disp_refr->stride_is_auto ? LV_STRIDE_AUTO : layer->draw_buf->header.stride);
+    }
+
+    /*Try to divide the area to smaller tiles*/
+    uint32_t tile_cnt = 1;
+    int32_t tile_h = lv_area_get_height(area_p);
+    if(LV_COLOR_FORMAT_IS_INDEXED(layer->color_format) == false) {
+        /* Assume that the buffer size (can be screen sized or smaller in case of partial mode)
+         * and max tile size are the optimal scenario. From this calculate the ideal tile size
+         * and set the tile count and tile height accordingly.
+         */
+        uint32_t max_tile_cnt = disp_refr->tile_cnt;
+        uint32_t total_buf_size = layer->draw_buf->data_size;
+        uint32_t ideal_tile_size = total_buf_size / max_tile_cnt;
+        uint32_t area_buf_size = lv_area_get_size(area_p) * lv_color_format_get_size(layer->color_format);
+
+        tile_cnt = (area_buf_size + (ideal_tile_size - 1)) / ideal_tile_size; /*Round up*/
+        tile_h = lv_area_get_height(area_p) / tile_cnt;
+    }
+
+    if(tile_cnt == 1) {
+        refr_configured_layer(layer);
+    }
+    else {
+        /* Don't draw to the layers buffer of the display but create smaller dummy layers which are using the
+         * display's layer buffer. These will be the tiles. By using tiles it's more likely that there will
+         * be independent areas for each draw unit. */
+        lv_layer_t * tile_layers = lv_malloc(tile_cnt * sizeof(lv_layer_t));
+        LV_ASSERT_MALLOC(tile_layers);
+        if(tile_layers == NULL) {
+            disp_refr->refreshed_area = *area_p;
+            LV_PROFILER_REFR_END;
+            return;
+        }
+        uint32_t i;
+        for(i = 0; i < tile_cnt; i++) {
+            lv_area_t tile_area;
+            lv_area_set(&tile_area, area_p->x1, area_p->y1 + i * tile_h,
+                        area_p->x2, area_p->y1 + (i + 1) * tile_h - 1);
+
+            if(i == tile_cnt - 1) {
+                tile_area.y2 = area_p->y2;
+            }
+
+            lv_layer_t * tile_layer = &tile_layers[i];
+            lv_draw_layer_init(tile_layer, NULL, layer->color_format, &tile_area);
+            tile_layer->buf_area = layer->buf_area; /*the buffer is still large*/
+            tile_layer->draw_buf = layer->draw_buf;
+            refr_configured_layer(tile_layer);
+        }
+
+
+        /*Wait until all tiles are ready and destroy remove them*/
+        for(i = 0; i < tile_cnt; i++) {
+            lv_layer_t * tile_layer = &tile_layers[i];
+            while(tile_layer->draw_task_head) {
+                lv_draw_dispatch_wait_for_request();
+                lv_draw_dispatch();
+            }
+
+            lv_layer_t * layer_i = disp_refr->layer_head;
+            while(layer_i) {
+                if(layer_i->next == tile_layer) {
+                    layer_i->next = tile_layer->next;
+                    break;
+                }
+                layer_i = layer_i->next;
+            }
+
+            if(disp_refr->layer_deinit) disp_refr->layer_deinit(disp_refr, tile_layer);
+        }
+        lv_free(tile_layers);
+    }
+
+    disp_refr->refreshed_area = *area_p;
+    LV_PROFILER_REFR_END;
+}
+
+static void refr_configured_layer(lv_layer_t * layer)
+{
+    LV_PROFILER_REFR_BEGIN;
+
+    lv_layer_reset(layer);
+
+#if LV_DRAW_TRANSFORM_USE_MATRIX
+    if(lv_display_get_matrix_rotation(disp_refr)) {
+        const lv_display_rotation_t rotation = lv_display_get_rotation(disp_refr);
+        if(rotation != LV_DISPLAY_ROTATION_0) {
+            lv_display_rotate_area(disp_refr, &layer->phy_clip_area);
+
+            /**
+             * The screen rotation direction defined by LVGL is opposite to the drawing angle.
+             * Use direct matrix assignment to reduce precision loss and improve efficiency.
+             */
+            switch(rotation) {
+                case LV_DISPLAY_ROTATION_90:
+                    /**
+                     * lv_matrix_rotate(&layer->matrix, 270);
+                     * lv_matrix_translate(&layer->matrix, -disp_refr->ver_res, 0);
+                     */
+                    layer->matrix.m[0][0] = 0;
+                    layer->matrix.m[0][1] = 1;
+                    layer->matrix.m[0][2] = 0;
+                    layer->matrix.m[1][0] = -1;
+                    layer->matrix.m[1][1] = 0;
+                    layer->matrix.m[1][2] = disp_refr->ver_res;
+                    break;
+
+                case LV_DISPLAY_ROTATION_180:
+                    /**
+                     * lv_matrix_rotate(&layer->matrix, 180);
+                     * lv_matrix_translate(&layer->matrix, -disp_refr->hor_res, -disp_refr->ver_res);
+                     */
+                    layer->matrix.m[0][0] = -1;
+                    layer->matrix.m[0][1] = 0;
+                    layer->matrix.m[0][2] = disp_refr->hor_res;
+                    layer->matrix.m[1][0] = 0;
+                    layer->matrix.m[1][1] = -1;
+                    layer->matrix.m[1][2] = disp_refr->ver_res;
+                    break;
+
+                case LV_DISPLAY_ROTATION_270:
+                    /**
+                     * lv_matrix_rotate(&layer->matrix, 90);
+                     * lv_matrix_translate(&layer->matrix, 0, -disp_refr->hor_res);
+                     */
+                    layer->matrix.m[0][0] = 0;
+                    layer->matrix.m[0][1] = -1;
+                    layer->matrix.m[0][2] = disp_refr->hor_res;
+                    layer->matrix.m[1][0] = 1;
+                    layer->matrix.m[1][1] = 0;
+                    layer->matrix.m[1][2] = 0;
+                    break;
+
+                default:
+                    LV_LOG_WARN("Invalid rotation: %d", rotation);
+                    break;
+            }
+        }
+    }
+#endif /* LV_DRAW_TRANSFORM_USE_MATRIX */
+
+    /* In single buffered mode wait here until the buffer is freed.
+     * Else we would draw into the buffer while it's still being transferred to the display*/
+    if(!lv_display_is_double_buffered(disp_refr)) {
+        wait_for_flushing(disp_refr);
+    }
+    /*If the screen is transparent initialize it when the flushing is ready*/
+    if(lv_color_format_has_alpha(disp_refr->color_format)) {
+        lv_area_t clear_area = layer->_clip_area;
+        lv_area_move(&clear_area, -layer->buf_area.x1, -layer->buf_area.y1);
+        lv_draw_buf_clear(layer->draw_buf, &clear_area);
+    }
+
+    lv_obj_t * top_act_scr = NULL;
+    lv_obj_t * top_prev_scr = NULL;
+
+    /*Get the most top object which is not covered by others*/
+    top_act_scr = lv_refr_get_top_obj(&layer->_clip_area, lv_display_get_screen_active(disp_refr));
+    if(disp_refr->prev_scr) {
+        top_prev_scr = lv_refr_get_top_obj(&layer->_clip_area, disp_refr->prev_scr);
+    }
+
+    /*Draw a bottom layer background if there is no top object*/
+    if(top_act_scr == NULL && top_prev_scr == NULL) {
+        refr_obj_and_children(layer, lv_display_get_layer_bottom(disp_refr));
+    }
+
+    if(disp_refr->draw_prev_over_act) {
+        if(top_act_scr == NULL) top_act_scr = disp_refr->act_scr;
+        refr_obj_and_children(layer, top_act_scr);
+
+        /*Refresh the previous screen if any*/
+        if(disp_refr->prev_scr) {
+            if(top_prev_scr == NULL) top_prev_scr = disp_refr->prev_scr;
+            refr_obj_and_children(layer, top_prev_scr);
+        }
+    }
+    else {
+        /*Refresh the previous screen if any*/
+        if(disp_refr->prev_scr) {
+            if(top_prev_scr == NULL) top_prev_scr = disp_refr->prev_scr;
+            refr_obj_and_children(layer, top_prev_scr);
+        }
+
+        if(top_act_scr == NULL) top_act_scr = disp_refr->act_scr;
+        refr_obj_and_children(layer, top_act_scr);
+    }
+
+    /*Also refresh top and sys layer unconditionally*/
+    refr_obj_and_children(layer, lv_display_get_layer_top(disp_refr));
+    refr_obj_and_children(layer, lv_display_get_layer_sys(disp_refr));
+
+    LV_PROFILER_REFR_END;
+}
+
+/**
+ * Make the refreshing from an object. Draw all its children and the youngers too.
+ * @param top_p pointer to an objects. Start the drawing from it.
+ * @param mask_p pointer to an area, the objects will be drawn only here
+ */
+static void refr_obj_and_children(lv_layer_t * layer, lv_obj_t * top_obj)
+{
+    /*Normally always will be a top_obj (at least the screen)
+     *but in special cases (e.g. if the screen has alpha) it won't.
+     *In this case use the screen directly*/
+    if(top_obj == NULL) top_obj = lv_display_get_screen_active(disp_refr);
+    if(top_obj == NULL) return;  /*Shouldn't happen*/
+
+    LV_PROFILER_REFR_BEGIN;
+    /*Draw the 'younger' sibling objects because they can be on top_obj*/
+    lv_obj_t * parent;
+    lv_obj_t * border_p = top_obj;
+
+    parent = lv_obj_get_parent(top_obj);
+
+    /*Calculate the recolor before the parent*/
+    if(parent) {
+        layer->recolor = lv_obj_get_style_recolor_recursive(parent, LV_PART_MAIN);
+    }
+
+    /*Refresh the top object and its children*/
+    lv_obj_refr(layer, top_obj);
+
+    /*Do until not reach the screen*/
+    while(parent != NULL) {
+        bool go = false;
+        uint32_t i;
+        uint32_t child_cnt = lv_obj_get_child_count(parent);
+        for(i = 0; i < child_cnt; i++) {
+            lv_obj_t * child = parent->spec_attr->children[i];
+            if(!go) {
+                if(child == border_p) go = true;
+            }
+            else {
+                /*Refresh the objects*/
+                lv_obj_refr(layer, child);
+            }
+        }
+
+        /*Call the post draw function of the parents of the to object*/
+        lv_obj_send_event(parent, LV_EVENT_DRAW_POST_BEGIN, (void *)layer);
+        lv_obj_send_event(parent, LV_EVENT_DRAW_POST, (void *)layer);
+        lv_obj_send_event(parent, LV_EVENT_DRAW_POST_END, (void *)layer);
+
+        /*The new border will be the last parents,
+         *so the 'younger' brothers of parent will be refreshed*/
+        border_p = parent;
+        /*Go a level deeper*/
+        parent = lv_obj_get_parent(parent);
+    }
+    LV_PROFILER_REFR_END;
+}
+
+static lv_result_t layer_get_area(lv_layer_t * layer, lv_obj_t * obj, lv_layer_type_t layer_type,
+                                  lv_area_t * layer_area_out, lv_area_t * obj_draw_size_out)
+{
+    int32_t ext_draw_size = lv_obj_get_ext_draw_size(obj);
+    lv_obj_get_coords(obj, obj_draw_size_out);
+    lv_area_increase(obj_draw_size_out, ext_draw_size, ext_draw_size);
+
+    if(layer_type == LV_LAYER_TYPE_TRANSFORM) {
+        /*Get the transformed area and clip it to the current clip area.
+         *This area needs to be updated on the screen.*/
+        lv_area_t clip_coords_for_obj;
+        lv_area_t tranf_coords = *obj_draw_size_out;
+        lv_obj_get_transformed_area(obj, &tranf_coords, LV_OBJ_POINT_TRANSFORM_FLAG_NONE);
+        if(!lv_area_intersect(&clip_coords_for_obj, &layer->_clip_area, &tranf_coords)) {
+            return LV_RESULT_INVALID;
+        }
+
+        /*Transform back (inverse) the transformed area.
+         *It will tell which area of the non-transformed widget needs to be redrawn
+         *in order to cover transformed area after transformation.*/
+        lv_area_t inverse_clip_coords_for_obj = clip_coords_for_obj;
+        lv_obj_get_transformed_area(obj, &inverse_clip_coords_for_obj, LV_OBJ_POINT_TRANSFORM_FLAG_INVERSE);
+        if(!lv_area_intersect(&inverse_clip_coords_for_obj, &inverse_clip_coords_for_obj, obj_draw_size_out)) {
+            return LV_RESULT_INVALID;
+        }
+
+        *layer_area_out = inverse_clip_coords_for_obj;
+        lv_area_increase(layer_area_out, 5, 5); /*To avoid rounding error*/
+    }
+    else if(layer_type == LV_LAYER_TYPE_SIMPLE) {
+        lv_area_t clip_coords_for_obj;
+        if(!lv_area_intersect(&clip_coords_for_obj, &layer->_clip_area, obj_draw_size_out)) {
+            return LV_RESULT_INVALID;
+        }
+        *layer_area_out = clip_coords_for_obj;
+    }
+    else {
+        LV_LOG_WARN("Unhandled layer type");
+        return LV_RESULT_INVALID;
+    }
+
+    return LV_RESULT_OK;
+}
+
+static bool alpha_test_area_on_obj(lv_obj_t * obj, const lv_area_t * area)
+{
+    /*Test for alpha by assuming there is no alpha. If it fails, fall back to rendering with alpha*/
+    /*If the layer area is not fully on the object, it can't fully cover it*/
+    if(!lv_area_is_on(area, &obj->coords)) return true;
+
+    lv_cover_check_info_t info;
+    info.res = LV_COVER_RES_COVER;
+    info.area = area;
+    lv_obj_send_event(obj, LV_EVENT_COVER_CHECK, &info);
+    if(info.res == LV_COVER_RES_COVER) return false;
+    else return true;
+}
+
+#if LV_DRAW_TRANSFORM_USE_MATRIX
+
+static bool obj_get_matrix(lv_obj_t * obj, lv_matrix_t * matrix)
+{
+    lv_matrix_identity(matrix);
+
+    const lv_matrix_t * obj_matrix = lv_obj_get_transform(obj);
+    if(obj_matrix) {
+        lv_matrix_translate(matrix, obj->coords.x1, obj->coords.y1);
+        lv_matrix_multiply(matrix, obj_matrix);
+        lv_matrix_translate(matrix, -obj->coords.x1, -obj->coords.y1);
+        return true;
+    }
+
+    lv_point_t pivot = {
+        .x = lv_obj_get_style_transform_pivot_x(obj, LV_PART_MAIN),
+        .y = lv_obj_get_style_transform_pivot_y(obj, LV_PART_MAIN)
+    };
+
+    pivot.x = obj->coords.x1 + lv_pct_to_px(pivot.x, lv_area_get_width(&obj->coords));
+    pivot.y = obj->coords.y1 + lv_pct_to_px(pivot.y, lv_area_get_height(&obj->coords));
+
+    int32_t rotation = lv_obj_get_style_transform_rotation(obj, LV_PART_MAIN);
+    int32_t scale_x = lv_obj_get_style_transform_scale_x(obj, LV_PART_MAIN);
+    int32_t scale_y = lv_obj_get_style_transform_scale_y(obj, LV_PART_MAIN);
+    int32_t skew_x = lv_obj_get_style_transform_skew_x(obj, LV_PART_MAIN);
+    int32_t skew_y = lv_obj_get_style_transform_skew_y(obj, LV_PART_MAIN);
+
+    if(scale_x <= 0 || scale_y <= 0) {
+        /* NOT draw if scale is negative or zero */
+        return false;
+    }
+
+    /* generate the obj matrix */
+    lv_matrix_translate(matrix, pivot.x, pivot.y);
+    if(rotation != 0) {
+        lv_matrix_rotate(matrix, rotation * 0.1f);
+    }
+
+    if(scale_x != LV_SCALE_NONE || scale_y != LV_SCALE_NONE) {
+        lv_matrix_scale(
+            matrix,
+            (float)scale_x / LV_SCALE_NONE,
+            (float)scale_y / LV_SCALE_NONE
+        );
+    }
+
+    if(skew_x != 0 || skew_y != 0) {
+        lv_matrix_skew(matrix, skew_x, skew_y);
+    }
+
+    lv_matrix_translate(matrix, -pivot.x, -pivot.y);
+    return true;
+}
+
+static void refr_obj_matrix(lv_layer_t * layer, lv_obj_t * obj)
+{
+    LV_PROFILER_REFR_BEGIN;
+    lv_matrix_t obj_matrix;
+    if(!obj_get_matrix(obj, &obj_matrix)) {
+        /* NOT draw if obj matrix is not available */
+        LV_PROFILER_REFR_END;
+        return;
+    }
+
+    lv_matrix_t matrix_inv;
+    if(!lv_matrix_inverse(&matrix_inv, &obj_matrix)) {
+        /* NOT draw if matrix is not invertible */
+        LV_PROFILER_REFR_END;
+        return;
+    }
+
+    /* save original matrix */
+    lv_matrix_t ori_matrix = layer->matrix;
+
+    /* apply the obj matrix */
+    lv_matrix_multiply(&layer->matrix, &obj_matrix);
+
+    /* calculate clip area without transform */
+    lv_area_t clip_area = layer->_clip_area;
+    lv_area_t clip_area_ori = layer->_clip_area;
+    clip_area = lv_matrix_transform_area(&matrix_inv, &clip_area);
+
+    /* increase the clip area by 1 pixel to avoid rounding errors */
+    if(!lv_matrix_is_identity_or_translation(&obj_matrix)) {
+        lv_area_increase(&clip_area, 1, 1);
+    }
+
+    layer->_clip_area = clip_area;
+
+    /* redraw obj */
+    lv_obj_redraw(layer, obj);
+
+    /* restore original matrix */
+    layer->matrix = ori_matrix;
+    /* restore clip area */
+    layer->_clip_area = clip_area_ori;
+    LV_PROFILER_REFR_END;
+}
+
+static bool refr_check_obj_clip_overflow(lv_layer_t * layer, lv_obj_t * obj)
+{
+    if(lv_obj_get_style_transform_rotation(obj, LV_PART_MAIN) == 0) {
+        return false;
+    }
+
+    /*Truncate the area to the object*/
+    lv_area_t obj_coords;
+    int32_t ext_size = lv_obj_get_ext_draw_size(obj);
+    lv_area_copy(&obj_coords, &obj->coords);
+    lv_area_increase(&obj_coords, ext_size, ext_size);
+
+    lv_obj_get_transformed_area(obj, &obj_coords, LV_OBJ_POINT_TRANSFORM_FLAG_RECURSIVE);
+
+    lv_area_t clip_coords_for_obj;
+    if(!lv_area_intersect(&clip_coords_for_obj, &layer->_clip_area, &obj_coords)) {
+        return false;
+    }
+
+    bool has_clip = lv_memcmp(&clip_coords_for_obj, &obj_coords, sizeof(lv_area_t)) != 0;
+    return has_clip;
+}
+
+#endif /* LV_DRAW_TRANSFORM_USE_MATRIX */
+
+static uint32_t get_max_row(lv_display_t * disp, int32_t area_w, int32_t area_h)
+{
+    lv_color_format_t cf = disp->color_format;
+    uint32_t stride = lv_draw_buf_width_to_stride(area_w, cf);
+    uint32_t overhead = LV_COLOR_INDEXED_PALETTE_SIZE(cf) * sizeof(lv_color32_t);
+
+    if(stride == 0) {
+        LV_LOG_WARN("Invalid stride. Value is 0");
+        return 0;
+    }
+
+    int32_t max_row = (uint32_t)(disp->buf_act->data_size - overhead) / stride;
+
+    if(max_row > area_h) max_row = area_h;
+
+    /*Round down the lines of draw_buf if rounding is added*/
+    lv_area_t tmp;
+    tmp.x1 = 0;
+    tmp.x2 = 0;
+    tmp.y1 = 0;
+
+    int32_t h_tmp = max_row;
+    do {
+        tmp.y2 = h_tmp - 1;
+        lv_display_send_event(disp_refr, LV_EVENT_INVALIDATE_AREA, &tmp);
+
+        /*If this height fits into `max_row` then fine*/
+        if(lv_area_get_height(&tmp) <= max_row) break;
+
+        /*Decrement the height of the area until it fits into `max_row` after rounding*/
+        h_tmp--;
+    } while(h_tmp > 0);
+
+    if(h_tmp <= 0) {
+        LV_LOG_WARN("Can't set draw_buf height using the round function. (Wrong round_cb or too "
+                    "small draw_buf)");
+        return 0;
+    }
+    else {
+        max_row = tmp.y2 + 1;
+    }
+
+    return max_row;
+}
+
+/**
+ * Flush the content of the draw buffer
+ */
+static void draw_buf_flush(lv_display_t * disp)
+{
+    /*Flush the rendered content to the display*/
+    lv_layer_t * layer = disp->layer_head;
+
+    while(layer->draw_task_head) {
+        lv_draw_dispatch_wait_for_request();
+        lv_draw_dispatch();
+    }
+
+    /* In double buffered mode wait until the other buffer is freed
+     * and driver is ready to receive the new buffer.
+     * If we need to wait here it means that the content of one buffer is being sent to display
+     * and other buffer already contains the new rendered image. */
+    if(lv_display_is_double_buffered(disp)) {
+        wait_for_flushing(disp_refr);
+    }
+
+    disp->flushing = 1;
+
+    if(disp->last_area && disp->last_part) disp->flushing_last = 1;
+    else disp->flushing_last = 0;
+
+    bool flushing_last = disp->flushing_last;
+
+    if(disp->flush_cb) {
+        call_flush_cb(disp, &disp->refreshed_area, layer->draw_buf->data);
+    }
+    /*If there are 2 buffers swap them. With direct mode swap only on the last area*/
+    if(lv_display_is_double_buffered(disp) && (disp->render_mode != LV_DISPLAY_RENDER_MODE_DIRECT || flushing_last)) {
+        if(disp->buf_act == disp->buf_1) {
+            disp->buf_act = disp->buf_2;
+        }
+        else if(disp->buf_act == disp->buf_2) {
+            disp->buf_act = disp->buf_3 ? disp->buf_3 : disp->buf_1;
+        }
+        else {
+            disp->buf_act = disp->buf_1;
+        }
+    }
+}
+
+static void call_flush_cb(lv_display_t * disp, const lv_area_t * area, uint8_t * px_map)
+{
+    LV_PROFILER_REFR_BEGIN;
+    LV_TRACE_REFR("Calling flush_cb on (%d;%d)(%d;%d) area with %p image pointer",
+                  (int)area->x1, (int)area->y1, (int)area->x2, (int)area->y2, (void *)px_map);
+
+    lv_area_t offset_area = {
+        .x1 = area->x1 + disp->offset_x,
+        .y1 = area->y1 + disp->offset_y,
+        .x2 = area->x2 + disp->offset_x,
+        .y2 = area->y2 + disp->offset_y
+    };
+
+    lv_display_send_event(disp, LV_EVENT_FLUSH_START, &offset_area);
+
+    /*For backward compatibility support LV_COLOR_16_SWAP (from v8)*/
+#if defined(LV_COLOR_16_SWAP) && LV_COLOR_16_SWAP
+    lv_draw_sw_rgb565_swap(px_map, lv_area_get_size(&offset_area));
+#endif
+
+    disp->flush_cb(disp, &offset_area, px_map);
+    lv_display_send_event(disp, LV_EVENT_FLUSH_FINISH, &offset_area);
+
+    LV_PROFILER_REFR_END;
+}
+
+static void wait_for_flushing(lv_display_t * disp)
+{
+    LV_PROFILER_REFR_BEGIN;
+    LV_LOG_TRACE("begin");
+
+    lv_display_send_event(disp, LV_EVENT_FLUSH_WAIT_START, NULL);
+
+    if(disp->flush_wait_cb) {
+        if(disp->flushing) {
+            disp->flush_wait_cb(disp);
+            disp->flushing = 0;
+        }
+    }
+    else {
+        while(disp->flushing);
+    }
+    disp->flushing_last = 0;
+
+    lv_display_send_event(disp, LV_EVENT_FLUSH_WAIT_FINISH, NULL);
+
+    LV_LOG_TRACE("end");
+    LV_PROFILER_REFR_END;
+}

+ 74 - 0
components/easylvgl/lvgl9/src/core/lv_refr.h

@@ -0,0 +1,74 @@
+/**
+ * @file lv_refr.h
+ *
+ */
+
+#ifndef LV_REFR_H
+#define LV_REFR_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/*********************
+ *      INCLUDES
+ *********************/
+#include "lv_obj.h"
+#include "../display/lv_display.h"
+#include "../misc/lv_types.h"
+
+/*********************
+ *      DEFINES
+ *********************/
+
+/**********************
+ *      TYPEDEFS
+ **********************/
+
+/**********************
+ *  STATIC PROTOTYPES
+ **********************/
+
+/**********************
+ *  STATIC VARIABLES
+ **********************/
+
+/**********************
+ *      MACROS
+ **********************/
+
+/**********************
+ *   GLOBAL FUNCTIONS
+ **********************/
+
+/**
+ * Redraw the invalidated areas now.
+ * Normally the redrawing is periodically executed in `lv_timer_handler` but a long blocking process
+ * can prevent the call of `lv_timer_handler`. In this case if the GUI is updated in the process
+ * (e.g. progress bar) this function can be called when the screen should be updated.
+ * @param disp pointer to display to refresh. NULL to refresh all displays.
+ */
+void lv_refr_now(lv_display_t * disp);
+
+/**
+ * Redrawn on object and all its children using the passed draw context
+ * @param layer pointer to a layer where to draw.
+ * @param obj   the start object from the redraw should start
+ */
+void lv_obj_redraw(lv_layer_t * layer, lv_obj_t * obj);
+
+/**
+ * Called periodically to handle the refreshing
+ * @param timer pointer to the timer itself, or `NULL`
+ */
+void lv_display_refr_timer(lv_timer_t * timer);
+
+/**********************
+ *   STATIC FUNCTIONS
+ **********************/
+
+#ifdef __cplusplus
+} /*extern "C"*/
+#endif
+
+#endif /*LV_REFR_H*/

+ 84 - 0
components/easylvgl/lvgl9/src/core/lv_refr_private.h

@@ -0,0 +1,84 @@
+/**
+ * @file lv_refr_private.h
+ *
+ */
+
+#ifndef LV_REFR_PRIVATE_H
+#define LV_REFR_PRIVATE_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/*********************
+ *      INCLUDES
+ *********************/
+
+#include "lv_refr.h"
+
+/*********************
+ *      DEFINES
+ *********************/
+
+/**********************
+ *      TYPEDEFS
+ **********************/
+
+/**********************
+ * GLOBAL PROTOTYPES
+ **********************/
+
+/**
+ * Initialize the screen refresh subsystem
+ */
+void lv_refr_init(void);
+
+/**
+ * Deinitialize the screen refresh subsystem
+ */
+void lv_refr_deinit(void);
+
+/**
+ * Invalidate an area on display to redraw it
+ * @param area_p pointer to area which should be invalidated (NULL: delete the invalidated areas)
+ * @param disp pointer to display where the area should be invalidated (NULL can be used if there is
+ * only one display)
+ */
+void lv_inv_area(lv_display_t * disp, const lv_area_t * area_p);
+
+/**
+ * Get the display which is being refreshed
+ * @return the display being refreshed
+ */
+lv_display_t * lv_refr_get_disp_refreshing(void);
+
+/**
+ * Set the display which is being refreshed
+ * @param disp the display being refreshed
+ */
+void lv_refr_set_disp_refreshing(lv_display_t * disp);
+
+/**
+ * Search the most top object which fully covers an area
+ * @param area_p pointer to an area
+ * @param obj the first object to start the searching (typically a screen)
+ * @return
+ */
+lv_obj_t * lv_refr_get_top_obj(const lv_area_t * area_p, lv_obj_t * obj);
+
+/**
+ * Render an object to a layer
+ * @param layer target drawing layer
+ * @param obj   object to render
+ */
+void lv_obj_refr(lv_layer_t * layer, lv_obj_t * obj);
+
+/**********************
+ *      MACROS
+ **********************/
+
+#ifdef __cplusplus
+} /*extern "C"*/
+#endif
+
+#endif /*LV_REFR_PRIVATE_H*/

+ 1372 - 0
components/easylvgl/lvgl9/src/display/lv_display.c

@@ -0,0 +1,1372 @@
+/**
+ * @file lv_display.c
+ *
+ */
+
+/*********************
+ *      INCLUDES
+ *********************/
+#include "../display/lv_display_private.h"
+#include "../misc/lv_event_private.h"
+#include "../misc/lv_anim_private.h"
+#include "../draw/lv_draw_private.h"
+#include "../core/lv_obj_private.h"
+#include "lv_display.h"
+#include "../misc/lv_math.h"
+#include "../core/lv_refr_private.h"
+#include "../stdlib/lv_string.h"
+#include "../themes/lv_theme.h"
+#include "../core/lv_global.h"
+#include "../others/sysmon/lv_sysmon.h"
+
+#if LV_USE_DRAW_SW
+    #include "../draw/sw/lv_draw_sw.h"
+#endif
+
+/*********************
+ *      DEFINES
+ *********************/
+#define disp_def LV_GLOBAL_DEFAULT()->disp_default
+#define disp_ll_p &(LV_GLOBAL_DEFAULT()->disp_ll)
+
+/**********************
+ *      TYPEDEFS
+ **********************/
+
+/**********************
+ *  STATIC PROTOTYPES
+ **********************/
+static lv_obj_tree_walk_res_t invalidate_layout_cb(lv_obj_t * obj, void * user_data);
+static void update_resolution(lv_display_t * disp);
+static void scr_load_internal(lv_obj_t * scr);
+static void scr_load_anim_start(lv_anim_t * a);
+static void opa_scale_anim(void * obj, int32_t v);
+static void set_x_anim(void * obj, int32_t v);
+static void set_y_anim(void * obj, int32_t v);
+static void scr_anim_completed(lv_anim_t * a);
+static bool is_out_anim(lv_screen_load_anim_t a);
+static void disp_event_cb(lv_event_t * e);
+
+/**********************
+ *  STATIC VARIABLES
+ **********************/
+
+/**********************
+ *      MACROS
+ **********************/
+
+/**********************
+ *   GLOBAL FUNCTIONS
+ **********************/
+
+lv_display_t * lv_display_create(int32_t hor_res, int32_t ver_res)
+{
+    lv_display_t * disp = lv_ll_ins_head(disp_ll_p);
+    LV_ASSERT_MALLOC(disp);
+    if(!disp) return NULL;
+
+    lv_memzero(disp, sizeof(lv_display_t));
+
+    disp->hor_res          = hor_res;
+    disp->ver_res          = ver_res;
+    disp->physical_hor_res = -1;
+    disp->physical_ver_res = -1;
+    disp->offset_x         = 0;
+    disp->offset_y         = 0;
+    disp->antialiasing     = LV_COLOR_DEPTH > 8 ? 1 : 0;
+    disp->dpi              = LV_DPI_DEF;
+    disp->color_format = LV_COLOR_FORMAT_NATIVE;
+
+
+#if defined(LV_DRAW_SW_DRAW_UNIT_CNT) && (LV_DRAW_SW_DRAW_UNIT_CNT != 0)
+    disp->tile_cnt = LV_DRAW_SW_DRAW_UNIT_CNT;
+#else
+    disp->tile_cnt = 1;
+#endif
+
+    disp->layer_head = lv_malloc(sizeof(lv_layer_t));
+    LV_ASSERT_MALLOC(disp->layer_head);
+    if(disp->layer_head == NULL) return NULL;
+    lv_layer_init(disp->layer_head);
+
+    if(disp->layer_init) disp->layer_init(disp, disp->layer_head);
+    disp->layer_head->buf_area.x1 = 0;
+    disp->layer_head->buf_area.y1 = 0;
+    disp->layer_head->buf_area.x2 = hor_res - 1;
+    disp->layer_head->buf_area.y2 = ver_res - 1;
+    disp->layer_head->color_format = disp->color_format;
+
+    disp->inv_en_cnt = 1;
+    disp->last_activity_time = lv_tick_get();
+
+    lv_ll_init(&disp->sync_areas, sizeof(lv_area_t));
+
+    lv_display_t * disp_def_tmp = disp_def;
+    disp_def                 = disp; /*Temporarily change the default screen to create the default screens on the
+                                        new display*/
+    /*Create a refresh timer*/
+    disp->refr_timer = lv_timer_create(lv_display_refr_timer, LV_DEF_REFR_PERIOD, disp);
+    LV_ASSERT_MALLOC(disp->refr_timer);
+    if(disp->refr_timer == NULL) {
+        lv_free(disp);
+        return NULL;
+    }
+
+#if LV_USE_THEME_DEFAULT
+    if(lv_theme_default_is_inited() == false) {
+        disp->theme = lv_theme_default_init(disp, lv_palette_main(LV_PALETTE_BLUE), lv_palette_main(LV_PALETTE_RED),
+                                            LV_THEME_DEFAULT_DARK, LV_FONT_DEFAULT);
+    }
+    else {
+        disp->theme = lv_theme_default_get();
+    }
+#elif LV_USE_THEME_SIMPLE
+    if(lv_theme_simple_is_inited() == false) {
+        disp->theme = lv_theme_simple_init(disp);
+    }
+    else {
+        disp->theme = lv_theme_simple_get();
+    }
+#elif LV_USE_THEME_MONO
+    if(lv_theme_mono_is_inited() == false) {
+        disp->theme = lv_theme_mono_init(disp, false, LV_FONT_DEFAULT);
+    }
+    else {
+        disp->theme = lv_theme_mono_get();
+    }
+#endif
+
+    disp->bottom_layer = lv_obj_create(NULL); /*Create bottom layer on the display*/
+    disp->act_scr   = lv_obj_create(NULL); /*Create a default screen on the display*/
+    disp->top_layer = lv_obj_create(NULL); /*Create top layer on the display*/
+    disp->sys_layer = lv_obj_create(NULL); /*Create sys layer on the display*/
+    lv_obj_remove_style_all(disp->bottom_layer);
+    lv_obj_remove_style_all(disp->top_layer);
+    lv_obj_remove_style_all(disp->sys_layer);
+    lv_obj_remove_flag(disp->top_layer, LV_OBJ_FLAG_CLICKABLE);
+    lv_obj_remove_flag(disp->sys_layer, LV_OBJ_FLAG_CLICKABLE);
+
+    lv_obj_set_scrollbar_mode(disp->bottom_layer, LV_SCROLLBAR_MODE_OFF);
+    lv_obj_set_scrollbar_mode(disp->top_layer, LV_SCROLLBAR_MODE_OFF);
+    lv_obj_set_scrollbar_mode(disp->sys_layer, LV_SCROLLBAR_MODE_OFF);
+
+    lv_obj_invalidate(disp->act_scr);
+
+    disp_def = disp_def_tmp; /*Revert the default display*/
+    if(disp_def == NULL) disp_def = disp; /*Initialize the default display*/
+
+    lv_display_add_event_cb(disp, disp_event_cb, LV_EVENT_REFR_REQUEST, NULL);
+
+    lv_timer_ready(disp->refr_timer); /*Be sure the screen will be refreshed immediately on start up*/
+
+#if LV_USE_PERF_MONITOR
+    lv_sysmon_show_performance(disp);
+#endif
+
+#if LV_USE_MEM_MONITOR
+    lv_sysmon_show_memory(disp);
+#endif
+
+    return disp;
+}
+
+void lv_display_delete(lv_display_t * disp)
+{
+    bool was_default = false;
+    bool was_refr = false;
+    if(disp == lv_display_get_default()) was_default = true;
+    if(disp == lv_refr_get_disp_refreshing()) was_refr = true;
+
+    lv_display_send_event(disp, LV_EVENT_DELETE, NULL);
+    lv_event_mark_deleted(disp);
+    lv_event_remove_all(&(disp->event_list));
+
+    /*Detach the input devices*/
+    lv_indev_t * indev;
+    indev = lv_indev_get_next(NULL);
+    while(indev) {
+        if(lv_indev_get_display(indev) == disp) {
+            lv_indev_set_display(indev, NULL);
+        }
+        indev = lv_indev_get_next(indev);
+    }
+
+    /* Delete screen and other obj */
+    if(disp->sys_layer) {
+        lv_obj_delete(disp->sys_layer);
+        disp->sys_layer = NULL;
+    }
+    if(disp->top_layer) {
+        lv_obj_delete(disp->top_layer);
+        disp->top_layer = NULL;
+    }
+
+    if(disp->bottom_layer) {
+        lv_obj_delete(disp->bottom_layer);
+        disp->bottom_layer = NULL;
+    }
+
+    disp->act_scr = NULL;
+
+    while(disp->screen_cnt != 0) {
+        /*Delete the screens*/
+        lv_obj_delete(disp->screens[0]);
+    }
+
+    lv_ll_clear(&disp->sync_areas);
+    lv_ll_remove(disp_ll_p, disp);
+    if(disp->refr_timer) lv_timer_delete(disp->refr_timer);
+
+    if(disp->layer_deinit) disp->layer_deinit(disp, disp->layer_head);
+    lv_free(disp->layer_head);
+
+    lv_free(disp);
+
+    if(was_default) lv_display_set_default(lv_ll_get_head(disp_ll_p));
+
+    if(was_refr) lv_refr_set_disp_refreshing(NULL);
+}
+
+void lv_display_set_default(lv_display_t * disp)
+{
+    disp_def = disp;
+}
+
+lv_display_t * lv_display_get_default(void)
+{
+    return disp_def;
+}
+
+lv_display_t * lv_display_get_next(lv_display_t * disp)
+{
+    if(disp == NULL)
+        return lv_ll_get_head(disp_ll_p);
+    else
+        return lv_ll_get_next(disp_ll_p, disp);
+}
+
+/*---------------------
+ * RESOLUTION
+ *--------------------*/
+
+void lv_display_set_resolution(lv_display_t * disp, int32_t hor_res, int32_t ver_res)
+{
+    if(disp == NULL) disp = lv_display_get_default();
+    if(disp == NULL) return;
+
+    if(disp->hor_res == hor_res && disp->ver_res == ver_res) return;
+
+    disp->hor_res = hor_res;
+    disp->ver_res = ver_res;
+
+    update_resolution(disp);
+}
+
+void lv_display_set_physical_resolution(lv_display_t * disp, int32_t hor_res, int32_t ver_res)
+{
+    if(disp == NULL) disp = lv_display_get_default();
+    if(disp == NULL) return;
+
+    disp->physical_hor_res = hor_res;
+    disp->physical_ver_res = ver_res;
+
+    lv_obj_invalidate(disp->sys_layer);
+
+}
+
+void lv_display_set_offset(lv_display_t * disp, int32_t x, int32_t y)
+{
+    if(disp == NULL) disp = lv_display_get_default();
+    if(disp == NULL) return;
+
+    disp->offset_x = x;
+    disp->offset_y = y;
+
+    lv_obj_invalidate(disp->sys_layer);
+
+}
+
+void lv_display_set_dpi(lv_display_t * disp, int32_t dpi)
+{
+    if(disp == NULL) disp = lv_display_get_default();
+    if(disp == NULL) return;
+
+    disp->dpi = dpi;
+}
+
+int32_t lv_display_get_horizontal_resolution(const lv_display_t * disp)
+{
+    if(disp == NULL) disp = lv_display_get_default();
+
+    if(disp == NULL) {
+        return 0;
+    }
+    else {
+        switch(disp->rotation) {
+            case LV_DISPLAY_ROTATION_90:
+            case LV_DISPLAY_ROTATION_270:
+                return disp->ver_res;
+            default:
+                return disp->hor_res;
+        }
+    }
+}
+
+int32_t lv_display_get_vertical_resolution(const lv_display_t * disp)
+{
+    if(disp == NULL) disp = lv_display_get_default();
+
+    if(disp == NULL) {
+        return 0;
+    }
+    else {
+        switch(disp->rotation) {
+            case LV_DISPLAY_ROTATION_90:
+            case LV_DISPLAY_ROTATION_270:
+                return disp->hor_res;
+            default:
+                return disp->ver_res;
+        }
+    }
+}
+
+int32_t lv_display_get_original_horizontal_resolution(const lv_display_t * disp)
+{
+    if(disp == NULL) disp = lv_display_get_default();
+    if(disp == NULL) {
+        return 0;
+    }
+
+    return disp->hor_res;
+}
+
+int32_t lv_display_get_original_vertical_resolution(const lv_display_t * disp)
+{
+    if(disp == NULL) disp = lv_display_get_default();
+    if(disp == NULL) {
+        return 0;
+    }
+
+    return disp->ver_res;
+}
+
+int32_t lv_display_get_physical_horizontal_resolution(const lv_display_t * disp)
+{
+    if(disp == NULL) disp = lv_display_get_default();
+
+    if(disp == NULL) {
+        return 0;
+    }
+    else {
+        switch(disp->rotation) {
+            case LV_DISPLAY_ROTATION_90:
+            case LV_DISPLAY_ROTATION_270:
+                return disp->physical_ver_res > 0 ? disp->physical_ver_res : disp->ver_res;
+            default:
+                return disp->physical_hor_res > 0 ? disp->physical_hor_res : disp->hor_res;
+        }
+    }
+}
+
+int32_t lv_display_get_physical_vertical_resolution(const lv_display_t * disp)
+{
+    if(disp == NULL) disp = lv_display_get_default();
+
+    if(disp == NULL) {
+        return 0;
+    }
+    else {
+        switch(disp->rotation) {
+            case LV_DISPLAY_ROTATION_90:
+            case LV_DISPLAY_ROTATION_270:
+                return disp->physical_hor_res > 0 ? disp->physical_hor_res : disp->hor_res;
+            default:
+                return disp->physical_ver_res > 0 ? disp->physical_ver_res : disp->ver_res;
+        }
+    }
+}
+
+int32_t lv_display_get_offset_x(const lv_display_t * disp)
+{
+    if(disp == NULL) disp = lv_display_get_default();
+
+    if(disp == NULL) {
+        return 0;
+    }
+    else {
+        switch(disp->rotation) {
+            case LV_DISPLAY_ROTATION_90:
+                return disp->offset_y;
+            case LV_DISPLAY_ROTATION_180:
+                return lv_display_get_physical_horizontal_resolution(disp) - disp->offset_x;
+            case LV_DISPLAY_ROTATION_270:
+                return lv_display_get_physical_horizontal_resolution(disp) - disp->offset_y;
+            default:
+                return disp->offset_x;
+        }
+    }
+}
+
+int32_t lv_display_get_offset_y(const lv_display_t * disp)
+{
+    if(disp == NULL) disp = lv_display_get_default();
+
+    if(disp == NULL) {
+        return 0;
+    }
+    else {
+        switch(disp->rotation) {
+            case LV_DISPLAY_ROTATION_90:
+                return disp->offset_x;
+            case LV_DISPLAY_ROTATION_180:
+                return lv_display_get_physical_vertical_resolution(disp) - disp->offset_y;
+            case LV_DISPLAY_ROTATION_270:
+                return lv_display_get_physical_vertical_resolution(disp) - disp->offset_x;
+            default:
+                return disp->offset_y;
+        }
+    }
+}
+
+int32_t lv_display_get_dpi(const lv_display_t * disp)
+{
+    if(disp == NULL) disp = lv_display_get_default();
+    if(disp == NULL) return LV_DPI_DEF;  /*Do not return 0 because it might be a divider*/
+    return disp->dpi;
+}
+
+/*---------------------
+ * BUFFERING
+ *--------------------*/
+
+void lv_display_set_draw_buffers(lv_display_t * disp, lv_draw_buf_t * buf1, lv_draw_buf_t * buf2)
+{
+    if(disp == NULL) disp = lv_display_get_default();
+    if(disp == NULL) return;
+
+    disp->buf_1 = buf1;
+    disp->buf_2 = buf2;
+    disp->buf_act = disp->buf_1;
+
+    disp->stride_is_auto = 0;
+}
+
+void lv_display_set_3rd_draw_buffer(lv_display_t * disp, lv_draw_buf_t * buf3)
+{
+    if(disp == NULL) disp = lv_display_get_default();
+    if(disp == NULL) return;
+
+    LV_ASSERT_MSG(disp->buf_1 != NULL, "buf1 is null");
+    LV_ASSERT_MSG(disp->buf_2 != NULL, "buf2 is null");
+
+    disp->buf_3 = buf3;
+}
+
+void lv_display_set_buffers(lv_display_t * disp, void * buf1, void * buf2, uint32_t buf_size,
+                            lv_display_render_mode_t render_mode)
+{
+    LV_ASSERT_MSG(buf1 != NULL, "Null buffer");
+    lv_color_format_t cf = lv_display_get_color_format(disp);
+    uint32_t w = lv_display_get_original_horizontal_resolution(disp);
+    uint32_t h = lv_display_get_original_vertical_resolution(disp);
+    LV_ASSERT_MSG(w != 0 && h != 0, "display resolution is 0");
+
+    /* buf1 or buf2 is not aligned according to LV_DRAW_BUF_ALIGN */
+    LV_ASSERT_FORMAT_MSG(buf1 == lv_draw_buf_align(buf1, cf), "buf1 is not aligned: %p", buf1);
+    LV_ASSERT_FORMAT_MSG(buf2 == NULL || buf2 == lv_draw_buf_align(buf2, cf), "buf2 is not aligned: %p", buf2);
+
+    uint32_t stride = lv_draw_buf_width_to_stride(w, cf);
+    if(render_mode == LV_DISPLAY_RENDER_MODE_PARTIAL) {
+        LV_ASSERT_FORMAT_MSG(stride != 0, "stride is 0, check your color format %d and width: %" LV_PRIu32, cf, w);
+        /* for partial mode, we calculate the height based on the buf_size and stride */
+        h = buf_size / stride;
+        LV_ASSERT_MSG(h != 0, "the buffer is too small");
+    }
+    else {
+        LV_ASSERT_FORMAT_MSG(stride * h <= buf_size, "%s mode requires screen sized buffer(s)",
+                             render_mode == LV_DISPLAY_RENDER_MODE_FULL ? "FULL" : "DIRECT");
+    }
+
+    lv_draw_buf_init(&disp->_static_buf1, w, h, cf, stride, buf1, buf_size);
+    lv_draw_buf_init(&disp->_static_buf2, w, h, cf, stride, buf2, buf_size);
+    lv_display_set_draw_buffers(disp, &disp->_static_buf1, buf2 ? &disp->_static_buf2 : NULL);
+    lv_display_set_render_mode(disp, render_mode);
+
+    /* the stride was not set explicitly */
+    disp->stride_is_auto = 1;
+}
+
+void lv_display_set_buffers_with_stride(lv_display_t * disp, void * buf1, void * buf2, uint32_t buf_size,
+                                        uint32_t stride, lv_display_render_mode_t render_mode)
+{
+    if(stride == LV_STRIDE_AUTO) {
+        lv_display_set_buffers(disp, buf1, buf2, buf_size, render_mode);
+        return;
+    }
+
+    LV_ASSERT_MSG(buf1 != NULL, "Null buffer");
+    lv_color_format_t cf = lv_display_get_color_format(disp);
+    uint32_t w = lv_display_get_original_horizontal_resolution(disp);
+    uint32_t h = lv_display_get_original_vertical_resolution(disp);
+    LV_ASSERT_MSG(w != 0 && h != 0, "display resolution is 0");
+
+    if(render_mode == LV_DISPLAY_RENDER_MODE_PARTIAL) {
+        /* for partial mode, we calculate the height based on the buf_size and stride */
+        h = buf_size / stride;
+        LV_ASSERT_MSG(h != 0, "the buffer is too small");
+    }
+    else {
+        LV_ASSERT_FORMAT_MSG(stride * h <= buf_size, "%s mode requires screen sized buffer(s)",
+                             render_mode == LV_DISPLAY_RENDER_MODE_FULL ? "FULL" : "DIRECT");
+    }
+
+    lv_draw_buf_init(&disp->_static_buf1, w, h, cf, stride, buf1, buf_size);
+    lv_draw_buf_init(&disp->_static_buf2, w, h, cf, stride, buf2, buf_size);
+    lv_display_set_draw_buffers(disp, &disp->_static_buf1, buf2 ? &disp->_static_buf2 : NULL);
+    lv_display_set_render_mode(disp, render_mode);
+
+    disp->stride_is_auto = 0;
+}
+
+void lv_display_set_render_mode(lv_display_t * disp, lv_display_render_mode_t render_mode)
+{
+    if(disp == NULL) disp = lv_display_get_default();
+    if(disp == NULL) return;
+    disp->render_mode = render_mode;
+}
+
+void lv_display_set_flush_cb(lv_display_t * disp, lv_display_flush_cb_t flush_cb)
+{
+    if(disp == NULL) disp = lv_display_get_default();
+    if(disp == NULL) return;
+
+    disp->flush_cb = flush_cb;
+}
+
+void lv_display_set_flush_wait_cb(lv_display_t * disp, lv_display_flush_wait_cb_t wait_cb)
+{
+    if(disp == NULL) disp = lv_display_get_default();
+    if(disp == NULL) return;
+
+    disp->flush_wait_cb = wait_cb;
+}
+
+void lv_display_set_color_format(lv_display_t * disp, lv_color_format_t color_format)
+{
+    if(disp == NULL) disp = lv_display_get_default();
+    if(disp == NULL) return;
+
+    disp->color_format = color_format;
+    disp->layer_head->color_format = color_format;
+    if(disp->buf_1) disp->buf_1->header.cf = color_format;
+    if(disp->buf_2) disp->buf_2->header.cf = color_format;
+    if(disp->buf_3) disp->buf_3->header.cf = color_format;
+
+    lv_display_send_event(disp, LV_EVENT_COLOR_FORMAT_CHANGED, NULL);
+}
+
+lv_color_format_t lv_display_get_color_format(lv_display_t * disp)
+{
+    if(disp == NULL) disp = lv_display_get_default();
+    if(disp == NULL) return LV_COLOR_FORMAT_UNKNOWN;
+
+    return disp->color_format;
+}
+
+void lv_display_set_tile_cnt(lv_display_t * disp, uint32_t tile_cnt)
+{
+    LV_ASSERT_FORMAT_MSG(tile_cnt < 256, "tile_cnt must be smaller than 256 (%" LV_PRId32 " was used)", tile_cnt);
+
+    if(disp == NULL) disp = lv_display_get_default();
+    if(disp == NULL) return;
+
+    disp->tile_cnt = tile_cnt;
+}
+
+uint32_t lv_display_get_tile_cnt(lv_display_t * disp)
+{
+    if(disp == NULL) disp = lv_display_get_default();
+    if(disp == NULL) return 0;
+
+    return disp->tile_cnt;
+}
+
+void lv_display_set_antialiasing(lv_display_t * disp, bool en)
+{
+    LV_LOG_WARN("Disabling anti-aliasing is not supported since v9. This function will be removed.");
+
+    if(disp == NULL) disp = lv_display_get_default();
+    if(disp == NULL) return;
+
+    disp->antialiasing = en;
+}
+
+bool lv_display_get_antialiasing(lv_display_t * disp)
+{
+    if(disp == NULL) disp = lv_display_get_default();
+    if(disp == NULL) return false;
+
+    return disp->antialiasing;
+}
+
+LV_ATTRIBUTE_FLUSH_READY void lv_display_flush_ready(lv_display_t * disp)
+{
+    disp->flushing = 0;
+}
+
+LV_ATTRIBUTE_FLUSH_READY bool lv_display_flush_is_last(lv_display_t * disp)
+{
+    return disp->flushing_last;
+}
+
+bool lv_display_is_double_buffered(lv_display_t * disp)
+{
+    return disp->buf_2 != NULL;
+}
+
+/*---------------------
+  * SCREENS
+  *--------------------*/
+
+lv_obj_t * lv_display_get_screen_active(lv_display_t * disp)
+{
+    if(!disp) disp = lv_display_get_default();
+    if(!disp) {
+        LV_LOG_WARN("no display registered to get its active screen");
+        return NULL;
+    }
+
+    return disp->act_scr;
+}
+
+lv_obj_t * lv_display_get_screen_loading(lv_display_t * disp)
+{
+    if(!disp) disp = lv_display_get_default();
+    if(!disp) {
+        LV_LOG_WARN("no display registered to get the current screen being loaded");
+        return NULL;
+    }
+
+    return disp->scr_to_load;
+}
+
+lv_obj_t * lv_display_get_screen_prev(lv_display_t * disp)
+{
+    if(!disp) disp = lv_display_get_default();
+    if(!disp) {
+        LV_LOG_WARN("no display registered to get its previous screen");
+        return NULL;
+    }
+
+    return disp->prev_scr;
+}
+
+lv_obj_t * lv_display_get_layer_top(lv_display_t * disp)
+{
+    if(!disp) disp = lv_display_get_default();
+    if(!disp) {
+        LV_LOG_WARN("lv_layer_top: no display registered to get its top layer");
+        return NULL;
+    }
+
+    return disp->top_layer;
+}
+
+lv_obj_t * lv_display_get_layer_sys(lv_display_t * disp)
+{
+    if(!disp) disp = lv_display_get_default();
+    if(!disp) {
+        LV_LOG_WARN("lv_layer_sys: no display registered to get its sys. layer");
+        return NULL;
+    }
+
+    return disp->sys_layer;
+}
+
+lv_obj_t * lv_display_get_layer_bottom(lv_display_t * disp)
+{
+    if(!disp) disp = lv_display_get_default();
+    if(!disp) {
+        LV_LOG_WARN("lv_layer_bottom: no display registered to get its bottom layer");
+        return NULL;
+    }
+
+    return disp->bottom_layer;
+}
+
+#if LV_USE_OBJ_NAME
+
+lv_obj_t * lv_display_get_screen_by_name(const lv_display_t * disp, const char * screen_name)
+{
+    if(!disp) disp = lv_display_get_default();
+    if(!disp) {
+        LV_LOG_WARN("no display registered to get a screen by name");
+        return NULL;
+    }
+
+    uint32_t i;
+    for(i = 0; i < disp->screen_cnt; i++) {
+        const char * n = lv_obj_get_name(disp->screens[i]);
+        if(n && lv_streq(screen_name, n)) return disp->screens[i];
+    }
+
+    return NULL;
+
+}
+
+#endif /*LV_USE_OBJ_NAME*/
+
+void lv_screen_load(struct _lv_obj_t * scr)
+{
+    lv_screen_load_anim(scr, LV_SCREEN_LOAD_ANIM_NONE, 0, 0, false);
+}
+
+void lv_screen_load_anim(lv_obj_t * new_scr, lv_screen_load_anim_t anim_type, uint32_t time, uint32_t delay,
+                         bool auto_del)
+{
+    lv_display_t * d = lv_obj_get_display(new_scr);
+    lv_obj_t * act_scr = d->act_scr;
+
+    if(act_scr == new_scr || d->scr_to_load == new_scr) {
+        return;
+    }
+
+    /*If another screen load animation is in progress
+     *make target screen loaded immediately. */
+    if(d->scr_to_load && act_scr != d->scr_to_load) {
+        lv_anim_delete(d->scr_to_load, NULL);
+        lv_obj_set_pos(d->scr_to_load, 0, 0);
+        lv_obj_remove_local_style_prop(d->scr_to_load, LV_STYLE_OPA, 0);
+
+        d->prev_scr = d->act_scr;
+        act_scr = d->scr_to_load; /*Active screen changed.*/
+
+        scr_load_internal(d->scr_to_load);
+    }
+
+    d->scr_to_load = new_scr;
+
+    if(d->prev_scr && d->del_prev) lv_obj_delete(d->prev_scr);
+    d->prev_scr = NULL;
+
+    d->draw_prev_over_act = is_out_anim(anim_type);
+    d->del_prev = auto_del;
+
+    /*Be sure there is no other animation on the screens*/
+    lv_anim_delete(new_scr, NULL);
+    if(act_scr) lv_anim_delete(act_scr, NULL);
+
+    /*Be sure both screens are in a normal position*/
+    lv_obj_set_pos(new_scr, 0, 0);
+    if(act_scr) lv_obj_set_pos(act_scr, 0, 0);
+    lv_obj_remove_local_style_prop(new_scr, LV_STYLE_OPA, 0);
+    if(act_scr) lv_obj_remove_local_style_prop(act_scr, LV_STYLE_OPA, 0);
+
+    /*Shortcut for immediate load*/
+    if(time == 0 && delay == 0) {
+        scr_load_internal(new_scr);
+        if(auto_del && act_scr) lv_obj_delete(act_scr);
+        return;
+    }
+
+    lv_anim_t a_new;
+    lv_anim_init(&a_new);
+    lv_anim_set_var(&a_new, new_scr);
+    lv_anim_set_start_cb(&a_new, scr_load_anim_start);
+    lv_anim_set_completed_cb(&a_new, scr_anim_completed);
+    lv_anim_set_duration(&a_new, time);
+    lv_anim_set_delay(&a_new, delay);
+
+    lv_anim_t a_old;
+    lv_anim_init(&a_old);
+    lv_anim_set_var(&a_old, act_scr);
+    lv_anim_set_duration(&a_old, time);
+    lv_anim_set_delay(&a_old, delay);
+
+    switch(anim_type) {
+        case LV_SCREEN_LOAD_ANIM_NONE:
+            /*Create a dummy animation to apply the delay*/
+            lv_anim_set_exec_cb(&a_new, set_x_anim);
+            lv_anim_set_values(&a_new, 0, 0);
+            break;
+        case LV_SCREEN_LOAD_ANIM_OVER_LEFT:
+            lv_anim_set_exec_cb(&a_new, set_x_anim);
+            lv_anim_set_values(&a_new, lv_display_get_horizontal_resolution(d), 0);
+            break;
+        case LV_SCREEN_LOAD_ANIM_OVER_RIGHT:
+            lv_anim_set_exec_cb(&a_new, set_x_anim);
+            lv_anim_set_values(&a_new, -lv_display_get_horizontal_resolution(d), 0);
+            break;
+        case LV_SCREEN_LOAD_ANIM_OVER_TOP:
+            lv_anim_set_exec_cb(&a_new, set_y_anim);
+            lv_anim_set_values(&a_new, lv_display_get_vertical_resolution(d), 0);
+            break;
+        case LV_SCREEN_LOAD_ANIM_OVER_BOTTOM:
+            lv_anim_set_exec_cb(&a_new, set_y_anim);
+            lv_anim_set_values(&a_new, -lv_display_get_vertical_resolution(d), 0);
+            break;
+        case LV_SCREEN_LOAD_ANIM_MOVE_LEFT:
+            lv_anim_set_exec_cb(&a_new, set_x_anim);
+            lv_anim_set_values(&a_new, lv_display_get_horizontal_resolution(d), 0);
+
+            lv_anim_set_exec_cb(&a_old, set_x_anim);
+            lv_anim_set_values(&a_old, 0, -lv_display_get_horizontal_resolution(d));
+            break;
+        case LV_SCREEN_LOAD_ANIM_MOVE_RIGHT:
+            lv_anim_set_exec_cb(&a_new, set_x_anim);
+            lv_anim_set_values(&a_new, -lv_display_get_horizontal_resolution(d), 0);
+
+            lv_anim_set_exec_cb(&a_old, set_x_anim);
+            lv_anim_set_values(&a_old, 0, lv_display_get_horizontal_resolution(d));
+            break;
+        case LV_SCREEN_LOAD_ANIM_MOVE_TOP:
+            lv_anim_set_exec_cb(&a_new, set_y_anim);
+            lv_anim_set_values(&a_new, lv_display_get_vertical_resolution(d), 0);
+
+            lv_anim_set_exec_cb(&a_old, set_y_anim);
+            lv_anim_set_values(&a_old, 0, -lv_display_get_vertical_resolution(d));
+            break;
+        case LV_SCREEN_LOAD_ANIM_MOVE_BOTTOM:
+            lv_anim_set_exec_cb(&a_new, set_y_anim);
+            lv_anim_set_values(&a_new, -lv_display_get_vertical_resolution(d), 0);
+
+            lv_anim_set_exec_cb(&a_old, set_y_anim);
+            lv_anim_set_values(&a_old, 0, lv_display_get_vertical_resolution(d));
+            break;
+        case LV_SCREEN_LOAD_ANIM_FADE_IN:
+            lv_anim_set_exec_cb(&a_new, opa_scale_anim);
+            lv_anim_set_values(&a_new, LV_OPA_TRANSP, LV_OPA_COVER);
+            break;
+        case LV_SCREEN_LOAD_ANIM_FADE_OUT:
+            lv_anim_set_exec_cb(&a_old, opa_scale_anim);
+            lv_anim_set_values(&a_old, LV_OPA_COVER, LV_OPA_TRANSP);
+            break;
+        case LV_SCREEN_LOAD_ANIM_OUT_LEFT:
+            lv_anim_set_exec_cb(&a_old, set_x_anim);
+            lv_anim_set_values(&a_old, 0, -lv_display_get_horizontal_resolution(d));
+            break;
+        case LV_SCREEN_LOAD_ANIM_OUT_RIGHT:
+            lv_anim_set_exec_cb(&a_old, set_x_anim);
+            lv_anim_set_values(&a_old, 0, lv_display_get_horizontal_resolution(d));
+            break;
+        case LV_SCREEN_LOAD_ANIM_OUT_TOP:
+            lv_anim_set_exec_cb(&a_old, set_y_anim);
+            lv_anim_set_values(&a_old, 0, -lv_display_get_vertical_resolution(d));
+            break;
+        case LV_SCREEN_LOAD_ANIM_OUT_BOTTOM:
+            lv_anim_set_exec_cb(&a_old, set_y_anim);
+            lv_anim_set_values(&a_old, 0, lv_display_get_vertical_resolution(d));
+            break;
+    }
+
+    if(act_scr) lv_obj_send_event(act_scr, LV_EVENT_SCREEN_UNLOAD_START, NULL);
+
+    lv_anim_start(&a_new);
+    if(act_scr) lv_anim_start(&a_old);
+}
+
+/*---------------------
+ * OTHERS
+ *--------------------*/
+
+void lv_display_add_event_cb(lv_display_t * disp, lv_event_cb_t event_cb, lv_event_code_t filter, void * user_data)
+{
+    LV_ASSERT_NULL(disp);
+
+    lv_event_add(&disp->event_list, event_cb, filter, user_data);
+}
+
+uint32_t lv_display_get_event_count(lv_display_t * disp)
+{
+    LV_ASSERT_NULL(disp);
+    return lv_event_get_count(&disp->event_list);
+}
+
+lv_event_dsc_t * lv_display_get_event_dsc(lv_display_t * disp, uint32_t index)
+{
+    LV_ASSERT_NULL(disp);
+    return lv_event_get_dsc(&disp->event_list, index);
+
+}
+
+bool lv_display_delete_event(lv_display_t * disp, uint32_t index)
+{
+    LV_ASSERT_NULL(disp);
+
+    return lv_event_remove(&disp->event_list, index);
+}
+
+uint32_t lv_display_remove_event_cb_with_user_data(lv_display_t * disp, lv_event_cb_t event_cb, void * user_data)
+{
+    LV_ASSERT_NULL(disp);
+
+    uint32_t event_cnt = lv_display_get_event_count(disp);
+    uint32_t removed_count = 0;
+    int32_t i;
+
+    for(i = event_cnt - 1; i >= 0; i--) {
+        lv_event_dsc_t * dsc = lv_display_get_event_dsc(disp, i);
+        if(dsc && dsc->cb == event_cb && dsc->user_data == user_data) {
+            lv_display_delete_event(disp, i);
+            removed_count ++;
+        }
+    }
+
+    return removed_count;
+}
+
+lv_result_t lv_display_send_event(lv_display_t * disp, lv_event_code_t code, void * param)
+{
+    return lv_event_push_and_send(&disp->event_list, code, disp, param);
+}
+
+lv_area_t * lv_event_get_invalidated_area(lv_event_t * e)
+{
+    if(e->code == LV_EVENT_INVALIDATE_AREA) {
+        return lv_event_get_param(e);
+    }
+    else {
+        LV_LOG_WARN("Not interpreted with this event code");
+        return NULL;
+    }
+}
+
+void lv_display_set_rotation(lv_display_t * disp, lv_display_rotation_t rotation)
+{
+    if(disp == NULL) disp = lv_display_get_default();
+    if(disp == NULL) return;
+
+    disp->rotation = rotation;
+    update_resolution(disp);
+}
+
+lv_display_rotation_t lv_display_get_rotation(lv_display_t * disp)
+{
+    if(disp == NULL) disp = lv_display_get_default();
+    if(disp == NULL) return LV_DISPLAY_ROTATION_0;
+    return disp->rotation;
+}
+
+void lv_display_set_matrix_rotation(lv_display_t * disp, bool enable)
+{
+#if LV_DRAW_TRANSFORM_USE_MATRIX
+    if(disp == NULL) disp = lv_display_get_default();
+    if(disp == NULL) return;
+
+    if(!(disp->render_mode == LV_DISPLAY_RENDER_MODE_DIRECT || disp->render_mode == LV_DISPLAY_RENDER_MODE_FULL)) {
+        LV_LOG_WARN("Unsupported rendering mode: %d", disp->render_mode);
+        return;
+    }
+
+    disp->matrix_rotation = enable;
+#else
+    (void)disp;
+    (void)enable;
+    LV_LOG_WARN("LV_DRAW_TRANSFORM_USE_MATRIX was not enabled");
+#endif
+}
+
+bool lv_display_get_matrix_rotation(lv_display_t * disp)
+{
+    if(disp == NULL) disp = lv_display_get_default();
+    if(disp == NULL) return false;
+    return disp->matrix_rotation;
+}
+
+void lv_display_set_theme(lv_display_t * disp, lv_theme_t * th)
+{
+    if(!disp) disp = lv_display_get_default();
+    if(!disp) {
+        LV_LOG_WARN("no display registered");
+        return;
+    }
+
+    disp->theme = th;
+
+    if(disp->screen_cnt == 4 &&
+       lv_obj_get_child_count(disp->screens[0]) == 0 &&
+       lv_obj_get_child_count(disp->screens[1]) == 0 &&
+       lv_obj_get_child_count(disp->screens[2]) == 0) {
+        lv_theme_apply(disp->screens[0]);
+    }
+}
+
+lv_theme_t * lv_display_get_theme(lv_display_t * disp)
+{
+    if(disp == NULL) disp = lv_display_get_default();
+    return disp->theme;
+}
+
+uint32_t lv_display_get_inactive_time(const lv_display_t * disp)
+{
+    if(disp) return lv_tick_elaps(disp->last_activity_time);
+
+    lv_display_t * d;
+    uint32_t t = UINT32_MAX;
+    d          = lv_display_get_next(NULL);
+    while(d) {
+        uint32_t elaps = lv_tick_elaps(d->last_activity_time);
+        t = LV_MIN(t, elaps);
+        d = lv_display_get_next(d);
+    }
+
+    return t;
+}
+
+void lv_display_trigger_activity(lv_display_t * disp)
+{
+    if(!disp) disp = lv_display_get_default();
+    if(!disp) {
+        LV_LOG_WARN("lv_display_trigger_activity: no display registered");
+        return;
+    }
+
+    disp->last_activity_time = lv_tick_get();
+}
+
+void lv_display_enable_invalidation(lv_display_t * disp, bool en)
+{
+    if(!disp) disp = lv_display_get_default();
+    if(!disp) {
+        LV_LOG_WARN("no display registered");
+        return;
+    }
+
+    disp->inv_en_cnt += en ? 1 : -1;
+}
+
+bool lv_display_is_invalidation_enabled(lv_display_t * disp)
+{
+    if(!disp) disp = lv_display_get_default();
+    if(!disp) {
+        LV_LOG_WARN("no display registered");
+        return false;
+    }
+
+    return (disp->inv_en_cnt > 0);
+}
+
+lv_timer_t * lv_display_get_refr_timer(lv_display_t * disp)
+{
+    if(!disp) disp = lv_display_get_default();
+    if(!disp) return NULL;
+
+    return disp->refr_timer;
+}
+
+void lv_display_delete_refr_timer(lv_display_t * disp)
+{
+    if(!disp) disp = lv_display_get_default();
+    if(!disp || !disp->refr_timer) return;
+
+    lv_timer_delete(disp->refr_timer);
+    disp->refr_timer = NULL;
+}
+
+lv_result_t lv_display_send_vsync_event(lv_display_t * disp, void * param)
+{
+    if(!disp) disp = lv_display_get_default();
+    if(!disp) return LV_RESULT_INVALID;
+
+    if(disp->vsync_count > 0)
+        return lv_display_send_event(disp, LV_EVENT_VSYNC, param);
+
+    return LV_RESULT_INVALID;
+}
+
+bool lv_display_register_vsync_event(lv_display_t * disp, lv_event_cb_t event_cb, void * user_data)
+{
+    if(!disp) disp = lv_display_get_default();
+    if(!disp) return false;
+
+    lv_display_add_event_cb(disp, event_cb, LV_EVENT_VSYNC, user_data);
+
+    /*only send once*/
+    if(disp->vsync_count == 0)
+        lv_display_send_event(disp, LV_EVENT_VSYNC_REQUEST, disp);
+
+    disp->vsync_count++;
+    return true;
+}
+
+bool lv_display_unregister_vsync_event(lv_display_t * disp, lv_event_cb_t event_cb, void * user_data)
+{
+    if(!disp) disp = lv_display_get_default();
+    if(!disp) return false;
+
+    uint32_t removed_count = lv_display_remove_event_cb_with_user_data(disp, event_cb, user_data);
+    if(removed_count == 0)
+        return false;
+
+    disp->vsync_count -= removed_count;
+    /*only send once*/
+    if(disp->vsync_count == 0)
+        lv_display_send_event(disp, LV_EVENT_VSYNC_REQUEST, NULL);
+
+    return true;
+}
+
+void lv_display_set_user_data(lv_display_t * disp, void * user_data)
+{
+    if(!disp) disp = lv_display_get_default();
+    if(!disp) return;
+    disp->user_data = user_data;
+}
+
+void lv_display_set_driver_data(lv_display_t * disp, void * driver_data)
+{
+    if(!disp) disp = lv_display_get_default();
+    if(!disp) return;
+
+    disp->driver_data = driver_data;
+}
+
+void * lv_display_get_user_data(lv_display_t * disp)
+{
+    if(!disp) disp = lv_display_get_default();
+    if(!disp) return NULL;
+    return disp->user_data;
+}
+
+void * lv_display_get_driver_data(lv_display_t * disp)
+{
+    if(!disp) disp = lv_display_get_default();
+    if(!disp) return NULL;
+
+    return disp->driver_data;
+}
+
+lv_draw_buf_t * lv_display_get_buf_active(lv_display_t * disp)
+{
+    if(!disp) disp = lv_display_get_default();
+    if(!disp) return NULL;
+    return disp->buf_act;
+}
+
+void lv_display_rotate_area(lv_display_t * disp, lv_area_t * area)
+{
+    lv_display_rotation_t rotation = lv_display_get_rotation(disp);
+
+    if(rotation == LV_DISPLAY_ROTATION_0) return;
+
+    int32_t w = lv_area_get_width(area);
+    int32_t h = lv_area_get_height(area);
+
+    switch(rotation) {
+        case LV_DISPLAY_ROTATION_90:
+            area->y2 = disp->ver_res - area->x1 - 1;
+            area->x1 = area->y1;
+            area->x2 = area->x1 + h - 1;
+            area->y1 = area->y2 - w + 1;
+            break;
+        case LV_DISPLAY_ROTATION_180:
+            area->y2 = disp->ver_res - area->y1 - 1;
+            area->y1 = area->y2 - h + 1;
+            area->x2 = disp->hor_res - area->x1 - 1;
+            area->x1 = area->x2 - w + 1;
+            break;
+        case LV_DISPLAY_ROTATION_270:
+            area->x1 = disp->hor_res - area->y2 - 1;
+            area->y2 = area->x2;
+            area->x2 = area->x1 + h - 1;
+            area->y1 = area->y2 - w + 1;
+            break;
+        default:
+            break;
+    }
+}
+
+uint32_t lv_display_get_draw_buf_size(lv_display_t * disp)
+{
+    if(!disp) disp = lv_display_get_default();
+    if(!disp) return 0;
+
+    if(disp->buf_1) {
+        return disp->buf_1->data_size;
+    }
+    return 0;
+}
+
+uint32_t lv_display_get_invalidated_draw_buf_size(lv_display_t * disp, uint32_t width, uint32_t height)
+{
+    if(!disp) disp = lv_display_get_default();
+    if(!disp) return 0;
+
+    if(disp->render_mode == LV_DISPLAY_RENDER_MODE_FULL) {
+        width = lv_display_get_horizontal_resolution(disp);
+        height = lv_display_get_vertical_resolution(disp);
+    }
+
+    lv_color_format_t cf = lv_display_get_color_format(disp);
+    uint32_t stride = lv_draw_buf_width_to_stride(width, cf);
+    uint32_t buf_size = stride * height;
+
+    LV_ASSERT(disp->buf_1 && disp->buf_1->data_size >= buf_size);
+    if(disp->buf_2) LV_ASSERT(disp->buf_2->data_size >= buf_size);
+    if(disp->buf_3) LV_ASSERT(disp->buf_3->data_size >= buf_size);
+
+    return buf_size;
+}
+
+lv_obj_t * lv_screen_active(void)
+{
+    return lv_display_get_screen_active(lv_display_get_default());
+}
+
+lv_obj_t * lv_layer_top(void)
+{
+    return lv_display_get_layer_top(lv_display_get_default());
+}
+
+lv_obj_t * lv_layer_sys(void)
+{
+    return lv_display_get_layer_sys(lv_display_get_default());
+}
+
+lv_obj_t * lv_layer_bottom(void)
+{
+    return lv_display_get_layer_bottom(lv_display_get_default());
+}
+
+int32_t lv_dpx(int32_t n)
+{
+    return LV_DPX(n);
+}
+
+int32_t lv_display_dpx(const lv_display_t * disp, int32_t n)
+{
+    return LV_DPX_CALC(lv_display_get_dpi(disp), n);
+}
+
+/**********************
+ *   STATIC FUNCTIONS
+ **********************/
+
+static void update_resolution(lv_display_t * disp)
+{
+    int32_t hor_res = lv_display_get_horizontal_resolution(disp);
+    int32_t ver_res = lv_display_get_vertical_resolution(disp);
+
+    lv_area_t prev_coords;
+    lv_obj_get_coords(disp->sys_layer, &prev_coords);
+    uint32_t i;
+    for(i = 0; i < disp->screen_cnt; i++) {
+        lv_area_set_width(&disp->screens[i]->coords, hor_res);
+        lv_area_set_height(&disp->screens[i]->coords, ver_res);
+        lv_obj_send_event(disp->screens[i], LV_EVENT_SIZE_CHANGED, &prev_coords);
+    }
+
+    lv_area_set_width(&disp->top_layer->coords, hor_res);
+    lv_area_set_height(&disp->top_layer->coords, ver_res);
+    lv_obj_send_event(disp->top_layer, LV_EVENT_SIZE_CHANGED, &prev_coords);
+
+    lv_area_set_width(&disp->sys_layer->coords, hor_res);
+    lv_area_set_height(&disp->sys_layer->coords, ver_res);
+    lv_obj_send_event(disp->sys_layer, LV_EVENT_SIZE_CHANGED, &prev_coords);
+
+    lv_area_set_width(&disp->bottom_layer->coords, hor_res);
+    lv_area_set_height(&disp->bottom_layer->coords, ver_res);
+    lv_obj_send_event(disp->bottom_layer, LV_EVENT_SIZE_CHANGED, &prev_coords);
+
+    lv_memzero(disp->inv_areas, sizeof(disp->inv_areas));
+    lv_memzero(disp->inv_area_joined, sizeof(disp->inv_area_joined));
+    disp->inv_p = 0;
+    lv_obj_invalidate(disp->sys_layer);
+
+    lv_obj_tree_walk(NULL, invalidate_layout_cb, NULL);
+
+    lv_display_send_event(disp, LV_EVENT_RESOLUTION_CHANGED, NULL);
+}
+
+static lv_obj_tree_walk_res_t invalidate_layout_cb(lv_obj_t * obj, void * user_data)
+{
+    LV_UNUSED(user_data);
+    lv_obj_mark_layout_as_dirty(obj);
+    return LV_OBJ_TREE_WALK_NEXT;
+}
+
+static void scr_load_internal(lv_obj_t * scr)
+{
+    /*scr must not be NULL, but d->act_scr might be*/
+    LV_ASSERT_NULL(scr);
+    if(scr == NULL) return;
+
+    lv_display_t * d = lv_obj_get_display(scr);
+    if(!d) return;  /*Shouldn't happen, just to be sure*/
+
+    lv_obj_t * old_scr = d->act_scr;
+
+    if(old_scr) lv_obj_send_event(old_scr, LV_EVENT_SCREEN_UNLOAD_START, NULL);
+    lv_obj_send_event(scr, LV_EVENT_SCREEN_LOAD_START, NULL);
+
+    d->act_scr = scr;
+    d->scr_to_load = NULL;
+
+    lv_obj_send_event(scr, LV_EVENT_SCREEN_LOADED, NULL);
+    if(old_scr) lv_obj_send_event(old_scr, LV_EVENT_SCREEN_UNLOADED, NULL);
+
+    lv_obj_invalidate(scr);
+}
+
+static void scr_load_anim_start(lv_anim_t * a)
+{
+    lv_display_t * d = lv_obj_get_display(a->var);
+
+    d->prev_scr = d->act_scr;
+    d->act_scr = a->var;
+
+    lv_obj_send_event(d->act_scr, LV_EVENT_SCREEN_LOAD_START, NULL);
+}
+
+static void opa_scale_anim(void * obj, int32_t v)
+{
+    lv_obj_set_style_opa(obj, v, 0);
+}
+
+static void set_x_anim(void * obj, int32_t v)
+{
+    lv_obj_set_x(obj, v);
+}
+
+static void set_y_anim(void * obj, int32_t v)
+{
+    lv_obj_set_y(obj, v);
+}
+
+static void scr_anim_completed(lv_anim_t * a)
+{
+    lv_display_t * d = lv_obj_get_display(a->var);
+
+    lv_obj_send_event(d->act_scr, LV_EVENT_SCREEN_LOADED, NULL);
+    lv_obj_send_event(d->prev_scr, LV_EVENT_SCREEN_UNLOADED, NULL);
+
+    if(d->prev_scr && d->del_prev) lv_obj_delete(d->prev_scr);
+    d->prev_scr = NULL;
+    d->draw_prev_over_act = false;
+    d->scr_to_load = NULL;
+    lv_obj_remove_local_style_prop(a->var, LV_STYLE_OPA, 0);
+    lv_obj_invalidate(d->act_scr);
+}
+
+static bool is_out_anim(lv_screen_load_anim_t anim_type)
+{
+    return anim_type == LV_SCREEN_LOAD_ANIM_FADE_OUT  ||
+           anim_type == LV_SCREEN_LOAD_ANIM_OUT_LEFT  ||
+           anim_type == LV_SCREEN_LOAD_ANIM_OUT_RIGHT ||
+           anim_type == LV_SCREEN_LOAD_ANIM_OUT_TOP   ||
+           anim_type == LV_SCREEN_LOAD_ANIM_OUT_BOTTOM;
+}
+
+static void disp_event_cb(lv_event_t * e)
+{
+    lv_event_code_t code = lv_event_get_code(e);
+    lv_display_t * disp = lv_event_get_target(e);
+    switch(code) {
+        case LV_EVENT_REFR_REQUEST:
+            if(disp->refr_timer) lv_timer_resume(disp->refr_timer);
+            break;
+
+        default:
+            break;
+    }
+}

+ 732 - 0
components/easylvgl/lvgl9/src/display/lv_display.h

@@ -0,0 +1,732 @@
+/**
+ * @file lv_display.h
+ *
+ */
+
+#ifndef LV_DISPLAY_H
+#define LV_DISPLAY_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/*********************
+ *      INCLUDES
+ *********************/
+#include "../misc/lv_types.h"
+#include "../misc/lv_timer.h"
+#include "../misc/lv_event.h"
+#include "../misc/lv_color.h"
+#include "../misc/lv_area.h"
+
+/*********************
+ *      DEFINES
+ *********************/
+
+#ifndef LV_ATTRIBUTE_FLUSH_READY
+#define LV_ATTRIBUTE_FLUSH_READY
+#endif
+
+/**********************
+ *      TYPEDEFS
+ **********************/
+
+typedef enum {
+    LV_DISPLAY_ROTATION_0 = 0,
+    LV_DISPLAY_ROTATION_90,
+    LV_DISPLAY_ROTATION_180,
+    LV_DISPLAY_ROTATION_270
+} lv_display_rotation_t;
+
+typedef enum {
+    /**
+     * Use the buffer(s) to render the screen is smaller parts.
+     * This way the buffers can be smaller then the display to save RAM. At least 1/10 screen size buffer(s) are recommended.
+     */
+    LV_DISPLAY_RENDER_MODE_PARTIAL,
+
+    /**
+     * The buffer(s) has to be screen sized and LVGL will render into the correct location of the buffer.
+     * This way the buffer always contain the whole image. Only the changed ares will be updated.
+     * With 2 buffers the buffers' content are kept in sync automatically and in flush_cb only address change is required.
+     */
+    LV_DISPLAY_RENDER_MODE_DIRECT,
+
+    /**
+     * Always redraw the whole screen even if only one pixel has been changed.
+     * With 2 buffers in flush_cb only an address change is required.
+     */
+    LV_DISPLAY_RENDER_MODE_FULL,
+} lv_display_render_mode_t;
+
+typedef enum {
+    LV_SCREEN_LOAD_ANIM_NONE,
+    LV_SCREEN_LOAD_ANIM_OVER_LEFT,
+    LV_SCREEN_LOAD_ANIM_OVER_RIGHT,
+    LV_SCREEN_LOAD_ANIM_OVER_TOP,
+    LV_SCREEN_LOAD_ANIM_OVER_BOTTOM,
+    LV_SCREEN_LOAD_ANIM_MOVE_LEFT,
+    LV_SCREEN_LOAD_ANIM_MOVE_RIGHT,
+    LV_SCREEN_LOAD_ANIM_MOVE_TOP,
+    LV_SCREEN_LOAD_ANIM_MOVE_BOTTOM,
+    LV_SCREEN_LOAD_ANIM_FADE_IN,
+    LV_SCREEN_LOAD_ANIM_FADE_ON = LV_SCREEN_LOAD_ANIM_FADE_IN, /*For backward compatibility*/
+    LV_SCREEN_LOAD_ANIM_FADE_OUT,
+    LV_SCREEN_LOAD_ANIM_OUT_LEFT,
+    LV_SCREEN_LOAD_ANIM_OUT_RIGHT,
+    LV_SCREEN_LOAD_ANIM_OUT_TOP,
+    LV_SCREEN_LOAD_ANIM_OUT_BOTTOM,
+} lv_screen_load_anim_t;
+
+typedef void (*lv_display_flush_cb_t)(lv_display_t * disp, const lv_area_t * area, uint8_t * px_map);
+typedef void (*lv_display_flush_wait_cb_t)(lv_display_t * disp);
+
+/**********************
+ * GLOBAL PROTOTYPES
+ **********************/
+
+/**
+ * Create a new display with the given resolution
+ * @param hor_res   horizontal resolution in pixels
+ * @param ver_res   vertical resolution in pixels
+ * @return          pointer to a display object or `NULL` on error
+ */
+lv_display_t * lv_display_create(int32_t hor_res, int32_t ver_res);
+
+/**
+ * Remove a display
+ * @param disp      pointer to display
+ */
+void lv_display_delete(lv_display_t * disp);
+
+/**
+ * Set a default display. The new screens will be created on it by default.
+ * @param disp      pointer to a display
+ */
+void lv_display_set_default(lv_display_t * disp);
+
+/**
+ * Get the default display
+ * @return          pointer to the default display
+ */
+lv_display_t * lv_display_get_default(void);
+
+/**
+ * Get the next display.
+ * @param disp      pointer to the current display. NULL to initialize.
+ * @return          the next display or NULL if no more. Gives the first display when the parameter is NULL.
+ */
+lv_display_t * lv_display_get_next(lv_display_t * disp);
+
+/*---------------------
+ * RESOLUTION
+ *--------------------*/
+
+/**
+ * Sets the resolution of a display. `LV_EVENT_RESOLUTION_CHANGED` event will be sent.
+ * Here the native resolution of the device should be set. If the display will be rotated later with
+ * `lv_display_set_rotation` LVGL will swap the hor. and ver. resolution automatically.
+ * @param disp      pointer to a display
+ * @param hor_res   the new horizontal resolution
+ * @param ver_res   the new vertical resolution
+ */
+void lv_display_set_resolution(lv_display_t * disp, int32_t hor_res, int32_t ver_res);
+
+/**
+ * It's not mandatory to use the whole display for LVGL, however in some cases physical resolution is important.
+ * For example the touchpad still sees whole resolution and the values needs to be converted
+ * to the active LVGL display area.
+ * @param disp      pointer to a display
+ * @param hor_res   the new physical horizontal resolution, or -1 to assume it's the same as the normal hor. res.
+ * @param ver_res   the new physical vertical resolution, or -1 to assume it's the same as the normal hor. res.
+ */
+void lv_display_set_physical_resolution(lv_display_t * disp, int32_t hor_res, int32_t ver_res);
+
+/**
+ * If physical resolution is not the same as the normal resolution
+ * the offset of the active display area can be set here.
+ * @param disp      pointer to a display
+ * @param x         X offset
+ * @param y         Y offset
+ */
+void lv_display_set_offset(lv_display_t * disp, int32_t x, int32_t y);
+
+/**
+ * Set the rotation of this display. LVGL will swap the horizontal and vertical resolutions internally.
+ * @param disp      pointer to a display (NULL to use the default display)
+ * @param rotation  `LV_DISPLAY_ROTATION_0/90/180/270`
+ */
+void lv_display_set_rotation(lv_display_t * disp, lv_display_rotation_t rotation);
+
+/**
+ * Use matrix rotation for the display. This function is depended on `LV_DRAW_TRANSFORM_USE_MATRIX`
+ * @param disp      pointer to a display (NULL to use the default display)
+ * @param enable    true: enable matrix rotation, false: disable
+ */
+void lv_display_set_matrix_rotation(lv_display_t * disp, bool enable);
+
+/**
+ * Set the DPI (dot per inch) of the display.
+ * dpi = sqrt(hor_res^2 + ver_res^2) / diagonal"
+ * @param disp      pointer to a display
+ * @param dpi       the new DPI
+ */
+void lv_display_set_dpi(lv_display_t * disp, int32_t dpi);
+
+/**
+ * Get the horizontal resolution of a display.
+ * @param disp      pointer to a display (NULL to use the default display)
+ * @return          the horizontal resolution of the display.
+ */
+int32_t lv_display_get_horizontal_resolution(const lv_display_t * disp);
+
+/**
+ * Get the vertical resolution of a display
+ * @param disp      pointer to a display (NULL to use the default display)
+ * @return          the vertical resolution of the display
+ */
+int32_t lv_display_get_vertical_resolution(const lv_display_t * disp);
+
+/**
+ * Get the original horizontal resolution of a display without considering rotation
+ * @param disp      pointer to a display (NULL to use the default display)
+ * @return          the horizontal resolution of the display.
+ */
+int32_t lv_display_get_original_horizontal_resolution(const lv_display_t * disp);
+
+/**
+ * Get the original vertical resolution of a display without considering rotation
+ * @param disp      pointer to a display (NULL to use the default display)
+ * @return          the vertical resolution of the display
+ */
+int32_t lv_display_get_original_vertical_resolution(const lv_display_t * disp);
+
+/**
+ * Get the physical horizontal resolution of a display
+ * @param disp      pointer to a display (NULL to use the default display)
+ * @return the      physical horizontal resolution of the display
+ */
+int32_t lv_display_get_physical_horizontal_resolution(const lv_display_t * disp);
+
+/**
+ * Get the physical vertical resolution of a display
+ * @param disp      pointer to a display (NULL to use the default display)
+ * @return          the physical vertical resolution of the display
+ */
+int32_t lv_display_get_physical_vertical_resolution(const lv_display_t * disp);
+
+/**
+ * Get the horizontal offset from the full / physical display
+ * @param disp      pointer to a display (NULL to use the default display)
+ * @return          the horizontal offset from the physical display
+ */
+int32_t lv_display_get_offset_x(const lv_display_t * disp);
+
+/**
+ * Get the vertical offset from the full / physical display
+ * @param disp      pointer to a display (NULL to use the default display)
+ * @return          the horizontal offset from the physical display
+ */
+int32_t lv_display_get_offset_y(const lv_display_t * disp);
+
+/**
+ * Get the current rotation of this display.
+ * @param disp      pointer to a display (NULL to use the default display)
+ * @return          the current rotation
+ */
+lv_display_rotation_t lv_display_get_rotation(lv_display_t * disp);
+
+/**
+ * Get if matrix rotation is enabled for a display or not
+ * @param disp      pointer to a display (NULL to use the default display)
+ * @return          true: matrix rotation is enabled; false: disabled
+ */
+bool lv_display_get_matrix_rotation(lv_display_t * disp);
+
+/**
+ * Get the DPI of the display
+ * @param disp      pointer to a display (NULL to use the default display)
+ * @return          dpi of the display
+ */
+int32_t lv_display_get_dpi(const lv_display_t * disp);
+
+/*---------------------
+ * BUFFERING
+ *--------------------*/
+
+/**
+ * Set the buffers for a display, similarly to `lv_display_set_draw_buffers`, but accept the raw buffer pointers.
+ * For DIRECT/FULL rending modes, the buffer size must be at least
+ * `hor_res * ver_res * lv_color_format_get_size(lv_display_get_color_format(disp))`
+ * @param disp              pointer to a display
+ * @param buf1              first buffer
+ * @param buf2              second buffer (can be `NULL`)
+ * @param buf_size          buffer size in byte
+ * @param render_mode       LV_DISPLAY_RENDER_MODE_PARTIAL/DIRECT/FULL
+ */
+void lv_display_set_buffers(lv_display_t * disp, void * buf1, void * buf2, uint32_t buf_size,
+                            lv_display_render_mode_t render_mode);
+
+/**
+ * Set the frame buffers for a display, similarly to `lv_display_set_buffers`, but allow
+ * for a custom stride as required by a display controller.
+ * This allows the frame buffers to have a stride alignment different from the rest of
+ * the buffers`
+ * @param disp              pointer to a display
+ * @param buf1              first buffer
+ * @param buf2              second buffer (can be `NULL`)
+ * @param buf_size          buffer size in byte
+ * @param stride            buffer stride in bytes
+ * @param render_mode       LV_DISPLAY_RENDER_MODE_PARTIAL/DIRECT/FULL
+ */
+void lv_display_set_buffers_with_stride(lv_display_t * disp, void * buf1, void * buf2, uint32_t buf_size,
+                                        uint32_t stride, lv_display_render_mode_t render_mode);
+
+/**
+ * Set the buffers for a display, accept a draw buffer pointer.
+ * Normally use `lv_display_set_buffers` is enough for most cases.
+ * Use this function when an existing lv_draw_buf_t is available.
+ * @param disp              pointer to a display
+ * @param buf1              first buffer
+ * @param buf2              second buffer (can be `NULL`)
+ */
+void lv_display_set_draw_buffers(lv_display_t * disp, lv_draw_buf_t * buf1, lv_draw_buf_t * buf2);
+
+/**
+ * Set the third draw buffer for a display.
+ * @param disp              pointer to a display
+ * @param buf3              third buffer
+ */
+void lv_display_set_3rd_draw_buffer(lv_display_t * disp, lv_draw_buf_t * buf3);
+
+/**
+ * Set display render mode
+ * @param disp              pointer to a display
+ * @param render_mode       LV_DISPLAY_RENDER_MODE_PARTIAL/DIRECT/FULL
+ */
+void lv_display_set_render_mode(lv_display_t * disp, lv_display_render_mode_t render_mode);
+
+/**
+ * Set the flush callback which will be called to copy the rendered image to the display.
+ * @param disp      pointer to a display
+ * @param flush_cb  the flush callback (`px_map` contains the rendered image as raw pixel map and it should be copied to `area` on the display)
+ */
+void lv_display_set_flush_cb(lv_display_t * disp, lv_display_flush_cb_t flush_cb);
+
+/**
+ * Set a callback to be used while LVGL is waiting flushing to be finished.
+ * It can do any complex logic to wait, including semaphores, mutexes, polling flags, etc.
+ * If not set the `disp->flushing` flag is used which can be cleared with `lv_display_flush_ready()`
+ * @param disp      pointer to a display
+ * @param wait_cb   a callback to call while LVGL is waiting for flush ready.
+ *                  If NULL `lv_display_flush_ready()` can be used to signal that flushing is ready.
+ */
+void lv_display_set_flush_wait_cb(lv_display_t * disp, lv_display_flush_wait_cb_t wait_cb);
+
+/**
+ * Set the color format of the display.
+ * @param disp              pointer to a display
+ * @param color_format      Possible values are
+ *                          - LV_COLOR_FORMAT_RGB565
+ *                          - LV_COLOR_FORMAT_RGB888
+ *                          - LV_COLOR_FORMAT_XRGB888
+ *                          - LV_COLOR_FORMAT_ARGB888
+ *@note To change the endianness of the rendered image in case of RGB565 format
+ *      (i.e. swap the 2 bytes) call `lv_draw_sw_rgb565_swap` in the flush_cb
+ */
+void lv_display_set_color_format(lv_display_t * disp, lv_color_format_t color_format);
+
+/**
+ * Get the color format of the display
+ * @param disp              pointer to a display
+ * @return                  the color format
+ */
+lv_color_format_t lv_display_get_color_format(lv_display_t * disp);
+
+/**
+ * Set the number of tiles for parallel rendering.
+ * @param disp              pointer to a display
+ * @param tile_cnt          number of tiles (1 =< tile_cnt < 256)
+ */
+void lv_display_set_tile_cnt(lv_display_t * disp, uint32_t tile_cnt);
+
+/**
+ * Get the number of tiles used for parallel rendering
+ * @param disp              pointer to a display
+ * @return                  number of tiles
+ */
+uint32_t lv_display_get_tile_cnt(lv_display_t * disp);
+
+/**
+ * Disabling anti-aliasing is not supported since v9. This function will be removed.
+ * Enable anti-aliasing for the render engine
+ * @param disp      pointer to a display
+ * @param en        true/false
+ */
+void lv_display_set_antialiasing(lv_display_t * disp, bool en);
+
+/**
+ * Get if anti-aliasing is enabled for a display or not
+ * @param disp      pointer to a display (NULL to use the default display)
+ * @return          true/false
+ */
+bool lv_display_get_antialiasing(lv_display_t * disp);
+
+/**
+ * Call from the display driver when the flushing is finished
+ * @param disp      pointer to display whose `flush_cb` was called
+ */
+LV_ATTRIBUTE_FLUSH_READY void lv_display_flush_ready(lv_display_t * disp);
+
+/**
+ * Tell if it's the last area of the refreshing process.
+ * Can be called from `flush_cb` to execute some special display refreshing if needed when all areas area flushed.
+ * @param disp      pointer to display
+ * @return          true: it's the last area to flush;
+ *                  false: there are other areas too which will be refreshed soon
+ */
+LV_ATTRIBUTE_FLUSH_READY bool lv_display_flush_is_last(lv_display_t * disp);
+
+bool lv_display_is_double_buffered(lv_display_t * disp);
+
+/*---------------------
+ * SCREENS
+ *--------------------*/
+
+/**
+ * Return a pointer to the active screen on a display
+ * @param disp      pointer to display which active screen should be get.
+ *                  (NULL to use the default screen)
+ * @return          pointer to the active screen object (loaded by 'lv_screen_load()')
+ */
+lv_obj_t * lv_display_get_screen_active(lv_display_t * disp);
+
+/**
+ * Return with a pointer to the previous screen. Only used during screen transitions.
+ * @param disp      pointer to display which previous screen should be get.
+ *                  (NULL to use the default screen)
+ * @return          pointer to the previous screen object or NULL if not used now
+ */
+lv_obj_t * lv_display_get_screen_prev(lv_display_t * disp);
+
+/**
+ * Return the screen that is currently being loaded by the display
+ * @param disp      pointer to a display object (NULL to use the default screen)
+ * @return          pointer to the screen being loaded or NULL if no screen is currently being loaded
+ */
+lv_obj_t * lv_display_get_screen_loading(lv_display_t * disp);
+
+/**
+ * Return the top layer. The top layer is the same on all screens and it is above the normal screen layer.
+ * @param disp      pointer to display which top layer should be get. (NULL to use the default screen)
+ * @return          pointer to the top layer object
+ */
+lv_obj_t * lv_display_get_layer_top(lv_display_t * disp);
+
+/**
+ * Return the sys. layer. The system layer is the same on all screen and it is above the normal screen and the top layer.
+ * @param disp      pointer to display which sys. layer should be retrieved. (NULL to use the default screen)
+ * @return          pointer to the sys layer object
+ */
+lv_obj_t * lv_display_get_layer_sys(lv_display_t * disp);
+
+/**
+ * Return the bottom layer. The bottom layer is the same on all screen and it is under the normal screen layer.
+ * It's visible only if the screen is transparent.
+ * @param disp      pointer to display (NULL to use the default screen)
+ * @return          pointer to the bottom layer object
+ */
+lv_obj_t * lv_display_get_layer_bottom(lv_display_t * disp);
+
+
+#if LV_USE_OBJ_NAME
+
+/**
+ * Get screen by its name on a display. The name should be set by
+ * `lv_obj_set_name()` or `lv_obj_set_name_static()`.
+ * @param disp          pointer to a display or NULL to use default display
+ * @param screen_name   name of the screen to get
+ * @return              pointer to the screen, or NULL if not found.
+ */
+lv_obj_t * lv_display_get_screen_by_name(const lv_display_t * disp, const char * screen_name);
+
+#endif /*LV_USE_OBJ_NAME*/
+
+/**
+ * Load a screen on the default display
+ * @param scr       pointer to a screen
+ */
+void lv_screen_load(struct _lv_obj_t * scr);
+
+/**
+ * Switch screen with animation
+ * @param scr       pointer to the new screen to load
+ * @param anim_type type of the animation from `lv_screen_load_anim_t`, e.g. `LV_SCREEN_LOAD_ANIM_MOVE_LEFT`
+ * @param time      time of the animation
+ * @param delay     delay before the transition
+ * @param auto_del  true: automatically delete the old screen
+ */
+void lv_screen_load_anim(lv_obj_t * scr, lv_screen_load_anim_t anim_type, uint32_t time, uint32_t delay,
+                         bool auto_del);
+
+/**
+ * Get the active screen of the default display
+ * @return          pointer to the active screen
+ */
+lv_obj_t * lv_screen_active(void);
+
+/**
+ * Get the top layer  of the default display
+ * @return          pointer to the top layer
+ */
+lv_obj_t * lv_layer_top(void);
+
+/**
+ * Get the system layer  of the default display
+ * @return          pointer to the sys layer
+ */
+lv_obj_t * lv_layer_sys(void);
+
+/**
+ * Get the bottom layer  of the default display
+ * @return          pointer to the bottom layer
+ */
+lv_obj_t * lv_layer_bottom(void);
+
+/*---------------------
+ * OTHERS
+ *--------------------*/
+
+/**
+ * Add an event handler to the display
+ * @param disp          pointer to a display
+ * @param event_cb      an event callback
+ * @param filter        event code to react or `LV_EVENT_ALL`
+ * @param user_data     optional user_data
+ */
+void lv_display_add_event_cb(lv_display_t * disp, lv_event_cb_t event_cb, lv_event_code_t filter, void * user_data);
+
+/**
+ * Get the number of event attached to a display
+ * @param disp          pointer to a display
+ * @return              number of events
+ */
+uint32_t lv_display_get_event_count(lv_display_t * disp);
+
+/**
+ * Get an event descriptor for an event
+ * @param disp          pointer to a display
+ * @param index         the index of the event
+ * @return              the event descriptor
+ */
+lv_event_dsc_t * lv_display_get_event_dsc(lv_display_t * disp, uint32_t index);
+
+/**
+ * Remove an event
+ * @param disp          pointer to a display
+ * @param index         the index of the event to remove
+ * @return              true: and event was removed; false: no event was removed
+ */
+bool lv_display_delete_event(lv_display_t * disp, uint32_t index);
+
+/**
+ * Remove an event_cb with user_data
+ * @param disp          pointer to a display
+ * @param event_cb      the event_cb of the event to remove
+ * @param user_data     user_data
+ * @return              the count of the event removed
+ */
+uint32_t lv_display_remove_event_cb_with_user_data(lv_display_t * disp, lv_event_cb_t event_cb, void * user_data);
+
+/**
+ * Send an event to a display
+ * @param disp          pointer to a display
+ * @param code          an event code. LV_EVENT_...
+ * @param param         optional param
+ * @return              LV_RESULT_OK: disp wasn't deleted in the event.
+ */
+lv_result_t lv_display_send_event(lv_display_t * disp, lv_event_code_t code, void * param);
+
+/**
+ * Get the area to be invalidated. Can be used in `LV_EVENT_INVALIDATE_AREA`
+ * @param e     pointer to an event
+ * @return      the area to invalidated (can be modified as required)
+ */
+lv_area_t * lv_event_get_invalidated_area(lv_event_t * e);
+
+/**
+ * Set the theme of a display. If there are no user created widgets yet the screens' theme will be updated
+ * @param disp      pointer to a display
+ * @param th        pointer to a theme
+ */
+void lv_display_set_theme(lv_display_t * disp, lv_theme_t * th);
+
+/**
+ * Get the theme of a display
+ * @param disp      pointer to a display
+ * @return          the display's theme (can be NULL)
+ */
+lv_theme_t * lv_display_get_theme(lv_display_t * disp);
+
+/**
+ * Get elapsed time since last user activity on a display (e.g. click)
+ * @param disp      pointer to a display (NULL to get the overall smallest inactivity)
+ * @return          elapsed ticks (milliseconds) since the last activity
+ */
+uint32_t lv_display_get_inactive_time(const lv_display_t * disp);
+
+/**
+ * Manually trigger an activity on a display
+ * @param disp      pointer to a display (NULL to use the default display)
+ */
+void lv_display_trigger_activity(lv_display_t * disp);
+
+/**
+ * Temporarily enable and disable the invalidation of the display.
+ * @param disp      pointer to a display (NULL to use the default display)
+ * @param en        true: enable invalidation; false: invalidation
+ */
+void lv_display_enable_invalidation(lv_display_t * disp, bool en);
+
+/**
+ * Get display invalidation is enabled.
+ * @param disp      pointer to a display (NULL to use the default display)
+ * @return return   true if invalidation is enabled
+ */
+bool lv_display_is_invalidation_enabled(lv_display_t * disp);
+
+/**
+ * Get a pointer to the screen refresher timer to
+ * modify its parameters with `lv_timer_...` functions.
+ * @param disp      pointer to a display
+ * @return          pointer to the display refresher timer. (NULL on error)
+ */
+lv_timer_t * lv_display_get_refr_timer(lv_display_t * disp);
+
+/**
+ * Delete screen refresher timer
+ * @param disp      pointer to a display
+ */
+void lv_display_delete_refr_timer(lv_display_t * disp);
+
+/**
+ * Register vsync event of a display. `LV_EVENT_VSYNC` event will be sent periodically.
+ * Please don't use it in display event listeners, as it may cause memory leaks and illegal access issues.
+ *
+ * @param disp      pointer to a display
+ * @param event_cb      an event callback
+ * @param user_data     optional user_data
+ */
+bool lv_display_register_vsync_event(lv_display_t * disp, lv_event_cb_t event_cb, void * user_data);
+
+/**
+ * Unregister vsync event of a display. `LV_EVENT_VSYNC` event won't be sent periodically.
+ * Please don't use it in display event listeners, as it may cause memory leaks and illegal access issues.
+ * @param disp      pointer to a display
+ * @param event_cb      an event callback
+ * @param user_data     optional user_data
+ */
+bool lv_display_unregister_vsync_event(lv_display_t * disp, lv_event_cb_t event_cb, void * user_data);
+
+/**
+ * Send an vsync event to a display
+ * @param disp          pointer to a display
+ * @param param         optional param
+ * @return              LV_RESULT_OK: disp wasn't deleted in the event.
+ */
+lv_result_t lv_display_send_vsync_event(lv_display_t * disp, void * param);
+
+void lv_display_set_user_data(lv_display_t * disp, void * user_data);
+void lv_display_set_driver_data(lv_display_t * disp, void * driver_data);
+void * lv_display_get_user_data(lv_display_t * disp);
+void * lv_display_get_driver_data(lv_display_t * disp);
+lv_draw_buf_t * lv_display_get_buf_active(lv_display_t * disp);
+
+/**
+ * Rotate an area in-place according to the display's rotation
+ * @param disp      pointer to a display
+ * @param area      pointer to an area to rotate
+ */
+void lv_display_rotate_area(lv_display_t * disp, lv_area_t * area);
+
+/**
+ * Get the size of the draw buffers
+ * @param disp      pointer to a display
+ * @return          the size of the draw buffer in bytes for valid display, 0 otherwise
+ */
+uint32_t lv_display_get_draw_buf_size(lv_display_t * disp);
+
+/**
+ * Get the size of the invalidated draw buffer. Can be used in the flush callback
+ * to get the number of bytes used in the current render buffer.
+ * @param disp      pointer to a display
+ * @param width     the width of the invalidated area
+ * @param height    the height of the invalidated area
+ * @return          the size of the invalidated draw buffer in bytes, not accounting for
+ *                  any preceding palette information for a valid display, 0 otherwise
+ */
+uint32_t lv_display_get_invalidated_draw_buf_size(lv_display_t * disp, uint32_t width, uint32_t height);
+
+/**********************
+ *      MACROS
+ **********************/
+
+/*------------------------------------------------
+ * To improve backward compatibility
+ * Recommended only if you have one display
+ *------------------------------------------------*/
+
+#ifndef LV_HOR_RES
+/**
+ * The horizontal resolution of the currently active display.
+ */
+#define LV_HOR_RES lv_display_get_horizontal_resolution(lv_display_get_default())
+#endif
+
+#ifndef LV_VER_RES
+/**
+ * The vertical resolution of the currently active display.
+ */
+#define LV_VER_RES lv_display_get_vertical_resolution(lv_display_get_default())
+#endif
+
+/**
+ * See `lv_dpx()` and `lv_display_dpx()`.
+ * Same as Android's DIP. (Different name is chosen to avoid mistype between LV_DPI and LV_DIP)
+ *
+ * - 40 dip is 40 px on a 160 DPI screen (distance = 1/4 inch).
+ * - 40 dip is 80 px on a 320 DPI screen (distance still = 1/4 inch).
+ *
+ * @sa https://stackoverflow.com/questions/2025282/what-is-the-difference-between-px-dip-dp-and-sp
+ */
+#define LV_DPX_CALC(dpi, n)   ((n) == 0 ? 0 :LV_MAX((( (dpi) * (n) + 80) / 160), 1)) /*+80 for rounding*/
+#define LV_DPX(n)   LV_DPX_CALC(lv_display_get_dpi(NULL), n)
+
+/**
+ * For default display, computes the number of pixels (a distance or size) as if the
+ * display had 160 DPI.  This allows you to specify 1/160-th fractions of an inch to
+ * get real distance on the display that will be consistent regardless of its current
+ * DPI.  It ensures `lv_dpx(100)`, for example, will have the same physical size
+ * regardless to the DPI of the display.
+ * @param n     number of 1/160-th-inch units to compute with
+ * @return      number of pixels to use to make that distance
+ */
+int32_t lv_dpx(int32_t n);
+
+/**
+ * For specified display, computes the number of pixels (a distance or size) as if the
+ * display had 160 DPI.  This allows you to specify 1/160-th fractions of an inch to
+ * get real distance on the display that will be consistent regardless of its current
+ * DPI.  It ensures `lv_dpx(100)`, for example, will have the same physical size
+ * regardless to the DPI of the display.
+ * @param disp  pointer to display whose dpi should be considered
+ * @param n     number of 1/160-th-inch units to compute with
+ * @return      number of pixels to use to make that distance
+ */
+int32_t lv_display_dpx(const lv_display_t * disp, int32_t n);
+
+#ifdef __cplusplus
+} /*extern "C"*/
+#endif
+
+#endif /*LV_DISPLAY_H*/

+ 188 - 0
components/easylvgl/lvgl9/src/display/lv_display_private.h

@@ -0,0 +1,188 @@
+/**
+ * @file lv_display_private.h
+ *
+ */
+
+#ifndef LV_DISPLAY_PRIVATE_H
+#define LV_DISPLAY_PRIVATE_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/*********************
+ *      INCLUDES
+ *********************/
+#include "../misc/lv_types.h"
+#include "../core/lv_obj.h"
+#include "../draw/lv_draw.h"
+#include "lv_display.h"
+
+#if LV_USE_SYSMON
+#include "../others/sysmon/lv_sysmon_private.h"
+#endif
+
+/*********************
+ *      DEFINES
+ *********************/
+#ifndef LV_INV_BUF_SIZE
+#define LV_INV_BUF_SIZE 32 /**< Buffer size for invalid areas */
+#endif
+
+/**********************
+ *      TYPEDEFS
+ **********************/
+
+struct _lv_display_t {
+
+    /*---------------------
+     * Resolution
+     *--------------------*/
+
+    /** Horizontal resolution.*/
+    int32_t hor_res;
+
+    /** Vertical resolution.*/
+    int32_t ver_res;
+
+    /** Horizontal resolution of the full / physical display. Set to -1 for fullscreen mode.*/
+    int32_t physical_hor_res;
+
+    /** Vertical resolution of the full / physical display. Set to -1 for fullscreen mode.*/
+    int32_t physical_ver_res;
+
+    /** Horizontal offset from the full / physical display. Set to 0 for fullscreen mode.*/
+    int32_t offset_x;
+
+    /** Vertical offset from the full / physical display. Set to 0 for fullscreen mode.*/
+    int32_t offset_y;
+
+    /** DPI (dot per inch) of the display. Default value is `LV_DPI_DEF`.*/
+    uint32_t dpi;
+
+    /*---------------------
+     * Buffering
+     *--------------------*/
+    lv_draw_buf_t * buf_1;
+    lv_draw_buf_t * buf_2;
+    lv_draw_buf_t * buf_3;
+
+    /** Internal, used by the library*/
+    lv_draw_buf_t * buf_act;
+
+    /** MANDATORY: Write the internal buffer (draw_buf) to the display. 'lv_display_flush_ready()' has to be
+     * called when finished*/
+    lv_display_flush_cb_t flush_cb;
+
+    /**
+     * Used to wait while flushing is ready.
+     * It can do any complex logic to wait, including semaphores, mutexes, polling flags, etc.
+     * If not set `flushing` flag is used which can be cleared with `lv_display_flush_ready()` */
+    lv_display_flush_wait_cb_t flush_wait_cb;
+
+    /** 1: flushing is in progress. (It can't be a bit field because when it's cleared from IRQ
+     * Read-Modify-Write issue might occur) */
+    volatile int flushing;
+
+    /** 1: It was the last chunk to flush. (It can't be a bit field because when it's cleared
+     * from IRQ Read-Modify-Write issue might occur) */
+    volatile int flushing_last;
+    volatile uint32_t last_area         : 1; /**< 1: last area is being rendered */
+    volatile uint32_t last_part         : 1; /**< 1: last part of the current area is being rendered */
+
+    lv_display_render_mode_t render_mode;
+    uint32_t antialiasing : 1;       /**< 1: anti-aliasing is enabled on this display.*/
+    uint32_t tile_cnt     : 8;       /**< Divide the display buffer into these number of tiles */
+    uint32_t stride_is_auto : 1;     /**< 1: The stride of the buffers was not set explicitly. */
+
+
+    /** 1: The current screen rendering is in progress*/
+    uint32_t rendering_in_progress : 1;
+
+    lv_color_format_t   color_format;
+
+    /** Invalidated (marked to redraw) areas*/
+    lv_area_t inv_areas[LV_INV_BUF_SIZE];
+    uint8_t inv_area_joined[LV_INV_BUF_SIZE];
+    uint32_t inv_p;
+    int32_t inv_en_cnt;
+
+    /** Double buffer sync areas (redrawn during last refresh) */
+    lv_ll_t sync_areas;
+
+    lv_draw_buf_t _static_buf1; /**< Used when user pass in a raw buffer as display draw buffer */
+    lv_draw_buf_t _static_buf2;
+    /*---------------------
+     * Layer
+     *--------------------*/
+    lv_layer_t * layer_head;
+    void (*layer_init)(lv_display_t * disp, lv_layer_t * layer);
+    void (*layer_deinit)(lv_display_t * disp, lv_layer_t * layer);
+
+    /*---------------------
+     * Screens
+     *--------------------*/
+
+    /** Screens of the display*/
+    lv_obj_t ** screens;    /**< Array of screen objects.*/
+    lv_obj_t * sys_layer;   /**< @see lv_display_get_layer_sys*/
+    lv_obj_t * top_layer;   /**< @see lv_display_get_layer_top*/
+    lv_obj_t * act_scr;     /**< Currently active screen on this display*/
+    lv_obj_t * bottom_layer;/**< @see lv_display_get_layer_bottom*/
+    lv_obj_t * prev_scr;    /**< Previous screen. Used during screen animations*/
+    lv_obj_t * scr_to_load; /**< The screen prepared to load in lv_screen_load_anim*/
+    uint32_t screen_cnt;
+    uint8_t draw_prev_over_act  : 1;/** 1: Draw previous screen over active screen*/
+    uint8_t del_prev  : 1;  /** 1: Automatically delete the previous screen when the screen load animation is ready*/
+
+    /*---------------------
+     * Others
+     *--------------------*/
+
+    void * driver_data; /**< Custom user data*/
+
+    void * user_data; /**< Custom user data*/
+
+    lv_event_list_t event_list;
+
+    uint32_t rotation  : 3; /**< Element of  lv_display_rotation_t*/
+
+    uint32_t matrix_rotation : 1; /**< 1: Use matrix for display rotation*/
+
+    lv_theme_t * theme;     /**< The theme assigned to the screen*/
+
+    /** A timer which periodically checks the dirty areas and refreshes them*/
+    lv_timer_t * refr_timer;
+
+    /*Miscellaneous data*/
+    uint32_t last_activity_time;        /**< Last time when there was activity on this display*/
+
+    /** The area being refreshed*/
+    lv_area_t refreshed_area;
+    uint32_t vsync_count;
+
+#if LV_USE_PERF_MONITOR
+    lv_obj_t * perf_label;
+    lv_sysmon_backend_data_t perf_sysmon_backend;
+    lv_sysmon_perf_info_t perf_sysmon_info;
+#endif
+
+#if LV_USE_MEM_MONITOR
+    lv_obj_t * mem_label;
+#endif
+
+};
+
+/**********************
+ * GLOBAL PROTOTYPES
+ **********************/
+
+/**********************
+ *      MACROS
+ **********************/
+
+#ifdef __cplusplus
+} /*extern "C"*/
+#endif
+
+#endif /*LV_DISPLAY_PRIVATE_H*/

+ 100 - 0
components/easylvgl/lvgl9/src/draw/convert/helium/lv_draw_buf_convert_helium.c

@@ -0,0 +1,100 @@
+/**
+ * @file lv_draw_buf_convert_helium.c
+ *
+ */
+
+/*********************
+ *      INCLUDES
+ *********************/
+#include "../../../lv_conf_internal.h"
+
+#if LV_USE_DRAW_SW_ASM == LV_DRAW_SW_ASM_HELIUM
+#include "lv_draw_buf_convert_helium.h"
+
+/*********************
+ *      DEFINES
+ *********************/
+
+/**********************
+ *  STATIC PROTOTYPES
+ **********************/
+
+/**********************
+ *   STATIC FUNCTIONS
+ **********************/
+
+/**********************
+ *      MACROS
+ **********************/
+
+/**********************
+ *   GLOBAL FUNCTIONS
+ **********************/
+
+lv_result_t _lv_draw_buf_convert_premultiply_indexed_helium(lv_draw_buf_t * buf)
+{
+    lv_draw_buf_t palette_draw_buf;
+
+    LV_ASSERT_NULL(buf);
+
+    if(!LV_COLOR_FORMAT_IS_INDEXED(buf->header.cf)) {
+        LV_LOG_WARN("Unsupported color format : %d", buf->header.cf);
+        return LV_RESULT_INVALID;
+    }
+
+    lv_memcpy(&palette_draw_buf, buf, sizeof(lv_draw_buf_t));
+
+    palette_draw_buf.header.w = LV_COLOR_INDEXED_PALETTE_SIZE(buf->header.cf);
+    palette_draw_buf.header.h = 1;
+    palette_draw_buf.header.cf = LV_COLOR_FORMAT_ARGB8888;
+    palette_draw_buf.header.stride = 4 * palette_draw_buf.header.w;
+
+    return _lv_draw_buf_convert_premultiply_argb8888_helium(&palette_draw_buf);
+}
+
+lv_result_t _lv_draw_buf_convert_premultiply_argb8888_helium(lv_draw_buf_t * buf)
+{
+    LV_ASSERT_NULL(buf);
+
+    uint32_t h = buf->header.h;
+    uint32_t w = buf->header.w;
+    uint32_t stride = buf->header.stride;
+    uint8_t * data = (uint8_t *)buf->data;
+
+    if(buf->header.cf != LV_COLOR_FORMAT_ARGB8888) {
+        LV_LOG_WARN("Unsupported color format : %d", buf->header.cf);
+        return LV_RESULT_INVALID;
+    }
+
+    __asm volatile(
+        "   .p2align 2                                                  \n"
+        "   1:                                                          \n"
+        "   mov                     r0, %[pSource]                      \n"
+        "   mov                     r1, %[pTarget]                      \n"
+        "   wlstp.8                 lr, %[w], 3f                        \n"
+        "   2:                                                          \n"
+        "   vld40.u8                {q0, q1, q2, q3}, [r0]              \n"
+        "   vld41.u8                {q0, q1, q2, q3}, [r0]              \n"
+        "   vld42.u8                {q0, q1, q2, q3}, [r0]              \n"
+        "   vld43.u8                {q0, q1, q2, q3}, [r0]!             \n"
+        "   vrmulh.u8               q0, q0, q3                          \n"
+        "   vrmulh.u8               q1, q1, q3                          \n"
+        "   vrmulh.u8               q2, q2, q3                          \n"
+        "   vst40.u8                {q0, q1, q2, q3}, [r1]              \n"
+        "   vst41.u8                {q0, q1, q2, q3}, [r1]              \n"
+        "   vst42.u8                {q0, q1, q2, q3}, [r1]              \n"
+        "   vst43.u8                {q0, q1, q2, q3}, [r1]!             \n"
+        "   letp                    lr, 2b                              \n"
+        "   3:                                                          \n"
+        "   adds                    %[pSource], %[src_stride]           \n"
+        "   adds                    %[pTarget], %[dst_stride]           \n"
+        "   subs                    %[h], #1                            \n"
+        "   bne                     1b                                  \n"
+        : [pSource] "+r"(data), [pTarget] "+r"(data), [h] "+r"(h)
+        : [w] "r"(w), [src_stride] "r"(stride), [dst_stride] "r"(stride)
+        : "q0", "q1", "q2", "q3", "r0", "r1", "lr", "memory");
+
+    return LV_RESULT_OK;
+}
+
+#endif  /* LV_USE_DRAW_SW_ASM == LV_DRAW_SW_ASM_HELIUM */

+ 57 - 0
components/easylvgl/lvgl9/src/draw/convert/helium/lv_draw_buf_convert_helium.h

@@ -0,0 +1,57 @@
+/**
+ * @file lv_draw_buf_convert_helium.h
+ *
+ */
+
+#ifndef LV_DRAW_BUF_CONVERT_HELIUM_H
+#define LV_DRAW_BUF_CONVERT_HELIUM_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include "../../../misc/lv_color.h"
+#include "../../lv_draw_buf.h"
+
+#if LV_USE_DRAW_SW_ASM == LV_DRAW_SW_ASM_HELIUM
+
+/*********************
+ *      DEFINES
+ *********************/
+
+/**********************
+ *      TYPEDEFS
+ **********************/
+
+#ifndef LV_DRAW_CONVERT_PREMULTIPLY_INDEXED
+#define LV_DRAW_CONVERT_PREMULTIPLY_INDEXED(buf) \
+    _lv_draw_buf_convert_premultiply_indexed_helium(buf)
+#endif
+
+#ifndef LV_DRAW_CONVERT_PREMULTIPLY_ARGB8888
+#define LV_DRAW_CONVERT_PREMULTIPLY_ARGB8888(buf) \
+    _lv_draw_buf_convert_premultiply_argb8888_helium(buf)
+#endif
+
+/**********************
+ * GLOBAL PROTOTYPES
+ **********************/
+
+/**
+ * Convert indexed draw_buf to premultiplied format with helium specific optimizations
+ * @param buf     pointer to a draw buf
+ */
+lv_result_t _lv_draw_buf_convert_premultiply_indexed_helium(lv_draw_buf_t * buf);
+
+/**
+ * Convert argb8888 draw_buf to premultiplied format with helium specific optimizations
+ * @param buf     pointer to a draw buf
+ */
+lv_result_t _lv_draw_buf_convert_premultiply_argb8888_helium(lv_draw_buf_t * buf);
+
+#endif /*LV_USE_DRAW_SW_ASM == LV_DRAW_SW_ASM_HELIUM*/
+#ifdef __cplusplus
+} /* extern "C" */
+#endif
+
+#endif /* LV_DRAW_BUF_CONVERT_HELIUM_H */

+ 130 - 0
components/easylvgl/lvgl9/src/draw/convert/lv_draw_buf_convert.c

@@ -0,0 +1,130 @@
+/**
+ * @file lv_draw_buf_convert.c
+ *
+ */
+
+/*********************
+ *      INCLUDES
+ *********************/
+#include "lv_draw_buf_convert.h"
+
+#if LV_USE_DRAW_SW_ASM == LV_DRAW_SW_ASM_NEON
+    #include "neon/lv_draw_buf_convert_neon.h"
+#elif LV_USE_DRAW_SW_ASM == LV_DRAW_SW_ASM_HELIUM
+    #include "helium/lv_draw_buf_convert_helium.h"
+#elif LV_USE_DRAW_SW_ASM == LV_DRAW_SW_ASM_CUSTOM
+    #include LV_DRAW_SW_ASM_CUSTOM_INCLUDE
+#endif
+
+/*********************
+ *      DEFINES
+ *********************/
+
+/**********************
+ *  STATIC PROTOTYPES
+ **********************/
+
+/**********************
+ *   STATIC FUNCTIONS
+ **********************/
+
+/**********************
+ *      MACROS
+ **********************/
+
+#ifndef LV_DRAW_CONVERT_PREMULTIPLY_INDEXED
+    #define LV_DRAW_CONVERT_PREMULTIPLY_INDEXED(...)                         LV_RESULT_INVALID
+#endif
+
+#ifndef LV_DRAW_CONVERT_PREMULTIPLY_ARGB8888
+    #define LV_DRAW_CONVERT_PREMULTIPLY_ARGB8888(...)                         LV_RESULT_INVALID
+#endif
+
+#ifndef LV_DRAW_CONVERT_PREMULTIPLY_RGB565A8
+    #define LV_DRAW_CONVERT_PREMULTIPLY_RGB565A8(...)                         LV_RESULT_INVALID
+#endif
+
+#ifndef LV_DRAW_CONVERT_PREMULTIPLY_ARGB8565
+    #define LV_DRAW_CONVERT_PREMULTIPLY_ARGB8565(...)                         LV_RESULT_INVALID
+#endif
+
+/**********************
+ *   GLOBAL FUNCTIONS
+ **********************/
+
+lv_result_t lv_draw_buf_convert_premultiply(lv_draw_buf_t * draw_buf)
+{
+    LV_ASSERT_NULL(draw_buf);
+
+    /*Premultiply color with alpha, do case by case by judging color format*/
+    lv_color_format_t cf = draw_buf->header.cf;
+    if(LV_COLOR_FORMAT_IS_INDEXED(cf)) {
+        if(LV_RESULT_INVALID == LV_DRAW_CONVERT_PREMULTIPLY_INDEXED(draw_buf)) {
+            int size = LV_COLOR_INDEXED_PALETTE_SIZE(cf);
+            lv_color32_t * palette = (lv_color32_t *)draw_buf->data;
+            for(int i = 0; i < size; i++) {
+                lv_color_premultiply(&palette[i]);
+            }
+        }
+    }
+    else if(cf == LV_COLOR_FORMAT_ARGB8888) {
+        if(LV_RESULT_INVALID == LV_DRAW_CONVERT_PREMULTIPLY_ARGB8888(draw_buf)) {
+            uint32_t h = draw_buf->header.h;
+            uint32_t w = draw_buf->header.w;
+            uint32_t stride = draw_buf->header.stride;
+            uint8_t * line = (uint8_t *)draw_buf->data;
+            for(uint32_t y = 0; y < h; y++) {
+                lv_color32_t * pixel = (lv_color32_t *)line;
+                for(uint32_t x = 0; x < w; x++) {
+                    lv_color_premultiply(pixel);
+                    pixel++;
+                }
+                line += stride;
+            }
+        }
+    }
+    else if(cf == LV_COLOR_FORMAT_RGB565A8) {
+        if(LV_RESULT_INVALID == LV_DRAW_CONVERT_PREMULTIPLY_RGB565A8(draw_buf)) {
+            uint32_t h = draw_buf->header.h;
+            uint32_t w = draw_buf->header.w;
+            uint32_t stride = draw_buf->header.stride;
+            uint32_t alpha_stride = stride / 2;
+            uint8_t * line = (uint8_t *)draw_buf->data;
+            lv_opa_t * alpha = (lv_opa_t *)(line + stride * h);
+            for(uint32_t y = 0; y < h; y++) {
+                lv_color16_t * pixel = (lv_color16_t *)line;
+                for(uint32_t x = 0; x < w; x++) {
+                    lv_color16_premultiply(pixel, alpha[x]);
+                    pixel++;
+                }
+                line += stride;
+                alpha += alpha_stride;
+            }
+        }
+    }
+    else if(cf == LV_COLOR_FORMAT_ARGB8565) {
+        if(LV_RESULT_INVALID == LV_DRAW_CONVERT_PREMULTIPLY_ARGB8565(draw_buf)) {
+            uint32_t h = draw_buf->header.h;
+            uint32_t w = draw_buf->header.w;
+            uint32_t stride = draw_buf->header.stride;
+            uint8_t * line = (uint8_t *)draw_buf->data;
+            for(uint32_t y = 0; y < h; y++) {
+                uint8_t * pixel = line;
+                for(uint32_t x = 0; x < w; x++) {
+                    uint8_t alpha = pixel[2];
+                    lv_color16_premultiply((lv_color16_t *)pixel, alpha);
+                    pixel += 3;
+                }
+                line += stride;
+            }
+        }
+    }
+    else if(LV_COLOR_FORMAT_IS_ALPHA_ONLY(cf)) {
+        /*Pass*/
+    }
+    else {
+        LV_LOG_WARN("draw buf has no alpha, cf: %d", cf);
+    }
+
+    return LV_RESULT_OK;
+}

+ 39 - 0
components/easylvgl/lvgl9/src/draw/convert/lv_draw_buf_convert.h

@@ -0,0 +1,39 @@
+/**
+ * @file lv_draw_buf_convert.h
+ *
+ */
+
+#ifndef LV_DRAW_BUF_CONVERT_H
+#define LV_DRAW_BUF_CONVERT_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include "../../misc/lv_color.h"
+#include "../lv_draw_buf.h"
+
+/*********************
+ *      DEFINES
+ *********************/
+
+/**********************
+ *      TYPEDEFS
+ **********************/
+
+/**********************
+ * GLOBAL PROTOTYPES
+ **********************/
+
+/**
+ * Convert draw_buf  to premultiplied format
+ * @param buf     pointer to a draw buf
+ */
+lv_result_t lv_draw_buf_convert_premultiply(lv_draw_buf_t * buf);
+
+
+#ifdef __cplusplus
+} /* extern "C" */
+#endif
+
+#endif /* LV_DRAW_BUF_CONVERT_H */

+ 132 - 0
components/easylvgl/lvgl9/src/draw/convert/neon/lv_draw_buf_convert_neon.c

@@ -0,0 +1,132 @@
+/**
+ * @file lv_draw_buf_convert_neon.c
+ *
+ */
+
+/*********************
+ *      INCLUDES
+ *********************/
+#include "../../../lv_conf_internal.h"
+
+#if LV_USE_DRAW_SW_ASM == LV_DRAW_SW_ASM_NEON
+
+#include <arm_neon.h>
+#include "lv_draw_buf_convert_neon.h"
+
+/*********************
+ *      DEFINES
+ *********************/
+
+/**********************
+ *  STATIC PROTOTYPES
+ **********************/
+
+/**********************
+ *   STATIC FUNCTIONS
+ **********************/
+
+/**********************
+ *      MACROS
+ **********************/
+
+/**********************
+ *   GLOBAL FUNCTIONS
+ **********************/
+
+lv_result_t _lv_draw_buf_convert_premultiply_indexed_neon(lv_draw_buf_t * buf)
+{
+    lv_draw_buf_t palette_draw_buf;
+
+    LV_ASSERT_NULL(buf);
+
+    if(!LV_COLOR_FORMAT_IS_INDEXED(buf->header.cf)) {
+        LV_LOG_WARN("Unsupported color format : %d", buf->header.cf);
+        return LV_RESULT_INVALID;
+    }
+
+    lv_memcpy(&palette_draw_buf, buf, sizeof(lv_draw_buf_t));
+
+    palette_draw_buf.header.w = LV_COLOR_INDEXED_PALETTE_SIZE(buf->header.cf);
+    palette_draw_buf.header.h = 1;
+    palette_draw_buf.header.cf = LV_COLOR_FORMAT_ARGB8888;
+    palette_draw_buf.header.stride = 4 * palette_draw_buf.header.w;
+
+    return _lv_draw_buf_convert_premultiply_argb8888_neon(&palette_draw_buf);
+
+}
+
+lv_result_t _lv_draw_buf_convert_premultiply_argb8888_neon(lv_draw_buf_t * buf)
+{
+    LV_ASSERT_NULL(buf);
+
+    uint32_t h = buf->header.h;
+    uint32_t w = buf->header.w;
+    uint32_t stride = buf->header.stride;
+    uint8_t * data = (uint8_t *)buf->data;
+
+    if(buf->header.cf != LV_COLOR_FORMAT_ARGB8888) {
+        LV_LOG_WARN("Unsupported color format : %d", buf->header.cf);
+        return LV_RESULT_INVALID;
+    }
+
+    for(uint32_t y = 0; y < h; y++) {
+        uint8_t * p = (uint8_t *)data;
+        uint32_t remaining_pixels = w;
+
+        while(remaining_pixels >= 8) {
+            uint8x8x4_t rgba = vld4_u8(p);
+
+            uint16x8_t r16 = vmovl_u8(rgba.val[0]);
+            uint16x8_t g16 = vmovl_u8(rgba.val[1]);
+            uint16x8_t b16 = vmovl_u8(rgba.val[2]);
+            uint16x8_t a16 = vmovl_u8(rgba.val[3]);
+
+            rgba.val[0] = vshrn_n_u16(vmulq_u16(r16, a16), 8);
+            rgba.val[1] = vshrn_n_u16(vmulq_u16(g16, a16), 8);
+            rgba.val[2] = vshrn_n_u16(vmulq_u16(b16, a16), 8);
+
+            vst4_u8(p, rgba);
+
+            p += 8 * 4;
+            remaining_pixels -= 8;
+        }
+
+        if(remaining_pixels >= 4) {
+            uint8x8x4_t rgba;
+            rgba = vld4_lane_u8(p, rgba, 0);
+            rgba = vld4_lane_u8(p + 4, rgba, 1);
+            rgba = vld4_lane_u8(p + 8, rgba, 2);
+            rgba = vld4_lane_u8(p + 12, rgba, 3);
+
+            uint16x8_t r16 = vmovl_u8(rgba.val[0]);
+            uint16x8_t g16 = vmovl_u8(rgba.val[1]);
+            uint16x8_t b16 = vmovl_u8(rgba.val[2]);
+            uint16x8_t a16 = vmovl_u8(rgba.val[3]);
+
+            rgba.val[0] = vshrn_n_u16(vmulq_u16(r16, a16), 8);
+            rgba.val[1] = vshrn_n_u16(vmulq_u16(g16, a16), 8);
+            rgba.val[2] = vshrn_n_u16(vmulq_u16(b16, a16), 8);
+
+            vst4_lane_u8(p, rgba, 0);
+            vst4_lane_u8(p + 4, rgba, 1);
+            vst4_lane_u8(p + 8, rgba, 2);
+            vst4_lane_u8(p + 12, rgba, 3);
+
+            p += 4 * 4;
+            remaining_pixels -= 4;
+        }
+
+        while(remaining_pixels--) {
+            uint8_t a = p[3];
+            p[0] = ((uint16_t)(p[0]) * a) >> 8;
+            p[1] = ((uint16_t)(p[1]) * a) >> 8;
+            p[2] = ((uint16_t)(p[2]) * a) >> 8;
+            p += 4;
+        }
+
+        data += stride;
+    }
+
+    return LV_RESULT_OK;
+}
+#endif /* LV_USE_DRAW_SW_ASM == LV_DRAW_SW_ASM_NEON */

+ 58 - 0
components/easylvgl/lvgl9/src/draw/convert/neon/lv_draw_buf_convert_neon.h

@@ -0,0 +1,58 @@
+/**
+ * @file lv_draw_buf_convert_neon.h
+ *
+ */
+
+#ifndef LV_DRAW_BUF_CONVERT_NEON_H
+#define LV_DRAW_BUF_CONVERT_NEON_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include "../../../misc/lv_color.h"
+#include "../../lv_draw_buf.h"
+
+#if LV_USE_DRAW_SW_ASM == LV_DRAW_SW_ASM_NEON
+
+/*********************
+ *      DEFINES
+ *********************/
+
+/**********************
+ *      TYPEDEFS
+ **********************/
+
+#ifndef LV_DRAW_CONVERT_PREMULTIPLY_INDEXED
+#define LV_DRAW_CONVERT_PREMULTIPLY_INDEXED(buf) \
+    _lv_draw_buf_convert_premultiply_indexed_neon(buf)
+#endif
+
+#ifndef LV_DRAW_CONVERT_PREMULTIPLY_ARGB8888
+#define LV_DRAW_CONVERT_PREMULTIPLY_ARGB8888(buf) \
+    _lv_draw_buf_convert_premultiply_argb8888_neon(buf)
+#endif
+
+/**********************
+ * GLOBAL PROTOTYPES
+ **********************/
+
+/**
+ * Convert indexed draw_buf to premultiplied format with neon specific optimizations
+ * @param buf     pointer to a draw buf
+ */
+
+lv_result_t _lv_draw_buf_convert_premultiply_indexed_neon(lv_draw_buf_t * buf);
+
+/**
+ * Convert argb8888 draw_buf to premultiplied format with neon specific optimizations
+ * @param buf     pointer to a draw buf
+ */
+lv_result_t _lv_draw_buf_convert_premultiply_argb8888_neon(lv_draw_buf_t * buf);
+#endif /*LV_USE_DRAW_SW_ASM == LV_DRAW_SW_ASM_NEON*/
+
+#ifdef __cplusplus
+} /* extern "C" */
+#endif
+
+#endif /* LV_DRAW_BUF_CONVERT_NEON_H */

+ 394 - 0
components/easylvgl/lvgl9/src/draw/dma2d/lv_draw_dma2d.c

@@ -0,0 +1,394 @@
+/**
+ * @file lv_draw_dma2d.c
+ *
+ */
+
+/*********************
+ *      INCLUDES
+ *********************/
+
+#include "lv_draw_dma2d_private.h"
+#if LV_USE_DRAW_DMA2D
+
+#include "../sw/lv_draw_sw.h"
+#include "../../misc/lv_area_private.h"
+
+#if !LV_DRAW_DMA2D_ASYNC && LV_USE_DRAW_DMA2D_INTERRUPT
+    #warning LV_USE_DRAW_DMA2D_INTERRUPT is 1 but has no effect because LV_USE_OS is LV_OS_NONE
+#endif
+
+/*********************
+ *      DEFINES
+ *********************/
+
+#define DRAW_UNIT_ID_DMA2D 5
+
+/**********************
+ *      TYPEDEFS
+ **********************/
+
+/**********************
+ *  STATIC PROTOTYPES
+ **********************/
+
+static int32_t evaluate_cb(lv_draw_unit_t * draw_unit, lv_draw_task_t * task);
+static int32_t dispatch_cb(lv_draw_unit_t * draw_unit, lv_layer_t * layer);
+static int32_t delete_cb(lv_draw_unit_t * draw_unit);
+#if LV_DRAW_DMA2D_ASYNC
+    static int32_t wait_finish_cb(lv_draw_unit_t * u);
+#endif
+#if !LV_DRAW_DMA2D_ASYNC
+    static bool check_transfer_completion(void);
+#endif
+static void post_transfer_tasks(lv_draw_dma2d_unit_t * u);
+
+/**********************
+ *  STATIC VARIABLES
+ **********************/
+
+#if LV_DRAW_DMA2D_ASYNC
+    static lv_draw_dma2d_unit_t * g_unit;
+#endif
+
+/**********************
+ *      MACROS
+ **********************/
+
+/**********************
+ *   GLOBAL FUNCTIONS
+ **********************/
+
+void lv_draw_dma2d_init(void)
+{
+    lv_draw_dma2d_unit_t * draw_dma2d_unit = lv_draw_create_unit(sizeof(lv_draw_dma2d_unit_t));
+    draw_dma2d_unit->base_unit.evaluate_cb = evaluate_cb;
+    draw_dma2d_unit->base_unit.dispatch_cb = dispatch_cb;
+    draw_dma2d_unit->base_unit.delete_cb = delete_cb;
+#if LV_DRAW_DMA2D_ASYNC
+    draw_dma2d_unit->base_unit.wait_for_finish_cb = wait_finish_cb;
+#endif
+    draw_dma2d_unit->base_unit.name = "DMA2D";
+
+#if LV_DRAW_DMA2D_ASYNC
+    g_unit = draw_dma2d_unit;
+    lv_thread_sync_init(&draw_dma2d_unit->interrupt_signal);
+#endif
+
+    /* enable the DMA2D clock */
+#if defined(STM32F4) || defined(STM32F7) || defined(STM32U5) || defined(STM32L4)
+    RCC->AHB1ENR |= RCC_AHB1ENR_DMA2DEN;
+#elif defined(STM32H7)
+    RCC->AHB3ENR |= RCC_AHB3ENR_DMA2DEN;
+#elif defined(STM32H7RS) || defined(STM32N6)
+    RCC->AHB5ENR |= RCC_AHB5ENR_DMA2DEN;
+#else
+#warning "LVGL can't enable the clock for DMA2D"
+#endif
+
+    /* disable dead time */
+    DMA2D->AMTCR = 0;
+
+    /* enable the interrupt */
+    NVIC_EnableIRQ(DMA2D_IRQn);
+}
+
+void lv_draw_dma2d_deinit(void)
+{
+    /* disable the interrupt */
+    NVIC_DisableIRQ(DMA2D_IRQn);
+
+    /* disable the DMA2D clock */
+#if defined(STM32F4) || defined(STM32F7) || defined(STM32U5) || defined(STM32L4)
+    RCC->AHB1ENR &= ~RCC_AHB1ENR_DMA2DEN;
+#elif defined(STM32H7)
+    RCC->AHB3ENR &= ~RCC_AHB3ENR_DMA2DEN;
+#elif defined(STM32H7RS) || defined(STM32N6)
+    RCC->AHB5ENR &= ~RCC_AHB5ENR_DMA2DEN;
+#endif
+
+#if LV_DRAW_DMA2D_ASYNC
+    lv_result_t res = lv_thread_sync_delete(&g_unit->interrupt_signal);
+    LV_ASSERT(res == LV_RESULT_OK);
+
+    g_unit = NULL;
+#endif
+}
+
+#if LV_USE_DRAW_DMA2D_INTERRUPT
+void lv_draw_dma2d_transfer_complete_interrupt_handler(void)
+{
+#if LV_DRAW_DMA2D_ASYNC
+    lv_thread_sync_signal_isr(&g_unit->interrupt_signal);
+#endif
+}
+#endif
+
+lv_draw_dma2d_output_cf_t lv_draw_dma2d_cf_to_dma2d_output_cf(lv_color_format_t cf)
+{
+    switch(cf) {
+        case LV_COLOR_FORMAT_ARGB8888:
+        case LV_COLOR_FORMAT_XRGB8888:
+            return LV_DRAW_DMA2D_OUTPUT_CF_ARGB8888;
+        case LV_COLOR_FORMAT_RGB888:
+            return LV_DRAW_DMA2D_OUTPUT_CF_RGB888;
+        case LV_COLOR_FORMAT_RGB565:
+            return LV_DRAW_DMA2D_OUTPUT_CF_RGB565;
+        case LV_COLOR_FORMAT_ARGB1555:
+            return LV_DRAW_DMA2D_OUTPUT_CF_ARGB1555;
+        default:
+            LV_ASSERT_MSG(false, "unsupported output color format");
+    }
+    return LV_DRAW_DMA2D_OUTPUT_CF_RGB565;
+}
+
+uint32_t lv_draw_dma2d_color_to_dma2d_color(lv_draw_dma2d_output_cf_t cf, lv_color_t color)
+{
+    switch(cf) {
+        case LV_DRAW_DMA2D_OUTPUT_CF_ARGB8888:
+        case LV_DRAW_DMA2D_OUTPUT_CF_RGB888:
+            return lv_color_to_u32(color);
+        case LV_DRAW_DMA2D_OUTPUT_CF_RGB565:
+            return lv_color_to_u16(color);
+        default:
+            LV_ASSERT_MSG(false, "unsupported output color format");
+    }
+    return 0;
+}
+
+void lv_draw_dma2d_configure_and_start_transfer(const lv_draw_dma2d_configuration_t * conf)
+{
+    /* number of lines register */
+    DMA2D->NLR = (conf->w << DMA2D_NLR_PL_Pos) | (conf->h << DMA2D_NLR_NL_Pos);
+
+    /* output */
+
+    /* output memory address register */
+    DMA2D->OMAR = (uint32_t)(uintptr_t) conf->output_address;
+    /* output offset register */
+    DMA2D->OOR = conf->output_offset;
+    /* output pixel format converter control register */
+    DMA2D->OPFCCR = ((uint32_t) conf->output_cf) << DMA2D_OPFCCR_CM_Pos;
+
+    /* Fill color. Only for mode LV_DRAW_DMA2D_MODE_REGISTER_TO_MEMORY */
+    DMA2D->OCOLR = conf->reg_to_mem_mode_color;
+
+    /* foreground */
+
+    /* foreground memory address register */
+    DMA2D->FGMAR = (uint32_t)(uintptr_t) conf->fg_address;
+    /* foreground offset register */
+    DMA2D->FGOR = conf->fg_offset;
+    /* foreground color. only for mem-to-mem with blending and fixed-color foreground */
+    DMA2D->FGCOLR = conf->fg_color;
+    /* foreground pixel format converter control register */
+    DMA2D->FGPFCCR = (((uint32_t) conf->fg_cf) << DMA2D_FGPFCCR_CM_Pos)
+                     | (conf->fg_alpha << DMA2D_FGPFCCR_ALPHA_Pos)
+                     | (conf->fg_alpha_mode << DMA2D_FGPFCCR_AM_Pos);
+
+    /* background */
+
+    DMA2D->BGMAR = (uint32_t)(uintptr_t) conf->bg_address;
+    DMA2D->BGOR = conf->bg_offset;
+    DMA2D->BGCOLR = conf->bg_color;
+    DMA2D->BGPFCCR = (((uint32_t) conf->bg_cf) << DMA2D_BGPFCCR_CM_Pos)
+                     | (conf->bg_alpha << DMA2D_BGPFCCR_ALPHA_Pos)
+                     | (conf->bg_alpha_mode << DMA2D_BGPFCCR_AM_Pos);
+
+    /* ensure the DMA2D register values are observed before the start transfer bit is set */
+    __DSB();
+
+    /* start the transfer (also set mode and enable transfer complete interrupt) */
+    DMA2D->CR = DMA2D_CR_START | (((uint32_t) conf->mode) << DMA2D_CR_MODE_Pos)
+#if LV_USE_DRAW_DMA2D_INTERRUPT
+                | DMA2D_CR_TCIE
+#endif
+                ;
+}
+
+#if LV_DRAW_DMA2D_CACHE
+void lv_draw_dma2d_invalidate_cache(const lv_draw_dma2d_cache_area_t * mem_area)
+{
+    if(SCB->CCR & SCB_CCR_DC_Msk) {
+        SCB_InvalidateDCache();
+    }
+}
+
+void lv_draw_dma2d_clean_cache(const lv_draw_dma2d_cache_area_t * mem_area)
+{
+    if(SCB->CCR & SCB_CCR_DC_Msk) {
+        SCB_CleanDCache();
+    }
+}
+#endif
+
+/**********************
+ *   STATIC FUNCTIONS
+ **********************/
+
+static int32_t evaluate_cb(lv_draw_unit_t * draw_unit, lv_draw_task_t * task)
+{
+    switch(task->type) {
+        case LV_DRAW_TASK_TYPE_FILL: {
+                lv_draw_fill_dsc_t * dsc = task->draw_dsc;
+                if(!(dsc->radius == 0
+                     && dsc->grad.dir == LV_GRAD_DIR_NONE
+                     && (dsc->base.layer->color_format == LV_COLOR_FORMAT_ARGB8888
+                         || dsc->base.layer->color_format == LV_COLOR_FORMAT_XRGB8888
+                         || dsc->base.layer->color_format == LV_COLOR_FORMAT_RGB888
+                         || dsc->base.layer->color_format == LV_COLOR_FORMAT_RGB565))) {
+                    return 0;
+                }
+            }
+            break;
+        case LV_DRAW_TASK_TYPE_IMAGE: {
+                lv_draw_image_dsc_t * dsc = task->draw_dsc;
+                if(!(dsc->header.cf < LV_COLOR_FORMAT_PROPRIETARY_START
+                     && dsc->clip_radius == 0
+                     && dsc->bitmap_mask_src == NULL
+                     && dsc->sup == NULL
+                     && dsc->tile == 0
+                     && dsc->blend_mode == LV_BLEND_MODE_NORMAL
+                     && dsc->recolor_opa <= LV_OPA_MIN
+                     && dsc->skew_y == 0
+                     && dsc->skew_x == 0
+                     && dsc->scale_x == 256
+                     && dsc->scale_y == 256
+                     && dsc->rotation == 0
+                     && lv_image_src_get_type(dsc->src) == LV_IMAGE_SRC_VARIABLE
+                     && (dsc->header.cf == LV_COLOR_FORMAT_ARGB8888
+                         || dsc->header.cf == LV_COLOR_FORMAT_XRGB8888
+                         || dsc->header.cf == LV_COLOR_FORMAT_RGB888
+                         || dsc->header.cf == LV_COLOR_FORMAT_RGB565
+                         || dsc->header.cf == LV_COLOR_FORMAT_ARGB1555)
+                     && (dsc->base.layer->color_format == LV_COLOR_FORMAT_ARGB8888
+                         || dsc->base.layer->color_format == LV_COLOR_FORMAT_XRGB8888
+                         || dsc->base.layer->color_format == LV_COLOR_FORMAT_RGB888
+                         || dsc->base.layer->color_format == LV_COLOR_FORMAT_RGB565))) {
+                    return 0;
+                }
+            }
+            break;
+        default:
+            return 0;
+    }
+
+    task->preferred_draw_unit_id = DRAW_UNIT_ID_DMA2D;
+    task->preference_score = 0;
+
+    return 0;
+}
+
+static int32_t dispatch_cb(lv_draw_unit_t * draw_unit, lv_layer_t * layer)
+{
+    lv_draw_dma2d_unit_t * draw_dma2d_unit = (lv_draw_dma2d_unit_t *) draw_unit;
+
+    if(draw_dma2d_unit->task_act) {
+#if LV_DRAW_DMA2D_ASYNC
+        /*Return immediately if it's busy with draw task*/
+        return LV_DRAW_UNIT_IDLE;
+#else
+        if(!check_transfer_completion()) {
+            return LV_DRAW_UNIT_IDLE;
+        }
+        post_transfer_tasks(draw_dma2d_unit);
+#endif
+    }
+
+    lv_draw_task_t * t = lv_draw_get_available_task(layer, NULL, DRAW_UNIT_ID_DMA2D);
+    if(t == NULL) {
+        return LV_DRAW_UNIT_IDLE;
+    }
+
+    void * buf = lv_draw_layer_alloc_buf(layer);
+    if(buf == NULL) {
+        return LV_DRAW_UNIT_IDLE;
+    }
+
+    t->state = LV_DRAW_TASK_STATE_IN_PROGRESS;
+    t->draw_unit = draw_unit;
+    draw_dma2d_unit->task_act = t;
+
+    if(t->type == LV_DRAW_TASK_TYPE_FILL) {
+        lv_draw_fill_dsc_t * dsc = t->draw_dsc;
+        const lv_area_t * coords = &t->area;
+        lv_area_t clipped_coords;
+        if(!lv_area_intersect(&clipped_coords, coords, &t->clip_area)) {
+            return LV_DRAW_UNIT_IDLE;
+        }
+
+        void * dest = lv_draw_layer_go_to_xy(layer,
+                                             clipped_coords.x1 - layer->buf_area.x1,
+                                             clipped_coords.y1 - layer->buf_area.y1);
+
+        if(dsc->opa >= LV_OPA_MAX) {
+            lv_draw_dma2d_opaque_fill(t,
+                                      dest,
+                                      lv_area_get_width(&clipped_coords),
+                                      lv_area_get_height(&clipped_coords),
+                                      lv_draw_buf_width_to_stride(lv_area_get_width(&layer->buf_area), dsc->base.layer->color_format));
+        }
+        else {
+            lv_draw_dma2d_fill(t,
+                               dest,
+                               lv_area_get_width(&clipped_coords),
+                               lv_area_get_height(&clipped_coords),
+                               lv_draw_buf_width_to_stride(lv_area_get_width(&layer->buf_area), dsc->base.layer->color_format));
+        }
+    }
+    else if(t->type == LV_DRAW_TASK_TYPE_IMAGE) {
+        lv_draw_image_dsc_t * dsc = t->draw_dsc;
+        const lv_area_t * coords = &t->area;
+        lv_area_t clipped_coords;
+        if(!lv_area_intersect(&clipped_coords, coords, &t->clip_area)) {
+            return LV_DRAW_UNIT_IDLE;
+        }
+
+        if(dsc->opa >= LV_OPA_MAX) {
+            lv_draw_dma2d_opaque_image(t, dsc, &t->area);
+        }
+        else {
+            lv_draw_dma2d_image(t, dsc, &t->area);
+        }
+    }
+
+    lv_draw_dispatch_request();
+
+    return 1;
+}
+
+static int32_t delete_cb(lv_draw_unit_t * draw_unit)
+{
+    return 0;
+}
+
+#if LV_DRAW_DMA2D_ASYNC
+static int32_t wait_finish_cb(lv_draw_unit_t * draw_unit)
+{
+    lv_draw_dma2d_unit_t * u = (lv_draw_dma2d_unit_t *) draw_unit;
+
+    /* If a DMA2D task has been dispatched, wait its interrupt */
+    lv_thread_sync_wait(&u->interrupt_signal);
+
+    /* Then cleanup the DMA2D draw unit to accept a new task */
+    post_transfer_tasks(u);
+    return 0;
+}
+#endif /*LV_DRAW_DMA2D_ASYNC*/
+
+#if !LV_DRAW_DMA2D_ASYNC
+static bool check_transfer_completion(void)
+{
+    return !(DMA2D->CR & DMA2D_CR_START);
+}
+#endif
+
+static void post_transfer_tasks(lv_draw_dma2d_unit_t * u)
+{
+#if LV_DRAW_DMA2D_CACHE
+    lv_draw_dma2d_invalidate_cache(&u->writing_area);
+#endif
+    u->task_act->state = LV_DRAW_TASK_STATE_FINISHED;
+    u->task_act = NULL;
+}
+
+#endif /*LV_USE_DRAW_DMA2D*/

+ 49 - 0
components/easylvgl/lvgl9/src/draw/dma2d/lv_draw_dma2d.h

@@ -0,0 +1,49 @@
+/**
+ * @file lv_draw_dma2d.h
+ *
+ */
+
+#ifndef LV_DRAW_DMA2D_H
+#define LV_DRAW_DMA2D_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/*********************
+ *      INCLUDES
+ *********************/
+
+#include "../../lv_conf_internal.h"
+#if LV_USE_DRAW_DMA2D
+
+/*********************
+ *      DEFINES
+ *********************/
+
+/**********************
+ *      TYPEDEFS
+ **********************/
+
+/**********************
+ * GLOBAL PROTOTYPES
+ **********************/
+
+void lv_draw_dma2d_init(void);
+void lv_draw_dma2d_deinit(void);
+
+#if LV_USE_DRAW_DMA2D_INTERRUPT
+void lv_draw_dma2d_transfer_complete_interrupt_handler(void);
+#endif
+
+/**********************
+ *      MACROS
+ **********************/
+
+#endif /*LV_USE_DRAW_DMA2D*/
+
+#ifdef __cplusplus
+} /*extern "C"*/
+#endif
+
+#endif /*LV_DRAW_DMA2D_H*/

+ 133 - 0
components/easylvgl/lvgl9/src/draw/dma2d/lv_draw_dma2d_fill.c

@@ -0,0 +1,133 @@
+/**
+ * @file lv_draw_dma2d_fill.c
+ *
+ */
+
+/*********************
+ *      INCLUDES
+ *********************/
+
+#include "lv_draw_dma2d_private.h"
+#if LV_USE_DRAW_DMA2D
+
+/*********************
+ *      DEFINES
+ *********************/
+
+/**********************
+ *      TYPEDEFS
+ **********************/
+
+/**********************
+ *  STATIC PROTOTYPES
+ **********************/
+
+/**********************
+ *  STATIC VARIABLES
+ **********************/
+
+/**********************
+ *      MACROS
+ **********************/
+
+/**********************
+ *   GLOBAL FUNCTIONS
+ **********************/
+
+void lv_draw_dma2d_opaque_fill(lv_draw_task_t * t, void * first_pixel, int32_t w, int32_t h, int32_t stride)
+{
+    lv_draw_fill_dsc_t * dsc = t->draw_dsc;
+    lv_color_format_t cf = dsc->base.layer->color_format;
+
+    lv_draw_dma2d_output_cf_t output_cf = lv_draw_dma2d_cf_to_dma2d_output_cf(cf);
+    uint32_t cf_size = LV_COLOR_FORMAT_GET_SIZE(cf);
+    uint32_t reg_to_mem_color = lv_draw_dma2d_color_to_dma2d_color(output_cf, dsc->color);
+
+#if LV_DRAW_DMA2D_CACHE
+    lv_draw_dma2d_cache_area_t cache_area = {
+        .first_byte = first_pixel,
+        .width_bytes = w * cf_size,
+        .height = h,
+        .stride = stride
+    };
+    lv_draw_dma2d_unit_t * u = (lv_draw_dma2d_unit_t *) t->draw_unit;
+    lv_memcpy(&u->writing_area, &cache_area, sizeof(lv_draw_dma2d_cache_area_t));
+#endif
+
+    lv_draw_dma2d_configuration_t conf = {
+        .mode = LV_DRAW_DMA2D_MODE_REGISTER_TO_MEMORY,
+        .w = w,
+        .h = h,
+
+        .output_address = first_pixel,
+        .output_offset = (stride / cf_size) - w,
+        .output_cf = output_cf,
+
+        .reg_to_mem_mode_color = reg_to_mem_color
+    };
+    lv_draw_dma2d_configure_and_start_transfer(&conf);
+}
+
+void lv_draw_dma2d_fill(lv_draw_task_t * t, void * first_pixel, int32_t w, int32_t h, int32_t stride)
+{
+    lv_draw_fill_dsc_t * dsc = t->draw_dsc;
+    lv_color_t color = dsc->color;
+    lv_color_format_t cf = dsc->base.layer->color_format;
+    lv_opa_t opa = dsc->opa;
+
+    lv_draw_dma2d_output_cf_t output_cf = lv_draw_dma2d_cf_to_dma2d_output_cf(cf);
+    uint32_t cf_size = LV_COLOR_FORMAT_GET_SIZE(cf);
+
+#if LV_DRAW_DMA2D_CACHE
+    lv_draw_dma2d_cache_area_t cache_area = {
+        .first_byte = first_pixel,
+        .width_bytes = w * cf_size,
+        .height = h,
+        .stride = stride
+    };
+    lv_draw_dma2d_unit_t * u = (lv_draw_dma2d_unit_t *) t->draw_unit;
+    lv_memcpy(&u->writing_area, &cache_area, sizeof(lv_draw_dma2d_cache_area_t));
+
+    /* make sure the background area DMA2D is blending is up-to-date in main memory */
+    lv_draw_dma2d_clean_cache(&cache_area);
+#endif
+
+    uint32_t output_offset = (stride / cf_size) - w;
+
+    lv_draw_dma2d_configuration_t conf = {
+        .mode = LV_DRAW_DMA2D_MODE_MEMORY_TO_MEMORY_WITH_BLENDING,
+        .w = w,
+        .h = h,
+
+        .output_address = first_pixel,
+        .output_offset = output_offset,
+        .output_cf = output_cf,
+
+        .fg_color = lv_color_to_u32(color),
+        .fg_address = first_pixel,
+        .fg_offset = output_offset,
+        .fg_alpha_mode = LV_DRAW_DMA2D_ALPHA_MODE_REPLACE_ALPHA_CHANNEL,
+        .fg_alpha = opa,
+        .fg_cf = LV_DRAW_DMA2D_FGBG_CF_A8,
+
+        .bg_address = first_pixel,
+        .bg_offset = output_offset,
+        .bg_alpha_mode = LV_DRAW_DMA2D_ALPHA_MODE_NO_MODIFY_IMAGE_ALPHA_CHANNEL,
+        .bg_alpha = opa,
+        .bg_cf = (lv_draw_dma2d_fgbg_cf_t) output_cf
+    };
+
+    /* Background alpha channel should be treated as 0xFF if the cf is XRGB */
+    if(cf == LV_COLOR_FORMAT_XRGB8888) {
+        conf.bg_alpha_mode = LV_DRAW_DMA2D_ALPHA_MODE_REPLACE_ALPHA_CHANNEL;
+        conf.bg_alpha = 0xff;
+    }
+
+    lv_draw_dma2d_configure_and_start_transfer(&conf);
+}
+
+/**********************
+ *   STATIC FUNCTIONS
+ **********************/
+
+#endif /*LV_USE_DRAW_DMA2D*/

+ 262 - 0
components/easylvgl/lvgl9/src/draw/dma2d/lv_draw_dma2d_img.c

@@ -0,0 +1,262 @@
+/**
+ * @file lv_draw_dma2d_img.c
+ *
+ */
+
+/*********************
+ *      INCLUDES
+ *********************/
+
+#include "lv_draw_dma2d_private.h"
+#if LV_USE_DRAW_DMA2D
+
+#include "../lv_draw_image_private.h"
+#include "../lv_image_decoder_private.h"
+#include "../../misc/lv_area_private.h"
+
+/*********************
+ *      DEFINES
+ *********************/
+
+/**********************
+ *      TYPEDEFS
+ **********************/
+
+/**********************
+ *  STATIC PROTOTYPES
+ **********************/
+
+static void lv_draw_dma2d_opaque_image_core(lv_draw_task_t * t, const lv_draw_image_dsc_t * draw_dsc,
+                                            const lv_image_decoder_dsc_t * decoder_dsc, lv_draw_image_sup_t * sup,
+                                            const lv_area_t * img_coords, const lv_area_t * clipped_img_area);
+
+static void lv_draw_dma2d_image_core(lv_draw_task_t * t, const lv_draw_image_dsc_t * draw_dsc,
+                                     const lv_image_decoder_dsc_t * decoder_dsc, lv_draw_image_sup_t * sup,
+                                     const lv_area_t * img_coords, const lv_area_t * clipped_img_area);
+
+/**********************
+ *  STATIC VARIABLES
+ **********************/
+
+/**********************
+ *      MACROS
+ **********************/
+
+/**********************
+ *   GLOBAL FUNCTIONS
+ **********************/
+
+void lv_draw_dma2d_opaque_image(lv_draw_task_t * t, const lv_draw_image_dsc_t * draw_dsc,
+                                const lv_area_t * coords)
+{
+    if(!draw_dsc->tile) {
+        lv_draw_image_normal_helper(t, draw_dsc, coords, lv_draw_dma2d_opaque_image_core);
+    }
+    else {
+        lv_draw_image_tiled_helper(t, draw_dsc, coords, lv_draw_dma2d_opaque_image_core);
+    }
+}
+
+void lv_draw_dma2d_image(lv_draw_task_t * t, const lv_draw_image_dsc_t * draw_dsc,
+                         const lv_area_t * coords)
+{
+    if(!draw_dsc->tile) {
+        lv_draw_image_normal_helper(t, draw_dsc, coords, lv_draw_dma2d_image_core);
+    }
+    else {
+        lv_draw_image_tiled_helper(t, draw_dsc, coords, lv_draw_dma2d_image_core);
+    }
+}
+
+/**********************
+ *   STATIC FUNCTIONS
+ **********************/
+
+static void lv_draw_dma2d_opaque_image_core(lv_draw_task_t * t, const lv_draw_image_dsc_t * draw_dsc,
+                                            const lv_image_decoder_dsc_t * decoder_dsc, lv_draw_image_sup_t * sup,
+                                            const lv_area_t * img_coords, const lv_area_t * clipped_img_area)
+{
+    LV_UNUSED(sup);
+    LV_UNUSED(img_coords);
+
+    lv_layer_t * layer = t->target_layer;
+
+    void * dest_first_pixel = lv_draw_layer_go_to_xy(layer,
+                                                     clipped_img_area->x1 - layer->buf_area.x1,
+                                                     clipped_img_area->y1 - layer->buf_area.y1);
+    int32_t dest_stride = lv_draw_buf_width_to_stride(lv_area_get_width(&layer->buf_area), layer->color_format);
+
+    int32_t w = lv_area_get_width(clipped_img_area);
+    int32_t h = lv_area_get_height(clipped_img_area);
+
+    lv_color_format_t output_cf = layer->color_format;
+    uint32_t output_cf_size = lv_color_format_get_size(output_cf);
+    lv_draw_dma2d_output_cf_t output_cf_dma2d = lv_draw_dma2d_cf_to_dma2d_output_cf(output_cf);
+
+    const lv_draw_buf_t * decoded = decoder_dsc->decoded;
+    const uint8_t * src_buf = decoded->data;
+    uint32_t image_stride = decoded->header.stride;
+    lv_color_format_t image_cf = decoded->header.cf;
+    lv_draw_dma2d_fgbg_cf_t image_cf_dma2d = (lv_draw_dma2d_fgbg_cf_t) lv_draw_dma2d_cf_to_dma2d_output_cf(image_cf);
+    uint32_t image_cf_size = LV_COLOR_FORMAT_GET_SIZE(image_cf);
+    if(image_stride == 0) image_stride = image_cf_size * decoded->header.w;
+
+#if LV_DRAW_DMA2D_CACHE
+    lv_draw_dma2d_cache_area_t dest_area = {
+        .first_byte = dest_first_pixel,
+        .width_bytes = w * output_cf_size,
+        .height = h,
+        .stride = dest_stride
+    };
+    lv_draw_dma2d_unit_t * u = (lv_draw_dma2d_unit_t *) t->draw_unit;
+    lv_memcpy(&u->writing_area, &dest_area, sizeof(lv_draw_dma2d_cache_area_t));
+    if(lv_color_format_has_alpha(image_cf)) {
+        /* make sure the background area DMA2D is blending is up-to-date in main memory */
+        lv_draw_dma2d_clean_cache(&dest_area);
+    }
+#endif
+
+    const void * image_first_byte = src_buf
+                                    + (image_stride * (clipped_img_area->y1 - draw_dsc->image_area.y1))
+                                    + (image_cf_size * (clipped_img_area->x1 - draw_dsc->image_area.x1));
+
+#if LV_DRAW_DMA2D_CACHE
+    lv_draw_dma2d_cache_area_t src_area = {
+        .first_byte = image_first_byte,
+        .width_bytes = w * image_cf_size,
+        .height = h,
+        .stride = image_stride
+    };
+    /* make sure the image area is up-to-date in main memory for DMA2D */
+    lv_draw_dma2d_clean_cache(&src_area);
+#endif
+
+    uint32_t output_offset = (dest_stride / output_cf_size) - w;
+    lv_draw_dma2d_configuration_t conf = {
+        .mode = LV_DRAW_DMA2D_MODE_MEMORY_TO_MEMORY_WITH_PFC,
+        .w = w,
+        .h = h,
+
+        .output_address = dest_first_pixel,
+        .output_offset = output_offset,
+        .output_cf = output_cf_dma2d,
+
+        .fg_address = image_first_byte,
+        .fg_offset = (image_stride / image_cf_size) - w,
+        .fg_cf = image_cf_dma2d
+    };
+
+    /* only process the background if the image might be transparent */
+    if(lv_color_format_has_alpha(image_cf)) {
+        conf.mode = LV_DRAW_DMA2D_MODE_MEMORY_TO_MEMORY_WITH_BLENDING;
+
+        conf.bg_address = dest_first_pixel;
+        conf.bg_offset = output_offset;
+        conf.bg_cf = output_cf_dma2d;
+    }
+
+    /* Alpha channel should be treated as 0xFF if the cf is XRGB */
+    if(image_cf == LV_COLOR_FORMAT_XRGB8888) {
+        conf.fg_alpha_mode = LV_DRAW_DMA2D_ALPHA_MODE_REPLACE_ALPHA_CHANNEL;
+        conf.fg_alpha = 0xff;
+    }
+    if(output_cf == LV_COLOR_FORMAT_XRGB8888) {
+        conf.bg_alpha_mode = LV_DRAW_DMA2D_ALPHA_MODE_REPLACE_ALPHA_CHANNEL;
+        conf.bg_alpha = 0xff;
+    }
+
+    lv_draw_dma2d_configure_and_start_transfer(&conf);
+}
+
+static void lv_draw_dma2d_image_core(lv_draw_task_t * t, const lv_draw_image_dsc_t * draw_dsc,
+                                     const lv_image_decoder_dsc_t * decoder_dsc, lv_draw_image_sup_t * sup,
+                                     const lv_area_t * img_coords, const lv_area_t * clipped_img_area)
+{
+    LV_UNUSED(sup);
+    LV_UNUSED(img_coords);
+
+    lv_layer_t * layer = t->target_layer;
+
+    void * dest_first_pixel = lv_draw_layer_go_to_xy(layer,
+                                                     clipped_img_area->x1 - layer->buf_area.x1,
+                                                     clipped_img_area->y1 - layer->buf_area.y1);
+    int32_t dest_stride = lv_draw_buf_width_to_stride(lv_area_get_width(&layer->buf_area), layer->color_format);
+
+    int32_t w = lv_area_get_width(clipped_img_area);
+    int32_t h = lv_area_get_height(clipped_img_area);
+
+    lv_color_format_t output_cf = layer->color_format;
+    uint32_t output_cf_size = lv_color_format_get_size(output_cf);
+    lv_draw_dma2d_output_cf_t output_cf_dma2d = lv_draw_dma2d_cf_to_dma2d_output_cf(output_cf);
+
+    const lv_draw_buf_t * decoded = decoder_dsc->decoded;
+    const uint8_t * src_buf = decoded->data;
+    uint32_t image_stride = decoded->header.stride;
+    lv_color_format_t image_cf = decoded->header.cf;
+    lv_opa_t opa = draw_dsc->opa;
+    lv_draw_dma2d_fgbg_cf_t image_cf_dma2d = (lv_draw_dma2d_fgbg_cf_t) lv_draw_dma2d_cf_to_dma2d_output_cf(image_cf);
+    uint32_t image_cf_size = LV_COLOR_FORMAT_GET_SIZE(image_cf);
+    if(image_stride == 0) image_stride = image_cf_size * decoded->header.w;
+
+#if LV_DRAW_DMA2D_CACHE
+    lv_draw_dma2d_cache_area_t dest_area = {
+        .first_byte = dest_first_pixel,
+        .width_bytes = w * output_cf_size,
+        .height = h,
+        .stride = dest_stride
+    };
+    lv_draw_dma2d_unit_t * u = (lv_draw_dma2d_unit_t *) t->draw_unit;
+    lv_memcpy(&u->writing_area, &dest_area, sizeof(lv_draw_dma2d_cache_area_t));
+    /* make sure the background area DMA2D is blending is up-to-date in main memory */
+    lv_draw_dma2d_clean_cache(&dest_area);
+#endif
+
+    const void * image_first_byte = src_buf
+                                    + (image_stride * (clipped_img_area->y1 - draw_dsc->image_area.y1))
+                                    + (image_cf_size * (clipped_img_area->x1 - draw_dsc->image_area.x1));
+
+#if LV_DRAW_DMA2D_CACHE
+    lv_draw_dma2d_cache_area_t src_area = {
+        .first_byte = image_first_byte,
+        .width_bytes = w * image_cf_size,
+        .height = h,
+        .stride = image_stride
+    };
+    /* make sure the image area is up-to-date in main memory for DMA2D */
+    lv_draw_dma2d_clean_cache(&src_area);
+#endif
+
+    uint32_t output_offset = (dest_stride / output_cf_size) - w;
+    lv_draw_dma2d_configuration_t conf = {
+        .mode = LV_DRAW_DMA2D_MODE_MEMORY_TO_MEMORY_WITH_BLENDING,
+        .w = w,
+        .h = h,
+
+        .output_address = dest_first_pixel,
+        .output_offset = output_offset,
+        .output_cf = output_cf_dma2d,
+
+        .fg_address = image_first_byte,
+        .fg_offset = (image_stride / image_cf_size) - w,
+        .fg_cf = image_cf_dma2d,
+        .fg_alpha_mode = LV_DRAW_DMA2D_ALPHA_MODE_MULTIPLY_IMAGE_ALPHA_CHANNEL,
+        .fg_alpha = opa,
+
+        .bg_address = dest_first_pixel,
+        .bg_offset = output_offset,
+        .bg_cf = output_cf_dma2d,
+    };
+
+    /* Alpha channel should be treated as 0xFF if the cf is XRGB */
+    if(image_cf == LV_COLOR_FORMAT_XRGB8888) {
+        conf.fg_alpha_mode = LV_DRAW_DMA2D_ALPHA_MODE_REPLACE_ALPHA_CHANNEL;
+    }
+    if(output_cf == LV_COLOR_FORMAT_XRGB8888) {
+        conf.bg_alpha_mode = LV_DRAW_DMA2D_ALPHA_MODE_REPLACE_ALPHA_CHANNEL;
+        conf.bg_alpha = 0xff;
+    }
+
+    lv_draw_dma2d_configure_and_start_transfer(&conf);
+}
+
+#endif /*LV_USE_DRAW_DMA2D*/

+ 155 - 0
components/easylvgl/lvgl9/src/draw/dma2d/lv_draw_dma2d_private.h

@@ -0,0 +1,155 @@
+/**
+ * @file lv_draw_dma2d_private.h
+ *
+ */
+
+#ifndef LV_DRAW_DMA2D_PRIVATE_H
+#define LV_DRAW_DMA2D_PRIVATE_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/*********************
+ *      INCLUDES
+ *********************/
+
+#include "lv_draw_dma2d.h"
+#if LV_USE_DRAW_DMA2D
+
+#include "../lv_draw_private.h"
+#include "../sw/lv_draw_sw.h"
+#include LV_DRAW_DMA2D_HAL_INCLUDE
+
+/*********************
+ *      DEFINES
+ *********************/
+
+#if LV_USE_DRAW_DMA2D_INTERRUPT && LV_USE_OS
+#define LV_DRAW_DMA2D_ASYNC 1
+#else
+#define LV_DRAW_DMA2D_ASYNC 0
+#endif
+
+#if defined(__CORTEX_M) && ((__CORTEX_M == 7) || (__CORTEX_M == 55))
+#define LV_DRAW_DMA2D_CACHE 1
+#else
+#define LV_DRAW_DMA2D_CACHE 0
+#endif
+
+/**********************
+ *      TYPEDEFS
+ **********************/
+
+typedef enum {
+    LV_DRAW_DMA2D_OUTPUT_CF_ARGB8888 = 0,
+    LV_DRAW_DMA2D_OUTPUT_CF_RGB888,
+    LV_DRAW_DMA2D_OUTPUT_CF_RGB565,
+    LV_DRAW_DMA2D_OUTPUT_CF_ARGB1555,
+    LV_DRAW_DMA2D_OUTPUT_CF_ARGB4444
+} lv_draw_dma2d_output_cf_t;
+
+typedef enum {
+    LV_DRAW_DMA2D_FGBG_CF_ARGB8888 = 0,
+    LV_DRAW_DMA2D_FGBG_CF_RGB888,
+    LV_DRAW_DMA2D_FGBG_CF_RGB565,
+    LV_DRAW_DMA2D_FGBG_CF_ARGB1555,
+    LV_DRAW_DMA2D_FGBG_CF_ARGB4444,
+    LV_DRAW_DMA2D_FGBG_CF_L8,
+    LV_DRAW_DMA2D_FGBG_CF_AL44,
+    LV_DRAW_DMA2D_FGBG_CF_AL88,
+    LV_DRAW_DMA2D_FGBG_CF_L4,
+    LV_DRAW_DMA2D_FGBG_CF_A8,
+    LV_DRAW_DMA2D_FGBG_CF_A4,
+    LV_DRAW_DMA2D_FGBG_CF_YCBCR
+} lv_draw_dma2d_fgbg_cf_t;
+
+typedef enum {
+    LV_DRAW_DMA2D_MODE_MEMORY_TO_MEMORY = 0,
+    LV_DRAW_DMA2D_MODE_MEMORY_TO_MEMORY_WITH_PFC,
+    LV_DRAW_DMA2D_MODE_MEMORY_TO_MEMORY_WITH_BLENDING,
+    LV_DRAW_DMA2D_MODE_REGISTER_TO_MEMORY,
+    LV_DRAW_DMA2D_MODE_MEMORY_TO_MEMORY_WITH_BLENDING_AND_FIXED_COLOR_FG,
+    LV_DRAW_DMA2D_MODE_MEMORY_TO_MEMORY_WITH_BLENDING_AND_FIXED_COLOR_BG
+} lv_draw_dma2d_mode_t;
+
+typedef enum {
+    LV_DRAW_DMA2D_ALPHA_MODE_NO_MODIFY_IMAGE_ALPHA_CHANNEL = 0,
+    LV_DRAW_DMA2D_ALPHA_MODE_REPLACE_ALPHA_CHANNEL,
+    LV_DRAW_DMA2D_ALPHA_MODE_MULTIPLY_IMAGE_ALPHA_CHANNEL
+} lv_draw_dma2d_alpha_mode_t;
+
+typedef struct {
+    lv_draw_dma2d_mode_t mode;
+    uint32_t w;
+    uint32_t h;
+
+    void * output_address;
+    uint32_t output_offset;
+    lv_draw_dma2d_output_cf_t output_cf;
+
+    uint32_t reg_to_mem_mode_color;
+
+    const void * fg_address;
+    uint32_t fg_offset;
+    lv_draw_dma2d_fgbg_cf_t fg_cf;
+    uint32_t fg_color;
+    uint32_t fg_alpha_mode;
+    uint32_t fg_alpha;
+
+    const void * bg_address;
+    uint32_t bg_offset;
+    lv_draw_dma2d_fgbg_cf_t bg_cf;
+    uint32_t bg_color;
+    uint32_t bg_alpha_mode;
+    uint32_t bg_alpha;
+
+} lv_draw_dma2d_configuration_t;
+
+typedef struct {
+    const void * first_byte;
+    uint32_t width_bytes;
+    uint32_t height;
+    uint32_t stride;
+} lv_draw_dma2d_cache_area_t;
+
+typedef struct {
+    lv_draw_unit_t base_unit;
+    lv_draw_task_t * volatile task_act;
+#if LV_DRAW_DMA2D_CACHE
+    lv_draw_dma2d_cache_area_t writing_area;
+#endif
+#if LV_DRAW_DMA2D_ASYNC
+    lv_thread_sync_t interrupt_signal;
+#endif
+} lv_draw_dma2d_unit_t;
+
+/**********************
+ * GLOBAL PROTOTYPES
+ **********************/
+
+void lv_draw_dma2d_opaque_fill(lv_draw_task_t * t, void * first_pixel, int32_t w, int32_t h, int32_t stride);
+void lv_draw_dma2d_fill(lv_draw_task_t * t, void * first_pixel, int32_t w, int32_t h, int32_t stride);
+void lv_draw_dma2d_opaque_image(lv_draw_task_t * t, const lv_draw_image_dsc_t * draw_dsc,
+                                const lv_area_t * coords);
+void lv_draw_dma2d_image(lv_draw_task_t * t, const lv_draw_image_dsc_t * draw_dsc,
+                         const lv_area_t * coords);
+lv_draw_dma2d_output_cf_t lv_draw_dma2d_cf_to_dma2d_output_cf(lv_color_format_t cf);
+uint32_t lv_draw_dma2d_color_to_dma2d_color(lv_draw_dma2d_output_cf_t cf, lv_color_t color);
+void lv_draw_dma2d_configure_and_start_transfer(const lv_draw_dma2d_configuration_t * conf);
+#if LV_DRAW_DMA2D_CACHE
+void lv_draw_dma2d_invalidate_cache(const lv_draw_dma2d_cache_area_t * mem_area);
+void lv_draw_dma2d_clean_cache(const lv_draw_dma2d_cache_area_t * mem_area);
+#endif
+
+/**********************
+ *      MACROS
+ **********************/
+
+#endif /*LV_USE_DRAW_DMA2D*/
+
+#ifdef __cplusplus
+} /*extern "C"*/
+#endif
+
+#endif /*LV_DRAW_DMA2D_PRIVATE_H*/

+ 255 - 0
components/easylvgl/lvgl9/src/draw/espressif/ppa/lv_draw_ppa.c

@@ -0,0 +1,255 @@
+/**
+ * @file lv_draw_ppa.c
+ *
+ */
+
+/*********************
+*      INCLUDES
+*********************/
+
+#include "lv_draw_ppa_private.h"
+#include "lv_draw_ppa.h"
+
+#if LV_USE_PPA
+
+/*********************
+*      DEFINES
+*********************/
+
+#define DRAW_UNIT_ID_PPA         80
+#define DRAW_UNIT_PPA_PREF_SCORE 70
+
+/**********************
+*  STATIC PROTOTYPES
+**********************/
+
+static int32_t ppa_evaluate(lv_draw_unit_t * draw_unit, lv_draw_task_t * task);
+static int32_t ppa_dispatch(lv_draw_unit_t * draw_unit, lv_layer_t * layer);
+static int32_t ppa_delete(lv_draw_unit_t * draw_unit);
+static void  ppa_execute_drawing(lv_draw_ppa_unit_t * u);
+static bool ppa_isr(ppa_client_handle_t ppa_client, ppa_event_data_t * event_data, void * user_data);
+
+#if LV_PPA_NONBLOCKING_OPS
+    static void ppa_thread(void * arg);
+#endif
+
+static bool g_ppa_complete = true;
+
+/**********************
+*   GLOBAL FUNCTIONS
+**********************/
+
+void lv_draw_ppa_init(void)
+{
+    esp_err_t res;
+    ppa_client_config_t cfg = {0};
+    ppa_event_callbacks_t ppa_cbs = {
+        .on_trans_done = ppa_isr,
+
+    };
+
+    /* Create draw unit */
+    lv_draw_buf_ppa_init_handlers();
+    lv_draw_ppa_unit_t * draw_ppa_unit = lv_draw_create_unit(sizeof(lv_draw_ppa_unit_t));
+    draw_ppa_unit->base_unit.evaluate_cb = ppa_evaluate;
+    draw_ppa_unit->base_unit.dispatch_cb  = ppa_dispatch;
+    draw_ppa_unit->base_unit.delete_cb    = ppa_delete;
+    draw_ppa_unit->base_unit.name         = "ESP_PPA";
+
+    /* Register SRM client */
+    cfg.oper_type = PPA_OPERATION_SRM;
+    cfg.max_pending_trans_num = 8;
+    cfg.data_burst_length = PPA_DATA_BURST_LENGTH_128;
+
+    res = ppa_register_client(&cfg, &draw_ppa_unit->srm_client);
+    LV_ASSERT(res == ESP_OK);
+
+    /* Register Fill client */
+    cfg.oper_type = PPA_OPERATION_FILL;
+    cfg.data_burst_length = PPA_DATA_BURST_LENGTH_128;
+    res = ppa_register_client(&cfg, &draw_ppa_unit->fill_client);
+    LV_ASSERT(res == ESP_OK);
+
+    /* Register Blend client */
+    cfg.oper_type = PPA_OPERATION_BLEND;
+    cfg.data_burst_length = PPA_DATA_BURST_LENGTH_32;
+
+    res = ppa_register_client(&cfg, &draw_ppa_unit->blend_client);
+    LV_ASSERT(res == ESP_OK);
+
+    ppa_client_register_event_callbacks(draw_ppa_unit->srm_client, &ppa_cbs);
+    ppa_client_register_event_callbacks(draw_ppa_unit->fill_client, &ppa_cbs);
+    ppa_client_register_event_callbacks(draw_ppa_unit->blend_client, &ppa_cbs);
+
+#if LV_PPA_NONBLOCKING_OPS
+    lv_result_t lv_res = lv_thread_init(&draw_ppa_unit->thread, "ppa_thread", LV_DRAW_THREAD_PRIO, ppa_thread, 8192,
+                                        draw_ppa_unit);
+    LV_ASSERT(lv_res == LV_RESULT_OK);
+#endif
+
+}
+
+void lv_draw_ppa_deinit(void)
+{
+    /* No global deinit required */
+}
+
+/**********************
+*   STATIC FUNCTIONS
+**********************/
+
+static bool ppa_isr(ppa_client_handle_t ppa_client, ppa_event_data_t * event_data, void * user_data)
+{
+    g_ppa_complete = true;
+
+#if LV_PPA_NONBLOCKING_OPS
+    lv_draw_ppa_unit_t * u = (lv_draw_ppa_unit_t *)user_data;
+    lv_thread_sync_signal_isr(&u->interrupt_signal);
+#endif
+
+    return false;
+}
+
+static int32_t ppa_evaluate(lv_draw_unit_t * u, lv_draw_task_t * t)
+{
+    LV_UNUSED(u);
+    const lv_draw_dsc_base_t * base = (lv_draw_dsc_base_t *)t->draw_dsc;
+
+    if(!ppa_dest_cf_supported(base->layer->color_format)) return 0;
+
+    switch(t->type) {
+        case LV_DRAW_TASK_TYPE_FILL: {
+                const lv_draw_fill_dsc_t * dsc = (lv_draw_fill_dsc_t *)t->draw_dsc;
+                if((dsc->radius != 0 || dsc->grad.dir != LV_GRAD_DIR_NONE)) return 0;
+                if(dsc->opa <= (lv_opa_t)LV_OPA_MAX) return 0;
+
+                if(t->preference_score > DRAW_UNIT_PPA_PREF_SCORE) {
+                    t->preference_score = DRAW_UNIT_PPA_PREF_SCORE;
+                    t->preferred_draw_unit_id = DRAW_UNIT_ID_PPA;
+                }
+                return 1;
+            }
+
+#if LV_USE_PPA_IMG
+        case LV_DRAW_TASK_TYPE_IMAGE: {
+                lv_draw_image_dsc_t * dsc = t->draw_dsc;
+                if(!(dsc->header.cf < LV_COLOR_FORMAT_PROPRIETARY_START
+                     && dsc->clip_radius == 0
+                     && dsc->bitmap_mask_src == NULL
+                     && dsc->sup == NULL
+                     && dsc->tile == 0
+                     && dsc->blend_mode == LV_BLEND_MODE_NORMAL
+                     && dsc->recolor_opa <= LV_OPA_MIN
+                     && dsc->opa <= (lv_opa_t)LV_OPA_MAX
+                     && dsc->skew_y == 0
+                     && dsc->skew_x == 0
+                     && dsc->scale_x == 256
+                     && dsc->scale_y == 256
+                     && dsc->rotation == 0
+                     && lv_image_src_get_type(dsc->src) == LV_IMAGE_SRC_VARIABLE
+                     && (dsc->header.cf == LV_COLOR_FORMAT_RGB888
+                         || dsc->header.cf == LV_COLOR_FORMAT_RGB565)
+                     && (dsc->base.layer->color_format == LV_COLOR_FORMAT_RGB888
+                         || dsc->base.layer->color_format == LV_COLOR_FORMAT_RGB565))) {
+                    return 0;
+                }
+
+                if(t->preference_score > DRAW_UNIT_PPA_PREF_SCORE) {
+                    t->preference_score = DRAW_UNIT_PPA_PREF_SCORE;
+                    t->preferred_draw_unit_id = DRAW_UNIT_ID_PPA;
+                }
+                return 1;
+            }
+#endif
+        default:
+            return 0;
+    }
+}
+
+static int32_t ppa_dispatch(lv_draw_unit_t * draw_unit, lv_layer_t * layer)
+{
+    lv_draw_ppa_unit_t * u = (lv_draw_ppa_unit_t *)draw_unit;
+    if(u->task_act) {
+        if(!g_ppa_complete) {
+            return LV_DRAW_UNIT_IDLE;
+        }
+        else {
+            u->task_act->state = LV_DRAW_TASK_STATE_FINISHED;
+            u->task_act = NULL;
+        }
+    }
+
+    lv_draw_task_t * t = lv_draw_get_available_task(layer, NULL, DRAW_UNIT_ID_PPA);
+    if(!t || t->preferred_draw_unit_id != DRAW_UNIT_ID_PPA) return LV_DRAW_UNIT_IDLE;
+    if(!lv_draw_layer_alloc_buf(layer)) return LV_DRAW_UNIT_IDLE;
+
+    t->state = LV_DRAW_TASK_STATE_IN_PROGRESS;
+    u->task_act = t;
+    u->task_act->draw_unit = draw_unit;
+
+    ppa_execute_drawing(u);
+
+#if !LV_PPA_NONBLOCKING_OPS
+    u->task_act->state = LV_DRAW_TASK_STATE_FINISHED;
+    u->task_act = NULL;
+    lv_draw_dispatch_request();
+#endif
+
+    return 1;
+}
+
+static int32_t ppa_delete(lv_draw_unit_t * draw_unit)
+{
+    lv_draw_ppa_unit_t * u = (lv_draw_ppa_unit_t *)draw_unit;
+    ppa_unregister_client(u->srm_client);
+    ppa_unregister_client(u->fill_client);
+    ppa_unregister_client(u->blend_client);
+    return 0;
+}
+
+static void ppa_execute_drawing(lv_draw_ppa_unit_t * u)
+{
+    lv_draw_task_t * t         = u->task_act;
+    lv_layer_t * layer         = t->target_layer;
+    lv_draw_buf_t * buf        = layer->draw_buf;
+    lv_area_t area;
+    lv_area_t draw_area;
+
+    if(!lv_area_intersect(&area, &t->area, &t->clip_area)) return;
+
+    lv_area_move(&draw_area, -layer->buf_area.x1, -layer->buf_area.y1);
+    lv_draw_buf_invalidate_cache(buf, &draw_area);
+
+    switch(t->type) {
+        case LV_DRAW_TASK_TYPE_FILL:
+            g_ppa_complete = false;
+            lv_draw_ppa_fill(t, (lv_draw_fill_dsc_t *)t->draw_dsc, &area);
+            break;
+        case LV_DRAW_TASK_TYPE_IMAGE:
+            g_ppa_complete = false;
+            lv_draw_ppa_img(t, (lv_draw_image_dsc_t *)t->draw_dsc, &area);
+            break;
+        default:
+            break;
+    }
+}
+
+#if LV_PPA_NONBLOCKING_OPS
+static void ppa_thread(void * arg)
+{
+    lv_draw_ppa_unit_t * u = arg;
+    lv_thread_sync_init(&u->interrupt_signal);
+
+    while(1) {
+        do {
+            lv_thread_sync_wait(&u->interrupt_signal);
+        } while(u->task_act != NULL);
+
+        u->task_act->state = LV_DRAW_TASK_STATE_FINISHED;
+        u->task_act = NULL;
+        lv_draw_dispatch_request();
+    }
+}
+#endif
+
+#endif /*LV_USE_PPA*/

+ 57 - 0
components/easylvgl/lvgl9/src/draw/espressif/ppa/lv_draw_ppa.h

@@ -0,0 +1,57 @@
+/**
+ * @file lv_draw_ppa.h
+ *
+ */
+
+#ifndef LV_DRAW_PPA_H
+#define LV_DRAW_PPA_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/*********************
+ *      INCLUDES
+ *********************/
+
+#include "../../../lv_conf_internal.h"
+
+#if LV_USE_PPA
+
+#include "../../lv_draw_private.h"
+#include "../../../display/lv_display_private.h"
+#include "../../../misc/lv_area_private.h"
+
+/*********************
+ *      DEFINES
+ *********************/
+
+/**********************
+ *      TYPEDEFS
+ **********************/
+
+/**********************
+ * GLOBAL PROTOTYPES
+ **********************/
+
+void lv_draw_ppa_init(void);
+void lv_draw_ppa_deinit(void);
+void lv_draw_buf_ppa_init_handlers(void);
+
+void lv_draw_ppa_fill(lv_draw_task_t * t, const lv_draw_fill_dsc_t * dsc,
+                      const lv_area_t * coords);
+
+void lv_draw_ppa_img(lv_draw_task_t * t, const lv_draw_image_dsc_t * dsc,
+                     const lv_area_t * coords);
+
+/**********************
+ *      MACROS
+ **********************/
+
+#endif /* LV_USE_PPA */
+
+#ifdef __cplusplus
+} /*extern "C"*/
+#endif
+
+#endif /* LV_DRAW_PPA_H */

+ 52 - 0
components/easylvgl/lvgl9/src/draw/espressif/ppa/lv_draw_ppa_buf.c

@@ -0,0 +1,52 @@
+/**
+ * @file lv_draw_ppa_buf.c
+ *
+ */
+
+/*********************
+ *      INCLUDES
+ *********************/
+#include "lv_draw_ppa_private.h"
+#include "lv_draw_ppa.h"
+
+#if LV_USE_PPA
+#include LV_STDINT_INCLUDE
+#include "../../lv_draw_buf_private.h"
+
+/*********************
+ *      DEFINES
+ *********************/
+
+/**********************
+ *      TYPEDEFS
+ *********************/
+
+/**********************
+ *  STATIC PROTOTYPES
+ *********************/
+static void invalidate_cache(const lv_draw_buf_t * draw_buf, const lv_area_t * area);
+
+/**********************
+ *   GLOBAL FUNCTIONS
+ *********************/
+
+void lv_draw_buf_ppa_init_handlers(void)
+{
+    lv_draw_buf_handlers_t * handlers = lv_draw_buf_get_handlers();
+    lv_draw_buf_handlers_t * image_handlers = lv_draw_buf_get_image_handlers();
+
+    handlers->invalidate_cache_cb       = invalidate_cache;
+    image_handlers->invalidate_cache_cb = invalidate_cache;
+}
+
+/**********************
+ *   STATIC FUNCTIONS
+ *********************/
+
+static void invalidate_cache(const lv_draw_buf_t * draw_buf, const lv_area_t * area)
+{
+    esp_cache_msync((void *)PPA_PTR_ALIGN_DOWN(draw_buf->data, CONFIG_CACHE_L1_CACHE_LINE_SIZE),
+                    PPA_ALIGN_DOWN(draw_buf->data_size, CONFIG_CACHE_L1_CACHE_LINE_SIZE),
+                    ESP_CACHE_MSYNC_FLAG_DIR_C2M | ESP_CACHE_MSYNC_FLAG_TYPE_DATA);
+}
+#endif /* LV_USE_PPA */

+ 48 - 0
components/easylvgl/lvgl9/src/draw/espressif/ppa/lv_draw_ppa_fill.c

@@ -0,0 +1,48 @@
+/**
+ * @file lv_draw_ppa_fill.c
+ *
+ */
+
+#include "lv_draw_ppa_private.h"
+#include "lv_draw_ppa.h"
+
+#if LV_USE_PPA
+
+void lv_draw_ppa_fill(lv_draw_task_t * t, const lv_draw_fill_dsc_t * dsc,
+                      const lv_area_t * coords)
+{
+    lv_draw_ppa_unit_t * u = (lv_draw_ppa_unit_t *)t->draw_unit;
+    lv_draw_buf_t * draw_buf = t->target_layer->draw_buf;
+    int width  = lv_area_get_width(coords);
+    int height = lv_area_get_height(coords);
+
+    if(width <= 0 || height <= 0) {
+        LV_LOG_WARN("Invalid draw area for filling!");
+        return;
+    }
+
+    ppa_fill_oper_config_t cfg = {
+        .fill_argb_color.val = lv_color_to_u32(dsc->color),
+        .fill_block_w    = width,
+        .fill_block_h    = height,
+        .out = {
+            .buffer         = draw_buf->data,
+            .buffer_size    = draw_buf->data_size,
+            .pic_w          = width,
+            .pic_h          = height,
+            .block_offset_x = 0,
+            .block_offset_y = 0,
+            .fill_cm        = lv_color_format_to_ppa_fill(draw_buf->header.cf),
+        },
+
+        .mode            = PPA_TRANS_MODE_NON_BLOCKING,
+        .user_data       = u,
+    };
+
+    esp_err_t ret = ppa_do_fill(u->fill_client, &cfg);
+    if(ret != ESP_OK) {
+        LV_LOG_ERROR("PPA fill failed: %d", ret);
+    }
+}
+
+#endif /* LV_USE_PPA */

+ 77 - 0
components/easylvgl/lvgl9/src/draw/espressif/ppa/lv_draw_ppa_img.c

@@ -0,0 +1,77 @@
+/**
+ * @file lv_draw_ppa_img.c
+ *
+ */
+
+#include "lv_draw_ppa_private.h"
+#include "lv_draw_ppa.h"
+
+#if LV_USE_PPA
+
+void lv_draw_ppa_img(lv_draw_task_t * t, const lv_draw_image_dsc_t * dsc,
+                     const lv_area_t * coords)
+{
+    if(dsc->opa <= (lv_opa_t)LV_OPA_MIN) {
+        return;
+    }
+
+    lv_draw_ppa_unit_t * u = (lv_draw_ppa_unit_t *)t->draw_unit;
+    lv_draw_buf_t * draw_buf = t->target_layer->draw_buf;
+    const lv_image_dsc_t * img_dsc = dsc->src;
+    int width  = lv_area_get_width(coords);
+    int height = lv_area_get_height(coords);
+
+    ppa_blend_oper_config_t cfg = {
+        .in_bg = {
+            .buffer          = (void *)draw_buf->data,
+            .pic_w           = draw_buf->header.w,
+            .pic_h           = draw_buf->header.h,
+            .block_w         = width,
+            .block_h         = height,
+            .block_offset_x  = 0,
+            .block_offset_y  = 0,
+            .blend_cm        = lv_color_format_to_ppa_blend(draw_buf->header.cf),
+        },
+        .bg_rgb_swap           = false,
+        .bg_byte_swap          = false,
+        .bg_alpha_update_mode  = PPA_ALPHA_NO_CHANGE,
+        .bg_alpha_fix_val      = 0,
+        .bg_ck_en              = false,
+
+        .in_fg = {
+            .buffer          = (void *)img_dsc->data,
+            .pic_w           = width,
+            .pic_h           = height,
+            .block_w         = width,
+            .block_h         = height,
+            .block_offset_x  = 0,
+            .block_offset_y  = 0,
+            .blend_cm        = lv_color_format_to_ppa_blend(dsc->header.cf),
+        },
+        .fg_rgb_swap           = false,
+        .fg_byte_swap          = false,
+        .fg_alpha_update_mode = PPA_ALPHA_NO_CHANGE,
+        .fg_alpha_fix_val      = 0xFF,
+        .fg_ck_en              = false,
+
+        .out = {
+            .buffer          = draw_buf->data,
+            .buffer_size     = PPA_ALIGN_UP(draw_buf->data_size, CONFIG_CACHE_L1_CACHE_LINE_SIZE),
+            .pic_w           = draw_buf->header.w,
+            .pic_h           = draw_buf->header.h,
+            .block_offset_x  = 0,
+            .block_offset_y  = 0,
+            .blend_cm        = lv_color_format_to_ppa_blend(draw_buf->header.cf),
+        },
+
+        .mode            = PPA_TRANS_MODE_NON_BLOCKING,
+        .user_data       = u,
+    };
+
+    esp_err_t ret = ppa_do_blend(u->blend_client, &cfg);
+    if(ret != ESP_OK) {
+        LV_LOG_WARN("PPA draw_img blend failed: %d", ret);
+    }
+}
+
+#endif /* LV_USE_PPA */

+ 185 - 0
components/easylvgl/lvgl9/src/draw/espressif/ppa/lv_draw_ppa_private.h

@@ -0,0 +1,185 @@
+/**
+ * @file lv_draw_ppa_private.h
+ *
+ */
+
+#ifndef LV_DRAW_PPA_PRIVATE_H
+#define LV_DRAW_PPA_PRIVATE_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/*********************
+*      INCLUDES
+*********************/
+#include "../../../lv_conf_internal.h"
+
+#if LV_USE_PPA
+#if LV_PPA_NONBLOCKING_OPS
+#error "PPA draw in nonblocking is experimental and not supported yet, please make it to 0!"
+#endif
+
+#ifndef LV_PPA_NONBLOCKING_OPS
+#define LV_PPA_NONBLOCKING_OPS 0
+#endif
+
+#include LV_STDDEF_INCLUDE
+#include LV_STDBOOL_INCLUDE
+#include LV_STDINT_INCLUDE
+
+#include "../../../misc/lv_color.h"
+#include "../../../misc/lv_log.h"
+#include "../../lv_draw_private.h"
+#include "../../../display/lv_display_private.h"
+#include "../../../misc/lv_area_private.h"
+
+/* The ppa driver depends heavily on the esp-idf headers*/
+#include "sdkconfig.h"
+
+#if CONFIG_LV_ATTRIBUTE_MEM_ALIGN_SIZE != CONFIG_CACHE_L1_CACHE_LINE_SIZE || CONFIG_LV_DRAW_BUF_ALIGN != CONFIG_CACHE_L1_CACHE_LINE_SIZE
+#error "For using PPA buffers need to be aligned to 64-byte boundary!"
+#endif
+
+
+#ifndef CONFIG_SOC_PPA_SUPPORTED
+#error "This SoC does not support PPA"
+#endif
+
+#include "driver/ppa.h"
+#include "esp_heap_caps.h"
+#include "esp_err.h"
+#include "hal/color_hal.h"
+#include "esp_cache.h"
+#include "esp_log.h"
+/*********************
+*      DEFINES
+*********************/
+
+/**********************
+*      TYPEDEFS
+**********************/
+typedef struct lv_draw_ppa_unit {
+    lv_draw_unit_t base_unit;
+    lv_draw_task_t * task_act;
+    ppa_client_handle_t srm_client;
+    ppa_client_handle_t fill_client;
+    ppa_client_handle_t blend_client;
+    uint8_t * buf;
+#if LV_PPA_NONBLOCKING_OPS
+    lv_thread_t thread;
+    lv_thread_sync_t interrupt_signal;
+#endif
+} lv_draw_ppa_unit_t;
+
+/**********************
+*  STATIC PROTOTYPES
+**********************/
+
+/**********************
+* GLOBAL PROTOTYPES
+**********************/
+
+/**********************
+*      MACROS
+**********************/
+
+/**********************
+*   STATIC FUNCTIONS
+**********************/
+
+static inline bool ppa_src_cf_supported(lv_color_format_t cf)
+{
+    bool is_cf_supported = false;
+
+    switch(cf) {
+        case LV_COLOR_FORMAT_RGB565:
+        case LV_COLOR_FORMAT_ARGB8888:
+        case LV_COLOR_FORMAT_XRGB8888:
+            is_cf_supported = true;
+            break;
+        default:
+            break;
+    }
+
+    return is_cf_supported;
+}
+
+static inline bool ppa_dest_cf_supported(lv_color_format_t cf)
+{
+    bool is_cf_supported = false;
+
+    switch(cf) {
+        case LV_COLOR_FORMAT_RGB565:
+        case LV_COLOR_FORMAT_RGB888:
+        case LV_COLOR_FORMAT_ARGB8888:
+        case LV_COLOR_FORMAT_XRGB8888:
+            is_cf_supported = true;
+            break;
+        default:
+            break;
+    }
+
+    return is_cf_supported;
+}
+
+static inline ppa_fill_color_mode_t lv_color_format_to_ppa_fill(lv_color_format_t lv_fmt)
+{
+    switch(lv_fmt) {
+        case LV_COLOR_FORMAT_RGB565:
+            return PPA_FILL_COLOR_MODE_RGB565;
+        case LV_COLOR_FORMAT_RGB888:
+            return PPA_FILL_COLOR_MODE_RGB888;
+        case LV_COLOR_FORMAT_ARGB8888:
+        case LV_COLOR_FORMAT_XRGB8888:
+            return PPA_FILL_COLOR_MODE_ARGB8888;
+        default:
+            return PPA_FILL_COLOR_MODE_RGB565;
+    }
+}
+
+static inline ppa_blend_color_mode_t lv_color_format_to_ppa_blend(lv_color_format_t lv_fmt)
+{
+    switch(lv_fmt) {
+        case LV_COLOR_FORMAT_RGB565:
+            return PPA_BLEND_COLOR_MODE_RGB565;
+        case LV_COLOR_FORMAT_RGB888:
+            return PPA_BLEND_COLOR_MODE_RGB888;
+        case LV_COLOR_FORMAT_ARGB8888:
+        case LV_COLOR_FORMAT_XRGB8888:
+            return PPA_BLEND_COLOR_MODE_ARGB8888;
+        default:
+            return PPA_BLEND_COLOR_MODE_RGB565;
+    }
+}
+
+static inline ppa_srm_color_mode_t lv_color_format_to_ppa_srm(lv_color_format_t lv_fmt)
+{
+    switch(lv_fmt) {
+        case LV_COLOR_FORMAT_RGB565:
+            return PPA_SRM_COLOR_MODE_RGB565;
+        case LV_COLOR_FORMAT_RGB888:
+            return PPA_SRM_COLOR_MODE_RGB888;
+        case LV_COLOR_FORMAT_XRGB8888:
+        case LV_COLOR_FORMAT_ARGB8888:
+            return PPA_SRM_COLOR_MODE_ARGB8888;
+        default:
+            return PPA_SRM_COLOR_MODE_RGB565;
+    }
+}
+
+#define PPA_ALIGN_UP(x, align)  ((((x) + (align) - 1) / (align)) * (align))
+#define PPA_PTR_ALIGN_UP(p, align) \
+    ((void*)(((uintptr_t)(p) + (uintptr_t)((align) - 1)) & ~(uintptr_t)((align) - 1)))
+
+#define PPA_ALIGN_DOWN(x, align)  ((((x) - (align) - 1) / (align)) * (align))
+#define PPA_PTR_ALIGN_DOWN(p, align) \
+    ((void*)(((uintptr_t)(p) - (uintptr_t)((align) - 1)) & ~(uintptr_t)((align) - 1)))
+
+#endif /* LV_USE_PPA */
+
+#ifdef __cplusplus
+} /*extern "C"*/
+#endif
+
+#endif /* LV_DRAW_PPA_PRIVATE_H */

+ 164 - 0
components/easylvgl/lvgl9/src/draw/eve/lv_draw_eve.c

@@ -0,0 +1,164 @@
+/**
+ * @file lv_draw_eve.c
+ *
+ */
+
+/*  Created on: 3 dic 2023
+ *      Author: juanj
+ *
+ *  Modified by LVGL
+ */
+
+/*********************
+ *      INCLUDES
+ *********************/
+
+#include "lv_draw_eve_private.h"
+#if LV_USE_DRAW_EVE
+
+#include "../../core/lv_refr.h"
+#include "../../display/lv_display_private.h"
+#include "../../stdlib/lv_string.h"
+#include "lv_draw_eve_ram_g.h"
+#include "lv_draw_eve.h"
+#include "lv_eve.h"
+
+/*********************
+ *      DEFINES
+ *********************/
+
+#define DRAW_UNIT_ID_EVE 9
+
+/**********************
+ *      TYPEDEFS
+ **********************/
+
+/**********************
+ *  STATIC PROTOTYPES
+ **********************/
+static void eve_execute_drawing(lv_draw_eve_unit_t * u);
+
+static int32_t eve_dispatch(lv_draw_unit_t * draw_unit, lv_layer_t * layer);
+
+static int32_t eve_evaluate(lv_draw_unit_t * draw_unit, lv_draw_task_t * task);
+
+static void disp_delete_cb(lv_event_t * e);
+
+/**********************
+ *  GLOBAL PROTOTYPES
+ **********************/
+
+/**********************
+ *  STATIC VARIABLES
+ **********************/
+
+/**********************
+ *      MACROS
+ **********************/
+
+/**********************
+ *   GLOBAL FUNCTIONS
+ **********************/
+
+void lv_draw_eve_init(void)
+{
+    lv_draw_eve_unit_t * draw_eve_unit = lv_draw_create_unit(sizeof(lv_draw_eve_unit_t));
+    draw_eve_unit->base_unit.dispatch_cb = eve_dispatch;
+    draw_eve_unit->base_unit.evaluate_cb = eve_evaluate;
+
+    lv_draw_eve_unit_g = draw_eve_unit;
+}
+
+void lv_draw_eve_set_display_data(lv_display_t * disp, const lv_draw_eve_parameters_t * params,
+                                  lv_draw_eve_operation_cb_t op_cb)
+{
+    if(lv_draw_eve_unit_g == NULL) {
+        LV_LOG_WARN("lv_draw_eve is not initialized.");
+        return;
+    }
+
+    lv_draw_eve_unit_g->disp = disp;
+    lv_draw_eve_unit_g->params = *params; /* make a copy */
+    lv_draw_eve_unit_g->op_cb = op_cb;
+
+    lv_display_add_event_cb(disp, disp_delete_cb, LV_EVENT_DELETE, NULL);
+}
+
+/**********************
+ *   STATIC FUNCTIONS
+ **********************/
+
+static int32_t eve_dispatch(lv_draw_unit_t * draw_unit, lv_layer_t * layer)
+{
+    lv_draw_eve_unit_t * draw_eve_unit = (lv_draw_eve_unit_t *) draw_unit;
+
+    lv_draw_task_t * t = NULL;
+    t = lv_draw_get_next_available_task(layer, NULL, DRAW_UNIT_ID_EVE);
+    if(t == NULL) return LV_DRAW_UNIT_IDLE;
+
+
+    t->state = LV_DRAW_TASK_STATE_IN_PROGRESS;
+    draw_eve_unit->task_act = t;
+
+    eve_execute_drawing(draw_eve_unit);
+
+    draw_eve_unit->task_act->state = LV_DRAW_TASK_STATE_FINISHED;
+    draw_eve_unit->task_act = NULL;
+
+    /*The draw unit is free now. Request a new dispatching as it can get a new task*/
+    lv_draw_dispatch_request();
+
+
+    return 1;
+}
+
+static int32_t eve_evaluate(lv_draw_unit_t * draw_unit, lv_draw_task_t * task)
+{
+    LV_UNUSED(draw_unit);
+
+    if(((lv_draw_dsc_base_t *)task->draw_dsc)->user_data == NULL) {
+        task->preference_score = 0;
+        task->preferred_draw_unit_id = DRAW_UNIT_ID_EVE;
+    }
+    return 0;
+}
+
+static void eve_execute_drawing(lv_draw_eve_unit_t * u)
+{
+    lv_draw_task_t * t = u->task_act;
+
+    switch(t->type) {
+        case LV_DRAW_TASK_TYPE_LINE:
+            lv_draw_eve_line(t, t->draw_dsc);
+            break;
+        case LV_DRAW_TASK_TYPE_BORDER:
+            lv_draw_eve_border(t, t->draw_dsc, &t->area);
+            break;
+        case LV_DRAW_TASK_TYPE_FILL:
+            lv_draw_eve_fill(t, t->draw_dsc, &t->area);
+            break;
+        case LV_DRAW_TASK_TYPE_IMAGE:
+            lv_draw_eve_image(t, t->draw_dsc, &t->area);
+            break;
+        case LV_DRAW_TASK_TYPE_LABEL:
+            lv_draw_eve_label(t, t->draw_dsc, &t->area);
+            break;
+        case LV_DRAW_TASK_TYPE_ARC:
+            lv_draw_eve_arc(t, t->draw_dsc, &t->area);
+            break;
+        case LV_DRAW_TASK_TYPE_TRIANGLE:
+            lv_draw_eve_triangle(t, t->draw_dsc);
+            break;
+        default:
+            break;
+    }
+}
+
+static void disp_delete_cb(lv_event_t * e)
+{
+    lv_draw_eve_unit_g->disp = NULL;
+    lv_draw_eve_unit_g = NULL;
+}
+
+
+#endif /*LV_USE_DRAW_EVE*/

+ 57 - 0
components/easylvgl/lvgl9/src/draw/eve/lv_draw_eve.h

@@ -0,0 +1,57 @@
+/**
+ * @file lv_draw_eve.h
+ *
+ */
+
+/*  Created on: 3 dic 2023
+ *      Author: juanj
+ *
+ *  Modified by LVGL
+ */
+
+#ifndef LV_DRAW_EVE_H
+#define LV_DRAW_EVE_H
+
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/*********************
+ *      INCLUDES
+ *********************/
+
+#include "../../lv_conf_internal.h"
+#if LV_USE_DRAW_EVE
+
+#include "lv_draw_eve_target.h"
+
+/*********************
+ *      DEFINES
+ *********************/
+
+/**********************
+ *      TYPEDEFS
+ **********************/
+
+/**********************
+ * GLOBAL PROTOTYPES
+ **********************/
+
+void lv_draw_eve_init(void);
+
+void lv_draw_eve_set_display_data(lv_display_t * disp, const lv_draw_eve_parameters_t * params,
+                                  lv_draw_eve_operation_cb_t op_cb);
+
+/**********************
+ *      MACROS
+ **********************/
+
+#endif /*LV_USE_DRAW_EVE*/
+
+#ifdef __cplusplus
+} /*extern "C"*/
+#endif
+
+
+#endif /* LV_DRAW_EVE_H */

+ 325 - 0
components/easylvgl/lvgl9/src/draw/eve/lv_draw_eve_arc.c

@@ -0,0 +1,325 @@
+/**
+ * @file lv_draw_eve_arc.c
+ *
+ */
+
+/*  Created on: 11 dic 2023
+ *      Author: juanj
+ *
+ *  Modified by LVGL
+ */
+
+/*********************
+ *      INCLUDES
+ *********************/
+#include "lv_draw_eve_private.h"
+#if LV_USE_DRAW_EVE
+
+#include "../lv_draw_arc.h"
+#include "lv_eve.h"
+
+/*********************
+ *      DEFINES
+ *********************/
+
+/**********************
+ * STATIC PROTOTYPES
+ **********************/
+
+static void draw_eve_arc(lv_draw_task_t * t, const lv_draw_arc_dsc_t * dsc, const lv_area_t * coords);
+static bool is_same_quadrant(int16_t start_angle, int16_t end_angle) ;
+static void draw_rounded_end(lv_point_t center, int32_t radius, int32_t angle, int32_t width);
+static void lv_draw_eve_mask_angle(const lv_draw_arc_dsc_t * dsc, int32_t vertex_x, int32_t vertex_y,
+                                   int32_t start_angle, int32_t end_angle);
+static lv_eve_primitive_t get_mask_direction(int16_t angle);
+static int32_t chord_length(int16_t radius, int16_t angle_degrees);
+
+/**********************
+ *   GLOBAL FUNCTIONS
+ **********************/
+
+void lv_draw_eve_arc(lv_draw_task_t * t, const lv_draw_arc_dsc_t * dsc, const lv_area_t * coords)
+{
+    draw_eve_arc(t, dsc, coords);
+}
+
+/**********************
+ *   STATIC FUNCTIONS
+ **********************/
+
+
+static int32_t chord_length(int16_t radius, int16_t angle_degrees)
+{
+    angle_degrees %= 360;
+    if(angle_degrees < 0) angle_degrees += 360;
+    int32_t sin_value = lv_trigo_sin(angle_degrees / 2);
+    int64_t chord_length = 2 * radius * sin_value / 32768.0;
+    return (int32_t)chord_length ;
+}
+
+
+static lv_eve_primitive_t get_mask_direction(int16_t angle)
+{
+    if(angle >= 315 || angle <  45) {
+        return LV_EVE_PRIMITIVE_EDGE_STRIP_R;
+    }
+    if(angle >=  45 && angle < 135) {
+        return LV_EVE_PRIMITIVE_EDGE_STRIP_B;
+    }
+    if(angle >= 135 && angle < 225) {
+        return LV_EVE_PRIMITIVE_EDGE_STRIP_L;
+    }
+    if(angle >= 225 && angle < 315) {
+        return LV_EVE_PRIMITIVE_EDGE_STRIP_A;
+    }
+    return 0;
+}
+
+
+static void draw_rounded_end(lv_point_t center, int32_t radius, int32_t angle, int32_t width)
+{
+    int32_t rounded_y  = center.y + ((lv_trigo_sin(angle) * radius) >> LV_TRIGO_SHIFT);
+    int32_t rounded_x  = center.x + ((lv_trigo_cos(angle) * radius) >> LV_TRIGO_SHIFT);
+    lv_eve_draw_circle_simple(rounded_x, rounded_y, width);
+}
+
+
+
+static bool is_same_quadrant(int16_t start_angle, int16_t end_angle)
+{
+    if(start_angle > end_angle) {
+        if((start_angle >= 0 && start_angle < 90) && (end_angle >= 0 && end_angle < 90)) {
+            return true;
+        }
+        else if((start_angle >= 90 && start_angle < 180) && (end_angle >= 90 && end_angle < 180)) {
+            return true;
+        }
+        else if((start_angle >= 180 && start_angle < 270) && (end_angle >= 180 && end_angle < 270)) {
+            return true;
+        }
+        else if((start_angle >= 270 && start_angle < 360) && (end_angle >= 270 && end_angle < 360)) {
+            return true;
+        }
+        else {
+            return false;
+        }
+    }
+    else {
+        return false;
+    }
+}
+
+
+
+static void lv_draw_eve_mask_angle(const lv_draw_arc_dsc_t * dsc, int32_t vertex_x, int32_t vertex_y,
+                                   int32_t start_angle, int32_t end_angle)
+{
+
+    /*Constrain the input angles*/
+
+
+    if(start_angle < 0)
+        start_angle = 0;
+    else if(start_angle > 359)
+        start_angle = 359;
+
+    if(end_angle < 0)
+        end_angle = 0;
+    else if(end_angle > 359)
+        end_angle = 359;
+
+    LV_ASSERT_MSG(start_angle >= 0 && start_angle <= 360, "Unexpected start angle");
+
+    int32_t mid_angle_op;
+    int32_t angle_range;
+    int32_t mask_dir_start;
+    int32_t mask_dir_end;
+    lv_point_t start;
+    lv_point_t end;
+    lv_point_t angle_range_op;
+
+    if(end_angle > start_angle) {
+        angle_range = LV_ABS(end_angle - start_angle);
+    }
+    else {
+        angle_range = 360 - start_angle + end_angle;
+    }
+
+    mid_angle_op = (angle_range / 2) + start_angle + 180;
+    mid_angle_op = mid_angle_op % 360;
+
+    mask_dir_end = LV_ABS(((360 - angle_range) / 4) + end_angle);
+    mask_dir_start = LV_ABS(((360 - angle_range) / 4) + mid_angle_op);
+
+    mask_dir_start = mask_dir_start % 360;
+    mask_dir_end = mask_dir_end % 360;
+
+    start.y = (lv_trigo_sin(start_angle) >> 5) + vertex_y;
+    start.x = (lv_trigo_cos(start_angle) >> 5) + vertex_x;
+
+    end.y = (lv_trigo_sin(end_angle) >> 5) + vertex_y;
+    end.x = (lv_trigo_cos(end_angle) >> 5) + vertex_x;
+
+    angle_range_op.y = (lv_trigo_sin(mid_angle_op) >> 5) + vertex_y;
+    angle_range_op.x = (lv_trigo_cos(mid_angle_op) >> 5) + vertex_x;
+
+    if(angle_range <= 180) {
+        /* Two sides mask and 6 vertex points */
+
+        /* Masking end angle */
+        lv_eve_primitive_t edge = get_mask_direction(mask_dir_end);
+        lv_eve_primitive(edge); /* Side one */
+        lv_eve_vertex_2f(angle_range_op.x, angle_range_op.y);
+        lv_eve_vertex_2f(vertex_x, vertex_y);
+        lv_eve_vertex_2f(end.x, end.y);
+
+        /* Masking start angle */
+        edge = get_mask_direction(mask_dir_start);
+        lv_eve_primitive(edge); /* Side two */
+        lv_eve_vertex_2f(angle_range_op.x, angle_range_op.y);
+        lv_eve_vertex_2f(vertex_x, vertex_y);
+        lv_eve_vertex_2f(start.x, start.y);
+
+    }
+
+    else {
+
+        if(is_same_quadrant(start_angle,
+                            end_angle)) { /* "It is not an optimal implementation for the case where both angles (start and end) are in the same quadrant */
+            /* todo */
+            lv_point_t end_line_cntr;
+            lv_point_t start_line_cntr;
+
+            lv_point_t end_line_brd;
+            lv_point_t start_line_brd;
+
+            int16_t chord = chord_length(dsc->radius, 360 - angle_range);
+            int16_t w = ((chord / 4) < 1) ? 1 : chord / 4;
+            int16_t r_width = w;
+
+            end_line_brd.y = vertex_y + ((lv_trigo_sin(end_angle) * dsc->radius) >> LV_TRIGO_SHIFT);
+            end_line_brd.x = vertex_x + ((lv_trigo_cos(end_angle) * dsc->radius) >> LV_TRIGO_SHIFT);
+
+            start_line_brd.y = vertex_y + ((lv_trigo_sin(start_angle) * dsc->radius) >> LV_TRIGO_SHIFT);
+            start_line_brd.x = vertex_x + ((lv_trigo_cos(start_angle) * dsc->radius) >> LV_TRIGO_SHIFT);
+
+            lv_eve_draw_rect_simple(start_line_brd.x, start_line_brd.y, end_line_brd.x, end_line_brd.y, 0);
+
+            start_line_brd.y = start_line_brd.y + ((lv_trigo_sin(start_angle - 90) * r_width) >> LV_TRIGO_SHIFT);
+            start_line_brd.x = start_line_brd.x + ((lv_trigo_cos(start_angle - 90) * r_width) >> LV_TRIGO_SHIFT);
+
+            end_line_brd.y = end_line_brd.y + ((lv_trigo_sin(end_angle + 90) * r_width) >> LV_TRIGO_SHIFT);
+            end_line_brd.x = end_line_brd.x + ((lv_trigo_cos(end_angle + 90) * r_width) >> LV_TRIGO_SHIFT);
+
+            end_line_cntr.y = vertex_y + ((lv_trigo_sin(end_angle + 90) * r_width) >> LV_TRIGO_SHIFT);
+            end_line_cntr.x = vertex_x + ((lv_trigo_cos(end_angle + 90) * r_width) >> LV_TRIGO_SHIFT);
+
+            start_line_cntr.y = vertex_y + ((lv_trigo_sin(start_angle + 270) * r_width) >> LV_TRIGO_SHIFT);
+            start_line_cntr.x = vertex_x + ((lv_trigo_cos(start_angle + 270) * r_width) >> LV_TRIGO_SHIFT);
+
+            lv_eve_primitive(LV_EVE_PRIMITIVE_LINE_STRIP);
+            lv_eve_line_width(r_width * 16);
+            lv_eve_vertex_2f(start_line_cntr.x, start_line_cntr.y);
+            lv_eve_vertex_2f(start_line_brd.x, start_line_brd.y);
+            lv_eve_vertex_2f(end_line_brd.x, end_line_brd.y);
+            lv_eve_vertex_2f(end_line_cntr.x, end_line_cntr.y);
+
+        }
+        else { /* One side mask and 3 vertex points */
+            /* Masking end and start angles */
+            lv_eve_primitive_t edge = get_mask_direction(mid_angle_op);
+            lv_eve_primitive(edge);
+            lv_eve_vertex_2f(end.x, end.y);
+            lv_eve_vertex_2f(vertex_x, vertex_y);
+            lv_eve_vertex_2f(start.x, start.y);
+        }
+    }
+
+}
+
+
+
+static void draw_eve_arc(lv_draw_task_t * t, const lv_draw_arc_dsc_t * dsc, const lv_area_t * coords)
+{
+
+    if(dsc->opa <= LV_OPA_MIN)
+        return;
+    if(dsc->width == 0)
+        return;
+    if(dsc->start_angle == dsc->end_angle)
+        return;
+
+    lv_color_t color = dsc->color;
+    lv_opa_t opa = dsc->opa;
+    lv_point_t center = dsc->center;
+    int32_t width = dsc->width;
+    uint16_t radius_out = dsc->radius;
+    uint16_t radius_in = dsc->radius - dsc->width;
+    int32_t start_angle = (int32_t) dsc->start_angle;
+    int32_t end_angle = (int32_t) dsc->end_angle;
+
+    if(width > radius_out)
+        width = radius_out;
+
+    while(start_angle >= 360)
+        start_angle -= 360;
+    while(end_angle >= 360)
+        end_angle -= 360;
+
+    lv_eve_scissor(t->clip_area.x1, t->clip_area.y1, t->clip_area.x2, t->clip_area.y2);
+
+    lv_eve_save_context();
+
+    lv_eve_color(color);
+    lv_eve_color_opa(opa);
+
+    lv_eve_color_mask(0, 0, 0, 1);
+    lv_eve_stencil_func(EVE_ALWAYS, 0, 1);
+    lv_eve_stencil_op(EVE_REPLACE, EVE_REPLACE);
+    lv_eve_draw_circle_simple(center.x, center.y, radius_out); /* radius_out */
+
+    lv_eve_blend_func(EVE_ONE, EVE_ZERO);
+    lv_eve_draw_circle_simple(center.x, center.y, radius_in + 2); /* radius_in */
+
+    lv_eve_stencil_func(EVE_ALWAYS, 1, 1);
+    lv_eve_stencil_op(EVE_REPLACE, EVE_REPLACE);
+    lv_eve_blend_func(EVE_ZERO, EVE_ONE_MINUS_SRC_ALPHA);
+    lv_eve_color_opa(0XFF);
+
+    /* Start masking arc */
+
+    lv_draw_eve_mask_angle(dsc, center.x, center.y, start_angle, end_angle);
+
+    /* End masking arc */
+
+    lv_eve_draw_circle_simple(center.x, center.y, radius_in); /* radius_in */
+
+    lv_eve_color_mask(1, 1, 1, 1);
+    lv_eve_blend_func(EVE_DST_ALPHA, EVE_ONE_MINUS_DST_ALPHA);
+    lv_eve_draw_circle_simple(center.x, center.y, radius_in); /* radius_in */
+
+    lv_eve_stencil_func(EVE_NOTEQUAL, 1, 0XFF);
+    lv_eve_stencil_op(EVE_KEEP, EVE_KEEP);
+    lv_eve_blend_func(EVE_SRC_ALPHA, EVE_ONE_MINUS_SRC_ALPHA);
+
+    lv_eve_color_opa(opa);
+    lv_eve_draw_circle_simple(center.x, center.y, radius_out); /* radius_out */
+
+    if(dsc->rounded) {
+        lv_eve_stencil_func(EVE_EQUAL, 1, 0XFF);
+        if(opa < 255) {
+            lv_eve_stencil_op(EVE_ZERO, EVE_ZERO);
+        }
+
+        int32_t half_width = width / 2;
+        int32_t adjusted_radius = radius_out - half_width;
+        draw_rounded_end(center, adjusted_radius, end_angle, half_width);
+        draw_rounded_end(center, adjusted_radius, start_angle, half_width);
+    }
+
+    lv_eve_restore_context();
+}
+
+
+
+#endif /*LV_USE_DRAW_EVE*/

+ 121 - 0
components/easylvgl/lvgl9/src/draw/eve/lv_draw_eve_fill.c

@@ -0,0 +1,121 @@
+/**
+ * @file lv_draw_eve_fill.c
+ *
+ */
+
+/*  Created on: 27 mar 2023
+ *      Author: juanj
+ *
+ *  Modified by LVGL
+ */
+
+/*********************
+ *      INCLUDES
+ *********************/
+#include "lv_draw_eve_private.h"
+#if LV_USE_DRAW_EVE
+#include "lv_eve.h"
+
+
+/**********************
+ * STATIC PROTOTYPES
+ **********************/
+
+/**********************
+ *   GLOBAL FUNCTIONS
+ **********************/
+
+void lv_draw_eve_fill(lv_draw_task_t * t, const lv_draw_fill_dsc_t * dsc, const lv_area_t * coords)
+{
+
+    int32_t rad = dsc->radius;
+    int32_t bg_w = lv_area_get_width(coords);
+    int32_t bg_h = lv_area_get_height(coords);
+    int32_t real_radius = LV_MIN3(bg_w / 2, bg_h / 2, rad);
+
+    lv_eve_scissor(t->clip_area.x1, t->clip_area.y1, t->clip_area.x2, t->clip_area.y2);
+    lv_eve_save_context();
+
+    lv_eve_color(dsc->color);
+    lv_eve_color_opa(dsc->opa);
+
+    if(bg_w == bg_h && rad == LV_RADIUS_CIRCLE) {
+        lv_eve_draw_circle_simple(coords->x1 + (bg_w / 2), coords->y1 + (bg_h / 2), real_radius);
+    }
+    else {
+        lv_eve_draw_rect_simple(coords->x1, coords->y1, coords->x2, coords->y2, real_radius);
+    }
+
+    lv_eve_restore_context();
+}
+
+
+/**********************
+ *   STATIC FUNCTIONS
+ **********************/
+
+
+
+void lv_draw_eve_border(lv_draw_task_t * t, const lv_draw_border_dsc_t * dsc, const lv_area_t * coords)
+{
+
+    if(dsc->opa <= LV_OPA_MIN) return;
+    if(dsc->width == 0) return;
+    if(dsc->side == LV_BORDER_SIDE_NONE) return;
+
+    int32_t coords_w = lv_area_get_width(coords);
+    int32_t coords_h = lv_area_get_height(coords);
+    int32_t rout = dsc->radius;
+    int32_t short_side = LV_MIN(coords_w, coords_h);
+    if(rout > short_side >> 1) rout = short_side >> 1;
+
+    /*Get the inner area*/
+    lv_area_t area_inner;
+    lv_area_copy(&area_inner, coords);
+    area_inner.x1 += ((dsc->side & LV_BORDER_SIDE_LEFT) ? dsc->width : - (dsc->width));
+    area_inner.x2 -= ((dsc->side & LV_BORDER_SIDE_RIGHT) ? dsc->width : - (dsc->width));
+    area_inner.y1 += ((dsc->side & LV_BORDER_SIDE_TOP) ? dsc->width : - (dsc->width));
+    area_inner.y2 -= ((dsc->side & LV_BORDER_SIDE_BOTTOM) ? dsc->width : - (dsc->width));
+
+    int32_t rin = rout - dsc->width;
+    if(rin < 0) rin = 0;
+
+    lv_eve_save_context();
+
+    lv_eve_scissor(t->clip_area.x1, t->clip_area.y1, t->clip_area.x2, t->clip_area.y2);
+
+    lv_eve_color(dsc->color);
+    lv_eve_color_opa(dsc->opa);
+
+    lv_eve_color_mask(0, 0, 0, 1);
+    lv_eve_stencil_func(EVE_ALWAYS, 0, 1);
+    lv_eve_stencil_op(EVE_REPLACE, EVE_REPLACE);
+    lv_eve_draw_rect_simple(coords->x1, coords->y1, coords->x2, coords->y2, 0);
+
+    lv_eve_blend_func(EVE_ONE, EVE_ZERO);
+    lv_eve_draw_rect_simple(area_inner.x1 - 2, area_inner.y1 - 1, area_inner.x2 + 1, area_inner.y2 + 2, rin);
+
+    lv_eve_stencil_func(EVE_ALWAYS, 1, 1);
+    lv_eve_stencil_op(EVE_REPLACE, EVE_REPLACE);
+    lv_eve_blend_func(EVE_ZERO, EVE_ONE_MINUS_SRC_ALPHA);
+    lv_eve_color_opa(255);
+    lv_eve_draw_rect_simple(area_inner.x1, area_inner.y1, area_inner.x2, area_inner.y2, rin);
+
+    lv_eve_color_mask(1, 1, 1, 1);
+
+    if(dsc->side == LV_BORDER_SIDE_FULL) {
+        lv_eve_blend_func(EVE_DST_ALPHA, EVE_ONE_MINUS_DST_ALPHA);
+        lv_eve_draw_rect_simple(area_inner.x1, area_inner.y1, area_inner.x2, area_inner.y2, rin);
+    }
+
+    lv_eve_stencil_func(EVE_NOTEQUAL, 1, 255);
+    lv_eve_blend_func(EVE_SRC_ALPHA, EVE_ONE_MINUS_SRC_ALPHA);
+
+    lv_eve_color_opa(dsc->opa);
+    lv_eve_draw_rect_simple(coords->x1, coords->y1, coords->x2, coords->y2, rout);
+
+    lv_eve_restore_context();
+}
+
+#endif /*LV_USE_DRAW_EVE*/
+

Some files were not shown because too many files changed in this diff