summaryrefslogtreecommitdiff
path: root/lib/lvgl/src/misc
diff options
context:
space:
mode:
Diffstat (limited to 'lib/lvgl/src/misc')
m---------lib/lvgl0
-rw-r--r--lib/lvgl/src/misc/lv_anim.c470
-rw-r--r--lib/lvgl/src/misc/lv_anim.h484
-rw-r--r--lib/lvgl/src/misc/lv_anim_timeline.c198
-rw-r--r--lib/lvgl/src/misc/lv_anim_timeline.h103
-rw-r--r--lib/lvgl/src/misc/lv_area.c535
-rw-r--r--lib/lvgl/src/misc/lv_area.h295
-rw-r--r--lib/lvgl/src/misc/lv_assert.h79
-rw-r--r--lib/lvgl/src/misc/lv_async.c105
-rw-r--r--lib/lvgl/src/misc/lv_async.h61
-rw-r--r--lib/lvgl/src/misc/lv_bidi.c682
-rw-r--r--lib/lvgl/src/misc/lv_bidi.h141
-rw-r--r--lib/lvgl/src/misc/lv_color.c369
-rw-r--r--lib/lvgl/src/misc/lv_color.h708
-rw-r--r--lib/lvgl/src/misc/lv_fs.c518
-rw-r--r--lib/lvgl/src/misc/lv_fs.h262
-rw-r--r--lib/lvgl/src/misc/lv_gc.c47
-rw-r--r--lib/lvgl/src/misc/lv_gc.h97
-rw-r--r--lib/lvgl/src/misc/lv_ll.c408
-rw-r--r--lib/lvgl/src/misc/lv_ll.h167
-rw-r--r--lib/lvgl/src/misc/lv_log.c144
-rw-r--r--lib/lvgl/src/misc/lv_log.h154
-rw-r--r--lib/lvgl/src/misc/lv_lru.c349
-rw-r--r--lib/lvgl/src/misc/lv_lru.h87
-rw-r--r--lib/lvgl/src/misc/lv_math.c273
-rw-r--r--lib/lvgl/src/misc/lv_math.h143
-rw-r--r--lib/lvgl/src/misc/lv_mem.c566
-rw-r--r--lib/lvgl/src/misc/lv_mem.h243
-rw-r--r--lib/lvgl/src/misc/lv_misc.mk26
-rw-r--r--lib/lvgl/src/misc/lv_printf.c879
-rw-r--r--lib/lvgl/src/misc/lv_printf.h92
-rw-r--r--lib/lvgl/src/misc/lv_style.c485
-rw-r--r--lib/lvgl/src/misc/lv_style.h597
-rw-r--r--lib/lvgl/src/misc/lv_style_gen.c673
-rw-r--r--lib/lvgl/src/misc/lv_style_gen.h504
-rw-r--r--lib/lvgl/src/misc/lv_templ.c40
-rw-r--r--lib/lvgl/src/misc/lv_templ.h37
-rw-r--r--lib/lvgl/src/misc/lv_timer.c341
-rw-r--r--lib/lvgl/src/misc/lv_timer.h183
-rw-r--r--lib/lvgl/src/misc/lv_tlsf.c1246
-rw-r--r--lib/lvgl/src/misc/lv_tlsf.h95
-rw-r--r--lib/lvgl/src/misc/lv_txt.c864
-rw-r--r--lib/lvgl/src/misc/lv_txt.h264
-rw-r--r--lib/lvgl/src/misc/lv_txt_ap.c301
-rw-r--r--lib/lvgl/src/misc/lv_txt_ap.h49
-rw-r--r--lib/lvgl/src/misc/lv_types.h94
-rw-r--r--lib/lvgl/src/misc/lv_utils.c79
-rw-r--r--lib/lvgl/src/misc/lv_utils.h58
48 files changed, 14595 insertions, 0 deletions
diff --git a/lib/lvgl b/lib/lvgl
deleted file mode 160000
-Subproject 0732400e7b564dd0e7dc4a924619d8e19c5b23a
diff --git a/lib/lvgl/src/misc/lv_anim.c b/lib/lvgl/src/misc/lv_anim.c
new file mode 100644
index 00000000..4e4253a6
--- /dev/null
+++ b/lib/lvgl/src/misc/lv_anim.c
@@ -0,0 +1,470 @@
+/**
+ * @file lv_anim.c
+ *
+ */
+
+/*********************
+ * INCLUDES
+ *********************/
+#include "lv_anim.h"
+
+#include "../hal/lv_hal_tick.h"
+#include "lv_assert.h"
+#include "lv_timer.h"
+#include "lv_math.h"
+#include "lv_mem.h"
+#include "lv_gc.h"
+
+/*********************
+ * DEFINES
+ *********************/
+#define LV_ANIM_RESOLUTION 1024
+#define LV_ANIM_RES_SHIFT 10
+
+/**********************
+ * TYPEDEFS
+ **********************/
+
+/**********************
+ * STATIC PROTOTYPES
+ **********************/
+static void anim_timer(lv_timer_t * param);
+static void anim_mark_list_change(void);
+static void anim_ready_handler(lv_anim_t * a);
+
+/**********************
+ * STATIC VARIABLES
+ **********************/
+static uint32_t last_timer_run;
+static bool anim_list_changed;
+static bool anim_run_round;
+static lv_timer_t * _lv_anim_tmr;
+
+/**********************
+ * MACROS
+ **********************/
+#if LV_LOG_TRACE_ANIM
+ #define TRACE_ANIM(...) LV_LOG_TRACE(__VA_ARGS__)
+#else
+ #define TRACE_ANIM(...)
+#endif
+
+
+/**********************
+ * GLOBAL FUNCTIONS
+ **********************/
+
+void _lv_anim_core_init(void)
+{
+ _lv_ll_init(&LV_GC_ROOT(_lv_anim_ll), sizeof(lv_anim_t));
+ _lv_anim_tmr = lv_timer_create(anim_timer, LV_DISP_DEF_REFR_PERIOD, NULL);
+ anim_mark_list_change(); /*Turn off the animation timer*/
+ anim_list_changed = false;
+}
+
+void lv_anim_init(lv_anim_t * a)
+{
+ lv_memset_00(a, sizeof(lv_anim_t));
+ a->time = 500;
+ a->start_value = 0;
+ a->end_value = 100;
+ a->repeat_cnt = 1;
+ a->path_cb = lv_anim_path_linear;
+ a->early_apply = 1;
+}
+
+lv_anim_t * lv_anim_start(const lv_anim_t * a)
+{
+ TRACE_ANIM("begin");
+
+ /*Do not let two animations for the same 'var' with the same 'exec_cb'*/
+ if(a->exec_cb != NULL) lv_anim_del(a->var, a->exec_cb); /*exec_cb == NULL would delete all animations of var*/
+
+ /*If the list is empty the anim timer was suspended and it's last run measure is invalid*/
+ if(_lv_ll_is_empty(&LV_GC_ROOT(_lv_anim_ll))) {
+ last_timer_run = lv_tick_get();
+ }
+
+ /*Add the new animation to the animation linked list*/
+ lv_anim_t * new_anim = _lv_ll_ins_head(&LV_GC_ROOT(_lv_anim_ll));
+ LV_ASSERT_MALLOC(new_anim);
+ if(new_anim == NULL) return NULL;
+
+ /*Initialize the animation descriptor*/
+ lv_memcpy(new_anim, a, sizeof(lv_anim_t));
+ if(a->var == a) new_anim->var = new_anim;
+ new_anim->run_round = anim_run_round;
+
+ /*Set the start value*/
+ if(new_anim->early_apply) {
+ if(new_anim->get_value_cb) {
+ int32_t v_ofs = new_anim->get_value_cb(new_anim);
+ new_anim->start_value += v_ofs;
+ new_anim->end_value += v_ofs;
+ }
+
+ if(new_anim->exec_cb && new_anim->var) new_anim->exec_cb(new_anim->var, new_anim->start_value);
+ }
+
+ /*Creating an animation changed the linked list.
+ *It's important if it happens in a ready callback. (see `anim_timer`)*/
+ anim_mark_list_change();
+
+ TRACE_ANIM("finished");
+ return new_anim;
+}
+
+uint32_t lv_anim_get_playtime(lv_anim_t * a)
+{
+ uint32_t playtime = LV_ANIM_PLAYTIME_INFINITE;
+
+ if(a->repeat_cnt == LV_ANIM_REPEAT_INFINITE)
+ return playtime;
+
+ playtime = a->time - a->act_time;
+ if(a->playback_now == 0)
+ playtime += a->playback_delay + a->playback_time;
+
+ if(a->repeat_cnt <= 1)
+ return playtime;
+
+ playtime += (a->repeat_delay + a->time +
+ a->playback_delay + a->playback_time) *
+ (a->repeat_cnt - 1);
+
+ return playtime;
+}
+
+bool lv_anim_del(void * var, lv_anim_exec_xcb_t exec_cb)
+{
+ lv_anim_t * a;
+ lv_anim_t * a_next;
+ bool del = false;
+ a = _lv_ll_get_head(&LV_GC_ROOT(_lv_anim_ll));
+ while(a != NULL) {
+ /*'a' might be deleted, so get the next object while 'a' is valid*/
+ a_next = _lv_ll_get_next(&LV_GC_ROOT(_lv_anim_ll), a);
+
+ if((a->var == var || var == NULL) && (a->exec_cb == exec_cb || exec_cb == NULL)) {
+ _lv_ll_remove(&LV_GC_ROOT(_lv_anim_ll), a);
+ if(a->deleted_cb != NULL) a->deleted_cb(a);
+ lv_mem_free(a);
+ anim_mark_list_change(); /*Read by `anim_timer`. It need to know if a delete occurred in
+ the linked list*/
+ del = true;
+ }
+
+ a = a_next;
+ }
+
+ return del;
+}
+
+void lv_anim_del_all(void)
+{
+ _lv_ll_clear(&LV_GC_ROOT(_lv_anim_ll));
+ anim_mark_list_change();
+}
+
+lv_anim_t * lv_anim_get(void * var, lv_anim_exec_xcb_t exec_cb)
+{
+ lv_anim_t * a;
+ _LV_LL_READ(&LV_GC_ROOT(_lv_anim_ll), a) {
+ if(a->var == var && (a->exec_cb == exec_cb || exec_cb == NULL)) {
+ return a;
+ }
+ }
+
+ return NULL;
+}
+
+struct _lv_timer_t * lv_anim_get_timer(void)
+{
+ return _lv_anim_tmr;
+}
+
+uint16_t lv_anim_count_running(void)
+{
+ uint16_t cnt = 0;
+ lv_anim_t * a;
+ _LV_LL_READ(&LV_GC_ROOT(_lv_anim_ll), a) cnt++;
+
+ return cnt;
+}
+
+uint32_t lv_anim_speed_to_time(uint32_t speed, int32_t start, int32_t end)
+{
+ uint32_t d = LV_ABS(start - end);
+ uint32_t time = (d * 1000) / speed;
+
+ if(time == 0) {
+ time++;
+ }
+
+ return time;
+}
+
+void lv_anim_refr_now(void)
+{
+ anim_timer(NULL);
+}
+
+int32_t lv_anim_path_linear(const lv_anim_t * a)
+{
+ /*Calculate the current step*/
+ int32_t step = lv_map(a->act_time, 0, a->time, 0, LV_ANIM_RESOLUTION);
+
+ /*Get the new value which will be proportional to `step`
+ *and the `start` and `end` values*/
+ int32_t new_value;
+ new_value = step * (a->end_value - a->start_value);
+ new_value = new_value >> LV_ANIM_RES_SHIFT;
+ new_value += a->start_value;
+
+ return new_value;
+}
+
+int32_t lv_anim_path_ease_in(const lv_anim_t * a)
+{
+ /*Calculate the current step*/
+ uint32_t t = lv_map(a->act_time, 0, a->time, 0, LV_BEZIER_VAL_MAX);
+ int32_t step = lv_bezier3(t, 0, 50, 100, LV_BEZIER_VAL_MAX);
+
+ int32_t new_value;
+ new_value = step * (a->end_value - a->start_value);
+ new_value = new_value >> LV_BEZIER_VAL_SHIFT;
+ new_value += a->start_value;
+
+ return new_value;
+}
+
+int32_t lv_anim_path_ease_out(const lv_anim_t * a)
+{
+ /*Calculate the current step*/
+ uint32_t t = lv_map(a->act_time, 0, a->time, 0, LV_BEZIER_VAL_MAX);
+ int32_t step = lv_bezier3(t, 0, 900, 950, LV_BEZIER_VAL_MAX);
+
+ int32_t new_value;
+ new_value = step * (a->end_value - a->start_value);
+ new_value = new_value >> LV_BEZIER_VAL_SHIFT;
+ new_value += a->start_value;
+
+ return new_value;
+}
+
+int32_t lv_anim_path_ease_in_out(const lv_anim_t * a)
+{
+ /*Calculate the current step*/
+ uint32_t t = lv_map(a->act_time, 0, a->time, 0, LV_BEZIER_VAL_MAX);
+ int32_t step = lv_bezier3(t, 0, 50, 952, LV_BEZIER_VAL_MAX);
+
+ int32_t new_value;
+ new_value = step * (a->end_value - a->start_value);
+ new_value = new_value >> LV_BEZIER_VAL_SHIFT;
+ new_value += a->start_value;
+
+ return new_value;
+}
+
+int32_t lv_anim_path_overshoot(const lv_anim_t * a)
+{
+ /*Calculate the current step*/
+ uint32_t t = lv_map(a->act_time, 0, a->time, 0, LV_BEZIER_VAL_MAX);
+ int32_t step = lv_bezier3(t, 0, 1000, 1300, LV_BEZIER_VAL_MAX);
+
+ int32_t new_value;
+ new_value = step * (a->end_value - a->start_value);
+ new_value = new_value >> LV_BEZIER_VAL_SHIFT;
+ new_value += a->start_value;
+
+ return new_value;
+}
+
+int32_t lv_anim_path_bounce(const lv_anim_t * a)
+{
+ /*Calculate the current step*/
+ int32_t t = lv_map(a->act_time, 0, a->time, 0, LV_BEZIER_VAL_MAX);
+ int32_t diff = (a->end_value - a->start_value);
+
+ /*3 bounces has 5 parts: 3 down and 2 up. One part is t / 5 long*/
+
+ if(t < 408) {
+ /*Go down*/
+ t = (t * 2500) >> LV_BEZIER_VAL_SHIFT; /*[0..1024] range*/
+ }
+ else if(t >= 408 && t < 614) {
+ /*First bounce back*/
+ t -= 408;
+ t = t * 5; /*to [0..1024] range*/
+ t = LV_BEZIER_VAL_MAX - t;
+ diff = diff / 20;
+ }
+ else if(t >= 614 && t < 819) {
+ /*Fall back*/
+ t -= 614;
+ t = t * 5; /*to [0..1024] range*/
+ diff = diff / 20;
+ }
+ else if(t >= 819 && t < 921) {
+ /*Second bounce back*/
+ t -= 819;
+ t = t * 10; /*to [0..1024] range*/
+ t = LV_BEZIER_VAL_MAX - t;
+ diff = diff / 40;
+ }
+ else if(t >= 921 && t <= LV_BEZIER_VAL_MAX) {
+ /*Fall back*/
+ t -= 921;
+ t = t * 10; /*to [0..1024] range*/
+ diff = diff / 40;
+ }
+
+ if(t > LV_BEZIER_VAL_MAX) t = LV_BEZIER_VAL_MAX;
+ if(t < 0) t = 0;
+ int32_t step = lv_bezier3(t, LV_BEZIER_VAL_MAX, 800, 500, 0);
+
+ int32_t new_value;
+ new_value = step * diff;
+ new_value = new_value >> LV_BEZIER_VAL_SHIFT;
+ new_value = a->end_value - new_value;
+
+ return new_value;
+}
+
+int32_t lv_anim_path_step(const lv_anim_t * a)
+{
+ if(a->act_time >= a->time)
+ return a->end_value;
+ else
+ return a->start_value;
+}
+
+/**********************
+ * STATIC FUNCTIONS
+ **********************/
+
+/**
+ * Periodically handle the animations.
+ * @param param unused
+ */
+static void anim_timer(lv_timer_t * param)
+{
+ LV_UNUSED(param);
+
+ uint32_t elaps = lv_tick_elaps(last_timer_run);
+
+ /*Flip the run round*/
+ anim_run_round = anim_run_round ? false : true;
+
+ lv_anim_t * a = _lv_ll_get_head(&LV_GC_ROOT(_lv_anim_ll));
+
+ while(a != NULL) {
+ /*It can be set by `lv_anim_del()` typically in `end_cb`. If set then an animation delete
+ * happened in `anim_ready_handler` which could make this linked list reading corrupt
+ * because the list is changed meanwhile
+ */
+ anim_list_changed = false;
+
+ if(a->run_round != anim_run_round) {
+ a->run_round = anim_run_round; /*The list readying might be reset so need to know which anim has run already*/
+
+ /*The animation will run now for the first time. Call `start_cb`*/
+ int32_t new_act_time = a->act_time + elaps;
+ if(!a->start_cb_called && a->act_time <= 0 && new_act_time >= 0) {
+ if(a->early_apply == 0 && a->get_value_cb) {
+ int32_t v_ofs = a->get_value_cb(a);
+ a->start_value += v_ofs;
+ a->end_value += v_ofs;
+ }
+ if(a->start_cb) a->start_cb(a);
+ a->start_cb_called = 1;
+ }
+ a->act_time += elaps;
+ if(a->act_time >= 0) {
+ if(a->act_time > a->time) a->act_time = a->time;
+
+ int32_t new_value;
+ new_value = a->path_cb(a);
+
+ if(new_value != a->current_value) {
+ a->current_value = new_value;
+ /*Apply the calculated value*/
+ if(a->exec_cb) a->exec_cb(a->var, new_value);
+ }
+
+ /*If the time is elapsed the animation is ready*/
+ if(a->act_time >= a->time) {
+ anim_ready_handler(a);
+ }
+ }
+ }
+
+ /*If the linked list changed due to anim. delete then it's not safe to continue
+ *the reading of the list from here -> start from the head*/
+ if(anim_list_changed)
+ a = _lv_ll_get_head(&LV_GC_ROOT(_lv_anim_ll));
+ else
+ a = _lv_ll_get_next(&LV_GC_ROOT(_lv_anim_ll), a);
+ }
+
+ last_timer_run = lv_tick_get();
+}
+
+/**
+ * Called when an animation is ready to do the necessary thinks
+ * e.g. repeat, play back, delete etc.
+ * @param a pointer to an animation descriptor
+ */
+static void anim_ready_handler(lv_anim_t * a)
+{
+ /*In the end of a forward anim decrement repeat cnt.*/
+ if(a->playback_now == 0 && a->repeat_cnt > 0 && a->repeat_cnt != LV_ANIM_REPEAT_INFINITE) {
+ a->repeat_cnt--;
+ }
+
+ /*Delete the animation if
+ * - no repeat left and no play back (simple one shot animation)
+ * - no repeat, play back is enabled and play back is ready*/
+ if(a->repeat_cnt == 0 && (a->playback_time == 0 || a->playback_now == 1)) {
+
+ /*Delete the animation from the list.
+ * This way the `ready_cb` will see the animations like it's animation is ready deleted*/
+ _lv_ll_remove(&LV_GC_ROOT(_lv_anim_ll), a);
+ /*Flag that the list has changed*/
+ anim_mark_list_change();
+
+ /*Call the callback function at the end*/
+ if(a->ready_cb != NULL) a->ready_cb(a);
+ if(a->deleted_cb != NULL) a->deleted_cb(a);
+ lv_mem_free(a);
+ }
+ /*If the animation is not deleted then restart it*/
+ else {
+ a->act_time = -(int32_t)(a->repeat_delay); /*Restart the animation*/
+ /*Swap the start and end values in play back mode*/
+ if(a->playback_time != 0) {
+ /*If now turning back use the 'playback_pause*/
+ if(a->playback_now == 0) a->act_time = -(int32_t)(a->playback_delay);
+
+ /*Toggle the play back state*/
+ a->playback_now = a->playback_now == 0 ? 1 : 0;
+ /*Swap the start and end values*/
+ int32_t tmp = a->start_value;
+ a->start_value = a->end_value;
+ a->end_value = tmp;
+ /*Swap the time and playback_time*/
+ tmp = a->time;
+ a->time = a->playback_time;
+ a->playback_time = tmp;
+ }
+ }
+}
+
+static void anim_mark_list_change(void)
+{
+ anim_list_changed = true;
+ if(_lv_ll_get_head(&LV_GC_ROOT(_lv_anim_ll)) == NULL)
+ lv_timer_pause(_lv_anim_tmr);
+ else
+ lv_timer_resume(_lv_anim_tmr);
+}
diff --git a/lib/lvgl/src/misc/lv_anim.h b/lib/lvgl/src/misc/lv_anim.h
new file mode 100644
index 00000000..faef7278
--- /dev/null
+++ b/lib/lvgl/src/misc/lv_anim.h
@@ -0,0 +1,484 @@
+/**
+ * @file lv_anim.h
+ *
+ */
+
+#ifndef LV_ANIM_H
+#define LV_ANIM_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/*********************
+ * INCLUDES
+ *********************/
+#include "../lv_conf_internal.h"
+
+#include <stdint.h>
+#include <stdbool.h>
+#include <stddef.h>
+
+/*********************
+ * DEFINES
+ *********************/
+
+#define LV_ANIM_REPEAT_INFINITE 0xFFFF
+#define LV_ANIM_PLAYTIME_INFINITE 0xFFFFFFFF
+
+LV_EXPORT_CONST_INT(LV_ANIM_REPEAT_INFINITE);
+LV_EXPORT_CONST_INT(LV_ANIM_PLAYTIME_INFINITE);
+
+/**********************
+ * TYPEDEFS
+ **********************/
+
+/** Can be used to indicate if animations are enabled or disabled in a case*/
+typedef enum {
+ LV_ANIM_OFF,
+ LV_ANIM_ON,
+} lv_anim_enable_t;
+
+struct _lv_anim_t;
+struct _lv_timer_t;
+
+/** Get the current value during an animation*/
+typedef int32_t (*lv_anim_path_cb_t)(const struct _lv_anim_t *);
+
+/** Generic prototype of "animator" functions.
+ * First parameter is the variable to animate.
+ * Second parameter is the value to set.
+ * Compatible with `lv_xxx_set_yyy(obj, value)` functions
+ * The `x` in `_xcb_t` means it's not a fully generic prototype because
+ * it doesn't receive `lv_anim_t *` as its first argument*/
+typedef void (*lv_anim_exec_xcb_t)(void *, int32_t);
+
+/** Same as `lv_anim_exec_xcb_t` but receives `lv_anim_t *` as the first parameter.
+ * It's more consistent but less convenient. Might be used by binding generator functions.*/
+typedef void (*lv_anim_custom_exec_cb_t)(struct _lv_anim_t *, int32_t);
+
+/** Callback to call when the animation is ready*/
+typedef void (*lv_anim_ready_cb_t)(struct _lv_anim_t *);
+
+/** Callback to call when the animation really stars (considering `delay`)*/
+typedef void (*lv_anim_start_cb_t)(struct _lv_anim_t *);
+
+/** Callback used when the animation values are relative to get the current value*/
+typedef int32_t (*lv_anim_get_value_cb_t)(struct _lv_anim_t *);
+
+/** Callback used when the animation is deleted*/
+typedef void (*lv_anim_deleted_cb_t)(struct _lv_anim_t *);
+
+/** Describes an animation*/
+typedef struct _lv_anim_t {
+ void * var; /**<Variable to animate*/
+ lv_anim_exec_xcb_t exec_cb; /**< Function to execute to animate*/
+ lv_anim_start_cb_t start_cb; /**< Call it when the animation is starts (considering `delay`)*/
+ lv_anim_ready_cb_t ready_cb; /**< Call it when the animation is ready*/
+ lv_anim_deleted_cb_t deleted_cb; /**< Call it when the animation is deleted*/
+ lv_anim_get_value_cb_t get_value_cb; /**< Get the current value in relative mode*/
+#if LV_USE_USER_DATA
+ void * user_data; /**< Custom user data*/
+#endif
+ lv_anim_path_cb_t path_cb; /**< Describe the path (curve) of animations*/
+ int32_t start_value; /**< Start value*/
+ int32_t current_value; /**< Current value*/
+ int32_t end_value; /**< End value*/
+ int32_t time; /**< Animation time in ms*/
+ int32_t act_time; /**< Current time in animation. Set to negative to make delay.*/
+ uint32_t playback_delay; /**< Wait before play back*/
+ uint32_t playback_time; /**< Duration of playback animation*/
+ uint32_t repeat_delay; /**< Wait before repeat*/
+ uint16_t repeat_cnt; /**< Repeat count for the animation*/
+ uint8_t early_apply : 1; /**< 1: Apply start value immediately even is there is `delay`*/
+
+ /*Animation system use these - user shouldn't set*/
+ uint8_t playback_now : 1; /**< Play back is in progress*/
+ uint8_t run_round : 1; /**< Indicates the animation has run in this round*/
+ uint8_t start_cb_called : 1; /**< Indicates that the `start_cb` was already called*/
+} lv_anim_t;
+
+/**********************
+ * GLOBAL PROTOTYPES
+ **********************/
+
+/**
+ * Init. the animation module
+ */
+void _lv_anim_core_init(void);
+
+/**
+ * Initialize an animation variable.
+ * E.g.:
+ * lv_anim_t a;
+ * lv_anim_init(&a);
+ * lv_anim_set_...(&a);
+ * lv_anim_start(&a);
+ * @param a pointer to an `lv_anim_t` variable to initialize
+ */
+void lv_anim_init(lv_anim_t * a);
+
+/**
+ * Set a variable to animate
+ * @param a pointer to an initialized `lv_anim_t` variable
+ * @param var pointer to a variable to animate
+ */
+static inline void lv_anim_set_var(lv_anim_t * a, void * var)
+{
+ a->var = var;
+}
+
+/**
+ * Set a function to animate `var`
+ * @param a pointer to an initialized `lv_anim_t` variable
+ * @param exec_cb a function to execute during animation
+ * LVGL's built-in functions can be used.
+ * E.g. lv_obj_set_x
+ */
+static inline void lv_anim_set_exec_cb(lv_anim_t * a, lv_anim_exec_xcb_t exec_cb)
+{
+ a->exec_cb = exec_cb;
+}
+
+/**
+ * Set the duration of an animation
+ * @param a pointer to an initialized `lv_anim_t` variable
+ * @param duration duration of the animation in milliseconds
+ */
+static inline void lv_anim_set_time(lv_anim_t * a, uint32_t duration)
+{
+ a->time = duration;
+}
+
+/**
+ * Set a delay before starting the animation
+ * @param a pointer to an initialized `lv_anim_t` variable
+ * @param delay delay before the animation in milliseconds
+ */
+static inline void lv_anim_set_delay(lv_anim_t * a, uint32_t delay)
+{
+ a->act_time = -(int32_t)(delay);
+}
+
+/**
+ * Set the start and end values of an animation
+ * @param a pointer to an initialized `lv_anim_t` variable
+ * @param start the start value
+ * @param end the end value
+ */
+static inline void lv_anim_set_values(lv_anim_t * a, int32_t start, int32_t end)
+{
+ a->start_value = start;
+ a->current_value = start;
+ a->end_value = end;
+}
+
+/**
+ * Similar to `lv_anim_set_exec_cb` but `lv_anim_custom_exec_cb_t` receives
+ * `lv_anim_t * ` as its first parameter instead of `void *`.
+ * This function might be used when LVGL is bound to other languages because
+ * it's more consistent to have `lv_anim_t *` as first parameter.
+ * The variable to animate can be stored in the animation's `user_data`
+ * @param a pointer to an initialized `lv_anim_t` variable
+ * @param exec_cb a function to execute.
+ */
+static inline void lv_anim_set_custom_exec_cb(lv_anim_t * a, lv_anim_custom_exec_cb_t exec_cb)
+{
+ a->var = a;
+ a->exec_cb = (lv_anim_exec_xcb_t)exec_cb;
+}
+
+/**
+ * Set the path (curve) of the animation.
+ * @param a pointer to an initialized `lv_anim_t` variable
+ * @param path_cb a function to set the current value of the animation.
+ */
+static inline void lv_anim_set_path_cb(lv_anim_t * a, lv_anim_path_cb_t path_cb)
+{
+ a->path_cb = path_cb;
+}
+
+/**
+ * Set a function call when the animation really starts (considering `delay`)
+ * @param a pointer to an initialized `lv_anim_t` variable
+ * @param start_cb a function call when the animation starts
+ */
+static inline void lv_anim_set_start_cb(lv_anim_t * a, lv_anim_start_cb_t start_cb)
+{
+ a->start_cb = start_cb;
+}
+
+/**
+ * Set a function to use the current value of the variable and make start and end value
+ * relative to the returned current value.
+ * @param a pointer to an initialized `lv_anim_t` variable
+ * @param get_value_cb a function call when the animation starts
+ */
+static inline void lv_anim_set_get_value_cb(lv_anim_t * a, lv_anim_get_value_cb_t get_value_cb)
+{
+ a->get_value_cb = get_value_cb;
+}
+
+/**
+ * Set a function call when the animation is ready
+ * @param a pointer to an initialized `lv_anim_t` variable
+ * @param ready_cb a function call when the animation is ready
+ */
+static inline void lv_anim_set_ready_cb(lv_anim_t * a, lv_anim_ready_cb_t ready_cb)
+{
+ a->ready_cb = ready_cb;
+}
+
+/**
+ * Set a function call when the animation is deleted.
+ * @param a pointer to an initialized `lv_anim_t` variable
+ * @param deleted_cb a function call when the animation is deleted
+ */
+static inline void lv_anim_set_deleted_cb(lv_anim_t * a, lv_anim_deleted_cb_t deleted_cb)
+{
+ a->deleted_cb = deleted_cb;
+}
+
+/**
+ * Make the animation to play back to when the forward direction is ready
+ * @param a pointer to an initialized `lv_anim_t` variable
+ * @param time the duration of the playback animation in milliseconds. 0: disable playback
+ */
+static inline void lv_anim_set_playback_time(lv_anim_t * a, uint32_t time)
+{
+ a->playback_time = time;
+}
+
+/**
+ * Make the animation to play back to when the forward direction is ready
+ * @param a pointer to an initialized `lv_anim_t` variable
+ * @param delay delay in milliseconds before starting the playback animation.
+ */
+static inline void lv_anim_set_playback_delay(lv_anim_t * a, uint32_t delay)
+{
+ a->playback_delay = delay;
+}
+
+/**
+ * Make the animation repeat itself.
+ * @param a pointer to an initialized `lv_anim_t` variable
+ * @param cnt repeat count or `LV_ANIM_REPEAT_INFINITE` for infinite repetition. 0: to disable repetition.
+ */
+static inline void lv_anim_set_repeat_count(lv_anim_t * a, uint16_t cnt)
+{
+ a->repeat_cnt = cnt;
+}
+
+/**
+ * Set a delay before repeating the animation.
+ * @param a pointer to an initialized `lv_anim_t` variable
+ * @param delay delay in milliseconds before repeating the animation.
+ */
+static inline void lv_anim_set_repeat_delay(lv_anim_t * a, uint32_t delay)
+{
+ a->repeat_delay = delay;
+}
+
+/**
+ * Set a whether the animation's should be applied immediately or only when the delay expired.
+ * @param a pointer to an initialized `lv_anim_t` variable
+ * @param en true: apply the start value immediately in `lv_anim_start`;
+ * false: apply the start value only when `delay` ms is elapsed and the animations really starts
+ */
+static inline void lv_anim_set_early_apply(lv_anim_t * a, bool en)
+{
+ a->early_apply = en;
+}
+
+/**
+ * Set the custom user data field of the animation.
+ * @param a pointer to an initialized `lv_anim_t` variable
+ * @param user_data pointer to the new user_data.
+ */
+#if LV_USE_USER_DATA
+static inline void lv_anim_set_user_data(lv_anim_t * a, void * user_data)
+{
+ a->user_data = user_data;
+}
+#endif
+
+/**
+ * Create an animation
+ * @param a an initialized 'anim_t' variable. Not required after call.
+ * @return pointer to the created animation (different from the `a` parameter)
+ */
+lv_anim_t * lv_anim_start(const lv_anim_t * a);
+
+/**
+ * Get a delay before starting the animation
+ * @param a pointer to an initialized `lv_anim_t` variable
+ * @return delay before the animation in milliseconds
+ */
+static inline uint32_t lv_anim_get_delay(lv_anim_t * a)
+{
+ return -a->act_time;
+}
+
+/**
+ * Get the time used to play the animation.
+ * @param a pointer to an animation.
+ * @return the play time in milliseconds.
+ */
+uint32_t lv_anim_get_playtime(lv_anim_t * a);
+
+/**
+ * Get the user_data field of the animation
+ * @param a pointer to an initialized `lv_anim_t` variable
+ * @return the pointer to the custom user_data of the animation
+ */
+#if LV_USE_USER_DATA
+static inline void * lv_anim_get_user_data(lv_anim_t * a)
+{
+ return a->user_data;
+}
+#endif
+
+/**
+ * Delete an animation of a variable with a given animator function
+ * @param var pointer to variable
+ * @param exec_cb a function pointer which is animating 'var',
+ * or NULL to ignore it and delete all the animations of 'var
+ * @return true: at least 1 animation is deleted, false: no animation is deleted
+ */
+bool lv_anim_del(void * var, lv_anim_exec_xcb_t exec_cb);
+
+/**
+ * Delete all the animations
+ */
+void lv_anim_del_all(void);
+
+/**
+ * Get the animation of a variable and its `exec_cb`.
+ * @param var pointer to variable
+ * @param exec_cb a function pointer which is animating 'var', or NULL to return first matching 'var'
+ * @return pointer to the animation.
+ */
+lv_anim_t * lv_anim_get(void * var, lv_anim_exec_xcb_t exec_cb);
+
+/**
+ * Get global animation refresher timer.
+ * @return pointer to the animation refresher timer.
+ */
+struct _lv_timer_t * lv_anim_get_timer(void);
+
+/**
+ * Delete an animation by getting the animated variable from `a`.
+ * Only animations with `exec_cb` will be deleted.
+ * This function exists because it's logical that all anim. functions receives an
+ * `lv_anim_t` as their first parameter. It's not practical in C but might make
+ * the API more consequent and makes easier to generate bindings.
+ * @param a pointer to an animation.
+ * @param exec_cb a function pointer which is animating 'var',
+ * or NULL to ignore it and delete all the animations of 'var
+ * @return true: at least 1 animation is deleted, false: no animation is deleted
+ */
+static inline bool lv_anim_custom_del(lv_anim_t * a, lv_anim_custom_exec_cb_t exec_cb)
+{
+ return lv_anim_del(a ? a->var : NULL, (lv_anim_exec_xcb_t)exec_cb);
+}
+
+/**
+ * Get the animation of a variable and its `exec_cb`.
+ * This function exists because it's logical that all anim. functions receives an
+ * `lv_anim_t` as their first parameter. It's not practical in C but might make
+ * the API more consequent and makes easier to generate bindings.
+ * @param a pointer to an animation.
+ * @param exec_cb a function pointer which is animating 'var', or NULL to return first matching 'var'
+ * @return pointer to the animation.
+ */
+static inline lv_anim_t * lv_anim_custom_get(lv_anim_t * a, lv_anim_custom_exec_cb_t exec_cb)
+{
+ return lv_anim_get(a ? a->var : NULL, (lv_anim_exec_xcb_t)exec_cb);
+}
+
+/**
+ * Get the number of currently running animations
+ * @return the number of running animations
+ */
+uint16_t lv_anim_count_running(void);
+
+/**
+ * Calculate the time of an animation with a given speed and the start and end values
+ * @param speed speed of animation in unit/sec
+ * @param start start value of the animation
+ * @param end end value of the animation
+ * @return the required time [ms] for the animation with the given parameters
+ */
+uint32_t lv_anim_speed_to_time(uint32_t speed, int32_t start, int32_t end);
+
+/**
+ * Manually refresh the state of the animations.
+ * Useful to make the animations running in a blocking process where
+ * `lv_timer_handler` can't run for a while.
+ * Shouldn't be used directly because it is called in `lv_refr_now()`.
+ */
+void lv_anim_refr_now(void);
+
+/**
+ * Calculate the current value of an animation applying linear characteristic
+ * @param a pointer to an animation
+ * @return the current value to set
+ */
+int32_t lv_anim_path_linear(const lv_anim_t * a);
+
+/**
+ * Calculate the current value of an animation slowing down the start phase
+ * @param a pointer to an animation
+ * @return the current value to set
+ */
+int32_t lv_anim_path_ease_in(const lv_anim_t * a);
+
+/**
+ * Calculate the current value of an animation slowing down the end phase
+ * @param a pointer to an animation
+ * @return the current value to set
+ */
+int32_t lv_anim_path_ease_out(const lv_anim_t * a);
+
+/**
+ * Calculate the current value of an animation applying an "S" characteristic (cosine)
+ * @param a pointer to an animation
+ * @return the current value to set
+ */
+int32_t lv_anim_path_ease_in_out(const lv_anim_t * a);
+
+/**
+ * Calculate the current value of an animation with overshoot at the end
+ * @param a pointer to an animation
+ * @return the current value to set
+ */
+int32_t lv_anim_path_overshoot(const lv_anim_t * a);
+
+/**
+ * Calculate the current value of an animation with 3 bounces
+ * @param a pointer to an animation
+ * @return the current value to set
+ */
+int32_t lv_anim_path_bounce(const lv_anim_t * a);
+
+/**
+ * Calculate the current value of an animation applying step characteristic.
+ * (Set end value on the end of the animation)
+ * @param a pointer to an animation
+ * @return the current value to set
+ */
+int32_t lv_anim_path_step(const lv_anim_t * a);
+
+/**********************
+ * GLOBAL VARIABLES
+ **********************/
+
+/**********************
+ * MACROS
+ **********************/
+
+#ifdef __cplusplus
+} /*extern "C"*/
+#endif
+
+#endif /*LV_ANIM_H*/
diff --git a/lib/lvgl/src/misc/lv_anim_timeline.c b/lib/lvgl/src/misc/lv_anim_timeline.c
new file mode 100644
index 00000000..08d5321f
--- /dev/null
+++ b/lib/lvgl/src/misc/lv_anim_timeline.c
@@ -0,0 +1,198 @@
+/**
+ * @file lv_anim_timeline.c
+ *
+ */
+
+/*********************
+ * INCLUDES
+ *********************/
+#include "lv_anim_timeline.h"
+#include "lv_mem.h"
+#include "lv_assert.h"
+
+/*********************
+ * DEFINES
+ *********************/
+
+/**********************
+ * TYPEDEFS
+ **********************/
+
+/*Data of anim_timeline_dsc*/
+typedef struct {
+ lv_anim_t anim;
+ uint32_t start_time;
+} lv_anim_timeline_dsc_t;
+
+/*Data of anim_timeline*/
+struct _lv_anim_timeline_t {
+ lv_anim_timeline_dsc_t * anim_dsc; /**< Dynamically allocated anim dsc array*/
+ uint32_t anim_dsc_cnt; /**< The length of anim dsc array*/
+ bool reverse; /**< Reverse playback*/
+};
+
+/**********************
+ * STATIC PROTOTYPES
+ **********************/
+static void lv_anim_timeline_virtual_exec_cb(void * var, int32_t v);
+
+/**********************
+ * STATIC VARIABLES
+ **********************/
+
+/**********************
+ * MACROS
+ **********************/
+
+/**********************
+ * GLOBAL FUNCTIONS
+ **********************/
+
+lv_anim_timeline_t * lv_anim_timeline_create(void)
+{
+ lv_anim_timeline_t * at = (lv_anim_timeline_t *)lv_mem_alloc(sizeof(lv_anim_timeline_t));
+
+ LV_ASSERT_MALLOC(at);
+
+ if(at) lv_memset_00(at, sizeof(lv_anim_timeline_t));
+
+ return at;
+}
+
+void lv_anim_timeline_del(lv_anim_timeline_t * at)
+{
+ LV_ASSERT_NULL(at);
+
+ lv_anim_timeline_stop(at);
+
+ lv_mem_free(at->anim_dsc);
+ lv_mem_free(at);
+}
+
+void lv_anim_timeline_add(lv_anim_timeline_t * at, uint32_t start_time, lv_anim_t * a)
+{
+ LV_ASSERT_NULL(at);
+
+ at->anim_dsc_cnt++;
+ at->anim_dsc = lv_mem_realloc(at->anim_dsc, at->anim_dsc_cnt * sizeof(lv_anim_timeline_dsc_t));
+
+ LV_ASSERT_MALLOC(at->anim_dsc);
+
+ at->anim_dsc[at->anim_dsc_cnt - 1].anim = *a;
+ at->anim_dsc[at->anim_dsc_cnt - 1].start_time = start_time;
+
+ /*Add default var and virtual exec_cb, used to delete animation.*/
+ if(a->var == NULL && a->exec_cb == NULL) {
+ at->anim_dsc[at->anim_dsc_cnt - 1].anim.var = at;
+ at->anim_dsc[at->anim_dsc_cnt - 1].anim.exec_cb = lv_anim_timeline_virtual_exec_cb;
+ }
+}
+
+uint32_t lv_anim_timeline_start(lv_anim_timeline_t * at)
+{
+ LV_ASSERT_NULL(at);
+
+ const uint32_t playtime = lv_anim_timeline_get_playtime(at);
+ bool reverse = at->reverse;
+
+ for(uint32_t i = 0; i < at->anim_dsc_cnt; i++) {
+ lv_anim_t a = at->anim_dsc[i].anim;
+ uint32_t start_time = at->anim_dsc[i].start_time;
+
+ if(reverse) {
+ int32_t temp = a.start_value;
+ a.start_value = a.end_value;
+ a.end_value = temp;
+ lv_anim_set_delay(&a, playtime - (start_time + a.time));
+ }
+ else {
+ lv_anim_set_delay(&a, start_time);
+ }
+
+ lv_anim_start(&a);
+ }
+
+ return playtime;
+}
+
+void lv_anim_timeline_stop(lv_anim_timeline_t * at)
+{
+ LV_ASSERT_NULL(at);
+
+ for(uint32_t i = 0; i < at->anim_dsc_cnt; i++) {
+ lv_anim_t * a = &(at->anim_dsc[i].anim);
+ lv_anim_del(a->var, a->exec_cb);
+ }
+}
+
+void lv_anim_timeline_set_reverse(lv_anim_timeline_t * at, bool reverse)
+{
+ LV_ASSERT_NULL(at);
+ at->reverse = reverse;
+}
+
+void lv_anim_timeline_set_progress(lv_anim_timeline_t * at, uint16_t progress)
+{
+ LV_ASSERT_NULL(at);
+
+ const uint32_t playtime = lv_anim_timeline_get_playtime(at);
+ const uint32_t act_time = progress * playtime / 0xFFFF;
+
+ for(uint32_t i = 0; i < at->anim_dsc_cnt; i++) {
+ lv_anim_t * a = &(at->anim_dsc[i].anim);
+
+ if(a->exec_cb == NULL) {
+ continue;
+ }
+
+ uint32_t start_time = at->anim_dsc[i].start_time;
+ int32_t value = 0;
+
+ if(act_time < start_time) {
+ value = a->start_value;
+ }
+ else if(act_time < (start_time + a->time)) {
+ a->act_time = act_time - start_time;
+ value = a->path_cb(a);
+ }
+ else {
+ value = a->end_value;
+ }
+
+ a->exec_cb(a->var, value);
+ }
+}
+
+uint32_t lv_anim_timeline_get_playtime(lv_anim_timeline_t * at)
+{
+ LV_ASSERT_NULL(at);
+
+ uint32_t playtime = 0;
+ for(uint32_t i = 0; i < at->anim_dsc_cnt; i++) {
+ uint32_t end = lv_anim_get_playtime(&at->anim_dsc[i].anim);
+ if(end == LV_ANIM_PLAYTIME_INFINITE)
+ return end;
+ end += at->anim_dsc[i].start_time;
+ if(end > playtime) {
+ playtime = end;
+ }
+ }
+
+ return playtime;
+}
+
+bool lv_anim_timeline_get_reverse(lv_anim_timeline_t * at)
+{
+ LV_ASSERT_NULL(at);
+ return at->reverse;
+}
+
+/**********************
+ * STATIC FUNCTIONS
+ **********************/
+
+static void lv_anim_timeline_virtual_exec_cb(void * var, int32_t v)
+{
+ LV_UNUSED(var);
+ LV_UNUSED(v);
+}
diff --git a/lib/lvgl/src/misc/lv_anim_timeline.h b/lib/lvgl/src/misc/lv_anim_timeline.h
new file mode 100644
index 00000000..d4dd0fcf
--- /dev/null
+++ b/lib/lvgl/src/misc/lv_anim_timeline.h
@@ -0,0 +1,103 @@
+/**
+ * @file lv_anim_timeline.h
+ *
+ */
+
+#ifndef LV_ANIM_TIMELINE_H
+#define LV_ANIM_TIMELINE_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/*********************
+ * INCLUDES
+ *********************/
+#include "lv_anim.h"
+
+/*********************
+ * DEFINES
+ *********************/
+
+/**********************
+ * TYPEDEFS
+ **********************/
+
+struct _lv_anim_timeline_t;
+
+typedef struct _lv_anim_timeline_t lv_anim_timeline_t;
+
+/**********************
+* GLOBAL PROTOTYPES
+**********************/
+
+/**
+ * Create an animation timeline.
+ * @return pointer to the animation timeline.
+ */
+lv_anim_timeline_t * lv_anim_timeline_create(void);
+
+/**
+ * Delete animation timeline.
+ * @param at pointer to the animation timeline.
+ */
+void lv_anim_timeline_del(lv_anim_timeline_t * at);
+
+/**
+ * Add animation to the animation timeline.
+ * @param at pointer to the animation timeline.
+ * @param start_time the time the animation started on the timeline, note that start_time will override the value of delay.
+ * @param a pointer to an animation.
+ */
+void lv_anim_timeline_add(lv_anim_timeline_t * at, uint32_t start_time, lv_anim_t * a);
+
+/**
+ * Start the animation timeline.
+ * @param at pointer to the animation timeline.
+ * @return total time spent in animation timeline.
+ */
+uint32_t lv_anim_timeline_start(lv_anim_timeline_t * at);
+
+/**
+ * Stop the animation timeline.
+ * @param at pointer to the animation timeline.
+ */
+void lv_anim_timeline_stop(lv_anim_timeline_t * at);
+
+/**
+ * Set the playback direction of the animation timeline.
+ * @param at pointer to the animation timeline.
+ * @param reverse whether to play in reverse.
+ */
+void lv_anim_timeline_set_reverse(lv_anim_timeline_t * at, bool reverse);
+
+/**
+ * Set the progress of the animation timeline.
+ * @param at pointer to the animation timeline.
+ * @param progress set value 0~65535 to map 0~100% animation progress.
+ */
+void lv_anim_timeline_set_progress(lv_anim_timeline_t * at, uint16_t progress);
+
+/**
+ * Get the time used to play the animation timeline.
+ * @param at pointer to the animation timeline.
+ * @return total time spent in animation timeline.
+ */
+uint32_t lv_anim_timeline_get_playtime(lv_anim_timeline_t * at);
+
+/**
+ * Get whether the animation timeline is played in reverse.
+ * @param at pointer to the animation timeline.
+ * @return return true if it is reverse playback.
+ */
+bool lv_anim_timeline_get_reverse(lv_anim_timeline_t * at);
+
+/**********************
+ * MACROS
+ **********************/
+
+#ifdef __cplusplus
+} /*extern "C"*/
+#endif
+
+#endif /*LV_ANIM_TIMELINE_H*/
diff --git a/lib/lvgl/src/misc/lv_area.c b/lib/lvgl/src/misc/lv_area.c
new file mode 100644
index 00000000..c0221f7e
--- /dev/null
+++ b/lib/lvgl/src/misc/lv_area.c
@@ -0,0 +1,535 @@
+/**
+ * @file lv_area.c
+ *
+ */
+
+/*********************
+ * INCLUDES
+ *********************/
+#include "../lv_conf_internal.h"
+
+#include "lv_area.h"
+#include "lv_math.h"
+
+/*********************
+ * DEFINES
+ *********************/
+
+/**********************
+ * TYPEDEFS
+ **********************/
+
+/**********************
+ * STATIC PROTOTYPES
+ **********************/
+
+static bool lv_point_within_circle(const lv_area_t * area, const lv_point_t * p);
+
+/**********************
+ * STATIC VARIABLES
+ **********************/
+
+/**********************
+ * MACROS
+ **********************/
+
+/**********************
+ * GLOBAL FUNCTIONS
+ **********************/
+
+/**
+ * Initialize an area
+ * @param area_p pointer to an area
+ * @param x1 left coordinate of the area
+ * @param y1 top coordinate of the area
+ * @param x2 right coordinate of the area
+ * @param y2 bottom coordinate of the area
+ */
+void lv_area_set(lv_area_t * area_p, lv_coord_t x1, lv_coord_t y1, lv_coord_t x2, lv_coord_t y2)
+{
+ area_p->x1 = x1;
+ area_p->y1 = y1;
+ area_p->x2 = x2;
+ area_p->y2 = y2;
+}
+
+/**
+ * Set the width of an area
+ * @param area_p pointer to an area
+ * @param w the new width of the area (w == 1 makes x1 == x2)
+ */
+void lv_area_set_width(lv_area_t * area_p, lv_coord_t w)
+{
+ area_p->x2 = area_p->x1 + w - 1;
+}
+
+/**
+ * Set the height of an area
+ * @param area_p pointer to an area
+ * @param h the new height of the area (h == 1 makes y1 == y2)
+ */
+void lv_area_set_height(lv_area_t * area_p, lv_coord_t h)
+{
+ area_p->y2 = area_p->y1 + h - 1;
+}
+
+/**
+ * Set the position of an area (width and height will be kept)
+ * @param area_p pointer to an area
+ * @param x the new x coordinate of the area
+ * @param y the new y coordinate of the area
+ */
+void _lv_area_set_pos(lv_area_t * area_p, lv_coord_t x, lv_coord_t y)
+{
+ lv_coord_t w = lv_area_get_width(area_p);
+ lv_coord_t h = lv_area_get_height(area_p);
+ area_p->x1 = x;
+ area_p->y1 = y;
+ lv_area_set_width(area_p, w);
+ lv_area_set_height(area_p, h);
+}
+
+/**
+ * Return with area of an area (x * y)
+ * @param area_p pointer to an area
+ * @return size of area
+ */
+uint32_t lv_area_get_size(const lv_area_t * area_p)
+{
+ uint32_t size;
+
+ size = (uint32_t)(area_p->x2 - area_p->x1 + 1) * (area_p->y2 - area_p->y1 + 1);
+
+ return size;
+}
+
+void lv_area_increase(lv_area_t * area, lv_coord_t w_extra, lv_coord_t h_extra)
+{
+ area->x1 -= w_extra;
+ area->x2 += w_extra;
+ area->y1 -= h_extra;
+ area->y2 += h_extra;
+}
+
+void lv_area_move(lv_area_t * area, lv_coord_t x_ofs, lv_coord_t y_ofs)
+{
+ area->x1 += x_ofs;
+ area->x2 += x_ofs;
+ area->y1 += y_ofs;
+ area->y2 += y_ofs;
+}
+
+/**
+ * Get the common parts of two areas
+ * @param res_p pointer to an area, the result will be stored here
+ * @param a1_p pointer to the first area
+ * @param a2_p pointer to the second area
+ * @return false: the two area has NO common parts, res_p is invalid
+ */
+bool _lv_area_intersect(lv_area_t * res_p, const lv_area_t * a1_p, const lv_area_t * a2_p)
+{
+ /*Get the smaller area from 'a1_p' and 'a2_p'*/
+ res_p->x1 = LV_MAX(a1_p->x1, a2_p->x1);
+ res_p->y1 = LV_MAX(a1_p->y1, a2_p->y1);
+ res_p->x2 = LV_MIN(a1_p->x2, a2_p->x2);
+ res_p->y2 = LV_MIN(a1_p->y2, a2_p->y2);
+
+ /*If x1 or y1 greater than x2 or y2 then the areas union is empty*/
+ bool union_ok = true;
+ if((res_p->x1 > res_p->x2) || (res_p->y1 > res_p->y2)) {
+ union_ok = false;
+ }
+
+ return union_ok;
+}
+
+/**
+ * Join two areas into a third which involves the other two
+ * @param res_p pointer to an area, the result will be stored here
+ * @param a1_p pointer to the first area
+ * @param a2_p pointer to the second area
+ */
+void _lv_area_join(lv_area_t * a_res_p, const lv_area_t * a1_p, const lv_area_t * a2_p)
+{
+ a_res_p->x1 = LV_MIN(a1_p->x1, a2_p->x1);
+ a_res_p->y1 = LV_MIN(a1_p->y1, a2_p->y1);
+ a_res_p->x2 = LV_MAX(a1_p->x2, a2_p->x2);
+ a_res_p->y2 = LV_MAX(a1_p->y2, a2_p->y2);
+}
+
+/**
+ * Check if a point is on an area
+ * @param a_p pointer to an area
+ * @param p_p pointer to a point
+ * @param radius radius of area (e.g. for rounded rectangle)
+ * @return false:the point is out of the area
+ */
+bool _lv_area_is_point_on(const lv_area_t * a_p, const lv_point_t * p_p, lv_coord_t radius)
+{
+ /*First check the basic area*/
+ bool is_on_rect = false;
+ if((p_p->x >= a_p->x1 && p_p->x <= a_p->x2) && ((p_p->y >= a_p->y1 && p_p->y <= a_p->y2))) {
+ is_on_rect = true;
+ }
+ if(!is_on_rect)
+ return false;
+ /*Now handle potential rounded rectangles*/
+ if(radius <= 0) {
+ /*No radius, it is within the rectangle*/
+ return true;
+ }
+ lv_coord_t w = lv_area_get_width(a_p) / 2;
+ lv_coord_t h = lv_area_get_height(a_p) / 2;
+ lv_coord_t max_radius = LV_MIN(w, h);
+ if(radius > max_radius)
+ radius = max_radius;
+
+ /*Check if it's in one of the corners*/
+ lv_area_t corner_area;
+ /*Top left*/
+ corner_area.x1 = a_p->x1;
+ corner_area.x2 = a_p->x1 + radius;
+ corner_area.y1 = a_p->y1;
+ corner_area.y2 = a_p->y1 + radius;
+ if(_lv_area_is_point_on(&corner_area, p_p, 0)) {
+ corner_area.x2 += radius;
+ corner_area.y2 += radius;
+ return lv_point_within_circle(&corner_area, p_p);
+ }
+ /*Bottom left*/
+ corner_area.y1 = a_p->y2 - radius;
+ corner_area.y2 = a_p->y2;
+ if(_lv_area_is_point_on(&corner_area, p_p, 0)) {
+ corner_area.x2 += radius;
+ corner_area.y1 -= radius;
+ return lv_point_within_circle(&corner_area, p_p);
+ }
+ /*Bottom right*/
+ corner_area.x1 = a_p->x2 - radius;
+ corner_area.x2 = a_p->x2;
+ if(_lv_area_is_point_on(&corner_area, p_p, 0)) {
+ corner_area.x1 -= radius;
+ corner_area.y1 -= radius;
+ return lv_point_within_circle(&corner_area, p_p);
+ }
+ /*Top right*/
+ corner_area.y1 = a_p->y1;
+ corner_area.y2 = a_p->y1 + radius;
+ if(_lv_area_is_point_on(&corner_area, p_p, 0)) {
+ corner_area.x1 -= radius;
+ corner_area.y2 += radius;
+ return lv_point_within_circle(&corner_area, p_p);
+ }
+ /*Not within corners*/
+ return true;
+}
+
+/**
+ * Check if two area has common parts
+ * @param a1_p pointer to an area.
+ * @param a2_p pointer to an other area
+ * @return false: a1_p and a2_p has no common parts
+ */
+bool _lv_area_is_on(const lv_area_t * a1_p, const lv_area_t * a2_p)
+{
+ if((a1_p->x1 <= a2_p->x2) && (a1_p->x2 >= a2_p->x1) && (a1_p->y1 <= a2_p->y2) && (a1_p->y2 >= a2_p->y1)) {
+ return true;
+ }
+ else {
+ return false;
+ }
+}
+
+/**
+ * Check if an area is fully on an other
+ * @param ain_p pointer to an area which could be in 'aholder_p'
+ * @param aholder_p pointer to an area which could involve 'ain_p'
+ * @param radius radius of `aholder_p` (e.g. for rounded rectangle)
+ * @return true: `ain_p` is fully inside `aholder_p`
+ */
+bool _lv_area_is_in(const lv_area_t * ain_p, const lv_area_t * aholder_p, lv_coord_t radius)
+{
+ bool is_in = false;
+
+ if(ain_p->x1 >= aholder_p->x1 && ain_p->y1 >= aholder_p->y1 && ain_p->x2 <= aholder_p->x2 &&
+ ain_p->y2 <= aholder_p->y2) {
+ is_in = true;
+ }
+
+ if(!is_in) return false;
+ if(radius == 0) return true;
+
+ /*Check if the corner points are inside the radius or not*/
+ lv_point_t p;
+
+ p.x = ain_p->x1;
+ p.y = ain_p->y1;
+ if(_lv_area_is_point_on(aholder_p, &p, radius) == false) return false;
+
+ p.x = ain_p->x2;
+ p.y = ain_p->y1;
+ if(_lv_area_is_point_on(aholder_p, &p, radius) == false) return false;
+
+ p.x = ain_p->x1;
+ p.y = ain_p->y2;
+ if(_lv_area_is_point_on(aholder_p, &p, radius) == false) return false;
+
+ p.x = ain_p->x2;
+ p.y = ain_p->y2;
+ if(_lv_area_is_point_on(aholder_p, &p, radius) == false) return false;
+
+ return true;
+}
+
+/**
+ * Check if an area is fully out of an other
+ * @param aout_p pointer to an area which could be in 'aholder_p'
+ * @param aholder_p pointer to an area which could involve 'ain_p'
+ * @param radius radius of `aholder_p` (e.g. for rounded rectangle)
+ * @return true: `aout_p` is fully outside `aholder_p`
+ */
+bool _lv_area_is_out(const lv_area_t * aout_p, const lv_area_t * aholder_p, lv_coord_t radius)
+{
+ if(aout_p->x2 < aholder_p->x1 || aout_p->y2 < aholder_p->y1 || aout_p->x1 > aholder_p->x2 ||
+ aout_p->y1 > aholder_p->y2) {
+ return true;
+ }
+
+ if(radius == 0) return false;
+
+ /*Check if the corner points are outside the radius or not*/
+ lv_point_t p;
+
+ p.x = aout_p->x1;
+ p.y = aout_p->y1;
+ if(_lv_area_is_point_on(aholder_p, &p, radius)) return false;
+
+ p.x = aout_p->x2;
+ p.y = aout_p->y1;
+ if(_lv_area_is_point_on(aholder_p, &p, radius)) return false;
+
+ p.x = aout_p->x1;
+ p.y = aout_p->y2;
+ if(_lv_area_is_point_on(aholder_p, &p, radius)) return false;
+
+ p.x = aout_p->x2;
+ p.y = aout_p->y2;
+ if(_lv_area_is_point_on(aholder_p, &p, radius)) return false;
+
+ return true;
+}
+
+bool _lv_area_is_equal(const lv_area_t * a, const lv_area_t * b)
+{
+ return a->x1 == b->x1 && a->x2 == b->x2 && a->y1 == b->y1 && a->y2 == b->y2;
+}
+
+/**
+ * Align an area to an other
+ * @param base an are where the other will be aligned
+ * @param to_align the area to align
+ * @param align `LV_ALIGN_...`
+ * @param res x/y coordinates where `to_align` align area should be placed
+ */
+void lv_area_align(const lv_area_t * base, lv_area_t * to_align, lv_align_t align, lv_coord_t ofs_x, lv_coord_t ofs_y)
+{
+
+ lv_coord_t x;
+ lv_coord_t y;
+ switch(align) {
+ case LV_ALIGN_CENTER:
+ x = lv_area_get_width(base) / 2 - lv_area_get_width(to_align) / 2;
+ y = lv_area_get_height(base) / 2 - lv_area_get_height(to_align) / 2;
+ break;
+
+ case LV_ALIGN_TOP_LEFT:
+ x = 0;
+ y = 0;
+ break;
+ case LV_ALIGN_TOP_MID:
+ x = lv_area_get_width(base) / 2 - lv_area_get_width(to_align) / 2;
+ y = 0;
+ break;
+
+ case LV_ALIGN_TOP_RIGHT:
+ x = lv_area_get_width(base) - lv_area_get_width(to_align);
+ y = 0;
+ break;
+
+ case LV_ALIGN_BOTTOM_LEFT:
+ x = 0;
+ y = lv_area_get_height(base) - lv_area_get_height(to_align);
+ break;
+ case LV_ALIGN_BOTTOM_MID:
+ x = lv_area_get_width(base) / 2 - lv_area_get_width(to_align) / 2;
+ y = lv_area_get_height(base) - lv_area_get_height(to_align);
+ break;
+
+ case LV_ALIGN_BOTTOM_RIGHT:
+ x = lv_area_get_width(base) - lv_area_get_width(to_align);
+ y = lv_area_get_height(base) - lv_area_get_height(to_align);
+ break;
+
+ case LV_ALIGN_LEFT_MID:
+ x = 0;
+ y = lv_area_get_height(base) / 2 - lv_area_get_height(to_align) / 2;
+ break;
+
+ case LV_ALIGN_RIGHT_MID:
+ x = lv_area_get_width(base) - lv_area_get_width(to_align);
+ y = lv_area_get_height(base) / 2 - lv_area_get_height(to_align) / 2;
+ break;
+
+ case LV_ALIGN_OUT_TOP_LEFT:
+ x = 0;
+ y = -lv_area_get_height(to_align);
+ break;
+
+ case LV_ALIGN_OUT_TOP_MID:
+ x = lv_area_get_width(base) / 2 - lv_area_get_width(to_align) / 2;
+ y = -lv_area_get_height(to_align);
+ break;
+
+ case LV_ALIGN_OUT_TOP_RIGHT:
+ x = lv_area_get_width(base) - lv_area_get_width(to_align);
+ y = -lv_area_get_height(to_align);
+ break;
+
+ case LV_ALIGN_OUT_BOTTOM_LEFT:
+ x = 0;
+ y = lv_area_get_height(base);
+ break;
+
+ case LV_ALIGN_OUT_BOTTOM_MID:
+ x = lv_area_get_width(base) / 2 - lv_area_get_width(to_align) / 2;
+ y = lv_area_get_height(base);
+ break;
+
+ case LV_ALIGN_OUT_BOTTOM_RIGHT:
+ x = lv_area_get_width(base) - lv_area_get_width(to_align);
+ y = lv_area_get_height(base);
+ break;
+
+ case LV_ALIGN_OUT_LEFT_TOP:
+ x = -lv_area_get_width(to_align);
+ y = 0;
+ break;
+
+ case LV_ALIGN_OUT_LEFT_MID:
+ x = -lv_area_get_width(to_align);
+ y = lv_area_get_height(base) / 2 - lv_area_get_height(to_align) / 2;
+ break;
+
+ case LV_ALIGN_OUT_LEFT_BOTTOM:
+ x = -lv_area_get_width(to_align);
+ y = lv_area_get_height(base) - lv_area_get_height(to_align);
+ break;
+
+ case LV_ALIGN_OUT_RIGHT_TOP:
+ x = lv_area_get_width(base);
+ y = 0;
+ break;
+
+ case LV_ALIGN_OUT_RIGHT_MID:
+ x = lv_area_get_width(base);
+ y = lv_area_get_height(base) / 2 - lv_area_get_height(to_align) / 2;
+ break;
+
+ case LV_ALIGN_OUT_RIGHT_BOTTOM:
+ x = lv_area_get_width(base);
+ y = lv_area_get_height(base) - lv_area_get_height(to_align);
+ break;
+ default:
+ x = 0;
+ y = 0;
+ break;
+ }
+
+ x += base->x1;
+ y += base->y1;
+
+ lv_coord_t w = lv_area_get_width(to_align);
+ lv_coord_t h = lv_area_get_height(to_align);
+ to_align->x1 = x + ofs_x;
+ to_align->y1 = y + ofs_y;
+ to_align->x2 = to_align->x1 + w - 1;
+ to_align->y2 = to_align->y1 + h - 1;
+}
+
+#define _LV_TRANSFORM_TRIGO_SHIFT 10
+void lv_point_transform(lv_point_t * p, int32_t angle, int32_t zoom, const lv_point_t * pivot)
+{
+ if(angle == 0 && zoom == 256) {
+ return;
+ }
+
+ p->x -= pivot->x;
+ p->y -= pivot->y;
+
+ if(angle == 0) {
+ p->x = (((int32_t)(p->x) * zoom) >> 8) + pivot->x;
+ p->y = (((int32_t)(p->y) * zoom) >> 8) + pivot->y;
+ return;
+ }
+
+ static int32_t angle_prev = INT32_MIN;
+ static int32_t sinma;
+ static int32_t cosma;
+ if(angle_prev != angle) {
+ int32_t angle_limited = angle;
+ if(angle_limited > 3600) angle_limited -= 3600;
+ if(angle_limited < 0) angle_limited += 3600;
+
+ int32_t angle_low = angle_limited / 10;
+ int32_t angle_high = angle_low + 1;
+ int32_t angle_rem = angle_limited - (angle_low * 10);
+
+ int32_t s1 = lv_trigo_sin(angle_low);
+ int32_t s2 = lv_trigo_sin(angle_high);
+
+ int32_t c1 = lv_trigo_sin(angle_low + 90);
+ int32_t c2 = lv_trigo_sin(angle_high + 90);
+
+ sinma = (s1 * (10 - angle_rem) + s2 * angle_rem) / 10;
+ cosma = (c1 * (10 - angle_rem) + c2 * angle_rem) / 10;
+ sinma = sinma >> (LV_TRIGO_SHIFT - _LV_TRANSFORM_TRIGO_SHIFT);
+ cosma = cosma >> (LV_TRIGO_SHIFT - _LV_TRANSFORM_TRIGO_SHIFT);
+ angle_prev = angle;
+ }
+ int32_t x = p->x;
+ int32_t y = p->y;
+ if(zoom == 256) {
+ p->x = ((cosma * x - sinma * y) >> _LV_TRANSFORM_TRIGO_SHIFT) + pivot->x;
+ p->y = ((sinma * x + cosma * y) >> _LV_TRANSFORM_TRIGO_SHIFT) + pivot->y;
+ }
+ else {
+ p->x = (((cosma * x - sinma * y) * zoom) >> (_LV_TRANSFORM_TRIGO_SHIFT + 8)) + pivot->x;
+ p->y = (((sinma * x + cosma * y) * zoom) >> (_LV_TRANSFORM_TRIGO_SHIFT + 8)) + pivot->y;
+ }
+}
+
+
+/**********************
+ * STATIC FUNCTIONS
+ **********************/
+
+static bool lv_point_within_circle(const lv_area_t * area, const lv_point_t * p)
+{
+ lv_coord_t r = (area->x2 - area->x1) / 2;
+
+ /*Circle center*/
+ lv_coord_t cx = area->x1 + r;
+ lv_coord_t cy = area->y1 + r;
+
+ /*Simplify the code by moving everything to (0, 0)*/
+ lv_coord_t px = p->x - cx;
+ lv_coord_t py = p->y - cy;
+
+ uint32_t r_sqrd = r * r;
+ uint32_t dist = (px * px) + (py * py);
+
+ if(dist <= r_sqrd)
+ return true;
+ else
+ return false;
+}
diff --git a/lib/lvgl/src/misc/lv_area.h b/lib/lvgl/src/misc/lv_area.h
new file mode 100644
index 00000000..137931a2
--- /dev/null
+++ b/lib/lvgl/src/misc/lv_area.h
@@ -0,0 +1,295 @@
+/**
+ * @file lv_area.h
+ *
+ */
+
+#ifndef LV_AREA_H
+#define LV_AREA_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/*********************
+ * INCLUDES
+ *********************/
+#include "../lv_conf_internal.h"
+#include <stdbool.h>
+#include <stdint.h>
+
+/*********************
+ * DEFINES
+ *********************/
+
+#if LV_USE_LARGE_COORD
+typedef int32_t lv_coord_t;
+#else
+typedef int16_t lv_coord_t;
+#endif
+
+/**********************
+ * TYPEDEFS
+ **********************/
+
+/**
+ * Represents a point on the screen.
+ */
+typedef struct {
+ lv_coord_t x;
+ lv_coord_t y;
+} lv_point_t;
+
+/** Represents an area of the screen.*/
+typedef struct {
+ lv_coord_t x1;
+ lv_coord_t y1;
+ lv_coord_t x2;
+ lv_coord_t y2;
+} lv_area_t;
+
+/** Alignments*/
+enum {
+ LV_ALIGN_DEFAULT = 0,
+ LV_ALIGN_TOP_LEFT,
+ LV_ALIGN_TOP_MID,
+ LV_ALIGN_TOP_RIGHT,
+ LV_ALIGN_BOTTOM_LEFT,
+ LV_ALIGN_BOTTOM_MID,
+ LV_ALIGN_BOTTOM_RIGHT,
+ LV_ALIGN_LEFT_MID,
+ LV_ALIGN_RIGHT_MID,
+ LV_ALIGN_CENTER,
+
+ LV_ALIGN_OUT_TOP_LEFT,
+ LV_ALIGN_OUT_TOP_MID,
+ LV_ALIGN_OUT_TOP_RIGHT,
+ LV_ALIGN_OUT_BOTTOM_LEFT,
+ LV_ALIGN_OUT_BOTTOM_MID,
+ LV_ALIGN_OUT_BOTTOM_RIGHT,
+ LV_ALIGN_OUT_LEFT_TOP,
+ LV_ALIGN_OUT_LEFT_MID,
+ LV_ALIGN_OUT_LEFT_BOTTOM,
+ LV_ALIGN_OUT_RIGHT_TOP,
+ LV_ALIGN_OUT_RIGHT_MID,
+ LV_ALIGN_OUT_RIGHT_BOTTOM,
+};
+typedef uint8_t lv_align_t;
+
+enum {
+ LV_DIR_NONE = 0x00,
+ LV_DIR_LEFT = (1 << 0),
+ LV_DIR_RIGHT = (1 << 1),
+ LV_DIR_TOP = (1 << 2),
+ LV_DIR_BOTTOM = (1 << 3),
+ LV_DIR_HOR = LV_DIR_LEFT | LV_DIR_RIGHT,
+ LV_DIR_VER = LV_DIR_TOP | LV_DIR_BOTTOM,
+ LV_DIR_ALL = LV_DIR_HOR | LV_DIR_VER,
+};
+
+typedef uint8_t lv_dir_t;
+
+/**********************
+ * GLOBAL PROTOTYPES
+ **********************/
+
+/**
+ * Initialize an area
+ * @param area_p pointer to an area
+ * @param x1 left coordinate of the area
+ * @param y1 top coordinate of the area
+ * @param x2 right coordinate of the area
+ * @param y2 bottom coordinate of the area
+ */
+void lv_area_set(lv_area_t * area_p, lv_coord_t x1, lv_coord_t y1, lv_coord_t x2, lv_coord_t y2);
+
+/**
+ * Copy an area
+ * @param dest pointer to the destination area
+ * @param src pointer to the source area
+ */
+inline static void lv_area_copy(lv_area_t * dest, const lv_area_t * src)
+{
+ dest->x1 = src->x1;
+ dest->y1 = src->y1;
+ dest->x2 = src->x2;
+ dest->y2 = src->y2;
+}
+
+/**
+ * Get the width of an area
+ * @param area_p pointer to an area
+ * @return the width of the area (if x1 == x2 -> width = 1)
+ */
+static inline lv_coord_t lv_area_get_width(const lv_area_t * area_p)
+{
+ return (lv_coord_t)(area_p->x2 - area_p->x1 + 1);
+}
+
+/**
+ * Get the height of an area
+ * @param area_p pointer to an area
+ * @return the height of the area (if y1 == y2 -> height = 1)
+ */
+static inline lv_coord_t lv_area_get_height(const lv_area_t * area_p)
+{
+ return (lv_coord_t)(area_p->y2 - area_p->y1 + 1);
+}
+
+/**
+ * Set the width of an area
+ * @param area_p pointer to an area
+ * @param w the new width of the area (w == 1 makes x1 == x2)
+ */
+void lv_area_set_width(lv_area_t * area_p, lv_coord_t w);
+
+/**
+ * Set the height of an area
+ * @param area_p pointer to an area
+ * @param h the new height of the area (h == 1 makes y1 == y2)
+ */
+void lv_area_set_height(lv_area_t * area_p, lv_coord_t h);
+
+/**
+ * Set the position of an area (width and height will be kept)
+ * @param area_p pointer to an area
+ * @param x the new x coordinate of the area
+ * @param y the new y coordinate of the area
+ */
+void _lv_area_set_pos(lv_area_t * area_p, lv_coord_t x, lv_coord_t y);
+
+/**
+ * Return with area of an area (x * y)
+ * @param area_p pointer to an area
+ * @return size of area
+ */
+uint32_t lv_area_get_size(const lv_area_t * area_p);
+
+void lv_area_increase(lv_area_t * area, lv_coord_t w_extra, lv_coord_t h_extra);
+
+void lv_area_move(lv_area_t * area, lv_coord_t x_ofs, lv_coord_t y_ofs);
+
+/**
+ * Get the common parts of two areas
+ * @param res_p pointer to an area, the result will be stored her
+ * @param a1_p pointer to the first area
+ * @param a2_p pointer to the second area
+ * @return false: the two area has NO common parts, res_p is invalid
+ */
+bool _lv_area_intersect(lv_area_t * res_p, const lv_area_t * a1_p, const lv_area_t * a2_p);
+
+/**
+ * Join two areas into a third which involves the other two
+ * @param res_p pointer to an area, the result will be stored here
+ * @param a1_p pointer to the first area
+ * @param a2_p pointer to the second area
+ */
+void _lv_area_join(lv_area_t * a_res_p, const lv_area_t * a1_p, const lv_area_t * a2_p);
+
+/**
+ * Check if a point is on an area
+ * @param a_p pointer to an area
+ * @param p_p pointer to a point
+ * @param radius radius of area (e.g. for rounded rectangle)
+ * @return false:the point is out of the area
+ */
+bool _lv_area_is_point_on(const lv_area_t * a_p, const lv_point_t * p_p, lv_coord_t radius);
+
+/**
+ * Check if two area has common parts
+ * @param a1_p pointer to an area.
+ * @param a2_p pointer to an other area
+ * @return false: a1_p and a2_p has no common parts
+ */
+bool _lv_area_is_on(const lv_area_t * a1_p, const lv_area_t * a2_p);
+
+/**
+ * Check if an area is fully on an other
+ * @param ain_p pointer to an area which could be in 'aholder_p'
+ * @param aholder_p pointer to an area which could involve 'ain_p'
+ * @param radius radius of `aholder_p` (e.g. for rounded rectangle)
+ * @return true: `ain_p` is fully inside `aholder_p`
+ */
+bool _lv_area_is_in(const lv_area_t * ain_p, const lv_area_t * aholder_p, lv_coord_t radius);
+
+
+/**
+ * Check if an area is fully out of an other
+ * @param aout_p pointer to an area which could be in 'aholder_p'
+ * @param aholder_p pointer to an area which could involve 'ain_p'
+ * @param radius radius of `aholder_p` (e.g. for rounded rectangle)
+ * @return true: `aout_p` is fully outside `aholder_p`
+ */
+bool _lv_area_is_out(const lv_area_t * aout_p, const lv_area_t * aholder_p, lv_coord_t radius);
+
+/**
+ * Check if 2 area is the same
+ * @param a pointer to an area
+ * @param b pointer to another area
+ */
+bool _lv_area_is_equal(const lv_area_t * a, const lv_area_t * b);
+
+/**
+ * Align an area to an other
+ * @param base an are where the other will be aligned
+ * @param to_align the area to align
+ * @param align `LV_ALIGN_...`
+ */
+void lv_area_align(const lv_area_t * base, lv_area_t * to_align, lv_align_t align, lv_coord_t ofs_x, lv_coord_t ofs_y);
+
+void lv_point_transform(lv_point_t * p, int32_t angle, int32_t zoom, const lv_point_t * pivot);
+
+/**********************
+ * MACROS
+ **********************/
+
+#if LV_USE_LARGE_COORD
+#define _LV_COORD_TYPE_SHIFT (29U)
+#else
+#define _LV_COORD_TYPE_SHIFT (13U)
+#endif
+
+#define _LV_COORD_TYPE_MASK (3 << _LV_COORD_TYPE_SHIFT)
+#define _LV_COORD_TYPE(x) ((x) & _LV_COORD_TYPE_MASK) /*Extract type specifiers*/
+#define _LV_COORD_PLAIN(x) ((x) & ~_LV_COORD_TYPE_MASK) /*Remove type specifiers*/
+
+#define _LV_COORD_TYPE_PX (0 << _LV_COORD_TYPE_SHIFT)
+#define _LV_COORD_TYPE_SPEC (1 << _LV_COORD_TYPE_SHIFT)
+#define _LV_COORD_TYPE_PX_NEG (3 << _LV_COORD_TYPE_SHIFT)
+
+#define LV_COORD_IS_PX(x) (_LV_COORD_TYPE(x) == _LV_COORD_TYPE_PX || \
+ _LV_COORD_TYPE(x) == _LV_COORD_TYPE_PX_NEG ? true : false)
+#define LV_COORD_IS_SPEC(x) (_LV_COORD_TYPE(x) == _LV_COORD_TYPE_SPEC ? true : false)
+
+#define LV_COORD_SET_SPEC(x) ((x) | _LV_COORD_TYPE_SPEC)
+
+/*Special coordinates*/
+#define LV_PCT(x) (x < 0 ? LV_COORD_SET_SPEC(1000 - (x)) : LV_COORD_SET_SPEC(x))
+#define LV_COORD_IS_PCT(x) ((LV_COORD_IS_SPEC(x) && _LV_COORD_PLAIN(x) <= 2000) ? true : false)
+#define LV_COORD_GET_PCT(x) (_LV_COORD_PLAIN(x) > 1000 ? 1000 - _LV_COORD_PLAIN(x) : _LV_COORD_PLAIN(x))
+#define LV_SIZE_CONTENT LV_COORD_SET_SPEC(2001)
+
+LV_EXPORT_CONST_INT(LV_SIZE_CONTENT);
+
+/*Max coordinate value*/
+#define LV_COORD_MAX ((1 << _LV_COORD_TYPE_SHIFT) - 1)
+#define LV_COORD_MIN (-LV_COORD_MAX)
+
+LV_EXPORT_CONST_INT(LV_COORD_MAX);
+LV_EXPORT_CONST_INT(LV_COORD_MIN);
+
+/**
+ * Convert a percentage value to `lv_coord_t`.
+ * Percentage values are stored in special range
+ * @param x the percentage (0..1000)
+ * @return a coordinate that stores the percentage
+ */
+static inline lv_coord_t lv_pct(lv_coord_t x)
+{
+ return LV_PCT(x);
+}
+
+#ifdef __cplusplus
+} /*extern "C"*/
+#endif
+
+#endif
diff --git a/lib/lvgl/src/misc/lv_assert.h b/lib/lvgl/src/misc/lv_assert.h
new file mode 100644
index 00000000..48db7443
--- /dev/null
+++ b/lib/lvgl/src/misc/lv_assert.h
@@ -0,0 +1,79 @@
+/**
+ * @file lv_assert.h
+ *
+ */
+
+#ifndef LV_ASSERT_H
+#define LV_ASSERT_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/*********************
+ * INCLUDES
+ *********************/
+#include "../lv_conf_internal.h"
+#include "lv_log.h"
+#include "lv_mem.h"
+#include LV_ASSERT_HANDLER_INCLUDE
+
+/*********************
+ * DEFINES
+ *********************/
+
+/**********************
+ * TYPEDEFS
+ **********************/
+
+/**********************
+ * GLOBAL PROTOTYPES
+ **********************/
+
+/**********************
+ * MACROS
+ **********************/
+
+#define LV_ASSERT(expr) \
+ do { \
+ if(!(expr)) { \
+ LV_LOG_ERROR("Asserted at expression: %s", #expr); \
+ LV_ASSERT_HANDLER \
+ } \
+ } while(0)
+
+#define LV_ASSERT_MSG(expr, msg) \
+ do { \
+ if(!(expr)) { \
+ LV_LOG_ERROR("Asserted at expression: %s (%s)", #expr, msg); \
+ LV_ASSERT_HANDLER \
+ } \
+ } while(0)
+
+/*-----------------
+ * ASSERTS
+ *-----------------*/
+
+#if LV_USE_ASSERT_NULL
+# define LV_ASSERT_NULL(p) LV_ASSERT_MSG(p != NULL, "NULL pointer");
+#else
+# define LV_ASSERT_NULL(p)
+#endif
+
+#if LV_USE_ASSERT_MALLOC
+# define LV_ASSERT_MALLOC(p) LV_ASSERT_MSG(p != NULL, "Out of memory");
+#else
+# define LV_ASSERT_MALLOC(p)
+#endif
+
+#if LV_USE_ASSERT_MEM_INTEGRITY
+# define LV_ASSERT_MEM_INTEGRITY() LV_ASSERT_MSG(lv_mem_test() == LV_RES_OK, "Memory integrity error");
+#else
+# define LV_ASSERT_MEM_INTEGRITY()
+#endif
+
+#ifdef __cplusplus
+} /*extern "C"*/
+#endif
+
+#endif /*LV_ASSERT_H*/
diff --git a/lib/lvgl/src/misc/lv_async.c b/lib/lvgl/src/misc/lv_async.c
new file mode 100644
index 00000000..c4941e81
--- /dev/null
+++ b/lib/lvgl/src/misc/lv_async.c
@@ -0,0 +1,105 @@
+/**
+ * @file lv_async.c
+ *
+ */
+
+/*********************
+ * INCLUDES
+ *********************/
+
+#include "lv_async.h"
+#include "lv_mem.h"
+#include "lv_timer.h"
+
+/*********************
+ * DEFINES
+ *********************/
+
+/**********************
+ * TYPEDEFS
+ **********************/
+
+typedef struct _lv_async_info_t {
+ lv_async_cb_t cb;
+ void * user_data;
+} lv_async_info_t;
+
+/**********************
+ * STATIC PROTOTYPES
+ **********************/
+
+static void lv_async_timer_cb(lv_timer_t * timer);
+
+/**********************
+ * STATIC VARIABLES
+ **********************/
+
+/**********************
+ * MACROS
+ **********************/
+
+/**********************
+ * GLOBAL FUNCTIONS
+ **********************/
+
+lv_res_t lv_async_call(lv_async_cb_t async_xcb, void * user_data)
+{
+ /*Allocate an info structure*/
+ lv_async_info_t * info = lv_mem_alloc(sizeof(lv_async_info_t));
+
+ if(info == NULL)
+ return LV_RES_INV;
+
+ /*Create a new timer*/
+ lv_timer_t * timer = lv_timer_create(lv_async_timer_cb, 0, info);
+
+ if(timer == NULL) {
+ lv_mem_free(info);
+ return LV_RES_INV;
+ }
+
+ info->cb = async_xcb;
+ info->user_data = user_data;
+
+ lv_timer_set_repeat_count(timer, 1);
+ return LV_RES_OK;
+}
+
+lv_res_t lv_async_call_cancel(lv_async_cb_t async_xcb, void * user_data)
+{
+ lv_timer_t * timer = lv_timer_get_next(NULL);
+ lv_res_t res = LV_RES_INV;
+
+ while(timer != NULL) {
+ /*Find the next timer node*/
+ lv_timer_t * timer_next = lv_timer_get_next(timer);
+
+ /*Find async timer callback*/
+ if(timer->timer_cb == lv_async_timer_cb) {
+ lv_async_info_t * info = (lv_async_info_t *)timer->user_data;
+
+ /*Match user function callback and user data*/
+ if(info->cb == async_xcb && info->user_data == user_data) {
+ lv_timer_del(timer);
+ lv_mem_free(info);
+ res = LV_RES_OK;
+ }
+ }
+
+ timer = timer_next;
+ }
+
+ return res;
+}
+
+/**********************
+ * STATIC FUNCTIONS
+ **********************/
+
+static void lv_async_timer_cb(lv_timer_t * timer)
+{
+ lv_async_info_t * info = (lv_async_info_t *)timer->user_data;
+
+ info->cb(info->user_data);
+ lv_mem_free(info);
+}
diff --git a/lib/lvgl/src/misc/lv_async.h b/lib/lvgl/src/misc/lv_async.h
new file mode 100644
index 00000000..4ad5756d
--- /dev/null
+++ b/lib/lvgl/src/misc/lv_async.h
@@ -0,0 +1,61 @@
+/**
+ * @file lv_async.h
+ *
+ */
+
+#ifndef LV_ASYNC_H
+#define LV_ASYNC_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/*********************
+ * INCLUDES
+ *********************/
+
+#include "lv_types.h"
+
+/*********************
+ * DEFINES
+ *********************/
+
+/**********************
+ * TYPEDEFS
+ **********************/
+
+/**
+ * Type for async callback.
+ */
+typedef void (*lv_async_cb_t)(void *);
+
+/**********************
+ * GLOBAL PROTOTYPES
+ **********************/
+
+/**
+ * Call an asynchronous function the next time lv_timer_handler() is run. This function is likely to return
+ * **before** the call actually happens!
+ * @param async_xcb a callback which is the task itself.
+ * (the 'x' in the argument name indicates that it's not a fully generic function because it not follows
+ * the `func_name(object, callback, ...)` convention)
+ * @param user_data custom parameter
+ */
+lv_res_t lv_async_call(lv_async_cb_t async_xcb, void * user_data);
+
+/**
+ * Cancel an asynchronous function call
+ * @param async_xcb a callback which is the task itself.
+ * @param user_data custom parameter
+ */
+lv_res_t lv_async_call_cancel(lv_async_cb_t async_xcb, void * user_data);
+
+/**********************
+ * MACROS
+ **********************/
+
+#ifdef __cplusplus
+} /*extern "C"*/
+#endif
+
+#endif /*LV_ASYNC_H*/
diff --git a/lib/lvgl/src/misc/lv_bidi.c b/lib/lvgl/src/misc/lv_bidi.c
new file mode 100644
index 00000000..3dc3ce7f
--- /dev/null
+++ b/lib/lvgl/src/misc/lv_bidi.c
@@ -0,0 +1,682 @@
+/**
+ * @file lv_bidi.c
+ *
+ */
+
+/*********************
+ * INCLUDES
+ *********************/
+#include <stddef.h>
+#include "lv_bidi.h"
+#include "lv_txt.h"
+#include "../misc/lv_mem.h"
+
+#if LV_USE_BIDI
+
+/*********************
+ * DEFINES
+ *********************/
+#define LV_BIDI_BRACKLET_DEPTH 4
+
+// Highest bit of the 16-bit pos_conv value specifies whether this pos is RTL or not
+#define GET_POS(x) ((x) & 0x7FFF)
+#define IS_RTL_POS(x) (((x) & 0x8000) != 0)
+#define SET_RTL_POS(x, is_rtl) (GET_POS(x) | ((is_rtl)? 0x8000: 0))
+
+/**********************
+ * TYPEDEFS
+ **********************/
+typedef struct {
+ uint32_t bracklet_pos;
+ lv_base_dir_t dir;
+} bracket_stack_t;
+
+/**********************
+ * STATIC PROTOTYPES
+ **********************/
+
+static uint32_t lv_bidi_get_next_paragraph(const char * txt);
+static lv_base_dir_t lv_bidi_get_letter_dir(uint32_t letter);
+static bool lv_bidi_letter_is_weak(uint32_t letter);
+static bool lv_bidi_letter_is_rtl(uint32_t letter);
+static bool lv_bidi_letter_is_neutral(uint32_t letter);
+
+static lv_base_dir_t get_next_run(const char * txt, lv_base_dir_t base_dir, uint32_t max_len, uint32_t * len,
+ uint16_t * pos_conv_len);
+static void rtl_reverse(char * dest, const char * src, uint32_t len, uint16_t * pos_conv_out, uint16_t pos_conv_rd_base,
+ uint16_t pos_conv_len);
+static uint32_t char_change_to_pair(uint32_t letter);
+static lv_base_dir_t bracket_process(const char * txt, uint32_t next_pos, uint32_t len, uint32_t letter,
+ lv_base_dir_t base_dir);
+static void fill_pos_conv(uint16_t * out, uint16_t len, uint16_t index);
+static uint32_t get_txt_len(const char * txt, uint32_t max_len);
+
+/**********************
+ * STATIC VARIABLES
+ **********************/
+static const uint8_t bracket_left[] = {"<({["};
+static const uint8_t bracket_right[] = {">)}]"};
+static bracket_stack_t br_stack[LV_BIDI_BRACKLET_DEPTH];
+static uint8_t br_stack_p;
+
+/**********************
+ * MACROS
+ **********************/
+
+/**********************
+ * GLOBAL FUNCTIONS
+ **********************/
+
+/**
+ * Convert a text to get the characters in the correct visual order according to
+ * Unicode Bidirectional Algorithm
+ * @param str_in the text to process
+ * @param str_out store the result here. Has the be `strlen(str_in)` length
+ * @param base_dir `LV_BASE_DIR_LTR` or `LV_BASE_DIR_RTL`
+ */
+void _lv_bidi_process(const char * str_in, char * str_out, lv_base_dir_t base_dir)
+{
+ if(base_dir == LV_BASE_DIR_AUTO) base_dir = _lv_bidi_detect_base_dir(str_in);
+
+ uint32_t par_start = 0;
+ uint32_t par_len;
+
+ while(str_in[par_start] == '\n' || str_in[par_start] == '\r') {
+ str_out[par_start] = str_in[par_start];
+ par_start ++;
+ }
+
+ while(str_in[par_start] != '\0') {
+ par_len = lv_bidi_get_next_paragraph(&str_in[par_start]);
+ _lv_bidi_process_paragraph(&str_in[par_start], &str_out[par_start], par_len, base_dir, NULL, 0);
+ par_start += par_len;
+
+ while(str_in[par_start] == '\n' || str_in[par_start] == '\r') {
+ str_out[par_start] = str_in[par_start];
+ par_start ++;
+ }
+ }
+
+ str_out[par_start] = '\0';
+}
+
+/**
+ * Auto-detect the direction of a text based on the first strong character
+ * @param txt the text to process
+ * @return `LV_BASE_DIR_LTR` or `LV_BASE_DIR_RTL`
+ */
+lv_base_dir_t _lv_bidi_detect_base_dir(const char * txt)
+{
+ uint32_t i = 0;
+ uint32_t letter;
+ while(txt[i] != '\0') {
+ letter = _lv_txt_encoded_next(txt, &i);
+
+ lv_base_dir_t dir;
+ dir = lv_bidi_get_letter_dir(letter);
+ if(dir == LV_BASE_DIR_RTL || dir == LV_BASE_DIR_LTR) return dir;
+ }
+
+ /*If there were no strong char earlier return with the default base dir*/
+ if(LV_BIDI_BASE_DIR_DEF == LV_BASE_DIR_AUTO) return LV_BASE_DIR_LTR;
+ else return LV_BIDI_BASE_DIR_DEF;
+}
+
+/**
+ * Get the logical position of a character in a line
+ * @param str_in the input string. Can be only one line.
+ * @param bidi_txt internally the text is bidi processed which buffer can be get here.
+ * If not required anymore has to freed with `lv_mem_free()`
+ * Can be `NULL` is unused
+ * @param len length of the line in character count
+ * @param base_dir base direction of the text: `LV_BASE_DIR_LTR` or `LV_BASE_DIR_RTL`
+ * @param visual_pos the visual character position which logical position should be get
+ * @param is_rtl tell the char at `visual_pos` is RTL or LTR context
+ * @return the logical character position
+ */
+uint16_t _lv_bidi_get_logical_pos(const char * str_in, char ** bidi_txt, uint32_t len, lv_base_dir_t base_dir,
+ uint32_t visual_pos, bool * is_rtl)
+{
+ uint32_t pos_conv_len = get_txt_len(str_in, len);
+ char * buf = lv_mem_buf_get(len + 1);
+ if(buf == NULL) return (uint16_t) -1;
+
+ uint16_t * pos_conv_buf = lv_mem_buf_get(pos_conv_len * sizeof(uint16_t));
+ if(pos_conv_buf == NULL) {
+ lv_mem_buf_release(buf);
+ return (uint16_t) -1;
+ }
+
+ if(bidi_txt) *bidi_txt = buf;
+
+ _lv_bidi_process_paragraph(str_in, bidi_txt ? *bidi_txt : NULL, len, base_dir, pos_conv_buf, pos_conv_len);
+
+ if(is_rtl) *is_rtl = IS_RTL_POS(pos_conv_buf[visual_pos]);
+
+ if(bidi_txt == NULL) lv_mem_buf_release(buf);
+ uint16_t res = GET_POS(pos_conv_buf[visual_pos]);
+ lv_mem_buf_release(pos_conv_buf);
+ return res;
+}
+
+/**
+ * Get the visual position of a character in a line
+ * @param str_in the input string. Can be only one line.
+ * @param bidi_txt internally the text is bidi processed which buffer can be get here.
+ * If not required anymore has to freed with `lv_mem_free()`
+ * Can be `NULL` is unused
+ * @param len length of the line in character count
+ * @param base_dir base direction of the text: `LV_BASE_DIR_LTR` or `LV_BASE_DIR_RTL`
+ * @param logical_pos the logical character position which visual position should be get
+ * @param is_rtl tell the char at `logical_pos` is RTL or LTR context
+ * @return the visual character position
+ */
+uint16_t _lv_bidi_get_visual_pos(const char * str_in, char ** bidi_txt, uint16_t len, lv_base_dir_t base_dir,
+ uint32_t logical_pos, bool * is_rtl)
+{
+ uint32_t pos_conv_len = get_txt_len(str_in, len);
+ char * buf = lv_mem_buf_get(len + 1);
+ if(buf == NULL) return (uint16_t) -1;
+
+ uint16_t * pos_conv_buf = lv_mem_buf_get(pos_conv_len * sizeof(uint16_t));
+ if(pos_conv_buf == NULL) {
+ lv_mem_buf_release(buf);
+ return (uint16_t) -1;
+ }
+
+ if(bidi_txt) *bidi_txt = buf;
+
+ _lv_bidi_process_paragraph(str_in, bidi_txt ? *bidi_txt : NULL, len, base_dir, pos_conv_buf, pos_conv_len);
+
+ for(uint16_t i = 0; i < pos_conv_len; i++) {
+ if(GET_POS(pos_conv_buf[i]) == logical_pos) {
+
+ if(is_rtl) *is_rtl = IS_RTL_POS(pos_conv_buf[i]);
+ lv_mem_buf_release(pos_conv_buf);
+
+ if(bidi_txt == NULL) lv_mem_buf_release(buf);
+ return i;
+ }
+ }
+ lv_mem_buf_release(pos_conv_buf);
+ if(bidi_txt == NULL) lv_mem_buf_release(buf);
+ return (uint16_t) -1;
+}
+
+/**
+ * Bidi process a paragraph of text
+ * @param str_in the string to process
+ * @param str_out store the result here
+ * @param len length of the text
+ * @param base_dir base dir of the text
+ * @param pos_conv_out an `uint16_t` array to store the related logical position of the character.
+ * Can be `NULL` is unused
+ * @param pos_conv_len length of `pos_conv_out` in element count
+ */
+void _lv_bidi_process_paragraph(const char * str_in, char * str_out, uint32_t len, lv_base_dir_t base_dir,
+ uint16_t * pos_conv_out, uint16_t pos_conv_len)
+{
+ uint32_t run_len = 0;
+ lv_base_dir_t run_dir;
+ uint32_t rd = 0;
+ uint32_t wr;
+ uint16_t pos_conv_run_len = 0;
+ uint16_t pos_conv_rd = 0;
+ uint16_t pos_conv_wr;
+
+ if(base_dir == LV_BASE_DIR_AUTO) base_dir = _lv_bidi_detect_base_dir(str_in);
+ if(base_dir == LV_BASE_DIR_RTL) {
+ wr = len;
+ pos_conv_wr = pos_conv_len;
+ }
+ else {
+ wr = 0;
+ pos_conv_wr = 0;
+ }
+
+ if(str_out) str_out[len] = '\0';
+
+ lv_base_dir_t dir = base_dir;
+
+ /*Empty the bracket stack*/
+ br_stack_p = 0;
+
+ /*Process neutral chars in the beginning*/
+ while(rd < len) {
+ uint32_t letter = _lv_txt_encoded_next(str_in, &rd);
+ pos_conv_rd++;
+ dir = lv_bidi_get_letter_dir(letter);
+ if(dir == LV_BASE_DIR_NEUTRAL) dir = bracket_process(str_in, rd, len, letter, base_dir);
+ if(dir != LV_BASE_DIR_NEUTRAL && dir != LV_BASE_DIR_WEAK) break;
+ }
+
+ if(rd && str_in[rd] != '\0') {
+ _lv_txt_encoded_prev(str_in, &rd);
+ pos_conv_rd--;
+ }
+
+ if(rd) {
+ if(base_dir == LV_BASE_DIR_LTR) {
+ if(str_out) {
+ lv_memcpy(&str_out[wr], str_in, rd);
+ wr += rd;
+ }
+ if(pos_conv_out) {
+ fill_pos_conv(&pos_conv_out[pos_conv_wr], pos_conv_rd, 0);
+ pos_conv_wr += pos_conv_rd;
+ }
+ }
+ else {
+ wr -= rd;
+ pos_conv_wr -= pos_conv_rd;
+ rtl_reverse(str_out ? &str_out[wr] : NULL, str_in, rd, pos_conv_out ? &pos_conv_out[pos_conv_wr] : NULL, 0,
+ pos_conv_rd);
+ }
+ }
+
+ /*Get and process the runs*/
+
+ while(rd < len && str_in[rd]) {
+ run_dir = get_next_run(&str_in[rd], base_dir, len - rd, &run_len, &pos_conv_run_len);
+
+ if(base_dir == LV_BASE_DIR_LTR) {
+ if(run_dir == LV_BASE_DIR_LTR) {
+ if(str_out) lv_memcpy(&str_out[wr], &str_in[rd], run_len);
+ if(pos_conv_out) fill_pos_conv(&pos_conv_out[pos_conv_wr], pos_conv_run_len, pos_conv_rd);
+ }
+ else rtl_reverse(str_out ? &str_out[wr] : NULL, &str_in[rd], run_len, pos_conv_out ? &pos_conv_out[pos_conv_wr] : NULL,
+ pos_conv_rd, pos_conv_run_len);
+ wr += run_len;
+ pos_conv_wr += pos_conv_run_len;
+ }
+ else {
+ wr -= run_len;
+ pos_conv_wr -= pos_conv_run_len;
+ if(run_dir == LV_BASE_DIR_LTR) {
+ if(str_out) lv_memcpy(&str_out[wr], &str_in[rd], run_len);
+ if(pos_conv_out) fill_pos_conv(&pos_conv_out[pos_conv_wr], pos_conv_run_len, pos_conv_rd);
+ }
+ else rtl_reverse(str_out ? &str_out[wr] : NULL, &str_in[rd], run_len, pos_conv_out ? &pos_conv_out[pos_conv_wr] : NULL,
+ pos_conv_rd, pos_conv_run_len);
+ }
+
+ rd += run_len;
+ pos_conv_rd += pos_conv_run_len;
+ }
+}
+
+void lv_bidi_calculate_align(lv_text_align_t * align, lv_base_dir_t * base_dir, const char * txt)
+{
+ if(*base_dir == LV_BASE_DIR_AUTO) *base_dir = _lv_bidi_detect_base_dir(txt);
+
+ if(*align == LV_TEXT_ALIGN_AUTO) {
+ if(*base_dir == LV_BASE_DIR_RTL) *align = LV_TEXT_ALIGN_RIGHT;
+ else *align = LV_TEXT_ALIGN_LEFT;
+ }
+}
+
+/**********************
+ * STATIC FUNCTIONS
+ **********************/
+
+/**
+ * Get the next paragraph from a text
+ * @param txt the text to process
+ * @return the length of the current paragraph in byte count
+ */
+static uint32_t lv_bidi_get_next_paragraph(const char * txt)
+{
+ uint32_t i = 0;
+
+ _lv_txt_encoded_next(txt, &i);
+
+ while(txt[i] != '\0' && txt[i] != '\n' && txt[i] != '\r') {
+ _lv_txt_encoded_next(txt, &i);
+ }
+
+ return i;
+}
+
+/**
+ * Get the direction of a character
+ * @param letter a Unicode character
+ * @return `LV_BASE_DIR_RTL/LTR/WEAK/NEUTRAL`
+ */
+static lv_base_dir_t lv_bidi_get_letter_dir(uint32_t letter)
+{
+ if(lv_bidi_letter_is_rtl(letter)) return LV_BASE_DIR_RTL;
+ if(lv_bidi_letter_is_neutral(letter)) return LV_BASE_DIR_NEUTRAL;
+ if(lv_bidi_letter_is_weak(letter)) return LV_BASE_DIR_WEAK;
+
+ return LV_BASE_DIR_LTR;
+}
+/**
+ * Tell whether a character is weak or not
+ * @param letter a Unicode character
+ * @return true/false
+ */
+static bool lv_bidi_letter_is_weak(uint32_t letter)
+{
+ uint32_t i = 0;
+ static const char weaks[] = "0123456789";
+
+ do {
+ uint32_t x = _lv_txt_encoded_next(weaks, &i);
+ if(letter == x) {
+ return true;
+ }
+ } while(weaks[i] != '\0');
+
+ return false;
+}
+/**
+ * Tell whether a character is RTL or not
+ * @param letter a Unicode character
+ * @return true/false
+ */
+static bool lv_bidi_letter_is_rtl(uint32_t letter)
+{
+ if(letter >= 0x5d0 && letter <= 0x5ea) return true;
+ if(letter == 0x202E) return true; /*Unicode of LV_BIDI_RLO*/
+
+ /*Check for Persian and Arabic characters [https://en.wikipedia.org/wiki/Arabic_script_in_Unicode]*/
+ if(letter >= 0x600 && letter <= 0x6FF) return true;
+ if(letter >= 0xFB50 && letter <= 0xFDFF) return true;
+ if(letter >= 0xFE70 && letter <= 0xFEFF) return true;
+
+ return false;
+}
+
+/**
+ * Tell whether a character is neutral or not
+ * @param letter a Unicode character
+ * @return true/false
+ */
+static bool lv_bidi_letter_is_neutral(uint32_t letter)
+{
+ uint16_t i;
+ static const char neutrals[] = " \t\n\r.,:;'\"`!?%/\\-=()[]{}<>@#&$|";
+ for(i = 0; neutrals[i] != '\0'; i++) {
+ if(letter == (uint32_t)neutrals[i]) return true;
+ }
+
+ return false;
+}
+
+static uint32_t get_txt_len(const char * txt, uint32_t max_len)
+{
+ uint32_t len = 0;
+ uint32_t i = 0;
+
+ while(i < max_len && txt[i] != '\0') {
+ _lv_txt_encoded_next(txt, &i);
+ len++;
+ }
+
+ return len;
+}
+
+static void fill_pos_conv(uint16_t * out, uint16_t len, uint16_t index)
+{
+ uint16_t i;
+ for(i = 0; i < len; i++) {
+ out[i] = SET_RTL_POS(index, false);
+ index++;
+ }
+}
+
+static lv_base_dir_t get_next_run(const char * txt, lv_base_dir_t base_dir, uint32_t max_len, uint32_t * len,
+ uint16_t * pos_conv_len)
+{
+ uint32_t i = 0;
+ uint32_t letter;
+
+ uint16_t pos_conv_i = 0;
+
+ letter = _lv_txt_encoded_next(txt, NULL);
+ lv_base_dir_t dir = lv_bidi_get_letter_dir(letter);
+ if(dir == LV_BASE_DIR_NEUTRAL) dir = bracket_process(txt, 0, max_len, letter, base_dir);
+
+ /*Find the first strong char. Skip the neutrals*/
+ while(dir == LV_BASE_DIR_NEUTRAL || dir == LV_BASE_DIR_WEAK) {
+ letter = _lv_txt_encoded_next(txt, &i);
+
+ pos_conv_i++;
+ dir = lv_bidi_get_letter_dir(letter);
+ if(dir == LV_BASE_DIR_NEUTRAL) dir = bracket_process(txt, i, max_len, letter, base_dir);
+
+ if(dir == LV_BASE_DIR_LTR || dir == LV_BASE_DIR_RTL) break;
+
+ if(i >= max_len || txt[i] == '\0' || txt[i] == '\n' || txt[i] == '\r') {
+ *len = i;
+ *pos_conv_len = pos_conv_i;
+ return base_dir;
+ }
+ }
+
+ lv_base_dir_t run_dir = dir;
+
+ uint32_t i_prev = i;
+ uint32_t i_last_strong = i;
+ uint16_t pos_conv_i_prev = pos_conv_i;
+ uint16_t pos_conv_i_last_strong = pos_conv_i;
+
+ /*Find the next char which has different direction*/
+ lv_base_dir_t next_dir = base_dir;
+ while(i_prev < max_len && txt[i] != '\0' && txt[i] != '\n' && txt[i] != '\r') {
+ letter = _lv_txt_encoded_next(txt, &i);
+ pos_conv_i++;
+ next_dir = lv_bidi_get_letter_dir(letter);
+ if(next_dir == LV_BASE_DIR_NEUTRAL) next_dir = bracket_process(txt, i, max_len, letter, base_dir);
+
+ if(next_dir == LV_BASE_DIR_WEAK) {
+ if(run_dir == LV_BASE_DIR_RTL) {
+ if(base_dir == LV_BASE_DIR_RTL) {
+ next_dir = LV_BASE_DIR_LTR;
+ }
+ }
+ }
+
+ /*New dir found?*/
+ if((next_dir == LV_BASE_DIR_RTL || next_dir == LV_BASE_DIR_LTR) && next_dir != run_dir) {
+ /*Include neutrals if `run_dir == base_dir`*/
+ if(run_dir == base_dir) {
+ *len = i_prev;
+ *pos_conv_len = pos_conv_i_prev;
+ }
+ /*Exclude neutrals if `run_dir != base_dir`*/
+ else {
+ *len = i_last_strong;
+ *pos_conv_len = pos_conv_i_last_strong;
+ }
+
+ return run_dir;
+ }
+
+ if(next_dir != LV_BASE_DIR_NEUTRAL) {
+ i_last_strong = i;
+ pos_conv_i_last_strong = pos_conv_i;
+ }
+
+ i_prev = i;
+ pos_conv_i_prev = pos_conv_i;
+ }
+
+ /*Handle end of of string. Apply `base_dir` on trailing neutrals*/
+
+ /*Include neutrals if `run_dir == base_dir`*/
+ if(run_dir == base_dir) {
+ *len = i_prev;
+ *pos_conv_len = pos_conv_i_prev;
+ }
+ /*Exclude neutrals if `run_dir != base_dir`*/
+ else {
+ *len = i_last_strong;
+ *pos_conv_len = pos_conv_i_last_strong;
+ }
+
+ return run_dir;
+}
+
+static void rtl_reverse(char * dest, const char * src, uint32_t len, uint16_t * pos_conv_out, uint16_t pos_conv_rd_base,
+ uint16_t pos_conv_len)
+{
+ uint32_t i = len;
+ uint32_t wr = 0;
+ uint16_t pos_conv_i = pos_conv_len;
+ uint16_t pos_conv_wr = 0;
+
+ while(i) {
+ uint32_t letter = _lv_txt_encoded_prev(src, &i);
+ uint16_t pos_conv_letter = --pos_conv_i;
+
+ /*Keep weak letters (numbers) as LTR*/
+ if(lv_bidi_letter_is_weak(letter)) {
+ uint32_t last_weak = i;
+ uint32_t first_weak = i;
+ uint16_t pos_conv_last_weak = pos_conv_i;
+ uint16_t pos_conv_first_weak = pos_conv_i;
+ while(i) {
+ letter = _lv_txt_encoded_prev(src, &i);
+ pos_conv_letter = --pos_conv_i;
+
+ /*No need to call `char_change_to_pair` because there not such chars here*/
+
+ /*Finish on non-weak char*/
+ /*but treat number and currency related chars as weak*/
+ if(lv_bidi_letter_is_weak(letter) == false && letter != '.' && letter != ',' && letter != '$' && letter != '%') {
+ _lv_txt_encoded_next(src, &i); /*Rewind one letter*/
+ pos_conv_i++;
+ first_weak = i;
+ pos_conv_first_weak = pos_conv_i;
+ break;
+ }
+ }
+ if(i == 0) {
+ first_weak = 0;
+ pos_conv_first_weak = 0;
+ }
+
+ if(dest) lv_memcpy(&dest[wr], &src[first_weak], last_weak - first_weak + 1);
+ if(pos_conv_out) fill_pos_conv(&pos_conv_out[pos_conv_wr], pos_conv_last_weak - pos_conv_first_weak + 1,
+ pos_conv_rd_base + pos_conv_first_weak);
+ wr += last_weak - first_weak + 1;
+ pos_conv_wr += pos_conv_last_weak - pos_conv_first_weak + 1;
+ }
+
+ /*Simply store in reversed order*/
+ else {
+ uint32_t letter_size = _lv_txt_encoded_size((const char *)&src[i]);
+ /*Swap arithmetical symbols*/
+ if(letter_size == 1) {
+ uint32_t new_letter = letter = char_change_to_pair(letter);
+ if(dest) dest[wr] = (uint8_t)new_letter;
+ if(pos_conv_out) pos_conv_out[pos_conv_wr] = SET_RTL_POS(pos_conv_rd_base + pos_conv_letter, true);
+ wr++;
+ pos_conv_wr++;
+ }
+ /*Just store the letter*/
+ else {
+ if(dest) lv_memcpy(&dest[wr], &src[i], letter_size);
+ if(pos_conv_out) pos_conv_out[pos_conv_wr] = SET_RTL_POS(pos_conv_rd_base + pos_conv_i, true);
+ wr += letter_size;
+ pos_conv_wr++;
+ }
+ }
+ }
+}
+
+static uint32_t char_change_to_pair(uint32_t letter)
+{
+
+ uint8_t i;
+ for(i = 0; bracket_left[i] != '\0'; i++) {
+ if(letter == bracket_left[i]) return bracket_right[i];
+ }
+
+ for(i = 0; bracket_right[i] != '\0'; i++) {
+ if(letter == bracket_right[i]) return bracket_left[i];
+ }
+
+ return letter;
+}
+
+static lv_base_dir_t bracket_process(const char * txt, uint32_t next_pos, uint32_t len, uint32_t letter,
+ lv_base_dir_t base_dir)
+{
+ lv_base_dir_t bracket_dir = LV_BASE_DIR_NEUTRAL;
+
+ uint8_t i;
+ /*Is the letter an opening bracket?*/
+ for(i = 0; bracket_left[i] != '\0'; i++) {
+ if(bracket_left[i] == letter) {
+ /*If so find its matching closing bracket.
+ *If a char with base dir. direction is found then the brackets will have `base_dir` direction*/
+ uint32_t txt_i = next_pos;
+ while(txt_i < len) {
+ uint32_t letter_next = _lv_txt_encoded_next(txt, &txt_i);
+ if(letter_next == bracket_right[i]) {
+ /*Closing bracket found*/
+ break;
+ }
+ else {
+ /*Save the dir*/
+ lv_base_dir_t letter_dir = lv_bidi_get_letter_dir(letter_next);
+ if(letter_dir == base_dir) {
+ bracket_dir = base_dir;
+ }
+ }
+ }
+
+ /*There were no matching closing bracket*/
+ if(txt_i > len) return LV_BASE_DIR_NEUTRAL;
+
+ /*There where a strong char with base dir in the bracket so the dir is found.*/
+ if(bracket_dir != LV_BASE_DIR_NEUTRAL && bracket_dir != LV_BASE_DIR_WEAK) break;
+
+ /*If there were no matching strong chars in the brackets then check the previous chars*/
+ txt_i = next_pos;
+ if(txt_i) _lv_txt_encoded_prev(txt, &txt_i);
+ while(txt_i > 0) {
+ uint32_t letter_next = _lv_txt_encoded_prev(txt, &txt_i);
+ lv_base_dir_t letter_dir = lv_bidi_get_letter_dir(letter_next);
+ if(letter_dir == LV_BASE_DIR_LTR || letter_dir == LV_BASE_DIR_RTL) {
+ bracket_dir = letter_dir;
+ break;
+ }
+ }
+
+ /*There where a previous strong char which can be used*/
+ if(bracket_dir != LV_BASE_DIR_NEUTRAL) break;
+
+ /*There were no strong chars before the bracket, so use the base dir.*/
+ if(txt_i == 0) bracket_dir = base_dir;
+
+ break;
+ }
+ }
+
+ /*The letter was an opening bracket*/
+ if(bracket_left[i] != '\0') {
+
+ if(bracket_dir == LV_BASE_DIR_NEUTRAL || br_stack_p == LV_BIDI_BRACKLET_DEPTH) return LV_BASE_DIR_NEUTRAL;
+
+ br_stack[br_stack_p].bracklet_pos = i;
+ br_stack[br_stack_p].dir = bracket_dir;
+
+ br_stack_p++;
+ return bracket_dir;
+ }
+ else if(br_stack_p > 0) {
+ /*Is the letter a closing bracket of the last opening?*/
+ if(letter == bracket_right[br_stack[br_stack_p - 1].bracklet_pos]) {
+ bracket_dir = br_stack[br_stack_p - 1].dir;
+ br_stack_p--;
+ return bracket_dir;
+ }
+ }
+
+ return LV_BASE_DIR_NEUTRAL;
+}
+
+#endif /*LV_USE_BIDI*/
diff --git a/lib/lvgl/src/misc/lv_bidi.h b/lib/lvgl/src/misc/lv_bidi.h
new file mode 100644
index 00000000..a27b5808
--- /dev/null
+++ b/lib/lvgl/src/misc/lv_bidi.h
@@ -0,0 +1,141 @@
+/**
+ * @file lv_bidi.h
+ *
+ */
+
+#ifndef LV_BIDI_H
+#define LV_BIDI_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/*********************
+ * INCLUDES
+ *********************/
+#include "../lv_conf_internal.h"
+
+#include <stdbool.h>
+#include <stdint.h>
+#include "lv_txt.h"
+
+/*********************
+ * DEFINES
+ *********************/
+/*Special non printable strong characters.
+ *They can be inserted to texts to affect the run's direction*/
+#define LV_BIDI_LRO "\xE2\x80\xAD" /*U+202D*/
+#define LV_BIDI_RLO "\xE2\x80\xAE" /*U+202E*/
+
+/**********************
+ * TYPEDEFS
+ **********************/
+enum {
+ LV_BASE_DIR_LTR = 0x00,
+ LV_BASE_DIR_RTL = 0x01,
+ LV_BASE_DIR_AUTO = 0x02,
+
+ LV_BASE_DIR_NEUTRAL = 0x20,
+ LV_BASE_DIR_WEAK = 0x21,
+};
+
+typedef uint8_t lv_base_dir_t;
+
+/**********************
+ * GLOBAL PROTOTYPES
+ **********************/
+#if LV_USE_BIDI
+
+/**
+ * Convert a text to get the characters in the correct visual order according to
+ * Unicode Bidirectional Algorithm
+ * @param str_in the text to process
+ * @param str_out store the result here. Has the be `strlen(str_in)` length
+ * @param base_dir `LV_BASE_DIR_LTR` or `LV_BASE_DIR_RTL`
+ */
+void _lv_bidi_process(const char * str_in, char * str_out, lv_base_dir_t base_dir);
+
+/**
+ * Auto-detect the direction of a text based on the first strong character
+ * @param txt the text to process
+ * @return `LV_BASE_DIR_LTR` or `LV_BASE_DIR_RTL`
+ */
+lv_base_dir_t _lv_bidi_detect_base_dir(const char * txt);
+
+/**
+ * Get the logical position of a character in a line
+ * @param str_in the input string. Can be only one line.
+ * @param bidi_txt internally the text is bidi processed which buffer can be get here.
+ * If not required anymore has to freed with `lv_mem_free()`
+ * Can be `NULL` is unused
+ * @param len length of the line in character count
+ * @param base_dir base direction of the text: `LV_BASE_DIR_LTR` or `LV_BASE_DIR_RTL`
+ * @param visual_pos the visual character position which logical position should be get
+ * @param is_rtl tell the char at `visual_pos` is RTL or LTR context
+ * @return the logical character position
+ */
+uint16_t _lv_bidi_get_logical_pos(const char * str_in, char ** bidi_txt, uint32_t len, lv_base_dir_t base_dir,
+ uint32_t visual_pos, bool * is_rtl);
+
+/**
+ * Get the visual position of a character in a line
+ * @param str_in the input string. Can be only one line.
+ * @param bidi_txt internally the text is bidi processed which buffer can be get here.
+ * If not required anymore has to freed with `lv_mem_free()`
+ * Can be `NULL` is unused
+ * @param len length of the line in character count
+ * @param base_dir base direction of the text: `LV_BASE_DIR_LTR` or `LV_BASE_DIR_RTL`
+ * @param logical_pos the logical character position which visual position should be get
+ * @param is_rtl tell the char at `logical_pos` is RTL or LTR context
+ * @return the visual character position
+ */
+uint16_t _lv_bidi_get_visual_pos(const char * str_in, char ** bidi_txt, uint16_t len, lv_base_dir_t base_dir,
+ uint32_t logical_pos, bool * is_rtl);
+
+/**
+ * Bidi process a paragraph of text
+ * @param str_in the string to process
+ * @param str_out store the result here
+ * @param len length of the text
+ * @param base_dir base dir of the text
+ * @param pos_conv_out an `uint16_t` array to store the related logical position of the character.
+ * Can be `NULL` is unused
+ * @param pos_conv_len length of `pos_conv_out` in element count
+ */
+void _lv_bidi_process_paragraph(const char * str_in, char * str_out, uint32_t len, lv_base_dir_t base_dir,
+ uint16_t * pos_conv_out, uint16_t pos_conv_len);
+
+/**
+ * Get the real text alignment from the a text alignment, base direction and a text.
+ * @param align LV_TEXT_ALIGN_..., write back the calculated align here (LV_TEXT_ALIGN_LEFT/RIGHT/CENTER)
+ * @param base_dir LV_BASE_DIR_..., write the calculated base dir here (LV_BASE_DIR_LTR/RTL)
+ * @param txt a text, used with LV_BASE_DIR_AUTO to determine the base direction
+ */
+void lv_bidi_calculate_align(lv_text_align_t * align, lv_base_dir_t * base_dir, const char * txt);
+
+
+/**********************
+ * MACROS
+ **********************/
+
+#else /*LV_USE_BIDI*/
+/**
+ * For compatibility if LV_USE_BIDI = 0
+ * Get the real text alignment from the a text alignment, base direction and a text.
+ * @param align For LV_TEXT_ALIGN_AUTO give LV_TEXT_ALIGN_LEFT else leave unchanged, write back the calculated align here
+ * @param base_dir Unused
+ * @param txt Unused
+ */
+static inline void lv_bidi_calculate_align(lv_text_align_t * align, lv_base_dir_t * base_dir, const char * txt)
+{
+ LV_UNUSED(txt);
+ LV_UNUSED(base_dir);
+ if(*align == LV_TEXT_ALIGN_AUTO) * align = LV_TEXT_ALIGN_LEFT;
+}
+#endif /*LV_USE_BIDI*/
+
+#ifdef __cplusplus
+} /*extern "C"*/
+#endif
+
+#endif /*LV_BIDI_H*/
diff --git a/lib/lvgl/src/misc/lv_color.c b/lib/lvgl/src/misc/lv_color.c
new file mode 100644
index 00000000..0e26624f
--- /dev/null
+++ b/lib/lvgl/src/misc/lv_color.c
@@ -0,0 +1,369 @@
+/**
+ * @file lv_color.c
+ *
+ */
+
+/*********************
+ * INCLUDES
+ *********************/
+#include "lv_color.h"
+#include "lv_log.h"
+
+/*********************
+ * DEFINES
+ *********************/
+
+/**********************
+ * TYPEDEFS
+ **********************/
+
+/**********************
+ * STATIC PROTOTYPES
+ **********************/
+
+/**********************
+ * STATIC VARIABLES
+ **********************/
+
+/**********************
+ * MACROS
+ **********************/
+
+/**********************
+ * GLOBAL FUNCTIONS
+ **********************/
+
+LV_ATTRIBUTE_FAST_MEM void lv_color_fill(lv_color_t * buf, lv_color_t color, uint32_t px_num)
+{
+#if LV_COLOR_DEPTH == 16
+ uintptr_t buf_int = (uintptr_t)buf;
+ if(buf_int & 0x3) {
+ *buf = color;
+ buf++;
+ px_num--;
+ }
+
+ uint32_t c32 = (uint32_t)color.full + ((uint32_t)color.full << 16);
+ uint32_t * buf32 = (uint32_t *)buf;
+
+ while(px_num > 16) {
+ *buf32 = c32;
+ buf32++;
+ *buf32 = c32;
+ buf32++;
+ *buf32 = c32;
+ buf32++;
+ *buf32 = c32;
+ buf32++;
+
+ *buf32 = c32;
+ buf32++;
+ *buf32 = c32;
+ buf32++;
+ *buf32 = c32;
+ buf32++;
+ *buf32 = c32;
+ buf32++;
+
+ px_num -= 16;
+ }
+
+ buf = (lv_color_t *)buf32;
+
+ while(px_num) {
+ *buf = color;
+ buf++;
+ px_num--;
+ }
+#else
+ while(px_num > 16) {
+ *buf = color;
+ buf++;
+ *buf = color;
+ buf++;
+ *buf = color;
+ buf++;
+ *buf = color;
+ buf++;
+
+ *buf = color;
+ buf++;
+ *buf = color;
+ buf++;
+ *buf = color;
+ buf++;
+ *buf = color;
+ buf++;
+
+ *buf = color;
+ buf++;
+ *buf = color;
+ buf++;
+ *buf = color;
+ buf++;
+ *buf = color;
+ buf++;
+
+ *buf = color;
+ buf++;
+ *buf = color;
+ buf++;
+ *buf = color;
+ buf++;
+ *buf = color;
+ buf++;
+
+ px_num -= 16;
+ }
+ while(px_num) {
+ *buf = color;
+ buf++;
+ px_num--;
+ }
+#endif
+}
+
+lv_color_t lv_color_lighten(lv_color_t c, lv_opa_t lvl)
+{
+ return lv_color_mix(lv_color_white(), c, lvl);
+}
+
+lv_color_t lv_color_darken(lv_color_t c, lv_opa_t lvl)
+{
+ return lv_color_mix(lv_color_black(), c, lvl);
+}
+
+lv_color_t lv_color_change_lightness(lv_color_t c, lv_opa_t lvl)
+{
+ /*It'd be better to convert the color to HSL, change L and convert back to RGB.*/
+ if(lvl == LV_OPA_50) return c;
+ else if(lvl < LV_OPA_50) return lv_color_darken(c, (LV_OPA_50 - lvl) * 2);
+ else return lv_color_lighten(c, (lvl - LV_OPA_50) * 2);
+}
+
+/**
+ * Convert a HSV color to RGB
+ * @param h hue [0..359]
+ * @param s saturation [0..100]
+ * @param v value [0..100]
+ * @return the given RGB color in RGB (with LV_COLOR_DEPTH depth)
+ */
+lv_color_t lv_color_hsv_to_rgb(uint16_t h, uint8_t s, uint8_t v)
+{
+ h = (uint32_t)((uint32_t)h * 255) / 360;
+ s = (uint16_t)((uint16_t)s * 255) / 100;
+ v = (uint16_t)((uint16_t)v * 255) / 100;
+
+ uint8_t r, g, b;
+
+ uint8_t region, remainder, p, q, t;
+
+ if(s == 0) {
+ return lv_color_make(v, v, v);
+ }
+
+ region = h / 43;
+ remainder = (h - (region * 43)) * 6;
+
+ p = (v * (255 - s)) >> 8;
+ q = (v * (255 - ((s * remainder) >> 8))) >> 8;
+ t = (v * (255 - ((s * (255 - remainder)) >> 8))) >> 8;
+
+ switch(region) {
+ case 0:
+ r = v;
+ g = t;
+ b = p;
+ break;
+ case 1:
+ r = q;
+ g = v;
+ b = p;
+ break;
+ case 2:
+ r = p;
+ g = v;
+ b = t;
+ break;
+ case 3:
+ r = p;
+ g = q;
+ b = v;
+ break;
+ case 4:
+ r = t;
+ g = p;
+ b = v;
+ break;
+ default:
+ r = v;
+ g = p;
+ b = q;
+ break;
+ }
+
+ lv_color_t result = lv_color_make(r, g, b);
+ return result;
+}
+
+/**
+ * Convert a 32-bit RGB color to HSV
+ * @param r8 8-bit red
+ * @param g8 8-bit green
+ * @param b8 8-bit blue
+ * @return the given RGB color in HSV
+ */
+lv_color_hsv_t lv_color_rgb_to_hsv(uint8_t r8, uint8_t g8, uint8_t b8)
+{
+ uint16_t r = ((uint32_t)r8 << 10) / 255;
+ uint16_t g = ((uint32_t)g8 << 10) / 255;
+ uint16_t b = ((uint32_t)b8 << 10) / 255;
+
+ uint16_t rgbMin = r < g ? (r < b ? r : b) : (g < b ? g : b);
+ uint16_t rgbMax = r > g ? (r > b ? r : b) : (g > b ? g : b);
+
+ lv_color_hsv_t hsv;
+
+ // https://en.wikipedia.org/wiki/HSL_and_HSV#Lightness
+ hsv.v = (100 * rgbMax) >> 10;
+
+ int32_t delta = rgbMax - rgbMin;
+ if(delta < 3) {
+ hsv.h = 0;
+ hsv.s = 0;
+ return hsv;
+ }
+
+ // https://en.wikipedia.org/wiki/HSL_and_HSV#Saturation
+ hsv.s = 100 * delta / rgbMax;
+ if(hsv.s < 3) {
+ hsv.h = 0;
+ return hsv;
+ }
+
+ // https://en.wikipedia.org/wiki/HSL_and_HSV#Hue_and_chroma
+ int32_t h;
+ if(rgbMax == r)
+ h = (((g - b) << 10) / delta) + (g < b ? (6 << 10) : 0); // between yellow & magenta
+ else if(rgbMax == g)
+ h = (((b - r) << 10) / delta) + (2 << 10); // between cyan & yellow
+ else if(rgbMax == b)
+ h = (((r - g) << 10) / delta) + (4 << 10); // between magenta & cyan
+ else
+ h = 0;
+ h *= 60;
+ h >>= 10;
+ if(h < 0) h += 360;
+
+ hsv.h = h;
+ return hsv;
+}
+
+/**
+ * Convert a color to HSV
+ * @param color color
+ * @return the given color in HSV
+ */
+lv_color_hsv_t lv_color_to_hsv(lv_color_t color)
+{
+ lv_color32_t color32;
+ color32.full = lv_color_to32(color);
+ return lv_color_rgb_to_hsv(color32.ch.red, color32.ch.green, color32.ch.blue);
+}
+
+lv_color_t lv_palette_main(lv_palette_t p)
+{
+ static const lv_color_t colors[] = {
+ LV_COLOR_MAKE(0xF4, 0x43, 0x36), LV_COLOR_MAKE(0xE9, 0x1E, 0x63), LV_COLOR_MAKE(0x9C, 0x27, 0xB0), LV_COLOR_MAKE(0x67, 0x3A, 0xB7),
+ LV_COLOR_MAKE(0x3F, 0x51, 0xB5), LV_COLOR_MAKE(0x21, 0x96, 0xF3), LV_COLOR_MAKE(0x03, 0xA9, 0xF4), LV_COLOR_MAKE(0x00, 0xBC, 0xD4),
+ LV_COLOR_MAKE(0x00, 0x96, 0x88), LV_COLOR_MAKE(0x4C, 0xAF, 0x50), LV_COLOR_MAKE(0x8B, 0xC3, 0x4A), LV_COLOR_MAKE(0xCD, 0xDC, 0x39),
+ LV_COLOR_MAKE(0xFF, 0xEB, 0x3B), LV_COLOR_MAKE(0xFF, 0xC1, 0x07), LV_COLOR_MAKE(0xFF, 0x98, 0x00), LV_COLOR_MAKE(0xFF, 0x57, 0x22),
+ LV_COLOR_MAKE(0x79, 0x55, 0x48), LV_COLOR_MAKE(0x60, 0x7D, 0x8B), LV_COLOR_MAKE(0x9E, 0x9E, 0x9E)
+ };
+
+ if(p >= _LV_PALETTE_LAST) {
+ LV_LOG_WARN("Invalid palette: %d", p);
+ return lv_color_black();
+ }
+
+ return colors[p];
+
+}
+
+lv_color_t lv_palette_lighten(lv_palette_t p, uint8_t lvl)
+{
+ static const lv_color_t colors[][5] = {
+ {LV_COLOR_MAKE(0xEF, 0x53, 0x50), LV_COLOR_MAKE(0xE5, 0x73, 0x73), LV_COLOR_MAKE(0xEF, 0x9A, 0x9A), LV_COLOR_MAKE(0xFF, 0xCD, 0xD2), LV_COLOR_MAKE(0xFF, 0xEB, 0xEE)},
+ {LV_COLOR_MAKE(0xEC, 0x40, 0x7A), LV_COLOR_MAKE(0xF0, 0x62, 0x92), LV_COLOR_MAKE(0xF4, 0x8F, 0xB1), LV_COLOR_MAKE(0xF8, 0xBB, 0xD0), LV_COLOR_MAKE(0xFC, 0xE4, 0xEC)},
+ {LV_COLOR_MAKE(0xAB, 0x47, 0xBC), LV_COLOR_MAKE(0xBA, 0x68, 0xC8), LV_COLOR_MAKE(0xCE, 0x93, 0xD8), LV_COLOR_MAKE(0xE1, 0xBE, 0xE7), LV_COLOR_MAKE(0xF3, 0xE5, 0xF5)},
+ {LV_COLOR_MAKE(0x7E, 0x57, 0xC2), LV_COLOR_MAKE(0x95, 0x75, 0xCD), LV_COLOR_MAKE(0xB3, 0x9D, 0xDB), LV_COLOR_MAKE(0xD1, 0xC4, 0xE9), LV_COLOR_MAKE(0xED, 0xE7, 0xF6)},
+ {LV_COLOR_MAKE(0x5C, 0x6B, 0xC0), LV_COLOR_MAKE(0x79, 0x86, 0xCB), LV_COLOR_MAKE(0x9F, 0xA8, 0xDA), LV_COLOR_MAKE(0xC5, 0xCA, 0xE9), LV_COLOR_MAKE(0xE8, 0xEA, 0xF6)},
+ {LV_COLOR_MAKE(0x42, 0xA5, 0xF5), LV_COLOR_MAKE(0x64, 0xB5, 0xF6), LV_COLOR_MAKE(0x90, 0xCA, 0xF9), LV_COLOR_MAKE(0xBB, 0xDE, 0xFB), LV_COLOR_MAKE(0xE3, 0xF2, 0xFD)},
+ {LV_COLOR_MAKE(0x29, 0xB6, 0xF6), LV_COLOR_MAKE(0x4F, 0xC3, 0xF7), LV_COLOR_MAKE(0x81, 0xD4, 0xFA), LV_COLOR_MAKE(0xB3, 0xE5, 0xFC), LV_COLOR_MAKE(0xE1, 0xF5, 0xFE)},
+ {LV_COLOR_MAKE(0x26, 0xC6, 0xDA), LV_COLOR_MAKE(0x4D, 0xD0, 0xE1), LV_COLOR_MAKE(0x80, 0xDE, 0xEA), LV_COLOR_MAKE(0xB2, 0xEB, 0xF2), LV_COLOR_MAKE(0xE0, 0xF7, 0xFA)},
+ {LV_COLOR_MAKE(0x26, 0xA6, 0x9A), LV_COLOR_MAKE(0x4D, 0xB6, 0xAC), LV_COLOR_MAKE(0x80, 0xCB, 0xC4), LV_COLOR_MAKE(0xB2, 0xDF, 0xDB), LV_COLOR_MAKE(0xE0, 0xF2, 0xF1)},
+ {LV_COLOR_MAKE(0x66, 0xBB, 0x6A), LV_COLOR_MAKE(0x81, 0xC7, 0x84), LV_COLOR_MAKE(0xA5, 0xD6, 0xA7), LV_COLOR_MAKE(0xC8, 0xE6, 0xC9), LV_COLOR_MAKE(0xE8, 0xF5, 0xE9)},
+ {LV_COLOR_MAKE(0x9C, 0xCC, 0x65), LV_COLOR_MAKE(0xAE, 0xD5, 0x81), LV_COLOR_MAKE(0xC5, 0xE1, 0xA5), LV_COLOR_MAKE(0xDC, 0xED, 0xC8), LV_COLOR_MAKE(0xF1, 0xF8, 0xE9)},
+ {LV_COLOR_MAKE(0xD4, 0xE1, 0x57), LV_COLOR_MAKE(0xDC, 0xE7, 0x75), LV_COLOR_MAKE(0xE6, 0xEE, 0x9C), LV_COLOR_MAKE(0xF0, 0xF4, 0xC3), LV_COLOR_MAKE(0xF9, 0xFB, 0xE7)},
+ {LV_COLOR_MAKE(0xFF, 0xEE, 0x58), LV_COLOR_MAKE(0xFF, 0xF1, 0x76), LV_COLOR_MAKE(0xFF, 0xF5, 0x9D), LV_COLOR_MAKE(0xFF, 0xF9, 0xC4), LV_COLOR_MAKE(0xFF, 0xFD, 0xE7)},
+ {LV_COLOR_MAKE(0xFF, 0xCA, 0x28), LV_COLOR_MAKE(0xFF, 0xD5, 0x4F), LV_COLOR_MAKE(0xFF, 0xE0, 0x82), LV_COLOR_MAKE(0xFF, 0xEC, 0xB3), LV_COLOR_MAKE(0xFF, 0xF8, 0xE1)},
+ {LV_COLOR_MAKE(0xFF, 0xA7, 0x26), LV_COLOR_MAKE(0xFF, 0xB7, 0x4D), LV_COLOR_MAKE(0xFF, 0xCC, 0x80), LV_COLOR_MAKE(0xFF, 0xE0, 0xB2), LV_COLOR_MAKE(0xFF, 0xF3, 0xE0)},
+ {LV_COLOR_MAKE(0xFF, 0x70, 0x43), LV_COLOR_MAKE(0xFF, 0x8A, 0x65), LV_COLOR_MAKE(0xFF, 0xAB, 0x91), LV_COLOR_MAKE(0xFF, 0xCC, 0xBC), LV_COLOR_MAKE(0xFB, 0xE9, 0xE7)},
+ {LV_COLOR_MAKE(0x8D, 0x6E, 0x63), LV_COLOR_MAKE(0xA1, 0x88, 0x7F), LV_COLOR_MAKE(0xBC, 0xAA, 0xA4), LV_COLOR_MAKE(0xD7, 0xCC, 0xC8), LV_COLOR_MAKE(0xEF, 0xEB, 0xE9)},
+ {LV_COLOR_MAKE(0x78, 0x90, 0x9C), LV_COLOR_MAKE(0x90, 0xA4, 0xAE), LV_COLOR_MAKE(0xB0, 0xBE, 0xC5), LV_COLOR_MAKE(0xCF, 0xD8, 0xDC), LV_COLOR_MAKE(0xEC, 0xEF, 0xF1)},
+ {LV_COLOR_MAKE(0xBD, 0xBD, 0xBD), LV_COLOR_MAKE(0xE0, 0xE0, 0xE0), LV_COLOR_MAKE(0xEE, 0xEE, 0xEE), LV_COLOR_MAKE(0xF5, 0xF5, 0xF5), LV_COLOR_MAKE(0xFA, 0xFA, 0xFA)},
+ };
+
+ if(p >= _LV_PALETTE_LAST) {
+ LV_LOG_WARN("Invalid palette: %d", p);
+ return lv_color_black();
+ }
+
+ if(lvl == 0 || lvl > 5) {
+ LV_LOG_WARN("Invalid level: %d. Must be 1..5", lvl);
+ return lv_color_black();
+ }
+
+ lvl--;
+
+ return colors[p][lvl];
+}
+
+lv_color_t lv_palette_darken(lv_palette_t p, uint8_t lvl)
+{
+ static const lv_color_t colors[][4] = {
+ {LV_COLOR_MAKE(0xE5, 0x39, 0x35), LV_COLOR_MAKE(0xD3, 0x2F, 0x2F), LV_COLOR_MAKE(0xC6, 0x28, 0x28), LV_COLOR_MAKE(0xB7, 0x1C, 0x1C)},
+ {LV_COLOR_MAKE(0xD8, 0x1B, 0x60), LV_COLOR_MAKE(0xC2, 0x18, 0x5B), LV_COLOR_MAKE(0xAD, 0x14, 0x57), LV_COLOR_MAKE(0x88, 0x0E, 0x4F)},
+ {LV_COLOR_MAKE(0x8E, 0x24, 0xAA), LV_COLOR_MAKE(0x7B, 0x1F, 0xA2), LV_COLOR_MAKE(0x6A, 0x1B, 0x9A), LV_COLOR_MAKE(0x4A, 0x14, 0x8C)},
+ {LV_COLOR_MAKE(0x5E, 0x35, 0xB1), LV_COLOR_MAKE(0x51, 0x2D, 0xA8), LV_COLOR_MAKE(0x45, 0x27, 0xA0), LV_COLOR_MAKE(0x31, 0x1B, 0x92)},
+ {LV_COLOR_MAKE(0x39, 0x49, 0xAB), LV_COLOR_MAKE(0x30, 0x3F, 0x9F), LV_COLOR_MAKE(0x28, 0x35, 0x93), LV_COLOR_MAKE(0x1A, 0x23, 0x7E)},
+ {LV_COLOR_MAKE(0x1E, 0x88, 0xE5), LV_COLOR_MAKE(0x19, 0x76, 0xD2), LV_COLOR_MAKE(0x15, 0x65, 0xC0), LV_COLOR_MAKE(0x0D, 0x47, 0xA1)},
+ {LV_COLOR_MAKE(0x03, 0x9B, 0xE5), LV_COLOR_MAKE(0x02, 0x88, 0xD1), LV_COLOR_MAKE(0x02, 0x77, 0xBD), LV_COLOR_MAKE(0x01, 0x57, 0x9B)},
+ {LV_COLOR_MAKE(0x00, 0xAC, 0xC1), LV_COLOR_MAKE(0x00, 0x97, 0xA7), LV_COLOR_MAKE(0x00, 0x83, 0x8F), LV_COLOR_MAKE(0x00, 0x60, 0x64)},
+ {LV_COLOR_MAKE(0x00, 0x89, 0x7B), LV_COLOR_MAKE(0x00, 0x79, 0x6B), LV_COLOR_MAKE(0x00, 0x69, 0x5C), LV_COLOR_MAKE(0x00, 0x4D, 0x40)},
+ {LV_COLOR_MAKE(0x43, 0xA0, 0x47), LV_COLOR_MAKE(0x38, 0x8E, 0x3C), LV_COLOR_MAKE(0x2E, 0x7D, 0x32), LV_COLOR_MAKE(0x1B, 0x5E, 0x20)},
+ {LV_COLOR_MAKE(0x7C, 0xB3, 0x42), LV_COLOR_MAKE(0x68, 0x9F, 0x38), LV_COLOR_MAKE(0x55, 0x8B, 0x2F), LV_COLOR_MAKE(0x33, 0x69, 0x1E)},
+ {LV_COLOR_MAKE(0xC0, 0xCA, 0x33), LV_COLOR_MAKE(0xAF, 0xB4, 0x2B), LV_COLOR_MAKE(0x9E, 0x9D, 0x24), LV_COLOR_MAKE(0x82, 0x77, 0x17)},
+ {LV_COLOR_MAKE(0xFD, 0xD8, 0x35), LV_COLOR_MAKE(0xFB, 0xC0, 0x2D), LV_COLOR_MAKE(0xF9, 0xA8, 0x25), LV_COLOR_MAKE(0xF5, 0x7F, 0x17)},
+ {LV_COLOR_MAKE(0xFF, 0xB3, 0x00), LV_COLOR_MAKE(0xFF, 0xA0, 0x00), LV_COLOR_MAKE(0xFF, 0x8F, 0x00), LV_COLOR_MAKE(0xFF, 0x6F, 0x00)},
+ {LV_COLOR_MAKE(0xFB, 0x8C, 0x00), LV_COLOR_MAKE(0xF5, 0x7C, 0x00), LV_COLOR_MAKE(0xEF, 0x6C, 0x00), LV_COLOR_MAKE(0xE6, 0x51, 0x00)},
+ {LV_COLOR_MAKE(0xF4, 0x51, 0x1E), LV_COLOR_MAKE(0xE6, 0x4A, 0x19), LV_COLOR_MAKE(0xD8, 0x43, 0x15), LV_COLOR_MAKE(0xBF, 0x36, 0x0C)},
+ {LV_COLOR_MAKE(0x6D, 0x4C, 0x41), LV_COLOR_MAKE(0x5D, 0x40, 0x37), LV_COLOR_MAKE(0x4E, 0x34, 0x2E), LV_COLOR_MAKE(0x3E, 0x27, 0x23)},
+ {LV_COLOR_MAKE(0x54, 0x6E, 0x7A), LV_COLOR_MAKE(0x45, 0x5A, 0x64), LV_COLOR_MAKE(0x37, 0x47, 0x4F), LV_COLOR_MAKE(0x26, 0x32, 0x38)},
+ {LV_COLOR_MAKE(0x75, 0x75, 0x75), LV_COLOR_MAKE(0x61, 0x61, 0x61), LV_COLOR_MAKE(0x42, 0x42, 0x42), LV_COLOR_MAKE(0x21, 0x21, 0x21)},
+ };
+
+ if(p >= _LV_PALETTE_LAST) {
+ LV_LOG_WARN("Invalid palette: %d", p);
+ return lv_color_black();
+ }
+
+ if(lvl == 0 || lvl > 4) {
+ LV_LOG_WARN("Invalid level: %d. Must be 1..4", lvl);
+ return lv_color_black();
+ }
+
+ lvl--;
+
+ return colors[p][lvl];
+}
diff --git a/lib/lvgl/src/misc/lv_color.h b/lib/lvgl/src/misc/lv_color.h
new file mode 100644
index 00000000..2cc92f27
--- /dev/null
+++ b/lib/lvgl/src/misc/lv_color.h
@@ -0,0 +1,708 @@
+/**
+ * @file lv_color.h
+ *
+ */
+
+#ifndef LV_COLOR_H
+#define LV_COLOR_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/*********************
+ * INCLUDES
+ *********************/
+#include "../lv_conf_internal.h"
+#include "lv_assert.h"
+#include "lv_math.h"
+#include "lv_types.h"
+
+/*Error checking*/
+#if LV_COLOR_DEPTH == 24
+#error "LV_COLOR_DEPTH 24 is deprecated. Use LV_COLOR_DEPTH 32 instead (lv_conf.h)"
+#endif
+
+#if LV_COLOR_DEPTH != 16 && LV_COLOR_16_SWAP != 0
+#error "LV_COLOR_16_SWAP requires LV_COLOR_DEPTH == 16. Set it in lv_conf.h"
+#endif
+
+#include <stdint.h>
+
+/*********************
+ * DEFINES
+ *********************/
+LV_EXPORT_CONST_INT(LV_COLOR_DEPTH);
+LV_EXPORT_CONST_INT(LV_COLOR_16_SWAP);
+
+/**
+ * Opacity percentages.
+ */
+enum {
+ LV_OPA_TRANSP = 0,
+ LV_OPA_0 = 0,
+ LV_OPA_10 = 25,
+ LV_OPA_20 = 51,
+ LV_OPA_30 = 76,
+ LV_OPA_40 = 102,
+ LV_OPA_50 = 127,
+ LV_OPA_60 = 153,
+ LV_OPA_70 = 178,
+ LV_OPA_80 = 204,
+ LV_OPA_90 = 229,
+ LV_OPA_100 = 255,
+ LV_OPA_COVER = 255,
+};
+
+#define LV_OPA_MIN 2 /*Opacities below this will be transparent*/
+#define LV_OPA_MAX 253 /*Opacities above this will fully cover*/
+
+#if LV_COLOR_DEPTH == 1
+#define LV_COLOR_SIZE 8
+#elif LV_COLOR_DEPTH == 8
+#define LV_COLOR_SIZE 8
+#elif LV_COLOR_DEPTH == 16
+#define LV_COLOR_SIZE 16
+#elif LV_COLOR_DEPTH == 32
+#define LV_COLOR_SIZE 32
+#else
+#error "Invalid LV_COLOR_DEPTH in lv_conf.h! Set it to 1, 8, 16 or 32!"
+#endif
+
+#if defined(__cplusplus) && !defined(_LV_COLOR_HAS_MODERN_CPP)
+/**
+* MSVC compiler's definition of the __cplusplus indicating 199711L regardless to C++ standard version
+* see https://devblogs.microsoft.com/cppblog/msvc-now-correctly-reports-cplusplus
+* so we use _MSC_VER macro instead of __cplusplus
+*/
+#ifdef _MSC_VER
+#if _MSC_VER >= 1900 /*Visual Studio 2015*/
+#define _LV_COLOR_HAS_MODERN_CPP 1
+#endif
+#else
+#if __cplusplus >= 201103L
+#define _LV_COLOR_HAS_MODERN_CPP 1
+#endif
+#endif
+#endif /*__cplusplus*/
+
+#ifndef _LV_COLOR_HAS_MODERN_CPP
+#define _LV_COLOR_HAS_MODERN_CPP 0
+#endif
+
+#if _LV_COLOR_HAS_MODERN_CPP
+/*Fix msvc compiler error C4576 inside C++ code*/
+#define _LV_COLOR_MAKE_TYPE_HELPER lv_color_t
+#else
+#define _LV_COLOR_MAKE_TYPE_HELPER (lv_color_t)
+#endif
+
+/*---------------------------------------
+ * Macros for all existing color depths
+ * to set/get values of the color channels
+ *------------------------------------------*/
+# define LV_COLOR_SET_R1(c, v) (c).ch.red = (uint8_t)((v) & 0x1)
+# define LV_COLOR_SET_G1(c, v) (c).ch.green = (uint8_t)((v) & 0x1)
+# define LV_COLOR_SET_B1(c, v) (c).ch.blue = (uint8_t)((v) & 0x1)
+# define LV_COLOR_SET_A1(c, v) do {} while(0)
+
+# define LV_COLOR_GET_R1(c) (c).ch.red
+# define LV_COLOR_GET_G1(c) (c).ch.green
+# define LV_COLOR_GET_B1(c) (c).ch.blue
+# define LV_COLOR_GET_A1(c) 0xFF
+
+# define _LV_COLOR_ZERO_INITIALIZER1 {0x00}
+# define LV_COLOR_MAKE1(r8, g8, b8) {(uint8_t)((b8 >> 7) | (g8 >> 7) | (r8 >> 7))}
+
+# define LV_COLOR_SET_R8(c, v) (c).ch.red = (uint8_t)((v) & 0x7U)
+# define LV_COLOR_SET_G8(c, v) (c).ch.green = (uint8_t)((v) & 0x7U)
+# define LV_COLOR_SET_B8(c, v) (c).ch.blue = (uint8_t)((v) & 0x3U)
+# define LV_COLOR_SET_A8(c, v) do {} while(0)
+
+# define LV_COLOR_GET_R8(c) (c).ch.red
+# define LV_COLOR_GET_G8(c) (c).ch.green
+# define LV_COLOR_GET_B8(c) (c).ch.blue
+# define LV_COLOR_GET_A8(c) 0xFF
+
+# define _LV_COLOR_ZERO_INITIALIZER8 {{0x00, 0x00, 0x00}}
+# define LV_COLOR_MAKE8(r8, g8, b8) {{(uint8_t)((b8 >> 6) & 0x3U), (uint8_t)((g8 >> 5) & 0x7U), (uint8_t)((r8 >> 5) & 0x7U)}}
+
+# define LV_COLOR_SET_R16(c, v) (c).ch.red = (uint8_t)((v) & 0x1FU)
+#if LV_COLOR_16_SWAP == 0
+# define LV_COLOR_SET_G16(c, v) (c).ch.green = (uint8_t)((v) & 0x3FU)
+#else
+# define LV_COLOR_SET_G16(c, v) {(c).ch.green_h = (uint8_t)(((v) >> 3) & 0x7); (c).ch.green_l = (uint8_t)((v) & 0x7);}
+#endif
+# define LV_COLOR_SET_B16(c, v) (c).ch.blue = (uint8_t)((v) & 0x1FU)
+# define LV_COLOR_SET_A16(c, v) do {} while(0)
+
+# define LV_COLOR_GET_R16(c) (c).ch.red
+#if LV_COLOR_16_SWAP == 0
+# define LV_COLOR_GET_G16(c) (c).ch.green
+#else
+# define LV_COLOR_GET_G16(c) (((c).ch.green_h << 3) + (c).ch.green_l)
+#endif
+# define LV_COLOR_GET_B16(c) (c).ch.blue
+# define LV_COLOR_GET_A16(c) 0xFF
+
+#if LV_COLOR_16_SWAP == 0
+# define _LV_COLOR_ZERO_INITIALIZER16 {{0x00, 0x00, 0x00}}
+# define LV_COLOR_MAKE16(r8, g8, b8) {{(uint8_t)((b8 >> 3) & 0x1FU), (uint8_t)((g8 >> 2) & 0x3FU), (uint8_t)((r8 >> 3) & 0x1FU)}}
+#else
+# define _LV_COLOR_ZERO_INITIALIZER16 {{0x00, 0x00, 0x00, 0x00}}
+# define LV_COLOR_MAKE16(r8, g8, b8) {{(uint8_t)((g8 >> 5) & 0x7U), (uint8_t)((r8 >> 3) & 0x1FU), (uint8_t)((b8 >> 3) & 0x1FU), (uint8_t)((g8 >> 2) & 0x7U)}}
+#endif
+
+# define LV_COLOR_SET_R32(c, v) (c).ch.red = (uint8_t)((v) & 0xFF)
+# define LV_COLOR_SET_G32(c, v) (c).ch.green = (uint8_t)((v) & 0xFF)
+# define LV_COLOR_SET_B32(c, v) (c).ch.blue = (uint8_t)((v) & 0xFF)
+# define LV_COLOR_SET_A32(c, v) (c).ch.alpha = (uint8_t)((v) & 0xFF)
+
+# define LV_COLOR_GET_R32(c) (c).ch.red
+# define LV_COLOR_GET_G32(c) (c).ch.green
+# define LV_COLOR_GET_B32(c) (c).ch.blue
+# define LV_COLOR_GET_A32(c) (c).ch.alpha
+
+# define _LV_COLOR_ZERO_INITIALIZER32 {{0x00, 0x00, 0x00, 0x00}}
+# define LV_COLOR_MAKE32(r8, g8, b8) {{b8, g8, r8, 0xff}} /*Fix 0xff alpha*/
+
+/*---------------------------------------
+ * Macros for the current color depth
+ * to set/get values of the color channels
+ *------------------------------------------*/
+#define LV_COLOR_SET_R(c, v) LV_CONCAT(LV_COLOR_SET_R, LV_COLOR_DEPTH)(c, v)
+#define LV_COLOR_SET_G(c, v) LV_CONCAT(LV_COLOR_SET_G, LV_COLOR_DEPTH)(c, v)
+#define LV_COLOR_SET_B(c, v) LV_CONCAT(LV_COLOR_SET_B, LV_COLOR_DEPTH)(c, v)
+#define LV_COLOR_SET_A(c, v) LV_CONCAT(LV_COLOR_SET_A, LV_COLOR_DEPTH)(c, v)
+
+#define LV_COLOR_GET_R(c) LV_CONCAT(LV_COLOR_GET_R, LV_COLOR_DEPTH)(c)
+#define LV_COLOR_GET_G(c) LV_CONCAT(LV_COLOR_GET_G, LV_COLOR_DEPTH)(c)
+#define LV_COLOR_GET_B(c) LV_CONCAT(LV_COLOR_GET_B, LV_COLOR_DEPTH)(c)
+#define LV_COLOR_GET_A(c) LV_CONCAT(LV_COLOR_GET_A, LV_COLOR_DEPTH)(c)
+
+#define _LV_COLOR_ZERO_INITIALIZER LV_CONCAT(_LV_COLOR_ZERO_INITIALIZER, LV_COLOR_DEPTH)
+#define LV_COLOR_MAKE(r8, g8, b8) LV_CONCAT(LV_COLOR_MAKE, LV_COLOR_DEPTH)(r8, g8, b8)
+
+/**********************
+ * TYPEDEFS
+ **********************/
+
+typedef union {
+ uint8_t full; /*must be declared first to set all bits of byte via initializer list*/
+ union {
+ uint8_t blue : 1;
+ uint8_t green : 1;
+ uint8_t red : 1;
+ } ch;
+} lv_color1_t;
+
+typedef union {
+ struct {
+ uint8_t blue : 2;
+ uint8_t green : 3;
+ uint8_t red : 3;
+ } ch;
+ uint8_t full;
+} lv_color8_t;
+
+typedef union {
+ struct {
+#if LV_COLOR_16_SWAP == 0
+ uint16_t blue : 5;
+ uint16_t green : 6;
+ uint16_t red : 5;
+#else
+ uint16_t green_h : 3;
+ uint16_t red : 5;
+ uint16_t blue : 5;
+ uint16_t green_l : 3;
+#endif
+ } ch;
+ uint16_t full;
+} lv_color16_t;
+
+typedef union {
+ struct {
+ uint8_t blue;
+ uint8_t green;
+ uint8_t red;
+ uint8_t alpha;
+ } ch;
+ uint32_t full;
+} lv_color32_t;
+
+typedef LV_CONCAT3(uint, LV_COLOR_SIZE, _t) lv_color_int_t;
+typedef LV_CONCAT3(lv_color, LV_COLOR_DEPTH, _t) lv_color_t;
+
+typedef struct {
+ uint16_t h;
+ uint8_t s;
+ uint8_t v;
+} lv_color_hsv_t;
+
+//! @cond Doxygen_Suppress
+/*No idea where the guard is required but else throws warnings in the docs*/
+typedef uint8_t lv_opa_t;
+//! @endcond
+
+struct _lv_color_filter_dsc_t;
+
+typedef lv_color_t (*lv_color_filter_cb_t)(const struct _lv_color_filter_dsc_t *, lv_color_t, lv_opa_t);
+
+typedef struct _lv_color_filter_dsc_t {
+ lv_color_filter_cb_t filter_cb;
+ void * user_data;
+} lv_color_filter_dsc_t;
+
+
+typedef enum {
+ LV_PALETTE_RED,
+ LV_PALETTE_PINK,
+ LV_PALETTE_PURPLE,
+ LV_PALETTE_DEEP_PURPLE,
+ LV_PALETTE_INDIGO,
+ LV_PALETTE_BLUE,
+ LV_PALETTE_LIGHT_BLUE,
+ LV_PALETTE_CYAN,
+ LV_PALETTE_TEAL,
+ LV_PALETTE_GREEN,
+ LV_PALETTE_LIGHT_GREEN,
+ LV_PALETTE_LIME,
+ LV_PALETTE_YELLOW,
+ LV_PALETTE_AMBER,
+ LV_PALETTE_ORANGE,
+ LV_PALETTE_DEEP_ORANGE,
+ LV_PALETTE_BROWN,
+ LV_PALETTE_BLUE_GREY,
+ LV_PALETTE_GREY,
+ _LV_PALETTE_LAST,
+ LV_PALETTE_NONE = 0xff,
+} lv_palette_t;
+
+/**********************
+ * GLOBAL PROTOTYPES
+ **********************/
+
+/*In color conversations:
+ * - When converting to bigger color type the LSB weight of 1 LSB is calculated
+ * E.g. 16 bit Red has 5 bits
+ * 8 bit Red has 3 bits
+ * ----------------------
+ * 8 bit red LSB = (2^5 - 1) / (2^3 - 1) = 31 / 7 = 4
+ *
+ * - When calculating to smaller color type simply shift out the LSBs
+ * E.g. 8 bit Red has 3 bits
+ * 16 bit Red has 5 bits
+ * ----------------------
+ * Shift right with 5 - 3 = 2
+ */
+static inline uint8_t lv_color_to1(lv_color_t color)
+{
+#if LV_COLOR_DEPTH == 1
+ return color.full;
+#elif LV_COLOR_DEPTH == 8
+ if((LV_COLOR_GET_R(color) & 0x4) || (LV_COLOR_GET_G(color) & 0x4) || (LV_COLOR_GET_B(color) & 0x2)) {
+ return 1;
+ }
+ else {
+ return 0;
+ }
+#elif LV_COLOR_DEPTH == 16
+ if((LV_COLOR_GET_R(color) & 0x10) || (LV_COLOR_GET_G(color) & 0x20) || (LV_COLOR_GET_B(color) & 0x10)) {
+ return 1;
+ }
+ else {
+ return 0;
+ }
+#elif LV_COLOR_DEPTH == 32
+ if((LV_COLOR_GET_R(color) & 0x80) || (LV_COLOR_GET_G(color) & 0x80) || (LV_COLOR_GET_B(color) & 0x80)) {
+ return 1;
+ }
+ else {
+ return 0;
+ }
+#endif
+}
+
+static inline uint8_t lv_color_to8(lv_color_t color)
+{
+#if LV_COLOR_DEPTH == 1
+ if(color.full == 0)
+ return 0;
+ else
+ return 0xFF;
+#elif LV_COLOR_DEPTH == 8
+ return color.full;
+#elif LV_COLOR_DEPTH == 16
+ lv_color8_t ret;
+ LV_COLOR_SET_R8(ret, LV_COLOR_GET_R(color) >> 2); /*5 - 3 = 2*/
+ LV_COLOR_SET_G8(ret, LV_COLOR_GET_G(color) >> 3); /*6 - 3 = 3*/
+ LV_COLOR_SET_B8(ret, LV_COLOR_GET_B(color) >> 3); /*5 - 2 = 3*/
+ return ret.full;
+#elif LV_COLOR_DEPTH == 32
+ lv_color8_t ret;
+ LV_COLOR_SET_R8(ret, LV_COLOR_GET_R(color) >> 5); /*8 - 3 = 5*/
+ LV_COLOR_SET_G8(ret, LV_COLOR_GET_G(color) >> 5); /*8 - 3 = 5*/
+ LV_COLOR_SET_B8(ret, LV_COLOR_GET_B(color) >> 6); /*8 - 2 = 6*/
+ return ret.full;
+#endif
+}
+
+static inline uint16_t lv_color_to16(lv_color_t color)
+{
+#if LV_COLOR_DEPTH == 1
+ if(color.full == 0)
+ return 0;
+ else
+ return 0xFFFF;
+#elif LV_COLOR_DEPTH == 8
+ lv_color16_t ret;
+ LV_COLOR_SET_R16(ret, LV_COLOR_GET_R(color) * 4); /*(2^5 - 1)/(2^3 - 1) = 31/7 = 4*/
+ LV_COLOR_SET_G16(ret, LV_COLOR_GET_G(color) * 9); /*(2^6 - 1)/(2^3 - 1) = 63/7 = 9*/
+ LV_COLOR_SET_B16(ret, LV_COLOR_GET_B(color) * 10); /*(2^5 - 1)/(2^2 - 1) = 31/3 = 10*/
+ return ret.full;
+#elif LV_COLOR_DEPTH == 16
+ return color.full;
+#elif LV_COLOR_DEPTH == 32
+ lv_color16_t ret;
+ LV_COLOR_SET_R16(ret, LV_COLOR_GET_R(color) >> 3); /*8 - 5 = 3*/
+ LV_COLOR_SET_G16(ret, LV_COLOR_GET_G(color) >> 2); /*8 - 6 = 2*/
+ LV_COLOR_SET_B16(ret, LV_COLOR_GET_B(color) >> 3); /*8 - 5 = 3*/
+ return ret.full;
+#endif
+}
+
+static inline uint32_t lv_color_to32(lv_color_t color)
+{
+#if LV_COLOR_DEPTH == 1
+ if(color.full == 0)
+ return 0xFF000000;
+ else
+ return 0xFFFFFFFF;
+#elif LV_COLOR_DEPTH == 8
+ lv_color32_t ret;
+ LV_COLOR_SET_R32(ret, LV_COLOR_GET_R(color) * 36); /*(2^8 - 1)/(2^3 - 1) = 255/7 = 36*/
+ LV_COLOR_SET_G32(ret, LV_COLOR_GET_G(color) * 36); /*(2^8 - 1)/(2^3 - 1) = 255/7 = 36*/
+ LV_COLOR_SET_B32(ret, LV_COLOR_GET_B(color) * 85); /*(2^8 - 1)/(2^2 - 1) = 255/3 = 85*/
+ LV_COLOR_SET_A32(ret, 0xFF);
+ return ret.full;
+#elif LV_COLOR_DEPTH == 16
+ /**
+ * The floating point math for conversion is:
+ * valueto = valuefrom * ( (2^bitsto - 1) / (float)(2^bitsfrom - 1) )
+ * The faster integer math for conversion is:
+ * valueto = ( valuefrom * multiplier + adder ) >> divisor
+ * multiplier = FLOOR( ( (2^bitsto - 1) << divisor ) / (float)(2^bitsfrom - 1) )
+ *
+ * Find the first divisor where ( adder >> divisor ) <= 0
+ *
+ * 5-bit to 8-bit: ( 31 * multiplier + adder ) >> divisor = 255
+ * divisor multiplier adder min (0) max (31)
+ * 0 8 7 7 255
+ * 1 16 14 7 255
+ * 2 32 28 7 255
+ * 3 65 25 3 255
+ * 4 131 19 1 255
+ * 5 263 7 0 255
+ *
+ * 6-bit to 8-bit: 255 = ( 63 * multiplier + adder ) >> divisor
+ * divisor multiplier adder min (0) max (63)
+ * 0 4 3 3 255
+ * 1 8 6 3 255
+ * 2 16 12 3 255
+ * 3 32 24 3 255
+ * 4 64 48 3 255
+ * 5 129 33 1 255
+ * 6 259 3 0 255
+ */
+
+ lv_color32_t ret;
+ LV_COLOR_SET_R32(ret, (LV_COLOR_GET_R(color) * 263 + 7) >> 5);
+ LV_COLOR_SET_G32(ret, (LV_COLOR_GET_G(color) * 259 + 3) >> 6);
+ LV_COLOR_SET_B32(ret, (LV_COLOR_GET_B(color) * 263 + 7) >> 5);
+ LV_COLOR_SET_A32(ret, 0xFF);
+ return ret.full;
+#elif LV_COLOR_DEPTH == 32
+ return color.full;
+#endif
+}
+
+//! @cond Doxygen_Suppress
+
+/**
+ * Mix two colors with a given ratio.
+ * @param c1 the first color to mix (usually the foreground)
+ * @param c2 the second color to mix (usually the background)
+ * @param mix The ratio of the colors. 0: full `c2`, 255: full `c1`, 127: half `c1` and half`c2`
+ * @return the mixed color
+ */
+LV_ATTRIBUTE_FAST_MEM static inline lv_color_t lv_color_mix(lv_color_t c1, lv_color_t c2, uint8_t mix)
+{
+ lv_color_t ret;
+
+#if LV_COLOR_DEPTH == 16 && LV_COLOR_16_SWAP == 0 && LV_COLOR_MIX_ROUND_OFS == 0
+ /*Source: https://stackoverflow.com/a/50012418/1999969*/
+ mix = (uint32_t)((uint32_t)mix + 4) >> 3;
+ uint32_t bg = (uint32_t)((uint32_t)c2.full | ((uint32_t)c2.full << 16)) &
+ 0x7E0F81F; /*0b00000111111000001111100000011111*/
+ uint32_t fg = (uint32_t)((uint32_t)c1.full | ((uint32_t)c1.full << 16)) & 0x7E0F81F;
+ uint32_t result = ((((fg - bg) * mix) >> 5) + bg) & 0x7E0F81F;
+ ret.full = (uint16_t)((result >> 16) | result);
+#elif LV_COLOR_DEPTH != 1
+ /*LV_COLOR_DEPTH == 8, 16 or 32*/
+ LV_COLOR_SET_R(ret, LV_UDIV255((uint16_t)LV_COLOR_GET_R(c1) * mix + LV_COLOR_GET_R(c2) *
+ (255 - mix) + LV_COLOR_MIX_ROUND_OFS));
+ LV_COLOR_SET_G(ret, LV_UDIV255((uint16_t)LV_COLOR_GET_G(c1) * mix + LV_COLOR_GET_G(c2) *
+ (255 - mix) + LV_COLOR_MIX_ROUND_OFS));
+ LV_COLOR_SET_B(ret, LV_UDIV255((uint16_t)LV_COLOR_GET_B(c1) * mix + LV_COLOR_GET_B(c2) *
+ (255 - mix) + LV_COLOR_MIX_ROUND_OFS));
+ LV_COLOR_SET_A(ret, 0xFF);
+#else
+ /*LV_COLOR_DEPTH == 1*/
+ ret.full = mix > LV_OPA_50 ? c1.full : c2.full;
+#endif
+
+ return ret;
+}
+
+LV_ATTRIBUTE_FAST_MEM static inline void lv_color_premult(lv_color_t c, uint8_t mix, uint16_t * out)
+{
+#if LV_COLOR_DEPTH != 1
+ out[0] = (uint16_t)LV_COLOR_GET_R(c) * mix;
+ out[1] = (uint16_t)LV_COLOR_GET_G(c) * mix;
+ out[2] = (uint16_t)LV_COLOR_GET_B(c) * mix;
+#else
+ (void) mix;
+ /*Pre-multiplication can't be used with 1 bpp*/
+ out[0] = LV_COLOR_GET_R(c);
+ out[1] = LV_COLOR_GET_G(c);
+ out[2] = LV_COLOR_GET_B(c);
+#endif
+
+}
+
+/**
+ * Mix two colors with a given ratio. It runs faster then `lv_color_mix` but requires some pre computation.
+ * @param premult_c1 The first color. Should be preprocessed with `lv_color_premult(c1)`
+ * @param c2 The second color. As it is no pre computation required on it
+ * @param mix The ratio of the colors. 0: full `c1`, 255: full `c2`, 127: half `c1` and half `c2`.
+ * Should be modified like mix = `255 - mix`
+ * @return the mixed color
+ * @note 255 won't give clearly `c1`.
+ */
+LV_ATTRIBUTE_FAST_MEM static inline lv_color_t lv_color_mix_premult(uint16_t * premult_c1, lv_color_t c2, uint8_t mix)
+{
+ lv_color_t ret;
+#if LV_COLOR_DEPTH != 1
+ /*LV_COLOR_DEPTH == 8 or 32*/
+ LV_COLOR_SET_R(ret, LV_UDIV255(premult_c1[0] + LV_COLOR_GET_R(c2) * mix + LV_COLOR_MIX_ROUND_OFS));
+ LV_COLOR_SET_G(ret, LV_UDIV255(premult_c1[1] + LV_COLOR_GET_G(c2) * mix + LV_COLOR_MIX_ROUND_OFS));
+ LV_COLOR_SET_B(ret, LV_UDIV255(premult_c1[2] + LV_COLOR_GET_B(c2) * mix + LV_COLOR_MIX_ROUND_OFS));
+ LV_COLOR_SET_A(ret, 0xFF);
+#else
+ /*LV_COLOR_DEPTH == 1*/
+ /*Restore color1*/
+ lv_color_t c1;
+ LV_COLOR_SET_R(c1, premult_c1[0]);
+ LV_COLOR_SET_G(c1, premult_c1[1]);
+ LV_COLOR_SET_B(c1, premult_c1[2]);
+ ret.full = mix > LV_OPA_50 ? c2.full : c1.full;
+#endif
+
+ return ret;
+}
+
+/**
+ * Mix two colors. Both color can have alpha value.
+ * @param bg_color background color
+ * @param bg_opa alpha of the background color
+ * @param fg_color foreground color
+ * @param fg_opa alpha of the foreground color
+ * @param res_color the result color
+ * @param res_opa the result opacity
+ */
+LV_ATTRIBUTE_FAST_MEM static inline void lv_color_mix_with_alpha(lv_color_t bg_color, lv_opa_t bg_opa,
+ lv_color_t fg_color, lv_opa_t fg_opa,
+ lv_color_t * res_color, lv_opa_t * res_opa)
+{
+ /*Pick the foreground if it's fully opaque or the Background is fully transparent*/
+ if(fg_opa >= LV_OPA_MAX || bg_opa <= LV_OPA_MIN) {
+ res_color->full = fg_color.full;
+ *res_opa = fg_opa;
+ }
+ /*Transparent foreground: use the Background*/
+ else if(fg_opa <= LV_OPA_MIN) {
+ res_color->full = bg_color.full;
+ *res_opa = bg_opa;
+ }
+ /*Opaque background: use simple mix*/
+ else if(bg_opa >= LV_OPA_MAX) {
+ *res_color = lv_color_mix(fg_color, bg_color, fg_opa);
+ *res_opa = LV_OPA_COVER;
+ }
+ /*Both colors have alpha. Expensive calculation need to be applied*/
+ else {
+ /*Save the parameters and the result. If they will be asked again don't compute again*/
+ static lv_opa_t fg_opa_save = 0;
+ static lv_opa_t bg_opa_save = 0;
+ static lv_color_t fg_color_save = _LV_COLOR_ZERO_INITIALIZER;
+ static lv_color_t bg_color_save = _LV_COLOR_ZERO_INITIALIZER;
+ static lv_color_t res_color_saved = _LV_COLOR_ZERO_INITIALIZER;
+ static lv_opa_t res_opa_saved = 0;
+
+ if(fg_opa != fg_opa_save || bg_opa != bg_opa_save || fg_color.full != fg_color_save.full ||
+ bg_color.full != bg_color_save.full) {
+ fg_opa_save = fg_opa;
+ bg_opa_save = bg_opa;
+ fg_color_save.full = fg_color.full;
+ bg_color_save.full = bg_color.full;
+ /*Info:
+ * https://en.wikipedia.org/wiki/Alpha_compositing#Analytical_derivation_of_the_over_operator*/
+ res_opa_saved = 255 - ((uint16_t)((uint16_t)(255 - fg_opa) * (255 - bg_opa)) >> 8);
+ LV_ASSERT(res_opa_saved != 0);
+ lv_opa_t ratio = (uint16_t)((uint16_t)fg_opa * 255) / res_opa_saved;
+ res_color_saved = lv_color_mix(fg_color, bg_color, ratio);
+
+ }
+
+ res_color->full = res_color_saved.full;
+ *res_opa = res_opa_saved;
+ }
+}
+
+//! @endcond
+
+/**
+ * Get the brightness of a color
+ * @param color a color
+ * @return the brightness [0..255]
+ */
+static inline uint8_t lv_color_brightness(lv_color_t color)
+{
+ lv_color32_t c32;
+ c32.full = lv_color_to32(color);
+ uint16_t bright = (uint16_t)(3u * LV_COLOR_GET_R32(c32) + LV_COLOR_GET_B32(c32) + 4u * LV_COLOR_GET_G32(c32));
+ return (uint8_t)(bright >> 3);
+}
+
+static inline lv_color_t lv_color_make(uint8_t r, uint8_t g, uint8_t b)
+{
+ return _LV_COLOR_MAKE_TYPE_HELPER LV_COLOR_MAKE(r, g, b);
+}
+
+static inline lv_color_t lv_color_hex(uint32_t c)
+{
+#if LV_COLOR_DEPTH == 16
+ lv_color_t r;
+#if LV_COLOR_16_SWAP == 0
+ /* Convert a 4 bytes per pixel in format ARGB32 to R5G6B5 format
+ naive way (by calling lv_color_make with components):
+ r = ((c & 0xFF0000) >> 19)
+ g = ((c & 0xFF00) >> 10)
+ b = ((c & 0xFF) >> 3)
+ rgb565 = (r << 11) | (g << 5) | b
+ That's 3 mask, 5 bitshift and 2 or operations
+
+ A bit better:
+ r = ((c & 0xF80000) >> 8)
+ g = ((c & 0xFC00) >> 5)
+ b = ((c & 0xFF) >> 3)
+ rgb565 = r | g | b
+ That's 3 mask, 3 bitshifts and 2 or operations */
+ r.full = (uint16_t)(((c & 0xF80000) >> 8) | ((c & 0xFC00) >> 5) | ((c & 0xFF) >> 3));
+#else
+ /* We want: rrrr rrrr GGGg gggg bbbb bbbb => gggb bbbb rrrr rGGG */
+ r.full = (uint16_t)(((c & 0xF80000) >> 16) | ((c & 0xFC00) >> 13) | ((c & 0x1C00) << 3) | ((c & 0xF8) << 5));
+#endif
+ return r;
+#elif LV_COLOR_DEPTH == 32
+ lv_color_t r;
+ r.full = c | 0xFF000000;
+ return r;
+#else /*LV_COLOR_DEPTH == 8*/
+ return lv_color_make((uint8_t)((c >> 16) & 0xFF), (uint8_t)((c >> 8) & 0xFF), (uint8_t)(c & 0xFF));
+#endif
+}
+
+static inline lv_color_t lv_color_hex3(uint32_t c)
+{
+ return lv_color_make((uint8_t)(((c >> 4) & 0xF0) | ((c >> 8) & 0xF)), (uint8_t)((c & 0xF0) | ((c & 0xF0) >> 4)),
+ (uint8_t)((c & 0xF) | ((c & 0xF) << 4)));
+}
+
+static inline void lv_color_filter_dsc_init(lv_color_filter_dsc_t * dsc, lv_color_filter_cb_t cb)
+{
+ dsc->filter_cb = cb;
+}
+
+//! @cond Doxygen_Suppress
+//!
+LV_ATTRIBUTE_FAST_MEM void lv_color_fill(lv_color_t * buf, lv_color_t color, uint32_t px_num);
+
+//! @endcond
+lv_color_t lv_color_lighten(lv_color_t c, lv_opa_t lvl);
+
+lv_color_t lv_color_darken(lv_color_t c, lv_opa_t lvl);
+
+lv_color_t lv_color_change_lightness(lv_color_t c, lv_opa_t lvl);
+
+/**
+ * Convert a HSV color to RGB
+ * @param h hue [0..359]
+ * @param s saturation [0..100]
+ * @param v value [0..100]
+ * @return the given RGB color in RGB (with LV_COLOR_DEPTH depth)
+ */
+lv_color_t lv_color_hsv_to_rgb(uint16_t h, uint8_t s, uint8_t v);
+
+/**
+ * Convert a 32-bit RGB color to HSV
+ * @param r8 8-bit red
+ * @param g8 8-bit green
+ * @param b8 8-bit blue
+ * @return the given RGB color in HSV
+ */
+lv_color_hsv_t lv_color_rgb_to_hsv(uint8_t r8, uint8_t g8, uint8_t b8);
+
+/**
+ * Convert a color to HSV
+ * @param color color
+ * @return the given color in HSV
+ */
+lv_color_hsv_t lv_color_to_hsv(lv_color_t color);
+
+/**
+ * Just a wrapper around LV_COLOR_CHROMA_KEY because it might be more convenient to use a function in some cases
+ * @return LV_COLOR_CHROMA_KEY
+ */
+static inline lv_color_t lv_color_chroma_key(void)
+{
+ return LV_COLOR_CHROMA_KEY;
+}
+
+/**********************
+ * PREDEFINED COLORS
+ **********************/
+/*Source: https://vuetifyjs.com/en/styles/colors/#material-colors*/
+
+lv_color_t lv_palette_main(lv_palette_t p);
+static inline lv_color_t lv_color_white(void)
+{
+ return lv_color_make(0xff, 0xff, 0xff);
+}
+static inline lv_color_t lv_color_black(void)
+{
+ return lv_color_make(0x00, 0x0, 0x00);
+}
+lv_color_t lv_palette_lighten(lv_palette_t p, uint8_t lvl);
+lv_color_t lv_palette_darken(lv_palette_t p, uint8_t lvl);
+
+/**********************
+ * MACROS
+ **********************/
+
+#ifdef __cplusplus
+} /*extern "C"*/
+#endif
+
+#endif /*LV_COLOR_H*/
diff --git a/lib/lvgl/src/misc/lv_fs.c b/lib/lvgl/src/misc/lv_fs.c
new file mode 100644
index 00000000..52f3ce07
--- /dev/null
+++ b/lib/lvgl/src/misc/lv_fs.c
@@ -0,0 +1,518 @@
+/**
+ * @file lv_fs.c
+ *
+ */
+
+/*********************
+ * INCLUDES
+ *********************/
+#include "lv_fs.h"
+
+#include "../misc/lv_assert.h"
+#include "lv_ll.h"
+#include <string.h>
+#include "lv_gc.h"
+
+/*********************
+ * DEFINES
+ *********************/
+
+/**********************
+ * TYPEDEFS
+ **********************/
+
+/**********************
+ * STATIC PROTOTYPES
+ **********************/
+static const char * lv_fs_get_real_path(const char * path);
+
+/**********************
+ * STATIC VARIABLES
+ **********************/
+
+/**********************
+ * MACROS
+ **********************/
+
+/**********************
+ * GLOBAL FUNCTIONS
+ **********************/
+
+void _lv_fs_init(void)
+{
+ _lv_ll_init(&LV_GC_ROOT(_lv_fsdrv_ll), sizeof(lv_fs_drv_t *));
+}
+
+bool lv_fs_is_ready(char letter)
+{
+ lv_fs_drv_t * drv = lv_fs_get_drv(letter);
+
+ if(drv == NULL) return false; /*An unknown driver in not ready*/
+
+ if(drv->ready_cb == NULL) return true; /*Assume the driver is always ready if no handler provided*/
+
+ return drv->ready_cb(drv);
+}
+
+lv_fs_res_t lv_fs_open(lv_fs_file_t * file_p, const char * path, lv_fs_mode_t mode)
+{
+ if(path == NULL) {
+ LV_LOG_WARN("Can't open file: path is NULL");
+ return LV_FS_RES_INV_PARAM;
+ }
+
+ char letter = path[0];
+ lv_fs_drv_t * drv = lv_fs_get_drv(letter);
+
+ if(drv == NULL) {
+ LV_LOG_WARN("Can't open file (%s): unknown driver letter", path);
+ return LV_FS_RES_NOT_EX;
+ }
+
+ if(drv->ready_cb) {
+ if(drv->ready_cb(drv) == false) {
+ LV_LOG_WARN("Can't open file (%s): driver not ready", path);
+ return LV_FS_RES_HW_ERR;
+ }
+ }
+
+ if(drv->open_cb == NULL) {
+ LV_LOG_WARN("Can't open file (%s): open function not exists", path);
+ return LV_FS_RES_NOT_IMP;
+ }
+
+ const char * real_path = lv_fs_get_real_path(path);
+ void * file_d = drv->open_cb(drv, real_path, mode);
+
+ if(file_d == NULL || file_d == (void *)(-1)) {
+ return LV_FS_RES_UNKNOWN;
+ }
+
+ file_p->drv = drv;
+ file_p->file_d = file_d;
+
+ if(drv->cache_size) {
+ file_p->cache = lv_mem_alloc(sizeof(lv_fs_file_cache_t));
+ LV_ASSERT_MALLOC(file_p->cache);
+ lv_memset_00(file_p->cache, sizeof(lv_fs_file_cache_t));
+ file_p->cache->start = UINT32_MAX; /*Set an invalid range by default*/
+ file_p->cache->end = UINT32_MAX - 1;
+ }
+
+ return LV_FS_RES_OK;
+}
+
+lv_fs_res_t lv_fs_close(lv_fs_file_t * file_p)
+{
+ if(file_p->drv == NULL) {
+ return LV_FS_RES_INV_PARAM;
+ }
+
+ if(file_p->drv->close_cb == NULL) {
+ return LV_FS_RES_NOT_IMP;
+ }
+
+ lv_fs_res_t res = file_p->drv->close_cb(file_p->drv, file_p->file_d);
+
+ if(file_p->drv->cache_size && file_p->cache) {
+ if(file_p->cache->buffer) {
+ lv_mem_free(file_p->cache->buffer);
+ }
+
+ lv_mem_free(file_p->cache);
+ }
+
+ file_p->file_d = NULL;
+ file_p->drv = NULL;
+ file_p->cache = NULL;
+
+ return res;
+}
+
+static lv_fs_res_t lv_fs_read_cached(lv_fs_file_t * file_p, char * buf, uint32_t btr, uint32_t * br)
+{
+ lv_fs_res_t res = LV_FS_RES_OK;
+ uint32_t file_position = file_p->cache->file_position;
+ uint32_t start = file_p->cache->start;
+ uint32_t end = file_p->cache->end;
+ char * buffer = file_p->cache->buffer;
+ uint16_t buffer_size = file_p->drv->cache_size;
+
+ if(start <= file_position && file_position < end) {
+ /* Data can be read from cache buffer */
+ uint16_t buffer_offset = file_position - start;
+ uint32_t buffer_remaining_length = LV_MIN((uint32_t)buffer_size - buffer_offset, (uint32_t)end - file_position);
+
+ if(btr <= buffer_remaining_length) {
+ /*Data is in cache buffer, and buffer end not reached, no need to read from FS*/
+ lv_memcpy(buf, buffer + buffer_offset, btr);
+ *br = btr;
+ }
+ else {
+ /*First part of data is in cache buffer, but we need to read rest of data from FS*/
+ lv_memcpy(buf, buffer + buffer_offset, buffer_remaining_length);
+
+ uint32_t bytes_read_to_buffer = 0;
+ if(btr > buffer_size) {
+ /*If remaining data chuck is bigger than buffer size, then do not use cache, instead read it directly from FS*/
+ res = file_p->drv->read_cb(file_p->drv, file_p->file_d, (void *)(buf + buffer_remaining_length),
+ btr - buffer_remaining_length, &bytes_read_to_buffer);
+ }
+ else {
+ /*If remaining data chunk is smaller than buffer size, then read into cache buffer*/
+ res = file_p->drv->read_cb(file_p->drv, file_p->file_d, (void *)buffer, buffer_size, &bytes_read_to_buffer);
+ file_p->cache->start = file_p->cache->end;
+ file_p->cache->end = file_p->cache->start + bytes_read_to_buffer;
+
+ uint16_t data_chunk_remaining = LV_MIN(btr - buffer_remaining_length, bytes_read_to_buffer);
+ lv_memcpy(buf + buffer_remaining_length, buffer, data_chunk_remaining);
+ }
+ *br = LV_MIN(buffer_remaining_length + bytes_read_to_buffer, btr);
+ }
+ }
+ else {
+ /*Data is not in cache buffer*/
+ if(btr > buffer_size) {
+ /*If bigger data is requested, then do not use cache, instead read it directly*/
+ res = file_p->drv->read_cb(file_p->drv, file_p->file_d, (void *)buf, btr, br);
+ }
+ else {
+ /*If small data is requested, then read from FS into cache buffer*/
+ if(buffer == NULL) {
+ file_p->cache->buffer = lv_mem_alloc(buffer_size);
+ LV_ASSERT_MALLOC(file_p->cache->buffer);
+ buffer = file_p->cache->buffer;
+ }
+
+ uint32_t bytes_read_to_buffer = 0;
+ res = file_p->drv->read_cb(file_p->drv, file_p->file_d, (void *)buffer, buffer_size, &bytes_read_to_buffer);
+ file_p->cache->start = file_position;
+ file_p->cache->end = file_p->cache->start + bytes_read_to_buffer;
+
+ *br = LV_MIN(btr, bytes_read_to_buffer);
+ lv_memcpy(buf, buffer, *br);
+
+ }
+ }
+
+ if(res == LV_FS_RES_OK) {
+ file_p->cache->file_position += *br;
+ }
+
+ return res;
+}
+
+lv_fs_res_t lv_fs_read(lv_fs_file_t * file_p, void * buf, uint32_t btr, uint32_t * br)
+{
+ if(br != NULL) *br = 0;
+ if(file_p->drv == NULL) return LV_FS_RES_INV_PARAM;
+ if(file_p->drv->read_cb == NULL) return LV_FS_RES_NOT_IMP;
+
+ uint32_t br_tmp = 0;
+ lv_fs_res_t res;
+
+ if(file_p->drv->cache_size) {
+ res = lv_fs_read_cached(file_p, (char *)buf, btr, &br_tmp);
+ }
+ else {
+ res = file_p->drv->read_cb(file_p->drv, file_p->file_d, buf, btr, &br_tmp);
+ }
+
+ if(br != NULL) *br = br_tmp;
+
+ return res;
+}
+
+lv_fs_res_t lv_fs_write(lv_fs_file_t * file_p, const void * buf, uint32_t btw, uint32_t * bw)
+{
+ if(bw != NULL) *bw = 0;
+
+ if(file_p->drv == NULL) {
+ return LV_FS_RES_INV_PARAM;
+ }
+
+ if(file_p->drv->write_cb == NULL) {
+ return LV_FS_RES_NOT_IMP;
+ }
+
+ uint32_t bw_tmp = 0;
+ lv_fs_res_t res = file_p->drv->write_cb(file_p->drv, file_p->file_d, buf, btw, &bw_tmp);
+ if(bw != NULL) *bw = bw_tmp;
+
+ return res;
+}
+
+lv_fs_res_t lv_fs_seek(lv_fs_file_t * file_p, uint32_t pos, lv_fs_whence_t whence)
+{
+ if(file_p->drv == NULL) {
+ return LV_FS_RES_INV_PARAM;
+ }
+
+ if(file_p->drv->seek_cb == NULL) {
+ return LV_FS_RES_NOT_IMP;
+ }
+
+ lv_fs_res_t res = LV_FS_RES_OK;
+ if(file_p->drv->cache_size) {
+ switch(whence) {
+ case LV_FS_SEEK_SET: {
+ file_p->cache->file_position = pos;
+
+ /*FS seek if new position is outside cache buffer*/
+ if(file_p->cache->file_position < file_p->cache->start || file_p->cache->file_position > file_p->cache->end) {
+ res = file_p->drv->seek_cb(file_p->drv, file_p->file_d, file_p->cache->file_position, LV_FS_SEEK_SET);
+ }
+
+ break;
+ }
+ case LV_FS_SEEK_CUR: {
+ file_p->cache->file_position += pos;
+
+ /*FS seek if new position is outside cache buffer*/
+ if(file_p->cache->file_position < file_p->cache->start || file_p->cache->file_position > file_p->cache->end) {
+ res = file_p->drv->seek_cb(file_p->drv, file_p->file_d, file_p->cache->file_position, LV_FS_SEEK_SET);
+ }
+
+ break;
+ }
+ case LV_FS_SEEK_END: {
+ /*Because we don't know the file size, we do a little trick: do a FS seek, then get new file position from FS*/
+ res = file_p->drv->seek_cb(file_p->drv, file_p->file_d, pos, whence);
+ if(res == LV_FS_RES_OK) {
+ uint32_t tmp_position;
+ res = file_p->drv->tell_cb(file_p->drv, file_p->file_d, &tmp_position);
+
+ if(res == LV_FS_RES_OK) {
+ file_p->cache->file_position = tmp_position;
+ }
+ }
+ break;
+ }
+ }
+ }
+ else {
+ res = file_p->drv->seek_cb(file_p->drv, file_p->file_d, pos, whence);
+ }
+
+ return res;
+}
+
+lv_fs_res_t lv_fs_tell(lv_fs_file_t * file_p, uint32_t * pos)
+{
+ if(file_p->drv == NULL) {
+ *pos = 0;
+ return LV_FS_RES_INV_PARAM;
+ }
+
+ if(file_p->drv->tell_cb == NULL) {
+ *pos = 0;
+ return LV_FS_RES_NOT_IMP;
+ }
+
+ lv_fs_res_t res;
+ if(file_p->drv->cache_size) {
+ *pos = file_p->cache->file_position;
+ res = LV_FS_RES_OK;
+ }
+ else {
+ res = file_p->drv->tell_cb(file_p->drv, file_p->file_d, pos);
+ }
+
+ return res;
+}
+
+lv_fs_res_t lv_fs_dir_open(lv_fs_dir_t * rddir_p, const char * path)
+{
+ if(path == NULL) return LV_FS_RES_INV_PARAM;
+
+ char letter = path[0];
+ lv_fs_drv_t * drv = lv_fs_get_drv(letter);
+
+ if(drv == NULL) {
+ return LV_FS_RES_NOT_EX;
+ }
+
+ if(drv->ready_cb) {
+ if(drv->ready_cb(drv) == false) {
+ return LV_FS_RES_HW_ERR;
+ }
+ }
+
+ if(drv->dir_open_cb == NULL) {
+ return LV_FS_RES_NOT_IMP;
+ }
+
+ const char * real_path = lv_fs_get_real_path(path);
+ void * dir_d = drv->dir_open_cb(drv, real_path);
+
+ if(dir_d == NULL || dir_d == (void *)(-1)) {
+ return LV_FS_RES_UNKNOWN;
+ }
+
+ rddir_p->drv = drv;
+ rddir_p->dir_d = dir_d;
+
+ return LV_FS_RES_OK;
+}
+
+lv_fs_res_t lv_fs_dir_read(lv_fs_dir_t * rddir_p, char * fn)
+{
+ if(rddir_p->drv == NULL || rddir_p->dir_d == NULL) {
+ fn[0] = '\0';
+ return LV_FS_RES_INV_PARAM;
+ }
+
+ if(rddir_p->drv->dir_read_cb == NULL) {
+ fn[0] = '\0';
+ return LV_FS_RES_NOT_IMP;
+ }
+
+ lv_fs_res_t res = rddir_p->drv->dir_read_cb(rddir_p->drv, rddir_p->dir_d, fn);
+
+ return res;
+}
+
+lv_fs_res_t lv_fs_dir_close(lv_fs_dir_t * rddir_p)
+{
+ if(rddir_p->drv == NULL || rddir_p->dir_d == NULL) {
+ return LV_FS_RES_INV_PARAM;
+ }
+
+ if(rddir_p->drv->dir_close_cb == NULL) {
+ return LV_FS_RES_NOT_IMP;
+ }
+
+ lv_fs_res_t res = rddir_p->drv->dir_close_cb(rddir_p->drv, rddir_p->dir_d);
+
+ rddir_p->dir_d = NULL;
+ rddir_p->drv = NULL;
+
+ return res;
+}
+
+void lv_fs_drv_init(lv_fs_drv_t * drv)
+{
+ lv_memset_00(drv, sizeof(lv_fs_drv_t));
+}
+
+void lv_fs_drv_register(lv_fs_drv_t * drv_p)
+{
+ /*Save the new driver*/
+ lv_fs_drv_t ** new_drv;
+ new_drv = _lv_ll_ins_head(&LV_GC_ROOT(_lv_fsdrv_ll));
+ LV_ASSERT_MALLOC(new_drv);
+ if(new_drv == NULL) return;
+
+ *new_drv = drv_p;
+}
+
+lv_fs_drv_t * lv_fs_get_drv(char letter)
+{
+ lv_fs_drv_t ** drv;
+
+ _LV_LL_READ(&LV_GC_ROOT(_lv_fsdrv_ll), drv) {
+ if((*drv)->letter == letter) {
+ return *drv;
+ }
+ }
+
+ return NULL;
+}
+
+char * lv_fs_get_letters(char * buf)
+{
+ lv_fs_drv_t ** drv;
+ uint8_t i = 0;
+
+ _LV_LL_READ(&LV_GC_ROOT(_lv_fsdrv_ll), drv) {
+ buf[i] = (*drv)->letter;
+ i++;
+ }
+
+ buf[i] = '\0';
+
+ return buf;
+}
+
+const char * lv_fs_get_ext(const char * fn)
+{
+ size_t i;
+ for(i = strlen(fn); i > 0; i--) {
+ if(fn[i] == '.') {
+ return &fn[i + 1];
+ }
+ else if(fn[i] == '/' || fn[i] == '\\') {
+ return ""; /*No extension if a '\' or '/' found*/
+ }
+ }
+
+ return ""; /*Empty string if no '.' in the file name.*/
+}
+
+char * lv_fs_up(char * path)
+{
+ size_t len = strlen(path);
+ if(len == 0) return path;
+
+ len--; /*Go before the trailing '\0'*/
+
+ /*Ignore trailing '/' or '\'*/
+ while(path[len] == '/' || path[len] == '\\') {
+ path[len] = '\0';
+ if(len > 0)
+ len--;
+ else
+ return path;
+ }
+
+ size_t i;
+ for(i = len; i > 0; i--) {
+ if(path[i] == '/' || path[i] == '\\') break;
+ }
+
+ if(i > 0) path[i] = '\0';
+
+ return path;
+}
+
+const char * lv_fs_get_last(const char * path)
+{
+ size_t len = strlen(path);
+ if(len == 0) return path;
+
+ len--; /*Go before the trailing '\0'*/
+
+ /*Ignore trailing '/' or '\'*/
+ while(path[len] == '/' || path[len] == '\\') {
+ if(len > 0)
+ len--;
+ else
+ return path;
+ }
+
+ size_t i;
+ for(i = len; i > 0; i--) {
+ if(path[i] == '/' || path[i] == '\\') break;
+ }
+
+ /*No '/' or '\' in the path so return with path itself*/
+ if(i == 0) return path;
+
+ return &path[i + 1];
+}
+/**********************
+ * STATIC FUNCTIONS
+ **********************/
+
+/**
+ * Skip the driver letter and the possible : after the letter
+ * @param path path string (E.g. S:/folder/file.txt)
+ * @return pointer to the beginning of the real path (E.g. /folder/file.txt)
+ */
+static const char * lv_fs_get_real_path(const char * path)
+{
+ path++; /*Ignore the driver letter*/
+ if(*path == ':') path++;
+
+ return path;
+}
diff --git a/lib/lvgl/src/misc/lv_fs.h b/lib/lvgl/src/misc/lv_fs.h
new file mode 100644
index 00000000..9f65e1b2
--- /dev/null
+++ b/lib/lvgl/src/misc/lv_fs.h
@@ -0,0 +1,262 @@
+/**
+ * @file lv_fs.h
+ *
+ */
+
+#ifndef LV_FS_H
+#define LV_FS_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/*********************
+ * INCLUDES
+ *********************/
+#include "../lv_conf_internal.h"
+
+#include <stdint.h>
+#include <stdbool.h>
+
+/*********************
+ * DEFINES
+ *********************/
+#define LV_FS_MAX_FN_LENGTH 64
+#define LV_FS_MAX_PATH_LENGTH 256
+
+/**********************
+ * TYPEDEFS
+ **********************/
+
+/**
+ * Errors in the file system module.
+ */
+enum {
+ LV_FS_RES_OK = 0,
+ LV_FS_RES_HW_ERR, /*Low level hardware error*/
+ LV_FS_RES_FS_ERR, /*Error in the file system structure*/
+ LV_FS_RES_NOT_EX, /*Driver, file or directory is not exists*/
+ LV_FS_RES_FULL, /*Disk full*/
+ LV_FS_RES_LOCKED, /*The file is already opened*/
+ LV_FS_RES_DENIED, /*Access denied. Check 'fs_open' modes and write protect*/
+ LV_FS_RES_BUSY, /*The file system now can't handle it, try later*/
+ LV_FS_RES_TOUT, /*Process time outed*/
+ LV_FS_RES_NOT_IMP, /*Requested function is not implemented*/
+ LV_FS_RES_OUT_OF_MEM, /*Not enough memory for an internal operation*/
+ LV_FS_RES_INV_PARAM, /*Invalid parameter among arguments*/
+ LV_FS_RES_UNKNOWN, /*Other unknown error*/
+};
+typedef uint8_t lv_fs_res_t;
+
+/**
+ * File open mode.
+ */
+enum {
+ LV_FS_MODE_WR = 0x01,
+ LV_FS_MODE_RD = 0x02,
+};
+typedef uint8_t lv_fs_mode_t;
+
+
+/**
+ * Seek modes.
+ */
+typedef enum {
+ LV_FS_SEEK_SET = 0x00, /**< Set the position from absolutely (from the start of file)*/
+ LV_FS_SEEK_CUR = 0x01, /**< Set the position from the current position*/
+ LV_FS_SEEK_END = 0x02, /**< Set the position from the end of the file*/
+} lv_fs_whence_t;
+
+typedef struct _lv_fs_drv_t {
+ char letter;
+ uint16_t cache_size;
+ bool (*ready_cb)(struct _lv_fs_drv_t * drv);
+
+ void * (*open_cb)(struct _lv_fs_drv_t * drv, const char * path, lv_fs_mode_t mode);
+ lv_fs_res_t (*close_cb)(struct _lv_fs_drv_t * drv, void * file_p);
+ lv_fs_res_t (*read_cb)(struct _lv_fs_drv_t * drv, void * file_p, void * buf, uint32_t btr, uint32_t * br);
+ lv_fs_res_t (*write_cb)(struct _lv_fs_drv_t * drv, void * file_p, const void * buf, uint32_t btw, uint32_t * bw);
+ lv_fs_res_t (*seek_cb)(struct _lv_fs_drv_t * drv, void * file_p, uint32_t pos, lv_fs_whence_t whence);
+ lv_fs_res_t (*tell_cb)(struct _lv_fs_drv_t * drv, void * file_p, uint32_t * pos_p);
+
+ void * (*dir_open_cb)(struct _lv_fs_drv_t * drv, const char * path);
+ lv_fs_res_t (*dir_read_cb)(struct _lv_fs_drv_t * drv, void * rddir_p, char * fn);
+ lv_fs_res_t (*dir_close_cb)(struct _lv_fs_drv_t * drv, void * rddir_p);
+
+#if LV_USE_USER_DATA
+ void * user_data; /**< Custom file user data*/
+#endif
+} lv_fs_drv_t;
+
+typedef struct {
+ uint32_t start;
+ uint32_t end;
+ uint32_t file_position;
+ void * buffer;
+} lv_fs_file_cache_t;
+
+typedef struct {
+ void * file_d;
+ lv_fs_drv_t * drv;
+ lv_fs_file_cache_t * cache;
+} lv_fs_file_t;
+
+typedef struct {
+ void * dir_d;
+ lv_fs_drv_t * drv;
+} lv_fs_dir_t;
+
+/**********************
+ * GLOBAL PROTOTYPES
+ **********************/
+
+/**
+ * Initialize the File system interface
+ */
+void _lv_fs_init(void);
+
+/**
+ * Initialize a file system driver with default values.
+ * It is used to surly have known values in the fields ant not memory junk.
+ * After it you can set the fields.
+ * @param drv pointer to driver variable to initialize
+ */
+void lv_fs_drv_init(lv_fs_drv_t * drv);
+
+/**
+ * Add a new drive
+ * @param drv pointer to an lv_fs_drv_t structure which is inited with the
+ * corresponding function pointers. Only pointer is saved, so the
+ * driver should be static or dynamically allocated.
+ */
+void lv_fs_drv_register(lv_fs_drv_t * drv);
+
+/**
+ * Give a pointer to a driver from its letter
+ * @param letter the driver letter
+ * @return pointer to a driver or NULL if not found
+ */
+lv_fs_drv_t * lv_fs_get_drv(char letter);
+
+/**
+ * Test if a drive is ready or not. If the `ready` function was not initialized `true` will be
+ * returned.
+ * @param letter letter of the drive
+ * @return true: drive is ready; false: drive is not ready
+ */
+bool lv_fs_is_ready(char letter);
+
+/**
+ * Open a file
+ * @param file_p pointer to a lv_fs_file_t variable
+ * @param path path to the file beginning with the driver letter (e.g. S:/folder/file.txt)
+ * @param mode read: FS_MODE_RD, write: FS_MODE_WR, both: FS_MODE_RD | FS_MODE_WR
+ * @return LV_FS_RES_OK or any error from lv_fs_res_t enum
+ */
+lv_fs_res_t lv_fs_open(lv_fs_file_t * file_p, const char * path, lv_fs_mode_t mode);
+
+/**
+ * Close an already opened file
+ * @param file_p pointer to a lv_fs_file_t variable
+ * @return LV_FS_RES_OK or any error from lv_fs_res_t enum
+ */
+lv_fs_res_t lv_fs_close(lv_fs_file_t * file_p);
+
+/**
+ * Read from a file
+ * @param file_p pointer to a lv_fs_file_t variable
+ * @param buf pointer to a buffer where the read bytes are stored
+ * @param btr Bytes To Read
+ * @param br the number of real read bytes (Bytes Read). NULL if unused.
+ * @return LV_FS_RES_OK or any error from lv_fs_res_t enum
+ */
+lv_fs_res_t lv_fs_read(lv_fs_file_t * file_p, void * buf, uint32_t btr, uint32_t * br);
+
+/**
+ * Write into a file
+ * @param file_p pointer to a lv_fs_file_t variable
+ * @param buf pointer to a buffer with the bytes to write
+ * @param btw Bytes To Write
+ * @param bw the number of real written bytes (Bytes Written). NULL if unused.
+ * @return LV_FS_RES_OK or any error from lv_fs_res_t enum
+ */
+lv_fs_res_t lv_fs_write(lv_fs_file_t * file_p, const void * buf, uint32_t btw, uint32_t * bw);
+
+/**
+ * Set the position of the 'cursor' (read write pointer) in a file
+ * @param file_p pointer to a lv_fs_file_t variable
+ * @param pos the new position expressed in bytes index (0: start of file)
+ * @param whence tells from where set the position. See @lv_fs_whence_t
+ * @return LV_FS_RES_OK or any error from lv_fs_res_t enum
+ */
+lv_fs_res_t lv_fs_seek(lv_fs_file_t * file_p, uint32_t pos, lv_fs_whence_t whence);
+
+/**
+ * Give the position of the read write pointer
+ * @param file_p pointer to a lv_fs_file_t variable
+ * @param pos_p pointer to store the position of the read write pointer
+ * @return LV_FS_RES_OK or any error from 'fs_res_t'
+ */
+lv_fs_res_t lv_fs_tell(lv_fs_file_t * file_p, uint32_t * pos);
+
+/**
+ * Initialize a 'fs_dir_t' variable for directory reading
+ * @param rddir_p pointer to a 'lv_fs_dir_t' variable
+ * @param path path to a directory
+ * @return LV_FS_RES_OK or any error from lv_fs_res_t enum
+ */
+lv_fs_res_t lv_fs_dir_open(lv_fs_dir_t * rddir_p, const char * path);
+
+/**
+ * Read the next filename form a directory.
+ * The name of the directories will begin with '/'
+ * @param rddir_p pointer to an initialized 'fs_dir_t' variable
+ * @param fn pointer to a buffer to store the filename
+ * @return LV_FS_RES_OK or any error from lv_fs_res_t enum
+ */
+lv_fs_res_t lv_fs_dir_read(lv_fs_dir_t * rddir_p, char * fn);
+
+/**
+ * Close the directory reading
+ * @param rddir_p pointer to an initialized 'fs_dir_t' variable
+ * @return LV_FS_RES_OK or any error from lv_fs_res_t enum
+ */
+lv_fs_res_t lv_fs_dir_close(lv_fs_dir_t * rddir_p);
+
+/**
+ * Fill a buffer with the letters of existing drivers
+ * @param buf buffer to store the letters ('\0' added after the last letter)
+ * @return the buffer
+ */
+char * lv_fs_get_letters(char * buf);
+
+/**
+ * Return with the extension of the filename
+ * @param fn string with a filename
+ * @return pointer to the beginning extension or empty string if no extension
+ */
+const char * lv_fs_get_ext(const char * fn);
+
+/**
+ * Step up one level
+ * @param path pointer to a file name
+ * @return the truncated file name
+ */
+char * lv_fs_up(char * path);
+
+/**
+ * Get the last element of a path (e.g. U:/folder/file -> file)
+ * @param path pointer to a file name
+ * @return pointer to the beginning of the last element in the path
+ */
+const char * lv_fs_get_last(const char * path);
+
+/**********************
+ * MACROS
+ **********************/
+
+#ifdef __cplusplus
+} /*extern "C"*/
+#endif
+
+#endif /*LV_FS_H*/
diff --git a/lib/lvgl/src/misc/lv_gc.c b/lib/lvgl/src/misc/lv_gc.c
new file mode 100644
index 00000000..95bb5499
--- /dev/null
+++ b/lib/lvgl/src/misc/lv_gc.c
@@ -0,0 +1,47 @@
+/**
+ * @file lv_gc.c
+ *
+ */
+
+/*********************
+ * INCLUDES
+ *********************/
+#include "lv_gc.h"
+
+/*********************
+ * DEFINES
+ *********************/
+
+/**********************
+ * TYPEDEFS
+ **********************/
+
+/**********************
+ * STATIC PROTOTYPES
+ **********************/
+
+/**********************
+ * STATIC VARIABLES
+ **********************/
+
+#if(!defined(LV_ENABLE_GC)) || LV_ENABLE_GC == 0
+ LV_ROOTS
+#endif /*LV_ENABLE_GC*/
+
+/**********************
+ * MACROS
+ **********************/
+
+/**********************
+ * GLOBAL FUNCTIONS
+ **********************/
+
+void _lv_gc_clear_roots(void)
+{
+#define LV_CLEAR_ROOT(root_type, root_name) lv_memset_00(&LV_GC_ROOT(root_name), sizeof(LV_GC_ROOT(root_name)));
+ LV_ITERATE_ROOTS(LV_CLEAR_ROOT)
+}
+
+/**********************
+ * STATIC FUNCTIONS
+ **********************/
diff --git a/lib/lvgl/src/misc/lv_gc.h b/lib/lvgl/src/misc/lv_gc.h
new file mode 100644
index 00000000..9d7d1bb9
--- /dev/null
+++ b/lib/lvgl/src/misc/lv_gc.h
@@ -0,0 +1,97 @@
+/**
+ * @file lv_gc.h
+ *
+ */
+
+#ifndef LV_GC_H
+#define LV_GC_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/*********************
+ * INCLUDES
+ *********************/
+#include "../lv_conf_internal.h"
+#include <stdint.h>
+#include "lv_mem.h"
+#include "lv_ll.h"
+#include "lv_timer.h"
+#include "lv_types.h"
+#include "../draw/lv_img_cache.h"
+#include "../draw/lv_draw_mask.h"
+#include "../core/lv_obj_pos.h"
+
+/*********************
+ * DEFINES
+ *********************/
+#if LV_IMG_CACHE_DEF_SIZE
+# define LV_IMG_CACHE_DEF 1
+#else
+# define LV_IMG_CACHE_DEF 0
+#endif
+
+#define LV_DISPATCH(f, t, n) f(t, n)
+#define LV_DISPATCH_COND(f, t, n, m, v) LV_CONCAT3(LV_DISPATCH, m, v)(f, t, n)
+
+#define LV_DISPATCH00(f, t, n) LV_DISPATCH(f, t, n)
+#define LV_DISPATCH01(f, t, n)
+#define LV_DISPATCH10(f, t, n)
+#define LV_DISPATCH11(f, t, n) LV_DISPATCH(f, t, n)
+
+#define LV_ITERATE_ROOTS(f) \
+ LV_DISPATCH(f, lv_ll_t, _lv_timer_ll) /*Linked list to store the lv_timers*/ \
+ LV_DISPATCH(f, lv_ll_t, _lv_disp_ll) /*Linked list of display device*/ \
+ LV_DISPATCH(f, lv_ll_t, _lv_indev_ll) /*Linked list of input device*/ \
+ LV_DISPATCH(f, lv_ll_t, _lv_fsdrv_ll) \
+ LV_DISPATCH(f, lv_ll_t, _lv_anim_ll) \
+ LV_DISPATCH(f, lv_ll_t, _lv_group_ll) \
+ LV_DISPATCH(f, lv_ll_t, _lv_img_decoder_ll) \
+ LV_DISPATCH(f, lv_ll_t, _lv_obj_style_trans_ll) \
+ LV_DISPATCH(f, lv_layout_dsc_t *, _lv_layout_list) \
+ LV_DISPATCH_COND(f, _lv_img_cache_entry_t*, _lv_img_cache_array, LV_IMG_CACHE_DEF, 1) \
+ LV_DISPATCH_COND(f, _lv_img_cache_entry_t, _lv_img_cache_single, LV_IMG_CACHE_DEF, 0) \
+ LV_DISPATCH(f, lv_timer_t*, _lv_timer_act) \
+ LV_DISPATCH(f, lv_mem_buf_arr_t , lv_mem_buf) \
+ LV_DISPATCH_COND(f, _lv_draw_mask_radius_circle_dsc_arr_t , _lv_circle_cache, LV_DRAW_COMPLEX, 1) \
+ LV_DISPATCH_COND(f, _lv_draw_mask_saved_arr_t , _lv_draw_mask_list, LV_DRAW_COMPLEX, 1) \
+ LV_DISPATCH(f, void * , _lv_theme_default_styles) \
+ LV_DISPATCH(f, void * , _lv_theme_basic_styles) \
+ LV_DISPATCH_COND(f, uint8_t *, _lv_font_decompr_buf, LV_USE_FONT_COMPRESSED, 1) \
+ LV_DISPATCH(f, uint8_t * , _lv_grad_cache_mem) \
+ LV_DISPATCH(f, uint8_t * , _lv_style_custom_prop_flag_lookup_table)
+
+#define LV_DEFINE_ROOT(root_type, root_name) root_type root_name;
+#define LV_ROOTS LV_ITERATE_ROOTS(LV_DEFINE_ROOT)
+
+#if LV_ENABLE_GC == 1
+#if LV_MEM_CUSTOM != 1
+#error "GC requires CUSTOM_MEM"
+#endif /*LV_MEM_CUSTOM*/
+#include LV_GC_INCLUDE
+#else /*LV_ENABLE_GC*/
+#define LV_GC_ROOT(x) x
+#define LV_EXTERN_ROOT(root_type, root_name) extern root_type root_name;
+LV_ITERATE_ROOTS(LV_EXTERN_ROOT)
+#endif /*LV_ENABLE_GC*/
+
+/**********************
+ * TYPEDEFS
+ **********************/
+
+/**********************
+ * GLOBAL PROTOTYPES
+ **********************/
+
+void _lv_gc_clear_roots(void);
+
+/**********************
+ * MACROS
+ **********************/
+
+#ifdef __cplusplus
+} /*extern "C"*/
+#endif
+
+#endif /*LV_GC_H*/
diff --git a/lib/lvgl/src/misc/lv_ll.c b/lib/lvgl/src/misc/lv_ll.c
new file mode 100644
index 00000000..e7582316
--- /dev/null
+++ b/lib/lvgl/src/misc/lv_ll.c
@@ -0,0 +1,408 @@
+/**
+ * @file lv_ll.c
+ * Handle linked lists.
+ * The nodes are dynamically allocated by the 'lv_mem' module,
+ */
+
+/*********************
+ * INCLUDES
+ *********************/
+#include "lv_ll.h"
+#include "lv_mem.h"
+
+/*********************
+ * DEFINES
+ *********************/
+#define LL_NODE_META_SIZE (sizeof(lv_ll_node_t *) + sizeof(lv_ll_node_t *))
+#define LL_PREV_P_OFFSET(ll_p) (ll_p->n_size)
+#define LL_NEXT_P_OFFSET(ll_p) (ll_p->n_size + sizeof(lv_ll_node_t *))
+
+/**********************
+ * TYPEDEFS
+ **********************/
+
+/**********************
+ * STATIC PROTOTYPES
+ **********************/
+static void node_set_prev(lv_ll_t * ll_p, lv_ll_node_t * act, lv_ll_node_t * prev);
+static void node_set_next(lv_ll_t * ll_p, lv_ll_node_t * act, lv_ll_node_t * next);
+
+/**********************
+ * STATIC VARIABLES
+ **********************/
+
+/**********************
+ * MACROS
+ **********************/
+
+/**********************
+ * GLOBAL FUNCTIONS
+ **********************/
+
+/**
+ * Initialize linked list
+ * @param ll_p pointer to lv_ll_t variable
+ * @param node_size the size of 1 node in bytes
+ */
+void _lv_ll_init(lv_ll_t * ll_p, uint32_t node_size)
+{
+ ll_p->head = NULL;
+ ll_p->tail = NULL;
+#ifdef LV_ARCH_64
+ /*Round the size up to 8*/
+ node_size = (node_size + 7) & (~0x7);
+#else
+ /*Round the size up to 4*/
+ node_size = (node_size + 3) & (~0x3);
+#endif
+
+ ll_p->n_size = node_size;
+}
+
+/**
+ * Add a new head to a linked list
+ * @param ll_p pointer to linked list
+ * @return pointer to the new head
+ */
+void * _lv_ll_ins_head(lv_ll_t * ll_p)
+{
+ lv_ll_node_t * n_new;
+
+ n_new = lv_mem_alloc(ll_p->n_size + LL_NODE_META_SIZE);
+
+ if(n_new != NULL) {
+ node_set_prev(ll_p, n_new, NULL); /*No prev. before the new head*/
+ node_set_next(ll_p, n_new, ll_p->head); /*After new comes the old head*/
+
+ if(ll_p->head != NULL) { /*If there is old head then before it goes the new*/
+ node_set_prev(ll_p, ll_p->head, n_new);
+ }
+
+ ll_p->head = n_new; /*Set the new head in the dsc.*/
+ if(ll_p->tail == NULL) { /*If there is no tail (1. node) set the tail too*/
+ ll_p->tail = n_new;
+ }
+ }
+
+ return n_new;
+}
+
+/**
+ * Insert a new node in front of the n_act node
+ * @param ll_p pointer to linked list
+ * @param n_act pointer a node
+ * @return pointer to the new node
+ */
+void * _lv_ll_ins_prev(lv_ll_t * ll_p, void * n_act)
+{
+ lv_ll_node_t * n_new;
+
+ if(NULL == ll_p || NULL == n_act) return NULL;
+
+ if(_lv_ll_get_head(ll_p) == n_act) {
+ n_new = _lv_ll_ins_head(ll_p);
+ if(n_new == NULL) return NULL;
+ }
+ else {
+ n_new = lv_mem_alloc(ll_p->n_size + LL_NODE_META_SIZE);
+ if(n_new == NULL) return NULL;
+
+ lv_ll_node_t * n_prev;
+ n_prev = _lv_ll_get_prev(ll_p, n_act);
+ node_set_next(ll_p, n_prev, n_new);
+ node_set_prev(ll_p, n_new, n_prev);
+ node_set_prev(ll_p, n_act, n_new);
+ node_set_next(ll_p, n_new, n_act);
+ }
+
+ return n_new;
+}
+
+/**
+ * Add a new tail to a linked list
+ * @param ll_p pointer to linked list
+ * @return pointer to the new tail
+ */
+void * _lv_ll_ins_tail(lv_ll_t * ll_p)
+{
+ lv_ll_node_t * n_new;
+
+ n_new = lv_mem_alloc(ll_p->n_size + LL_NODE_META_SIZE);
+
+ if(n_new != NULL) {
+ node_set_next(ll_p, n_new, NULL); /*No next after the new tail*/
+ node_set_prev(ll_p, n_new, ll_p->tail); /*The prev. before new is the old tail*/
+ if(ll_p->tail != NULL) { /*If there is old tail then the new comes after it*/
+ node_set_next(ll_p, ll_p->tail, n_new);
+ }
+
+ ll_p->tail = n_new; /*Set the new tail in the dsc.*/
+ if(ll_p->head == NULL) { /*If there is no head (1. node) set the head too*/
+ ll_p->head = n_new;
+ }
+ }
+
+ return n_new;
+}
+
+/**
+ * Remove the node 'node_p' from 'll_p' linked list.
+ * It does not free the memory of node.
+ * @param ll_p pointer to the linked list of 'node_p'
+ * @param node_p pointer to node in 'll_p' linked list
+ */
+void _lv_ll_remove(lv_ll_t * ll_p, void * node_p)
+{
+ if(ll_p == NULL) return;
+
+ if(_lv_ll_get_head(ll_p) == node_p) {
+ /*The new head will be the node after 'n_act'*/
+ ll_p->head = _lv_ll_get_next(ll_p, node_p);
+ if(ll_p->head == NULL) {
+ ll_p->tail = NULL;
+ }
+ else {
+ node_set_prev(ll_p, ll_p->head, NULL);
+ }
+ }
+ else if(_lv_ll_get_tail(ll_p) == node_p) {
+ /*The new tail will be the node before 'n_act'*/
+ ll_p->tail = _lv_ll_get_prev(ll_p, node_p);
+ if(ll_p->tail == NULL) {
+ ll_p->head = NULL;
+ }
+ else {
+ node_set_next(ll_p, ll_p->tail, NULL);
+ }
+ }
+ else {
+ lv_ll_node_t * n_prev = _lv_ll_get_prev(ll_p, node_p);
+ lv_ll_node_t * n_next = _lv_ll_get_next(ll_p, node_p);
+
+ node_set_next(ll_p, n_prev, n_next);
+ node_set_prev(ll_p, n_next, n_prev);
+ }
+}
+
+/**
+ * Remove and free all elements from a linked list. The list remain valid but become empty.
+ * @param ll_p pointer to linked list
+ */
+void _lv_ll_clear(lv_ll_t * ll_p)
+{
+ void * i;
+ void * i_next;
+
+ i = _lv_ll_get_head(ll_p);
+ i_next = NULL;
+
+ while(i != NULL) {
+ i_next = _lv_ll_get_next(ll_p, i);
+
+ _lv_ll_remove(ll_p, i);
+ lv_mem_free(i);
+
+ i = i_next;
+ }
+}
+
+/**
+ * Move a node to a new linked list
+ * @param ll_ori_p pointer to the original (old) linked list
+ * @param ll_new_p pointer to the new linked list
+ * @param node pointer to a node
+ * @param head true: be the head in the new list
+ * false be the tail in the new list
+ */
+void _lv_ll_chg_list(lv_ll_t * ll_ori_p, lv_ll_t * ll_new_p, void * node, bool head)
+{
+ _lv_ll_remove(ll_ori_p, node);
+
+ if(head) {
+ /*Set node as head*/
+ node_set_prev(ll_new_p, node, NULL);
+ node_set_next(ll_new_p, node, ll_new_p->head);
+
+ if(ll_new_p->head != NULL) { /*If there is old head then before it goes the new*/
+ node_set_prev(ll_new_p, ll_new_p->head, node);
+ }
+
+ ll_new_p->head = node; /*Set the new head in the dsc.*/
+ if(ll_new_p->tail == NULL) { /*If there is no tail (first node) set the tail too*/
+ ll_new_p->tail = node;
+ }
+ }
+ else {
+ /*Set node as tail*/
+ node_set_prev(ll_new_p, node, ll_new_p->tail);
+ node_set_next(ll_new_p, node, NULL);
+
+ if(ll_new_p->tail != NULL) { /*If there is old tail then after it goes the new*/
+ node_set_next(ll_new_p, ll_new_p->tail, node);
+ }
+
+ ll_new_p->tail = node; /*Set the new tail in the dsc.*/
+ if(ll_new_p->head == NULL) { /*If there is no head (first node) set the head too*/
+ ll_new_p->head = node;
+ }
+ }
+}
+
+/**
+ * Return with head node of the linked list
+ * @param ll_p pointer to linked list
+ * @return pointer to the head of 'll_p'
+ */
+void * _lv_ll_get_head(const lv_ll_t * ll_p)
+{
+ if(ll_p == NULL) return NULL;
+ return ll_p->head;
+}
+
+/**
+ * Return with tail node of the linked list
+ * @param ll_p pointer to linked list
+ * @return pointer to the tail of 'll_p'
+ */
+void * _lv_ll_get_tail(const lv_ll_t * ll_p)
+{
+ if(ll_p == NULL) return NULL;
+ return ll_p->tail;
+}
+
+/**
+ * Return with the pointer of the next node after 'n_act'
+ * @param ll_p pointer to linked list
+ * @param n_act pointer a node
+ * @return pointer to the next node
+ */
+void * _lv_ll_get_next(const lv_ll_t * ll_p, const void * n_act)
+{
+ /*Pointer to the next node is stored in the end of this node.
+ *Go there and return the address found there*/
+ const lv_ll_node_t * n_act_d = n_act;
+ n_act_d += LL_NEXT_P_OFFSET(ll_p);
+ return *((lv_ll_node_t **)n_act_d);
+}
+
+/**
+ * Return with the pointer of the previous node before 'n_act'
+ * @param ll_p pointer to linked list
+ * @param n_act pointer a node
+ * @return pointer to the previous node
+ */
+void * _lv_ll_get_prev(const lv_ll_t * ll_p, const void * n_act)
+{
+ /*Pointer to the prev. node is stored in the end of this node.
+ *Go there and return the address found there*/
+ const lv_ll_node_t * n_act_d = n_act;
+ n_act_d += LL_PREV_P_OFFSET(ll_p);
+ return *((lv_ll_node_t **)n_act_d);
+}
+
+/**
+ * Return the length of the linked list.
+ * @param ll_p pointer to linked list
+ * @return length of the linked list
+ */
+uint32_t _lv_ll_get_len(const lv_ll_t * ll_p)
+{
+ uint32_t len = 0;
+ void * node;
+
+ for(node = _lv_ll_get_head(ll_p); node != NULL; node = _lv_ll_get_next(ll_p, node)) {
+ len++;
+ }
+
+ return len;
+}
+
+/**
+ * Move a node before an other node in the same linked list
+ * @param ll_p pointer to a linked list
+ * @param n_act pointer to node to move
+ * @param n_after pointer to a node which should be after `n_act`
+ */
+void _lv_ll_move_before(lv_ll_t * ll_p, void * n_act, void * n_after)
+{
+ if(n_act == n_after) return; /*Can't move before itself*/
+
+ void * n_before;
+ if(n_after != NULL)
+ n_before = _lv_ll_get_prev(ll_p, n_after);
+ else
+ n_before = _lv_ll_get_tail(ll_p); /*if `n_after` is NULL `n_act` should be the new tail*/
+
+ if(n_act == n_before) return; /*Already before `n_after`*/
+
+ /*It's much easier to remove from the list and add again*/
+ _lv_ll_remove(ll_p, n_act);
+
+ /*Add again by setting the prev. and next nodes*/
+ node_set_next(ll_p, n_before, n_act);
+ node_set_prev(ll_p, n_act, n_before);
+ node_set_prev(ll_p, n_after, n_act);
+ node_set_next(ll_p, n_act, n_after);
+
+ /*If `n_act` was moved before NULL then it become the new tail*/
+ if(n_after == NULL) ll_p->tail = n_act;
+
+ /*If `n_act` was moved before `NULL` then it's the new head*/
+ if(n_before == NULL) ll_p->head = n_act;
+}
+
+/**
+ * Check if a linked list is empty
+ * @param ll_p pointer to a linked list
+ * @return true: the linked list is empty; false: not empty
+ */
+bool _lv_ll_is_empty(lv_ll_t * ll_p)
+{
+ if(ll_p == NULL) return true;
+
+ if(ll_p->head == NULL && ll_p->tail == NULL) return true;
+
+ return false;
+}
+
+/**********************
+ * STATIC FUNCTIONS
+ **********************/
+
+/**
+ * Set the previous node pointer of a node
+ * @param ll_p pointer to linked list
+ * @param act pointer to a node which prev. node pointer should be set
+ * @param prev pointer to a node which should be the previous node before 'act'
+ */
+static void node_set_prev(lv_ll_t * ll_p, lv_ll_node_t * act, lv_ll_node_t * prev)
+{
+ if(act == NULL) return; /*Can't set the prev node of `NULL`*/
+
+ uint8_t * act8 = (uint8_t *)act;
+
+ act8 += LL_PREV_P_OFFSET(ll_p);
+
+ lv_ll_node_t ** act_node_p = (lv_ll_node_t **) act8;
+ lv_ll_node_t ** prev_node_p = (lv_ll_node_t **) &prev;
+
+ *act_node_p = *prev_node_p;
+}
+
+/**
+ * Set the 'next node pointer' of a node
+ * @param ll_p pointer to linked list
+ * @param act pointer to a node which next node pointer should be set
+ * @param next pointer to a node which should be the next node before 'act'
+ */
+static void node_set_next(lv_ll_t * ll_p, lv_ll_node_t * act, lv_ll_node_t * next)
+{
+ if(act == NULL) return; /*Can't set the next node of `NULL`*/
+ uint8_t * act8 = (uint8_t *)act;
+
+ act8 += LL_NEXT_P_OFFSET(ll_p);
+ lv_ll_node_t ** act_node_p = (lv_ll_node_t **) act8;
+ lv_ll_node_t ** next_node_p = (lv_ll_node_t **) &next;
+
+ *act_node_p = *next_node_p;
+}
diff --git a/lib/lvgl/src/misc/lv_ll.h b/lib/lvgl/src/misc/lv_ll.h
new file mode 100644
index 00000000..d38f6928
--- /dev/null
+++ b/lib/lvgl/src/misc/lv_ll.h
@@ -0,0 +1,167 @@
+/**
+ * @file lv_ll.h
+ * Handle linked lists. The nodes are dynamically allocated by the 'lv_mem' module.
+ */
+
+#ifndef LV_LL_H
+#define LV_LL_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/*********************
+ * INCLUDES
+ *********************/
+#include <stdint.h>
+#include <stddef.h>
+#include <stdbool.h>
+
+/*********************
+ * DEFINES
+ *********************/
+
+/**********************
+ * TYPEDEFS
+ **********************/
+
+/** Dummy type to make handling easier*/
+typedef uint8_t lv_ll_node_t;
+
+/** Description of a linked list*/
+typedef struct {
+ uint32_t n_size;
+ lv_ll_node_t * head;
+ lv_ll_node_t * tail;
+} lv_ll_t;
+
+/**********************
+ * GLOBAL PROTOTYPES
+ **********************/
+
+/**
+ * Initialize linked list
+ * @param ll_p pointer to lv_ll_t variable
+ * @param node_size the size of 1 node in bytes
+ */
+void _lv_ll_init(lv_ll_t * ll_p, uint32_t node_size);
+
+/**
+ * Add a new head to a linked list
+ * @param ll_p pointer to linked list
+ * @return pointer to the new head
+ */
+void * _lv_ll_ins_head(lv_ll_t * ll_p);
+
+/**
+ * Insert a new node in front of the n_act node
+ * @param ll_p pointer to linked list
+ * @param n_act pointer a node
+ * @return pointer to the new node
+ */
+void * _lv_ll_ins_prev(lv_ll_t * ll_p, void * n_act);
+
+/**
+ * Add a new tail to a linked list
+ * @param ll_p pointer to linked list
+ * @return pointer to the new tail
+ */
+void * _lv_ll_ins_tail(lv_ll_t * ll_p);
+
+/**
+ * Remove the node 'node_p' from 'll_p' linked list.
+ * It does not free the memory of node.
+ * @param ll_p pointer to the linked list of 'node_p'
+ * @param node_p pointer to node in 'll_p' linked list
+ */
+void _lv_ll_remove(lv_ll_t * ll_p, void * node_p);
+
+/**
+ * Remove and free all elements from a linked list. The list remain valid but become empty.
+ * @param ll_p pointer to linked list
+ */
+void _lv_ll_clear(lv_ll_t * ll_p);
+
+/**
+ * Move a node to a new linked list
+ * @param ll_ori_p pointer to the original (old) linked list
+ * @param ll_new_p pointer to the new linked list
+ * @param node pointer to a node
+ * @param head true: be the head in the new list
+ * false be the tail in the new list
+ */
+void _lv_ll_chg_list(lv_ll_t * ll_ori_p, lv_ll_t * ll_new_p, void * node, bool head);
+
+/**
+ * Return with head node of the linked list
+ * @param ll_p pointer to linked list
+ * @return pointer to the head of 'll_p'
+ */
+void * _lv_ll_get_head(const lv_ll_t * ll_p);
+
+/**
+ * Return with tail node of the linked list
+ * @param ll_p pointer to linked list
+ * @return pointer to the tail of 'll_p'
+ */
+void * _lv_ll_get_tail(const lv_ll_t * ll_p);
+
+/**
+ * Return with the pointer of the next node after 'n_act'
+ * @param ll_p pointer to linked list
+ * @param n_act pointer a node
+ * @return pointer to the next node
+ */
+void * _lv_ll_get_next(const lv_ll_t * ll_p, const void * n_act);
+
+/**
+ * Return with the pointer of the previous node after 'n_act'
+ * @param ll_p pointer to linked list
+ * @param n_act pointer a node
+ * @return pointer to the previous node
+ */
+void * _lv_ll_get_prev(const lv_ll_t * ll_p, const void * n_act);
+
+/**
+ * Return the length of the linked list.
+ * @param ll_p pointer to linked list
+ * @return length of the linked list
+ */
+uint32_t _lv_ll_get_len(const lv_ll_t * ll_p);
+
+/**
+ * TODO
+ * @param ll_p
+ * @param n1_p
+ * @param n2_p
+void lv_ll_swap(lv_ll_t * ll_p, void * n1_p, void * n2_p);
+ */
+
+/**
+ * Move a node before an other node in the same linked list
+ * @param ll_p pointer to a linked list
+ * @param n_act pointer to node to move
+ * @param n_after pointer to a node which should be after `n_act`
+ */
+void _lv_ll_move_before(lv_ll_t * ll_p, void * n_act, void * n_after);
+
+/**
+ * Check if a linked list is empty
+ * @param ll_p pointer to a linked list
+ * @return true: the linked list is empty; false: not empty
+ */
+bool _lv_ll_is_empty(lv_ll_t * ll_p);
+
+/**********************
+ * MACROS
+ **********************/
+
+#define _LV_LL_READ(list, i) for(i = _lv_ll_get_head(list); i != NULL; i = _lv_ll_get_next(list, i))
+
+#define _LV_LL_READ_BACK(list, i) for(i = _lv_ll_get_tail(list); i != NULL; i = _lv_ll_get_prev(list, i))
+
+#ifdef __cplusplus
+} /*extern "C"*/
+#endif
+
+#endif
diff --git a/lib/lvgl/src/misc/lv_log.c b/lib/lvgl/src/misc/lv_log.c
new file mode 100644
index 00000000..d79463f2
--- /dev/null
+++ b/lib/lvgl/src/misc/lv_log.c
@@ -0,0 +1,144 @@
+/**
+ * @file lv_log.c
+ *
+ */
+
+/*********************
+ * INCLUDES
+ *********************/
+#include "lv_log.h"
+#if LV_USE_LOG
+
+#include <stdarg.h>
+#include <string.h>
+#include "lv_printf.h"
+#include "../hal/lv_hal_tick.h"
+
+#if LV_LOG_PRINTF
+ #include <stdio.h>
+#endif
+
+/*********************
+ * DEFINES
+ *********************/
+
+/**********************
+ * TYPEDEFS
+ **********************/
+
+/**********************
+ * STATIC PROTOTYPES
+ **********************/
+
+/**********************
+ * STATIC VARIABLES
+ **********************/
+static lv_log_print_g_cb_t custom_print_cb;
+
+/**********************
+ * MACROS
+ **********************/
+
+/**********************
+ * GLOBAL FUNCTIONS
+ **********************/
+
+/**
+ * Register custom print/write function to call when a log is added.
+ * It can format its "File path", "Line number" and "Description" as required
+ * and send the formatted log message to a console or serial port.
+ * @param print_cb a function pointer to print a log
+ */
+void lv_log_register_print_cb(lv_log_print_g_cb_t print_cb)
+{
+ custom_print_cb = print_cb;
+}
+
+/**
+ * Add a log
+ * @param level the level of log. (From `lv_log_level_t` enum)
+ * @param file name of the file when the log added
+ * @param line line number in the source code where the log added
+ * @param func name of the function when the log added
+ * @param format printf-like format string
+ * @param ... parameters for `format`
+ */
+void _lv_log_add(lv_log_level_t level, const char * file, int line, const char * func, const char * format, ...)
+{
+ if(level >= _LV_LOG_LEVEL_NUM) return; /*Invalid level*/
+
+ static uint32_t last_log_time = 0;
+
+ if(level >= LV_LOG_LEVEL) {
+ va_list args;
+ va_start(args, format);
+
+ /*Use only the file name not the path*/
+ size_t p;
+ for(p = strlen(file); p > 0; p--) {
+ if(file[p] == '/' || file[p] == '\\') {
+ p++; /*Skip the slash*/
+ break;
+ }
+ }
+
+ uint32_t t = lv_tick_get();
+ static const char * lvl_prefix[] = {"Trace", "Info", "Warn", "Error", "User"};
+
+#if LV_LOG_PRINTF
+ printf("[%s]\t(%" LV_PRId32 ".%03" LV_PRId32 ", +%" LV_PRId32 ")\t %s: ",
+ lvl_prefix[level], t / 1000, t % 1000, t - last_log_time, func);
+ vprintf(format, args);
+ printf(" \t(in %s line #%d)\n", &file[p], line);
+#else
+ if(custom_print_cb) {
+ char buf[512];
+#if LV_SPRINTF_CUSTOM
+ char msg[256];
+ lv_vsnprintf(msg, sizeof(msg), format, args);
+ lv_snprintf(buf, sizeof(buf), "[%s]\t(%" LV_PRId32 ".%03" LV_PRId32 ", +%" LV_PRId32 ")\t %s: %s \t(in %s line #%d)\n",
+ lvl_prefix[level], t / 1000, t % 1000, t - last_log_time, func, msg, &file[p], line);
+#else
+ lv_vaformat_t vaf = {format, &args};
+ lv_snprintf(buf, sizeof(buf), "[%s]\t(%" LV_PRId32 ".%03" LV_PRId32 ", +%" LV_PRId32 ")\t %s: %pV \t(in %s line #%d)\n",
+ lvl_prefix[level], t / 1000, t % 1000, t - last_log_time, func, (void *)&vaf, &file[p], line);
+#endif
+ custom_print_cb(buf);
+ }
+#endif
+
+ last_log_time = t;
+ va_end(args);
+ }
+}
+
+void lv_log(const char * format, ...)
+{
+ if(LV_LOG_LEVEL >= LV_LOG_LEVEL_NONE) return; /* disable log */
+
+ va_list args;
+ va_start(args, format);
+
+#if LV_LOG_PRINTF
+ vprintf(format, args);
+#else
+ if(custom_print_cb) {
+ char buf[512];
+#if LV_SPRINTF_CUSTOM
+ lv_vsnprintf(buf, sizeof(buf), format, args);
+#else
+ lv_vaformat_t vaf = {format, &args};
+ lv_snprintf(buf, sizeof(buf), "%pV", (void *)&vaf);
+#endif
+ custom_print_cb(buf);
+ }
+#endif
+
+ va_end(args);
+}
+
+/**********************
+ * STATIC FUNCTIONS
+ **********************/
+
+#endif /*LV_USE_LOG*/
diff --git a/lib/lvgl/src/misc/lv_log.h b/lib/lvgl/src/misc/lv_log.h
new file mode 100644
index 00000000..9a009930
--- /dev/null
+++ b/lib/lvgl/src/misc/lv_log.h
@@ -0,0 +1,154 @@
+/**
+ * @file lv_log.h
+ *
+ */
+
+#ifndef LV_LOG_H
+#define LV_LOG_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/*********************
+ * INCLUDES
+ *********************/
+#include "../lv_conf_internal.h"
+#include <stdint.h>
+
+#include "lv_types.h"
+
+/*********************
+ * DEFINES
+ *********************/
+
+/*Possible log level. For compatibility declare it independently from `LV_USE_LOG`*/
+
+#define LV_LOG_LEVEL_TRACE 0 /**< A lot of logs to give detailed information*/
+#define LV_LOG_LEVEL_INFO 1 /**< Log important events*/
+#define LV_LOG_LEVEL_WARN 2 /**< Log if something unwanted happened but didn't caused problem*/
+#define LV_LOG_LEVEL_ERROR 3 /**< Only critical issue, when the system may fail*/
+#define LV_LOG_LEVEL_USER 4 /**< Custom logs from the user*/
+#define LV_LOG_LEVEL_NONE 5 /**< Do not log anything*/
+#define _LV_LOG_LEVEL_NUM 6 /**< Number of log levels*/
+
+LV_EXPORT_CONST_INT(LV_LOG_LEVEL_TRACE);
+LV_EXPORT_CONST_INT(LV_LOG_LEVEL_INFO);
+LV_EXPORT_CONST_INT(LV_LOG_LEVEL_WARN);
+LV_EXPORT_CONST_INT(LV_LOG_LEVEL_ERROR);
+LV_EXPORT_CONST_INT(LV_LOG_LEVEL_USER);
+LV_EXPORT_CONST_INT(LV_LOG_LEVEL_NONE);
+
+typedef int8_t lv_log_level_t;
+
+#if LV_USE_LOG
+/**********************
+ * TYPEDEFS
+ **********************/
+
+/**
+ * Log print function. Receives a string buffer to print".
+ */
+typedef void (*lv_log_print_g_cb_t)(const char * buf);
+
+/**********************
+ * GLOBAL PROTOTYPES
+ **********************/
+
+/**
+ * Register custom print/write function to call when a log is added.
+ * It can format its "File path", "Line number" and "Description" as required
+ * and send the formatted log message to a console or serial port.
+ * @param print_cb a function pointer to print a log
+ */
+void lv_log_register_print_cb(lv_log_print_g_cb_t print_cb);
+
+/**
+ * Print a log message via `printf` if enabled with `LV_LOG_PRINTF` in `lv_conf.h`
+ * and/or a print callback if registered with `lv_log_register_print_cb`
+ * @param format printf-like format string
+ * @param ... parameters for `format`
+ */
+void lv_log(const char * format, ...) LV_FORMAT_ATTRIBUTE(1, 2);
+
+/**
+ * Add a log
+ * @param level the level of log. (From `lv_log_level_t` enum)
+ * @param file name of the file when the log added
+ * @param line line number in the source code where the log added
+ * @param func name of the function when the log added
+ * @param format printf-like format string
+ * @param ... parameters for `format`
+ */
+void _lv_log_add(lv_log_level_t level, const char * file, int line,
+ const char * func, const char * format, ...) LV_FORMAT_ATTRIBUTE(5, 6);
+
+/**********************
+ * MACROS
+ **********************/
+#ifndef LV_LOG_TRACE
+# if LV_LOG_LEVEL <= LV_LOG_LEVEL_TRACE
+# define LV_LOG_TRACE(...) _lv_log_add(LV_LOG_LEVEL_TRACE, __FILE__, __LINE__, __func__, __VA_ARGS__)
+# else
+# define LV_LOG_TRACE(...) do {}while(0)
+# endif
+#endif
+
+#ifndef LV_LOG_INFO
+# if LV_LOG_LEVEL <= LV_LOG_LEVEL_INFO
+# define LV_LOG_INFO(...) _lv_log_add(LV_LOG_LEVEL_INFO, __FILE__, __LINE__, __func__, __VA_ARGS__)
+# else
+# define LV_LOG_INFO(...) do {}while(0)
+# endif
+#endif
+
+#ifndef LV_LOG_WARN
+# if LV_LOG_LEVEL <= LV_LOG_LEVEL_WARN
+# define LV_LOG_WARN(...) _lv_log_add(LV_LOG_LEVEL_WARN, __FILE__, __LINE__, __func__, __VA_ARGS__)
+# else
+# define LV_LOG_WARN(...) do {}while(0)
+# endif
+#endif
+
+#ifndef LV_LOG_ERROR
+# if LV_LOG_LEVEL <= LV_LOG_LEVEL_ERROR
+# define LV_LOG_ERROR(...) _lv_log_add(LV_LOG_LEVEL_ERROR, __FILE__, __LINE__, __func__, __VA_ARGS__)
+# else
+# define LV_LOG_ERROR(...) do {}while(0)
+# endif
+#endif
+
+#ifndef LV_LOG_USER
+# if LV_LOG_LEVEL <= LV_LOG_LEVEL_USER
+# define LV_LOG_USER(...) _lv_log_add(LV_LOG_LEVEL_USER, __FILE__, __LINE__, __func__, __VA_ARGS__)
+# else
+# define LV_LOG_USER(...) do {}while(0)
+# endif
+#endif
+
+#ifndef LV_LOG
+# if LV_LOG_LEVEL < LV_LOG_LEVEL_NONE
+# define LV_LOG(...) lv_log(__VA_ARGS__)
+# else
+# define LV_LOG(...) do {} while(0)
+# endif
+#endif
+
+#else /*LV_USE_LOG*/
+
+/*Do nothing if `LV_USE_LOG 0`*/
+#define _lv_log_add(level, file, line, ...)
+#define LV_LOG_TRACE(...) do {}while(0)
+#define LV_LOG_INFO(...) do {}while(0)
+#define LV_LOG_WARN(...) do {}while(0)
+#define LV_LOG_ERROR(...) do {}while(0)
+#define LV_LOG_USER(...) do {}while(0)
+#define LV_LOG(...) do {}while(0)
+
+#endif /*LV_USE_LOG*/
+
+#ifdef __cplusplus
+} /*extern "C"*/
+#endif
+
+#endif /*LV_LOG_H*/
diff --git a/lib/lvgl/src/misc/lv_lru.c b/lib/lvgl/src/misc/lv_lru.c
new file mode 100644
index 00000000..6ff83903
--- /dev/null
+++ b/lib/lvgl/src/misc/lv_lru.c
@@ -0,0 +1,349 @@
+/**
+ * @file lv_lru.c
+ *
+ * @see https://github.com/willcannings/C-LRU-Cache
+ */
+
+/*********************
+ * INCLUDES
+ *********************/
+
+#include "lv_lru.h"
+#include "lv_math.h"
+#include "lv_mem.h"
+#include "lv_assert.h"
+#include "lv_log.h"
+
+/*********************
+ * DEFINES
+ *********************/
+
+/**********************
+ * TYPEDEFS
+ **********************/
+
+struct _lv_lru_item_t {
+ void * value;
+ void * key;
+ size_t value_length;
+ size_t key_length;
+ uint64_t access_count;
+ struct _lv_lru_item_t * next;
+};
+
+/**********************
+ * STATIC PROTOTYPES
+ **********************/
+
+/**
+ * MurmurHash2
+ * @author Austin Appleby
+ * @see http://sites.google.com/site/murmurhash/
+ */
+static uint32_t lv_lru_hash(lv_lru_t * cache, const void * key, uint32_t key_length);
+
+/** compare a key against an existing item's key */
+static int lv_lru_cmp_keys(lv_lru_item_t * item, const void * key, uint32_t key_length);
+
+/** remove an item and push it to the free items queue */
+static void lv_lru_remove_item(lv_lru_t * cache, lv_lru_item_t * prev, lv_lru_item_t * item, uint32_t hash_index);
+
+/** pop an existing item off the free queue, or create a new one */
+static lv_lru_item_t * lv_lru_pop_or_create_item(lv_lru_t * cache);
+
+/**********************
+ * STATIC VARIABLES
+ **********************/
+
+/**********************
+ * MACROS
+ **********************/
+
+/* error helpers */
+#define error_for(conditions, error) if(conditions) {return error;}
+#define test_for_missing_cache() error_for(!cache, LV_LRU_MISSING_CACHE)
+#define test_for_missing_key() error_for(!key, LV_LRU_MISSING_KEY)
+#define test_for_missing_value() error_for(!value || value_length == 0, LV_LRU_MISSING_VALUE)
+#define test_for_value_too_large() error_for(value_length > cache->total_memory, LV_LRU_VALUE_TOO_LARGE)
+
+/**********************
+ * GLOBAL FUNCTIONS
+ **********************/
+
+lv_lru_t * lv_lru_create(size_t cache_size, size_t average_length, lv_lru_free_t * value_free,
+ lv_lru_free_t * key_free)
+{
+ // create the cache
+ lv_lru_t * cache = (lv_lru_t *) lv_mem_alloc(sizeof(lv_lru_t));
+ lv_memset_00(cache, sizeof(lv_lru_t));
+ if(!cache) {
+ LV_LOG_WARN("LRU Cache unable to create cache object");
+ return NULL;
+ }
+ cache->hash_table_size = cache_size / average_length;
+ cache->average_item_length = average_length;
+ cache->free_memory = cache_size;
+ cache->total_memory = cache_size;
+ cache->seed = lv_rand(1, UINT32_MAX);
+ cache->value_free = value_free ? value_free : lv_mem_free;
+ cache->key_free = key_free ? key_free : lv_mem_free;
+
+ // size the hash table to a guestimate of the number of slots required (assuming a perfect hash)
+ cache->items = (lv_lru_item_t **) lv_mem_alloc(sizeof(lv_lru_item_t *) * cache->hash_table_size);
+ lv_memset_00(cache->items, sizeof(lv_lru_item_t *) * cache->hash_table_size);
+ if(!cache->items) {
+ LV_LOG_WARN("LRU Cache unable to create cache hash table");
+ lv_mem_free(cache);
+ return NULL;
+ }
+ return cache;
+}
+
+
+void lv_lru_del(lv_lru_t * cache)
+{
+ LV_ASSERT_NULL(cache);
+
+ // free each of the cached items, and the hash table
+ lv_lru_item_t * item = NULL, *next = NULL;
+ uint32_t i = 0;
+ if(cache->items) {
+ for(; i < cache->hash_table_size; i++) {
+ item = cache->items[i];
+ while(item) {
+ next = (lv_lru_item_t *) item->next;
+ cache->value_free(item->value);
+ cache->key_free(item->key);
+ cache->free_memory += item->value_length;
+ lv_mem_free(item);
+ item = next;
+ }
+ }
+ lv_mem_free(cache->items);
+ }
+
+ if(cache->free_items) {
+ item = cache->free_items;
+ while(item) {
+ next = (lv_lru_item_t *) item->next;
+ lv_mem_free(item);
+ item = next;
+ }
+ }
+
+ // free the cache
+ lv_mem_free(cache);
+}
+
+
+lv_lru_res_t lv_lru_set(lv_lru_t * cache, const void * key, size_t key_length, void * value, size_t value_length)
+{
+ test_for_missing_cache();
+ test_for_missing_key();
+ test_for_missing_value();
+ test_for_value_too_large();
+
+ // see if the key already exists
+ uint32_t hash_index = lv_lru_hash(cache, key, key_length);
+ int required = 0;
+ lv_lru_item_t * item = NULL, *prev = NULL;
+ item = cache->items[hash_index];
+
+ while(item && lv_lru_cmp_keys(item, key, key_length)) {
+ prev = item;
+ item = (lv_lru_item_t *) item->next;
+ }
+
+ if(item) {
+ // update the value and value_lengths
+ required = (int)(value_length - item->value_length);
+ cache->value_free(item->value);
+ item->value = value;
+ item->value_length = value_length;
+
+ }
+ else {
+ // insert a new item
+ item = lv_lru_pop_or_create_item(cache);
+ item->value = value;
+ item->key = lv_mem_alloc(key_length);
+ memcpy(item->key, key, key_length);
+ item->value_length = value_length;
+ item->key_length = key_length;
+ required = (int) value_length;
+
+ if(prev)
+ prev->next = item;
+ else
+ cache->items[hash_index] = item;
+ }
+ item->access_count = ++cache->access_count;
+
+ // remove as many items as necessary to free enough space
+ if(required > 0 && (size_t) required > cache->free_memory) {
+ while(cache->free_memory < (size_t) required)
+ lv_lru_remove_lru_item(cache);
+ }
+ cache->free_memory -= required;
+ return LV_LRU_OK;
+}
+
+
+lv_lru_res_t lv_lru_get(lv_lru_t * cache, const void * key, size_t key_size, void ** value)
+{
+ test_for_missing_cache();
+ test_for_missing_key();
+
+ // loop until we find the item, or hit the end of a chain
+ uint32_t hash_index = lv_lru_hash(cache, key, key_size);
+ lv_lru_item_t * item = cache->items[hash_index];
+
+ while(item && lv_lru_cmp_keys(item, key, key_size))
+ item = (lv_lru_item_t *) item->next;
+
+ if(item) {
+ *value = item->value;
+ item->access_count = ++cache->access_count;
+ }
+ else {
+ *value = NULL;
+ }
+
+ return LV_LRU_OK;
+}
+
+lv_lru_res_t lv_lru_remove(lv_lru_t * cache, const void * key, size_t key_size)
+{
+ test_for_missing_cache();
+ test_for_missing_key();
+
+ // loop until we find the item, or hit the end of a chain
+ lv_lru_item_t * item = NULL, *prev = NULL;
+ uint32_t hash_index = lv_lru_hash(cache, key, key_size);
+ item = cache->items[hash_index];
+
+ while(item && lv_lru_cmp_keys(item, key, key_size)) {
+ prev = item;
+ item = (lv_lru_item_t *) item->next;
+ }
+
+ if(item) {
+ lv_lru_remove_item(cache, prev, item, hash_index);
+ }
+
+ return LV_LRU_OK;
+}
+
+void lv_lru_remove_lru_item(lv_lru_t * cache)
+{
+ lv_lru_item_t * min_item = NULL, *min_prev = NULL;
+ lv_lru_item_t * item = NULL, *prev = NULL;
+ uint32_t i = 0, min_index = -1;
+ uint64_t min_access_count = -1;
+
+ for(; i < cache->hash_table_size; i++) {
+ item = cache->items[i];
+ prev = NULL;
+
+ while(item) {
+ if(item->access_count < min_access_count || (int64_t) min_access_count == -1) {
+ min_access_count = item->access_count;
+ min_item = item;
+ min_prev = prev;
+ min_index = i;
+ }
+ prev = item;
+ item = item->next;
+ }
+ }
+
+ if(min_item) {
+ lv_lru_remove_item(cache, min_prev, min_item, min_index);
+ }
+}
+
+/**********************
+ * STATIC FUNCTIONS
+ **********************/
+
+static uint32_t lv_lru_hash(lv_lru_t * cache, const void * key, uint32_t key_length)
+{
+ uint32_t m = 0x5bd1e995;
+ uint32_t r = 24;
+ uint32_t h = cache->seed ^ key_length;
+ char * data = (char *) key;
+
+ while(key_length >= 4) {
+ uint32_t k = *(uint32_t *) data;
+ k *= m;
+ k ^= k >> r;
+ k *= m;
+ h *= m;
+ h ^= k;
+ data += 4;
+ key_length -= 4;
+ }
+
+ if(key_length >= 3) {
+ h ^= data[2] << 16;
+ }
+ if(key_length >= 2) {
+ h ^= data[1] << 8;
+ }
+ if(key_length >= 1) {
+ h ^= data[0];
+ h *= m;
+ }
+
+ h ^= h >> 13;
+ h *= m;
+ h ^= h >> 15;
+ return h % cache->hash_table_size;
+}
+
+static int lv_lru_cmp_keys(lv_lru_item_t * item, const void * key, uint32_t key_length)
+{
+ if(key_length != item->key_length) {
+ return 1;
+ }
+ else {
+ return memcmp(key, item->key, key_length);
+ }
+}
+
+static void lv_lru_remove_item(lv_lru_t * cache, lv_lru_item_t * prev, lv_lru_item_t * item, uint32_t hash_index)
+{
+ if(prev) {
+ prev->next = item->next;
+ }
+ else {
+ cache->items[hash_index] = (lv_lru_item_t *) item->next;
+ }
+
+ // free memory and update the free memory counter
+ cache->free_memory += item->value_length;
+ cache->value_free(item->value);
+ cache->key_free(item->key);
+
+ // push the item to the free items queue
+ lv_memset_00(item, sizeof(lv_lru_item_t));
+ item->next = cache->free_items;
+ cache->free_items = item;
+}
+
+static lv_lru_item_t * lv_lru_pop_or_create_item(lv_lru_t * cache)
+{
+ lv_lru_item_t * item = NULL;
+
+ if(cache->free_items) {
+ item = cache->free_items;
+ cache->free_items = item->next;
+ lv_memset_00(item, sizeof(lv_lru_item_t));
+ }
+ else {
+ item = (lv_lru_item_t *) lv_mem_alloc(sizeof(lv_lru_item_t));
+ lv_memset_00(item, sizeof(lv_lru_item_t));
+ }
+
+ return item;
+}
diff --git a/lib/lvgl/src/misc/lv_lru.h b/lib/lvgl/src/misc/lv_lru.h
new file mode 100644
index 00000000..2d0134e5
--- /dev/null
+++ b/lib/lvgl/src/misc/lv_lru.h
@@ -0,0 +1,87 @@
+/**
+ * @file lv_lru.h
+ *
+ */
+
+#ifndef LV_LRU_H
+#define LV_LRU_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/*********************
+ * INCLUDES
+ *********************/
+
+#include "../lv_conf_internal.h"
+
+#include "lv_types.h"
+
+#include <stdint.h>
+#include <stddef.h>
+
+
+/*********************
+ * DEFINES
+ *********************/
+
+/**********************
+ * TYPEDEFS
+ **********************/
+
+typedef enum {
+ LV_LRU_OK = 0,
+ LV_LRU_MISSING_CACHE,
+ LV_LRU_MISSING_KEY,
+ LV_LRU_MISSING_VALUE,
+ LV_LRU_LOCK_ERROR,
+ LV_LRU_VALUE_TOO_LARGE
+} lv_lru_res_t;
+
+typedef void (lv_lru_free_t)(void * v);
+typedef struct _lv_lru_item_t lv_lru_item_t;
+
+typedef struct lv_lru_t {
+ lv_lru_item_t ** items;
+ uint64_t access_count;
+ size_t free_memory;
+ size_t total_memory;
+ size_t average_item_length;
+ size_t hash_table_size;
+ uint32_t seed;
+ lv_lru_free_t * value_free;
+ lv_lru_free_t * key_free;
+ lv_lru_item_t * free_items;
+} lv_lru_t;
+
+
+/**********************
+ * GLOBAL PROTOTYPES
+ **********************/
+
+lv_lru_t * lv_lru_create(size_t cache_size, size_t average_length, lv_lru_free_t * value_free,
+ lv_lru_free_t * key_free);
+
+void lv_lru_del(lv_lru_t * cache);
+
+lv_lru_res_t lv_lru_set(lv_lru_t * cache, const void * key, size_t key_length, void * value, size_t value_length);
+
+lv_lru_res_t lv_lru_get(lv_lru_t * cache, const void * key, size_t key_size, void ** value);
+
+lv_lru_res_t lv_lru_remove(lv_lru_t * cache, const void * key, size_t key_size);
+
+/**
+ * remove the least recently used item
+ *
+ * @todo we can optimise this by finding the n lru items, where n = required_space / average_length
+ */
+void lv_lru_remove_lru_item(lv_lru_t * cache);
+/**********************
+ * MACROS
+ **********************/
+#ifdef __cplusplus
+} /*extern "C"*/
+#endif
+
+#endif /*LV_LRU_H*/
diff --git a/lib/lvgl/src/misc/lv_math.c b/lib/lvgl/src/misc/lv_math.c
new file mode 100644
index 00000000..bfb3934b
--- /dev/null
+++ b/lib/lvgl/src/misc/lv_math.c
@@ -0,0 +1,273 @@
+/**
+ * @file lv_math.c
+ *
+ */
+
+/*********************
+ * INCLUDES
+ *********************/
+#include "lv_math.h"
+
+/*********************
+ * DEFINES
+ *********************/
+
+/**********************
+ * TYPEDEFS
+ **********************/
+
+/**********************
+ * STATIC PROTOTYPES
+ **********************/
+
+/**********************
+ * STATIC VARIABLES
+ **********************/
+static const int16_t sin0_90_table[] = {
+ 0, 572, 1144, 1715, 2286, 2856, 3425, 3993, 4560, 5126, 5690, 6252, 6813, 7371, 7927, 8481,
+ 9032, 9580, 10126, 10668, 11207, 11743, 12275, 12803, 13328, 13848, 14364, 14876, 15383, 15886, 16383, 16876,
+ 17364, 17846, 18323, 18794, 19260, 19720, 20173, 20621, 21062, 21497, 21925, 22347, 22762, 23170, 23571, 23964,
+ 24351, 24730, 25101, 25465, 25821, 26169, 26509, 26841, 27165, 27481, 27788, 28087, 28377, 28659, 28932, 29196,
+ 29451, 29697, 29934, 30162, 30381, 30591, 30791, 30982, 31163, 31335, 31498, 31650, 31794, 31927, 32051, 32165,
+ 32269, 32364, 32448, 32523, 32587, 32642, 32687, 32722, 32747, 32762, 32767
+};
+
+/**********************
+ * MACROS
+ **********************/
+
+/**********************
+ * GLOBAL FUNCTIONS
+ **********************/
+
+/**
+ * Return with sinus of an angle
+ * @param angle
+ * @return sinus of 'angle'. sin(-90) = -32767, sin(90) = 32767
+ */
+LV_ATTRIBUTE_FAST_MEM int16_t lv_trigo_sin(int16_t angle)
+{
+ int16_t ret = 0;
+ angle = angle % 360;
+
+ if(angle < 0) angle = 360 + angle;
+
+ if(angle < 90) {
+ ret = sin0_90_table[angle];
+ }
+ else if(angle >= 90 && angle < 180) {
+ angle = 180 - angle;
+ ret = sin0_90_table[angle];
+ }
+ else if(angle >= 180 && angle < 270) {
+ angle = angle - 180;
+ ret = -sin0_90_table[angle];
+ }
+ else { /*angle >=270*/
+ angle = 360 - angle;
+ ret = -sin0_90_table[angle];
+ }
+
+ return ret;
+}
+
+/**
+ * Calculate a value of a Cubic Bezier function.
+ * @param t time in range of [0..LV_BEZIER_VAL_MAX]
+ * @param u0 start values in range of [0..LV_BEZIER_VAL_MAX]
+ * @param u1 control value 1 values in range of [0..LV_BEZIER_VAL_MAX]
+ * @param u2 control value 2 in range of [0..LV_BEZIER_VAL_MAX]
+ * @param u3 end values in range of [0..LV_BEZIER_VAL_MAX]
+ * @return the value calculated from the given parameters in range of [0..LV_BEZIER_VAL_MAX]
+ */
+uint32_t lv_bezier3(uint32_t t, uint32_t u0, uint32_t u1, uint32_t u2, uint32_t u3)
+{
+ uint32_t t_rem = 1024 - t;
+ uint32_t t_rem2 = (t_rem * t_rem) >> 10;
+ uint32_t t_rem3 = (t_rem2 * t_rem) >> 10;
+ uint32_t t2 = (t * t) >> 10;
+ uint32_t t3 = (t2 * t) >> 10;
+
+ uint32_t v1 = (t_rem3 * u0) >> 10;
+ uint32_t v2 = (3 * t_rem2 * t * u1) >> 20;
+ uint32_t v3 = (3 * t_rem * t2 * u2) >> 20;
+ uint32_t v4 = (t3 * u3) >> 10;
+
+ return v1 + v2 + v3 + v4;
+}
+
+/**
+ * Get the square root of a number
+ * @param x integer which square root should be calculated
+ * @param q store the result here. q->i: integer part, q->f: fractional part in 1/256 unit
+ * @param mask optional to skip some iterations if the magnitude of the root is known.
+ * Set to 0x8000 by default.
+ * If root < 16: mask = 0x80
+ * If root < 256: mask = 0x800
+ * Else: mask = 0x8000
+ */
+LV_ATTRIBUTE_FAST_MEM void lv_sqrt(uint32_t x, lv_sqrt_res_t * q, uint32_t mask)
+{
+ x = x << 8; /*To get 4 bit precision. (sqrt(256) = 16 = 4 bit)*/
+
+ uint32_t root = 0;
+ uint32_t trial;
+ // http://ww1.microchip.com/...en/AppNotes/91040a.pdf
+ do {
+ trial = root + mask;
+ if(trial * trial <= x) root = trial;
+ mask = mask >> 1;
+ } while(mask);
+
+ q->i = root >> 4;
+ q->f = (root & 0xf) << 4;
+}
+
+/**
+ * Calculate the atan2 of a vector.
+ * @param x
+ * @param y
+ * @return the angle in degree calculated from the given parameters in range of [0..360]
+ */
+uint16_t lv_atan2(int x, int y)
+{
+ // Fast XY vector to integer degree algorithm - Jan 2011 www.RomanBlack.com
+ // Converts any XY values including 0 to a degree value that should be
+ // within +/- 1 degree of the accurate value without needing
+ // large slow trig functions like ArcTan() or ArcCos().
+ // NOTE! at least one of the X or Y values must be non-zero!
+ // This is the full version, for all 4 quadrants and will generate
+ // the angle in integer degrees from 0-360.
+ // Any values of X and Y are usable including negative values provided
+ // they are between -1456 and 1456 so the 16bit multiply does not overflow.
+
+ unsigned char negflag;
+ unsigned char tempdegree;
+ unsigned char comp;
+ unsigned int degree; // this will hold the result
+ unsigned int ux;
+ unsigned int uy;
+
+ // Save the sign flags then remove signs and get XY as unsigned ints
+ negflag = 0;
+ if(x < 0) {
+ negflag += 0x01; // x flag bit
+ x = (0 - x); // is now +
+ }
+ ux = x; // copy to unsigned var before multiply
+ if(y < 0) {
+ negflag += 0x02; // y flag bit
+ y = (0 - y); // is now +
+ }
+ uy = y; // copy to unsigned var before multiply
+
+ // 1. Calc the scaled "degrees"
+ if(ux > uy) {
+ degree = (uy * 45) / ux; // degree result will be 0-45 range
+ negflag += 0x10; // octant flag bit
+ }
+ else {
+ degree = (ux * 45) / uy; // degree result will be 0-45 range
+ }
+
+ // 2. Compensate for the 4 degree error curve
+ comp = 0;
+ tempdegree = degree; // use an unsigned char for speed!
+ if(tempdegree > 22) { // if top half of range
+ if(tempdegree <= 44) comp++;
+ if(tempdegree <= 41) comp++;
+ if(tempdegree <= 37) comp++;
+ if(tempdegree <= 32) comp++; // max is 4 degrees compensated
+ }
+ else { // else is lower half of range
+ if(tempdegree >= 2) comp++;
+ if(tempdegree >= 6) comp++;
+ if(tempdegree >= 10) comp++;
+ if(tempdegree >= 15) comp++; // max is 4 degrees compensated
+ }
+ degree += comp; // degree is now accurate to +/- 1 degree!
+
+ // Invert degree if it was X>Y octant, makes 0-45 into 90-45
+ if(negflag & 0x10) degree = (90 - degree);
+
+ // 3. Degree is now 0-90 range for this quadrant,
+ // need to invert it for whichever quadrant it was in
+ if(negflag & 0x02) { // if -Y
+ if(negflag & 0x01) // if -Y -X
+ degree = (180 + degree);
+ else // else is -Y +X
+ degree = (180 - degree);
+ }
+ else { // else is +Y
+ if(negflag & 0x01) // if +Y -X
+ degree = (360 - degree);
+ }
+ return degree;
+}
+
+/**
+ * Calculate the integer exponents.
+ * @param base
+ * @param power
+ * @return base raised to the power exponent
+ */
+int64_t lv_pow(int64_t base, int8_t exp)
+{
+ int64_t result = 1;
+ while(exp) {
+ if(exp & 1)
+ result *= base;
+ exp >>= 1;
+ base *= base;
+ }
+
+ return result;
+}
+
+/**
+ * Get the mapped of a number given an input and output range
+ * @param x integer which mapped value should be calculated
+ * @param min_in min input range
+ * @param max_in max input range
+ * @param min_out max output range
+ * @param max_out max output range
+ * @return the mapped number
+ */
+int32_t lv_map(int32_t x, int32_t min_in, int32_t max_in, int32_t min_out, int32_t max_out)
+{
+ if(max_in >= min_in && x >= max_in) return max_out;
+ if(max_in >= min_in && x <= min_in) return min_out;
+
+ if(max_in <= min_in && x <= max_in) return max_out;
+ if(max_in <= min_in && x >= min_in) return min_out;
+
+ /**
+ * The equation should be:
+ * ((x - min_in) * delta_out) / delta in) + min_out
+ * To avoid rounding error reorder the operations:
+ * (x - min_in) * (delta_out / delta_min) + min_out
+ */
+
+ int32_t delta_in = max_in - min_in;
+ int32_t delta_out = max_out - min_out;
+
+ return ((x - min_in) * delta_out) / delta_in + min_out;
+}
+
+uint32_t lv_rand(uint32_t min, uint32_t max)
+{
+ static uint32_t a = 0x1234ABCD; /*Seed*/
+
+ /*Algorithm "xor" from p. 4 of Marsaglia, "Xorshift RNGs"*/
+ uint32_t x = a;
+ x ^= x << 13;
+ x ^= x >> 17;
+ x ^= x << 5;
+ a = x;
+
+ return (a % (max - min + 1)) + min;
+}
+
+/**********************
+ * STATIC FUNCTIONS
+ **********************/
diff --git a/lib/lvgl/src/misc/lv_math.h b/lib/lvgl/src/misc/lv_math.h
new file mode 100644
index 00000000..4b2860a9
--- /dev/null
+++ b/lib/lvgl/src/misc/lv_math.h
@@ -0,0 +1,143 @@
+/**
+ * @file lv_math.h
+ *
+ */
+
+#ifndef LV_MATH_H
+#define LV_MATH_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/*********************
+ * INCLUDES
+ *********************/
+#include "../lv_conf_internal.h"
+#include <stdint.h>
+
+/*********************
+ * DEFINES
+ *********************/
+#define LV_TRIGO_SIN_MAX 32767
+#define LV_TRIGO_SHIFT 15 /**< >> LV_TRIGO_SHIFT to normalize*/
+
+#define LV_BEZIER_VAL_MAX 1024 /**< Max time in Bezier functions (not [0..1] to use integers)*/
+#define LV_BEZIER_VAL_SHIFT 10 /**< log2(LV_BEZIER_VAL_MAX): used to normalize up scaled values*/
+
+/**********************
+ * TYPEDEFS
+ **********************/
+
+typedef struct {
+ uint16_t i;
+ uint16_t f;
+} lv_sqrt_res_t;
+
+/**********************
+ * GLOBAL PROTOTYPES
+ **********************/
+
+//! @cond Doxygen_Suppress
+/**
+ * Return with sinus of an angle
+ * @param angle
+ * @return sinus of 'angle'. sin(-90) = -32767, sin(90) = 32767
+ */
+LV_ATTRIBUTE_FAST_MEM int16_t lv_trigo_sin(int16_t angle);
+
+static inline LV_ATTRIBUTE_FAST_MEM int16_t lv_trigo_cos(int16_t angle)
+{
+ return lv_trigo_sin(angle + 90);
+}
+
+//! @endcond
+
+/**
+ * Calculate a value of a Cubic Bezier function.
+ * @param t time in range of [0..LV_BEZIER_VAL_MAX]
+ * @param u0 start values in range of [0..LV_BEZIER_VAL_MAX]
+ * @param u1 control value 1 values in range of [0..LV_BEZIER_VAL_MAX]
+ * @param u2 control value 2 in range of [0..LV_BEZIER_VAL_MAX]
+ * @param u3 end values in range of [0..LV_BEZIER_VAL_MAX]
+ * @return the value calculated from the given parameters in range of [0..LV_BEZIER_VAL_MAX]
+ */
+uint32_t lv_bezier3(uint32_t t, uint32_t u0, uint32_t u1, uint32_t u2, uint32_t u3);
+
+/**
+ * Calculate the atan2 of a vector.
+ * @param x
+ * @param y
+ * @return the angle in degree calculated from the given parameters in range of [0..360]
+ */
+uint16_t lv_atan2(int x, int y);
+
+//! @cond Doxygen_Suppress
+
+/**
+ * Get the square root of a number
+ * @param x integer which square root should be calculated
+ * @param q store the result here. q->i: integer part, q->f: fractional part in 1/256 unit
+ * @param mask optional to skip some iterations if the magnitude of the root is known.
+ * Set to 0x8000 by default.
+ * If root < 16: mask = 0x80
+ * If root < 256: mask = 0x800
+ * Else: mask = 0x8000
+ */
+LV_ATTRIBUTE_FAST_MEM void lv_sqrt(uint32_t x, lv_sqrt_res_t * q, uint32_t mask);
+
+//! @endcond
+
+/**
+ * Calculate the integer exponents.
+ * @param base
+ * @param power
+ * @return base raised to the power exponent
+ */
+int64_t lv_pow(int64_t base, int8_t exp);
+
+/**
+ * Get the mapped of a number given an input and output range
+ * @param x integer which mapped value should be calculated
+ * @param min_in min input range
+ * @param max_in max input range
+ * @param min_out max output range
+ * @param max_out max output range
+ * @return the mapped number
+ */
+int32_t lv_map(int32_t x, int32_t min_in, int32_t max_in, int32_t min_out, int32_t max_out);
+
+/**
+ * Get a pseudo random number in the given range
+ * @param min the minimum value
+ * @param max the maximum value
+ * @return return the random number. min <= return_value <= max
+ */
+uint32_t lv_rand(uint32_t min, uint32_t max);
+
+/**********************
+ * MACROS
+ **********************/
+#define LV_MIN(a, b) ((a) < (b) ? (a) : (b))
+#define LV_MIN3(a, b, c) (LV_MIN(LV_MIN(a,b), c))
+#define LV_MIN4(a, b, c, d) (LV_MIN(LV_MIN(a,b), LV_MIN(c,d)))
+
+#define LV_MAX(a, b) ((a) > (b) ? (a) : (b))
+#define LV_MAX3(a, b, c) (LV_MAX(LV_MAX(a,b), c))
+#define LV_MAX4(a, b, c, d) (LV_MAX(LV_MAX(a,b), LV_MAX(c,d)))
+
+#define LV_CLAMP(min, val, max) (LV_MAX(min, (LV_MIN(val, max))))
+
+#define LV_ABS(x) ((x) > 0 ? (x) : (-(x)))
+#define LV_UDIV255(x) (((x) * 0x8081U) >> 0x17)
+
+#define LV_IS_SIGNED(t) (((t)(-1)) < ((t)0))
+#define LV_UMAX_OF(t) (((0x1ULL << ((sizeof(t) * 8ULL) - 1ULL)) - 1ULL) | (0xFULL << ((sizeof(t) * 8ULL) - 4ULL)))
+#define LV_SMAX_OF(t) (((0x1ULL << ((sizeof(t) * 8ULL) - 1ULL)) - 1ULL) | (0x7ULL << ((sizeof(t) * 8ULL) - 4ULL)))
+#define LV_MAX_OF(t) ((unsigned long)(LV_IS_SIGNED(t) ? LV_SMAX_OF(t) : LV_UMAX_OF(t)))
+
+#ifdef __cplusplus
+} /*extern "C"*/
+#endif
+
+#endif
diff --git a/lib/lvgl/src/misc/lv_mem.c b/lib/lvgl/src/misc/lv_mem.c
new file mode 100644
index 00000000..b7c602f7
--- /dev/null
+++ b/lib/lvgl/src/misc/lv_mem.c
@@ -0,0 +1,566 @@
+/**
+ * @file lv_mem.c
+ * General and portable implementation of malloc and free.
+ * The dynamic memory monitoring is also supported.
+ */
+
+/*********************
+ * INCLUDES
+ *********************/
+#include "lv_mem.h"
+#include "lv_tlsf.h"
+#include "lv_gc.h"
+#include "lv_assert.h"
+#include "lv_log.h"
+
+#if LV_MEM_CUSTOM != 0
+ #include LV_MEM_CUSTOM_INCLUDE
+#endif
+
+#ifdef LV_MEM_POOL_INCLUDE
+ #include LV_MEM_POOL_INCLUDE
+#endif
+
+/*********************
+ * DEFINES
+ *********************/
+/*memset the allocated memories to 0xaa and freed memories to 0xbb (just for testing purposes)*/
+#ifndef LV_MEM_ADD_JUNK
+ #define LV_MEM_ADD_JUNK 0
+#endif
+
+#ifdef LV_ARCH_64
+ #define MEM_UNIT uint64_t
+ #define ALIGN_MASK 0x7
+#else
+ #define MEM_UNIT uint32_t
+ #define ALIGN_MASK 0x3
+#endif
+
+#define ZERO_MEM_SENTINEL 0xa1b2c3d4
+
+/**********************
+ * TYPEDEFS
+ **********************/
+
+/**********************
+ * STATIC PROTOTYPES
+ **********************/
+#if LV_MEM_CUSTOM == 0
+ static void lv_mem_walker(void * ptr, size_t size, int used, void * user);
+#endif
+
+/**********************
+ * STATIC VARIABLES
+ **********************/
+#if LV_MEM_CUSTOM == 0
+ static lv_tlsf_t tlsf;
+ static uint32_t cur_used;
+ static uint32_t max_used;
+#endif
+
+static uint32_t zero_mem = ZERO_MEM_SENTINEL; /*Give the address of this variable if 0 byte should be allocated*/
+
+/**********************
+ * MACROS
+ **********************/
+#if LV_LOG_TRACE_MEM
+ #define MEM_TRACE(...) LV_LOG_TRACE(__VA_ARGS__)
+#else
+ #define MEM_TRACE(...)
+#endif
+
+#define COPY32 *d32 = *s32; d32++; s32++;
+#define COPY8 *d8 = *s8; d8++; s8++;
+#define SET32(x) *d32 = x; d32++;
+#define SET8(x) *d8 = x; d8++;
+#define REPEAT8(expr) expr expr expr expr expr expr expr expr
+
+/**********************
+ * GLOBAL FUNCTIONS
+ **********************/
+
+/**
+ * Initialize the dyn_mem module (work memory and other variables)
+ */
+void lv_mem_init(void)
+{
+#if LV_MEM_CUSTOM == 0
+
+#if LV_MEM_ADR == 0
+#ifdef LV_MEM_POOL_ALLOC
+ tlsf = lv_tlsf_create_with_pool((void *)LV_MEM_POOL_ALLOC(LV_MEM_SIZE), LV_MEM_SIZE);
+#else
+ /*Allocate a large array to store the dynamically allocated data*/
+ static LV_ATTRIBUTE_LARGE_RAM_ARRAY MEM_UNIT work_mem_int[LV_MEM_SIZE / sizeof(MEM_UNIT)];
+ tlsf = lv_tlsf_create_with_pool((void *)work_mem_int, LV_MEM_SIZE);
+#endif
+#else
+ tlsf = lv_tlsf_create_with_pool((void *)LV_MEM_ADR, LV_MEM_SIZE);
+#endif
+#endif
+
+#if LV_MEM_ADD_JUNK
+ LV_LOG_WARN("LV_MEM_ADD_JUNK is enabled which makes LVGL much slower");
+#endif
+}
+
+/**
+ * Clean up the memory buffer which frees all the allocated memories.
+ * @note It work only if `LV_MEM_CUSTOM == 0`
+ */
+void lv_mem_deinit(void)
+{
+#if LV_MEM_CUSTOM == 0
+ lv_tlsf_destroy(tlsf);
+ lv_mem_init();
+#endif
+}
+
+/**
+ * Allocate a memory dynamically
+ * @param size size of the memory to allocate in bytes
+ * @return pointer to the allocated memory
+ */
+void * lv_mem_alloc(size_t size)
+{
+ MEM_TRACE("allocating %lu bytes", (unsigned long)size);
+ if(size == 0) {
+ MEM_TRACE("using zero_mem");
+ return &zero_mem;
+ }
+
+#if LV_MEM_CUSTOM == 0
+ void * alloc = lv_tlsf_malloc(tlsf, size);
+#else
+ void * alloc = LV_MEM_CUSTOM_ALLOC(size);
+#endif
+
+ if(alloc == NULL) {
+ LV_LOG_INFO("couldn't allocate memory (%lu bytes)", (unsigned long)size);
+#if LV_LOG_LEVEL <= LV_LOG_LEVEL_INFO
+ lv_mem_monitor_t mon;
+ lv_mem_monitor(&mon);
+ LV_LOG_INFO("used: %6d (%3d %%), frag: %3d %%, biggest free: %6d",
+ (int)(mon.total_size - mon.free_size), mon.used_pct, mon.frag_pct,
+ (int)mon.free_biggest_size);
+#endif
+ }
+#if LV_MEM_ADD_JUNK
+ else {
+ lv_memset(alloc, 0xaa, size);
+ }
+#endif
+
+ if(alloc) {
+#if LV_MEM_CUSTOM == 0
+ cur_used += size;
+ max_used = LV_MAX(cur_used, max_used);
+#endif
+ MEM_TRACE("allocated at %p", alloc);
+ }
+ return alloc;
+}
+
+/**
+ * Free an allocated data
+ * @param data pointer to an allocated memory
+ */
+void lv_mem_free(void * data)
+{
+ MEM_TRACE("freeing %p", data);
+ if(data == &zero_mem) return;
+ if(data == NULL) return;
+
+#if LV_MEM_CUSTOM == 0
+# if LV_MEM_ADD_JUNK
+ lv_memset(data, 0xbb, lv_tlsf_block_size(data));
+# endif
+ size_t size = lv_tlsf_free(tlsf, data);
+ if(cur_used > size) cur_used -= size;
+ else cur_used = 0;
+#else
+ LV_MEM_CUSTOM_FREE(data);
+#endif
+}
+
+/**
+ * Reallocate a memory with a new size. The old content will be kept.
+ * @param data pointer to an allocated memory.
+ * Its content will be copied to the new memory block and freed
+ * @param new_size the desired new size in byte
+ * @return pointer to the new memory
+ */
+void * lv_mem_realloc(void * data_p, size_t new_size)
+{
+ MEM_TRACE("reallocating %p with %lu size", data_p, (unsigned long)new_size);
+ if(new_size == 0) {
+ MEM_TRACE("using zero_mem");
+ lv_mem_free(data_p);
+ return &zero_mem;
+ }
+
+ if(data_p == &zero_mem) return lv_mem_alloc(new_size);
+
+#if LV_MEM_CUSTOM == 0
+ void * new_p = lv_tlsf_realloc(tlsf, data_p, new_size);
+#else
+ void * new_p = LV_MEM_CUSTOM_REALLOC(data_p, new_size);
+#endif
+ if(new_p == NULL) {
+ LV_LOG_ERROR("couldn't allocate memory");
+ return NULL;
+ }
+
+ MEM_TRACE("allocated at %p", new_p);
+ return new_p;
+}
+
+lv_res_t lv_mem_test(void)
+{
+ if(zero_mem != ZERO_MEM_SENTINEL) {
+ LV_LOG_WARN("zero_mem is written");
+ return LV_RES_INV;
+ }
+
+#if LV_MEM_CUSTOM == 0
+ if(lv_tlsf_check(tlsf)) {
+ LV_LOG_WARN("failed");
+ return LV_RES_INV;
+ }
+
+ if(lv_tlsf_check_pool(lv_tlsf_get_pool(tlsf))) {
+ LV_LOG_WARN("pool failed");
+ return LV_RES_INV;
+ }
+#endif
+ MEM_TRACE("passed");
+ return LV_RES_OK;
+}
+
+/**
+ * Give information about the work memory of dynamic allocation
+ * @param mon_p pointer to a lv_mem_monitor_t variable,
+ * the result of the analysis will be stored here
+ */
+void lv_mem_monitor(lv_mem_monitor_t * mon_p)
+{
+ /*Init the data*/
+ lv_memset(mon_p, 0, sizeof(lv_mem_monitor_t));
+#if LV_MEM_CUSTOM == 0
+ MEM_TRACE("begin");
+
+ lv_tlsf_walk_pool(lv_tlsf_get_pool(tlsf), lv_mem_walker, mon_p);
+
+ mon_p->total_size = LV_MEM_SIZE;
+ mon_p->used_pct = 100 - (100U * mon_p->free_size) / mon_p->total_size;
+ if(mon_p->free_size > 0) {
+ mon_p->frag_pct = mon_p->free_biggest_size * 100U / mon_p->free_size;
+ mon_p->frag_pct = 100 - mon_p->frag_pct;
+ }
+ else {
+ mon_p->frag_pct = 0; /*no fragmentation if all the RAM is used*/
+ }
+
+ mon_p->max_used = max_used;
+
+ MEM_TRACE("finished");
+#endif
+}
+
+
+/**
+ * Get a temporal buffer with the given size.
+ * @param size the required size
+ */
+void * lv_mem_buf_get(uint32_t size)
+{
+ if(size == 0) return NULL;
+
+ MEM_TRACE("begin, getting %d bytes", size);
+
+ /*Try to find a free buffer with suitable size*/
+ int8_t i_guess = -1;
+ for(uint8_t i = 0; i < LV_MEM_BUF_MAX_NUM; i++) {
+ if(LV_GC_ROOT(lv_mem_buf[i]).used == 0 && LV_GC_ROOT(lv_mem_buf[i]).size >= size) {
+ if(LV_GC_ROOT(lv_mem_buf[i]).size == size) {
+ LV_GC_ROOT(lv_mem_buf[i]).used = 1;
+ return LV_GC_ROOT(lv_mem_buf[i]).p;
+ }
+ else if(i_guess < 0) {
+ i_guess = i;
+ }
+ /*If size of `i` is closer to `size` prefer it*/
+ else if(LV_GC_ROOT(lv_mem_buf[i]).size < LV_GC_ROOT(lv_mem_buf[i_guess]).size) {
+ i_guess = i;
+ }
+ }
+ }
+
+ if(i_guess >= 0) {
+ LV_GC_ROOT(lv_mem_buf[i_guess]).used = 1;
+ MEM_TRACE("returning already allocated buffer (buffer id: %d, address: %p)", i_guess,
+ LV_GC_ROOT(lv_mem_buf[i_guess]).p);
+ return LV_GC_ROOT(lv_mem_buf[i_guess]).p;
+ }
+
+ /*Reallocate a free buffer*/
+ for(uint8_t i = 0; i < LV_MEM_BUF_MAX_NUM; i++) {
+ if(LV_GC_ROOT(lv_mem_buf[i]).used == 0) {
+ /*if this fails you probably need to increase your LV_MEM_SIZE/heap size*/
+ void * buf = lv_mem_realloc(LV_GC_ROOT(lv_mem_buf[i]).p, size);
+ LV_ASSERT_MSG(buf != NULL, "Out of memory, can't allocate a new buffer (increase your LV_MEM_SIZE/heap size)");
+ if(buf == NULL) return NULL;
+
+ LV_GC_ROOT(lv_mem_buf[i]).used = 1;
+ LV_GC_ROOT(lv_mem_buf[i]).size = size;
+ LV_GC_ROOT(lv_mem_buf[i]).p = buf;
+ MEM_TRACE("allocated (buffer id: %d, address: %p)", i, LV_GC_ROOT(lv_mem_buf[i]).p);
+ return LV_GC_ROOT(lv_mem_buf[i]).p;
+ }
+ }
+
+ LV_LOG_ERROR("no more buffers. (increase LV_MEM_BUF_MAX_NUM)");
+ LV_ASSERT_MSG(false, "No more buffers. Increase LV_MEM_BUF_MAX_NUM.");
+ return NULL;
+}
+
+/**
+ * Release a memory buffer
+ * @param p buffer to release
+ */
+void lv_mem_buf_release(void * p)
+{
+ MEM_TRACE("begin (address: %p)", p);
+
+ for(uint8_t i = 0; i < LV_MEM_BUF_MAX_NUM; i++) {
+ if(LV_GC_ROOT(lv_mem_buf[i]).p == p) {
+ LV_GC_ROOT(lv_mem_buf[i]).used = 0;
+ return;
+ }
+ }
+
+ LV_LOG_ERROR("p is not a known buffer");
+}
+
+/**
+ * Free all memory buffers
+ */
+void lv_mem_buf_free_all(void)
+{
+ for(uint8_t i = 0; i < LV_MEM_BUF_MAX_NUM; i++) {
+ if(LV_GC_ROOT(lv_mem_buf[i]).p) {
+ lv_mem_free(LV_GC_ROOT(lv_mem_buf[i]).p);
+ LV_GC_ROOT(lv_mem_buf[i]).p = NULL;
+ LV_GC_ROOT(lv_mem_buf[i]).used = 0;
+ LV_GC_ROOT(lv_mem_buf[i]).size = 0;
+ }
+ }
+}
+
+#if LV_MEMCPY_MEMSET_STD == 0
+/**
+ * Same as `memcpy` but optimized for 4 byte operation.
+ * @param dst pointer to the destination buffer
+ * @param src pointer to the source buffer
+ * @param len number of byte to copy
+ */
+LV_ATTRIBUTE_FAST_MEM void * lv_memcpy(void * dst, const void * src, size_t len)
+{
+ uint8_t * d8 = dst;
+ const uint8_t * s8 = src;
+
+ lv_uintptr_t d_align = (lv_uintptr_t)d8 & ALIGN_MASK;
+ lv_uintptr_t s_align = (lv_uintptr_t)s8 & ALIGN_MASK;
+
+ /*Byte copy for unaligned memories*/
+ if(s_align != d_align) {
+ while(len > 32) {
+ REPEAT8(COPY8);
+ REPEAT8(COPY8);
+ REPEAT8(COPY8);
+ REPEAT8(COPY8);
+ len -= 32;
+ }
+ while(len) {
+ COPY8
+ len--;
+ }
+ return dst;
+ }
+
+ /*Make the memories aligned*/
+ if(d_align) {
+ d_align = ALIGN_MASK + 1 - d_align;
+ while(d_align && len) {
+ COPY8;
+ d_align--;
+ len--;
+ }
+ }
+
+ uint32_t * d32 = (uint32_t *)d8;
+ const uint32_t * s32 = (uint32_t *)s8;
+ while(len > 32) {
+ REPEAT8(COPY32)
+ len -= 32;
+ }
+
+ while(len > 4) {
+ COPY32;
+ len -= 4;
+ }
+
+ d8 = (uint8_t *)d32;
+ s8 = (const uint8_t *)s32;
+ while(len) {
+ COPY8
+ len--;
+ }
+
+ return dst;
+}
+
+/**
+ * Same as `memset` but optimized for 4 byte operation.
+ * @param dst pointer to the destination buffer
+ * @param v value to set [0..255]
+ * @param len number of byte to set
+ */
+LV_ATTRIBUTE_FAST_MEM void lv_memset(void * dst, uint8_t v, size_t len)
+{
+
+ uint8_t * d8 = (uint8_t *)dst;
+
+ uintptr_t d_align = (lv_uintptr_t) d8 & ALIGN_MASK;
+
+ /*Make the address aligned*/
+ if(d_align) {
+ d_align = ALIGN_MASK + 1 - d_align;
+ while(d_align && len) {
+ SET8(v);
+ len--;
+ d_align--;
+ }
+ }
+
+ uint32_t v32 = (uint32_t)v + ((uint32_t)v << 8) + ((uint32_t)v << 16) + ((uint32_t)v << 24);
+
+ uint32_t * d32 = (uint32_t *)d8;
+
+ while(len > 32) {
+ REPEAT8(SET32(v32));
+ len -= 32;
+ }
+
+ while(len > 4) {
+ SET32(v32);
+ len -= 4;
+ }
+
+ d8 = (uint8_t *)d32;
+ while(len) {
+ SET8(v);
+ len--;
+ }
+}
+
+/**
+ * Same as `memset(dst, 0x00, len)` but optimized for 4 byte operation.
+ * @param dst pointer to the destination buffer
+ * @param len number of byte to set
+ */
+LV_ATTRIBUTE_FAST_MEM void lv_memset_00(void * dst, size_t len)
+{
+ uint8_t * d8 = (uint8_t *)dst;
+ uintptr_t d_align = (lv_uintptr_t) d8 & ALIGN_MASK;
+
+ /*Make the address aligned*/
+ if(d_align) {
+ d_align = ALIGN_MASK + 1 - d_align;
+ while(d_align && len) {
+ SET8(0);
+ len--;
+ d_align--;
+ }
+ }
+
+ uint32_t * d32 = (uint32_t *)d8;
+ while(len > 32) {
+ REPEAT8(SET32(0));
+ len -= 32;
+ }
+
+ while(len > 4) {
+ SET32(0);
+ len -= 4;
+ }
+
+ d8 = (uint8_t *)d32;
+ while(len) {
+ SET8(0);
+ len--;
+ }
+}
+
+/**
+ * Same as `memset(dst, 0xFF, len)` but optimized for 4 byte operation.
+ * @param dst pointer to the destination buffer
+ * @param len number of byte to set
+ */
+LV_ATTRIBUTE_FAST_MEM void lv_memset_ff(void * dst, size_t len)
+{
+ uint8_t * d8 = (uint8_t *)dst;
+ uintptr_t d_align = (lv_uintptr_t) d8 & ALIGN_MASK;
+
+ /*Make the address aligned*/
+ if(d_align) {
+ d_align = ALIGN_MASK + 1 - d_align;
+ while(d_align && len) {
+ SET8(0xFF);
+ len--;
+ d_align--;
+ }
+ }
+
+ uint32_t * d32 = (uint32_t *)d8;
+ while(len > 32) {
+ REPEAT8(SET32(0xFFFFFFFF));
+ len -= 32;
+ }
+
+ while(len > 4) {
+ SET32(0xFFFFFFFF);
+ len -= 4;
+ }
+
+ d8 = (uint8_t *)d32;
+ while(len) {
+ SET8(0xFF);
+ len--;
+ }
+}
+
+#endif /*LV_MEMCPY_MEMSET_STD*/
+
+/**********************
+ * STATIC FUNCTIONS
+ **********************/
+
+#if LV_MEM_CUSTOM == 0
+static void lv_mem_walker(void * ptr, size_t size, int used, void * user)
+{
+ LV_UNUSED(ptr);
+
+ lv_mem_monitor_t * mon_p = user;
+ if(used) {
+ mon_p->used_cnt++;
+ }
+ else {
+ mon_p->free_cnt++;
+ mon_p->free_size += size;
+ if(size > mon_p->free_biggest_size)
+ mon_p->free_biggest_size = size;
+ }
+}
+#endif
diff --git a/lib/lvgl/src/misc/lv_mem.h b/lib/lvgl/src/misc/lv_mem.h
new file mode 100644
index 00000000..7a83b3d5
--- /dev/null
+++ b/lib/lvgl/src/misc/lv_mem.h
@@ -0,0 +1,243 @@
+/**
+ * @file lv_mem.h
+ *
+ */
+
+#ifndef LV_MEM_H
+#define LV_MEM_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/*********************
+ * INCLUDES
+ *********************/
+#include "../lv_conf_internal.h"
+
+#include <stdint.h>
+#include <stddef.h>
+#include <string.h>
+
+#include "lv_types.h"
+
+/*********************
+ * DEFINES
+ *********************/
+
+/**********************
+ * TYPEDEFS
+ **********************/
+
+/**
+ * Heap information structure.
+ */
+typedef struct {
+ uint32_t total_size; /**< Total heap size*/
+ uint32_t free_cnt;
+ uint32_t free_size; /**< Size of available memory*/
+ uint32_t free_biggest_size;
+ uint32_t used_cnt;
+ uint32_t max_used; /**< Max size of Heap memory used*/
+ uint8_t used_pct; /**< Percentage used*/
+ uint8_t frag_pct; /**< Amount of fragmentation*/
+} lv_mem_monitor_t;
+
+typedef struct {
+ void * p;
+ uint16_t size;
+ uint8_t used : 1;
+} lv_mem_buf_t;
+
+typedef lv_mem_buf_t lv_mem_buf_arr_t[LV_MEM_BUF_MAX_NUM];
+
+/**********************
+ * GLOBAL PROTOTYPES
+ **********************/
+
+/**
+ * Initialize the dyn_mem module (work memory and other variables)
+ */
+void lv_mem_init(void);
+
+/**
+ * Clean up the memory buffer which frees all the allocated memories.
+ * @note It work only if `LV_MEM_CUSTOM == 0`
+ */
+void lv_mem_deinit(void);
+
+/**
+ * Allocate a memory dynamically
+ * @param size size of the memory to allocate in bytes
+ * @return pointer to the allocated memory
+ */
+void * lv_mem_alloc(size_t size);
+
+/**
+ * Free an allocated data
+ * @param data pointer to an allocated memory
+ */
+void lv_mem_free(void * data);
+
+/**
+ * Reallocate a memory with a new size. The old content will be kept.
+ * @param data pointer to an allocated memory.
+ * Its content will be copied to the new memory block and freed
+ * @param new_size the desired new size in byte
+ * @return pointer to the new memory, NULL on failure
+ */
+void * lv_mem_realloc(void * data_p, size_t new_size);
+
+/**
+ *
+ * @return
+ */
+lv_res_t lv_mem_test(void);
+
+/**
+ * Give information about the work memory of dynamic allocation
+ * @param mon_p pointer to a lv_mem_monitor_t variable,
+ * the result of the analysis will be stored here
+ */
+void lv_mem_monitor(lv_mem_monitor_t * mon_p);
+
+
+/**
+ * Get a temporal buffer with the given size.
+ * @param size the required size
+ */
+void * lv_mem_buf_get(uint32_t size);
+
+/**
+ * Release a memory buffer
+ * @param p buffer to release
+ */
+void lv_mem_buf_release(void * p);
+
+/**
+ * Free all memory buffers
+ */
+void lv_mem_buf_free_all(void);
+
+//! @cond Doxygen_Suppress
+
+#if LV_MEMCPY_MEMSET_STD
+
+/**
+ * Wrapper for the standard memcpy
+ * @param dst pointer to the destination buffer
+ * @param src pointer to the source buffer
+ * @param len number of byte to copy
+ */
+static inline void * lv_memcpy(void * dst, const void * src, size_t len)
+{
+ return memcpy(dst, src, len);
+}
+
+/**
+ * Wrapper for the standard memcpy
+ * @param dst pointer to the destination buffer
+ * @param src pointer to the source buffer
+ * @param len number of byte to copy
+ */
+static inline void * lv_memcpy_small(void * dst, const void * src, size_t len)
+{
+ return memcpy(dst, src, len);
+}
+
+/**
+ * Wrapper for the standard memset
+ * @param dst pointer to the destination buffer
+ * @param v value to set [0..255]
+ * @param len number of byte to set
+ */
+static inline void lv_memset(void * dst, uint8_t v, size_t len)
+{
+ memset(dst, v, len);
+}
+
+/**
+ * Wrapper for the standard memset with fixed 0x00 value
+ * @param dst pointer to the destination buffer
+ * @param len number of byte to set
+ */
+static inline void lv_memset_00(void * dst, size_t len)
+{
+ memset(dst, 0x00, len);
+}
+
+/**
+ * Wrapper for the standard memset with fixed 0xFF value
+ * @param dst pointer to the destination buffer
+ * @param len number of byte to set
+ */
+static inline void lv_memset_ff(void * dst, size_t len)
+{
+ memset(dst, 0xFF, len);
+}
+
+#else
+/**
+ * Same as `memcpy` but optimized for 4 byte operation.
+ * @param dst pointer to the destination buffer
+ * @param src pointer to the source buffer
+ * @param len number of byte to copy
+ */
+LV_ATTRIBUTE_FAST_MEM void * lv_memcpy(void * dst, const void * src, size_t len);
+
+/**
+ * Same as `memcpy` but optimized to copy only a few bytes.
+ * @param dst pointer to the destination buffer
+ * @param src pointer to the source buffer
+ * @param len number of byte to copy
+ */
+LV_ATTRIBUTE_FAST_MEM static inline void * lv_memcpy_small(void * dst, const void * src, size_t len)
+{
+ uint8_t * d8 = (uint8_t *)dst;
+ const uint8_t * s8 = (const uint8_t *)src;
+
+ while(len) {
+ *d8 = *s8;
+ d8++;
+ s8++;
+ len--;
+ }
+
+ return dst;
+}
+
+/**
+ * Same as `memset` but optimized for 4 byte operation.
+ * @param dst pointer to the destination buffer
+ * @param v value to set [0..255]
+ * @param len number of byte to set
+ */
+LV_ATTRIBUTE_FAST_MEM void lv_memset(void * dst, uint8_t v, size_t len);
+
+/**
+ * Same as `memset(dst, 0x00, len)` but optimized for 4 byte operation.
+ * @param dst pointer to the destination buffer
+ * @param len number of byte to set
+ */
+LV_ATTRIBUTE_FAST_MEM void lv_memset_00(void * dst, size_t len);
+
+/**
+ * Same as `memset(dst, 0xFF, len)` but optimized for 4 byte operation.
+ * @param dst pointer to the destination buffer
+ * @param len number of byte to set
+ */
+LV_ATTRIBUTE_FAST_MEM void lv_memset_ff(void * dst, size_t len);
+
+//! @endcond
+
+#endif
+
+/**********************
+ * MACROS
+ **********************/
+
+#ifdef __cplusplus
+} /*extern "C"*/
+#endif
+
+#endif /*LV_MEM_H*/
diff --git a/lib/lvgl/src/misc/lv_misc.mk b/lib/lvgl/src/misc/lv_misc.mk
new file mode 100644
index 00000000..1dfd4eec
--- /dev/null
+++ b/lib/lvgl/src/misc/lv_misc.mk
@@ -0,0 +1,26 @@
+CSRCS += lv_anim.c
+CSRCS += lv_anim_timeline.c
+CSRCS += lv_area.c
+CSRCS += lv_async.c
+CSRCS += lv_bidi.c
+CSRCS += lv_color.c
+CSRCS += lv_fs.c
+CSRCS += lv_gc.c
+CSRCS += lv_ll.c
+CSRCS += lv_log.c
+CSRCS += lv_lru.c
+CSRCS += lv_math.c
+CSRCS += lv_mem.c
+CSRCS += lv_printf.c
+CSRCS += lv_style.c
+CSRCS += lv_style_gen.c
+CSRCS += lv_timer.c
+CSRCS += lv_tlsf.c
+CSRCS += lv_txt.c
+CSRCS += lv_txt_ap.c
+CSRCS += lv_utils.c
+
+DEPPATH += --dep-path $(LVGL_DIR)/$(LVGL_DIR_NAME)/src/misc
+VPATH += :$(LVGL_DIR)/$(LVGL_DIR_NAME)/src/misc
+
+CFLAGS += "-I$(LVGL_DIR)/$(LVGL_DIR_NAME)/src/misc"
diff --git a/lib/lvgl/src/misc/lv_printf.c b/lib/lvgl/src/misc/lv_printf.c
new file mode 100644
index 00000000..9077a7a1
--- /dev/null
+++ b/lib/lvgl/src/misc/lv_printf.c
@@ -0,0 +1,879 @@
+///////////////////////////////////////////////////////////////////////////////
+// \author (c) Marco Paland (info@paland.com)
+// 2014-2019, PALANDesign Hannover, Germany
+//
+// \license The MIT License (MIT)
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to deal
+// in the Software without restriction, including without limitation the rights
+// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+// copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+// THE SOFTWARE.
+//
+// \brief Tiny printf, sprintf and (v)snprintf implementation, optimized for speed on
+// embedded systems with a very limited resources. These routines are thread
+// safe and reentrant!
+// Use this instead of the bloated standard/newlib printf cause these use
+// malloc for printf (and may not be thread safe).
+//
+///////////////////////////////////////////////////////////////////////////////
+
+/*Original repository: https://github.com/mpaland/printf*/
+
+#include "lv_printf.h"
+
+#if LV_SPRINTF_CUSTOM == 0
+
+#include <stdbool.h>
+
+#define PRINTF_DISABLE_SUPPORT_FLOAT (!LV_SPRINTF_USE_FLOAT)
+
+// 'ntoa' conversion buffer size, this must be big enough to hold one converted
+// numeric number including padded zeros (dynamically created on stack)
+// default: 32 byte
+#ifndef PRINTF_NTOA_BUFFER_SIZE
+ #define PRINTF_NTOA_BUFFER_SIZE 32U
+#endif
+
+// 'ftoa' conversion buffer size, this must be big enough to hold one converted
+// float number including padded zeros (dynamically created on stack)
+// default: 32 byte
+#ifndef PRINTF_FTOA_BUFFER_SIZE
+ #define PRINTF_FTOA_BUFFER_SIZE 32U
+#endif
+
+// support for the floating point type (%f)
+// default: activated
+#if !PRINTF_DISABLE_SUPPORT_FLOAT
+ #define PRINTF_SUPPORT_FLOAT
+#endif
+
+// support for exponential floating point notation (%e/%g)
+// default: activated
+#ifndef PRINTF_DISABLE_SUPPORT_EXPONENTIAL
+ #define PRINTF_SUPPORT_EXPONENTIAL
+#endif
+
+// define the default floating point precision
+// default: 6 digits
+#ifndef PRINTF_DEFAULT_FLOAT_PRECISION
+ #define PRINTF_DEFAULT_FLOAT_PRECISION 6U
+#endif
+
+// define the largest float suitable to print with %f
+// default: 1e9
+#ifndef PRINTF_MAX_FLOAT
+ #define PRINTF_MAX_FLOAT 1e9
+#endif
+
+// support for the long long types (%llu or %p)
+// default: activated
+#ifndef PRINTF_DISABLE_SUPPORT_LONG_LONG
+ #define PRINTF_SUPPORT_LONG_LONG
+#endif
+
+// support for the ptrdiff_t type (%t)
+// ptrdiff_t is normally defined in <stddef.h> as long or long long type
+// default: activated
+#ifndef PRINTF_DISABLE_SUPPORT_PTRDIFF_T
+ #define PRINTF_SUPPORT_PTRDIFF_T
+#endif
+
+///////////////////////////////////////////////////////////////////////////////
+
+// internal flag definitions
+#define FLAGS_ZEROPAD (1U << 0U)
+#define FLAGS_LEFT (1U << 1U)
+#define FLAGS_PLUS (1U << 2U)
+#define FLAGS_SPACE (1U << 3U)
+#define FLAGS_HASH (1U << 4U)
+#define FLAGS_UPPERCASE (1U << 5U)
+#define FLAGS_CHAR (1U << 6U)
+#define FLAGS_SHORT (1U << 7U)
+#define FLAGS_LONG (1U << 8U)
+#define FLAGS_LONG_LONG (1U << 9U)
+#define FLAGS_PRECISION (1U << 10U)
+#define FLAGS_ADAPT_EXP (1U << 11U)
+
+// import float.h for DBL_MAX
+#if defined(PRINTF_SUPPORT_FLOAT)
+ #include <float.h>
+#endif
+
+// output function type
+typedef void (*out_fct_type)(char character, void * buffer, size_t idx, size_t maxlen);
+
+// wrapper (used as buffer) for output function type
+typedef struct {
+ void (*fct)(char character, void * arg);
+ void * arg;
+} out_fct_wrap_type;
+
+// internal buffer output
+static inline void _out_buffer(char character, void * buffer, size_t idx, size_t maxlen)
+{
+ if(idx < maxlen) {
+ ((char *)buffer)[idx] = character;
+ }
+}
+
+// internal null output
+static inline void _out_null(char character, void * buffer, size_t idx, size_t maxlen)
+{
+ LV_UNUSED(character);
+ LV_UNUSED(buffer);
+ LV_UNUSED(idx);
+ LV_UNUSED(maxlen);
+}
+
+// internal secure strlen
+// \return The length of the string (excluding the terminating 0) limited by 'maxsize'
+static inline unsigned int _strnlen_s(const char * str, size_t maxsize)
+{
+ const char * s;
+ for(s = str; *s && maxsize--; ++s);
+ return (unsigned int)(s - str);
+}
+
+// internal test if char is a digit (0-9)
+// \return true if char is a digit
+static inline bool _is_digit(char ch)
+{
+ return (ch >= '0') && (ch <= '9');
+}
+
+// internal ASCII string to unsigned int conversion
+static unsigned int _atoi(const char ** str)
+{
+ unsigned int i = 0U;
+ while(_is_digit(**str)) {
+ i = i * 10U + (unsigned int)(*((*str)++) - '0');
+ }
+ return i;
+}
+
+// output the specified string in reverse, taking care of any zero-padding
+static size_t _out_rev(out_fct_type out, char * buffer, size_t idx, size_t maxlen, const char * buf, size_t len,
+ unsigned int width, unsigned int flags)
+{
+ const size_t start_idx = idx;
+
+ // pad spaces up to given width
+ if(!(flags & FLAGS_LEFT) && !(flags & FLAGS_ZEROPAD)) {
+ size_t i;
+ for(i = len; i < width; i++) {
+ out(' ', buffer, idx++, maxlen);
+ }
+ }
+
+ // reverse string
+ while(len) {
+ out(buf[--len], buffer, idx++, maxlen);
+ }
+
+ // append pad spaces up to given width
+ if(flags & FLAGS_LEFT) {
+ while(idx - start_idx < width) {
+ out(' ', buffer, idx++, maxlen);
+ }
+ }
+
+ return idx;
+}
+
+// internal itoa format
+static size_t _ntoa_format(out_fct_type out, char * buffer, size_t idx, size_t maxlen, char * buf, size_t len,
+ bool negative, unsigned int base, unsigned int prec, unsigned int width, unsigned int flags)
+{
+ // pad leading zeros
+ if(!(flags & FLAGS_LEFT)) {
+ if(width && (flags & FLAGS_ZEROPAD) && (negative || (flags & (FLAGS_PLUS | FLAGS_SPACE)))) {
+ width--;
+ }
+ while((len < prec) && (len < PRINTF_NTOA_BUFFER_SIZE)) {
+ buf[len++] = '0';
+ }
+ while((flags & FLAGS_ZEROPAD) && (len < width) && (len < PRINTF_NTOA_BUFFER_SIZE)) {
+ buf[len++] = '0';
+ }
+ }
+
+ // handle hash
+ if(flags & FLAGS_HASH) {
+ if(!(flags & FLAGS_PRECISION) && len && ((len == prec) || (len == width))) {
+ len--;
+ if(len && (base == 16U)) {
+ len--;
+ }
+ }
+ if((base == 16U) && !(flags & FLAGS_UPPERCASE) && (len < PRINTF_NTOA_BUFFER_SIZE)) {
+ buf[len++] = 'x';
+ }
+ else if((base == 16U) && (flags & FLAGS_UPPERCASE) && (len < PRINTF_NTOA_BUFFER_SIZE)) {
+ buf[len++] = 'X';
+ }
+ else if((base == 2U) && (len < PRINTF_NTOA_BUFFER_SIZE)) {
+ buf[len++] = 'b';
+ }
+ if(len < PRINTF_NTOA_BUFFER_SIZE) {
+ buf[len++] = '0';
+ }
+ }
+
+ if(len < PRINTF_NTOA_BUFFER_SIZE) {
+ if(negative) {
+ buf[len++] = '-';
+ }
+ else if(flags & FLAGS_PLUS) {
+ buf[len++] = '+'; // ignore the space if the '+' exists
+ }
+ else if(flags & FLAGS_SPACE) {
+ buf[len++] = ' ';
+ }
+ }
+
+ return _out_rev(out, buffer, idx, maxlen, buf, len, width, flags);
+}
+
+// internal itoa for 'long' type
+static size_t _ntoa_long(out_fct_type out, char * buffer, size_t idx, size_t maxlen, unsigned long value, bool negative,
+ unsigned long base, unsigned int prec, unsigned int width, unsigned int flags)
+{
+ char buf[PRINTF_NTOA_BUFFER_SIZE];
+ size_t len = 0U;
+
+ // no hash for 0 values
+ if(!value) {
+ flags &= ~FLAGS_HASH;
+ }
+
+ // write if precision != 0 and value is != 0
+ if(!(flags & FLAGS_PRECISION) || value) {
+ do {
+ const char digit = (char)(value % base);
+ buf[len++] = digit < 10 ? '0' + digit : (flags & FLAGS_UPPERCASE ? 'A' : 'a') + digit - 10;
+ value /= base;
+ } while(value && (len < PRINTF_NTOA_BUFFER_SIZE));
+ }
+
+ return _ntoa_format(out, buffer, idx, maxlen, buf, len, negative, (unsigned int)base, prec, width, flags);
+}
+
+// internal itoa for 'long long' type
+#if defined(PRINTF_SUPPORT_LONG_LONG)
+static size_t _ntoa_long_long(out_fct_type out, char * buffer, size_t idx, size_t maxlen, unsigned long long value,
+ bool negative, unsigned long long base, unsigned int prec, unsigned int width, unsigned int flags)
+{
+ char buf[PRINTF_NTOA_BUFFER_SIZE];
+ size_t len = 0U;
+
+ // no hash for 0 values
+ if(!value) {
+ flags &= ~FLAGS_HASH;
+ }
+
+ // write if precision != 0 and value is != 0
+ if(!(flags & FLAGS_PRECISION) || value) {
+ do {
+ const char digit = (char)(value % base);
+ buf[len++] = digit < 10 ? '0' + digit : (flags & FLAGS_UPPERCASE ? 'A' : 'a') + digit - 10;
+ value /= base;
+ } while(value && (len < PRINTF_NTOA_BUFFER_SIZE));
+ }
+
+ return _ntoa_format(out, buffer, idx, maxlen, buf, len, negative, (unsigned int)base, prec, width, flags);
+}
+#endif // PRINTF_SUPPORT_LONG_LONG
+
+#if defined(PRINTF_SUPPORT_FLOAT)
+
+#if defined(PRINTF_SUPPORT_EXPONENTIAL)
+// forward declaration so that _ftoa can switch to exp notation for values > PRINTF_MAX_FLOAT
+static size_t _etoa(out_fct_type out, char * buffer, size_t idx, size_t maxlen, double value, unsigned int prec,
+ unsigned int width, unsigned int flags);
+#endif
+
+// internal ftoa for fixed decimal floating point
+static size_t _ftoa(out_fct_type out, char * buffer, size_t idx, size_t maxlen, double value, unsigned int prec,
+ unsigned int width, unsigned int flags)
+{
+ char buf[PRINTF_FTOA_BUFFER_SIZE];
+ size_t len = 0U;
+ double diff = 0.0;
+
+ // powers of 10
+ static const double pow10[] = { 1, 10, 100, 1000, 10000, 100000, 1000000, 10000000, 100000000, 1000000000 };
+
+ // test for special values
+ if(value != value)
+ return _out_rev(out, buffer, idx, maxlen, "nan", 3, width, flags);
+ if(value < -DBL_MAX)
+ return _out_rev(out, buffer, idx, maxlen, "fni-", 4, width, flags);
+ if(value > DBL_MAX)
+ return _out_rev(out, buffer, idx, maxlen, (flags & FLAGS_PLUS) ? "fni+" : "fni", (flags & FLAGS_PLUS) ? 4U : 3U, width,
+ flags);
+
+ // test for very large values
+ // standard printf behavior is to print EVERY whole number digit -- which could be 100s of characters overflowing your buffers == bad
+ if((value > PRINTF_MAX_FLOAT) || (value < -PRINTF_MAX_FLOAT)) {
+#if defined(PRINTF_SUPPORT_EXPONENTIAL)
+ return _etoa(out, buffer, idx, maxlen, value, prec, width, flags);
+#else
+ return 0U;
+#endif
+ }
+
+ // test for negative
+ bool negative = false;
+ if(value < 0) {
+ negative = true;
+ value = 0 - value;
+ }
+
+ // set default precision, if not set explicitly
+ if(!(flags & FLAGS_PRECISION)) {
+ prec = PRINTF_DEFAULT_FLOAT_PRECISION;
+ }
+ // limit precision to 9, cause a prec >= 10 can lead to overflow errors
+ while((len < PRINTF_FTOA_BUFFER_SIZE) && (prec > 9U)) {
+ buf[len++] = '0';
+ prec--;
+ }
+
+ int whole = (int)value;
+ double tmp = (value - whole) * pow10[prec];
+ unsigned long frac = (unsigned long)tmp;
+ diff = tmp - frac;
+
+ if(diff > 0.5) {
+ ++frac;
+ // handle rollover, e.g. case 0.99 with prec 1 is 1.0
+ if(frac >= pow10[prec]) {
+ frac = 0;
+ ++whole;
+ }
+ }
+ else if(diff < 0.5) {
+ }
+ else if((frac == 0U) || (frac & 1U)) {
+ // if halfway, round up if odd OR if last digit is 0
+ ++frac;
+ }
+
+ if(prec == 0U) {
+ diff = value - (double)whole;
+ if((!(diff < 0.5) || (diff > 0.5)) && (whole & 1)) {
+ // exactly 0.5 and ODD, then round up
+ // 1.5 -> 2, but 2.5 -> 2
+ ++whole;
+ }
+ }
+ else {
+ unsigned int count = prec;
+ // now do fractional part, as an unsigned number
+ while(len < PRINTF_FTOA_BUFFER_SIZE) {
+ --count;
+ buf[len++] = (char)(48U + (frac % 10U));
+ if(!(frac /= 10U)) {
+ break;
+ }
+ }
+ // add extra 0s
+ while((len < PRINTF_FTOA_BUFFER_SIZE) && (count-- > 0U)) {
+ buf[len++] = '0';
+ }
+ if(len < PRINTF_FTOA_BUFFER_SIZE) {
+ // add decimal
+ buf[len++] = '.';
+ }
+ }
+
+ // do whole part, number is reversed
+ while(len < PRINTF_FTOA_BUFFER_SIZE) {
+ buf[len++] = (char)(48 + (whole % 10));
+ if(!(whole /= 10)) {
+ break;
+ }
+ }
+
+ // pad leading zeros
+ if(!(flags & FLAGS_LEFT) && (flags & FLAGS_ZEROPAD)) {
+ if(width && (negative || (flags & (FLAGS_PLUS | FLAGS_SPACE)))) {
+ width--;
+ }
+ while((len < width) && (len < PRINTF_FTOA_BUFFER_SIZE)) {
+ buf[len++] = '0';
+ }
+ }
+
+ if(len < PRINTF_FTOA_BUFFER_SIZE) {
+ if(negative) {
+ buf[len++] = '-';
+ }
+ else if(flags & FLAGS_PLUS) {
+ buf[len++] = '+'; // ignore the space if the '+' exists
+ }
+ else if(flags & FLAGS_SPACE) {
+ buf[len++] = ' ';
+ }
+ }
+
+ return _out_rev(out, buffer, idx, maxlen, buf, len, width, flags);
+}
+
+#if defined(PRINTF_SUPPORT_EXPONENTIAL)
+// internal ftoa variant for exponential floating-point type, contributed by Martijn Jasperse <m.jasperse@gmail.com>
+static size_t _etoa(out_fct_type out, char * buffer, size_t idx, size_t maxlen, double value, unsigned int prec,
+ unsigned int width, unsigned int flags)
+{
+ // check for NaN and special values
+ if((value != value) || (value > DBL_MAX) || (value < -DBL_MAX)) {
+ return _ftoa(out, buffer, idx, maxlen, value, prec, width, flags);
+ }
+
+ // determine the sign
+ const bool negative = value < 0;
+ if(negative) {
+ value = -value;
+ }
+
+ // default precision
+ if(!(flags & FLAGS_PRECISION)) {
+ prec = PRINTF_DEFAULT_FLOAT_PRECISION;
+ }
+
+ // determine the decimal exponent
+ // based on the algorithm by David Gay (https://www.ampl.com/netlib/fp/dtoa.c)
+ union {
+ uint64_t U;
+ double F;
+ } conv;
+
+ conv.F = value;
+ int exp2 = (int)((conv.U >> 52U) & 0x07FFU) - 1023; // effectively log2
+ conv.U = (conv.U & ((1ULL << 52U) - 1U)) | (1023ULL << 52U); // drop the exponent so conv.F is now in [1,2)
+ // now approximate log10 from the log2 integer part and an expansion of ln around 1.5
+ int expval = (int)(0.1760912590558 + exp2 * 0.301029995663981 + (conv.F - 1.5) * 0.289529654602168);
+ // now we want to compute 10^expval but we want to be sure it won't overflow
+ exp2 = (int)(expval * 3.321928094887362 + 0.5);
+ const double z = expval * 2.302585092994046 - exp2 * 0.6931471805599453;
+ const double z2 = z * z;
+ conv.U = (uint64_t)(exp2 + 1023) << 52U;
+ // compute exp(z) using continued fractions, see https://en.wikipedia.org/wiki/Exponential_function#Continued_fractions_for_ex
+ conv.F *= 1 + 2 * z / (2 - z + (z2 / (6 + (z2 / (10 + z2 / 14)))));
+ // correct for rounding errors
+ if(value < conv.F) {
+ expval--;
+ conv.F /= 10;
+ }
+
+ // the exponent format is "%+03d" and largest value is "307", so set aside 4-5 characters
+ unsigned int minwidth = ((expval < 100) && (expval > -100)) ? 4U : 5U;
+
+ // in "%g" mode, "prec" is the number of *significant figures* not decimals
+ if(flags & FLAGS_ADAPT_EXP) {
+ // do we want to fall-back to "%f" mode?
+ if((value >= 1e-4) && (value < 1e6)) {
+ if((int)prec > expval) {
+ prec = (unsigned)((int)prec - expval - 1);
+ }
+ else {
+ prec = 0;
+ }
+ flags |= FLAGS_PRECISION; // make sure _ftoa respects precision
+ // no characters in exponent
+ minwidth = 0U;
+ expval = 0;
+ }
+ else {
+ // we use one sigfig for the whole part
+ if((prec > 0) && (flags & FLAGS_PRECISION)) {
+ --prec;
+ }
+ }
+ }
+
+ // will everything fit?
+ unsigned int fwidth = width;
+ if(width > minwidth) {
+ // we didn't fall-back so subtract the characters required for the exponent
+ fwidth -= minwidth;
+ }
+ else {
+ // not enough characters, so go back to default sizing
+ fwidth = 0U;
+ }
+ if((flags & FLAGS_LEFT) && minwidth) {
+ // if we're padding on the right, DON'T pad the floating part
+ fwidth = 0U;
+ }
+
+ // rescale the float value
+ if(expval) {
+ value /= conv.F;
+ }
+
+ // output the floating part
+ const size_t start_idx = idx;
+ idx = _ftoa(out, buffer, idx, maxlen, negative ? -value : value, prec, fwidth, flags & ~FLAGS_ADAPT_EXP);
+
+ // output the exponent part
+ if(minwidth) {
+ // output the exponential symbol
+ out((flags & FLAGS_UPPERCASE) ? 'E' : 'e', buffer, idx++, maxlen);
+ // output the exponent value
+ idx = _ntoa_long(out, buffer, idx, maxlen, (expval < 0) ? -expval : expval, expval < 0, 10, 0, minwidth - 1,
+ FLAGS_ZEROPAD | FLAGS_PLUS);
+ // might need to right-pad spaces
+ if(flags & FLAGS_LEFT) {
+ while(idx - start_idx < width) out(' ', buffer, idx++, maxlen);
+ }
+ }
+ return idx;
+}
+#endif // PRINTF_SUPPORT_EXPONENTIAL
+#endif // PRINTF_SUPPORT_FLOAT
+
+// internal vsnprintf
+static int _vsnprintf(out_fct_type out, char * buffer, const size_t maxlen, const char * format, va_list va)
+{
+ unsigned int flags, width, precision, n;
+ size_t idx = 0U;
+
+ if(!buffer) {
+ // use null output function
+ out = _out_null;
+ }
+
+ while(*format) {
+ // format specifier? %[flags][width][.precision][length]
+ if(*format != '%') {
+ // no
+ out(*format, buffer, idx++, maxlen);
+ format++;
+ continue;
+ }
+ else {
+ // yes, evaluate it
+ format++;
+ }
+
+ // evaluate flags
+ flags = 0U;
+ do {
+ switch(*format) {
+ case '0':
+ flags |= FLAGS_ZEROPAD;
+ format++;
+ n = 1U;
+ break;
+ case '-':
+ flags |= FLAGS_LEFT;
+ format++;
+ n = 1U;
+ break;
+ case '+':
+ flags |= FLAGS_PLUS;
+ format++;
+ n = 1U;
+ break;
+ case ' ':
+ flags |= FLAGS_SPACE;
+ format++;
+ n = 1U;
+ break;
+ case '#':
+ flags |= FLAGS_HASH;
+ format++;
+ n = 1U;
+ break;
+ default :
+ n = 0U;
+ break;
+ }
+ } while(n);
+
+ // evaluate width field
+ width = 0U;
+ if(_is_digit(*format)) {
+ width = _atoi(&format);
+ }
+ else if(*format == '*') {
+ const int w = va_arg(va, int);
+ if(w < 0) {
+ flags |= FLAGS_LEFT; // reverse padding
+ width = (unsigned int) - w;
+ }
+ else {
+ width = (unsigned int)w;
+ }
+ format++;
+ }
+
+ // evaluate precision field
+ precision = 0U;
+ if(*format == '.') {
+ flags |= FLAGS_PRECISION;
+ format++;
+ if(_is_digit(*format)) {
+ precision = _atoi(&format);
+ }
+ else if(*format == '*') {
+ const int prec = (int)va_arg(va, int);
+ precision = prec > 0 ? (unsigned int)prec : 0U;
+ format++;
+ }
+ }
+
+ // evaluate length field
+ switch(*format) {
+ case 'l' :
+ flags |= FLAGS_LONG;
+ format++;
+ if(*format == 'l') {
+ flags |= FLAGS_LONG_LONG;
+ format++;
+ }
+ break;
+ case 'h' :
+ flags |= FLAGS_SHORT;
+ format++;
+ if(*format == 'h') {
+ flags |= FLAGS_CHAR;
+ format++;
+ }
+ break;
+#if defined(PRINTF_SUPPORT_PTRDIFF_T)
+ case 't' :
+ flags |= (sizeof(ptrdiff_t) == sizeof(long) ? FLAGS_LONG : FLAGS_LONG_LONG);
+ format++;
+ break;
+#endif
+ case 'j' :
+ flags |= (sizeof(intmax_t) == sizeof(long) ? FLAGS_LONG : FLAGS_LONG_LONG);
+ format++;
+ break;
+ case 'z' :
+ flags |= (sizeof(size_t) == sizeof(long) ? FLAGS_LONG : FLAGS_LONG_LONG);
+ format++;
+ break;
+ default :
+ break;
+ }
+
+ // evaluate specifier
+ switch(*format) {
+ case 'd' :
+ case 'i' :
+ case 'u' :
+ case 'x' :
+ case 'X' :
+ case 'p' :
+ case 'P' :
+ case 'o' :
+ case 'b' : {
+ // set the base
+ unsigned int base;
+ if(*format == 'x' || *format == 'X') {
+ base = 16U;
+ }
+ else if(*format == 'p' || *format == 'P') {
+ base = 16U;
+ flags |= FLAGS_HASH; // always hash for pointer format
+#if defined(PRINTF_SUPPORT_LONG_LONG)
+ if(sizeof(uintptr_t) == sizeof(long long))
+ flags |= FLAGS_LONG_LONG;
+ else
+#endif
+ flags |= FLAGS_LONG;
+
+ if(*(format + 1) == 'V')
+ format++;
+ }
+ else if(*format == 'o') {
+ base = 8U;
+ }
+ else if(*format == 'b') {
+ base = 2U;
+ }
+ else {
+ base = 10U;
+ flags &= ~FLAGS_HASH; // no hash for dec format
+ }
+ // uppercase
+ if(*format == 'X' || *format == 'P') {
+ flags |= FLAGS_UPPERCASE;
+ }
+
+ // no plus or space flag for u, x, X, o, b
+ if((*format != 'i') && (*format != 'd')) {
+ flags &= ~(FLAGS_PLUS | FLAGS_SPACE);
+ }
+
+ // ignore '0' flag when precision is given
+ if(flags & FLAGS_PRECISION) {
+ flags &= ~FLAGS_ZEROPAD;
+ }
+
+ // convert the integer
+ if((*format == 'i') || (*format == 'd')) {
+ // signed
+ if(flags & FLAGS_LONG_LONG) {
+#if defined(PRINTF_SUPPORT_LONG_LONG)
+ const long long value = va_arg(va, long long);
+ idx = _ntoa_long_long(out, buffer, idx, maxlen, (unsigned long long)(value > 0 ? value : 0 - value), value < 0, base,
+ precision, width, flags);
+#endif
+ }
+ else if(flags & FLAGS_LONG) {
+ const long value = va_arg(va, long);
+ idx = _ntoa_long(out, buffer, idx, maxlen, (unsigned long)(value > 0 ? value : 0 - value), value < 0, base, precision,
+ width, flags);
+ }
+ else {
+ const int value = (flags & FLAGS_CHAR) ? (char)va_arg(va, int) : (flags & FLAGS_SHORT) ? (short int)va_arg(va,
+ int) : va_arg(va, int);
+ idx = _ntoa_long(out, buffer, idx, maxlen, (unsigned int)(value > 0 ? value : 0 - value), value < 0, base, precision,
+ width, flags);
+ }
+ }
+ else if(*format == 'V') {
+ lv_vaformat_t * vaf = va_arg(va, lv_vaformat_t *);
+ va_list copy;
+
+ va_copy(copy, *vaf->va);
+ idx += _vsnprintf(out, buffer + idx, maxlen - idx, vaf->fmt, copy);
+ va_end(copy);
+ }
+ else {
+ // unsigned
+ if(flags & FLAGS_LONG_LONG) {
+#if defined(PRINTF_SUPPORT_LONG_LONG)
+ idx = _ntoa_long_long(out, buffer, idx, maxlen, va_arg(va, unsigned long long), false, base, precision, width, flags);
+#endif
+ }
+ else if(flags & FLAGS_LONG) {
+ idx = _ntoa_long(out, buffer, idx, maxlen, va_arg(va, unsigned long), false, base, precision, width, flags);
+ }
+ else {
+ const unsigned int value = (flags & FLAGS_CHAR) ? (unsigned char)va_arg(va,
+ unsigned int) : (flags & FLAGS_SHORT) ? (unsigned short int)va_arg(va, unsigned int) : va_arg(va, unsigned int);
+ idx = _ntoa_long(out, buffer, idx, maxlen, value, false, base, precision, width, flags);
+ }
+ }
+ format++;
+ break;
+ }
+#if defined(PRINTF_SUPPORT_FLOAT)
+ case 'f' :
+ case 'F' :
+ if(*format == 'F') flags |= FLAGS_UPPERCASE;
+ idx = _ftoa(out, buffer, idx, maxlen, va_arg(va, double), precision, width, flags);
+ format++;
+ break;
+#if defined(PRINTF_SUPPORT_EXPONENTIAL)
+ case 'e':
+ case 'E':
+ case 'g':
+ case 'G':
+ if((*format == 'g') || (*format == 'G')) flags |= FLAGS_ADAPT_EXP;
+ if((*format == 'E') || (*format == 'G')) flags |= FLAGS_UPPERCASE;
+ idx = _etoa(out, buffer, idx, maxlen, va_arg(va, double), precision, width, flags);
+ format++;
+ break;
+#endif // PRINTF_SUPPORT_EXPONENTIAL
+#endif // PRINTF_SUPPORT_FLOAT
+ case 'c' : {
+ unsigned int l = 1U;
+ // pre padding
+ if(!(flags & FLAGS_LEFT)) {
+ while(l++ < width) {
+ out(' ', buffer, idx++, maxlen);
+ }
+ }
+ // char output
+ out((char)va_arg(va, int), buffer, idx++, maxlen);
+ // post padding
+ if(flags & FLAGS_LEFT) {
+ while(l++ < width) {
+ out(' ', buffer, idx++, maxlen);
+ }
+ }
+ format++;
+ break;
+ }
+
+ case 's' : {
+ const char * p = va_arg(va, char *);
+ unsigned int l = _strnlen_s(p, precision ? precision : (size_t) -1);
+ // pre padding
+ if(flags & FLAGS_PRECISION) {
+ l = (l < precision ? l : precision);
+ }
+ if(!(flags & FLAGS_LEFT)) {
+ while(l++ < width) {
+ out(' ', buffer, idx++, maxlen);
+ }
+ }
+ // string output
+ while((*p != 0) && (!(flags & FLAGS_PRECISION) || precision--)) {
+ out(*(p++), buffer, idx++, maxlen);
+ }
+ // post padding
+ if(flags & FLAGS_LEFT) {
+ while(l++ < width) {
+ out(' ', buffer, idx++, maxlen);
+ }
+ }
+ format++;
+ break;
+ }
+
+ case '%' :
+ out('%', buffer, idx++, maxlen);
+ format++;
+ break;
+
+ default :
+ out(*format, buffer, idx++, maxlen);
+ format++;
+ break;
+ }
+ }
+
+ // termination
+ out((char)0, buffer, idx < maxlen ? idx : maxlen - 1U, maxlen);
+
+ // return written chars without terminating \0
+ return (int)idx;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+int lv_snprintf(char * buffer, size_t count, const char * format, ...)
+{
+ va_list va;
+ va_start(va, format);
+ const int ret = _vsnprintf(_out_buffer, buffer, count, format, va);
+ va_end(va);
+ return ret;
+}
+
+int lv_vsnprintf(char * buffer, size_t count, const char * format, va_list va)
+{
+ return _vsnprintf(_out_buffer, buffer, count, format, va);
+}
+
+#endif /*LV_SPRINTF_CUSTOM*/
diff --git a/lib/lvgl/src/misc/lv_printf.h b/lib/lvgl/src/misc/lv_printf.h
new file mode 100644
index 00000000..4cbbd84c
--- /dev/null
+++ b/lib/lvgl/src/misc/lv_printf.h
@@ -0,0 +1,92 @@
+///////////////////////////////////////////////////////////////////////////////
+// \author (c) Marco Paland (info@paland.com)
+// 2014-2019, PALANDesign Hannover, Germany
+//
+// \license The MIT License (MIT)
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to deal
+// in the Software without restriction, including without limitation the rights
+// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+// copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+// THE SOFTWARE.
+//
+// \brief Tiny printf, sprintf and snprintf implementation, optimized for speed on
+// embedded systems with a very limited resources.
+// Use this instead of bloated standard/newlib printf.
+// These routines are thread safe and reentrant.
+//
+///////////////////////////////////////////////////////////////////////////////
+
+/*Original repository: https://github.com/mpaland/printf*/
+
+#ifndef _LV_PRINTF_H_
+#define _LV_PRINTF_H_
+
+#if defined(__has_include)
+ #if __has_include(<inttypes.h>)
+ #include <inttypes.h>
+ /* platform-specific printf format for int32_t, usually "d" or "ld" */
+ #define LV_PRId32 PRId32
+ #define LV_PRIu32 PRIu32
+ #else
+ #define LV_PRId32 "d"
+ #define LV_PRIu32 "u"
+ #endif
+#else
+ /* hope this is correct for ports without __has_include or without inttypes.h */
+ #define LV_PRId32 "d"
+ #define LV_PRIu32 "u"
+#endif
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include "../lv_conf_internal.h"
+
+#if LV_SPRINTF_CUSTOM == 0
+
+#include <stdarg.h>
+#include <stddef.h>
+
+#include "lv_types.h"
+
+typedef struct {
+ const char * fmt;
+ va_list * va;
+} lv_vaformat_t;
+
+/**
+ * Tiny snprintf/vsnprintf implementation
+ * \param buffer A pointer to the buffer where to store the formatted string
+ * \param count The maximum number of characters to store in the buffer, including a terminating null character
+ * \param format A string that specifies the format of the output
+ * \param va A value identifying a variable arguments list
+ * \return The number of characters that COULD have been written into the buffer, not counting the terminating
+ * null character. A value equal or larger than count indicates truncation. Only when the returned value
+ * is non-negative and less than count, the string has been completely written.
+ */
+int lv_snprintf(char * buffer, size_t count, const char * format, ...) LV_FORMAT_ATTRIBUTE(3, 4);
+int lv_vsnprintf(char * buffer, size_t count, const char * format, va_list va) LV_FORMAT_ATTRIBUTE(3, 0);
+
+#else
+#include LV_SPRINTF_INCLUDE
+#endif
+
+#ifdef __cplusplus
+} /*extern "C"*/
+#endif
+
+#endif // _LV_PRINTF_H_
diff --git a/lib/lvgl/src/misc/lv_style.c b/lib/lvgl/src/misc/lv_style.c
new file mode 100644
index 00000000..419c29e4
--- /dev/null
+++ b/lib/lvgl/src/misc/lv_style.c
@@ -0,0 +1,485 @@
+/**
+ * @file lv_style.c
+ *
+ */
+
+/*********************
+ * INCLUDES
+ *********************/
+#include "lv_style.h"
+#include "../misc/lv_gc.h"
+#include "../misc/lv_mem.h"
+#include "lv_assert.h"
+#include "lv_types.h"
+
+/*********************
+ * DEFINES
+ *********************/
+
+/**********************
+ * TYPEDEFS
+ **********************/
+
+/**********************
+ * STATIC PROTOTYPES
+ **********************/
+
+static void lv_style_set_prop_internal(lv_style_t * style, lv_style_prop_t prop_and_meta, lv_style_value_t value,
+ void (*value_adjustment_helper)(lv_style_prop_t, lv_style_value_t, uint16_t *, lv_style_value_t *));
+static void lv_style_set_prop_helper(lv_style_prop_t prop, lv_style_value_t value, uint16_t * prop_storage,
+ lv_style_value_t * value_storage);
+static void lv_style_set_prop_meta_helper(lv_style_prop_t prop, lv_style_value_t value, uint16_t * prop_storage,
+ lv_style_value_t * value_storage);
+
+/**********************
+ * GLOBAL VARIABLES
+ **********************/
+
+const uint8_t _lv_style_builtin_prop_flag_lookup_table[_LV_STYLE_NUM_BUILT_IN_PROPS] = {
+ [LV_STYLE_WIDTH] = LV_STYLE_PROP_LAYOUT_REFR,
+ [LV_STYLE_MIN_WIDTH] = LV_STYLE_PROP_LAYOUT_REFR,
+ [LV_STYLE_MAX_WIDTH] = LV_STYLE_PROP_LAYOUT_REFR,
+ [LV_STYLE_HEIGHT] = LV_STYLE_PROP_LAYOUT_REFR,
+ [LV_STYLE_MIN_HEIGHT] = LV_STYLE_PROP_LAYOUT_REFR,
+ [LV_STYLE_MAX_HEIGHT] = LV_STYLE_PROP_LAYOUT_REFR,
+ [LV_STYLE_X] = LV_STYLE_PROP_LAYOUT_REFR,
+ [LV_STYLE_Y] = LV_STYLE_PROP_LAYOUT_REFR,
+ [LV_STYLE_ALIGN] = LV_STYLE_PROP_LAYOUT_REFR,
+ [LV_STYLE_TRANSFORM_WIDTH] = LV_STYLE_PROP_EXT_DRAW,
+ [LV_STYLE_TRANSFORM_HEIGHT] = LV_STYLE_PROP_EXT_DRAW,
+ [LV_STYLE_TRANSLATE_X] = LV_STYLE_PROP_LAYOUT_REFR | LV_STYLE_PROP_PARENT_LAYOUT_REFR,
+ [LV_STYLE_TRANSLATE_Y] = LV_STYLE_PROP_LAYOUT_REFR | LV_STYLE_PROP_PARENT_LAYOUT_REFR,
+ [LV_STYLE_TRANSFORM_ZOOM] = LV_STYLE_PROP_EXT_DRAW | LV_STYLE_PROP_LAYER_REFR,
+ [LV_STYLE_TRANSFORM_ANGLE] = LV_STYLE_PROP_EXT_DRAW | LV_STYLE_PROP_LAYER_REFR,
+
+ [LV_STYLE_PAD_TOP] = LV_STYLE_PROP_EXT_DRAW | LV_STYLE_PROP_LAYOUT_REFR,
+ [LV_STYLE_PAD_BOTTOM] = LV_STYLE_PROP_EXT_DRAW | LV_STYLE_PROP_LAYOUT_REFR,
+ [LV_STYLE_PAD_LEFT] = LV_STYLE_PROP_EXT_DRAW | LV_STYLE_PROP_LAYOUT_REFR,
+ [LV_STYLE_PAD_RIGHT] = LV_STYLE_PROP_EXT_DRAW | LV_STYLE_PROP_LAYOUT_REFR,
+ [LV_STYLE_PAD_ROW] = LV_STYLE_PROP_EXT_DRAW | LV_STYLE_PROP_LAYOUT_REFR,
+ [LV_STYLE_PAD_COLUMN] = LV_STYLE_PROP_EXT_DRAW | LV_STYLE_PROP_LAYOUT_REFR,
+
+ [LV_STYLE_BG_COLOR] = 0,
+ [LV_STYLE_BG_OPA] = 0,
+ [LV_STYLE_BG_GRAD_COLOR] = 0,
+ [LV_STYLE_BG_GRAD_DIR] = 0,
+ [LV_STYLE_BG_MAIN_STOP] = 0,
+ [LV_STYLE_BG_GRAD_STOP] = 0,
+ [LV_STYLE_BG_GRAD] = 0,
+ [LV_STYLE_BG_DITHER_MODE] = 0,
+
+ [LV_STYLE_BG_IMG_SRC] = LV_STYLE_PROP_EXT_DRAW,
+ [LV_STYLE_BG_IMG_OPA] = 0,
+ [LV_STYLE_BG_IMG_RECOLOR] = 0,
+ [LV_STYLE_BG_IMG_RECOLOR_OPA] = 0,
+ [LV_STYLE_BG_IMG_TILED] = 0,
+
+ [LV_STYLE_BORDER_COLOR] = 0,
+ [LV_STYLE_BORDER_OPA] = 0,
+ [LV_STYLE_BORDER_WIDTH] = LV_STYLE_PROP_LAYOUT_REFR,
+ [LV_STYLE_BORDER_SIDE] = 0,
+ [LV_STYLE_BORDER_POST] = 0,
+
+ [LV_STYLE_OUTLINE_WIDTH] = LV_STYLE_PROP_EXT_DRAW,
+ [LV_STYLE_OUTLINE_COLOR] = 0,
+ [LV_STYLE_OUTLINE_OPA] = LV_STYLE_PROP_EXT_DRAW,
+ [LV_STYLE_OUTLINE_PAD] = LV_STYLE_PROP_EXT_DRAW,
+
+ [LV_STYLE_SHADOW_WIDTH] = LV_STYLE_PROP_EXT_DRAW,
+ [LV_STYLE_SHADOW_OFS_X] = LV_STYLE_PROP_EXT_DRAW,
+ [LV_STYLE_SHADOW_OFS_Y] = LV_STYLE_PROP_EXT_DRAW,
+ [LV_STYLE_SHADOW_SPREAD] = LV_STYLE_PROP_EXT_DRAW,
+ [LV_STYLE_SHADOW_COLOR] = 0,
+ [LV_STYLE_SHADOW_OPA] = LV_STYLE_PROP_EXT_DRAW,
+
+ [LV_STYLE_IMG_OPA] = 0,
+ [LV_STYLE_IMG_RECOLOR] = 0,
+ [LV_STYLE_IMG_RECOLOR_OPA] = 0,
+
+ [LV_STYLE_LINE_WIDTH] = LV_STYLE_PROP_EXT_DRAW,
+ [LV_STYLE_LINE_DASH_WIDTH] = 0,
+ [LV_STYLE_LINE_DASH_GAP] = 0,
+ [LV_STYLE_LINE_ROUNDED] = 0,
+ [LV_STYLE_LINE_COLOR] = 0,
+ [LV_STYLE_LINE_OPA] = 0,
+
+ [LV_STYLE_ARC_WIDTH] = LV_STYLE_PROP_EXT_DRAW,
+ [LV_STYLE_ARC_ROUNDED] = 0,
+ [LV_STYLE_ARC_COLOR] = 0,
+ [LV_STYLE_ARC_OPA] = 0,
+ [LV_STYLE_ARC_IMG_SRC] = 0,
+
+ [LV_STYLE_TEXT_COLOR] = LV_STYLE_PROP_INHERIT,
+ [LV_STYLE_TEXT_OPA] = LV_STYLE_PROP_INHERIT,
+ [LV_STYLE_TEXT_FONT] = LV_STYLE_PROP_INHERIT | LV_STYLE_PROP_LAYOUT_REFR,
+ [LV_STYLE_TEXT_LETTER_SPACE] = LV_STYLE_PROP_INHERIT | LV_STYLE_PROP_LAYOUT_REFR,
+ [LV_STYLE_TEXT_LINE_SPACE] = LV_STYLE_PROP_INHERIT | LV_STYLE_PROP_LAYOUT_REFR,
+ [LV_STYLE_TEXT_DECOR] = LV_STYLE_PROP_INHERIT,
+ [LV_STYLE_TEXT_ALIGN] = LV_STYLE_PROP_INHERIT | LV_STYLE_PROP_LAYOUT_REFR,
+
+ [LV_STYLE_RADIUS] = 0,
+ [LV_STYLE_CLIP_CORNER] = 0,
+ [LV_STYLE_OPA] = LV_STYLE_PROP_LAYER_REFR,
+ [LV_STYLE_COLOR_FILTER_DSC] = LV_STYLE_PROP_INHERIT,
+ [LV_STYLE_COLOR_FILTER_OPA] = LV_STYLE_PROP_INHERIT,
+ [LV_STYLE_ANIM_TIME] = 0,
+ [LV_STYLE_ANIM_SPEED] = 0,
+ [LV_STYLE_TRANSITION] = 0,
+ [LV_STYLE_BLEND_MODE] = LV_STYLE_PROP_LAYER_REFR,
+ [LV_STYLE_LAYOUT] = LV_STYLE_PROP_LAYOUT_REFR,
+ [LV_STYLE_BASE_DIR] = LV_STYLE_PROP_INHERIT | LV_STYLE_PROP_LAYOUT_REFR,
+};
+
+uint32_t _lv_style_custom_prop_flag_lookup_table_size = 0;
+
+/**********************
+ * STATIC VARIABLES
+ **********************/
+
+static uint16_t last_custom_prop_id = (uint16_t)_LV_STYLE_LAST_BUILT_IN_PROP;
+static const lv_style_value_t null_style_value = { .num = 0 };
+
+/**********************
+ * MACROS
+ **********************/
+
+/**********************
+ * GLOBAL FUNCTIONS
+ **********************/
+
+void lv_style_init(lv_style_t * style)
+{
+#if LV_USE_ASSERT_STYLE
+ if(style->sentinel == LV_STYLE_SENTINEL_VALUE && style->prop_cnt > 1) {
+ LV_LOG_WARN("Style might be already inited. (Potential memory leak)");
+ }
+#endif
+
+ lv_memset_00(style, sizeof(lv_style_t));
+#if LV_USE_ASSERT_STYLE
+ style->sentinel = LV_STYLE_SENTINEL_VALUE;
+#endif
+}
+
+void lv_style_reset(lv_style_t * style)
+{
+ LV_ASSERT_STYLE(style);
+
+ if(style->prop1 == LV_STYLE_PROP_ANY) {
+ LV_LOG_ERROR("Cannot reset const style");
+ return;
+ }
+
+ if(style->prop_cnt > 1) lv_mem_free(style->v_p.values_and_props);
+ lv_memset_00(style, sizeof(lv_style_t));
+#if LV_USE_ASSERT_STYLE
+ style->sentinel = LV_STYLE_SENTINEL_VALUE;
+#endif
+}
+
+lv_style_prop_t lv_style_register_prop(uint8_t flag)
+{
+ if(LV_GC_ROOT(_lv_style_custom_prop_flag_lookup_table) == NULL) {
+ _lv_style_custom_prop_flag_lookup_table_size = 0;
+ last_custom_prop_id = (uint16_t)_LV_STYLE_LAST_BUILT_IN_PROP;
+ }
+
+ if(((last_custom_prop_id + 1) & LV_STYLE_PROP_META_MASK) != 0) {
+ LV_LOG_ERROR("No more custom property IDs available");
+ return LV_STYLE_PROP_INV;
+ }
+
+ /*
+ * Allocate the lookup table if it's not yet available.
+ */
+ size_t required_size = (last_custom_prop_id + 1 - _LV_STYLE_LAST_BUILT_IN_PROP);
+ if(_lv_style_custom_prop_flag_lookup_table_size < required_size) {
+ /* Round required_size up to the nearest 32-byte value */
+ required_size = (required_size + 31) & ~31;
+ LV_ASSERT_MSG(required_size > 0, "required size has become 0?");
+ uint8_t * old_p = LV_GC_ROOT(_lv_style_custom_prop_flag_lookup_table);
+ uint8_t * new_p = lv_mem_realloc(old_p, required_size * sizeof(uint8_t));
+ if(new_p == NULL) {
+ LV_LOG_ERROR("Unable to allocate space for custom property lookup table");
+ return LV_STYLE_PROP_INV;
+ }
+ LV_GC_ROOT(_lv_style_custom_prop_flag_lookup_table) = new_p;
+ _lv_style_custom_prop_flag_lookup_table_size = required_size;
+ }
+ last_custom_prop_id++;
+ /* This should never happen - we should bail out above */
+ LV_ASSERT_NULL(LV_GC_ROOT(_lv_style_custom_prop_flag_lookup_table));
+ LV_GC_ROOT(_lv_style_custom_prop_flag_lookup_table)[last_custom_prop_id - _LV_STYLE_NUM_BUILT_IN_PROPS] = flag;
+ return last_custom_prop_id;
+}
+
+lv_style_prop_t lv_style_get_num_custom_props(void)
+{
+ return last_custom_prop_id - _LV_STYLE_LAST_BUILT_IN_PROP;
+}
+
+bool lv_style_remove_prop(lv_style_t * style, lv_style_prop_t prop)
+{
+ LV_ASSERT_STYLE(style);
+
+ if(style->prop1 == LV_STYLE_PROP_ANY) {
+ LV_LOG_ERROR("Cannot remove prop from const style");
+ return false;
+ }
+
+ if(style->prop_cnt == 0) return false;
+
+ if(style->prop_cnt == 1) {
+ if(LV_STYLE_PROP_ID_MASK(style->prop1) == prop) {
+ style->prop1 = LV_STYLE_PROP_INV;
+ style->prop_cnt = 0;
+ return true;
+ }
+ return false;
+ }
+
+ uint8_t * tmp = style->v_p.values_and_props + style->prop_cnt * sizeof(lv_style_value_t);
+ uint16_t * old_props = (uint16_t *)tmp;
+ uint32_t i;
+ for(i = 0; i < style->prop_cnt; i++) {
+ if(LV_STYLE_PROP_ID_MASK(old_props[i]) == prop) {
+ lv_style_value_t * old_values = (lv_style_value_t *)style->v_p.values_and_props;
+
+ if(style->prop_cnt == 2) {
+ style->prop_cnt = 1;
+ style->prop1 = i == 0 ? old_props[1] : old_props[0];
+ style->v_p.value1 = i == 0 ? old_values[1] : old_values[0];
+ }
+ else {
+ size_t size = (style->prop_cnt - 1) * (sizeof(lv_style_value_t) + sizeof(uint16_t));
+ uint8_t * new_values_and_props = lv_mem_alloc(size);
+ if(new_values_and_props == NULL) return false;
+ style->v_p.values_and_props = new_values_and_props;
+ style->prop_cnt--;
+
+ tmp = new_values_and_props + style->prop_cnt * sizeof(lv_style_value_t);
+ uint16_t * new_props = (uint16_t *)tmp;
+ lv_style_value_t * new_values = (lv_style_value_t *)new_values_and_props;
+
+ uint32_t j;
+ for(i = j = 0; j <= style->prop_cnt;
+ j++) { /*<=: because prop_cnt already reduced but all the old props. needs to be checked.*/
+ if(old_props[j] != prop) {
+ new_values[i] = old_values[j];
+ new_props[i++] = old_props[j];
+ }
+ }
+ }
+
+ lv_mem_free(old_values);
+ return true;
+ }
+ }
+
+ return false;
+}
+
+void lv_style_set_prop(lv_style_t * style, lv_style_prop_t prop, lv_style_value_t value)
+{
+ lv_style_set_prop_internal(style, prop, value, lv_style_set_prop_helper);
+}
+
+void lv_style_set_prop_meta(lv_style_t * style, lv_style_prop_t prop, uint16_t meta)
+{
+ lv_style_set_prop_internal(style, prop | meta, null_style_value, lv_style_set_prop_meta_helper);
+}
+
+lv_style_res_t lv_style_get_prop(const lv_style_t * style, lv_style_prop_t prop, lv_style_value_t * value)
+{
+ return lv_style_get_prop_inlined(style, prop, value);
+}
+
+void lv_style_transition_dsc_init(lv_style_transition_dsc_t * tr, const lv_style_prop_t props[],
+ lv_anim_path_cb_t path_cb, uint32_t time, uint32_t delay, void * user_data)
+{
+ lv_memset_00(tr, sizeof(lv_style_transition_dsc_t));
+ tr->props = props;
+ tr->path_xcb = path_cb == NULL ? lv_anim_path_linear : path_cb;
+ tr->time = time;
+ tr->delay = delay;
+#if LV_USE_USER_DATA
+ tr->user_data = user_data;
+#else
+ LV_UNUSED(user_data);
+#endif
+}
+
+lv_style_value_t lv_style_prop_get_default(lv_style_prop_t prop)
+{
+ lv_style_value_t value;
+ switch(prop) {
+ case LV_STYLE_TRANSFORM_ZOOM:
+ value.num = LV_IMG_ZOOM_NONE;
+ break;
+ case LV_STYLE_BG_COLOR:
+ value.color = lv_color_white();
+ break;
+ case LV_STYLE_BG_GRAD_COLOR:
+ case LV_STYLE_BORDER_COLOR:
+ case LV_STYLE_SHADOW_COLOR:
+ case LV_STYLE_OUTLINE_COLOR:
+ case LV_STYLE_ARC_COLOR:
+ case LV_STYLE_LINE_COLOR:
+ case LV_STYLE_TEXT_COLOR:
+ case LV_STYLE_IMG_RECOLOR:
+ value.color = lv_color_black();
+ break;
+ case LV_STYLE_OPA:
+ case LV_STYLE_BORDER_OPA:
+ case LV_STYLE_TEXT_OPA:
+ case LV_STYLE_IMG_OPA:
+ case LV_STYLE_BG_IMG_OPA:
+ case LV_STYLE_OUTLINE_OPA:
+ case LV_STYLE_SHADOW_OPA:
+ case LV_STYLE_LINE_OPA:
+ case LV_STYLE_ARC_OPA:
+ value.num = LV_OPA_COVER;
+ break;
+ case LV_STYLE_BG_GRAD_STOP:
+ value.num = 255;
+ break;
+ case LV_STYLE_BORDER_SIDE:
+ value.num = LV_BORDER_SIDE_FULL;
+ break;
+ case LV_STYLE_TEXT_FONT:
+ value.ptr = LV_FONT_DEFAULT;
+ break;
+ case LV_STYLE_MAX_WIDTH:
+ case LV_STYLE_MAX_HEIGHT:
+ value.num = LV_COORD_MAX;
+ break;
+ default:
+ value.ptr = NULL;
+ value.num = 0;
+ break;
+ }
+
+ return value;
+}
+
+bool lv_style_is_empty(const lv_style_t * style)
+{
+ LV_ASSERT_STYLE(style);
+
+ return style->prop_cnt == 0 ? true : false;
+}
+
+uint8_t _lv_style_get_prop_group(lv_style_prop_t prop)
+{
+ uint16_t group = (prop & 0x1FF) >> 4;
+ if(group > 7) group = 7; /*The MSB marks all the custom properties*/
+ return (uint8_t)group;
+}
+
+uint8_t _lv_style_prop_lookup_flags(lv_style_prop_t prop)
+{
+ extern const uint8_t _lv_style_builtin_prop_flag_lookup_table[];
+ extern uint32_t _lv_style_custom_prop_flag_lookup_table_size;
+ if(prop == LV_STYLE_PROP_ANY) return LV_STYLE_PROP_ALL; /*Any prop can have any flags*/
+ if(prop == LV_STYLE_PROP_INV) return 0;
+
+ if(prop < _LV_STYLE_NUM_BUILT_IN_PROPS)
+ return _lv_style_builtin_prop_flag_lookup_table[prop];
+ prop -= _LV_STYLE_NUM_BUILT_IN_PROPS;
+ if(LV_GC_ROOT(_lv_style_custom_prop_flag_lookup_table) != NULL && prop < _lv_style_custom_prop_flag_lookup_table_size)
+ return LV_GC_ROOT(_lv_style_custom_prop_flag_lookup_table)[prop];
+ return 0;
+}
+
+/**********************
+ * STATIC FUNCTIONS
+ **********************/
+
+static void lv_style_set_prop_helper(lv_style_prop_t prop, lv_style_value_t value, uint16_t * prop_storage,
+ lv_style_value_t * value_storage)
+{
+ *prop_storage = prop;
+ *value_storage = value;
+}
+
+static void lv_style_set_prop_meta_helper(lv_style_prop_t prop, lv_style_value_t value, uint16_t * prop_storage,
+ lv_style_value_t * value_storage)
+{
+ LV_UNUSED(value);
+ LV_UNUSED(value_storage);
+ *prop_storage = prop; /* meta is OR-ed into the prop ID already */
+}
+
+static void lv_style_set_prop_internal(lv_style_t * style, lv_style_prop_t prop_and_meta, lv_style_value_t value,
+ void (*value_adjustment_helper)(lv_style_prop_t, lv_style_value_t, uint16_t *, lv_style_value_t *))
+{
+ LV_ASSERT_STYLE(style);
+
+ if(style->prop1 == LV_STYLE_PROP_ANY) {
+ LV_LOG_ERROR("Cannot set property of constant style");
+ return;
+ }
+
+ lv_style_prop_t prop_id = LV_STYLE_PROP_ID_MASK(prop_and_meta);
+
+ if(style->prop_cnt > 1) {
+ uint8_t * tmp = style->v_p.values_and_props + style->prop_cnt * sizeof(lv_style_value_t);
+ uint16_t * props = (uint16_t *)tmp;
+ int32_t i;
+ for(i = style->prop_cnt - 1; i >= 0; i--) {
+ if(LV_STYLE_PROP_ID_MASK(props[i]) == prop_id) {
+ lv_style_value_t * values = (lv_style_value_t *)style->v_p.values_and_props;
+ value_adjustment_helper(prop_and_meta, value, &props[i], &values[i]);
+ return;
+ }
+ }
+
+ size_t size = (style->prop_cnt + 1) * (sizeof(lv_style_value_t) + sizeof(uint16_t));
+ uint8_t * values_and_props = lv_mem_realloc(style->v_p.values_and_props, size);
+ if(values_and_props == NULL) return;
+ style->v_p.values_and_props = values_and_props;
+
+ tmp = values_and_props + style->prop_cnt * sizeof(lv_style_value_t);
+ props = (uint16_t *)tmp;
+ /*Shift all props to make place for the value before them*/
+ for(i = style->prop_cnt - 1; i >= 0; i--) {
+ props[i + sizeof(lv_style_value_t) / sizeof(uint16_t)] = props[i];
+ }
+ style->prop_cnt++;
+
+ /*Go to the new position wit the props*/
+ tmp = values_and_props + style->prop_cnt * sizeof(lv_style_value_t);
+ props = (uint16_t *)tmp;
+ lv_style_value_t * values = (lv_style_value_t *)values_and_props;
+
+ /*Set the new property and value*/
+ value_adjustment_helper(prop_and_meta, value, &props[style->prop_cnt - 1], &values[style->prop_cnt - 1]);
+ }
+ else if(style->prop_cnt == 1) {
+ if(LV_STYLE_PROP_ID_MASK(style->prop1) == prop_id) {
+ value_adjustment_helper(prop_and_meta, value, &style->prop1, &style->v_p.value1);
+ return;
+ }
+ size_t size = (style->prop_cnt + 1) * (sizeof(lv_style_value_t) + sizeof(uint16_t));
+ uint8_t * values_and_props = lv_mem_alloc(size);
+ if(values_and_props == NULL) return;
+ lv_style_value_t value_tmp = style->v_p.value1;
+ style->v_p.values_and_props = values_and_props;
+ style->prop_cnt++;
+
+ uint8_t * tmp = values_and_props + style->prop_cnt * sizeof(lv_style_value_t);
+ uint16_t * props = (uint16_t *)tmp;
+ lv_style_value_t * values = (lv_style_value_t *)values_and_props;
+ props[0] = style->prop1;
+ values[0] = value_tmp;
+ value_adjustment_helper(prop_and_meta, value, &props[1], &values[1]);
+ }
+ else {
+ style->prop_cnt = 1;
+ value_adjustment_helper(prop_and_meta, value, &style->prop1, &style->v_p.value1);
+ }
+
+ uint8_t group = _lv_style_get_prop_group(prop_id);
+ style->has_group |= 1 << group;
+}
+
diff --git a/lib/lvgl/src/misc/lv_style.h b/lib/lvgl/src/misc/lv_style.h
new file mode 100644
index 00000000..1792dae8
--- /dev/null
+++ b/lib/lvgl/src/misc/lv_style.h
@@ -0,0 +1,597 @@
+/**
+ * @file lv_style.h
+ *
+ */
+
+#ifndef LV_STYLE_H
+#define LV_STYLE_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/*********************
+ * INCLUDES
+ *********************/
+#include <stdbool.h>
+#include <stdint.h>
+#include "../font/lv_font.h"
+#include "lv_color.h"
+#include "lv_area.h"
+#include "lv_anim.h"
+#include "lv_txt.h"
+#include "lv_types.h"
+#include "lv_assert.h"
+#include "lv_bidi.h"
+
+/*********************
+ * DEFINES
+ *********************/
+
+#define LV_STYLE_SENTINEL_VALUE 0xAABBCCDD
+
+/**
+ * Flags for style behavior
+ *
+ * The rest of the flags will have _FLAG added to their name in v9.
+ */
+#define LV_STYLE_PROP_FLAG_NONE (0)
+#define LV_STYLE_PROP_INHERIT (1 << 0) /*Inherited*/
+#define LV_STYLE_PROP_EXT_DRAW (1 << 1) /*Requires ext. draw size update when changed*/
+#define LV_STYLE_PROP_LAYOUT_REFR (1 << 2) /*Requires layout update when changed*/
+#define LV_STYLE_PROP_PARENT_LAYOUT_REFR (1 << 3) /*Requires layout update on parent when changed*/
+#define LV_STYLE_PROP_LAYER_REFR (1 << 4) /*Affects layer handling*/
+#define LV_STYLE_PROP_ALL (0x1F) /*Indicating all flags*/
+
+/**
+ * Other constants
+ */
+#define LV_IMG_ZOOM_NONE 256 /*Value for not zooming the image*/
+LV_EXPORT_CONST_INT(LV_IMG_ZOOM_NONE);
+
+// *INDENT-OFF*
+#if LV_USE_ASSERT_STYLE
+#define LV_STYLE_CONST_INIT(var_name, prop_array) \
+ const lv_style_t var_name = { \
+ .sentinel = LV_STYLE_SENTINEL_VALUE, \
+ .v_p = { .const_props = prop_array }, \
+ .has_group = 0xFF, \
+ .prop1 = LV_STYLE_PROP_ANY, \
+ .prop_cnt = (sizeof(prop_array) / sizeof((prop_array)[0])), \
+ }
+#else
+#define LV_STYLE_CONST_INIT(var_name, prop_array) \
+ const lv_style_t var_name = { \
+ .v_p = { .const_props = prop_array }, \
+ .has_group = 0xFF, \
+ .prop1 = LV_STYLE_PROP_ANY, \
+ .prop_cnt = (sizeof(prop_array) / sizeof((prop_array)[0])), \
+ }
+#endif
+// *INDENT-ON*
+
+/** On simple system, don't waste resources on gradients */
+#if !defined(LV_DRAW_COMPLEX) || !defined(LV_GRADIENT_MAX_STOPS)
+#define LV_GRADIENT_MAX_STOPS 2
+#endif
+
+#define LV_STYLE_PROP_META_INHERIT 0x8000
+#define LV_STYLE_PROP_META_INITIAL 0x4000
+#define LV_STYLE_PROP_META_MASK (LV_STYLE_PROP_META_INHERIT | LV_STYLE_PROP_META_INITIAL)
+
+#define LV_STYLE_PROP_ID_MASK(prop) ((lv_style_prop_t)((prop) & ~LV_STYLE_PROP_META_MASK))
+
+/**********************
+ * TYPEDEFS
+ **********************/
+
+/**
+ * Possible options how to blend opaque drawings
+ */
+enum {
+ LV_BLEND_MODE_NORMAL, /**< Simply mix according to the opacity value*/
+ LV_BLEND_MODE_ADDITIVE, /**< Add the respective color channels*/
+ LV_BLEND_MODE_SUBTRACTIVE,/**< Subtract the foreground from the background*/
+ LV_BLEND_MODE_MULTIPLY, /**< Multiply the foreground and background*/
+ LV_BLEND_MODE_REPLACE, /**< Replace background with foreground in the area*/
+};
+
+typedef uint8_t lv_blend_mode_t;
+
+/**
+ * Some options to apply decorations on texts.
+ * 'OR'ed values can be used.
+ */
+enum {
+ LV_TEXT_DECOR_NONE = 0x00,
+ LV_TEXT_DECOR_UNDERLINE = 0x01,
+ LV_TEXT_DECOR_STRIKETHROUGH = 0x02,
+};
+
+typedef uint8_t lv_text_decor_t;
+
+/**
+ * Selects on which sides border should be drawn
+ * 'OR'ed values can be used.
+ */
+enum {
+ LV_BORDER_SIDE_NONE = 0x00,
+ LV_BORDER_SIDE_BOTTOM = 0x01,
+ LV_BORDER_SIDE_TOP = 0x02,
+ LV_BORDER_SIDE_LEFT = 0x04,
+ LV_BORDER_SIDE_RIGHT = 0x08,
+ LV_BORDER_SIDE_FULL = 0x0F,
+ LV_BORDER_SIDE_INTERNAL = 0x10, /**< FOR matrix-like objects (e.g. Button matrix)*/
+};
+typedef uint8_t lv_border_side_t;
+
+/**
+ * The direction of the gradient.
+ */
+enum {
+ LV_GRAD_DIR_NONE, /**< No gradient (the `grad_color` property is ignored)*/
+ LV_GRAD_DIR_VER, /**< Vertical (top to bottom) gradient*/
+ LV_GRAD_DIR_HOR, /**< Horizontal (left to right) gradient*/
+};
+
+typedef uint8_t lv_grad_dir_t;
+
+/**
+ * The dithering algorithm for the gradient
+ * Depends on LV_DITHER_GRADIENT
+ */
+enum {
+ LV_DITHER_NONE, /**< No dithering, colors are just quantized to the output resolution*/
+ LV_DITHER_ORDERED, /**< Ordered dithering. Faster to compute and use less memory but lower quality*/
+ LV_DITHER_ERR_DIFF, /**< Error diffusion mode. Slower to compute and use more memory but give highest dither quality*/
+};
+
+typedef uint8_t lv_dither_mode_t;
+
+/** A gradient stop definition.
+ * This matches a color and a position in a virtual 0-255 scale.
+ */
+typedef struct {
+ lv_color_t color; /**< The stop color */
+ uint8_t frac; /**< The stop position in 1/255 unit */
+} lv_gradient_stop_t;
+
+/** A descriptor of a gradient. */
+typedef struct {
+ lv_gradient_stop_t stops[LV_GRADIENT_MAX_STOPS]; /**< A gradient stop array */
+ uint8_t stops_count; /**< The number of used stops in the array */
+ lv_grad_dir_t dir : 3; /**< The gradient direction.
+ * Any of LV_GRAD_DIR_HOR, LV_GRAD_DIR_VER, LV_GRAD_DIR_NONE */
+ lv_dither_mode_t dither : 3; /**< Whether to dither the gradient or not.
+ * Any of LV_DITHER_NONE, LV_DITHER_ORDERED, LV_DITHER_ERR_DIFF */
+} lv_grad_dsc_t;
+
+/**
+ * A common type to handle all the property types in the same way.
+ */
+typedef union {
+ int32_t num; /**< Number integer number (opacity, enums, booleans or "normal" numbers)*/
+ const void * ptr; /**< Constant pointers (font, cone text, etc)*/
+ lv_color_t color; /**< Colors*/
+} lv_style_value_t;
+
+/**
+ * Enumeration of all built in style properties
+ *
+ * Props are split into groups of 16. When adding a new prop to a group, ensure it does not overflow into the next one.
+ */
+typedef enum {
+ LV_STYLE_PROP_INV = 0,
+
+ /*Group 0*/
+ LV_STYLE_WIDTH = 1,
+ LV_STYLE_MIN_WIDTH = 2,
+ LV_STYLE_MAX_WIDTH = 3,
+ LV_STYLE_HEIGHT = 4,
+ LV_STYLE_MIN_HEIGHT = 5,
+ LV_STYLE_MAX_HEIGHT = 6,
+ LV_STYLE_X = 7,
+ LV_STYLE_Y = 8,
+ LV_STYLE_ALIGN = 9,
+ LV_STYLE_LAYOUT = 10,
+ LV_STYLE_RADIUS = 11,
+
+ /*Group 1*/
+ LV_STYLE_PAD_TOP = 16,
+ LV_STYLE_PAD_BOTTOM = 17,
+ LV_STYLE_PAD_LEFT = 18,
+ LV_STYLE_PAD_RIGHT = 19,
+ LV_STYLE_PAD_ROW = 20,
+ LV_STYLE_PAD_COLUMN = 21,
+ LV_STYLE_BASE_DIR = 22,
+ LV_STYLE_CLIP_CORNER = 23,
+
+ /*Group 2*/
+ LV_STYLE_BG_COLOR = 32,
+ LV_STYLE_BG_OPA = 33,
+ LV_STYLE_BG_GRAD_COLOR = 34,
+ LV_STYLE_BG_GRAD_DIR = 35,
+ LV_STYLE_BG_MAIN_STOP = 36,
+ LV_STYLE_BG_GRAD_STOP = 37,
+ LV_STYLE_BG_GRAD = 38,
+ LV_STYLE_BG_DITHER_MODE = 39,
+ LV_STYLE_BG_IMG_SRC = 40,
+ LV_STYLE_BG_IMG_OPA = 41,
+ LV_STYLE_BG_IMG_RECOLOR = 42,
+ LV_STYLE_BG_IMG_RECOLOR_OPA = 43,
+ LV_STYLE_BG_IMG_TILED = 44,
+
+ /*Group 3*/
+ LV_STYLE_BORDER_COLOR = 48,
+ LV_STYLE_BORDER_OPA = 49,
+ LV_STYLE_BORDER_WIDTH = 50,
+ LV_STYLE_BORDER_SIDE = 51,
+ LV_STYLE_BORDER_POST = 52,
+ LV_STYLE_OUTLINE_WIDTH = 53,
+ LV_STYLE_OUTLINE_COLOR = 54,
+ LV_STYLE_OUTLINE_OPA = 55,
+ LV_STYLE_OUTLINE_PAD = 56,
+
+ /*Group 4*/
+ LV_STYLE_SHADOW_WIDTH = 64,
+ LV_STYLE_SHADOW_OFS_X = 65,
+ LV_STYLE_SHADOW_OFS_Y = 66,
+ LV_STYLE_SHADOW_SPREAD = 67,
+ LV_STYLE_SHADOW_COLOR = 68,
+ LV_STYLE_SHADOW_OPA = 69,
+ LV_STYLE_IMG_OPA = 70,
+ LV_STYLE_IMG_RECOLOR = 71,
+ LV_STYLE_IMG_RECOLOR_OPA = 72,
+ LV_STYLE_LINE_WIDTH = 73,
+ LV_STYLE_LINE_DASH_WIDTH = 74,
+ LV_STYLE_LINE_DASH_GAP = 75,
+ LV_STYLE_LINE_ROUNDED = 76,
+ LV_STYLE_LINE_COLOR = 77,
+ LV_STYLE_LINE_OPA = 78,
+
+ /*Group 5*/
+ LV_STYLE_ARC_WIDTH = 80,
+ LV_STYLE_ARC_ROUNDED = 81,
+ LV_STYLE_ARC_COLOR = 82,
+ LV_STYLE_ARC_OPA = 83,
+ LV_STYLE_ARC_IMG_SRC = 84,
+ LV_STYLE_TEXT_COLOR = 85,
+ LV_STYLE_TEXT_OPA = 86,
+ LV_STYLE_TEXT_FONT = 87,
+ LV_STYLE_TEXT_LETTER_SPACE = 88,
+ LV_STYLE_TEXT_LINE_SPACE = 89,
+ LV_STYLE_TEXT_DECOR = 90,
+ LV_STYLE_TEXT_ALIGN = 91,
+
+ /*Group 6*/
+ LV_STYLE_OPA = 96,
+ LV_STYLE_COLOR_FILTER_DSC = 97,
+ LV_STYLE_COLOR_FILTER_OPA = 98,
+ LV_STYLE_ANIM = 99,
+ LV_STYLE_ANIM_TIME = 100,
+ LV_STYLE_ANIM_SPEED = 101,
+ LV_STYLE_TRANSITION = 102,
+ LV_STYLE_BLEND_MODE = 103,
+ LV_STYLE_TRANSFORM_WIDTH = 104,
+ LV_STYLE_TRANSFORM_HEIGHT = 105,
+ LV_STYLE_TRANSLATE_X = 106,
+ LV_STYLE_TRANSLATE_Y = 107,
+ LV_STYLE_TRANSFORM_ZOOM = 108,
+ LV_STYLE_TRANSFORM_ANGLE = 109,
+ LV_STYLE_TRANSFORM_PIVOT_X = 110,
+ LV_STYLE_TRANSFORM_PIVOT_Y = 111,
+
+ _LV_STYLE_LAST_BUILT_IN_PROP = 111,
+ _LV_STYLE_NUM_BUILT_IN_PROPS = _LV_STYLE_LAST_BUILT_IN_PROP + 1,
+
+ LV_STYLE_PROP_ANY = 0xFFFF,
+ _LV_STYLE_PROP_CONST = 0xFFFF /* magic value for const styles */
+} lv_style_prop_t;
+
+enum {
+ LV_STYLE_RES_NOT_FOUND,
+ LV_STYLE_RES_FOUND,
+ LV_STYLE_RES_INHERIT
+};
+
+typedef uint8_t lv_style_res_t;
+
+/**
+ * Descriptor for style transitions
+ */
+typedef struct {
+ const lv_style_prop_t * props; /**< An array with the properties to animate.*/
+#if LV_USE_USER_DATA
+ void * user_data; /**< A custom user data that will be passed to the animation's user_data */
+#endif
+ lv_anim_path_cb_t path_xcb; /**< A path for the animation.*/
+ uint32_t time; /**< Duration of the transition in [ms]*/
+ uint32_t delay; /**< Delay before the transition in [ms]*/
+} lv_style_transition_dsc_t;
+
+/**
+ * Descriptor of a constant style property.
+ */
+typedef struct {
+ lv_style_prop_t prop;
+ lv_style_value_t value;
+} lv_style_const_prop_t;
+
+/**
+ * Descriptor of a style (a collection of properties and values).
+ */
+typedef struct {
+
+#if LV_USE_ASSERT_STYLE
+ uint32_t sentinel;
+#endif
+
+ /*If there is only one property store it directly.
+ *For more properties allocate an array*/
+ union {
+ lv_style_value_t value1;
+ uint8_t * values_and_props;
+ const lv_style_const_prop_t * const_props;
+ } v_p;
+
+ uint16_t prop1;
+ uint8_t has_group;
+ uint8_t prop_cnt;
+} lv_style_t;
+
+/**********************
+ * GLOBAL PROTOTYPES
+ **********************/
+
+
+/**
+ * Initialize a style
+ * @param style pointer to a style to initialize
+ * @note Do not call `lv_style_init` on styles that already have some properties
+ * because this function won't free the used memory, just sets a default state for the style.
+ * In other words be sure to initialize styles only once!
+ */
+void lv_style_init(lv_style_t * style);
+
+/**
+ * Clear all properties from a style and free all allocated memories.
+ * @param style pointer to a style
+ */
+void lv_style_reset(lv_style_t * style);
+
+/**
+ * Register a new style property for custom usage
+ * @return a new property ID, or LV_STYLE_PROP_INV if there are no more available.
+ * @example
+ * lv_style_prop_t MY_PROP;
+ * static inline void lv_style_set_my_prop(lv_style_t * style, lv_color_t value) {
+ * lv_style_value_t v = {.color = value}; lv_style_set_prop(style, MY_PROP, v); }
+ *
+ * ...
+ * MY_PROP = lv_style_register_prop();
+ * ...
+ * lv_style_set_my_prop(&style1, lv_palette_main(LV_PALETTE_RED));
+ */
+lv_style_prop_t lv_style_register_prop(uint8_t flag);
+
+/**
+ * Get the number of custom properties that have been registered thus far.
+ */
+lv_style_prop_t lv_style_get_num_custom_props(void);
+
+/**
+ * Remove a property from a style
+ * @param style pointer to a style
+ * @param prop a style property ORed with a state.
+ * @return true: the property was found and removed; false: the property wasn't found
+ */
+bool lv_style_remove_prop(lv_style_t * style, lv_style_prop_t prop);
+
+/**
+ * Set the value of property in a style.
+ * This function shouldn't be used directly by the user.
+ * Instead use `lv_style_set_<prop_name>()`. E.g. `lv_style_set_bg_color()`
+ * @param style pointer to style
+ * @param prop the ID of a property (e.g. `LV_STYLE_BG_COLOR`)
+ * @param value `lv_style_value_t` variable in which a field is set according to the type of `prop`
+ */
+void lv_style_set_prop(lv_style_t * style, lv_style_prop_t prop, lv_style_value_t value);
+
+/**
+ * Set a special meta state for a property in a style.
+ * This function shouldn't be used directly by the user.
+ * @param style pointer to style
+ * @param prop the ID of a property (e.g. `LV_STYLE_BG_COLOR`)
+ * @param meta the meta value to attach to the property in the style
+ */
+void lv_style_set_prop_meta(lv_style_t * style, lv_style_prop_t prop, uint16_t meta);
+
+/**
+ * Get the value of a property
+ * @param style pointer to a style
+ * @param prop the ID of a property
+ * @param value pointer to a `lv_style_value_t` variable to store the value
+ * @return LV_RES_INV: the property wasn't found in the style (`value` is unchanged)
+ * LV_RES_OK: the property was fond, and `value` is set accordingly
+ * @note For performance reasons there are no sanity check on `style`
+ */
+lv_style_res_t lv_style_get_prop(const lv_style_t * style, lv_style_prop_t prop, lv_style_value_t * value);
+
+/**
+ * Initialize a transition descriptor.
+ * @param tr pointer to a transition descriptor to initialize
+ * @param props an array with the properties to transition. The last element must be zero.
+ * @param path_cb an animation path (ease) callback. If `NULL` liner path will be used.
+ * @param time duration of the transition in [ms]
+ * @param delay delay before the transition in [ms]
+ * @param user_data any custom data that will be saved in the transition animation and will be available when `path_cb` is called
+ * @example
+ * const static lv_style_prop_t trans_props[] = { LV_STYLE_BG_OPA, LV_STYLE_BG_COLOR, 0 };
+ * static lv_style_transition_dsc_t trans1;
+ * lv_style_transition_dsc_init(&trans1, trans_props, NULL, 300, 0, NULL);
+ */
+void lv_style_transition_dsc_init(lv_style_transition_dsc_t * tr, const lv_style_prop_t props[],
+ lv_anim_path_cb_t path_cb, uint32_t time, uint32_t delay, void * user_data);
+
+/**
+ * Get the default value of a property
+ * @param prop the ID of a property
+ * @return the default value
+ */
+lv_style_value_t lv_style_prop_get_default(lv_style_prop_t prop);
+
+/**
+ * Get the value of a property
+ * @param style pointer to a style
+ * @param prop the ID of a property
+ * @param value pointer to a `lv_style_value_t` variable to store the value
+ * @return LV_RES_INV: the property wasn't found in the style (`value` is unchanged)
+ * LV_RES_OK: the property was fond, and `value` is set accordingly
+ * @note For performance reasons there are no sanity check on `style`
+ * @note This function is the same as ::lv_style_get_prop but inlined. Use it only on performance critical places
+ */
+static inline lv_style_res_t lv_style_get_prop_inlined(const lv_style_t * style, lv_style_prop_t prop,
+ lv_style_value_t * value)
+{
+ if(style->prop1 == LV_STYLE_PROP_ANY) {
+ const lv_style_const_prop_t * const_prop;
+ uint32_t i;
+ for(i = 0; i < style->prop_cnt; i++) {
+ const_prop = style->v_p.const_props + i;
+ lv_style_prop_t prop_id = LV_STYLE_PROP_ID_MASK(const_prop->prop);
+ if(prop_id == prop) {
+ if(const_prop->prop & LV_STYLE_PROP_META_INHERIT)
+ return LV_STYLE_RES_INHERIT;
+ *value = (const_prop->prop & LV_STYLE_PROP_META_INITIAL) ? lv_style_prop_get_default(prop_id) : const_prop->value;
+ return LV_STYLE_RES_FOUND;
+ }
+ }
+ return LV_STYLE_RES_NOT_FOUND;
+ }
+
+ if(style->prop_cnt == 0) return LV_STYLE_RES_NOT_FOUND;
+
+ if(style->prop_cnt > 1) {
+ uint8_t * tmp = style->v_p.values_and_props + style->prop_cnt * sizeof(lv_style_value_t);
+ uint16_t * props = (uint16_t *)tmp;
+ uint32_t i;
+ for(i = 0; i < style->prop_cnt; i++) {
+ lv_style_prop_t prop_id = LV_STYLE_PROP_ID_MASK(props[i]);
+ if(prop_id == prop) {
+ if(props[i] & LV_STYLE_PROP_META_INHERIT)
+ return LV_STYLE_RES_INHERIT;
+ if(props[i] & LV_STYLE_PROP_META_INITIAL)
+ *value = lv_style_prop_get_default(prop_id);
+ else {
+ lv_style_value_t * values = (lv_style_value_t *)style->v_p.values_and_props;
+ *value = values[i];
+ }
+ return LV_STYLE_RES_FOUND;
+ }
+ }
+ }
+ else if(LV_STYLE_PROP_ID_MASK(style->prop1) == prop) {
+ if(style->prop1 & LV_STYLE_PROP_META_INHERIT)
+ return LV_STYLE_RES_INHERIT;
+ *value = (style->prop1 & LV_STYLE_PROP_META_INITIAL) ? lv_style_prop_get_default(LV_STYLE_PROP_ID_MASK(
+ style->prop1)) : style->v_p.value1;
+ return LV_STYLE_RES_FOUND;
+ }
+ return LV_STYLE_RES_NOT_FOUND;
+}
+
+/**
+ * Checks if a style is empty (has no properties)
+ * @param style pointer to a style
+ * @return true if the style is empty
+ */
+bool lv_style_is_empty(const lv_style_t * style);
+
+/**
+ * Tell the group of a property. If the a property from a group is set in a style the (1 << group) bit of style->has_group is set.
+ * It allows early skipping the style if the property is not exists in the style at all.
+ * @param prop a style property
+ * @return the group [0..7] 7 means all the custom properties with index > 112
+ */
+uint8_t _lv_style_get_prop_group(lv_style_prop_t prop);
+
+/**
+ * Get the flags of a built-in or custom property.
+ *
+ * @param prop a style property
+ * @return the flags of the property
+ */
+uint8_t _lv_style_prop_lookup_flags(lv_style_prop_t prop);
+
+#include "lv_style_gen.h"
+
+static inline void lv_style_set_size(lv_style_t * style, lv_coord_t value)
+{
+ lv_style_set_width(style, value);
+ lv_style_set_height(style, value);
+}
+
+static inline void lv_style_set_pad_all(lv_style_t * style, lv_coord_t value)
+{
+ lv_style_set_pad_left(style, value);
+ lv_style_set_pad_right(style, value);
+ lv_style_set_pad_top(style, value);
+ lv_style_set_pad_bottom(style, value);
+}
+
+static inline void lv_style_set_pad_hor(lv_style_t * style, lv_coord_t value)
+{
+ lv_style_set_pad_left(style, value);
+ lv_style_set_pad_right(style, value);
+}
+
+static inline void lv_style_set_pad_ver(lv_style_t * style, lv_coord_t value)
+{
+ lv_style_set_pad_top(style, value);
+ lv_style_set_pad_bottom(style, value);
+}
+
+static inline void lv_style_set_pad_gap(lv_style_t * style, lv_coord_t value)
+{
+ lv_style_set_pad_row(style, value);
+ lv_style_set_pad_column(style, value);
+}
+
+/**
+ * @brief Check if the style property has a specified behavioral flag.
+ *
+ * Do not pass multiple flags to this function as backwards-compatibility is not guaranteed
+ * for that.
+ *
+ * @param prop Property ID
+ * @param flag Flag
+ * @return true if the flag is set for this property
+ */
+static inline bool lv_style_prop_has_flag(lv_style_prop_t prop, uint8_t flag)
+{
+ return _lv_style_prop_lookup_flags(prop) & flag;
+}
+
+/*************************
+ * GLOBAL VARIABLES
+ *************************/
+
+/**********************
+ * MACROS
+ **********************/
+
+#if LV_USE_ASSERT_STYLE
+# define LV_ASSERT_STYLE(style_p) \
+ do { \
+ LV_ASSERT_MSG(style_p != NULL, "The style is NULL"); \
+ LV_ASSERT_MSG(style_p->sentinel == LV_STYLE_SENTINEL_VALUE, "Style is not initialized or corrupted"); \
+ } while(0)
+#else
+# define LV_ASSERT_STYLE(p) do{}while(0)
+#endif
+
+#ifdef __cplusplus
+} /*extern "C"*/
+#endif
+
+#endif /*LV_STYLE_H*/
diff --git a/lib/lvgl/src/misc/lv_style_gen.c b/lib/lvgl/src/misc/lv_style_gen.c
new file mode 100644
index 00000000..13d85607
--- /dev/null
+++ b/lib/lvgl/src/misc/lv_style_gen.c
@@ -0,0 +1,673 @@
+#include "lv_style.h"
+
+void lv_style_set_width(lv_style_t * style, lv_coord_t value)
+{
+ lv_style_value_t v = {
+ .num = (int32_t)value
+ };
+ lv_style_set_prop(style, LV_STYLE_WIDTH, v);
+}
+
+void lv_style_set_min_width(lv_style_t * style, lv_coord_t value)
+{
+ lv_style_value_t v = {
+ .num = (int32_t)value
+ };
+ lv_style_set_prop(style, LV_STYLE_MIN_WIDTH, v);
+}
+
+void lv_style_set_max_width(lv_style_t * style, lv_coord_t value)
+{
+ lv_style_value_t v = {
+ .num = (int32_t)value
+ };
+ lv_style_set_prop(style, LV_STYLE_MAX_WIDTH, v);
+}
+
+void lv_style_set_height(lv_style_t * style, lv_coord_t value)
+{
+ lv_style_value_t v = {
+ .num = (int32_t)value
+ };
+ lv_style_set_prop(style, LV_STYLE_HEIGHT, v);
+}
+
+void lv_style_set_min_height(lv_style_t * style, lv_coord_t value)
+{
+ lv_style_value_t v = {
+ .num = (int32_t)value
+ };
+ lv_style_set_prop(style, LV_STYLE_MIN_HEIGHT, v);
+}
+
+void lv_style_set_max_height(lv_style_t * style, lv_coord_t value)
+{
+ lv_style_value_t v = {
+ .num = (int32_t)value
+ };
+ lv_style_set_prop(style, LV_STYLE_MAX_HEIGHT, v);
+}
+
+void lv_style_set_x(lv_style_t * style, lv_coord_t value)
+{
+ lv_style_value_t v = {
+ .num = (int32_t)value
+ };
+ lv_style_set_prop(style, LV_STYLE_X, v);
+}
+
+void lv_style_set_y(lv_style_t * style, lv_coord_t value)
+{
+ lv_style_value_t v = {
+ .num = (int32_t)value
+ };
+ lv_style_set_prop(style, LV_STYLE_Y, v);
+}
+
+void lv_style_set_align(lv_style_t * style, lv_align_t value)
+{
+ lv_style_value_t v = {
+ .num = (int32_t)value
+ };
+ lv_style_set_prop(style, LV_STYLE_ALIGN, v);
+}
+
+void lv_style_set_transform_width(lv_style_t * style, lv_coord_t value)
+{
+ lv_style_value_t v = {
+ .num = (int32_t)value
+ };
+ lv_style_set_prop(style, LV_STYLE_TRANSFORM_WIDTH, v);
+}
+
+void lv_style_set_transform_height(lv_style_t * style, lv_coord_t value)
+{
+ lv_style_value_t v = {
+ .num = (int32_t)value
+ };
+ lv_style_set_prop(style, LV_STYLE_TRANSFORM_HEIGHT, v);
+}
+
+void lv_style_set_translate_x(lv_style_t * style, lv_coord_t value)
+{
+ lv_style_value_t v = {
+ .num = (int32_t)value
+ };
+ lv_style_set_prop(style, LV_STYLE_TRANSLATE_X, v);
+}
+
+void lv_style_set_translate_y(lv_style_t * style, lv_coord_t value)
+{
+ lv_style_value_t v = {
+ .num = (int32_t)value
+ };
+ lv_style_set_prop(style, LV_STYLE_TRANSLATE_Y, v);
+}
+
+void lv_style_set_transform_zoom(lv_style_t * style, lv_coord_t value)
+{
+ lv_style_value_t v = {
+ .num = (int32_t)value
+ };
+ lv_style_set_prop(style, LV_STYLE_TRANSFORM_ZOOM, v);
+}
+
+void lv_style_set_transform_angle(lv_style_t * style, lv_coord_t value)
+{
+ lv_style_value_t v = {
+ .num = (int32_t)value
+ };
+ lv_style_set_prop(style, LV_STYLE_TRANSFORM_ANGLE, v);
+}
+
+void lv_style_set_transform_pivot_x(lv_style_t * style, lv_coord_t value)
+{
+ lv_style_value_t v = {
+ .num = (int32_t)value
+ };
+ lv_style_set_prop(style, LV_STYLE_TRANSFORM_PIVOT_X, v);
+}
+
+void lv_style_set_transform_pivot_y(lv_style_t * style, lv_coord_t value)
+{
+ lv_style_value_t v = {
+ .num = (int32_t)value
+ };
+ lv_style_set_prop(style, LV_STYLE_TRANSFORM_PIVOT_Y, v);
+}
+
+void lv_style_set_pad_top(lv_style_t * style, lv_coord_t value)
+{
+ lv_style_value_t v = {
+ .num = (int32_t)value
+ };
+ lv_style_set_prop(style, LV_STYLE_PAD_TOP, v);
+}
+
+void lv_style_set_pad_bottom(lv_style_t * style, lv_coord_t value)
+{
+ lv_style_value_t v = {
+ .num = (int32_t)value
+ };
+ lv_style_set_prop(style, LV_STYLE_PAD_BOTTOM, v);
+}
+
+void lv_style_set_pad_left(lv_style_t * style, lv_coord_t value)
+{
+ lv_style_value_t v = {
+ .num = (int32_t)value
+ };
+ lv_style_set_prop(style, LV_STYLE_PAD_LEFT, v);
+}
+
+void lv_style_set_pad_right(lv_style_t * style, lv_coord_t value)
+{
+ lv_style_value_t v = {
+ .num = (int32_t)value
+ };
+ lv_style_set_prop(style, LV_STYLE_PAD_RIGHT, v);
+}
+
+void lv_style_set_pad_row(lv_style_t * style, lv_coord_t value)
+{
+ lv_style_value_t v = {
+ .num = (int32_t)value
+ };
+ lv_style_set_prop(style, LV_STYLE_PAD_ROW, v);
+}
+
+void lv_style_set_pad_column(lv_style_t * style, lv_coord_t value)
+{
+ lv_style_value_t v = {
+ .num = (int32_t)value
+ };
+ lv_style_set_prop(style, LV_STYLE_PAD_COLUMN, v);
+}
+
+void lv_style_set_bg_color(lv_style_t * style, lv_color_t value)
+{
+ lv_style_value_t v = {
+ .color = value
+ };
+ lv_style_set_prop(style, LV_STYLE_BG_COLOR, v);
+}
+
+void lv_style_set_bg_opa(lv_style_t * style, lv_opa_t value)
+{
+ lv_style_value_t v = {
+ .num = (int32_t)value
+ };
+ lv_style_set_prop(style, LV_STYLE_BG_OPA, v);
+}
+
+void lv_style_set_bg_grad_color(lv_style_t * style, lv_color_t value)
+{
+ lv_style_value_t v = {
+ .color = value
+ };
+ lv_style_set_prop(style, LV_STYLE_BG_GRAD_COLOR, v);
+}
+
+void lv_style_set_bg_grad_dir(lv_style_t * style, lv_grad_dir_t value)
+{
+ lv_style_value_t v = {
+ .num = (int32_t)value
+ };
+ lv_style_set_prop(style, LV_STYLE_BG_GRAD_DIR, v);
+}
+
+void lv_style_set_bg_main_stop(lv_style_t * style, lv_coord_t value)
+{
+ lv_style_value_t v = {
+ .num = (int32_t)value
+ };
+ lv_style_set_prop(style, LV_STYLE_BG_MAIN_STOP, v);
+}
+
+void lv_style_set_bg_grad_stop(lv_style_t * style, lv_coord_t value)
+{
+ lv_style_value_t v = {
+ .num = (int32_t)value
+ };
+ lv_style_set_prop(style, LV_STYLE_BG_GRAD_STOP, v);
+}
+
+void lv_style_set_bg_grad(lv_style_t * style, const lv_grad_dsc_t * value)
+{
+ lv_style_value_t v = {
+ .ptr = value
+ };
+ lv_style_set_prop(style, LV_STYLE_BG_GRAD, v);
+}
+
+void lv_style_set_bg_dither_mode(lv_style_t * style, lv_dither_mode_t value)
+{
+ lv_style_value_t v = {
+ .num = (int32_t)value
+ };
+ lv_style_set_prop(style, LV_STYLE_BG_DITHER_MODE, v);
+}
+
+void lv_style_set_bg_img_src(lv_style_t * style, const void * value)
+{
+ lv_style_value_t v = {
+ .ptr = value
+ };
+ lv_style_set_prop(style, LV_STYLE_BG_IMG_SRC, v);
+}
+
+void lv_style_set_bg_img_opa(lv_style_t * style, lv_opa_t value)
+{
+ lv_style_value_t v = {
+ .num = (int32_t)value
+ };
+ lv_style_set_prop(style, LV_STYLE_BG_IMG_OPA, v);
+}
+
+void lv_style_set_bg_img_recolor(lv_style_t * style, lv_color_t value)
+{
+ lv_style_value_t v = {
+ .color = value
+ };
+ lv_style_set_prop(style, LV_STYLE_BG_IMG_RECOLOR, v);
+}
+
+void lv_style_set_bg_img_recolor_opa(lv_style_t * style, lv_opa_t value)
+{
+ lv_style_value_t v = {
+ .num = (int32_t)value
+ };
+ lv_style_set_prop(style, LV_STYLE_BG_IMG_RECOLOR_OPA, v);
+}
+
+void lv_style_set_bg_img_tiled(lv_style_t * style, bool value)
+{
+ lv_style_value_t v = {
+ .num = (int32_t)value
+ };
+ lv_style_set_prop(style, LV_STYLE_BG_IMG_TILED, v);
+}
+
+void lv_style_set_border_color(lv_style_t * style, lv_color_t value)
+{
+ lv_style_value_t v = {
+ .color = value
+ };
+ lv_style_set_prop(style, LV_STYLE_BORDER_COLOR, v);
+}
+
+void lv_style_set_border_opa(lv_style_t * style, lv_opa_t value)
+{
+ lv_style_value_t v = {
+ .num = (int32_t)value
+ };
+ lv_style_set_prop(style, LV_STYLE_BORDER_OPA, v);
+}
+
+void lv_style_set_border_width(lv_style_t * style, lv_coord_t value)
+{
+ lv_style_value_t v = {
+ .num = (int32_t)value
+ };
+ lv_style_set_prop(style, LV_STYLE_BORDER_WIDTH, v);
+}
+
+void lv_style_set_border_side(lv_style_t * style, lv_border_side_t value)
+{
+ lv_style_value_t v = {
+ .num = (int32_t)value
+ };
+ lv_style_set_prop(style, LV_STYLE_BORDER_SIDE, v);
+}
+
+void lv_style_set_border_post(lv_style_t * style, bool value)
+{
+ lv_style_value_t v = {
+ .num = (int32_t)value
+ };
+ lv_style_set_prop(style, LV_STYLE_BORDER_POST, v);
+}
+
+void lv_style_set_outline_width(lv_style_t * style, lv_coord_t value)
+{
+ lv_style_value_t v = {
+ .num = (int32_t)value
+ };
+ lv_style_set_prop(style, LV_STYLE_OUTLINE_WIDTH, v);
+}
+
+void lv_style_set_outline_color(lv_style_t * style, lv_color_t value)
+{
+ lv_style_value_t v = {
+ .color = value
+ };
+ lv_style_set_prop(style, LV_STYLE_OUTLINE_COLOR, v);
+}
+
+void lv_style_set_outline_opa(lv_style_t * style, lv_opa_t value)
+{
+ lv_style_value_t v = {
+ .num = (int32_t)value
+ };
+ lv_style_set_prop(style, LV_STYLE_OUTLINE_OPA, v);
+}
+
+void lv_style_set_outline_pad(lv_style_t * style, lv_coord_t value)
+{
+ lv_style_value_t v = {
+ .num = (int32_t)value
+ };
+ lv_style_set_prop(style, LV_STYLE_OUTLINE_PAD, v);
+}
+
+void lv_style_set_shadow_width(lv_style_t * style, lv_coord_t value)
+{
+ lv_style_value_t v = {
+ .num = (int32_t)value
+ };
+ lv_style_set_prop(style, LV_STYLE_SHADOW_WIDTH, v);
+}
+
+void lv_style_set_shadow_ofs_x(lv_style_t * style, lv_coord_t value)
+{
+ lv_style_value_t v = {
+ .num = (int32_t)value
+ };
+ lv_style_set_prop(style, LV_STYLE_SHADOW_OFS_X, v);
+}
+
+void lv_style_set_shadow_ofs_y(lv_style_t * style, lv_coord_t value)
+{
+ lv_style_value_t v = {
+ .num = (int32_t)value
+ };
+ lv_style_set_prop(style, LV_STYLE_SHADOW_OFS_Y, v);
+}
+
+void lv_style_set_shadow_spread(lv_style_t * style, lv_coord_t value)
+{
+ lv_style_value_t v = {
+ .num = (int32_t)value
+ };
+ lv_style_set_prop(style, LV_STYLE_SHADOW_SPREAD, v);
+}
+
+void lv_style_set_shadow_color(lv_style_t * style, lv_color_t value)
+{
+ lv_style_value_t v = {
+ .color = value
+ };
+ lv_style_set_prop(style, LV_STYLE_SHADOW_COLOR, v);
+}
+
+void lv_style_set_shadow_opa(lv_style_t * style, lv_opa_t value)
+{
+ lv_style_value_t v = {
+ .num = (int32_t)value
+ };
+ lv_style_set_prop(style, LV_STYLE_SHADOW_OPA, v);
+}
+
+void lv_style_set_img_opa(lv_style_t * style, lv_opa_t value)
+{
+ lv_style_value_t v = {
+ .num = (int32_t)value
+ };
+ lv_style_set_prop(style, LV_STYLE_IMG_OPA, v);
+}
+
+void lv_style_set_img_recolor(lv_style_t * style, lv_color_t value)
+{
+ lv_style_value_t v = {
+ .color = value
+ };
+ lv_style_set_prop(style, LV_STYLE_IMG_RECOLOR, v);
+}
+
+void lv_style_set_img_recolor_opa(lv_style_t * style, lv_opa_t value)
+{
+ lv_style_value_t v = {
+ .num = (int32_t)value
+ };
+ lv_style_set_prop(style, LV_STYLE_IMG_RECOLOR_OPA, v);
+}
+
+void lv_style_set_line_width(lv_style_t * style, lv_coord_t value)
+{
+ lv_style_value_t v = {
+ .num = (int32_t)value
+ };
+ lv_style_set_prop(style, LV_STYLE_LINE_WIDTH, v);
+}
+
+void lv_style_set_line_dash_width(lv_style_t * style, lv_coord_t value)
+{
+ lv_style_value_t v = {
+ .num = (int32_t)value
+ };
+ lv_style_set_prop(style, LV_STYLE_LINE_DASH_WIDTH, v);
+}
+
+void lv_style_set_line_dash_gap(lv_style_t * style, lv_coord_t value)
+{
+ lv_style_value_t v = {
+ .num = (int32_t)value
+ };
+ lv_style_set_prop(style, LV_STYLE_LINE_DASH_GAP, v);
+}
+
+void lv_style_set_line_rounded(lv_style_t * style, bool value)
+{
+ lv_style_value_t v = {
+ .num = (int32_t)value
+ };
+ lv_style_set_prop(style, LV_STYLE_LINE_ROUNDED, v);
+}
+
+void lv_style_set_line_color(lv_style_t * style, lv_color_t value)
+{
+ lv_style_value_t v = {
+ .color = value
+ };
+ lv_style_set_prop(style, LV_STYLE_LINE_COLOR, v);
+}
+
+void lv_style_set_line_opa(lv_style_t * style, lv_opa_t value)
+{
+ lv_style_value_t v = {
+ .num = (int32_t)value
+ };
+ lv_style_set_prop(style, LV_STYLE_LINE_OPA, v);
+}
+
+void lv_style_set_arc_width(lv_style_t * style, lv_coord_t value)
+{
+ lv_style_value_t v = {
+ .num = (int32_t)value
+ };
+ lv_style_set_prop(style, LV_STYLE_ARC_WIDTH, v);
+}
+
+void lv_style_set_arc_rounded(lv_style_t * style, bool value)
+{
+ lv_style_value_t v = {
+ .num = (int32_t)value
+ };
+ lv_style_set_prop(style, LV_STYLE_ARC_ROUNDED, v);
+}
+
+void lv_style_set_arc_color(lv_style_t * style, lv_color_t value)
+{
+ lv_style_value_t v = {
+ .color = value
+ };
+ lv_style_set_prop(style, LV_STYLE_ARC_COLOR, v);
+}
+
+void lv_style_set_arc_opa(lv_style_t * style, lv_opa_t value)
+{
+ lv_style_value_t v = {
+ .num = (int32_t)value
+ };
+ lv_style_set_prop(style, LV_STYLE_ARC_OPA, v);
+}
+
+void lv_style_set_arc_img_src(lv_style_t * style, const void * value)
+{
+ lv_style_value_t v = {
+ .ptr = value
+ };
+ lv_style_set_prop(style, LV_STYLE_ARC_IMG_SRC, v);
+}
+
+void lv_style_set_text_color(lv_style_t * style, lv_color_t value)
+{
+ lv_style_value_t v = {
+ .color = value
+ };
+ lv_style_set_prop(style, LV_STYLE_TEXT_COLOR, v);
+}
+
+void lv_style_set_text_opa(lv_style_t * style, lv_opa_t value)
+{
+ lv_style_value_t v = {
+ .num = (int32_t)value
+ };
+ lv_style_set_prop(style, LV_STYLE_TEXT_OPA, v);
+}
+
+void lv_style_set_text_font(lv_style_t * style, const lv_font_t * value)
+{
+ lv_style_value_t v = {
+ .ptr = value
+ };
+ lv_style_set_prop(style, LV_STYLE_TEXT_FONT, v);
+}
+
+void lv_style_set_text_letter_space(lv_style_t * style, lv_coord_t value)
+{
+ lv_style_value_t v = {
+ .num = (int32_t)value
+ };
+ lv_style_set_prop(style, LV_STYLE_TEXT_LETTER_SPACE, v);
+}
+
+void lv_style_set_text_line_space(lv_style_t * style, lv_coord_t value)
+{
+ lv_style_value_t v = {
+ .num = (int32_t)value
+ };
+ lv_style_set_prop(style, LV_STYLE_TEXT_LINE_SPACE, v);
+}
+
+void lv_style_set_text_decor(lv_style_t * style, lv_text_decor_t value)
+{
+ lv_style_value_t v = {
+ .num = (int32_t)value
+ };
+ lv_style_set_prop(style, LV_STYLE_TEXT_DECOR, v);
+}
+
+void lv_style_set_text_align(lv_style_t * style, lv_text_align_t value)
+{
+ lv_style_value_t v = {
+ .num = (int32_t)value
+ };
+ lv_style_set_prop(style, LV_STYLE_TEXT_ALIGN, v);
+}
+
+void lv_style_set_radius(lv_style_t * style, lv_coord_t value)
+{
+ lv_style_value_t v = {
+ .num = (int32_t)value
+ };
+ lv_style_set_prop(style, LV_STYLE_RADIUS, v);
+}
+
+void lv_style_set_clip_corner(lv_style_t * style, bool value)
+{
+ lv_style_value_t v = {
+ .num = (int32_t)value
+ };
+ lv_style_set_prop(style, LV_STYLE_CLIP_CORNER, v);
+}
+
+void lv_style_set_opa(lv_style_t * style, lv_opa_t value)
+{
+ lv_style_value_t v = {
+ .num = (int32_t)value
+ };
+ lv_style_set_prop(style, LV_STYLE_OPA, v);
+}
+
+void lv_style_set_color_filter_dsc(lv_style_t * style, const lv_color_filter_dsc_t * value)
+{
+ lv_style_value_t v = {
+ .ptr = value
+ };
+ lv_style_set_prop(style, LV_STYLE_COLOR_FILTER_DSC, v);
+}
+
+void lv_style_set_color_filter_opa(lv_style_t * style, lv_opa_t value)
+{
+ lv_style_value_t v = {
+ .num = (int32_t)value
+ };
+ lv_style_set_prop(style, LV_STYLE_COLOR_FILTER_OPA, v);
+}
+
+void lv_style_set_anim(lv_style_t * style, const lv_anim_t * value)
+{
+ lv_style_value_t v = {
+ .ptr = value
+ };
+ lv_style_set_prop(style, LV_STYLE_ANIM, v);
+}
+
+void lv_style_set_anim_time(lv_style_t * style, uint32_t value)
+{
+ lv_style_value_t v = {
+ .num = (int32_t)value
+ };
+ lv_style_set_prop(style, LV_STYLE_ANIM_TIME, v);
+}
+
+void lv_style_set_anim_speed(lv_style_t * style, uint32_t value)
+{
+ lv_style_value_t v = {
+ .num = (int32_t)value
+ };
+ lv_style_set_prop(style, LV_STYLE_ANIM_SPEED, v);
+}
+
+void lv_style_set_transition(lv_style_t * style, const lv_style_transition_dsc_t * value)
+{
+ lv_style_value_t v = {
+ .ptr = value
+ };
+ lv_style_set_prop(style, LV_STYLE_TRANSITION, v);
+}
+
+void lv_style_set_blend_mode(lv_style_t * style, lv_blend_mode_t value)
+{
+ lv_style_value_t v = {
+ .num = (int32_t)value
+ };
+ lv_style_set_prop(style, LV_STYLE_BLEND_MODE, v);
+}
+
+void lv_style_set_layout(lv_style_t * style, uint16_t value)
+{
+ lv_style_value_t v = {
+ .num = (int32_t)value
+ };
+ lv_style_set_prop(style, LV_STYLE_LAYOUT, v);
+}
+
+void lv_style_set_base_dir(lv_style_t * style, lv_base_dir_t value)
+{
+ lv_style_value_t v = {
+ .num = (int32_t)value
+ };
+ lv_style_set_prop(style, LV_STYLE_BASE_DIR, v);
+}
diff --git a/lib/lvgl/src/misc/lv_style_gen.h b/lib/lvgl/src/misc/lv_style_gen.h
new file mode 100644
index 00000000..8bf3b68f
--- /dev/null
+++ b/lib/lvgl/src/misc/lv_style_gen.h
@@ -0,0 +1,504 @@
+void lv_style_set_width(lv_style_t * style, lv_coord_t value);
+void lv_style_set_min_width(lv_style_t * style, lv_coord_t value);
+void lv_style_set_max_width(lv_style_t * style, lv_coord_t value);
+void lv_style_set_height(lv_style_t * style, lv_coord_t value);
+void lv_style_set_min_height(lv_style_t * style, lv_coord_t value);
+void lv_style_set_max_height(lv_style_t * style, lv_coord_t value);
+void lv_style_set_x(lv_style_t * style, lv_coord_t value);
+void lv_style_set_y(lv_style_t * style, lv_coord_t value);
+void lv_style_set_align(lv_style_t * style, lv_align_t value);
+void lv_style_set_transform_width(lv_style_t * style, lv_coord_t value);
+void lv_style_set_transform_height(lv_style_t * style, lv_coord_t value);
+void lv_style_set_translate_x(lv_style_t * style, lv_coord_t value);
+void lv_style_set_translate_y(lv_style_t * style, lv_coord_t value);
+void lv_style_set_transform_zoom(lv_style_t * style, lv_coord_t value);
+void lv_style_set_transform_angle(lv_style_t * style, lv_coord_t value);
+void lv_style_set_transform_pivot_x(lv_style_t * style, lv_coord_t value);
+void lv_style_set_transform_pivot_y(lv_style_t * style, lv_coord_t value);
+void lv_style_set_pad_top(lv_style_t * style, lv_coord_t value);
+void lv_style_set_pad_bottom(lv_style_t * style, lv_coord_t value);
+void lv_style_set_pad_left(lv_style_t * style, lv_coord_t value);
+void lv_style_set_pad_right(lv_style_t * style, lv_coord_t value);
+void lv_style_set_pad_row(lv_style_t * style, lv_coord_t value);
+void lv_style_set_pad_column(lv_style_t * style, lv_coord_t value);
+void lv_style_set_bg_color(lv_style_t * style, lv_color_t value);
+void lv_style_set_bg_opa(lv_style_t * style, lv_opa_t value);
+void lv_style_set_bg_grad_color(lv_style_t * style, lv_color_t value);
+void lv_style_set_bg_grad_dir(lv_style_t * style, lv_grad_dir_t value);
+void lv_style_set_bg_main_stop(lv_style_t * style, lv_coord_t value);
+void lv_style_set_bg_grad_stop(lv_style_t * style, lv_coord_t value);
+void lv_style_set_bg_grad(lv_style_t * style, const lv_grad_dsc_t * value);
+void lv_style_set_bg_dither_mode(lv_style_t * style, lv_dither_mode_t value);
+void lv_style_set_bg_img_src(lv_style_t * style, const void * value);
+void lv_style_set_bg_img_opa(lv_style_t * style, lv_opa_t value);
+void lv_style_set_bg_img_recolor(lv_style_t * style, lv_color_t value);
+void lv_style_set_bg_img_recolor_opa(lv_style_t * style, lv_opa_t value);
+void lv_style_set_bg_img_tiled(lv_style_t * style, bool value);
+void lv_style_set_border_color(lv_style_t * style, lv_color_t value);
+void lv_style_set_border_opa(lv_style_t * style, lv_opa_t value);
+void lv_style_set_border_width(lv_style_t * style, lv_coord_t value);
+void lv_style_set_border_side(lv_style_t * style, lv_border_side_t value);
+void lv_style_set_border_post(lv_style_t * style, bool value);
+void lv_style_set_outline_width(lv_style_t * style, lv_coord_t value);
+void lv_style_set_outline_color(lv_style_t * style, lv_color_t value);
+void lv_style_set_outline_opa(lv_style_t * style, lv_opa_t value);
+void lv_style_set_outline_pad(lv_style_t * style, lv_coord_t value);
+void lv_style_set_shadow_width(lv_style_t * style, lv_coord_t value);
+void lv_style_set_shadow_ofs_x(lv_style_t * style, lv_coord_t value);
+void lv_style_set_shadow_ofs_y(lv_style_t * style, lv_coord_t value);
+void lv_style_set_shadow_spread(lv_style_t * style, lv_coord_t value);
+void lv_style_set_shadow_color(lv_style_t * style, lv_color_t value);
+void lv_style_set_shadow_opa(lv_style_t * style, lv_opa_t value);
+void lv_style_set_img_opa(lv_style_t * style, lv_opa_t value);
+void lv_style_set_img_recolor(lv_style_t * style, lv_color_t value);
+void lv_style_set_img_recolor_opa(lv_style_t * style, lv_opa_t value);
+void lv_style_set_line_width(lv_style_t * style, lv_coord_t value);
+void lv_style_set_line_dash_width(lv_style_t * style, lv_coord_t value);
+void lv_style_set_line_dash_gap(lv_style_t * style, lv_coord_t value);
+void lv_style_set_line_rounded(lv_style_t * style, bool value);
+void lv_style_set_line_color(lv_style_t * style, lv_color_t value);
+void lv_style_set_line_opa(lv_style_t * style, lv_opa_t value);
+void lv_style_set_arc_width(lv_style_t * style, lv_coord_t value);
+void lv_style_set_arc_rounded(lv_style_t * style, bool value);
+void lv_style_set_arc_color(lv_style_t * style, lv_color_t value);
+void lv_style_set_arc_opa(lv_style_t * style, lv_opa_t value);
+void lv_style_set_arc_img_src(lv_style_t * style, const void * value);
+void lv_style_set_text_color(lv_style_t * style, lv_color_t value);
+void lv_style_set_text_opa(lv_style_t * style, lv_opa_t value);
+void lv_style_set_text_font(lv_style_t * style, const lv_font_t * value);
+void lv_style_set_text_letter_space(lv_style_t * style, lv_coord_t value);
+void lv_style_set_text_line_space(lv_style_t * style, lv_coord_t value);
+void lv_style_set_text_decor(lv_style_t * style, lv_text_decor_t value);
+void lv_style_set_text_align(lv_style_t * style, lv_text_align_t value);
+void lv_style_set_radius(lv_style_t * style, lv_coord_t value);
+void lv_style_set_clip_corner(lv_style_t * style, bool value);
+void lv_style_set_opa(lv_style_t * style, lv_opa_t value);
+void lv_style_set_color_filter_dsc(lv_style_t * style, const lv_color_filter_dsc_t * value);
+void lv_style_set_color_filter_opa(lv_style_t * style, lv_opa_t value);
+void lv_style_set_anim(lv_style_t * style, const lv_anim_t * value);
+void lv_style_set_anim_time(lv_style_t * style, uint32_t value);
+void lv_style_set_anim_speed(lv_style_t * style, uint32_t value);
+void lv_style_set_transition(lv_style_t * style, const lv_style_transition_dsc_t * value);
+void lv_style_set_blend_mode(lv_style_t * style, lv_blend_mode_t value);
+void lv_style_set_layout(lv_style_t * style, uint16_t value);
+void lv_style_set_base_dir(lv_style_t * style, lv_base_dir_t value);
+
+#define LV_STYLE_CONST_WIDTH(val) \
+ { \
+ .prop = LV_STYLE_WIDTH, .value = { .num = (int32_t)val } \
+ }
+
+#define LV_STYLE_CONST_MIN_WIDTH(val) \
+ { \
+ .prop = LV_STYLE_MIN_WIDTH, .value = { .num = (int32_t)val } \
+ }
+
+#define LV_STYLE_CONST_MAX_WIDTH(val) \
+ { \
+ .prop = LV_STYLE_MAX_WIDTH, .value = { .num = (int32_t)val } \
+ }
+
+#define LV_STYLE_CONST_HEIGHT(val) \
+ { \
+ .prop = LV_STYLE_HEIGHT, .value = { .num = (int32_t)val } \
+ }
+
+#define LV_STYLE_CONST_MIN_HEIGHT(val) \
+ { \
+ .prop = LV_STYLE_MIN_HEIGHT, .value = { .num = (int32_t)val } \
+ }
+
+#define LV_STYLE_CONST_MAX_HEIGHT(val) \
+ { \
+ .prop = LV_STYLE_MAX_HEIGHT, .value = { .num = (int32_t)val } \
+ }
+
+#define LV_STYLE_CONST_X(val) \
+ { \
+ .prop = LV_STYLE_X, .value = { .num = (int32_t)val } \
+ }
+
+#define LV_STYLE_CONST_Y(val) \
+ { \
+ .prop = LV_STYLE_Y, .value = { .num = (int32_t)val } \
+ }
+
+#define LV_STYLE_CONST_ALIGN(val) \
+ { \
+ .prop = LV_STYLE_ALIGN, .value = { .num = (int32_t)val } \
+ }
+
+#define LV_STYLE_CONST_TRANSFORM_WIDTH(val) \
+ { \
+ .prop = LV_STYLE_TRANSFORM_WIDTH, .value = { .num = (int32_t)val } \
+ }
+
+#define LV_STYLE_CONST_TRANSFORM_HEIGHT(val) \
+ { \
+ .prop = LV_STYLE_TRANSFORM_HEIGHT, .value = { .num = (int32_t)val } \
+ }
+
+#define LV_STYLE_CONST_TRANSLATE_X(val) \
+ { \
+ .prop = LV_STYLE_TRANSLATE_X, .value = { .num = (int32_t)val } \
+ }
+
+#define LV_STYLE_CONST_TRANSLATE_Y(val) \
+ { \
+ .prop = LV_STYLE_TRANSLATE_Y, .value = { .num = (int32_t)val } \
+ }
+
+#define LV_STYLE_CONST_TRANSFORM_ZOOM(val) \
+ { \
+ .prop = LV_STYLE_TRANSFORM_ZOOM, .value = { .num = (int32_t)val } \
+ }
+
+#define LV_STYLE_CONST_TRANSFORM_ANGLE(val) \
+ { \
+ .prop = LV_STYLE_TRANSFORM_ANGLE, .value = { .num = (int32_t)val } \
+ }
+
+#define LV_STYLE_CONST_TRANSFORM_PIVOT_X(val) \
+ { \
+ .prop = LV_STYLE_TRANSFORM_PIVOT_X, .value = { .num = (int32_t)val } \
+ }
+
+#define LV_STYLE_CONST_TRANSFORM_PIVOT_Y(val) \
+ { \
+ .prop = LV_STYLE_TRANSFORM_PIVOT_Y, .value = { .num = (int32_t)val } \
+ }
+
+#define LV_STYLE_CONST_PAD_TOP(val) \
+ { \
+ .prop = LV_STYLE_PAD_TOP, .value = { .num = (int32_t)val } \
+ }
+
+#define LV_STYLE_CONST_PAD_BOTTOM(val) \
+ { \
+ .prop = LV_STYLE_PAD_BOTTOM, .value = { .num = (int32_t)val } \
+ }
+
+#define LV_STYLE_CONST_PAD_LEFT(val) \
+ { \
+ .prop = LV_STYLE_PAD_LEFT, .value = { .num = (int32_t)val } \
+ }
+
+#define LV_STYLE_CONST_PAD_RIGHT(val) \
+ { \
+ .prop = LV_STYLE_PAD_RIGHT, .value = { .num = (int32_t)val } \
+ }
+
+#define LV_STYLE_CONST_PAD_ROW(val) \
+ { \
+ .prop = LV_STYLE_PAD_ROW, .value = { .num = (int32_t)val } \
+ }
+
+#define LV_STYLE_CONST_PAD_COLUMN(val) \
+ { \
+ .prop = LV_STYLE_PAD_COLUMN, .value = { .num = (int32_t)val } \
+ }
+
+#define LV_STYLE_CONST_BG_COLOR(val) \
+ { \
+ .prop = LV_STYLE_BG_COLOR, .value = { .color = val } \
+ }
+
+#define LV_STYLE_CONST_BG_OPA(val) \
+ { \
+ .prop = LV_STYLE_BG_OPA, .value = { .num = (int32_t)val } \
+ }
+
+#define LV_STYLE_CONST_BG_GRAD_COLOR(val) \
+ { \
+ .prop = LV_STYLE_BG_GRAD_COLOR, .value = { .color = val } \
+ }
+
+#define LV_STYLE_CONST_BG_GRAD_DIR(val) \
+ { \
+ .prop = LV_STYLE_BG_GRAD_DIR, .value = { .num = (int32_t)val } \
+ }
+
+#define LV_STYLE_CONST_BG_MAIN_STOP(val) \
+ { \
+ .prop = LV_STYLE_BG_MAIN_STOP, .value = { .num = (int32_t)val } \
+ }
+
+#define LV_STYLE_CONST_BG_GRAD_STOP(val) \
+ { \
+ .prop = LV_STYLE_BG_GRAD_STOP, .value = { .num = (int32_t)val } \
+ }
+
+#define LV_STYLE_CONST_BG_GRAD(val) \
+ { \
+ .prop = LV_STYLE_BG_GRAD, .value = { .ptr = val } \
+ }
+
+#define LV_STYLE_CONST_BG_DITHER_MODE(val) \
+ { \
+ .prop = LV_STYLE_BG_DITHER_MODE, .value = { .num = (int32_t)val } \
+ }
+
+#define LV_STYLE_CONST_BG_IMG_SRC(val) \
+ { \
+ .prop = LV_STYLE_BG_IMG_SRC, .value = { .ptr = val } \
+ }
+
+#define LV_STYLE_CONST_BG_IMG_OPA(val) \
+ { \
+ .prop = LV_STYLE_BG_IMG_OPA, .value = { .num = (int32_t)val } \
+ }
+
+#define LV_STYLE_CONST_BG_IMG_RECOLOR(val) \
+ { \
+ .prop = LV_STYLE_BG_IMG_RECOLOR, .value = { .color = val } \
+ }
+
+#define LV_STYLE_CONST_BG_IMG_RECOLOR_OPA(val) \
+ { \
+ .prop = LV_STYLE_BG_IMG_RECOLOR_OPA, .value = { .num = (int32_t)val } \
+ }
+
+#define LV_STYLE_CONST_BG_IMG_TILED(val) \
+ { \
+ .prop = LV_STYLE_BG_IMG_TILED, .value = { .num = (int32_t)val } \
+ }
+
+#define LV_STYLE_CONST_BORDER_COLOR(val) \
+ { \
+ .prop = LV_STYLE_BORDER_COLOR, .value = { .color = val } \
+ }
+
+#define LV_STYLE_CONST_BORDER_OPA(val) \
+ { \
+ .prop = LV_STYLE_BORDER_OPA, .value = { .num = (int32_t)val } \
+ }
+
+#define LV_STYLE_CONST_BORDER_WIDTH(val) \
+ { \
+ .prop = LV_STYLE_BORDER_WIDTH, .value = { .num = (int32_t)val } \
+ }
+
+#define LV_STYLE_CONST_BORDER_SIDE(val) \
+ { \
+ .prop = LV_STYLE_BORDER_SIDE, .value = { .num = (int32_t)val } \
+ }
+
+#define LV_STYLE_CONST_BORDER_POST(val) \
+ { \
+ .prop = LV_STYLE_BORDER_POST, .value = { .num = (int32_t)val } \
+ }
+
+#define LV_STYLE_CONST_OUTLINE_WIDTH(val) \
+ { \
+ .prop = LV_STYLE_OUTLINE_WIDTH, .value = { .num = (int32_t)val } \
+ }
+
+#define LV_STYLE_CONST_OUTLINE_COLOR(val) \
+ { \
+ .prop = LV_STYLE_OUTLINE_COLOR, .value = { .color = val } \
+ }
+
+#define LV_STYLE_CONST_OUTLINE_OPA(val) \
+ { \
+ .prop = LV_STYLE_OUTLINE_OPA, .value = { .num = (int32_t)val } \
+ }
+
+#define LV_STYLE_CONST_OUTLINE_PAD(val) \
+ { \
+ .prop = LV_STYLE_OUTLINE_PAD, .value = { .num = (int32_t)val } \
+ }
+
+#define LV_STYLE_CONST_SHADOW_WIDTH(val) \
+ { \
+ .prop = LV_STYLE_SHADOW_WIDTH, .value = { .num = (int32_t)val } \
+ }
+
+#define LV_STYLE_CONST_SHADOW_OFS_X(val) \
+ { \
+ .prop = LV_STYLE_SHADOW_OFS_X, .value = { .num = (int32_t)val } \
+ }
+
+#define LV_STYLE_CONST_SHADOW_OFS_Y(val) \
+ { \
+ .prop = LV_STYLE_SHADOW_OFS_Y, .value = { .num = (int32_t)val } \
+ }
+
+#define LV_STYLE_CONST_SHADOW_SPREAD(val) \
+ { \
+ .prop = LV_STYLE_SHADOW_SPREAD, .value = { .num = (int32_t)val } \
+ }
+
+#define LV_STYLE_CONST_SHADOW_COLOR(val) \
+ { \
+ .prop = LV_STYLE_SHADOW_COLOR, .value = { .color = val } \
+ }
+
+#define LV_STYLE_CONST_SHADOW_OPA(val) \
+ { \
+ .prop = LV_STYLE_SHADOW_OPA, .value = { .num = (int32_t)val } \
+ }
+
+#define LV_STYLE_CONST_IMG_OPA(val) \
+ { \
+ .prop = LV_STYLE_IMG_OPA, .value = { .num = (int32_t)val } \
+ }
+
+#define LV_STYLE_CONST_IMG_RECOLOR(val) \
+ { \
+ .prop = LV_STYLE_IMG_RECOLOR, .value = { .color = val } \
+ }
+
+#define LV_STYLE_CONST_IMG_RECOLOR_OPA(val) \
+ { \
+ .prop = LV_STYLE_IMG_RECOLOR_OPA, .value = { .num = (int32_t)val } \
+ }
+
+#define LV_STYLE_CONST_LINE_WIDTH(val) \
+ { \
+ .prop = LV_STYLE_LINE_WIDTH, .value = { .num = (int32_t)val } \
+ }
+
+#define LV_STYLE_CONST_LINE_DASH_WIDTH(val) \
+ { \
+ .prop = LV_STYLE_LINE_DASH_WIDTH, .value = { .num = (int32_t)val } \
+ }
+
+#define LV_STYLE_CONST_LINE_DASH_GAP(val) \
+ { \
+ .prop = LV_STYLE_LINE_DASH_GAP, .value = { .num = (int32_t)val } \
+ }
+
+#define LV_STYLE_CONST_LINE_ROUNDED(val) \
+ { \
+ .prop = LV_STYLE_LINE_ROUNDED, .value = { .num = (int32_t)val } \
+ }
+
+#define LV_STYLE_CONST_LINE_COLOR(val) \
+ { \
+ .prop = LV_STYLE_LINE_COLOR, .value = { .color = val } \
+ }
+
+#define LV_STYLE_CONST_LINE_OPA(val) \
+ { \
+ .prop = LV_STYLE_LINE_OPA, .value = { .num = (int32_t)val } \
+ }
+
+#define LV_STYLE_CONST_ARC_WIDTH(val) \
+ { \
+ .prop = LV_STYLE_ARC_WIDTH, .value = { .num = (int32_t)val } \
+ }
+
+#define LV_STYLE_CONST_ARC_ROUNDED(val) \
+ { \
+ .prop = LV_STYLE_ARC_ROUNDED, .value = { .num = (int32_t)val } \
+ }
+
+#define LV_STYLE_CONST_ARC_COLOR(val) \
+ { \
+ .prop = LV_STYLE_ARC_COLOR, .value = { .color = val } \
+ }
+
+#define LV_STYLE_CONST_ARC_OPA(val) \
+ { \
+ .prop = LV_STYLE_ARC_OPA, .value = { .num = (int32_t)val } \
+ }
+
+#define LV_STYLE_CONST_ARC_IMG_SRC(val) \
+ { \
+ .prop = LV_STYLE_ARC_IMG_SRC, .value = { .ptr = val } \
+ }
+
+#define LV_STYLE_CONST_TEXT_COLOR(val) \
+ { \
+ .prop = LV_STYLE_TEXT_COLOR, .value = { .color = val } \
+ }
+
+#define LV_STYLE_CONST_TEXT_OPA(val) \
+ { \
+ .prop = LV_STYLE_TEXT_OPA, .value = { .num = (int32_t)val } \
+ }
+
+#define LV_STYLE_CONST_TEXT_FONT(val) \
+ { \
+ .prop = LV_STYLE_TEXT_FONT, .value = { .ptr = val } \
+ }
+
+#define LV_STYLE_CONST_TEXT_LETTER_SPACE(val) \
+ { \
+ .prop = LV_STYLE_TEXT_LETTER_SPACE, .value = { .num = (int32_t)val } \
+ }
+
+#define LV_STYLE_CONST_TEXT_LINE_SPACE(val) \
+ { \
+ .prop = LV_STYLE_TEXT_LINE_SPACE, .value = { .num = (int32_t)val } \
+ }
+
+#define LV_STYLE_CONST_TEXT_DECOR(val) \
+ { \
+ .prop = LV_STYLE_TEXT_DECOR, .value = { .num = (int32_t)val } \
+ }
+
+#define LV_STYLE_CONST_TEXT_ALIGN(val) \
+ { \
+ .prop = LV_STYLE_TEXT_ALIGN, .value = { .num = (int32_t)val } \
+ }
+
+#define LV_STYLE_CONST_RADIUS(val) \
+ { \
+ .prop = LV_STYLE_RADIUS, .value = { .num = (int32_t)val } \
+ }
+
+#define LV_STYLE_CONST_CLIP_CORNER(val) \
+ { \
+ .prop = LV_STYLE_CLIP_CORNER, .value = { .num = (int32_t)val } \
+ }
+
+#define LV_STYLE_CONST_OPA(val) \
+ { \
+ .prop = LV_STYLE_OPA, .value = { .num = (int32_t)val } \
+ }
+
+#define LV_STYLE_CONST_COLOR_FILTER_DSC(val) \
+ { \
+ .prop = LV_STYLE_COLOR_FILTER_DSC, .value = { .ptr = val } \
+ }
+
+#define LV_STYLE_CONST_COLOR_FILTER_OPA(val) \
+ { \
+ .prop = LV_STYLE_COLOR_FILTER_OPA, .value = { .num = (int32_t)val } \
+ }
+
+#define LV_STYLE_CONST_ANIM(val) \
+ { \
+ .prop = LV_STYLE_ANIM, .value = { .ptr = val } \
+ }
+
+#define LV_STYLE_CONST_ANIM_TIME(val) \
+ { \
+ .prop = LV_STYLE_ANIM_TIME, .value = { .num = (int32_t)val } \
+ }
+
+#define LV_STYLE_CONST_ANIM_SPEED(val) \
+ { \
+ .prop = LV_STYLE_ANIM_SPEED, .value = { .num = (int32_t)val } \
+ }
+
+#define LV_STYLE_CONST_TRANSITION(val) \
+ { \
+ .prop = LV_STYLE_TRANSITION, .value = { .ptr = val } \
+ }
+
+#define LV_STYLE_CONST_BLEND_MODE(val) \
+ { \
+ .prop = LV_STYLE_BLEND_MODE, .value = { .num = (int32_t)val } \
+ }
+
+#define LV_STYLE_CONST_LAYOUT(val) \
+ { \
+ .prop = LV_STYLE_LAYOUT, .value = { .num = (int32_t)val } \
+ }
+
+#define LV_STYLE_CONST_BASE_DIR(val) \
+ { \
+ .prop = LV_STYLE_BASE_DIR, .value = { .num = (int32_t)val } \
+ }
diff --git a/lib/lvgl/src/misc/lv_templ.c b/lib/lvgl/src/misc/lv_templ.c
new file mode 100644
index 00000000..939930cf
--- /dev/null
+++ b/lib/lvgl/src/misc/lv_templ.c
@@ -0,0 +1,40 @@
+/**
+ * @file lv_templ.c
+ *
+ */
+
+/*********************
+ * INCLUDES
+ *********************/
+
+/*********************
+ * DEFINES
+ *********************/
+
+/**********************
+ * TYPEDEFS
+ **********************/
+
+/*This typedef exists purely to keep -Wpedantic happy when the file is empty.*/
+/*It can be removed.*/
+typedef int _keep_pedantic_happy;
+
+/**********************
+ * STATIC PROTOTYPES
+ **********************/
+
+/**********************
+ * STATIC VARIABLES
+ **********************/
+
+/**********************
+ * MACROS
+ **********************/
+
+/**********************
+ * GLOBAL FUNCTIONS
+ **********************/
+
+/**********************
+ * STATIC FUNCTIONS
+ **********************/
diff --git a/lib/lvgl/src/misc/lv_templ.h b/lib/lvgl/src/misc/lv_templ.h
new file mode 100644
index 00000000..f7e3c268
--- /dev/null
+++ b/lib/lvgl/src/misc/lv_templ.h
@@ -0,0 +1,37 @@
+/**
+ * @file lv_templ.h
+ *
+ */
+
+#ifndef LV_TEMPL_H
+#define LV_TEMPL_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/*********************
+ * INCLUDES
+ *********************/
+
+/*********************
+ * DEFINES
+ *********************/
+
+/**********************
+ * TYPEDEFS
+ **********************/
+
+/**********************
+ * GLOBAL PROTOTYPES
+ **********************/
+
+/**********************
+ * MACROS
+ **********************/
+
+#ifdef __cplusplus
+} /*extern "C"*/
+#endif
+
+#endif /*LV_TEMPL_H*/
diff --git a/lib/lvgl/src/misc/lv_timer.c b/lib/lvgl/src/misc/lv_timer.c
new file mode 100644
index 00000000..d8dd55b7
--- /dev/null
+++ b/lib/lvgl/src/misc/lv_timer.c
@@ -0,0 +1,341 @@
+/**
+ * @file lv_timer.c
+ */
+
+/*********************
+ * INCLUDES
+ *********************/
+#include "lv_timer.h"
+#include "../hal/lv_hal_tick.h"
+#include "lv_assert.h"
+#include "lv_mem.h"
+#include "lv_ll.h"
+#include "lv_gc.h"
+
+/*********************
+ * DEFINES
+ *********************/
+#define IDLE_MEAS_PERIOD 500 /*[ms]*/
+#define DEF_PERIOD 500
+
+/**********************
+ * TYPEDEFS
+ **********************/
+
+/**********************
+ * STATIC PROTOTYPES
+ **********************/
+static bool lv_timer_exec(lv_timer_t * timer);
+static uint32_t lv_timer_time_remaining(lv_timer_t * timer);
+
+/**********************
+ * STATIC VARIABLES
+ **********************/
+static bool lv_timer_run = false;
+static uint8_t idle_last = 0;
+static bool timer_deleted;
+static bool timer_created;
+
+/**********************
+ * MACROS
+ **********************/
+#if LV_LOG_TRACE_TIMER
+ #define TIMER_TRACE(...) LV_LOG_TRACE(__VA_ARGS__)
+#else
+ #define TIMER_TRACE(...)
+#endif
+
+/**********************
+ * GLOBAL FUNCTIONS
+ **********************/
+
+/**
+ * Init the lv_timer module
+ */
+void _lv_timer_core_init(void)
+{
+ _lv_ll_init(&LV_GC_ROOT(_lv_timer_ll), sizeof(lv_timer_t));
+
+ /*Initially enable the lv_timer handling*/
+ lv_timer_enable(true);
+}
+
+/**
+ * Call it periodically to handle lv_timers.
+ * @return the time after which it must be called again
+ */
+LV_ATTRIBUTE_TIMER_HANDLER uint32_t lv_timer_handler(void)
+{
+ TIMER_TRACE("begin");
+
+ /*Avoid concurrent running of the timer handler*/
+ static bool already_running = false;
+ if(already_running) {
+ TIMER_TRACE("already running, concurrent calls are not allow, returning");
+ return 1;
+ }
+ already_running = true;
+
+ if(lv_timer_run == false) {
+ already_running = false; /*Release mutex*/
+ return 1;
+ }
+
+ static uint32_t idle_period_start = 0;
+ static uint32_t busy_time = 0;
+
+ uint32_t handler_start = lv_tick_get();
+
+ if(handler_start == 0) {
+ static uint32_t run_cnt = 0;
+ run_cnt++;
+ if(run_cnt > 100) {
+ run_cnt = 0;
+ LV_LOG_WARN("It seems lv_tick_inc() is not called.");
+ }
+ }
+
+ /*Run all timer from the list*/
+ lv_timer_t * next;
+ do {
+ timer_deleted = false;
+ timer_created = false;
+ LV_GC_ROOT(_lv_timer_act) = _lv_ll_get_head(&LV_GC_ROOT(_lv_timer_ll));
+ while(LV_GC_ROOT(_lv_timer_act)) {
+ /*The timer might be deleted if it runs only once ('repeat_count = 1')
+ *So get next element until the current is surely valid*/
+ next = _lv_ll_get_next(&LV_GC_ROOT(_lv_timer_ll), LV_GC_ROOT(_lv_timer_act));
+
+ if(lv_timer_exec(LV_GC_ROOT(_lv_timer_act))) {
+ /*If a timer was created or deleted then this or the next item might be corrupted*/
+ if(timer_created || timer_deleted) {
+ TIMER_TRACE("Start from the first timer again because a timer was created or deleted");
+ break;
+ }
+ }
+
+ LV_GC_ROOT(_lv_timer_act) = next; /*Load the next timer*/
+ }
+ } while(LV_GC_ROOT(_lv_timer_act));
+
+ uint32_t time_till_next = LV_NO_TIMER_READY;
+ next = _lv_ll_get_head(&LV_GC_ROOT(_lv_timer_ll));
+ while(next) {
+ if(!next->paused) {
+ uint32_t delay = lv_timer_time_remaining(next);
+ if(delay < time_till_next)
+ time_till_next = delay;
+ }
+
+ next = _lv_ll_get_next(&LV_GC_ROOT(_lv_timer_ll), next); /*Find the next timer*/
+ }
+
+ busy_time += lv_tick_elaps(handler_start);
+ uint32_t idle_period_time = lv_tick_elaps(idle_period_start);
+ if(idle_period_time >= IDLE_MEAS_PERIOD) {
+ idle_last = (busy_time * 100) / idle_period_time; /*Calculate the busy percentage*/
+ idle_last = idle_last > 100 ? 0 : 100 - idle_last; /*But we need idle time*/
+ busy_time = 0;
+ idle_period_start = lv_tick_get();
+ }
+
+ already_running = false; /*Release the mutex*/
+
+ TIMER_TRACE("finished (%d ms until the next timer call)", time_till_next);
+ return time_till_next;
+}
+
+/**
+ * Create an "empty" timer. It needs to initialized with at least
+ * `lv_timer_set_cb` and `lv_timer_set_period`
+ * @return pointer to the created timer
+ */
+lv_timer_t * lv_timer_create_basic(void)
+{
+ return lv_timer_create(NULL, DEF_PERIOD, NULL);
+}
+
+/**
+ * Create a new lv_timer
+ * @param timer_xcb a callback which is the timer itself. It will be called periodically.
+ * (the 'x' in the argument name indicates that it's not a fully generic function because it not follows
+ * the `func_name(object, callback, ...)` convention)
+ * @param period call period in ms unit
+ * @param user_data custom parameter
+ * @return pointer to the new timer
+ */
+lv_timer_t * lv_timer_create(lv_timer_cb_t timer_xcb, uint32_t period, void * user_data)
+{
+ lv_timer_t * new_timer = NULL;
+
+ new_timer = _lv_ll_ins_head(&LV_GC_ROOT(_lv_timer_ll));
+ LV_ASSERT_MALLOC(new_timer);
+ if(new_timer == NULL) return NULL;
+
+ new_timer->period = period;
+ new_timer->timer_cb = timer_xcb;
+ new_timer->repeat_count = -1;
+ new_timer->paused = 0;
+ new_timer->last_run = lv_tick_get();
+ new_timer->user_data = user_data;
+
+ timer_created = true;
+
+ return new_timer;
+}
+
+/**
+ * Set the callback the timer (the function to call periodically)
+ * @param timer pointer to a timer
+ * @param timer_cb the function to call periodically
+ */
+void lv_timer_set_cb(lv_timer_t * timer, lv_timer_cb_t timer_cb)
+{
+ timer->timer_cb = timer_cb;
+}
+
+/**
+ * Delete a lv_timer
+ * @param timer pointer to timer created by timer
+ */
+void lv_timer_del(lv_timer_t * timer)
+{
+ _lv_ll_remove(&LV_GC_ROOT(_lv_timer_ll), timer);
+ timer_deleted = true;
+
+ lv_mem_free(timer);
+}
+
+/**
+ * Pause/resume a timer.
+ * @param timer pointer to an lv_timer
+ */
+void lv_timer_pause(lv_timer_t * timer)
+{
+ timer->paused = true;
+}
+
+void lv_timer_resume(lv_timer_t * timer)
+{
+ timer->paused = false;
+}
+
+/**
+ * Set new period for a lv_timer
+ * @param timer pointer to a lv_timer
+ * @param period the new period
+ */
+void lv_timer_set_period(lv_timer_t * timer, uint32_t period)
+{
+ timer->period = period;
+}
+
+/**
+ * Make a lv_timer ready. It will not wait its period.
+ * @param timer pointer to a lv_timer.
+ */
+void lv_timer_ready(lv_timer_t * timer)
+{
+ timer->last_run = lv_tick_get() - timer->period - 1;
+}
+
+/**
+ * Set the number of times a timer will repeat.
+ * @param timer pointer to a lv_timer.
+ * @param repeat_count -1 : infinity; 0 : stop ; n >0: residual times
+ */
+void lv_timer_set_repeat_count(lv_timer_t * timer, int32_t repeat_count)
+{
+ timer->repeat_count = repeat_count;
+}
+
+/**
+ * Reset a lv_timer.
+ * It will be called the previously set period milliseconds later.
+ * @param timer pointer to a lv_timer.
+ */
+void lv_timer_reset(lv_timer_t * timer)
+{
+ timer->last_run = lv_tick_get();
+}
+
+/**
+ * Enable or disable the whole lv_timer handling
+ * @param en true: lv_timer handling is running, false: lv_timer handling is suspended
+ */
+void lv_timer_enable(bool en)
+{
+ lv_timer_run = en;
+}
+
+/**
+ * Get idle percentage
+ * @return the lv_timer idle in percentage
+ */
+uint8_t lv_timer_get_idle(void)
+{
+ return idle_last;
+}
+
+/**
+ * Iterate through the timers
+ * @param timer NULL to start iteration or the previous return value to get the next timer
+ * @return the next timer or NULL if there is no more timer
+ */
+lv_timer_t * lv_timer_get_next(lv_timer_t * timer)
+{
+ if(timer == NULL) return _lv_ll_get_head(&LV_GC_ROOT(_lv_timer_ll));
+ else return _lv_ll_get_next(&LV_GC_ROOT(_lv_timer_ll), timer);
+}
+
+/**********************
+ * STATIC FUNCTIONS
+ **********************/
+
+/**
+ * Execute timer if its remaining time is zero
+ * @param timer pointer to lv_timer
+ * @return true: execute, false: not executed
+ */
+static bool lv_timer_exec(lv_timer_t * timer)
+{
+ if(timer->paused) return false;
+
+ bool exec = false;
+ if(lv_timer_time_remaining(timer) == 0) {
+ /* Decrement the repeat count before executing the timer_cb.
+ * If any timer is deleted `if(timer->repeat_count == 0)` is not executed below
+ * but at least the repeat count is zero and the timer can be deleted in the next round*/
+ int32_t original_repeat_count = timer->repeat_count;
+ if(timer->repeat_count > 0) timer->repeat_count--;
+ timer->last_run = lv_tick_get();
+ TIMER_TRACE("calling timer callback: %p", *((void **)&timer->timer_cb));
+ if(timer->timer_cb && original_repeat_count != 0) timer->timer_cb(timer);
+ TIMER_TRACE("timer callback %p finished", *((void **)&timer->timer_cb));
+ LV_ASSERT_MEM_INTEGRITY();
+ exec = true;
+ }
+
+ if(timer_deleted == false) { /*The timer might be deleted by itself as well*/
+ if(timer->repeat_count == 0) { /*The repeat count is over, delete the timer*/
+ TIMER_TRACE("deleting timer with %p callback because the repeat count is over", *((void **)&timer->timer_cb));
+ lv_timer_del(timer);
+ }
+ }
+
+ return exec;
+}
+
+/**
+ * Find out how much time remains before a timer must be run.
+ * @param timer pointer to lv_timer
+ * @return the time remaining, or 0 if it needs to be run again
+ */
+static uint32_t lv_timer_time_remaining(lv_timer_t * timer)
+{
+ /*Check if at least 'period' time elapsed*/
+ uint32_t elp = lv_tick_elaps(timer->last_run);
+ if(elp >= timer->period)
+ return 0;
+ return timer->period - elp;
+}
diff --git a/lib/lvgl/src/misc/lv_timer.h b/lib/lvgl/src/misc/lv_timer.h
new file mode 100644
index 00000000..a9a8e50d
--- /dev/null
+++ b/lib/lvgl/src/misc/lv_timer.h
@@ -0,0 +1,183 @@
+/**
+ * @file lv_timer.h
+ */
+
+#ifndef LV_TIMER_H
+#define LV_TIMER_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/*********************
+ * INCLUDES
+ *********************/
+#include "../lv_conf_internal.h"
+#include "../hal/lv_hal_tick.h"
+
+#include <stdint.h>
+#include <stdbool.h>
+
+/*********************
+ * DEFINES
+ *********************/
+#ifndef LV_ATTRIBUTE_TIMER_HANDLER
+#define LV_ATTRIBUTE_TIMER_HANDLER
+#endif
+
+#define LV_NO_TIMER_READY 0xFFFFFFFF
+
+/**********************
+ * TYPEDEFS
+ **********************/
+
+struct _lv_timer_t;
+
+/**
+ * Timers execute this type of functions.
+ */
+typedef void (*lv_timer_cb_t)(struct _lv_timer_t *);
+
+/**
+ * Descriptor of a lv_timer
+ */
+typedef struct _lv_timer_t {
+ uint32_t period; /**< How often the timer should run*/
+ uint32_t last_run; /**< Last time the timer ran*/
+ lv_timer_cb_t timer_cb; /**< Timer function*/
+ void * user_data; /**< Custom user data*/
+ int32_t repeat_count; /**< 1: One time; -1 : infinity; n>0: residual times*/
+ uint32_t paused : 1;
+} lv_timer_t;
+
+/**********************
+ * GLOBAL PROTOTYPES
+ **********************/
+
+/**
+ * Init the lv_timer module
+ */
+void _lv_timer_core_init(void);
+
+//! @cond Doxygen_Suppress
+
+/**
+ * Call it periodically to handle lv_timers.
+ * @return time till it needs to be run next (in ms)
+ */
+LV_ATTRIBUTE_TIMER_HANDLER uint32_t lv_timer_handler(void);
+
+//! @endcond
+
+/**
+ * Call it in the super-loop of main() or threads. It will run lv_timer_handler()
+ * with a given period in ms. You can use it with sleep or delay in OS environment.
+ * This function is used to simplify the porting.
+ * @param __ms the period for running lv_timer_handler()
+ */
+static inline LV_ATTRIBUTE_TIMER_HANDLER uint32_t lv_timer_handler_run_in_period(uint32_t ms)
+{
+ static uint32_t last_tick = 0;
+ uint32_t curr_tick = lv_tick_get();
+
+ if((curr_tick - last_tick) >= (ms)) {
+ last_tick = curr_tick;
+ return lv_timer_handler();
+ }
+ return 1;
+}
+
+/**
+ * Create an "empty" timer. It needs to initialized with at least
+ * `lv_timer_set_cb` and `lv_timer_set_period`
+ * @return pointer to the created timer
+ */
+lv_timer_t * lv_timer_create_basic(void);
+
+/**
+ * Create a new lv_timer
+ * @param timer_xcb a callback to call periodically.
+ * (the 'x' in the argument name indicates that it's not a fully generic function because it not follows
+ * the `func_name(object, callback, ...)` convention)
+ * @param period call period in ms unit
+ * @param user_data custom parameter
+ * @return pointer to the new timer
+ */
+lv_timer_t * lv_timer_create(lv_timer_cb_t timer_xcb, uint32_t period, void * user_data);
+
+/**
+ * Delete a lv_timer
+ * @param timer pointer to an lv_timer
+ */
+void lv_timer_del(lv_timer_t * timer);
+
+/**
+ * Pause/resume a timer.
+ * @param timer pointer to an lv_timer
+ */
+void lv_timer_pause(lv_timer_t * timer);
+
+void lv_timer_resume(lv_timer_t * timer);
+
+/**
+ * Set the callback the timer (the function to call periodically)
+ * @param timer pointer to a timer
+ * @param timer_cb the function to call periodically
+ */
+void lv_timer_set_cb(lv_timer_t * timer, lv_timer_cb_t timer_cb);
+
+/**
+ * Set new period for a lv_timer
+ * @param timer pointer to a lv_timer
+ * @param period the new period
+ */
+void lv_timer_set_period(lv_timer_t * timer, uint32_t period);
+
+/**
+ * Make a lv_timer ready. It will not wait its period.
+ * @param timer pointer to a lv_timer.
+ */
+void lv_timer_ready(lv_timer_t * timer);
+
+/**
+ * Set the number of times a timer will repeat.
+ * @param timer pointer to a lv_timer.
+ * @param repeat_count -1 : infinity; 0 : stop ; n>0: residual times
+ */
+void lv_timer_set_repeat_count(lv_timer_t * timer, int32_t repeat_count);
+
+/**
+ * Reset a lv_timer.
+ * It will be called the previously set period milliseconds later.
+ * @param timer pointer to a lv_timer.
+ */
+void lv_timer_reset(lv_timer_t * timer);
+
+/**
+ * Enable or disable the whole lv_timer handling
+ * @param en true: lv_timer handling is running, false: lv_timer handling is suspended
+ */
+void lv_timer_enable(bool en);
+
+/**
+ * Get idle percentage
+ * @return the lv_timer idle in percentage
+ */
+uint8_t lv_timer_get_idle(void);
+
+/**
+ * Iterate through the timers
+ * @param timer NULL to start iteration or the previous return value to get the next timer
+ * @return the next timer or NULL if there is no more timer
+ */
+lv_timer_t * lv_timer_get_next(lv_timer_t * timer);
+
+/**********************
+ * MACROS
+ **********************/
+
+#ifdef __cplusplus
+} /*extern "C"*/
+#endif
+
+#endif
diff --git a/lib/lvgl/src/misc/lv_tlsf.c b/lib/lvgl/src/misc/lv_tlsf.c
new file mode 100644
index 00000000..27e0a46c
--- /dev/null
+++ b/lib/lvgl/src/misc/lv_tlsf.c
@@ -0,0 +1,1246 @@
+#include "../lv_conf_internal.h"
+#if LV_MEM_CUSTOM == 0
+
+#include <limits.h>
+#include "lv_tlsf.h"
+#include "lv_mem.h"
+#include "lv_log.h"
+#include "lv_assert.h"
+
+#undef printf
+#define printf LV_LOG_ERROR
+
+#define TLSF_MAX_POOL_SIZE LV_MEM_SIZE
+
+#if !defined(_DEBUG)
+ #define _DEBUG 0
+#endif
+
+#if defined(__cplusplus)
+ #define tlsf_decl inline
+#else
+ #define tlsf_decl static
+#endif
+
+/*
+** Architecture-specific bit manipulation routines.
+**
+** TLSF achieves O(1) cost for malloc and free operations by limiting
+** the search for a free block to a free list of guaranteed size
+** adequate to fulfill the request, combined with efficient free list
+** queries using bitmasks and architecture-specific bit-manipulation
+** routines.
+**
+** Most modern processors provide instructions to count leading zeroes
+** in a word, find the lowest and highest set bit, etc. These
+** specific implementations will be used when available, falling back
+** to a reasonably efficient generic implementation.
+**
+** NOTE: TLSF spec relies on ffs/fls returning value 0..31.
+** ffs/fls return 1-32 by default, returning 0 for error.
+*/
+
+/*
+** Detect whether or not we are building for a 32- or 64-bit (LP/LLP)
+** architecture. There is no reliable portable method at compile-time.
+*/
+#if defined (__alpha__) || defined (__ia64__) || defined (__x86_64__) \
+ || defined (_WIN64) || defined (__LP64__) || defined (__LLP64__)
+ #define TLSF_64BIT
+#endif
+
+/*
+** Returns one plus the index of the most significant 1-bit of n,
+** or if n is zero, returns zero.
+*/
+#ifdef TLSF_64BIT
+ #define TLSF_FLS(n) ((n) & 0xffffffff00000000ull ? 32 + TLSF_FLS32((size_t)(n) >> 32) : TLSF_FLS32(n))
+#else
+ #define TLSF_FLS(n) TLSF_FLS32(n)
+#endif
+
+#define TLSF_FLS32(n) ((n) & 0xffff0000 ? 16 + TLSF_FLS16((n) >> 16) : TLSF_FLS16(n))
+#define TLSF_FLS16(n) ((n) & 0xff00 ? 8 + TLSF_FLS8 ((n) >> 8) : TLSF_FLS8 (n))
+#define TLSF_FLS8(n) ((n) & 0xf0 ? 4 + TLSF_FLS4 ((n) >> 4) : TLSF_FLS4 (n))
+#define TLSF_FLS4(n) ((n) & 0xc ? 2 + TLSF_FLS2 ((n) >> 2) : TLSF_FLS2 (n))
+#define TLSF_FLS2(n) ((n) & 0x2 ? 1 + TLSF_FLS1 ((n) >> 1) : TLSF_FLS1 (n))
+#define TLSF_FLS1(n) ((n) & 0x1 ? 1 : 0)
+
+/*
+** Returns round up value of log2(n).
+** Note: it is used at compile time.
+*/
+#define TLSF_LOG2_CEIL(n) ((n) & (n - 1) ? TLSF_FLS(n) : TLSF_FLS(n) - 1)
+
+/*
+** gcc 3.4 and above have builtin support, specialized for architecture.
+** Some compilers masquerade as gcc; patchlevel test filters them out.
+*/
+#if defined (__GNUC__) && (__GNUC__ > 3 || (__GNUC__ == 3 && __GNUC_MINOR__ >= 4)) \
+ && defined (__GNUC_PATCHLEVEL__)
+
+#if defined (__SNC__)
+/* SNC for Playstation 3. */
+
+tlsf_decl int tlsf_ffs(unsigned int word)
+{
+ const unsigned int reverse = word & (~word + 1);
+ const int bit = 32 - __builtin_clz(reverse);
+ return bit - 1;
+}
+
+#else
+
+tlsf_decl int tlsf_ffs(unsigned int word)
+{
+ return __builtin_ffs(word) - 1;
+}
+
+#endif
+
+tlsf_decl int tlsf_fls(unsigned int word)
+{
+ const int bit = word ? 32 - __builtin_clz(word) : 0;
+ return bit - 1;
+}
+
+#elif defined (_MSC_VER) && (_MSC_VER >= 1400) && (defined (_M_IX86) || defined (_M_X64))
+/* Microsoft Visual C++ support on x86/X64 architectures. */
+
+#include <intrin.h>
+
+#pragma intrinsic(_BitScanReverse)
+#pragma intrinsic(_BitScanForward)
+
+tlsf_decl int tlsf_fls(unsigned int word)
+{
+ unsigned long index;
+ return _BitScanReverse(&index, word) ? index : -1;
+}
+
+tlsf_decl int tlsf_ffs(unsigned int word)
+{
+ unsigned long index;
+ return _BitScanForward(&index, word) ? index : -1;
+}
+
+#elif defined (_MSC_VER) && defined (_M_PPC)
+/* Microsoft Visual C++ support on PowerPC architectures. */
+
+#include <ppcintrinsics.h>
+
+tlsf_decl int tlsf_fls(unsigned int word)
+{
+ const int bit = 32 - _CountLeadingZeros(word);
+ return bit - 1;
+}
+
+tlsf_decl int tlsf_ffs(unsigned int word)
+{
+ const unsigned int reverse = word & (~word + 1);
+ const int bit = 32 - _CountLeadingZeros(reverse);
+ return bit - 1;
+}
+
+#elif defined (__ARMCC_VERSION)
+/* RealView Compilation Tools for ARM */
+
+tlsf_decl int tlsf_ffs(unsigned int word)
+{
+ const unsigned int reverse = word & (~word + 1);
+ const int bit = 32 - __clz(reverse);
+ return bit - 1;
+}
+
+tlsf_decl int tlsf_fls(unsigned int word)
+{
+ const int bit = word ? 32 - __clz(word) : 0;
+ return bit - 1;
+}
+
+#elif defined (__ghs__)
+/* Green Hills support for PowerPC */
+
+#include <ppc_ghs.h>
+
+tlsf_decl int tlsf_ffs(unsigned int word)
+{
+ const unsigned int reverse = word & (~word + 1);
+ const int bit = 32 - __CLZ32(reverse);
+ return bit - 1;
+}
+
+tlsf_decl int tlsf_fls(unsigned int word)
+{
+ const int bit = word ? 32 - __CLZ32(word) : 0;
+ return bit - 1;
+}
+
+#else
+/* Fall back to generic implementation. */
+
+/* Implement ffs in terms of fls. */
+tlsf_decl int tlsf_ffs(unsigned int word)
+{
+ const unsigned int reverse = word & (~word + 1);
+ return TLSF_FLS32(reverse) - 1;
+}
+
+tlsf_decl int tlsf_fls(unsigned int word)
+{
+ return TLSF_FLS32(word) - 1;
+}
+
+#endif
+
+/* Possibly 64-bit version of tlsf_fls. */
+#if defined (TLSF_64BIT)
+tlsf_decl int tlsf_fls_sizet(size_t size)
+{
+ int high = (int)(size >> 32);
+ int bits = 0;
+ if(high) {
+ bits = 32 + tlsf_fls(high);
+ }
+ else {
+ bits = tlsf_fls((int)size & 0xffffffff);
+
+ }
+ return bits;
+}
+#else
+#define tlsf_fls_sizet tlsf_fls
+#endif
+
+#undef tlsf_decl
+
+/*
+** Constants.
+*/
+
+/* Public constants: may be modified. */
+enum tlsf_public {
+ /* log2 of number of linear subdivisions of block sizes. Larger
+ ** values require more memory in the control structure. Values of
+ ** 4 or 5 are typical.
+ */
+ SL_INDEX_COUNT_LOG2 = 5,
+};
+
+/* Private constants: do not modify. */
+enum tlsf_private {
+#if defined (TLSF_64BIT)
+ /* All allocation sizes and addresses are aligned to 8 bytes. */
+ ALIGN_SIZE_LOG2 = 3,
+#else
+ /* All allocation sizes and addresses are aligned to 4 bytes. */
+ ALIGN_SIZE_LOG2 = 2,
+#endif
+ ALIGN_SIZE = (1 << ALIGN_SIZE_LOG2),
+
+ /*
+ ** We support allocations of sizes up to (1 << FL_INDEX_MAX) bits.
+ ** However, because we linearly subdivide the second-level lists, and
+ ** our minimum size granularity is 4 bytes, it doesn't make sense to
+ ** create first-level lists for sizes smaller than SL_INDEX_COUNT * 4,
+ ** or (1 << (SL_INDEX_COUNT_LOG2 + 2)) bytes, as there we will be
+ ** trying to split size ranges into more slots than we have available.
+ ** Instead, we calculate the minimum threshold size, and place all
+ ** blocks below that size into the 0th first-level list.
+ */
+
+#if defined (TLSF_MAX_POOL_SIZE)
+ FL_INDEX_MAX = TLSF_LOG2_CEIL(TLSF_MAX_POOL_SIZE),
+#elif defined (TLSF_64BIT)
+ /*
+ ** TODO: We can increase this to support larger sizes, at the expense
+ ** of more overhead in the TLSF structure.
+ */
+ FL_INDEX_MAX = 32,
+#else
+ FL_INDEX_MAX = 30,
+#endif
+ SL_INDEX_COUNT = (1 << SL_INDEX_COUNT_LOG2),
+ FL_INDEX_SHIFT = (SL_INDEX_COUNT_LOG2 + ALIGN_SIZE_LOG2),
+ FL_INDEX_COUNT = (FL_INDEX_MAX - FL_INDEX_SHIFT + 1),
+
+ SMALL_BLOCK_SIZE = (1 << FL_INDEX_SHIFT),
+};
+
+/*
+** Cast and min/max macros.
+*/
+
+#define tlsf_cast(t, exp) ((t) (exp))
+#define tlsf_min(a, b) ((a) < (b) ? (a) : (b))
+#define tlsf_max(a, b) ((a) > (b) ? (a) : (b))
+
+/*
+** Set assert macro, if it has not been provided by the user.
+*/
+#define tlsf_assert LV_ASSERT
+
+#if !defined (tlsf_assert)
+ #define tlsf_assert assert
+#endif
+
+/*
+** Static assertion mechanism.
+*/
+
+#define _tlsf_glue2(x, y) x ## y
+#define _tlsf_glue(x, y) _tlsf_glue2(x, y)
+#define tlsf_static_assert(exp) \
+ typedef char _tlsf_glue(static_assert, __LINE__) [(exp) ? 1 : -1]
+
+/* This code has been tested on 32- and 64-bit (LP/LLP) architectures. */
+tlsf_static_assert(sizeof(int) * CHAR_BIT == 32);
+tlsf_static_assert(sizeof(size_t) * CHAR_BIT >= 32);
+tlsf_static_assert(sizeof(size_t) * CHAR_BIT <= 64);
+
+/* SL_INDEX_COUNT must be <= number of bits in sl_bitmap's storage type. */
+tlsf_static_assert(sizeof(unsigned int) * CHAR_BIT >= SL_INDEX_COUNT);
+
+/* Ensure we've properly tuned our sizes. */
+tlsf_static_assert(ALIGN_SIZE == SMALL_BLOCK_SIZE / SL_INDEX_COUNT);
+
+/*
+** Data structures and associated constants.
+*/
+
+/*
+** Block header structure.
+**
+** There are several implementation subtleties involved:
+** - The prev_phys_block field is only valid if the previous block is free.
+** - The prev_phys_block field is actually stored at the end of the
+** previous block. It appears at the beginning of this structure only to
+** simplify the implementation.
+** - The next_free / prev_free fields are only valid if the block is free.
+*/
+typedef struct block_header_t {
+ /* Points to the previous physical block. */
+ struct block_header_t * prev_phys_block;
+
+ /* The size of this block, excluding the block header. */
+ size_t size;
+
+ /* Next and previous free blocks. */
+ struct block_header_t * next_free;
+ struct block_header_t * prev_free;
+} block_header_t;
+
+/*
+** Since block sizes are always at least a multiple of 4, the two least
+** significant bits of the size field are used to store the block status:
+** - bit 0: whether block is busy or free
+** - bit 1: whether previous block is busy or free
+*/
+static const size_t block_header_free_bit = 1 << 0;
+static const size_t block_header_prev_free_bit = 1 << 1;
+
+/*
+** The size of the block header exposed to used blocks is the size field.
+** The prev_phys_block field is stored *inside* the previous free block.
+*/
+static const size_t block_header_overhead = sizeof(size_t);
+
+/* User data starts directly after the size field in a used block. */
+static const size_t block_start_offset =
+ offsetof(block_header_t, size) + sizeof(size_t);
+
+/*
+** A free block must be large enough to store its header minus the size of
+** the prev_phys_block field, and no larger than the number of addressable
+** bits for FL_INDEX.
+*/
+static const size_t block_size_min =
+ sizeof(block_header_t) - sizeof(block_header_t *);
+static const size_t block_size_max = tlsf_cast(size_t, 1) << FL_INDEX_MAX;
+
+
+/* The TLSF control structure. */
+typedef struct control_t {
+ /* Empty lists point at this block to indicate they are free. */
+ block_header_t block_null;
+
+ /* Bitmaps for free lists. */
+ unsigned int fl_bitmap;
+ unsigned int sl_bitmap[FL_INDEX_COUNT];
+
+ /* Head of free lists. */
+ block_header_t * blocks[FL_INDEX_COUNT][SL_INDEX_COUNT];
+} control_t;
+
+/* A type used for casting when doing pointer arithmetic. */
+typedef ptrdiff_t tlsfptr_t;
+
+/*
+** block_header_t member functions.
+*/
+
+static size_t block_size(const block_header_t * block)
+{
+ return block->size & ~(block_header_free_bit | block_header_prev_free_bit);
+}
+
+static void block_set_size(block_header_t * block, size_t size)
+{
+ const size_t oldsize = block->size;
+ block->size = size | (oldsize & (block_header_free_bit | block_header_prev_free_bit));
+}
+
+static int block_is_last(const block_header_t * block)
+{
+ return block_size(block) == 0;
+}
+
+static int block_is_free(const block_header_t * block)
+{
+ return tlsf_cast(int, block->size & block_header_free_bit);
+}
+
+static void block_set_free(block_header_t * block)
+{
+ block->size |= block_header_free_bit;
+}
+
+static void block_set_used(block_header_t * block)
+{
+ block->size &= ~block_header_free_bit;
+}
+
+static int block_is_prev_free(const block_header_t * block)
+{
+ return tlsf_cast(int, block->size & block_header_prev_free_bit);
+}
+
+static void block_set_prev_free(block_header_t * block)
+{
+ block->size |= block_header_prev_free_bit;
+}
+
+static void block_set_prev_used(block_header_t * block)
+{
+ block->size &= ~block_header_prev_free_bit;
+}
+
+static block_header_t * block_from_ptr(const void * ptr)
+{
+ return tlsf_cast(block_header_t *,
+ tlsf_cast(unsigned char *, ptr) - block_start_offset);
+}
+
+static void * block_to_ptr(const block_header_t * block)
+{
+ return tlsf_cast(void *,
+ tlsf_cast(unsigned char *, block) + block_start_offset);
+}
+
+/* Return location of next block after block of given size. */
+static block_header_t * offset_to_block(const void * ptr, size_t size)
+{
+ return tlsf_cast(block_header_t *, tlsf_cast(tlsfptr_t, ptr) + size);
+}
+
+/* Return location of previous block. */
+static block_header_t * block_prev(const block_header_t * block)
+{
+ tlsf_assert(block_is_prev_free(block) && "previous block must be free");
+ return block->prev_phys_block;
+}
+
+/* Return location of next existing block. */
+static block_header_t * block_next(const block_header_t * block)
+{
+ block_header_t * next = offset_to_block(block_to_ptr(block),
+ block_size(block) - block_header_overhead);
+ tlsf_assert(!block_is_last(block));
+ return next;
+}
+
+/* Link a new block with its physical neighbor, return the neighbor. */
+static block_header_t * block_link_next(block_header_t * block)
+{
+ block_header_t * next = block_next(block);
+ next->prev_phys_block = block;
+ return next;
+}
+
+static void block_mark_as_free(block_header_t * block)
+{
+ /* Link the block to the next block, first. */
+ block_header_t * next = block_link_next(block);
+ block_set_prev_free(next);
+ block_set_free(block);
+}
+
+static void block_mark_as_used(block_header_t * block)
+{
+ block_header_t * next = block_next(block);
+ block_set_prev_used(next);
+ block_set_used(block);
+}
+
+static size_t align_up(size_t x, size_t align)
+{
+ tlsf_assert(0 == (align & (align - 1)) && "must align to a power of two");
+ return (x + (align - 1)) & ~(align - 1);
+}
+
+static size_t align_down(size_t x, size_t align)
+{
+ tlsf_assert(0 == (align & (align - 1)) && "must align to a power of two");
+ return x - (x & (align - 1));
+}
+
+static void * align_ptr(const void * ptr, size_t align)
+{
+ const tlsfptr_t aligned =
+ (tlsf_cast(tlsfptr_t, ptr) + (align - 1)) & ~(align - 1);
+ tlsf_assert(0 == (align & (align - 1)) && "must align to a power of two");
+ return tlsf_cast(void *, aligned);
+}
+
+/*
+** Adjust an allocation size to be aligned to word size, and no smaller
+** than internal minimum.
+*/
+static size_t adjust_request_size(size_t size, size_t align)
+{
+ size_t adjust = 0;
+ if(size) {
+ const size_t aligned = align_up(size, align);
+
+ /* aligned sized must not exceed block_size_max or we'll go out of bounds on sl_bitmap */
+ if(aligned < block_size_max) {
+ adjust = tlsf_max(aligned, block_size_min);
+ }
+ }
+ return adjust;
+}
+
+/*
+** TLSF utility functions. In most cases, these are direct translations of
+** the documentation found in the white paper.
+*/
+
+static void mapping_insert(size_t size, int * fli, int * sli)
+{
+ int fl, sl;
+ if(size < SMALL_BLOCK_SIZE) {
+ /* Store small blocks in first list. */
+ fl = 0;
+ sl = tlsf_cast(int, size) / (SMALL_BLOCK_SIZE / SL_INDEX_COUNT);
+ }
+ else {
+ fl = tlsf_fls_sizet(size);
+ sl = tlsf_cast(int, size >> (fl - SL_INDEX_COUNT_LOG2)) ^ (1 << SL_INDEX_COUNT_LOG2);
+ fl -= (FL_INDEX_SHIFT - 1);
+ }
+ *fli = fl;
+ *sli = sl;
+}
+
+/* This version rounds up to the next block size (for allocations) */
+static void mapping_search(size_t size, int * fli, int * sli)
+{
+ if(size >= SMALL_BLOCK_SIZE) {
+ const size_t round = (1 << (tlsf_fls_sizet(size) - SL_INDEX_COUNT_LOG2)) - 1;
+ size += round;
+ }
+ mapping_insert(size, fli, sli);
+}
+
+static block_header_t * search_suitable_block(control_t * control, int * fli, int * sli)
+{
+ int fl = *fli;
+ int sl = *sli;
+
+ /*
+ ** First, search for a block in the list associated with the given
+ ** fl/sl index.
+ */
+ unsigned int sl_map = control->sl_bitmap[fl] & (~0U << sl);
+ if(!sl_map) {
+ /* No block exists. Search in the next largest first-level list. */
+ const unsigned int fl_map = control->fl_bitmap & (~0U << (fl + 1));
+ if(!fl_map) {
+ /* No free blocks available, memory has been exhausted. */
+ return 0;
+ }
+
+ fl = tlsf_ffs(fl_map);
+ *fli = fl;
+ sl_map = control->sl_bitmap[fl];
+ }
+ tlsf_assert(sl_map && "internal error - second level bitmap is null");
+ sl = tlsf_ffs(sl_map);
+ *sli = sl;
+
+ /* Return the first block in the free list. */
+ return control->blocks[fl][sl];
+}
+
+/* Remove a free block from the free list.*/
+static void remove_free_block(control_t * control, block_header_t * block, int fl, int sl)
+{
+ block_header_t * prev = block->prev_free;
+ block_header_t * next = block->next_free;
+ tlsf_assert(prev && "prev_free field can not be null");
+ tlsf_assert(next && "next_free field can not be null");
+ next->prev_free = prev;
+ prev->next_free = next;
+
+ /* If this block is the head of the free list, set new head. */
+ if(control->blocks[fl][sl] == block) {
+ control->blocks[fl][sl] = next;
+
+ /* If the new head is null, clear the bitmap. */
+ if(next == &control->block_null) {
+ control->sl_bitmap[fl] &= ~(1U << sl);
+
+ /* If the second bitmap is now empty, clear the fl bitmap. */
+ if(!control->sl_bitmap[fl]) {
+ control->fl_bitmap &= ~(1U << fl);
+ }
+ }
+ }
+}
+
+/* Insert a free block into the free block list. */
+static void insert_free_block(control_t * control, block_header_t * block, int fl, int sl)
+{
+ block_header_t * current = control->blocks[fl][sl];
+ tlsf_assert(current && "free list cannot have a null entry");
+ tlsf_assert(block && "cannot insert a null entry into the free list");
+ block->next_free = current;
+ block->prev_free = &control->block_null;
+ current->prev_free = block;
+
+ tlsf_assert(block_to_ptr(block) == align_ptr(block_to_ptr(block), ALIGN_SIZE)
+ && "block not aligned properly");
+ /*
+ ** Insert the new block at the head of the list, and mark the first-
+ ** and second-level bitmaps appropriately.
+ */
+ control->blocks[fl][sl] = block;
+ control->fl_bitmap |= (1U << fl);
+ control->sl_bitmap[fl] |= (1U << sl);
+}
+
+/* Remove a given block from the free list. */
+static void block_remove(control_t * control, block_header_t * block)
+{
+ int fl, sl;
+ mapping_insert(block_size(block), &fl, &sl);
+ remove_free_block(control, block, fl, sl);
+}
+
+/* Insert a given block into the free list. */
+static void block_insert(control_t * control, block_header_t * block)
+{
+ int fl, sl;
+ mapping_insert(block_size(block), &fl, &sl);
+ insert_free_block(control, block, fl, sl);
+}
+
+static int block_can_split(block_header_t * block, size_t size)
+{
+ return block_size(block) >= sizeof(block_header_t) + size;
+}
+
+/* Split a block into two, the second of which is free. */
+static block_header_t * block_split(block_header_t * block, size_t size)
+{
+ /* Calculate the amount of space left in the remaining block. */
+ block_header_t * remaining =
+ offset_to_block(block_to_ptr(block), size - block_header_overhead);
+
+ const size_t remain_size = block_size(block) - (size + block_header_overhead);
+
+ tlsf_assert(block_to_ptr(remaining) == align_ptr(block_to_ptr(remaining), ALIGN_SIZE)
+ && "remaining block not aligned properly");
+
+ tlsf_assert(block_size(block) == remain_size + size + block_header_overhead);
+ block_set_size(remaining, remain_size);
+ tlsf_assert(block_size(remaining) >= block_size_min && "block split with invalid size");
+
+ block_set_size(block, size);
+ block_mark_as_free(remaining);
+
+ return remaining;
+}
+
+/* Absorb a free block's storage into an adjacent previous free block. */
+static block_header_t * block_absorb(block_header_t * prev, block_header_t * block)
+{
+ tlsf_assert(!block_is_last(prev) && "previous block can't be last");
+ /* Note: Leaves flags untouched. */
+ prev->size += block_size(block) + block_header_overhead;
+ block_link_next(prev);
+ return prev;
+}
+
+/* Merge a just-freed block with an adjacent previous free block. */
+static block_header_t * block_merge_prev(control_t * control, block_header_t * block)
+{
+ if(block_is_prev_free(block)) {
+ block_header_t * prev = block_prev(block);
+ tlsf_assert(prev && "prev physical block can't be null");
+ tlsf_assert(block_is_free(prev) && "prev block is not free though marked as such");
+ block_remove(control, prev);
+ block = block_absorb(prev, block);
+ }
+
+ return block;
+}
+
+/* Merge a just-freed block with an adjacent free block. */
+static block_header_t * block_merge_next(control_t * control, block_header_t * block)
+{
+ block_header_t * next = block_next(block);
+ tlsf_assert(next && "next physical block can't be null");
+
+ if(block_is_free(next)) {
+ tlsf_assert(!block_is_last(block) && "previous block can't be last");
+ block_remove(control, next);
+ block = block_absorb(block, next);
+ }
+
+ return block;
+}
+
+/* Trim any trailing block space off the end of a block, return to pool. */
+static void block_trim_free(control_t * control, block_header_t * block, size_t size)
+{
+ tlsf_assert(block_is_free(block) && "block must be free");
+ if(block_can_split(block, size)) {
+ block_header_t * remaining_block = block_split(block, size);
+ block_link_next(block);
+ block_set_prev_free(remaining_block);
+ block_insert(control, remaining_block);
+ }
+}
+
+/* Trim any trailing block space off the end of a used block, return to pool. */
+static void block_trim_used(control_t * control, block_header_t * block, size_t size)
+{
+ tlsf_assert(!block_is_free(block) && "block must be used");
+ if(block_can_split(block, size)) {
+ /* If the next block is free, we must coalesce. */
+ block_header_t * remaining_block = block_split(block, size);
+ block_set_prev_used(remaining_block);
+
+ remaining_block = block_merge_next(control, remaining_block);
+ block_insert(control, remaining_block);
+ }
+}
+
+static block_header_t * block_trim_free_leading(control_t * control, block_header_t * block, size_t size)
+{
+ block_header_t * remaining_block = block;
+ if(block_can_split(block, size)) {
+ /* We want the 2nd block. */
+ remaining_block = block_split(block, size - block_header_overhead);
+ block_set_prev_free(remaining_block);
+
+ block_link_next(block);
+ block_insert(control, block);
+ }
+
+ return remaining_block;
+}
+
+static block_header_t * block_locate_free(control_t * control, size_t size)
+{
+ int fl = 0, sl = 0;
+ block_header_t * block = 0;
+
+ if(size) {
+ mapping_search(size, &fl, &sl);
+
+ /*
+ ** mapping_search can futz with the size, so for excessively large sizes it can sometimes wind up
+ ** with indices that are off the end of the block array.
+ ** So, we protect against that here, since this is the only callsite of mapping_search.
+ ** Note that we don't need to check sl, since it comes from a modulo operation that guarantees it's always in range.
+ */
+ if(fl < FL_INDEX_COUNT) {
+ block = search_suitable_block(control, &fl, &sl);
+ }
+ }
+
+ if(block) {
+ tlsf_assert(block_size(block) >= size);
+ remove_free_block(control, block, fl, sl);
+ }
+
+ return block;
+}
+
+static void * block_prepare_used(control_t * control, block_header_t * block, size_t size)
+{
+ void * p = 0;
+ if(block) {
+ tlsf_assert(size && "size must be non-zero");
+ block_trim_free(control, block, size);
+ block_mark_as_used(block);
+ p = block_to_ptr(block);
+ }
+ return p;
+}
+
+/* Clear structure and point all empty lists at the null block. */
+static void control_constructor(control_t * control)
+{
+ int i, j;
+
+ control->block_null.next_free = &control->block_null;
+ control->block_null.prev_free = &control->block_null;
+
+ control->fl_bitmap = 0;
+ for(i = 0; i < FL_INDEX_COUNT; ++i) {
+ control->sl_bitmap[i] = 0;
+ for(j = 0; j < SL_INDEX_COUNT; ++j) {
+ control->blocks[i][j] = &control->block_null;
+ }
+ }
+}
+
+/*
+** Debugging utilities.
+*/
+
+typedef struct integrity_t {
+ int prev_status;
+ int status;
+} integrity_t;
+
+#define tlsf_insist(x) { tlsf_assert(x); if (!(x)) { status--; } }
+
+static void integrity_walker(void * ptr, size_t size, int used, void * user)
+{
+ block_header_t * block = block_from_ptr(ptr);
+ integrity_t * integ = tlsf_cast(integrity_t *, user);
+ const int this_prev_status = block_is_prev_free(block) ? 1 : 0;
+ const int this_status = block_is_free(block) ? 1 : 0;
+ const size_t this_block_size = block_size(block);
+
+ int status = 0;
+ LV_UNUSED(used);
+ tlsf_insist(integ->prev_status == this_prev_status && "prev status incorrect");
+ tlsf_insist(size == this_block_size && "block size incorrect");
+
+ integ->prev_status = this_status;
+ integ->status += status;
+}
+
+int lv_tlsf_check(lv_tlsf_t tlsf)
+{
+ int i, j;
+
+ control_t * control = tlsf_cast(control_t *, tlsf);
+ int status = 0;
+
+ /* Check that the free lists and bitmaps are accurate. */
+ for(i = 0; i < FL_INDEX_COUNT; ++i) {
+ for(j = 0; j < SL_INDEX_COUNT; ++j) {
+ const int fl_map = control->fl_bitmap & (1U << i);
+ const int sl_list = control->sl_bitmap[i];
+ const int sl_map = sl_list & (1U << j);
+ const block_header_t * block = control->blocks[i][j];
+
+ /* Check that first- and second-level lists agree. */
+ if(!fl_map) {
+ tlsf_insist(!sl_map && "second-level map must be null");
+ }
+
+ if(!sl_map) {
+ tlsf_insist(block == &control->block_null && "block list must be null");
+ continue;
+ }
+
+ /* Check that there is at least one free block. */
+ tlsf_insist(sl_list && "no free blocks in second-level map");
+ tlsf_insist(block != &control->block_null && "block should not be null");
+
+ while(block != &control->block_null) {
+ int fli, sli;
+ tlsf_insist(block_is_free(block) && "block should be free");
+ tlsf_insist(!block_is_prev_free(block) && "blocks should have coalesced");
+ tlsf_insist(!block_is_free(block_next(block)) && "blocks should have coalesced");
+ tlsf_insist(block_is_prev_free(block_next(block)) && "block should be free");
+ tlsf_insist(block_size(block) >= block_size_min && "block not minimum size");
+
+ mapping_insert(block_size(block), &fli, &sli);
+ tlsf_insist(fli == i && sli == j && "block size indexed in wrong list");
+ block = block->next_free;
+ }
+ }
+ }
+
+ return status;
+}
+
+#undef tlsf_insist
+
+static void default_walker(void * ptr, size_t size, int used, void * user)
+{
+ LV_UNUSED(user);
+ printf("\t%p %s size: %x (%p)\n", ptr, used ? "used" : "free", (unsigned int)size, (void *)block_from_ptr(ptr));
+}
+
+void lv_tlsf_walk_pool(lv_pool_t pool, lv_tlsf_walker walker, void * user)
+{
+ lv_tlsf_walker pool_walker = walker ? walker : default_walker;
+ block_header_t * block =
+ offset_to_block(pool, -(int)block_header_overhead);
+
+ while(block && !block_is_last(block)) {
+ pool_walker(
+ block_to_ptr(block),
+ block_size(block),
+ !block_is_free(block),
+ user);
+ block = block_next(block);
+ }
+}
+
+size_t lv_tlsf_block_size(void * ptr)
+{
+ size_t size = 0;
+ if(ptr) {
+ const block_header_t * block = block_from_ptr(ptr);
+ size = block_size(block);
+ }
+ return size;
+}
+
+int lv_tlsf_check_pool(lv_pool_t pool)
+{
+ /* Check that the blocks are physically correct. */
+ integrity_t integ = { 0, 0 };
+ lv_tlsf_walk_pool(pool, integrity_walker, &integ);
+
+ return integ.status;
+}
+
+/*
+** Size of the TLSF structures in a given memory block passed to
+** lv_tlsf_create, equal to the size of a control_t
+*/
+size_t lv_tlsf_size(void)
+{
+ return sizeof(control_t);
+}
+
+size_t lv_tlsf_align_size(void)
+{
+ return ALIGN_SIZE;
+}
+
+size_t lv_tlsf_block_size_min(void)
+{
+ return block_size_min;
+}
+
+size_t lv_tlsf_block_size_max(void)
+{
+ return block_size_max;
+}
+
+/*
+** Overhead of the TLSF structures in a given memory block passed to
+** lv_tlsf_add_pool, equal to the overhead of a free block and the
+** sentinel block.
+*/
+size_t lv_tlsf_pool_overhead(void)
+{
+ return 2 * block_header_overhead;
+}
+
+size_t lv_tlsf_alloc_overhead(void)
+{
+ return block_header_overhead;
+}
+
+lv_pool_t lv_tlsf_add_pool(lv_tlsf_t tlsf, void * mem, size_t bytes)
+{
+ block_header_t * block;
+ block_header_t * next;
+
+ const size_t pool_overhead = lv_tlsf_pool_overhead();
+ const size_t pool_bytes = align_down(bytes - pool_overhead, ALIGN_SIZE);
+
+ if(((ptrdiff_t)mem % ALIGN_SIZE) != 0) {
+ printf("lv_tlsf_add_pool: Memory must be aligned by %u bytes.\n",
+ (unsigned int)ALIGN_SIZE);
+ return 0;
+ }
+
+ if(pool_bytes < block_size_min || pool_bytes > block_size_max) {
+#if defined (TLSF_64BIT)
+ printf("lv_tlsf_add_pool: Memory size must be between 0x%x and 0x%x00 bytes.\n",
+ (unsigned int)(pool_overhead + block_size_min),
+ (unsigned int)((pool_overhead + block_size_max) / 256));
+#else
+ printf("lv_tlsf_add_pool: Memory size must be between %u and %u bytes.\n",
+ (unsigned int)(pool_overhead + block_size_min),
+ (unsigned int)(pool_overhead + block_size_max));
+#endif
+ return 0;
+ }
+
+ /*
+ ** Create the main free block. Offset the start of the block slightly
+ ** so that the prev_phys_block field falls outside of the pool -
+ ** it will never be used.
+ */
+ block = offset_to_block(mem, -(tlsfptr_t)block_header_overhead);
+ block_set_size(block, pool_bytes);
+ block_set_free(block);
+ block_set_prev_used(block);
+ block_insert(tlsf_cast(control_t *, tlsf), block);
+
+ /* Split the block to create a zero-size sentinel block. */
+ next = block_link_next(block);
+ block_set_size(next, 0);
+ block_set_used(next);
+ block_set_prev_free(next);
+
+ return mem;
+}
+
+void lv_tlsf_remove_pool(lv_tlsf_t tlsf, lv_pool_t pool)
+{
+ control_t * control = tlsf_cast(control_t *, tlsf);
+ block_header_t * block = offset_to_block(pool, -(int)block_header_overhead);
+
+ int fl = 0, sl = 0;
+
+ tlsf_assert(block_is_free(block) && "block should be free");
+ tlsf_assert(!block_is_free(block_next(block)) && "next block should not be free");
+ tlsf_assert(block_size(block_next(block)) == 0 && "next block size should be zero");
+
+ mapping_insert(block_size(block), &fl, &sl);
+ remove_free_block(control, block, fl, sl);
+}
+
+/*
+** TLSF main interface.
+*/
+
+#if _DEBUG
+int test_ffs_fls()
+{
+ /* Verify ffs/fls work properly. */
+ int rv = 0;
+ rv += (tlsf_ffs(0) == -1) ? 0 : 0x1;
+ rv += (tlsf_fls(0) == -1) ? 0 : 0x2;
+ rv += (tlsf_ffs(1) == 0) ? 0 : 0x4;
+ rv += (tlsf_fls(1) == 0) ? 0 : 0x8;
+ rv += (tlsf_ffs(0x80000000) == 31) ? 0 : 0x10;
+ rv += (tlsf_ffs(0x80008000) == 15) ? 0 : 0x20;
+ rv += (tlsf_fls(0x80000008) == 31) ? 0 : 0x40;
+ rv += (tlsf_fls(0x7FFFFFFF) == 30) ? 0 : 0x80;
+
+#if defined (TLSF_64BIT)
+ rv += (tlsf_fls_sizet(0x80000000) == 31) ? 0 : 0x100;
+ rv += (tlsf_fls_sizet(0x100000000) == 32) ? 0 : 0x200;
+ rv += (tlsf_fls_sizet(0xffffffffffffffff) == 63) ? 0 : 0x400;
+#endif
+
+ if(rv) {
+ printf("test_ffs_fls: %x ffs/fls tests failed.\n", rv);
+ }
+ return rv;
+}
+#endif
+
+lv_tlsf_t lv_tlsf_create(void * mem)
+{
+#if _DEBUG
+ if(test_ffs_fls()) {
+ return 0;
+ }
+#endif
+
+ if(((tlsfptr_t)mem % ALIGN_SIZE) != 0) {
+ printf("lv_tlsf_create: Memory must be aligned to %u bytes.\n",
+ (unsigned int)ALIGN_SIZE);
+ return 0;
+ }
+
+ control_constructor(tlsf_cast(control_t *, mem));
+
+ return tlsf_cast(lv_tlsf_t, mem);
+}
+
+lv_tlsf_t lv_tlsf_create_with_pool(void * mem, size_t bytes)
+{
+ lv_tlsf_t tlsf = lv_tlsf_create(mem);
+ lv_tlsf_add_pool(tlsf, (char *)mem + lv_tlsf_size(), bytes - lv_tlsf_size());
+ return tlsf;
+}
+
+void lv_tlsf_destroy(lv_tlsf_t tlsf)
+{
+ /* Nothing to do. */
+ LV_UNUSED(tlsf);
+}
+
+lv_pool_t lv_tlsf_get_pool(lv_tlsf_t tlsf)
+{
+ return tlsf_cast(lv_pool_t, (char *)tlsf + lv_tlsf_size());
+}
+
+void * lv_tlsf_malloc(lv_tlsf_t tlsf, size_t size)
+{
+ control_t * control = tlsf_cast(control_t *, tlsf);
+ const size_t adjust = adjust_request_size(size, ALIGN_SIZE);
+ block_header_t * block = block_locate_free(control, adjust);
+ return block_prepare_used(control, block, adjust);
+}
+
+void * lv_tlsf_memalign(lv_tlsf_t tlsf, size_t align, size_t size)
+{
+ control_t * control = tlsf_cast(control_t *, tlsf);
+ const size_t adjust = adjust_request_size(size, ALIGN_SIZE);
+
+ /*
+ ** We must allocate an additional minimum block size bytes so that if
+ ** our free block will leave an alignment gap which is smaller, we can
+ ** trim a leading free block and release it back to the pool. We must
+ ** do this because the previous physical block is in use, therefore
+ ** the prev_phys_block field is not valid, and we can't simply adjust
+ ** the size of that block.
+ */
+ const size_t gap_minimum = sizeof(block_header_t);
+ const size_t size_with_gap = adjust_request_size(adjust + align + gap_minimum, align);
+
+ /*
+ ** If alignment is less than or equals base alignment, we're done.
+ ** If we requested 0 bytes, return null, as lv_tlsf_malloc(0) does.
+ */
+ const size_t aligned_size = (adjust && align > ALIGN_SIZE) ? size_with_gap : adjust;
+
+ block_header_t * block = block_locate_free(control, aligned_size);
+
+ /* This can't be a static assert. */
+ tlsf_assert(sizeof(block_header_t) == block_size_min + block_header_overhead);
+
+ if(block) {
+ void * ptr = block_to_ptr(block);
+ void * aligned = align_ptr(ptr, align);
+ size_t gap = tlsf_cast(size_t,
+ tlsf_cast(tlsfptr_t, aligned) - tlsf_cast(tlsfptr_t, ptr));
+
+ /* If gap size is too small, offset to next aligned boundary. */
+ if(gap && gap < gap_minimum) {
+ const size_t gap_remain = gap_minimum - gap;
+ const size_t offset = tlsf_max(gap_remain, align);
+ const void * next_aligned = tlsf_cast(void *,
+ tlsf_cast(tlsfptr_t, aligned) + offset);
+
+ aligned = align_ptr(next_aligned, align);
+ gap = tlsf_cast(size_t,
+ tlsf_cast(tlsfptr_t, aligned) - tlsf_cast(tlsfptr_t, ptr));
+ }
+
+ if(gap) {
+ tlsf_assert(gap >= gap_minimum && "gap size too small");
+ block = block_trim_free_leading(control, block, gap);
+ }
+ }
+
+ return block_prepare_used(control, block, adjust);
+}
+
+size_t lv_tlsf_free(lv_tlsf_t tlsf, const void * ptr)
+{
+ size_t size = 0;
+ /* Don't attempt to free a NULL pointer. */
+ if(ptr) {
+ control_t * control = tlsf_cast(control_t *, tlsf);
+ block_header_t * block = block_from_ptr(ptr);
+ tlsf_assert(!block_is_free(block) && "block already marked as free");
+ size = block->size;
+ block_mark_as_free(block);
+ block = block_merge_prev(control, block);
+ block = block_merge_next(control, block);
+ block_insert(control, block);
+ }
+
+ return size;
+}
+
+/*
+** The TLSF block information provides us with enough information to
+** provide a reasonably intelligent implementation of realloc, growing or
+** shrinking the currently allocated block as required.
+**
+** This routine handles the somewhat esoteric edge cases of realloc:
+** - a non-zero size with a null pointer will behave like malloc
+** - a zero size with a non-null pointer will behave like free
+** - a request that cannot be satisfied will leave the original buffer
+** untouched
+** - an extended buffer size will leave the newly-allocated area with
+** contents undefined
+*/
+void * lv_tlsf_realloc(lv_tlsf_t tlsf, void * ptr, size_t size)
+{
+ control_t * control = tlsf_cast(control_t *, tlsf);
+ void * p = 0;
+
+ /* Zero-size requests are treated as free. */
+ if(ptr && size == 0) {
+ lv_tlsf_free(tlsf, ptr);
+ }
+ /* Requests with NULL pointers are treated as malloc. */
+ else if(!ptr) {
+ p = lv_tlsf_malloc(tlsf, size);
+ }
+ else {
+ block_header_t * block = block_from_ptr(ptr);
+ block_header_t * next = block_next(block);
+
+ const size_t cursize = block_size(block);
+ const size_t combined = cursize + block_size(next) + block_header_overhead;
+ const size_t adjust = adjust_request_size(size, ALIGN_SIZE);
+ if(size > cursize && adjust == 0) {
+ /* The request is probably too large, fail */
+ return NULL;
+ }
+
+ tlsf_assert(!block_is_free(block) && "block already marked as free");
+
+ /*
+ ** If the next block is used, or when combined with the current
+ ** block, does not offer enough space, we must reallocate and copy.
+ */
+ if(adjust > cursize && (!block_is_free(next) || adjust > combined)) {
+ p = lv_tlsf_malloc(tlsf, size);
+ if(p) {
+ const size_t minsize = tlsf_min(cursize, size);
+ lv_memcpy(p, ptr, minsize);
+ lv_tlsf_free(tlsf, ptr);
+ }
+ }
+ else {
+ /* Do we need to expand to the next block? */
+ if(adjust > cursize) {
+ block_merge_next(control, block);
+ block_mark_as_used(block);
+ }
+
+ /* Trim the resulting block and return the original pointer. */
+ block_trim_used(control, block, adjust);
+ p = ptr;
+ }
+ }
+
+ return p;
+}
+
+#endif /* LV_MEM_CUSTOM == 0 */
diff --git a/lib/lvgl/src/misc/lv_tlsf.h b/lib/lvgl/src/misc/lv_tlsf.h
new file mode 100644
index 00000000..f12590b6
--- /dev/null
+++ b/lib/lvgl/src/misc/lv_tlsf.h
@@ -0,0 +1,95 @@
+#include "../lv_conf_internal.h"
+#if LV_MEM_CUSTOM == 0
+
+#ifndef LV_TLSF_H
+#define LV_TLSF_H
+
+/*
+** Two Level Segregated Fit memory allocator, version 3.1.
+** Written by Matthew Conte
+** http://tlsf.baisoku.org
+**
+** Based on the original documentation by Miguel Masmano:
+** http://www.gii.upv.es/tlsf/main/docs
+**
+** This implementation was written to the specification
+** of the document, therefore no GPL restrictions apply.
+**
+** Copyright (c) 2006-2016, Matthew Conte
+** All rights reserved.
+**
+** Redistribution and use in source and binary forms, with or without
+** modification, are permitted provided that the following conditions are met:
+** * Redistributions of source code must retain the above copyright
+** notice, this list of conditions and the following disclaimer.
+** * Redistributions in binary form must reproduce the above copyright
+** notice, this list of conditions and the following disclaimer in the
+** documentation and/or other materials provided with the distribution.
+** * Neither the name of the copyright holder nor the
+** names of its contributors may be used to endorse or promote products
+** derived from this software without specific prior written permission.
+**
+** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
+** ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+** WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+** DISCLAIMED. IN NO EVENT SHALL MATTHEW CONTE BE LIABLE FOR ANY
+** DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+** (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+** LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+** ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+** SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+*/
+
+#include <stddef.h>
+
+#if defined(__cplusplus)
+extern "C" {
+#endif
+
+/* lv_tlsf_t: a TLSF structure. Can contain 1 to N pools. */
+/* lv_pool_t: a block of memory that TLSF can manage. */
+typedef void * lv_tlsf_t;
+typedef void * lv_pool_t;
+
+/* Create/destroy a memory pool. */
+lv_tlsf_t lv_tlsf_create(void * mem);
+lv_tlsf_t lv_tlsf_create_with_pool(void * mem, size_t bytes);
+void lv_tlsf_destroy(lv_tlsf_t tlsf);
+lv_pool_t lv_tlsf_get_pool(lv_tlsf_t tlsf);
+
+/* Add/remove memory pools. */
+lv_pool_t lv_tlsf_add_pool(lv_tlsf_t tlsf, void * mem, size_t bytes);
+void lv_tlsf_remove_pool(lv_tlsf_t tlsf, lv_pool_t pool);
+
+/* malloc/memalign/realloc/free replacements. */
+void * lv_tlsf_malloc(lv_tlsf_t tlsf, size_t bytes);
+void * lv_tlsf_memalign(lv_tlsf_t tlsf, size_t align, size_t bytes);
+void * lv_tlsf_realloc(lv_tlsf_t tlsf, void * ptr, size_t size);
+size_t lv_tlsf_free(lv_tlsf_t tlsf, const void * ptr);
+
+/* Returns internal block size, not original request size */
+size_t lv_tlsf_block_size(void * ptr);
+
+/* Overheads/limits of internal structures. */
+size_t lv_tlsf_size(void);
+size_t lv_tlsf_align_size(void);
+size_t lv_tlsf_block_size_min(void);
+size_t lv_tlsf_block_size_max(void);
+size_t lv_tlsf_pool_overhead(void);
+size_t lv_tlsf_alloc_overhead(void);
+
+/* Debugging. */
+typedef void (*lv_tlsf_walker)(void * ptr, size_t size, int used, void * user);
+void lv_tlsf_walk_pool(lv_pool_t pool, lv_tlsf_walker walker, void * user);
+/* Returns nonzero if any internal consistency check fails. */
+int lv_tlsf_check(lv_tlsf_t tlsf);
+int lv_tlsf_check_pool(lv_pool_t pool);
+
+#if defined(__cplusplus)
+};
+#endif
+
+#endif /*LV_TLSF_H*/
+
+#endif /* LV_MEM_CUSTOM == 0 */
diff --git a/lib/lvgl/src/misc/lv_txt.c b/lib/lvgl/src/misc/lv_txt.c
new file mode 100644
index 00000000..da7eca0b
--- /dev/null
+++ b/lib/lvgl/src/misc/lv_txt.c
@@ -0,0 +1,864 @@
+/**
+ * @file lv_txt.c
+ *
+ */
+
+/*********************
+ * INCLUDES
+ *********************/
+#include <stdarg.h>
+#include "lv_txt.h"
+#include "lv_txt_ap.h"
+#include "lv_math.h"
+#include "lv_log.h"
+#include "lv_mem.h"
+#include "lv_assert.h"
+
+/*********************
+ * DEFINES
+ *********************/
+#define NO_BREAK_FOUND UINT32_MAX
+
+/**********************
+ * TYPEDEFS
+ **********************/
+
+/**********************
+ * STATIC PROTOTYPES
+ **********************/
+
+#if LV_TXT_ENC == LV_TXT_ENC_UTF8
+ static uint8_t lv_txt_utf8_size(const char * str);
+ static uint32_t lv_txt_unicode_to_utf8(uint32_t letter_uni);
+ static uint32_t lv_txt_utf8_conv_wc(uint32_t c);
+ static uint32_t lv_txt_utf8_next(const char * txt, uint32_t * i);
+ static uint32_t lv_txt_utf8_prev(const char * txt, uint32_t * i_start);
+ static uint32_t lv_txt_utf8_get_byte_id(const char * txt, uint32_t utf8_id);
+ static uint32_t lv_txt_utf8_get_char_id(const char * txt, uint32_t byte_id);
+ static uint32_t lv_txt_utf8_get_length(const char * txt);
+#elif LV_TXT_ENC == LV_TXT_ENC_ASCII
+ static uint8_t lv_txt_iso8859_1_size(const char * str);
+ static uint32_t lv_txt_unicode_to_iso8859_1(uint32_t letter_uni);
+ static uint32_t lv_txt_iso8859_1_conv_wc(uint32_t c);
+ static uint32_t lv_txt_iso8859_1_next(const char * txt, uint32_t * i);
+ static uint32_t lv_txt_iso8859_1_prev(const char * txt, uint32_t * i_start);
+ static uint32_t lv_txt_iso8859_1_get_byte_id(const char * txt, uint32_t utf8_id);
+ static uint32_t lv_txt_iso8859_1_get_char_id(const char * txt, uint32_t byte_id);
+ static uint32_t lv_txt_iso8859_1_get_length(const char * txt);
+#endif
+/**********************
+ * STATIC VARIABLES
+ **********************/
+
+/**********************
+ * GLOBAL VARIABLES
+ **********************/
+#if LV_TXT_ENC == LV_TXT_ENC_UTF8
+ uint8_t (*_lv_txt_encoded_size)(const char *) = lv_txt_utf8_size;
+ uint32_t (*_lv_txt_unicode_to_encoded)(uint32_t) = lv_txt_unicode_to_utf8;
+ uint32_t (*_lv_txt_encoded_conv_wc)(uint32_t) = lv_txt_utf8_conv_wc;
+ uint32_t (*_lv_txt_encoded_next)(const char *, uint32_t *) = lv_txt_utf8_next;
+ uint32_t (*_lv_txt_encoded_prev)(const char *, uint32_t *) = lv_txt_utf8_prev;
+ uint32_t (*_lv_txt_encoded_get_byte_id)(const char *, uint32_t) = lv_txt_utf8_get_byte_id;
+ uint32_t (*_lv_txt_encoded_get_char_id)(const char *, uint32_t) = lv_txt_utf8_get_char_id;
+ uint32_t (*_lv_txt_get_encoded_length)(const char *) = lv_txt_utf8_get_length;
+#elif LV_TXT_ENC == LV_TXT_ENC_ASCII
+ uint8_t (*_lv_txt_encoded_size)(const char *) = lv_txt_iso8859_1_size;
+ uint32_t (*_lv_txt_unicode_to_encoded)(uint32_t) = lv_txt_unicode_to_iso8859_1;
+ uint32_t (*_lv_txt_encoded_conv_wc)(uint32_t) = lv_txt_iso8859_1_conv_wc;
+ uint32_t (*_lv_txt_encoded_next)(const char *, uint32_t *) = lv_txt_iso8859_1_next;
+ uint32_t (*_lv_txt_encoded_prev)(const char *, uint32_t *) = lv_txt_iso8859_1_prev;
+ uint32_t (*_lv_txt_encoded_get_byte_id)(const char *, uint32_t) = lv_txt_iso8859_1_get_byte_id;
+ uint32_t (*_lv_txt_encoded_get_char_id)(const char *, uint32_t) = lv_txt_iso8859_1_get_char_id;
+ uint32_t (*_lv_txt_get_encoded_length)(const char *) = lv_txt_iso8859_1_get_length;
+
+#endif
+
+/**********************
+ * MACROS
+ **********************/
+
+#define LV_IS_ASCII(value) ((value & 0x80U) == 0x00U)
+#define LV_IS_2BYTES_UTF8_CODE(value) ((value & 0xE0U) == 0xC0U)
+#define LV_IS_3BYTES_UTF8_CODE(value) ((value & 0xF0U) == 0xE0U)
+#define LV_IS_4BYTES_UTF8_CODE(value) ((value & 0xF8U) == 0xF0U)
+#define LV_IS_INVALID_UTF8_CODE(value) ((value & 0xC0U) != 0x80U)
+
+/**********************
+ * GLOBAL FUNCTIONS
+ **********************/
+
+void lv_txt_get_size(lv_point_t * size_res, const char * text, const lv_font_t * font, lv_coord_t letter_space,
+ lv_coord_t line_space, lv_coord_t max_width, lv_text_flag_t flag)
+{
+ size_res->x = 0;
+ size_res->y = 0;
+
+ if(text == NULL) return;
+ if(font == NULL) return;
+
+ if(flag & LV_TEXT_FLAG_EXPAND) max_width = LV_COORD_MAX;
+
+ uint32_t line_start = 0;
+ uint32_t new_line_start = 0;
+ uint16_t letter_height = lv_font_get_line_height(font);
+
+ /*Calc. the height and longest line*/
+ while(text[line_start] != '\0') {
+ new_line_start += _lv_txt_get_next_line(&text[line_start], font, letter_space, max_width, NULL, flag);
+
+ if((unsigned long)size_res->y + (unsigned long)letter_height + (unsigned long)line_space > LV_MAX_OF(lv_coord_t)) {
+ LV_LOG_WARN("lv_txt_get_size: integer overflow while calculating text height");
+ return;
+ }
+ else {
+ size_res->y += letter_height;
+ size_res->y += line_space;
+ }
+
+ /*Calculate the longest line*/
+ lv_coord_t act_line_length = lv_txt_get_width(&text[line_start], new_line_start - line_start, font, letter_space,
+ flag);
+
+ size_res->x = LV_MAX(act_line_length, size_res->x);
+ line_start = new_line_start;
+ }
+
+ /*Make the text one line taller if the last character is '\n' or '\r'*/
+ if((line_start != 0) && (text[line_start - 1] == '\n' || text[line_start - 1] == '\r')) {
+ size_res->y += letter_height + line_space;
+ }
+
+ /*Correction with the last line space or set the height manually if the text is empty*/
+ if(size_res->y == 0)
+ size_res->y = letter_height;
+ else
+ size_res->y -= line_space;
+}
+
+/**
+ * Get the next word of text. A word is delimited by break characters.
+ *
+ * If the word cannot fit in the max_width space, obey LV_TXT_LINE_BREAK_LONG_* rules.
+ *
+ * If the next word cannot fit anything, return 0.
+ *
+ * If the first character is a break character, returns the next index.
+ *
+ * Example calls from lv_txt_get_next_line() assuming sufficient max_width and
+ * txt = "Test text\n"
+ * 0123456789
+ *
+ * Calls would be as follows:
+ * 1. Return i=4, pointing at breakchar ' ', for the string "Test"
+ * 2. Return i=5, since i=4 was a breakchar.
+ * 3. Return i=9, pointing at breakchar '\n'
+ * 4. Parenting lv_txt_get_next_line() would detect subsequent '\0'
+ *
+ * TODO: Returned word_w_ptr may overestimate the returned word's width when
+ * max_width is reached. In current usage, this has no impact.
+ *
+ * @param txt a '\0' terminated string
+ * @param font pointer to a font
+ * @param letter_space letter space
+ * @param max_width max width of the text (break the lines to fit this size). Set COORD_MAX to avoid line breaks
+ * @param flags settings for the text from 'txt_flag_type' enum
+ * @param[out] word_w_ptr width (in pixels) of the parsed word. May be NULL.
+ * @param cmd_state pointer to a txt_cmd_state_t variable which stores the current state of command processing
+ * @param force Force return the fraction of the word that can fit in the provided space.
+ * @return the index of the first char of the next word (in byte index not letter index. With UTF-8 they are different)
+ */
+static uint32_t lv_txt_get_next_word(const char * txt, const lv_font_t * font,
+ lv_coord_t letter_space, lv_coord_t max_width,
+ lv_text_flag_t flag, uint32_t * word_w_ptr, lv_text_cmd_state_t * cmd_state, bool force)
+{
+ if(txt == NULL || txt[0] == '\0') return 0;
+ if(font == NULL) return 0;
+
+ if(flag & LV_TEXT_FLAG_EXPAND) max_width = LV_COORD_MAX;
+
+ uint32_t i = 0, i_next = 0, i_next_next = 0; /*Iterating index into txt*/
+ uint32_t letter = 0; /*Letter at i*/
+ uint32_t letter_next = 0; /*Letter at i_next*/
+ lv_coord_t letter_w;
+ lv_coord_t cur_w = 0; /*Pixel Width of transversed string*/
+ uint32_t word_len = 0; /*Number of characters in the transversed word*/
+ uint32_t break_index = NO_BREAK_FOUND; /*only used for "long" words*/
+ uint32_t break_letter_count = 0; /*Number of characters up to the long word break point*/
+
+ letter = _lv_txt_encoded_next(txt, &i_next);
+ i_next_next = i_next;
+
+ /*Obtain the full word, regardless if it fits or not in max_width*/
+ while(txt[i] != '\0') {
+ letter_next = _lv_txt_encoded_next(txt, &i_next_next);
+ word_len++;
+
+ /*Handle the recolor command*/
+ if((flag & LV_TEXT_FLAG_RECOLOR) != 0) {
+ if(_lv_txt_is_cmd(cmd_state, letter) != false) {
+ i = i_next;
+ i_next = i_next_next;
+ letter = letter_next;
+ continue; /*Skip the letter if it is part of a command*/
+ }
+ }
+
+ letter_w = lv_font_get_glyph_width(font, letter, letter_next);
+ cur_w += letter_w;
+
+ if(letter_w > 0) {
+ cur_w += letter_space;
+ }
+
+ /*Test if this character fits within max_width*/
+ if(break_index == NO_BREAK_FOUND && (cur_w - letter_space) > max_width) {
+ break_index = i;
+ break_letter_count = word_len - 1;
+ /*break_index is now pointing at the character that doesn't fit*/
+ }
+
+ /*Check for new line chars and breakchars*/
+ if(letter == '\n' || letter == '\r' || _lv_txt_is_break_char(letter)) {
+ /*Update the output width on the first character if it fits.
+ *Must do this here in case first letter is a break character.*/
+ if(i == 0 && break_index == NO_BREAK_FOUND && word_w_ptr != NULL) *word_w_ptr = cur_w;
+ word_len--;
+ break;
+ }
+
+ /*Update the output width*/
+ if(word_w_ptr != NULL && break_index == NO_BREAK_FOUND) *word_w_ptr = cur_w;
+
+ i = i_next;
+ i_next = i_next_next;
+ letter = letter_next;
+ }
+
+ /*Entire Word fits in the provided space*/
+ if(break_index == NO_BREAK_FOUND) {
+ if(word_len == 0 || (letter == '\r' && letter_next == '\n')) i = i_next;
+ return i;
+ }
+
+#if LV_TXT_LINE_BREAK_LONG_LEN > 0
+ /*Word doesn't fit in provided space, but isn't "long"*/
+ if(word_len < LV_TXT_LINE_BREAK_LONG_LEN) {
+ if(force) return break_index;
+ if(word_w_ptr != NULL) *word_w_ptr = 0; /*Return no word*/
+ return 0;
+ }
+
+ /*Word is "long," but insufficient amounts can fit in provided space*/
+ if(break_letter_count < LV_TXT_LINE_BREAK_LONG_PRE_MIN_LEN) {
+ if(force) return break_index;
+ if(word_w_ptr != NULL) *word_w_ptr = 0;
+ return 0;
+ }
+
+ /*Word is a "long", but letters may need to be better distributed*/
+ {
+ i = break_index;
+ int32_t n_move = LV_TXT_LINE_BREAK_LONG_POST_MIN_LEN - (word_len - break_letter_count);
+ /*Move pointer "i" backwards*/
+ for(; n_move > 0; n_move--) {
+ _lv_txt_encoded_prev(txt, &i);
+ // TODO: it would be appropriate to update the returned word width here
+ // However, in current usage, this doesn't impact anything.
+ }
+ }
+ return i;
+#else
+ if(force) return break_index;
+ if(word_w_ptr != NULL) *word_w_ptr = 0; /*Return no word*/
+ (void) break_letter_count;
+ return 0;
+#endif
+}
+
+uint32_t _lv_txt_get_next_line(const char * txt, const lv_font_t * font,
+ lv_coord_t letter_space, lv_coord_t max_width,
+ lv_coord_t * used_width, lv_text_flag_t flag)
+{
+ if(used_width) *used_width = 0;
+
+ if(txt == NULL) return 0;
+ if(txt[0] == '\0') return 0;
+ if(font == NULL) return 0;
+
+ lv_coord_t line_w = 0;
+
+ /*If max_width doesn't mater simply find the new line character
+ *without thinking about word wrapping*/
+ if((flag & LV_TEXT_FLAG_EXPAND) || (flag & LV_TEXT_FLAG_FIT)) {
+ uint32_t i;
+ for(i = 0; txt[i] != '\n' && txt[i] != '\r' && txt[i] != '\0'; i++) {
+ /*Just find the new line chars or string ends by incrementing `i`*/
+ }
+ if(txt[i] != '\0') i++; /*To go beyond `\n`*/
+ if(used_width) *used_width = -1;
+ return i;
+ }
+
+ if(flag & LV_TEXT_FLAG_EXPAND) max_width = LV_COORD_MAX;
+ lv_text_cmd_state_t cmd_state = LV_TEXT_CMD_STATE_WAIT;
+ uint32_t i = 0; /*Iterating index into txt*/
+
+ while(txt[i] != '\0' && max_width > 0) {
+ uint32_t word_w = 0;
+ uint32_t advance = lv_txt_get_next_word(&txt[i], font, letter_space, max_width, flag, &word_w, &cmd_state, i == 0);
+ max_width -= word_w;
+ line_w += word_w;
+
+ if(advance == 0) {
+ break;
+ }
+
+ i += advance;
+
+ if(txt[0] == '\n' || txt[0] == '\r') break;
+
+ if(txt[i] == '\n' || txt[i] == '\r') {
+ i++; /*Include the following newline in the current line*/
+ break;
+ }
+
+ }
+
+ /*Always step at least one to avoid infinite loops*/
+ if(i == 0) {
+ uint32_t letter = _lv_txt_encoded_next(txt, &i);
+ if(used_width != NULL) {
+ line_w = lv_font_get_glyph_width(font, letter, '\0');
+ }
+ }
+
+ if(used_width != NULL) {
+ *used_width = line_w;
+ }
+
+ return i;
+}
+
+lv_coord_t lv_txt_get_width(const char * txt, uint32_t length, const lv_font_t * font, lv_coord_t letter_space,
+ lv_text_flag_t flag)
+{
+ if(txt == NULL) return 0;
+ if(font == NULL) return 0;
+ if(txt[0] == '\0') return 0;
+
+ uint32_t i = 0;
+ lv_coord_t width = 0;
+ lv_text_cmd_state_t cmd_state = LV_TEXT_CMD_STATE_WAIT;
+
+ if(length != 0) {
+ while(i < length) {
+ uint32_t letter;
+ uint32_t letter_next;
+ _lv_txt_encoded_letter_next_2(txt, &letter, &letter_next, &i);
+
+ if((flag & LV_TEXT_FLAG_RECOLOR) != 0) {
+ if(_lv_txt_is_cmd(&cmd_state, letter) != false) {
+ continue;
+ }
+ }
+
+ lv_coord_t char_width = lv_font_get_glyph_width(font, letter, letter_next);
+ if(char_width > 0) {
+ width += char_width;
+ width += letter_space;
+ }
+ }
+
+ if(width > 0) {
+ width -= letter_space; /*Trim the last letter space. Important if the text is center
+ aligned*/
+ }
+ }
+
+ return width;
+}
+
+bool _lv_txt_is_cmd(lv_text_cmd_state_t * state, uint32_t c)
+{
+ bool ret = false;
+
+ if(c == (uint32_t)LV_TXT_COLOR_CMD[0]) {
+ if(*state == LV_TEXT_CMD_STATE_WAIT) { /*Start char*/
+ *state = LV_TEXT_CMD_STATE_PAR;
+ ret = true;
+ }
+ /*Other start char in parameter is escaped cmd. char*/
+ else if(*state == LV_TEXT_CMD_STATE_PAR) {
+ *state = LV_TEXT_CMD_STATE_WAIT;
+ }
+ /*Command end*/
+ else if(*state == LV_TEXT_CMD_STATE_IN) {
+ *state = LV_TEXT_CMD_STATE_WAIT;
+ ret = true;
+ }
+ }
+
+ /*Skip the color parameter and wait the space after it*/
+ if(*state == LV_TEXT_CMD_STATE_PAR) {
+ if(c == ' ') {
+ *state = LV_TEXT_CMD_STATE_IN; /*After the parameter the text is in the command*/
+ }
+ ret = true;
+ }
+
+ return ret;
+}
+
+void _lv_txt_ins(char * txt_buf, uint32_t pos, const char * ins_txt)
+{
+ if(txt_buf == NULL || ins_txt == NULL) return;
+
+ size_t old_len = strlen(txt_buf);
+ size_t ins_len = strlen(ins_txt);
+ if(ins_len == 0) return;
+
+ size_t new_len = ins_len + old_len;
+ pos = _lv_txt_encoded_get_byte_id(txt_buf, pos); /*Convert to byte index instead of letter index*/
+
+ /*Copy the second part into the end to make place to text to insert*/
+ size_t i;
+ for(i = new_len; i >= pos + ins_len; i--) {
+ txt_buf[i] = txt_buf[i - ins_len];
+ }
+
+ /*Copy the text into the new space*/
+ lv_memcpy_small(txt_buf + pos, ins_txt, ins_len);
+}
+
+void _lv_txt_cut(char * txt, uint32_t pos, uint32_t len)
+{
+ if(txt == NULL) return;
+
+ size_t old_len = strlen(txt);
+
+ pos = _lv_txt_encoded_get_byte_id(txt, pos); /*Convert to byte index instead of letter index*/
+ len = _lv_txt_encoded_get_byte_id(&txt[pos], len);
+
+ /*Copy the second part into the end to make place to text to insert*/
+ uint32_t i;
+ for(i = pos; i <= old_len - len; i++) {
+ txt[i] = txt[i + len];
+ }
+}
+
+char * _lv_txt_set_text_vfmt(const char * fmt, va_list ap)
+{
+ /*Allocate space for the new text by using trick from C99 standard section 7.19.6.12*/
+ va_list ap_copy;
+ va_copy(ap_copy, ap);
+ uint32_t len = lv_vsnprintf(NULL, 0, fmt, ap_copy);
+ va_end(ap_copy);
+
+ char * text = 0;
+#if LV_USE_ARABIC_PERSIAN_CHARS
+ /*Put together the text according to the format string*/
+ char * raw_txt = lv_mem_buf_get(len + 1);
+ LV_ASSERT_MALLOC(raw_txt);
+ if(raw_txt == NULL) {
+ return NULL;
+ }
+
+ lv_vsnprintf(raw_txt, len + 1, fmt, ap);
+
+ /*Get the size of the Arabic text and process it*/
+ size_t len_ap = _lv_txt_ap_calc_bytes_cnt(raw_txt);
+ text = lv_mem_alloc(len_ap + 1);
+ LV_ASSERT_MALLOC(text);
+ if(text == NULL) {
+ return NULL;
+ }
+ _lv_txt_ap_proc(raw_txt, text);
+
+ lv_mem_buf_release(raw_txt);
+#else
+ text = lv_mem_alloc(len + 1);
+ LV_ASSERT_MALLOC(text);
+ if(text == NULL) {
+ return NULL;
+ }
+ text[len] = 0; /*Ensure NULL termination*/
+
+ lv_vsnprintf(text, len + 1, fmt, ap);
+#endif
+
+ return text;
+}
+
+void _lv_txt_encoded_letter_next_2(const char * txt, uint32_t * letter, uint32_t * letter_next, uint32_t * ofs)
+{
+ *letter = _lv_txt_encoded_next(txt, ofs);
+ *letter_next = *letter != '\0' ? _lv_txt_encoded_next(&txt[*ofs], NULL) : 0;
+}
+
+#if LV_TXT_ENC == LV_TXT_ENC_UTF8
+/*******************************
+ * UTF-8 ENCODER/DECODER
+ ******************************/
+
+/**
+ * Give the size of an UTF-8 coded character
+ * @param str pointer to a character in a string
+ * @return length of the UTF-8 character (1,2,3 or 4), 0 on invalid code.
+ */
+static uint8_t lv_txt_utf8_size(const char * str)
+{
+ if(LV_IS_ASCII(str[0]))
+ return 1;
+ else if(LV_IS_2BYTES_UTF8_CODE(str[0]))
+ return 2;
+ else if(LV_IS_3BYTES_UTF8_CODE(str[0]))
+ return 3;
+ else if(LV_IS_4BYTES_UTF8_CODE(str[0]))
+ return 4;
+ return 0;
+}
+
+/**
+ * Convert a Unicode letter to UTF-8.
+ * @param letter_uni a Unicode letter
+ * @return UTF-8 coded character in Little Endian to be compatible with C chars (e.g. 'Á', 'Ű')
+ */
+static uint32_t lv_txt_unicode_to_utf8(uint32_t letter_uni)
+{
+ if(letter_uni < 128) return letter_uni;
+ uint8_t bytes[4];
+
+ if(letter_uni < 0x0800) {
+ bytes[0] = ((letter_uni >> 6) & 0x1F) | 0xC0;
+ bytes[1] = ((letter_uni >> 0) & 0x3F) | 0x80;
+ bytes[2] = 0;
+ bytes[3] = 0;
+ }
+ else if(letter_uni < 0x010000) {
+ bytes[0] = ((letter_uni >> 12) & 0x0F) | 0xE0;
+ bytes[1] = ((letter_uni >> 6) & 0x3F) | 0x80;
+ bytes[2] = ((letter_uni >> 0) & 0x3F) | 0x80;
+ bytes[3] = 0;
+ }
+ else if(letter_uni < 0x110000) {
+ bytes[0] = ((letter_uni >> 18) & 0x07) | 0xF0;
+ bytes[1] = ((letter_uni >> 12) & 0x3F) | 0x80;
+ bytes[2] = ((letter_uni >> 6) & 0x3F) | 0x80;
+ bytes[3] = ((letter_uni >> 0) & 0x3F) | 0x80;
+ }
+ else {
+ return 0;
+ }
+
+ uint32_t * res_p = (uint32_t *)bytes;
+ return *res_p;
+}
+
+/**
+ * Convert a wide character, e.g. 'Á' little endian to be UTF-8 compatible
+ * @param c a wide character or a Little endian number
+ * @return `c` in big endian
+ */
+static uint32_t lv_txt_utf8_conv_wc(uint32_t c)
+{
+#if LV_BIG_ENDIAN_SYSTEM == 0
+ /*Swap the bytes (UTF-8 is big endian, but the MCUs are little endian)*/
+ if((c & 0x80) != 0) {
+ uint32_t swapped;
+ uint8_t c8[4];
+ lv_memcpy_small(c8, &c, 4);
+ swapped = (c8[0] << 24) + (c8[1] << 16) + (c8[2] << 8) + (c8[3]);
+ uint8_t i;
+ for(i = 0; i < 4; i++) {
+ if((swapped & 0xFF) == 0)
+ swapped = (swapped >> 8); /*Ignore leading zeros (they were in the end originally)*/
+ }
+ c = swapped;
+ }
+#endif
+ return c;
+}
+
+/**
+ * Decode an UTF-8 character from a string.
+ * @param txt pointer to '\0' terminated string
+ * @param i start byte index in 'txt' where to start.
+ * After call it will point to the next UTF-8 char in 'txt'.
+ * NULL to use txt[0] as index
+ * @return the decoded Unicode character or 0 on invalid UTF-8 code
+ */
+static uint32_t lv_txt_utf8_next(const char * txt, uint32_t * i)
+{
+ /**
+ * Unicode to UTF-8
+ * 00000000 00000000 00000000 0xxxxxxx -> 0xxxxxxx
+ * 00000000 00000000 00000yyy yyxxxxxx -> 110yyyyy 10xxxxxx
+ * 00000000 00000000 zzzzyyyy yyxxxxxx -> 1110zzzz 10yyyyyy 10xxxxxx
+ * 00000000 000wwwzz zzzzyyyy yyxxxxxx -> 11110www 10zzzzzz 10yyyyyy 10xxxxxx
+ */
+
+ uint32_t result = 0;
+
+ /*Dummy 'i' pointer is required*/
+ uint32_t i_tmp = 0;
+ if(i == NULL) i = &i_tmp;
+
+ /*Normal ASCII*/
+ if(LV_IS_ASCII(txt[*i])) {
+ result = txt[*i];
+ (*i)++;
+ }
+ /*Real UTF-8 decode*/
+ else {
+ /*2 bytes UTF-8 code*/
+ if(LV_IS_2BYTES_UTF8_CODE(txt[*i])) {
+ result = (uint32_t)(txt[*i] & 0x1F) << 6;
+ (*i)++;
+ if(LV_IS_INVALID_UTF8_CODE(txt[*i])) return 0;
+ result += (txt[*i] & 0x3F);
+ (*i)++;
+ }
+ /*3 bytes UTF-8 code*/
+ else if(LV_IS_3BYTES_UTF8_CODE(txt[*i])) {
+ result = (uint32_t)(txt[*i] & 0x0F) << 12;
+ (*i)++;
+
+ if(LV_IS_INVALID_UTF8_CODE(txt[*i])) return 0;
+ result += (uint32_t)(txt[*i] & 0x3F) << 6;
+ (*i)++;
+
+ if(LV_IS_INVALID_UTF8_CODE(txt[*i])) return 0;
+ result += (txt[*i] & 0x3F);
+ (*i)++;
+ }
+ /*4 bytes UTF-8 code*/
+ else if(LV_IS_4BYTES_UTF8_CODE(txt[*i])) {
+ result = (uint32_t)(txt[*i] & 0x07) << 18;
+ (*i)++;
+
+ if(LV_IS_INVALID_UTF8_CODE(txt[*i])) return 0;
+ result += (uint32_t)(txt[*i] & 0x3F) << 12;
+ (*i)++;
+
+ if(LV_IS_INVALID_UTF8_CODE(txt[*i])) return 0;
+ result += (uint32_t)(txt[*i] & 0x3F) << 6;
+ (*i)++;
+
+ if(LV_IS_INVALID_UTF8_CODE(txt[*i])) return 0;
+ result += txt[*i] & 0x3F;
+ (*i)++;
+ }
+ else {
+ (*i)++; /*Not UTF-8 char. Go the next.*/
+ }
+ }
+ return result;
+}
+
+/**
+ * Get previous UTF-8 character form a string.
+ * @param txt pointer to '\0' terminated string
+ * @param i start byte index in 'txt' where to start. After the call it will point to the previous
+ * UTF-8 char in 'txt'.
+ * @return the decoded Unicode character or 0 on invalid UTF-8 code
+ */
+static uint32_t lv_txt_utf8_prev(const char * txt, uint32_t * i)
+{
+ uint8_t c_size;
+ uint8_t cnt = 0;
+
+ /*Try to find a !0 long UTF-8 char by stepping one character back*/
+ (*i)--;
+ do {
+ if(cnt >= 4) return 0; /*No UTF-8 char found before the initial*/
+
+ c_size = _lv_txt_encoded_size(&txt[*i]);
+ if(c_size == 0) {
+ if(*i != 0)
+ (*i)--;
+ else
+ return 0;
+ }
+ cnt++;
+ } while(c_size == 0);
+
+ uint32_t i_tmp = *i;
+ uint32_t letter = _lv_txt_encoded_next(txt, &i_tmp); /*Character found, get it*/
+
+ return letter;
+}
+
+/**
+ * Convert a character index (in an UTF-8 text) to byte index.
+ * E.g. in "AÁRT" index of 'R' is 2th char but start at byte 3 because 'Á' is 2 bytes long
+ * @param txt a '\0' terminated UTF-8 string
+ * @param utf8_id character index
+ * @return byte index of the 'utf8_id'th letter
+ */
+static uint32_t lv_txt_utf8_get_byte_id(const char * txt, uint32_t utf8_id)
+{
+ uint32_t i;
+ uint32_t byte_cnt = 0;
+ for(i = 0; i < utf8_id && txt[byte_cnt] != '\0'; i++) {
+ uint8_t c_size = _lv_txt_encoded_size(&txt[byte_cnt]);
+ /* If the char was invalid tell it's 1 byte long*/
+ byte_cnt += c_size ? c_size : 1;
+ }
+
+ return byte_cnt;
+}
+
+/**
+ * Convert a byte index (in an UTF-8 text) to character index.
+ * E.g. in "AÁRT" index of 'R' is 2th char but start at byte 3 because 'Á' is 2 bytes long
+ * @param txt a '\0' terminated UTF-8 string
+ * @param byte_id byte index
+ * @return character index of the letter at 'byte_id'th position
+ */
+static uint32_t lv_txt_utf8_get_char_id(const char * txt, uint32_t byte_id)
+{
+ uint32_t i = 0;
+ uint32_t char_cnt = 0;
+
+ while(i < byte_id) {
+ _lv_txt_encoded_next(txt, &i); /*'i' points to the next letter so use the prev. value*/
+ char_cnt++;
+ }
+
+ return char_cnt;
+}
+
+/**
+ * Get the number of characters (and NOT bytes) in a string. Decode it with UTF-8 if enabled.
+ * E.g.: "ÁBC" is 3 characters (but 4 bytes)
+ * @param txt a '\0' terminated char string
+ * @return number of characters
+ */
+static uint32_t lv_txt_utf8_get_length(const char * txt)
+{
+ uint32_t len = 0;
+ uint32_t i = 0;
+
+ while(txt[i] != '\0') {
+ _lv_txt_encoded_next(txt, &i);
+ len++;
+ }
+
+ return len;
+}
+
+#elif LV_TXT_ENC == LV_TXT_ENC_ASCII
+/*******************************
+ * ASCII ENCODER/DECODER
+ ******************************/
+
+/**
+ * Give the size of an ISO8859-1 coded character
+ * @param str pointer to a character in a string
+ * @return length of the UTF-8 character (1,2,3 or 4). O on invalid code
+ */
+static uint8_t lv_txt_iso8859_1_size(const char * str)
+{
+ LV_UNUSED(str); /*Unused*/
+ return 1;
+}
+
+/**
+ * Convert a Unicode letter to ISO8859-1.
+ * @param letter_uni a Unicode letter
+ * @return ISO8859-1 coded character in Little Endian to be compatible with C chars (e.g. 'Á', 'Ű')
+ */
+static uint32_t lv_txt_unicode_to_iso8859_1(uint32_t letter_uni)
+{
+ if(letter_uni < 256)
+ return letter_uni;
+ else
+ return ' ';
+}
+
+/**
+ * Convert wide characters to ASCII, however wide characters in ASCII range (e.g. 'A') are ASCII compatible by default.
+ * So this function does nothing just returns with `c`.
+ * @param c a character, e.g. 'A'
+ * @return same as `c`
+ */
+static uint32_t lv_txt_iso8859_1_conv_wc(uint32_t c)
+{
+ return c;
+}
+
+/**
+ * Decode an ISO8859-1 character from a string.
+ * @param txt pointer to '\0' terminated string
+ * @param i start byte index in 'txt' where to start.
+ * After call it will point to the next UTF-8 char in 'txt'.
+ * NULL to use txt[0] as index
+ * @return the decoded Unicode character or 0 on invalid UTF-8 code
+ */
+static uint32_t lv_txt_iso8859_1_next(const char * txt, uint32_t * i)
+{
+ if(i == NULL) return txt[0]; /*Get the next char*/
+
+ uint8_t letter = txt[*i];
+ (*i)++;
+ return letter;
+}
+
+/**
+ * Get previous ISO8859-1 character form a string.
+ * @param txt pointer to '\0' terminated string
+ * @param i start byte index in 'txt' where to start. After the call it will point to the previous UTF-8 char in 'txt'.
+ * @return the decoded Unicode character or 0 on invalid UTF-8 code
+ */
+static uint32_t lv_txt_iso8859_1_prev(const char * txt, uint32_t * i)
+{
+ if(i == NULL) return *(txt - 1); /*Get the prev. char*/
+
+ (*i)--;
+ uint8_t letter = txt[*i];
+
+ return letter;
+}
+
+/**
+ * Convert a character index (in an ISO8859-1 text) to byte index.
+ * E.g. in "AÁRT" index of 'R' is 2th char but start at byte 3 because 'Á' is 2 bytes long
+ * @param txt a '\0' terminated UTF-8 string
+ * @param utf8_id character index
+ * @return byte index of the 'utf8_id'th letter
+ */
+static uint32_t lv_txt_iso8859_1_get_byte_id(const char * txt, uint32_t utf8_id)
+{
+ LV_UNUSED(txt); /*Unused*/
+ return utf8_id; /*In Non encoded no difference*/
+}
+
+/**
+ * Convert a byte index (in an ISO8859-1 text) to character index.
+ * E.g. in "AÁRT" index of 'R' is 2th char but start at byte 3 because 'Á' is 2 bytes long
+ * @param txt a '\0' terminated UTF-8 string
+ * @param byte_id byte index
+ * @return character index of the letter at 'byte_id'th position
+ */
+static uint32_t lv_txt_iso8859_1_get_char_id(const char * txt, uint32_t byte_id)
+{
+ LV_UNUSED(txt); /*Unused*/
+ return byte_id; /*In Non encoded no difference*/
+}
+
+/**
+ * Get the number of characters (and NOT bytes) in a string. Decode it with UTF-8 if enabled.
+ * E.g.: "ÁBC" is 3 characters (but 4 bytes)
+ * @param txt a '\0' terminated char string
+ * @return number of characters
+ */
+static uint32_t lv_txt_iso8859_1_get_length(const char * txt)
+{
+ return strlen(txt);
+}
+#else
+
+#error "Invalid character encoding. See `LV_TXT_ENC` in `lv_conf.h`"
+
+#endif
diff --git a/lib/lvgl/src/misc/lv_txt.h b/lib/lvgl/src/misc/lv_txt.h
new file mode 100644
index 00000000..46050dc3
--- /dev/null
+++ b/lib/lvgl/src/misc/lv_txt.h
@@ -0,0 +1,264 @@
+/**
+ * @file lv_txt.h
+ *
+ */
+
+#ifndef LV_TXT_H
+#define LV_TXT_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/*********************
+ * INCLUDES
+ *********************/
+#include "../lv_conf_internal.h"
+
+#include <stdbool.h>
+#include <stdarg.h>
+#include "lv_area.h"
+#include "../font/lv_font.h"
+#include "lv_printf.h"
+#include "lv_types.h"
+
+/*********************
+ * DEFINES
+ *********************/
+#ifndef LV_TXT_COLOR_CMD
+#define LV_TXT_COLOR_CMD "#"
+#endif
+
+#define LV_TXT_ENC_UTF8 1
+#define LV_TXT_ENC_ASCII 2
+
+/**********************
+ * TYPEDEFS
+ **********************/
+
+/**
+ * Options for text rendering.
+ */
+enum {
+ LV_TEXT_FLAG_NONE = 0x00,
+ LV_TEXT_FLAG_RECOLOR = 0x01, /**< Enable parsing of recolor command*/
+ LV_TEXT_FLAG_EXPAND = 0x02, /**< Ignore max-width to avoid automatic word wrapping*/
+ LV_TEXT_FLAG_FIT = 0x04, /**< Max-width is already equal to the longest line. (Used to skip some calculation)*/
+};
+typedef uint8_t lv_text_flag_t;
+
+/**
+ * State machine for text renderer.*/
+enum {
+ LV_TEXT_CMD_STATE_WAIT, /**< Waiting for command*/
+ LV_TEXT_CMD_STATE_PAR, /**< Processing the parameter*/
+ LV_TEXT_CMD_STATE_IN, /**< Processing the command*/
+};
+typedef uint8_t lv_text_cmd_state_t;
+
+/** Label align policy*/
+enum {
+ LV_TEXT_ALIGN_AUTO, /**< Align text auto*/
+ LV_TEXT_ALIGN_LEFT, /**< Align text to left*/
+ LV_TEXT_ALIGN_CENTER, /**< Align text to center*/
+ LV_TEXT_ALIGN_RIGHT, /**< Align text to right*/
+};
+typedef uint8_t lv_text_align_t;
+
+/**********************
+ * GLOBAL PROTOTYPES
+ **********************/
+
+/**
+ * Get size of a text
+ * @param size_res pointer to a 'point_t' variable to store the result
+ * @param text pointer to a text
+ * @param font pointer to font of the text
+ * @param letter_space letter space of the text
+ * @param line_space line space of the text
+ * @param flags settings for the text from ::lv_text_flag_t
+ * @param max_width max width of the text (break the lines to fit this size). Set COORD_MAX to avoid
+ * line breaks
+ */
+void lv_txt_get_size(lv_point_t * size_res, const char * text, const lv_font_t * font, lv_coord_t letter_space,
+ lv_coord_t line_space, lv_coord_t max_width, lv_text_flag_t flag);
+
+/**
+ * Get the next line of text. Check line length and break chars too.
+ * @param txt a '\0' terminated string
+ * @param font pointer to a font
+ * @param letter_space letter space
+ * @param max_width max width of the text (break the lines to fit this size). Set COORD_MAX to avoid
+ * line breaks
+ * @param used_width When used_width != NULL, save the width of this line if
+ * flag == LV_TEXT_FLAG_NONE, otherwise save -1.
+ * @param flags settings for the text from 'txt_flag_type' enum
+ * @return the index of the first char of the new line (in byte index not letter index. With UTF-8
+ * they are different)
+ */
+uint32_t _lv_txt_get_next_line(const char * txt, const lv_font_t * font, lv_coord_t letter_space,
+ lv_coord_t max_width, lv_coord_t * used_width, lv_text_flag_t flag);
+
+/**
+ * Give the length of a text with a given font
+ * @param txt a '\0' terminate string
+ * @param length length of 'txt' in byte count and not characters (Á is 1 character but 2 bytes in
+ * UTF-8)
+ * @param font pointer to a font
+ * @param letter_space letter space
+ * @param flags settings for the text from 'txt_flag_t' enum
+ * @return length of a char_num long text
+ */
+lv_coord_t lv_txt_get_width(const char * txt, uint32_t length, const lv_font_t * font, lv_coord_t letter_space,
+ lv_text_flag_t flag);
+
+/**
+ * Check next character in a string and decide if the character is part of the command or not
+ * @param state pointer to a txt_cmd_state_t variable which stores the current state of command
+ * processing
+ * @param c the current character
+ * @return true: the character is part of a command and should not be written,
+ * false: the character should be written
+ */
+bool _lv_txt_is_cmd(lv_text_cmd_state_t * state, uint32_t c);
+
+/**
+ * Insert a string into an other
+ * @param txt_buf the original text (must be big enough for the result text and NULL terminated)
+ * @param pos position to insert (0: before the original text, 1: after the first char etc.)
+ * @param ins_txt text to insert, must be '\0' terminated
+ */
+void _lv_txt_ins(char * txt_buf, uint32_t pos, const char * ins_txt);
+
+/**
+ * Delete a part of a string
+ * @param txt string to modify, must be '\0' terminated and should point to a heap or stack frame, not read-only memory.
+ * @param pos position where to start the deleting (0: before the first char, 1: after the first
+ * char etc.)
+ * @param len number of characters to delete
+ */
+void _lv_txt_cut(char * txt, uint32_t pos, uint32_t len);
+
+/**
+ * return a new formatted text. Memory will be allocated to store the text.
+ * @param fmt `printf`-like format
+ * @return pointer to the allocated text string.
+ */
+char * _lv_txt_set_text_vfmt(const char * fmt, va_list ap) LV_FORMAT_ATTRIBUTE(1, 0);
+
+/**
+ * Decode two encoded character from a string.
+ * @param txt pointer to '\0' terminated string
+ * @param letter the first decoded Unicode character or 0 on invalid data code
+ * @param letter_next the second decoded Unicode character or 0 on invalid data code
+ * @param ofs start index in 'txt' where to start.
+ * After the call it will point to the next encoded char in 'txt'.
+ * NULL to use txt[0] as index
+ */
+void _lv_txt_encoded_letter_next_2(const char * txt, uint32_t * letter, uint32_t * letter_next, uint32_t * ofs);
+
+/**
+ * Test if char is break char or not (a text can broken here or not)
+ * @param letter a letter
+ * @return false: 'letter' is not break char
+ */
+static inline bool _lv_txt_is_break_char(uint32_t letter)
+{
+ uint8_t i;
+ bool ret = false;
+
+ /* each chinese character can be break */
+ if(letter >= 0x4E00 && letter <= 0x9FA5) {
+ return true;
+ }
+
+ /*Compare the letter to TXT_BREAK_CHARS*/
+ for(i = 0; LV_TXT_BREAK_CHARS[i] != '\0'; i++) {
+ if(letter == (uint32_t)LV_TXT_BREAK_CHARS[i]) {
+ ret = true; /*If match then it is break char*/
+ break;
+ }
+ }
+
+ return ret;
+}
+
+/***************************************************************
+ * GLOBAL FUNCTION POINTERS FOR CHARACTER ENCODING INTERFACE
+ ***************************************************************/
+
+/**
+ * Give the size of an encoded character
+ * @param str pointer to a character in a string
+ * @return length of the encoded character (1,2,3 ...). O in invalid
+ */
+extern uint8_t (*_lv_txt_encoded_size)(const char *);
+
+/**
+ * Convert a Unicode letter to encoded
+ * @param letter_uni a Unicode letter
+ * @return Encoded character in Little Endian to be compatible with C chars (e.g. 'Á', 'Ü')
+ */
+extern uint32_t (*_lv_txt_unicode_to_encoded)(uint32_t);
+
+/**
+ * Convert a wide character, e.g. 'Á' little endian to be compatible with the encoded format.
+ * @param c a wide character
+ * @return `c` in the encoded format
+ */
+extern uint32_t (*_lv_txt_encoded_conv_wc)(uint32_t c);
+
+/**
+ * Decode the next encoded character from a string.
+ * @param txt pointer to '\0' terminated string
+ * @param i start index in 'txt' where to start.
+ * After the call it will point to the next encoded char in 'txt'.
+ * NULL to use txt[0] as index
+ * @return the decoded Unicode character or 0 on invalid data code
+ */
+extern uint32_t (*_lv_txt_encoded_next)(const char *, uint32_t *);
+
+/**
+ * Get the previous encoded character form a string.
+ * @param txt pointer to '\0' terminated string
+ * @param i_start index in 'txt' where to start. After the call it will point to the previous
+ * encoded char in 'txt'.
+ * @return the decoded Unicode character or 0 on invalid data
+ */
+extern uint32_t (*_lv_txt_encoded_prev)(const char *, uint32_t *);
+
+/**
+ * Convert a letter index (in the encoded text) to byte index.
+ * E.g. in UTF-8 "AÁRT" index of 'R' is 2 but start at byte 3 because 'Á' is 2 bytes long
+ * @param txt a '\0' terminated UTF-8 string
+ * @param enc_id letter index
+ * @return byte index of the 'enc_id'th letter
+ */
+extern uint32_t (*_lv_txt_encoded_get_byte_id)(const char *, uint32_t);
+
+/**
+ * Convert a byte index (in an encoded text) to character index.
+ * E.g. in UTF-8 "AÁRT" index of 'R' is 2 but start at byte 3 because 'Á' is 2 bytes long
+ * @param txt a '\0' terminated UTF-8 string
+ * @param byte_id byte index
+ * @return character index of the letter at 'byte_id'th position
+ */
+extern uint32_t (*_lv_txt_encoded_get_char_id)(const char *, uint32_t);
+
+/**
+ * Get the number of characters (and NOT bytes) in a string.
+ * E.g. in UTF-8 "ÁBC" is 3 characters (but 4 bytes)
+ * @param txt a '\0' terminated char string
+ * @return number of characters
+ */
+extern uint32_t (*_lv_txt_get_encoded_length)(const char *);
+
+/**********************
+ * MACROS
+ **********************/
+
+#ifdef __cplusplus
+} /*extern "C"*/
+#endif
+
+#endif /*LV_TXT_H*/
diff --git a/lib/lvgl/src/misc/lv_txt_ap.c b/lib/lvgl/src/misc/lv_txt_ap.c
new file mode 100644
index 00000000..54faf8b4
--- /dev/null
+++ b/lib/lvgl/src/misc/lv_txt_ap.c
@@ -0,0 +1,301 @@
+/**
+ * @file lv_txt_ap.c
+ *
+ */
+
+/*********************
+ * INCLUDES
+ *********************/
+#include <stddef.h>
+#include "lv_bidi.h"
+#include "lv_txt.h"
+#include "lv_txt_ap.h"
+#include "lv_mem.h"
+#include "../draw/lv_draw.h"
+
+/*********************
+ * DEFINES
+ *********************/
+
+/**********************
+ * TYPEDEFS
+ **********************/
+typedef struct {
+ uint8_t char_offset;
+ uint16_t char_end_form;
+ int8_t char_begining_form_offset;
+ int8_t char_middle_form_offset;
+ int8_t char_isolated_form_offset;
+ struct {
+ uint8_t conj_to_previous;
+ uint8_t conj_to_next;
+ } ap_chars_conjunction;
+} ap_chars_map_t;
+
+/**********************
+ * STATIC PROTOTYPES
+ **********************/
+#if LV_USE_ARABIC_PERSIAN_CHARS == 1
+static uint32_t lv_ap_get_char_index(uint16_t c);
+static uint32_t lv_txt_lam_alef(uint32_t ch_curr, uint32_t ch_next);
+static bool lv_txt_is_arabic_vowel(uint16_t c);
+
+/**********************
+ * STATIC VARIABLES
+ **********************/
+
+const ap_chars_map_t ap_chars_map[] = {
+ /*{Key Offset, End, Beginning, Middle, Isolated, {conjunction}}*/
+ {1, 0xFE84, -1, 0, -1, {1, 0}}, // أ
+ {2, 0xFE86, -1, 0, -1, {1, 0}}, // ؤ
+ {3, 0xFE88, -1, 0, -1, {1, 0}}, // ﺇ
+ {4, 0xFE8A, 1, 2, -1, {1, 0}}, // ئ
+ {5, 0xFE8E, -1, 0, -1, {1, 0}}, // آ
+ {6, 0xFE90, 1, 2, -1, {1, 1}}, // ب
+ {92, 0xFB57, 1, 2, -1, {1, 1}}, // پ
+ {8, 0xFE96, 1, 2, -1, {1, 1}}, // ت
+ {9, 0xFE9A, 1, 2, -1, {1, 1}}, // ث
+ {10, 0xFE9E, 1, 2, -1, {1, 1}}, // ج
+ {100, 0xFB7B, 1, 2, -1, {1, 1}}, // چ
+ {11, 0xFEA2, 1, 2, -1, {1, 1}}, // ح
+ {12, 0xFEA6, 1, 2, -1, {1, 1}}, // خ
+ {13, 0xFEAA, -1, 0, -1, {1, 0}}, // د
+ {14, 0xFEAC, -1, 0, -1, {1, 0}}, // ذ
+ {15, 0xFEAE, -1, 0, -1, {1, 0}}, // ر
+ {16, 0xFEB0, -1, 0, -1, {1, 0}}, // ز
+ {118, 0xFB8B, -1, 0, -1, {1, 0}}, // ژ
+ {17, 0xFEB2, 1, 2, -1, {1, 1}}, // س
+ {18, 0xFEB6, 1, 2, -1, {1, 1}}, // ش
+ {19, 0xFEBA, 1, 2, -1, {1, 1}}, // ص
+ {20, 0xFEBE, 1, 2, -1, {1, 1}}, // ض
+ {21, 0xFEC2, 1, 2, -1, {1, 1}}, // ط
+ {22, 0xFEC6, 1, 2, -1, {1, 1}}, // ظ
+ {23, 0xFECA, 1, 2, -1, {1, 1}}, // ع
+ {24, 0xFECE, 1, 2, -1, {1, 1}}, // غ
+ {30, 0x0640, 0, 0, 0, {1, 1}}, // - (mad, hyphen)
+ {31, 0xFED2, 1, 2, -1, {1, 1}}, // ف
+ {32, 0xFED6, 1, 2, -1, {1, 1}}, // ق
+ {135, 0xFB8F, 1, 2, -1, {1, 1}}, // ک
+ {33, 0xFEDA, 1, 2, -1, {1, 1}}, // ﻙ
+ {141, 0xFB93, 1, 2, -1, {1, 1}}, // گ
+ {34, 0xFEDE, 1, 2, -1, {1, 1}}, // ل
+ {35, 0xFEE2, 1, 2, -1, {1, 1}}, // م
+ {36, 0xFEE6, 1, 2, -1, {1, 1}}, // ن
+ {38, 0xFEEE, -1, 0, -1, {1, 0}}, // و
+ {37, 0xFEEA, 1, 2, -1, {1, 1}}, // ه
+ {39, 0xFEF0, 0, 0, -1, {1, 0}}, // ى
+ {40, 0xFEF2, 1, 2, -1, {1, 1}}, // ي
+ {170, 0xFBFD, 1, 2, -1, {1, 1}}, // ی
+ {7, 0xFE94, 1, 2, -1, {1, 0}}, // ة
+ {206, 0x06F0, 1, 2, -1, {0, 0}}, // ۰
+ {207, 0x06F1, 0, 0, 0, {0, 0}}, // ۱
+ {208, 0x06F2, 0, 0, 0, {0, 0}}, // ۲
+ {209, 0x06F3, 0, 0, 0, {0, 0}}, // ۳
+ {210, 0x06F4, 0, 0, 0, {0, 0}}, // ۴
+ {211, 0x06F5, 0, 0, 0, {0, 0}}, // ۵
+ {212, 0x06F6, 0, 0, 0, {0, 0}}, // ۶
+ {213, 0x06F7, 0, 0, 0, {0, 0}}, // ۷
+ {214, 0x06F8, 0, 0, 0, {0, 0}}, // ۸
+ {215, 0x06F9, 0, 0, 0, {0, 0}}, // ۹
+ LV_AP_END_CHARS_LIST
+};
+/**********************
+* MACROS
+**********************/
+
+/**********************
+* GLOBAL FUNCTIONS
+**********************/
+uint32_t _lv_txt_ap_calc_bytes_cnt(const char * txt)
+{
+ uint32_t txt_length = 0;
+ uint32_t chars_cnt = 0;
+ uint32_t current_ap_idx = 0;
+ uint32_t i, j;
+ uint32_t ch_enc;
+
+ txt_length = _lv_txt_get_encoded_length(txt);
+
+ i = 0;
+ j = 0;
+ while(i < txt_length) {
+ ch_enc = _lv_txt_encoded_next(txt, &j);
+ current_ap_idx = lv_ap_get_char_index(ch_enc);
+
+ if(current_ap_idx != LV_UNDEF_ARABIC_PERSIAN_CHARS)
+ ch_enc = ap_chars_map[current_ap_idx].char_end_form;
+
+ if(ch_enc < 0x80)
+ chars_cnt++;
+ else if(ch_enc < 0x0800)
+ chars_cnt += 2;
+ else if(ch_enc < 0x010000)
+ chars_cnt += 3;
+ else
+ chars_cnt += 4;
+
+ i++;
+ }
+
+ return chars_cnt + 1;
+}
+
+void _lv_txt_ap_proc(const char * txt, char * txt_out)
+{
+ uint32_t txt_length = 0;
+ uint32_t index_current, idx_next, idx_previous, i, j;
+ uint32_t * ch_enc;
+ uint32_t * ch_fin;
+ char * txt_out_temp;
+
+ txt_length = _lv_txt_get_encoded_length(txt);
+
+ ch_enc = (uint32_t *)lv_mem_alloc(sizeof(uint32_t) * (txt_length + 1));
+ ch_fin = (uint32_t *)lv_mem_alloc(sizeof(uint32_t) * (txt_length + 1));
+
+ i = 0;
+ j = 0;
+ while(j < txt_length)
+ ch_enc[j++] = _lv_txt_encoded_next(txt, &i);
+
+ ch_enc[j] = 0;
+
+ i = 0;
+ j = 0;
+ idx_previous = LV_UNDEF_ARABIC_PERSIAN_CHARS;
+ while(i < txt_length) {
+ index_current = lv_ap_get_char_index(ch_enc[i]);
+ idx_next = lv_ap_get_char_index(ch_enc[i + 1]);
+
+ if(lv_txt_is_arabic_vowel(ch_enc[i])) { // Current character is a vowel
+ ch_fin[j] = ch_enc[i];
+ i++;
+ j++;
+ continue; // Skip this character
+ }
+ else if(lv_txt_is_arabic_vowel(ch_enc[i + 1])) { // Next character is a vowel
+ idx_next = lv_ap_get_char_index(ch_enc[i + 2]); // Skip the vowel character to join with the character after it
+ }
+
+ if(index_current == LV_UNDEF_ARABIC_PERSIAN_CHARS) {
+ ch_fin[j] = ch_enc[i];
+ j++;
+ i++;
+ idx_previous = LV_UNDEF_ARABIC_PERSIAN_CHARS;
+ continue;
+ }
+
+ uint8_t conjunction_to_previuse = (i == 0 ||
+ idx_previous == LV_UNDEF_ARABIC_PERSIAN_CHARS) ? 0 : ap_chars_map[idx_previous].ap_chars_conjunction.conj_to_next;
+ uint8_t conjunction_to_next = ((i == txt_length - 1) ||
+ idx_next == LV_UNDEF_ARABIC_PERSIAN_CHARS) ? 0 : ap_chars_map[idx_next].ap_chars_conjunction.conj_to_previous;
+
+ uint32_t lam_alef = lv_txt_lam_alef(index_current, idx_next);
+ if(lam_alef) {
+ if(conjunction_to_previuse) {
+ lam_alef ++;
+ }
+ ch_fin[j] = lam_alef;
+ idx_previous = LV_UNDEF_ARABIC_PERSIAN_CHARS;
+ i += 2;
+ j++;
+ continue;
+ }
+
+ if(conjunction_to_previuse && conjunction_to_next)
+ ch_fin[j] = ap_chars_map[index_current].char_end_form + ap_chars_map[index_current].char_middle_form_offset;
+ else if(!conjunction_to_previuse && conjunction_to_next)
+ ch_fin[j] = ap_chars_map[index_current].char_end_form + ap_chars_map[index_current].char_begining_form_offset;
+ else if(conjunction_to_previuse && !conjunction_to_next)
+ ch_fin[j] = ap_chars_map[index_current].char_end_form;
+ else
+ ch_fin[j] = ap_chars_map[index_current].char_end_form + ap_chars_map[index_current].char_isolated_form_offset;
+ idx_previous = index_current;
+ i++;
+ j++;
+ }
+ ch_fin[j] = 0;
+ for(i = 0; i < txt_length; i++)
+ ch_enc[i] = 0;
+ for(i = 0; i < j; i++)
+ ch_enc[i] = ch_fin[i];
+ lv_mem_free(ch_fin);
+
+ txt_out_temp = txt_out;
+ i = 0;
+
+ while(i < txt_length) {
+ if(ch_enc[i] < 0x80) {
+ *(txt_out_temp++) = ch_enc[i] & 0xFF;
+ }
+ else if(ch_enc[i] < 0x0800) {
+ *(txt_out_temp++) = ((ch_enc[i] >> 6) & 0x1F) | 0xC0;
+ *(txt_out_temp++) = ((ch_enc[i] >> 0) & 0x3F) | 0x80;
+ }
+ else if(ch_enc[i] < 0x010000) {
+ *(txt_out_temp++) = ((ch_enc[i] >> 12) & 0x0F) | 0xE0;
+ *(txt_out_temp++) = ((ch_enc[i] >> 6) & 0x3F) | 0x80;
+ *(txt_out_temp++) = ((ch_enc[i] >> 0) & 0x3F) | 0x80;
+ }
+ else if(ch_enc[i] < 0x110000) {
+ *(txt_out_temp++) = ((ch_enc[i] >> 18) & 0x07) | 0xF0;
+ *(txt_out_temp++) = ((ch_enc[i] >> 12) & 0x3F) | 0x80;
+ *(txt_out_temp++) = ((ch_enc[i] >> 6) & 0x3F) | 0x80;
+ *(txt_out_temp++) = ((ch_enc[i] >> 0) & 0x3F) | 0x80;
+ }
+
+ i++;
+ }
+ *(txt_out_temp) = '\0';
+ lv_mem_free(ch_enc);
+}
+/**********************
+* STATIC FUNCTIONS
+**********************/
+
+static uint32_t lv_ap_get_char_index(uint16_t c)
+{
+ for(uint8_t i = 0; ap_chars_map[i].char_end_form; i++) {
+ if(c == (ap_chars_map[i].char_offset + LV_AP_ALPHABET_BASE_CODE))
+ return i;
+ else if(c == ap_chars_map[i].char_end_form //is it an End form
+ || c == (ap_chars_map[i].char_end_form + ap_chars_map[i].char_begining_form_offset) //is it a Beginning form
+ || c == (ap_chars_map[i].char_end_form + ap_chars_map[i].char_middle_form_offset) //is it a middle form
+ || c == (ap_chars_map[i].char_end_form + ap_chars_map[i].char_isolated_form_offset)) { //is it an isolated form
+ return i;
+ }
+ }
+ return LV_UNDEF_ARABIC_PERSIAN_CHARS;
+}
+
+static uint32_t lv_txt_lam_alef(uint32_t ch_curr, uint32_t ch_next)
+{
+ uint32_t ch_code = 0;
+ if(ap_chars_map[ch_curr].char_offset != 34) {
+ return 0;
+ }
+ if(ch_next == LV_UNDEF_ARABIC_PERSIAN_CHARS) {
+ return 0;
+ }
+ ch_code = ap_chars_map[ch_next].char_offset + LV_AP_ALPHABET_BASE_CODE;
+ if(ch_code == 0x0622) {
+ return 0xFEF5; // (lam-alef) mad
+ }
+ if(ch_code == 0x0623) {
+ return 0xFEF7; // (lam-alef) top hamza
+ }
+ if(ch_code == 0x0625) {
+ return 0xFEF9; // (lam-alef) bot hamza
+ }
+ if(ch_code == 0x0627) {
+ return 0xFEFB; // (lam-alef) alef
+ }
+ return 0;
+}
+
+static bool lv_txt_is_arabic_vowel(uint16_t c)
+{
+ return (c >= 0x064B) && (c <= 0x0652);
+}
+
+#endif
diff --git a/lib/lvgl/src/misc/lv_txt_ap.h b/lib/lvgl/src/misc/lv_txt_ap.h
new file mode 100644
index 00000000..e2d94b8d
--- /dev/null
+++ b/lib/lvgl/src/misc/lv_txt_ap.h
@@ -0,0 +1,49 @@
+/**
+ * @file lv_txt_ap.h
+ *
+ */
+
+#ifndef LV_TXT_AP_H
+#define LV_TXT_AP_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/*********************
+ * INCLUDES
+ *********************/
+#include <stddef.h>
+#include "lv_txt.h"
+#include "../draw/lv_draw.h"
+
+#if LV_USE_ARABIC_PERSIAN_CHARS == 1
+
+/*********************
+ * DEFINES
+ *********************/
+
+#define LV_UNDEF_ARABIC_PERSIAN_CHARS (UINT32_MAX)
+#define LV_AP_ALPHABET_BASE_CODE 0x0622
+#define LV_AP_END_CHARS_LIST {0,0,0,0,0,{0,0}}
+/**********************
+ * TYPEDEFS
+ **********************/
+
+/**********************
+ * GLOBAL PROTOTYPES
+ **********************/
+uint32_t _lv_txt_ap_calc_bytes_cnt(const char * txt);
+void _lv_txt_ap_proc(const char * txt, char * txt_out);
+
+/**********************
+ * MACROS
+ **********************/
+
+#endif // LV_USE_ARABIC_PERSIAN_CHARS
+
+#ifdef __cplusplus
+} /*extern "C"*/
+#endif
+
+#endif /*LV_TXT_AP_H*/
diff --git a/lib/lvgl/src/misc/lv_types.h b/lib/lvgl/src/misc/lv_types.h
new file mode 100644
index 00000000..84aee103
--- /dev/null
+++ b/lib/lvgl/src/misc/lv_types.h
@@ -0,0 +1,94 @@
+/**
+ * @file lv_types.h
+ *
+ */
+
+#ifndef LV_TYPES_H
+#define LV_TYPES_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/*********************
+ * INCLUDES
+ *********************/
+#include <stdint.h>
+
+/*********************
+ * DEFINES
+ *********************/
+
+// If __UINTPTR_MAX__ or UINTPTR_MAX are available, use them to determine arch size
+#if defined(__UINTPTR_MAX__) && __UINTPTR_MAX__ > 0xFFFFFFFF
+#define LV_ARCH_64
+
+#elif defined(UINTPTR_MAX) && UINTPTR_MAX > 0xFFFFFFFF
+#define LV_ARCH_64
+
+// Otherwise use compiler-dependent means to determine arch size
+#elif defined(_WIN64) || defined(__x86_64__) || defined(__ppc64__) || defined (__aarch64__)
+#define LV_ARCH_64
+
+#endif
+
+/**********************
+ * TYPEDEFS
+ **********************/
+
+/**
+ * LVGL error codes.
+ */
+enum {
+ LV_RES_INV = 0, /*Typically indicates that the object is deleted (become invalid) in the action
+ function or an operation was failed*/
+ LV_RES_OK, /*The object is valid (no deleted) after the action*/
+};
+typedef uint8_t lv_res_t;
+
+#if defined(__cplusplus) || __STDC_VERSION__ >= 199901L
+// If c99 or newer, use the definition of uintptr_t directly from <stdint.h>
+typedef uintptr_t lv_uintptr_t;
+
+#else
+
+// Otherwise, use the arch size determination
+#ifdef LV_ARCH_64
+typedef uint64_t lv_uintptr_t;
+#else
+typedef uint32_t lv_uintptr_t;
+#endif
+
+#endif
+
+/**********************
+ * GLOBAL PROTOTYPES
+ **********************/
+
+/**********************
+ * MACROS
+ **********************/
+
+#define LV_UNUSED(x) ((void)x)
+
+#define _LV_CONCAT(x, y) x ## y
+#define LV_CONCAT(x, y) _LV_CONCAT(x, y)
+
+#define _LV_CONCAT3(x, y, z) x ## y ## z
+#define LV_CONCAT3(x, y, z) _LV_CONCAT3(x, y, z)
+
+#if defined(PYCPARSER) || defined(__CC_ARM)
+#define LV_FORMAT_ATTRIBUTE(fmtstr, vararg)
+#elif defined(__GNUC__) && ((__GNUC__ == 4 && __GNUC_MINOR__ >= 4) || __GNUC__ > 4)
+#define LV_FORMAT_ATTRIBUTE(fmtstr, vararg) __attribute__((format(gnu_printf, fmtstr, vararg)))
+#elif (defined(__clang__) || defined(__GNUC__) || defined(__GNUG__))
+#define LV_FORMAT_ATTRIBUTE(fmtstr, vararg) __attribute__((format(printf, fmtstr, vararg)))
+#else
+#define LV_FORMAT_ATTRIBUTE(fmtstr, vararg)
+#endif
+
+#ifdef __cplusplus
+} /*extern "C"*/
+#endif
+
+#endif /*LV_TYPES_H*/
diff --git a/lib/lvgl/src/misc/lv_utils.c b/lib/lvgl/src/misc/lv_utils.c
new file mode 100644
index 00000000..e17a231d
--- /dev/null
+++ b/lib/lvgl/src/misc/lv_utils.c
@@ -0,0 +1,79 @@
+/**
+ * @file lv_utils.c
+ *
+ */
+
+/*********************
+ * INCLUDES
+ *********************/
+#include <stddef.h>
+
+#include "lv_utils.h"
+
+/*********************
+ * DEFINES
+ *********************/
+
+/**********************
+ * TYPEDEFS
+ **********************/
+
+/**********************
+ * STATIC PROTOTYPES
+ **********************/
+
+/**********************
+ * STATIC VARIABLES
+ **********************/
+
+/**********************
+ * MACROS
+ **********************/
+
+/**********************
+ * GLOBAL FUNCTIONS
+ **********************/
+
+/** Searches base[0] to base[n - 1] for an item that matches *key.
+ *
+ * @note The function cmp must return negative if its first
+ * argument (the search key) is less than its second (a table entry),
+ * zero if equal, and positive if greater.
+ *
+ * @note Items in the array must be in ascending order.
+ *
+ * @param key Pointer to item being searched for
+ * @param base Pointer to first element to search
+ * @param n Number of elements
+ * @param size Size of each element
+ * @param cmp Pointer to comparison function (see #unicode_list_compare as a comparison function
+ * example)
+ *
+ * @return a pointer to a matching item, or NULL if none exists.
+ */
+void * _lv_utils_bsearch(const void * key, const void * base, uint32_t n, uint32_t size,
+ int32_t (*cmp)(const void * pRef, const void * pElement))
+{
+ const char * middle;
+ int32_t c;
+
+ for(middle = base; n != 0;) {
+ middle += (n / 2) * size;
+ if((c = (*cmp)(key, middle)) > 0) {
+ n = (n / 2) - ((n & 1) == 0);
+ base = (middle += size);
+ }
+ else if(c < 0) {
+ n /= 2;
+ middle = base;
+ }
+ else {
+ return (char *)middle;
+ }
+ }
+ return NULL;
+}
+
+/**********************
+ * STATIC FUNCTIONS
+ **********************/
diff --git a/lib/lvgl/src/misc/lv_utils.h b/lib/lvgl/src/misc/lv_utils.h
new file mode 100644
index 00000000..84d2bb95
--- /dev/null
+++ b/lib/lvgl/src/misc/lv_utils.h
@@ -0,0 +1,58 @@
+/**
+ * @file lv_utils.h
+ *
+ */
+
+#ifndef LV_UTILS_H
+#define LV_UTILS_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/*********************
+ * INCLUDES
+ *********************/
+#include <stdint.h>
+
+/*********************
+ * DEFINES
+ *********************/
+
+/**********************
+ * TYPEDEFS
+ **********************/
+
+/**********************
+ * GLOBAL PROTOTYPES
+ **********************/
+
+/** Searches base[0] to base[n - 1] for an item that matches *key.
+ *
+ * @note The function cmp must return negative if it's first
+ * argument (the search key) is less that it's second (a table entry),
+ * zero if equal, and positive if greater.
+ *
+ * @note Items in the array must be in ascending order.
+ *
+ * @param key Pointer to item being searched for
+ * @param base Pointer to first element to search
+ * @param n Number of elements
+ * @param size Size of each element
+ * @param cmp Pointer to comparison function (see #unicode_list_compare as a comparison function
+ * example)
+ *
+ * @return a pointer to a matching item, or NULL if none exists.
+ */
+void * _lv_utils_bsearch(const void * key, const void * base, uint32_t n, uint32_t size,
+ int32_t (*cmp)(const void * pRef, const void * pElement));
+
+/**********************
+ * MACROS
+ **********************/
+
+#ifdef __cplusplus
+} /*extern "C"*/
+#endif
+
+#endif