tft lvgl test ok

This commit is contained in:
TuxMonkey
2026-07-20 21:59:16 +08:00
parent 8928b7c7db
commit b2169513e1
766 changed files with 443311 additions and 22 deletions

View File

@@ -29,6 +29,7 @@
#include "ins_task.h"
#include "buzzer.h"
#include "tft.h"
#include "prts.h"
// #include "remoteTask.h"
//
// #include "bsp_dwt.h"
@@ -46,6 +47,8 @@
/* Private define ------------------------------------------------------------*/
/* USER CODE BEGIN PD */
#define PRTS_STATUS_UPDATE_PERIOD_MS 50U
#define PRTS_TASK_SLEEP_CAP_MS 2U
/* USER CODE END PD */
@@ -158,6 +161,19 @@ const osThreadAttr_t buzzer_attributes = {
osThreadId buzzerHandle;
extern void buzzerTask(void const *argument);
static StackType_t prtsTaskStack[1024];
static StaticTask_t prtsTaskControlBlock;
const osThreadAttr_t prts_attributes = {
.name = "prts",
.cb_mem = &prtsTaskControlBlock,
.cb_size = sizeof(prtsTaskControlBlock),
.stack_mem = prtsTaskStack,
.stack_size = sizeof(prtsTaskStack),
.priority = osPriorityBelowNormal
};
osThreadId_t prtsHandle;
void StartPRTSTask(void *argument);
/* USER CODE END FunctionPrototypes */
void StartDefaultTask(void *argument);
@@ -268,6 +284,7 @@ void MX_FREERTOS_Init(void) {
// 创建线程
daemonHandle = osThreadNew(StartDaemonTask, NULL, &daemon_attributes); // <--- 换成新的函数名
buzzerHandle = osThreadNew(buzzerTask, NULL, &buzzer_attributes);
prtsHandle = osThreadNew(StartPRTSTask, NULL, &prts_attributes);
/* USER CODE END RTOS_THREADS */
/* USER CODE BEGIN RTOS_EVENTS */
@@ -289,15 +306,9 @@ __weak void StartDefaultTask(void *argument)
MX_USB_DEVICE_Init();
/* USER CODE BEGIN StartDefaultTask */
/* Infinite loop */
TFT_Init();
static uint32_t tft_refresh_tick = 0U;
for (;;)
{
if ((tft_refresh_tick++ % 100U) == 0U)
{
TFT_ShowStatus(BMI088.Temperature, RobotMode);
}
osDelay(1);
osDelay(1000);
}
/* USER CODE END StartDefaultTask */
}
@@ -479,5 +490,52 @@ __attribute__((noreturn)) void StartDaemonTask(void *argument)
// 注意:把你原来写在 freertos.c 里的那个 __weak void DaemonTask() 整个删掉,防止干扰!
__attribute__((noreturn)) void StartPRTSTask(void *argument)
{
(void) argument;
bool prts_ok = PRTS_Init();
uint32_t refresh_ms = PRTS_STATUS_UPDATE_PERIOD_MS;
if (!prts_ok)
{
TFT_Init();
}
for (;;)
{
uint32_t delay_ms = PRTS_TASK_SLEEP_CAP_MS;
if (refresh_ms >= PRTS_STATUS_UPDATE_PERIOD_MS)
{
if (prts_ok)
{
PRTS_UpdateStatus(BMI088.Temperature, RobotMode);
}
else
{
TFT_ShowStatus(BMI088.Temperature, RobotMode);
}
refresh_ms = 0U;
}
if (prts_ok)
{
delay_ms = PRTS_Task();
if (delay_ms == 0U || delay_ms > PRTS_TASK_SLEEP_CAP_MS)
{
delay_ms = PRTS_TASK_SLEEP_CAP_MS;
}
}
else
{
prts_ok = PRTS_Init();
}
osDelay(delay_ms);
refresh_ms += delay_ms;
}
}
/* USER CODE END Application */

View File

@@ -35,7 +35,7 @@ static const uint8_t tft_font5x7[][TFT_FONT_H] = {
{0x00, 0x00, 0x00, 0x00, 0x00, 0x0C, 0x0C}, /* . */
{0x00, 0x0C, 0x0C, 0x00, 0x0C, 0x0C, 0x00}, /* : */
{0x0E, 0x11, 0x13, 0x15, 0x19, 0x11, 0x0E}, /* 0 */
{0x04, 0x0C, 0x04, 0x04, 0x04, 0x04, 0x0E}, /* 1 */
{0x04, 0x06, 0x04, 0x04, 0x04, 0x04, 0x0E}, /* 1 */
{0x0E, 0x11, 0x10, 0x08, 0x04, 0x02, 0x1F}, /* 2 */
{0x1F, 0x08, 0x04, 0x08, 0x10, 0x11, 0x0E}, /* 3 */
{0x08, 0x0C, 0x0A, 0x09, 0x1F, 0x08, 0x08}, /* 4 */
@@ -188,6 +188,25 @@ static void tft_write_bytes(const uint8_t *data, uint32_t len)
#endif
}
static void tft_write_bytes_continuous(const uint8_t *data, uint32_t len)
{
#if TFT_USE_SOFT_SPI
tft_write_bytes(data, len);
#else
while (len > 0U)
{
uint16_t chunk = len > UINT16_MAX ? UINT16_MAX : (uint16_t) len;
if (HAL_SPI_Transmit(&TFT_SPI_UNIT, (uint8_t *) data, chunk, TFT_SPI_TIMEOUT_MS) != HAL_OK)
{
tft_transfer_ok = false;
return;
}
data += chunk;
len -= chunk;
}
#endif
}
static void lcd_write_reg(uint8_t reg)
{
tft_cs_low();
@@ -237,6 +256,28 @@ static void tft_write_color_block(uint16_t color, uint32_t count)
tft_cs_high();
}
void TFT_WriteColorData(const uint8_t *data, uint32_t len)
{
if (data == NULL || len == 0U)
{
return;
}
tft_cs_low();
tft_dc_data();
#if TFT_HW_SPI_STREAM_COLOR
tft_write_bytes_continuous(data, len);
#else
tft_write_bytes(data, len);
#endif
tft_cs_high();
}
bool TFT_IsReady(void)
{
return tft_initialized && tft_transfer_ok;
}
void LCD_Address_Set(uint16_t x1, uint16_t y1, uint16_t x2, uint16_t y2)
{
uint16_t xs = x1 + TFT_X_OFFSET;

View File

@@ -4,6 +4,7 @@
#include "main.h"
#include "robot_def.h"
#include "spi.h"
#include <stdbool.h>
#include <stdint.h>
#ifdef __cplusplus
@@ -14,6 +15,7 @@ extern "C" {
#define TFT_USE_SOFT_SPI 0U
#define TFT_HW_SPI_BYTE_MODE 1U
#define TFT_HW_SPI_PULSE_CS 1U
#define TFT_HW_SPI_STREAM_COLOR 1U
#if (TFT_USE_HORIZONTAL == 0U) || (TFT_USE_HORIZONTAL == 1U)
#define TFT_LCD_W 240U
@@ -87,6 +89,8 @@ void TFT_FillRect(uint16_t x, uint16_t y, uint16_t width, uint16_t height, uint1
void TFT_DrawPoint(uint16_t x, uint16_t y, uint16_t color);
void TFT_DrawString(uint16_t x, uint16_t y, const char *text, uint16_t fc, uint16_t bc, uint8_t scale);
void TFT_ShowStatus(float temperature_c, RobotMode_t mode);
bool TFT_IsReady(void);
void TFT_WriteColorData(const uint8_t *data, uint32_t len);
const char *TFT_RobotModeText(RobotMode_t mode);
void LCD_Init(void);

View File

@@ -0,0 +1,8 @@
MIT licence
Copyright (c) 2025 LVGL Kft
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.

File diff suppressed because it is too large Load Diff

View File

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

View File

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

View File

@@ -0,0 +1,131 @@
/**
* @file lvgl_private.h
*
*/
#ifndef LVGL_PRIVATE_H
#define LVGL_PRIVATE_H
#ifdef __cplusplus
extern "C" {
#endif
/*********************
* INCLUDES
*********************/
#include "src/core/lv_global.h"
#include "src/display/lv_display_private.h"
#include "src/indev/lv_indev_private.h"
#include "src/misc/lv_text_private.h"
#include "src/misc/cache/lv_cache.h"
#include "src/misc/cache/lv_cache_entry_private.h"
#include "src/misc/cache/lv_cache_private.h"
#include "src/layouts/lv_layout_private.h"
#include "src/stdlib/lv_mem_private.h"
#include "src/others/file_explorer/lv_file_explorer_private.h"
#include "src/others/fragment/lv_fragment_private.h"
#include "src/others/translation/lv_translation_private.h"
#include "src/libs/qrcode/lv_qrcode_private.h"
#include "src/libs/barcode/lv_barcode_private.h"
#include "src/draw/lv_draw_triangle_private.h"
#include "src/draw/lv_draw_private.h"
#include "src/draw/lv_draw_rect_private.h"
#include "src/draw/lv_draw_image_private.h"
#include "src/draw/lv_image_decoder_private.h"
#include "src/draw/lv_draw_label_private.h"
#include "src/draw/lv_draw_vector_private.h"
#include "src/draw/lv_draw_buf_private.h"
#include "src/draw/sw/lv_draw_sw_private.h"
#include "src/draw/sw/lv_draw_sw_mask_private.h"
#include "src/draw/sw/blend/lv_draw_sw_blend_private.h"
#include "src/drivers/libinput/lv_xkb_private.h"
#include "src/drivers/libinput/lv_libinput_private.h"
#include "src/drivers/evdev/lv_evdev_private.h"
#include "src/themes/lv_theme_private.h"
#include "src/core/lv_refr_private.h"
#include "src/core/lv_obj_style_private.h"
#include "src/core/lv_obj_private.h"
#include "src/core/lv_obj_scroll_private.h"
#include "src/core/lv_obj_draw_private.h"
#include "src/core/lv_obj_class_private.h"
#include "src/core/lv_group_private.h"
#include "src/core/lv_obj_event_private.h"
#include "src/core/lv_observer_private.h"
#include "src/debugging/sysmon/lv_sysmon_private.h"
#include "src/debugging/monkey/lv_monkey_private.h"
#include "src/font/fmt_txt/lv_font_fmt_txt_private.h"
#include "src/misc/lv_timer_private.h"
#include "src/misc/lv_area_private.h"
#include "src/misc/lv_fs_private.h"
#include "src/misc/lv_profiler_builtin_private.h"
#include "src/misc/lv_event_private.h"
#include "src/misc/lv_bidi_private.h"
#include "src/misc/lv_rb_private.h"
#include "src/misc/lv_style_private.h"
#include "src/misc/lv_color_op_private.h"
#include "src/misc/lv_anim_private.h"
#include "src/misc/lv_anim_timeline_private.h"
#include "src/widgets/msgbox/lv_msgbox_private.h"
#include "src/widgets/buttonmatrix/lv_buttonmatrix_private.h"
#include "src/widgets/slider/lv_slider_private.h"
#include "src/widgets/switch/lv_switch_private.h"
#include "src/widgets/calendar/lv_calendar_private.h"
#include "src/widgets/imagebutton/lv_imagebutton_private.h"
#include "src/widgets/bar/lv_bar_private.h"
#include "src/widgets/image/lv_image_private.h"
#include "src/widgets/textarea/lv_textarea_private.h"
#include "src/widgets/table/lv_table_private.h"
#include "src/widgets/checkbox/lv_checkbox_private.h"
#include "src/widgets/roller/lv_roller_private.h"
#include "src/widgets/win/lv_win_private.h"
#include "src/widgets/keyboard/lv_keyboard_private.h"
#include "src/widgets/line/lv_line_private.h"
#include "src/widgets/animimage/lv_animimage_private.h"
#include "src/widgets/dropdown/lv_dropdown_private.h"
#include "src/widgets/menu/lv_menu_private.h"
#include "src/widgets/chart/lv_chart_private.h"
#include "src/widgets/button/lv_button_private.h"
#include "src/widgets/scale/lv_scale_private.h"
#include "src/widgets/led/lv_led_private.h"
#include "src/widgets/arc/lv_arc_private.h"
#include "src/widgets/tileview/lv_tileview_private.h"
#include "src/widgets/spinbox/lv_spinbox_private.h"
#include "src/widgets/span/lv_span_private.h"
#include "src/widgets/label/lv_label_private.h"
#include "src/widgets/canvas/lv_canvas_private.h"
#include "src/widgets/tabview/lv_tabview_private.h"
#include "src/widgets/3dtexture/lv_3dtexture_private.h"
#include "src/widgets/ime/lv_ime_pinyin_private.h"
#include "src/tick/lv_tick_private.h"
#include "src/stdlib/builtin/lv_tlsf_private.h"
#include "src/libs/rlottie/lv_rlottie_private.h"
#include "src/widgets/lottie/lv_lottie_private.h"
#include "src/osal/lv_os_private.h"
/*********************
* DEFINES
*********************/
/**********************
* TYPEDEFS
**********************/
/**********************
* GLOBAL PROTOTYPES
**********************/
/**********************
* MACROS
**********************/
#ifdef __cplusplus
} /*extern "C"*/
#endif
#endif /*LVGL_PRIVATE_H*/

View File

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

View File

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

View File

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

View File

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

File diff suppressed because it is too large Load Diff

View File

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

View File

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

View File

@@ -0,0 +1,89 @@
/**
* @file lv_obj_class.h
*
*/
#ifndef LV_OBJ_CLASS_H
#define LV_OBJ_CLASS_H
#ifdef __cplusplus
extern "C" {
#endif
/*********************
* INCLUDES
*********************/
#include "../misc/lv_types.h"
#include "../misc/lv_area.h"
#include "lv_obj_property.h"
/*********************
* DEFINES
*********************/
/**********************
* TYPEDEFS
**********************/
typedef enum {
LV_OBJ_CLASS_EDITABLE_INHERIT, /**< Check the base class. Must have 0 value to let zero initialized class inherit*/
LV_OBJ_CLASS_EDITABLE_TRUE,
LV_OBJ_CLASS_EDITABLE_FALSE,
} lv_obj_class_editable_t;
typedef enum {
LV_OBJ_CLASS_GROUP_DEF_INHERIT, /**< Check the base class. Must have 0 value to let zero initialized class inherit*/
LV_OBJ_CLASS_GROUP_DEF_TRUE,
LV_OBJ_CLASS_GROUP_DEF_FALSE,
} lv_obj_class_group_def_t;
typedef enum {
LV_OBJ_CLASS_THEME_INHERITABLE_FALSE, /**< Do not inherit theme from base class. */
LV_OBJ_CLASS_THEME_INHERITABLE_TRUE,
} lv_obj_class_theme_inheritable_t;
typedef void (*lv_obj_class_event_cb_t)(lv_obj_class_t * class_p, lv_event_t * e);
/**********************
* GLOBAL PROTOTYPES
**********************/
/**
* Create an object form a class descriptor
* @param class_p pointer to a class
* @param parent pointer to an object where the new object should be created
* @return pointer to the created object
*/
lv_obj_t * lv_obj_class_create_obj(const lv_obj_class_t * class_p, lv_obj_t * parent);
void lv_obj_class_init_obj(lv_obj_t * obj);
bool lv_obj_is_editable(lv_obj_t * obj);
bool lv_obj_is_group_def(lv_obj_t * obj);
#if LV_USE_EXT_DATA
/**
* @brief Associates an array of external data pointers with an LVGL object
*
* Associates custom user data with an LVGL object and specifies a destructor function
* that will be automatically invoked when the object is deleted to properly clean up
* the associated resources.
*
* @param obj Target LVGL object
* @param data User-defined data pointer to associate with a object
* @param free_cb Cleanup function called for each non-NULL data pointer during
* object deletion. Receives single data pointer as parameter.
* NULL means no automatic cleanup.
*/
void lv_obj_set_external_data(lv_obj_t * obj, void * data, void (* free_cb)(void * data));
#endif
/**********************
* MACROS
**********************/
#ifdef __cplusplus
} /*extern "C"*/
#endif
#endif /*LV_OBJ_CLASS_H*/

View File

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

View File

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

View File

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

View File

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

View File

@@ -0,0 +1,491 @@
/**
* @file lv_obj_event.c
*
*/
/*********************
* INCLUDES
*********************/
#include "../misc/lv_event_private.h"
#include "lv_obj_event_private.h"
#include "lv_obj_class_private.h"
#include "lv_obj_private.h"
#include "../indev/lv_indev.h"
#include "../indev/lv_indev_private.h"
/*********************
* DEFINES
*********************/
#define MY_CLASS (&lv_obj_class)
/**********************
* TYPEDEFS
**********************/
/**********************
* STATIC PROTOTYPES
**********************/
static lv_result_t event_send_core(lv_event_t * e);
static bool event_is_bubbled(lv_event_t * e);
static bool event_is_trickled(lv_event_t * e);
/**********************
* STATIC VARIABLES
**********************/
/**********************
* MACROS
**********************/
#if LV_USE_LOG && LV_LOG_TRACE_EVENT
#define LV_TRACE_EVENT(...) LV_LOG_TRACE(__VA_ARGS__)
#else
#define LV_TRACE_EVENT(...)
#endif
/**********************
* GLOBAL FUNCTIONS
**********************/
lv_result_t lv_obj_send_event(lv_obj_t * obj, lv_event_code_t event_code, void * param)
{
if(obj == NULL) return LV_RESULT_OK;
LV_ASSERT_OBJ(obj, MY_CLASS);
lv_event_t e;
e.current_target = obj;
e.original_target = obj;
e.code = event_code;
e.user_data = NULL;
e.param = param;
e.deleted = 0;
e.stop_bubbling = 0;
e.stop_processing = 0;
e.stop_trickling = 0;
lv_event_push(&e);
/*Send the event*/
lv_result_t res = event_send_core(&e);
/*Remove this element from the list*/
lv_event_pop(&e);
return res;
}
lv_result_t lv_obj_event_base(const lv_obj_class_t * class_p, lv_event_t * e)
{
const lv_obj_class_t * base;
if(class_p == NULL) base = ((lv_obj_t *)e->current_target)->class_p;
else base = class_p->base_class;
/*Find a base in which call the ancestor's event handler_cb if set*/
while(base && base->event_cb == NULL) base = base->base_class;
if(base == NULL) return LV_RESULT_OK;
if(base->event_cb == NULL) return LV_RESULT_OK;
/*Call the actual event callback*/
e->user_data = NULL;
LV_PROFILER_EVENT_BEGIN_TAG(lv_event_code_get_name(e->code));
base->event_cb(base, e);
LV_PROFILER_EVENT_END_TAG(lv_event_code_get_name(e->code));
lv_result_t res = LV_RESULT_OK;
/*Stop if the object is deleted*/
if(e->deleted) res = LV_RESULT_INVALID;
return res;
}
lv_event_dsc_t * lv_obj_add_event_cb(lv_obj_t * obj, lv_event_cb_t event_cb, lv_event_code_t filter, void * user_data)
{
LV_ASSERT_OBJ(obj, MY_CLASS);
lv_obj_allocate_spec_attr(obj);
return lv_event_add(&obj->spec_attr->event_list, event_cb, filter, user_data);
}
uint32_t lv_obj_get_event_count(lv_obj_t * obj)
{
LV_ASSERT_NULL(obj);
if(obj->spec_attr == NULL) return 0;
return lv_event_get_count(&obj->spec_attr->event_list);
}
lv_event_dsc_t * lv_obj_get_event_dsc(lv_obj_t * obj, uint32_t index)
{
LV_ASSERT_NULL(obj);
if(obj->spec_attr == NULL) return NULL;
return lv_event_get_dsc(&obj->spec_attr->event_list, index);
}
bool lv_obj_remove_event(lv_obj_t * obj, uint32_t index)
{
LV_ASSERT_NULL(obj);
if(obj->spec_attr == NULL) return false;
return lv_event_remove(&obj->spec_attr->event_list, index);
}
bool lv_obj_remove_event_dsc(lv_obj_t * obj, lv_event_dsc_t * dsc)
{
LV_ASSERT_NULL(obj);
LV_ASSERT_NULL(dsc);
if(obj->spec_attr == NULL) return false;
return lv_event_remove_dsc(&obj->spec_attr->event_list, dsc);
}
uint32_t lv_obj_remove_event_cb(lv_obj_t * obj, lv_event_cb_t event_cb)
{
LV_ASSERT_NULL(obj);
uint32_t event_cnt = lv_obj_get_event_count(obj);
uint32_t removed_count = 0;
int32_t i;
if(event_cnt == 0) return 0;
for(i = event_cnt - 1; i >= 0; i--) {
lv_event_dsc_t * dsc = lv_obj_get_event_dsc(obj, i);
if(dsc && dsc->cb == event_cb) {
lv_obj_remove_event(obj, i);
removed_count++;
}
}
return removed_count;
}
uint32_t lv_obj_remove_event_cb_with_user_data(lv_obj_t * obj, lv_event_cb_t event_cb, void * user_data)
{
LV_ASSERT_NULL(obj);
uint32_t event_cnt = lv_obj_get_event_count(obj);
uint32_t removed_count = 0;
int32_t i;
if(event_cnt == 0) return 0;
for(i = event_cnt - 1; i >= 0; i--) {
lv_event_dsc_t * dsc = lv_obj_get_event_dsc(obj, i);
if(dsc && (event_cb == NULL || dsc->cb == event_cb) && dsc->user_data == user_data) {
lv_obj_remove_event(obj, i);
removed_count ++;
}
}
return removed_count;
}
lv_obj_t * lv_event_get_current_target_obj(lv_event_t * e)
{
return lv_event_get_current_target(e);
}
lv_obj_t * lv_event_get_target_obj(lv_event_t * e)
{
return lv_event_get_target(e);
}
lv_indev_t * lv_event_get_indev(lv_event_t * e)
{
if(e->code == LV_EVENT_PRESSED ||
e->code == LV_EVENT_PRESSING ||
e->code == LV_EVENT_PRESS_LOST ||
e->code == LV_EVENT_SHORT_CLICKED ||
e->code == LV_EVENT_LONG_PRESSED ||
e->code == LV_EVENT_LONG_PRESSED_REPEAT ||
e->code == LV_EVENT_CLICKED ||
e->code == LV_EVENT_RELEASED ||
e->code == LV_EVENT_SCROLL_BEGIN ||
e->code == LV_EVENT_SCROLL_END ||
e->code == LV_EVENT_SCROLL ||
e->code == LV_EVENT_GESTURE ||
e->code == LV_EVENT_KEY ||
e->code == LV_EVENT_FOCUSED ||
e->code == LV_EVENT_DEFOCUSED ||
e->code == LV_EVENT_LEAVE ||
e->code == LV_EVENT_HOVER_OVER ||
e->code == LV_EVENT_HOVER_LEAVE) {
return lv_event_get_param(e);
}
else {
LV_LOG_WARN("Not interpreted with this event code");
return NULL;
}
}
lv_layer_t * lv_event_get_layer(lv_event_t * e)
{
if(e->code == LV_EVENT_DRAW_MAIN ||
e->code == LV_EVENT_DRAW_MAIN_BEGIN ||
e->code == LV_EVENT_DRAW_MAIN_END ||
e->code == LV_EVENT_DRAW_POST ||
e->code == LV_EVENT_DRAW_POST_BEGIN ||
e->code == LV_EVENT_DRAW_POST_END) {
return lv_event_get_param(e);
}
else {
LV_LOG_WARN("Not interpreted with this event code");
return NULL;
}
}
const lv_area_t * lv_event_get_old_size(lv_event_t * e)
{
if(e->code == LV_EVENT_SIZE_CHANGED) {
return lv_event_get_param(e);
}
else {
LV_LOG_WARN("Not interpreted with this event code");
return NULL;
}
}
uint32_t lv_event_get_key(lv_event_t * e)
{
if(e->code == LV_EVENT_KEY) {
uint32_t * k = lv_event_get_param(e);
if(k) return *k;
else return 0;
}
else {
LV_LOG_WARN("Not interpreted with this event code");
return 0;
}
}
int32_t lv_event_get_rotary_diff(lv_event_t * e)
{
if(e->code == LV_EVENT_ROTARY) {
int32_t * r = lv_event_get_param(e);
if(r) return *r;
else return 0;
}
else {
LV_LOG_WARN("Not interpreted with this event code");
return 0;
}
}
lv_anim_t * lv_event_get_scroll_anim(lv_event_t * e)
{
if(e->code == LV_EVENT_SCROLL_BEGIN) {
return lv_event_get_param(e);
}
else {
LV_LOG_WARN("Not interpreted with this event code");
return NULL;
}
}
void lv_event_set_ext_draw_size(lv_event_t * e, int32_t size)
{
if(e->code == LV_EVENT_REFR_EXT_DRAW_SIZE) {
int32_t * cur_size = lv_event_get_param(e);
*cur_size = LV_MAX(*cur_size, size);
}
else {
LV_LOG_WARN("Not interpreted with this event code");
}
}
lv_point_t * lv_event_get_self_size_info(lv_event_t * e)
{
if(e->code == LV_EVENT_GET_SELF_SIZE) {
return lv_event_get_param(e);
}
else {
LV_LOG_WARN("Not interpreted with this event code");
return 0;
}
}
lv_hit_test_info_t * lv_event_get_hit_test_info(lv_event_t * e)
{
if(e->code == LV_EVENT_HIT_TEST) {
return lv_event_get_param(e);
}
else {
LV_LOG_WARN("Not interpreted with this event code");
return 0;
}
}
const lv_area_t * lv_event_get_cover_area(lv_event_t * e)
{
if(e->code == LV_EVENT_COVER_CHECK) {
lv_cover_check_info_t * p = lv_event_get_param(e);
return p->area;
}
else {
LV_LOG_WARN("Not interpreted with this event code");
return NULL;
}
}
void lv_event_set_cover_res(lv_event_t * e, lv_cover_res_t res)
{
if(e->code == LV_EVENT_COVER_CHECK) {
lv_cover_check_info_t * p = lv_event_get_param(e);
if(res > p->res) p->res = res; /*Save only "stronger" results*/
}
else {
LV_LOG_WARN("Not interpreted with this event code");
}
}
lv_draw_task_t * lv_event_get_draw_task(lv_event_t * e)
{
if(e->code == LV_EVENT_DRAW_TASK_ADDED) {
return lv_event_get_param(e);
}
else {
LV_LOG_WARN("Not interpreted with this event code");
return NULL;
}
}
lv_state_t lv_event_get_prev_state(lv_event_t * e)
{
if(e->code == LV_EVENT_STATE_CHANGED) {
lv_state_t * state = lv_event_get_param(e);
return state ? *state : 0;
}
else {
LV_LOG_WARN("Not interpreted with this event code");
return 0;
}
}
/**********************
* STATIC FUNCTIONS
**********************/
static lv_result_t event_send_core(lv_event_t * e)
{
LV_TRACE_EVENT("Sending event %d to %p with %p param", e->code, (void *)e->original_target, e->param);
lv_indev_t * indev_act = lv_indev_active();
if(indev_act) {
if(e->stop_processing) return LV_RESULT_OK;
if(e->deleted) return LV_RESULT_INVALID;
}
lv_obj_t * target = e->current_target;
lv_result_t res = LV_RESULT_OK;
lv_event_list_t * list = target->spec_attr ? &target->spec_attr->event_list : NULL;
res = lv_event_send(list, e, true);
if(res != LV_RESULT_OK || e->stop_processing) return res;
res = lv_obj_event_base(NULL, e);
if(res != LV_RESULT_OK || e->stop_processing) return res;
res = lv_event_send(list, e, false);
if(res != LV_RESULT_OK || e->stop_processing) return res;
lv_obj_t * parent = lv_obj_get_parent(e->current_target);
if(parent && event_is_bubbled(e)) {
e->current_target = parent;
res = event_send_core(e);
}
if(res != LV_RESULT_OK) return res;
/*Trickle down to children if enabled*/
if(event_is_trickled(e)) {
uint32_t child_count = lv_obj_get_child_count(target);
/* we don't want the event to bubble up again when trickling down */
e->stop_bubbling = 1;
for(uint32_t i = 0; i < child_count && res == LV_RESULT_OK && !e->stop_processing; i++) {
lv_obj_t * child = lv_obj_get_child(target, i);
if(child) {
e->current_target = child;
res = event_send_core(e);
if(res != LV_RESULT_OK) {
LV_LOG_WARN("Trickle down event %d to child %p failed", e->code, (void *)child);
break;
}
}
}
}
return res;
}
static bool event_is_bubbled(lv_event_t * e)
{
if(e->stop_bubbling) return false;
/*Event codes that always bubble*/
switch(e->code) {
case LV_EVENT_CHILD_CREATED:
case LV_EVENT_CHILD_DELETED:
return true;
default:
break;
}
/*Check other codes only if bubbling is enabled*/
if(lv_obj_has_flag(e->current_target, LV_OBJ_FLAG_EVENT_BUBBLE) == false) return false;
switch(e->code) {
case LV_EVENT_HIT_TEST:
case LV_EVENT_COVER_CHECK:
case LV_EVENT_REFR_EXT_DRAW_SIZE:
case LV_EVENT_DRAW_MAIN_BEGIN:
case LV_EVENT_DRAW_MAIN:
case LV_EVENT_DRAW_MAIN_END:
case LV_EVENT_DRAW_POST_BEGIN:
case LV_EVENT_DRAW_POST:
case LV_EVENT_DRAW_POST_END:
case LV_EVENT_DRAW_TASK_ADDED:
case LV_EVENT_REFRESH:
case LV_EVENT_DELETE:
case LV_EVENT_CHILD_CREATED:
case LV_EVENT_CHILD_DELETED:
case LV_EVENT_CHILD_CHANGED:
case LV_EVENT_SIZE_CHANGED:
case LV_EVENT_STYLE_CHANGED:
case LV_EVENT_GET_SELF_SIZE:
return false;
default:
return true;
}
}
static bool event_is_trickled(lv_event_t * e)
{
if(e->stop_trickling) return false;
/*Check other codes only if trickle is enabled*/
if(lv_obj_has_flag(e->current_target, LV_OBJ_FLAG_EVENT_TRICKLE) == false) return false;
switch(e->code) {
case LV_EVENT_HIT_TEST:
case LV_EVENT_COVER_CHECK:
case LV_EVENT_REFR_EXT_DRAW_SIZE:
case LV_EVENT_DRAW_MAIN_BEGIN:
case LV_EVENT_DRAW_MAIN:
case LV_EVENT_DRAW_MAIN_END:
case LV_EVENT_DRAW_POST_BEGIN:
case LV_EVENT_DRAW_POST:
case LV_EVENT_DRAW_POST_END:
case LV_EVENT_DRAW_TASK_ADDED:
case LV_EVENT_REFRESH:
case LV_EVENT_DELETE:
case LV_EVENT_CHILD_CREATED:
case LV_EVENT_CHILD_DELETED:
case LV_EVENT_CHILD_CHANGED:
case LV_EVENT_SIZE_CHANGED:
case LV_EVENT_STYLE_CHANGED:
case LV_EVENT_GET_SELF_SIZE:
return false;
default:
return true;
}
}

View File

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

View File

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

View File

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

File diff suppressed because it is too large Load Diff

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

File diff suppressed because it is too large Load Diff

View File

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

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

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

View File

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

View File

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

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,622 @@
/**
* @file lv_observer.h
*
*/
#ifndef LV_OBSERVER_H
#define LV_OBSERVER_H
#ifdef __cplusplus
extern "C" {
#endif
/*********************
* INCLUDES
*********************/
#include "../misc/lv_ext_data.h"
#include "lv_obj.h"
#if LV_USE_OBSERVER
/*********************
* DEFINES
*********************/
/**********************
* TYPEDEFS
**********************/
/**
* Values for lv_subject_t's `type` field
*/
typedef enum {
LV_SUBJECT_TYPE_INVALID = 0, /**< indicates Subject not initialized yet */
LV_SUBJECT_TYPE_NONE = 1, /**< a null value like None or NILt */
LV_SUBJECT_TYPE_INT = 2, /**< an int32_t */
LV_SUBJECT_TYPE_FLOAT = 3, /**< a float, requires `LV_USE_FLOAT 1` */
LV_SUBJECT_TYPE_POINTER = 4, /**< a void pointer */
LV_SUBJECT_TYPE_COLOR = 5, /**< an lv_color_t */
LV_SUBJECT_TYPE_GROUP = 6, /**< an array of Subjects */
LV_SUBJECT_TYPE_STRING = 7, /**< a char pointer */
} lv_subject_type_t;
/**
* A common type to handle all the various observable types in the same way
*/
typedef union {
int32_t num; /**< Integer number (opacity, enums, booleans or "normal" numbers) */
const void * pointer; /**< Constant pointer (string buffer, format string, font, cone text, etc.) */
lv_color_t color; /**< Color */
#if LV_USE_FLOAT
float float_v; /**< Floating point value*/
#endif
} lv_subject_value_t;
/**
* The Subject (an observable value)
*/
struct _lv_subject_t {
#if LV_USE_EXT_DATA
lv_ext_data_t ext_data;
#endif
lv_ll_t subs_ll; /**< Subscribers */
lv_subject_value_t value; /**< Current value */
lv_subject_value_t prev_value; /**< Previous value */
lv_subject_value_t min_value; /**< Minimum value for min. int or float*/
lv_subject_value_t max_value; /**< Maximum value for max. int or float*/
void * user_data; /**< Additional parameter, can be used freely by user */
uint32_t type : 4; /**< One of the LV_SUBJECT_TYPE_... values */
uint32_t size : 24; /**< String buffer size or group length */
uint32_t notify_restart_query : 1; /**< If an Observer was deleted during notification,
* start notifying from the beginning. */
};
/**
* Callback called to notify Observer that Subject's value has changed
* @param observer pointer to Observer
* @param subject pointer to Subject being observed
*/
typedef void (*lv_observer_cb_t)(lv_observer_t * observer, lv_subject_t * subject);
/**********************
* GLOBAL PROTOTYPES
**********************/
#if LV_USE_EXT_DATA
/**
* @brief Attaches external user data to an integer Subject with lifecycle management
*
* Associates arbitrary user-defined data with an LVGL observer and registers a destructor
* callback that will be automatically invoked when the observer is deleted. This enables:
* - Safe resource cleanup through the destructor mechanism
* - Contextual data storage for observer callbacks
* - Proper memory management for observer-related resources
*
* @param subject pointer to Subject
* @param data User-defined data pointer to associate
* @param free_cb Cleanup function called when:
* - Observer is explicitly deleted
* - Observed object is deleted
* - New data replaces current association
* NULL indicates no cleanup required
*/
void lv_subject_set_external_data(lv_subject_t * subject, void * data, void (* free_cb)(void * data));
#endif
/**
* Initialize an integer-type Subject.
* @param subject pointer to Subject
* @param value initial value
*/
void lv_subject_init_int(lv_subject_t * subject, int32_t value);
/**
* Set value of an integer Subject and notify Observers.
* @param subject pointer to Subject
* @param value new value
*/
void lv_subject_set_int(lv_subject_t * subject, int32_t value);
/**
* Get current value of an integer Subject.
* @param subject pointer to Subject
* @return current value
*/
int32_t lv_subject_get_int(lv_subject_t * subject);
/**
* Get previous value of an integer Subject.
* @param subject pointer to Subject
* @return current value
*/
int32_t lv_subject_get_previous_int(lv_subject_t * subject);
/**
* Set a minimum value for an integer subject
* @param subject pointer to Subject
* @param min_value the minimum value
*/
void lv_subject_set_min_value_int(lv_subject_t * subject, int32_t min_value);
/**
* Set a maximum value for an integer subject
* @param subject pointer to Subject
* @param max_value the maximum value
*/
void lv_subject_set_max_value_int(lv_subject_t * subject, int32_t max_value);
#if LV_USE_FLOAT
/**
* Initialize an float-type Subject.
* @param subject pointer to Subject
* @param value initial value
*/
void lv_subject_init_float(lv_subject_t * subject, float value);
/**
* Set value of an float Subject and notify Observers.
* @param subject pointer to Subject
* @param value new value
*/
void lv_subject_set_float(lv_subject_t * subject, float value);
/**
* Get current value of an float Subject.
* @param subject pointer to Subject
* @return current value
*/
float lv_subject_get_float(lv_subject_t * subject);
/**
* Get previous value of an float Subject.
* @param subject pointer to Subject
* @return current value
*/
float lv_subject_get_previous_float(lv_subject_t * subject);
/**
* Set a minimum value for a float subject
* @param subject pointer to Subject
* @param min_value the minimum value
*/
void lv_subject_set_min_value_float(lv_subject_t * subject, float min_value);
/**
* Set a maximum value for a float subject
* @param subject pointer to Subject
* @param max_value the maximum value
*/
void lv_subject_set_max_value_float(lv_subject_t * subject, float max_value);
#endif /*LV_USE_FLOAT*/
/**
* Initialize a string-type Subject.
* @param subject pointer to Subject
* @param buf pointer to buffer to store string
* @param prev_buf pointer to buffer to store previous string; can be NULL if not used
* @param size size of buffer(s)
* @param value initial value of string, e.g. "hello"
* @note A string Subject stores its own copy of the string, not just the pointer.
*/
void lv_subject_init_string(lv_subject_t * subject, char * buf, char * prev_buf, size_t size, const char * value);
/**
* Copy a string to a Subject and notify Observers if it changed.
* @param subject pointer to Subject
* @param buf new string
*/
void lv_subject_copy_string(lv_subject_t * subject, const char * buf);
/**
* Format a new string, updating Subject, and notify Observers if it changed.
* @param subject pointer to Subject
* @param format format string
*/
void lv_subject_snprintf(lv_subject_t * subject, const char * format, ...) LV_FORMAT_ATTRIBUTE(2, 3);
/**
* Get current value of a string Subject.
* @param subject pointer to Subject
* @return pointer to buffer containing current value
*/
const char * lv_subject_get_string(lv_subject_t * subject);
/**
* Get previous value of a string Subject.
* @param subject pointer to Subject
* @return pointer to buffer containing previous value
* @note NULL will be returned if NULL was passed in `lv_subject_init_string()`
* as `prev_buf`.
*/
const char * lv_subject_get_previous_string(lv_subject_t * subject);
/**
* Initialize a pointer-type Subject.
* @param subject pointer to Subject
* @param value initial value
*/
void lv_subject_init_pointer(lv_subject_t * subject, void * value);
/**
* Set value of a pointer Subject and notify Observers (regardless of whether it changed).
* @param subject pointer to Subject
* @param ptr new value
*/
void lv_subject_set_pointer(lv_subject_t * subject, void * ptr);
/**
* Get current value of a pointer Subject.
* @param subject pointer to Subject
* @return current value
*/
const void * lv_subject_get_pointer(lv_subject_t * subject);
/**
* Get previous value of a pointer Subject.
* @param subject pointer to Subject
* @return previous value
*/
const void * lv_subject_get_previous_pointer(lv_subject_t * subject);
/**
* Initialize a color-type Subject.
* @param subject pointer to Subject
* @param color initial value
*/
void lv_subject_init_color(lv_subject_t * subject, lv_color_t color);
/**
* Set value of a color Subject and notify Observers if it changed.
* @param subject pointer to Subject
* @param color new value
*/
void lv_subject_set_color(lv_subject_t * subject, lv_color_t color);
/**
* Get current value of a color Subject.
* @param subject pointer to Subject
* @return current value
*/
lv_color_t lv_subject_get_color(lv_subject_t * subject);
/**
* Get previous value of a color Subject.
* @param subject pointer to Subject
* @return previous value
*/
lv_color_t lv_subject_get_previous_color(lv_subject_t * subject);
/**
* Initialize a Group-type Subject.
* @param group_subject pointer to Group-type Subject
* @param list list of other Subject addresses; when any of these have values
updated, Observers of `group_subject` will be notified.
* @param list_len number of elements in `list[]`
*/
void lv_subject_init_group(lv_subject_t * group_subject, lv_subject_t * list[], uint32_t list_len);
/**
* Remove all Observers from a Subject and free allocated memory, and delete
* any associated Widget-Binding events. This leaves `subject` "disconnected" from
* all Observers and all associated Widget events established through Widget Binding.
* @param subject pointer to Subject
* @note This can safely be called regardless of whether any Observers
* added with `lv_subject_add_observer_obj()` or bound to a Widget Property
* with one of the `..._bind_...()` functions.
*/
void lv_subject_deinit(lv_subject_t * subject);
/**
* Get an element from Subject Group's list.
* @param subject pointer to Group-type Subject
* @param index index of element to get
* @return pointer to indexed Subject from list, or NULL if index is out of bounds
*/
lv_subject_t * lv_subject_get_group_element(lv_subject_t * subject, int32_t index);
/**
* Add Observer to Subject. When Subject's value changes `observer_cb` will be called.
* @param subject pointer to Subject
* @param observer_cb notification callback
* @param user_data optional user data
* @return pointer to newly-created Observer
*/
lv_observer_t * lv_subject_add_observer(lv_subject_t * subject, lv_observer_cb_t observer_cb, void * user_data);
/**
* Add Observer to Subject for a Widget.
* When the Widget is deleted, Observer will be unsubscribed from Subject automatically.
* @param subject pointer to Subject
* @param observer_cb notification callback
* @param obj pointer to Widget
* @param user_data optional user data
* @return pointer to newly-created Observer
* @note Do not call `lv_observer_remove()` on Observers created this way.
* Only clean up such Observers by either:
* - deleting the Widget, or
* - calling `lv_subject_deinit()` to gracefully de-couple and
* remove all Observers.
*/
lv_observer_t * lv_subject_add_observer_obj(lv_subject_t * subject, lv_observer_cb_t observer_cb, lv_obj_t * obj,
void * user_data);
/**
* Add an Observer to a Subject and also save a target pointer.
* @param subject pointer to Subject
* @param observer_cb notification callback
* @param target any pointer (NULL is okay)
* @param user_data optional user data
* @return pointer to newly-created Observer
*/
lv_observer_t * lv_subject_add_observer_with_target(lv_subject_t * subject, lv_observer_cb_t observer_cb,
void * target, void * user_data);
/**
* Remove Observer from its Subject.
* @param observer pointer to Observer
*/
void lv_observer_remove(lv_observer_t * observer);
/**
* Remove Observers associated with Widget `obj` from specified `subject` or all Subjects.
* @param obj pointer to Widget whose Observers should be removed
* @param subject Subject to remove Widget from, or NULL to remove from all Subjects
* @note This function can be used e.g. when a Widget's Subject(s) needs to
* be replaced by other Subject(s)
*/
void lv_obj_remove_from_subject(lv_obj_t * obj, lv_subject_t * subject);
/**
* Get target of an Observer.
* @param observer pointer to Observer
* @return pointer to saved target
*/
void * lv_observer_get_target(lv_observer_t * observer);
/**
* Get target Widget of Observer.
* This is the same as `lv_observer_get_target()`, except it returns `target`
* as an `lv_obj_t *`.
* @param observer pointer to Observer
* @return pointer to saved Widget target
*/
lv_obj_t * lv_observer_get_target_obj(lv_observer_t * observer);
/**
* Get Observer's user data.
* @param observer pointer to Observer
* @return void pointer to saved user data
*/
void * lv_observer_get_user_data(const lv_observer_t * observer);
/**
* Notify all Observers of Subject.
* @param subject pointer to Subject
*/
void lv_subject_notify(lv_subject_t * subject);
/**
* Add an event handler to increment (or decrement) the value of a subject on a trigger.
* @param obj pointer to a widget
* @param subject pointer to a subject to change
* @param trigger the trigger on which the subject should be changed
* @param step value to add on trigger
* if the minimum value is reached, the maximum value will be set on rollover.
*/
lv_subject_increment_dsc_t * lv_obj_add_subject_increment_event(lv_obj_t * obj, lv_subject_t * subject,
lv_event_code_t trigger, int32_t step);
/**
* Set the minimum subject value to set by the event
* @param obj pointer to the Widget to which the event is attached
* @param dsc pointer to the descriptor returned by `lv_obj_add_subject_increment_event()`
* @param min_value the minimum value to set
*/
void lv_obj_set_subject_increment_event_min_value(lv_obj_t * obj, lv_subject_increment_dsc_t * dsc, int32_t min_value);
/**
* Set the maximum subject value to set by the event
* @param obj pointer to the Widget to which the event is attached
* @param dsc pointer to the descriptor returned by `lv_obj_add_subject_increment_event()`
* @param max_value the maximum value to set
*/
void lv_obj_set_subject_increment_event_max_value(lv_obj_t * obj, lv_subject_increment_dsc_t * dsc, int32_t max_value);
/**
* Set what to do when the min/max value is crossed.
* @param obj pointer to the Widget to which the event is attached
* @param dsc pointer to the descriptor returned by `lv_obj_add_subject_increment_event()`
* @param rollover false: stop at the min/max value; true: jump to the other end
* @note the subject also can have min/max values and always the smaller range will be considered
*/
void lv_obj_set_subject_increment_event_rollover(lv_obj_t * obj, lv_subject_increment_dsc_t * dsc, bool rollover);
/**
* Toggle the value of an integer subject on an event. If it was != 0 it will be 0.
* If it was 0, it will be 1.
* @param obj pointer to a widget
* @param subject pointer to a subject to toggle
* @param trigger the trigger on which the subject should be changed
*/
void lv_obj_add_subject_toggle_event(lv_obj_t * obj, lv_subject_t * subject, lv_event_code_t trigger);
/**
* Set the value of an integer subject.
* @param obj pointer to a widget
* @param subject pointer to a subject to change
* @param trigger the trigger on which the subject should be changed
* @param value the value to set
*/
void lv_obj_add_subject_set_int_event(lv_obj_t * obj, lv_subject_t * subject, lv_event_code_t trigger, int32_t value);
#if LV_USE_FLOAT
/**
* Set the value of a float subject.
* @param obj pointer to a widget
* @param subject pointer to a subject to change
* @param trigger the trigger on which the subject should be changed
* @param value the value to set
*/
void lv_obj_add_subject_set_float_event(lv_obj_t * obj, lv_subject_t * subject, lv_event_code_t trigger, float value);
#endif
/**
* Set the value of a string subject.
* @param obj pointer to a widget
* @param subject pointer to a subject to change
* @param trigger the trigger on which the subject should be changed
* @param value the value to set
*/
void lv_obj_add_subject_set_string_event(lv_obj_t * obj, lv_subject_t * subject, lv_event_code_t trigger,
const char * value);
/**
* Set Widget's flag(s) if an integer Subject's value is equal to a reference value, clear flag otherwise.
* @param obj pointer to Widget
* @param subject pointer to Subject
* @param flag flag(s) (can be bit-wise OR-ed) to set or clear (e.g. `LV_OBJ_FLAG_HIDDEN`)
* @param ref_value reference value to compare Subject's value with
* @return pointer to newly-created Observer
*/
lv_observer_t * lv_obj_bind_flag_if_eq(lv_obj_t * obj, lv_subject_t * subject, lv_obj_flag_t flag, int32_t ref_value);
/**
* Set Widget's flag(s) if an integer Subject's value is not equal to a reference value, clear flag otherwise.
* @param obj pointer to Widget
* @param subject pointer to Subject
* @param flag flag(s) (can be bit-wise OR-ed) to set or clear (e.g. `LV_OBJ_FLAG_HIDDEN`)
* @param ref_value reference value to compare Subject's value with
* @return pointer to newly-created Observer
*/
lv_observer_t * lv_obj_bind_flag_if_not_eq(lv_obj_t * obj, lv_subject_t * subject, lv_obj_flag_t flag,
int32_t ref_value);
/**
* Set Widget's flag(s) if an integer Subject's value is greater than a reference value, clear flag otherwise.
* @param obj pointer to Widget
* @param subject pointer to Subject
* @param flag flag(s) (can be bit-wise OR-ed) to set or clear (e.g. `LV_OBJ_FLAG_HIDDEN`)
* @param ref_value reference value to compare Subject's value with
* @return pointer to newly-created Observer
*/
lv_observer_t * lv_obj_bind_flag_if_gt(lv_obj_t * obj, lv_subject_t * subject, lv_obj_flag_t flag, int32_t ref_value);
/**
* Set Widget's flag(s) if an integer Subject's value is greater than or equal to a reference value, clear flag otherwise.
* @param obj pointer to Widget
* @param subject pointer to Subject
* @param flag flag(s) (can be bit-wise OR-ed) to set or clear (e.g. `LV_OBJ_FLAG_HIDDEN`)
* @param ref_value reference value to compare Subject's value with
* @return pointer to newly-created Observer
*/
lv_observer_t * lv_obj_bind_flag_if_ge(lv_obj_t * obj, lv_subject_t * subject, lv_obj_flag_t flag, int32_t ref_value);
/**
* Set Widget's flag(s) if an integer Subject's value is less than a reference value, clear flag otherwise.
* @param obj pointer to Widget
* @param subject pointer to Subject
* @param flag flag(s) (can be bit-wise OR-ed) to set or clear (e.g. `LV_OBJ_FLAG_HIDDEN`)
* @param ref_value reference value to compare Subject's value with
* @return pointer to newly-created Observer
*/
lv_observer_t * lv_obj_bind_flag_if_lt(lv_obj_t * obj, lv_subject_t * subject, lv_obj_flag_t flag, int32_t ref_value);
/**
* Set Widget's flag(s) if an integer Subject's value is less than or equal to a reference value, clear flag otherwise.
* @param obj pointer to Widget
* @param subject pointer to Subject
* @param flag flag(s) (can be bit-wise OR-ed) to set or clear (e.g. `LV_OBJ_FLAG_HIDDEN`)
* @param ref_value reference value to compare Subject's value with
* @return pointer to newly-created Observer
*/
lv_observer_t * lv_obj_bind_flag_if_le(lv_obj_t * obj, lv_subject_t * subject, lv_obj_flag_t flag, int32_t ref_value);
/**
* Set Widget's state(s) if an integer Subject's value is equal to a reference value, clear flag otherwise.
* @param obj pointer to Widget
* @param subject pointer to Subject
* @param state state(s) (can be bit-wise OR-ed) to set or clear (e.g. `LV_STATE_CHECKED`)
* @param ref_value reference value to compare Subject's value with
* @return pointer to newly-created Observer
*/
lv_observer_t * lv_obj_bind_state_if_eq(lv_obj_t * obj, lv_subject_t * subject, lv_state_t state, int32_t ref_value);
/**
* Set a Widget's state(s) if an integer Subject's value is not equal to a reference value, clear flag otherwise
* @param obj pointer to Widget
* @param subject pointer to Subject
* @param state state(s) (can be bit-wise OR-ed) to set or clear (e.g. `LV_STATE_CHECKED`)
* @param ref_value reference value to compare Subject's value with
* @return pointer to newly-created Observer
*/
lv_observer_t * lv_obj_bind_state_if_not_eq(lv_obj_t * obj, lv_subject_t * subject, lv_state_t state,
int32_t ref_value);
/**
* Set Widget's state(s) if an integer Subject's value is greater than a reference value, clear flag otherwise.
* @param obj pointer to Widget
* @param subject pointer to Subject
* @param state state(s) (can be bit-wise OR-ed) to set or clear (e.g. `LV_STATE_CHECKED`)
* @param ref_value reference value to compare Subject's value with
* @return pointer to newly-created Observer
*/
lv_observer_t * lv_obj_bind_state_if_gt(lv_obj_t * obj, lv_subject_t * subject, lv_state_t state, int32_t ref_value);
/**
* Set Widget's state(s) if an integer Subject's value is greater than or equal to a reference value, clear flag otherwise.
* @param obj pointer to Widget
* @param subject pointer to Subject
* @param state state(s) (can be bit-wise OR-ed) to set or clear (e.g. `LV_STATE_CHECKED`)
* @param ref_value reference value to compare Subject's value with
* @return pointer to newly-created Observer
*/
lv_observer_t * lv_obj_bind_state_if_ge(lv_obj_t * obj, lv_subject_t * subject, lv_state_t state, int32_t ref_value);
/**
* Set Widget's state(s) if an integer Subject's value is less than a reference value, clear flag otherwise.
* @param obj pointer to Widget
* @param subject pointer to Subject
* @param state state(s) (can be bit-wise OR-ed) to set or clear (e.g. `LV_STATE_CHECKED`)
* @param ref_value reference value to compare Subject's value with
* @return pointer to newly-created Observer
*/
lv_observer_t * lv_obj_bind_state_if_lt(lv_obj_t * obj, lv_subject_t * subject, lv_state_t state, int32_t ref_value);
/**
* Set Widget's state(s) if an integer Subject's value is less than or equal to a reference value, clear flag otherwise.
* @param obj pointer to Widget
* @param subject pointer to Subject
* @param state state(s) (can be bit-wise OR-ed) to set or clear (e.g. `LV_STATE_CHECKED`)
* @param ref_value reference value to compare Subject's value with
* @return pointer to newly-created Observer
*/
lv_observer_t * lv_obj_bind_state_if_le(lv_obj_t * obj, lv_subject_t * subject, lv_state_t state, int32_t ref_value);
/**
* Set an integer Subject to 1 when a Widget is checked and set it 0 when unchecked, and
* clear Widget's checked state when Subject's value changes to 0 and set it when non-zero.
* @param obj pointer to Widget
* @param subject pointer to a Subject
* @return pointer to newly-created Observer
* @note Ensure Widget's `LV_OBJ_FLAG_CHECKABLE` flag is set.
*/
lv_observer_t * lv_obj_bind_checked(lv_obj_t * obj, lv_subject_t * subject);
/**********************
* MACROS
**********************/
#endif /*LV_USE_OBSERVER*/
#ifdef __cplusplus
} /*extern "C"*/
#endif
#endif /*LV_OBSERVER_H*/

View File

@@ -0,0 +1,67 @@
/**
* @file lv_observer_private.h
*
*/
#ifndef LV_OBSERVER_PRIVATE_H
#define LV_OBSERVER_PRIVATE_H
#ifdef __cplusplus
extern "C" {
#endif
/*********************
* INCLUDES
*********************/
#include "lv_observer.h"
#if LV_USE_OBSERVER
/*********************
* DEFINES
*********************/
/**********************
* TYPEDEFS
**********************/
/**
* The observer object: a descriptor returned when subscribing LVGL widgets to subjects
*/
struct _lv_observer_t {
lv_subject_t * subject; /**< Observed subject */
lv_observer_cb_t cb; /**< Callback that notifies when value changes */
void * target; /**< A target for the observer, e.g. a widget or any pointer */
void * user_data; /**< Additional parameter supplied when subscribing */
uint32_t auto_free_user_data : 1; /**< Automatically free user data when observer is removed */
uint32_t notified : 1; /**< Was observer already notified? */
uint32_t for_obj : 1; /**< Is `target` a pointer to a Widget (`lv_obj_t *`)? */
};
/**
* Descriptor created by `lv_obj_add_subject_increment_event()`
*/
struct _lv_subject_increment_dsc_t {
lv_subject_t * subject; /**< The subject to adjust*/
int32_t step; /**< The step add to the subject */
bool rollover; /**< Where to start over from the other end when one end is exceeded*/
int32_t min_value; /**< Don't set a value smaller than this */
int32_t max_value; /**< Don't set a value larger than this */
};
/**********************
* GLOBAL PROTOTYPES
**********************/
/**********************
* MACROS
**********************/
#endif /* LV_USE_OBSERVER */
#ifdef __cplusplus
} /*extern "C"*/
#endif
#endif /*LV_OBSERVER_PRIVATE_H*/

File diff suppressed because it is too large Load Diff

View File

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

View File

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

View File

@@ -0,0 +1,178 @@
/**
* @file lv_monkey.c
*
*/
/*********************
* INCLUDES
*********************/
#include "lv_monkey_private.h"
#if LV_USE_MONKEY != 0
#include "../../misc/lv_math.h"
#include "../../misc/lv_assert.h"
#include "../../stdlib/lv_mem.h"
#include "../../display/lv_display.h"
/*********************
* DEFINES
*********************/
#define MONKEY_PERIOD_RANGE_MIN_DEF 100
#define MONKEY_PERIOD_RANGE_MAX_DEF 1000
/**********************
* TYPEDEFS
**********************/
struct _lv_monkey_t {
lv_monkey_config_t config;
lv_indev_data_t indev_data;
lv_indev_t * indev;
lv_timer_t * timer;
void * user_data;
};
static const lv_key_t lv_key_map[] = {
LV_KEY_UP,
LV_KEY_DOWN,
LV_KEY_RIGHT,
LV_KEY_LEFT,
LV_KEY_ESC,
LV_KEY_DEL,
LV_KEY_BACKSPACE,
LV_KEY_ENTER,
LV_KEY_NEXT,
LV_KEY_PREV,
LV_KEY_HOME,
LV_KEY_END,
};
/**********************
* STATIC PROTOTYPES
**********************/
static void lv_monkey_read_cb(lv_indev_t * indev, lv_indev_data_t * data);
static int32_t lv_monkey_random(int32_t howsmall, int32_t howbig);
static void lv_monkey_timer_cb(lv_timer_t * timer);
/**********************
* GLOBAL FUNCTIONS
**********************/
void lv_monkey_config_init(lv_monkey_config_t * config)
{
lv_memzero(config, sizeof(lv_monkey_config_t));
config->type = LV_INDEV_TYPE_POINTER;
config->period_range.min = MONKEY_PERIOD_RANGE_MIN_DEF;
config->period_range.max = MONKEY_PERIOD_RANGE_MAX_DEF;
}
lv_monkey_t * lv_monkey_create(const lv_monkey_config_t * config)
{
lv_monkey_t * monkey = lv_malloc_zeroed(sizeof(lv_monkey_t));
LV_ASSERT_MALLOC(monkey);
monkey->config = *config;
monkey->timer = lv_timer_create(lv_monkey_timer_cb, monkey->config.period_range.min, monkey);
lv_timer_pause(monkey->timer);
monkey->indev = lv_indev_create();
lv_indev_set_type(monkey->indev, config->type);
lv_indev_set_read_cb(monkey->indev, lv_monkey_read_cb);
lv_indev_set_user_data(monkey->indev, monkey);
return monkey;
}
lv_indev_t * lv_monkey_get_indev(lv_monkey_t * monkey)
{
LV_ASSERT_NULL(monkey);
return monkey->indev;
}
void lv_monkey_set_enable(lv_monkey_t * monkey, bool en)
{
LV_ASSERT_NULL(monkey);
en ? lv_timer_resume(monkey->timer) : lv_timer_pause(monkey->timer);
}
bool lv_monkey_get_enable(lv_monkey_t * monkey)
{
LV_ASSERT_NULL(monkey);
return !lv_timer_get_paused(monkey->timer);
}
void lv_monkey_set_user_data(lv_monkey_t * monkey, void * user_data)
{
LV_ASSERT_NULL(monkey);
monkey->user_data = user_data;
}
void * lv_monkey_get_user_data(lv_monkey_t * monkey)
{
LV_ASSERT_NULL(monkey);
return monkey->user_data;
}
void lv_monkey_delete(lv_monkey_t * monkey)
{
LV_ASSERT_NULL(monkey);
lv_timer_delete(monkey->timer);
lv_indev_delete(monkey->indev);
lv_free(monkey);
}
/**********************
* STATIC FUNCTIONS
**********************/
static void lv_monkey_read_cb(lv_indev_t * indev, lv_indev_data_t * data)
{
lv_monkey_t * monkey = lv_indev_get_user_data(indev);
data->btn_id = monkey->indev_data.btn_id;
data->point = monkey->indev_data.point;
data->enc_diff = monkey->indev_data.enc_diff;
data->state = monkey->indev_data.state;
}
static int32_t lv_monkey_random(int32_t howsmall, int32_t howbig)
{
if(howsmall >= howbig) {
return howsmall;
}
int32_t diff = howbig - howsmall;
return (int32_t)lv_rand(0, diff) + howsmall;
}
static void lv_monkey_timer_cb(lv_timer_t * timer)
{
lv_monkey_t * monkey = lv_timer_get_user_data(timer);
lv_indev_data_t * data = &monkey->indev_data;
switch(lv_indev_get_type(monkey->indev)) {
case LV_INDEV_TYPE_POINTER:
data->point.x = (int32_t)lv_monkey_random(0, LV_HOR_RES - 1);
data->point.y = (int32_t)lv_monkey_random(0, LV_VER_RES - 1);
break;
case LV_INDEV_TYPE_ENCODER:
data->enc_diff = (int16_t)lv_monkey_random(monkey->config.input_range.min, monkey->config.input_range.max);
break;
case LV_INDEV_TYPE_BUTTON:
data->btn_id = (uint32_t)lv_monkey_random(monkey->config.input_range.min, monkey->config.input_range.max);
break;
case LV_INDEV_TYPE_KEYPAD: {
int32_t index = lv_monkey_random(0, sizeof(lv_key_map) / sizeof(lv_key_map[0]) - 1);
data->key = lv_key_map[index];
break;
}
default:
break;
}
data->state = lv_monkey_random(0, 100) < 50 ? LV_INDEV_STATE_RELEASED : LV_INDEV_STATE_PRESSED;
lv_timer_set_period(monkey->timer, lv_monkey_random(monkey->config.period_range.min, monkey->config.period_range.max));
}
#endif /*LV_USE_MONKEY*/

View File

@@ -0,0 +1,119 @@
/**
* @file lv_monkey.h
*
*/
#ifndef LV_MONKEY_H
#define LV_MONKEY_H
#ifdef __cplusplus
extern "C" {
#endif
/*********************
* INCLUDES
*********************/
#include "../../lv_conf_internal.h"
#include "../../indev/lv_indev.h"
#if LV_USE_MONKEY != 0
/*********************
* DEFINES
*********************/
/**********************
* TYPEDEFS
**********************/
typedef struct _lv_monkey_t lv_monkey_t;
typedef struct {
int32_t min;
int32_t max;
} lv_range_t;
typedef struct {
uint32_t min;
uint32_t max;
} lv_urange_t;
struct _lv_monkey_config_t {
/** Input device type */
lv_indev_type_t type;
/** Monkey execution period */
lv_urange_t period_range;
/** The range of input value */
lv_range_t input_range;
};
/**********************
* GLOBAL PROTOTYPES
**********************/
/**
* Initialize a monkey config with default values
* @param config pointer to 'lv_monkey_config_t' variable to initialize
*/
void lv_monkey_config_init(lv_monkey_config_t * config);
/**
* Create monkey for test
* @param config pointer to 'lv_monkey_config_t' variable
* @return pointer to the created monkey
*/
lv_monkey_t * lv_monkey_create(const lv_monkey_config_t * config);
/**
* Get monkey input device
* @param monkey pointer to a monkey
* @return pointer to the input device
*/
lv_indev_t * lv_monkey_get_indev(lv_monkey_t * monkey);
/**
* Enable monkey
* @param monkey pointer to a monkey
* @param en set to true to enable
*/
void lv_monkey_set_enable(lv_monkey_t * monkey, bool en);
/**
* Get whether monkey is enabled
* @param monkey pointer to a monkey
* @return return true if monkey enabled
*/
bool lv_monkey_get_enable(lv_monkey_t * monkey);
/**
* Set the user_data field of the monkey
* @param monkey pointer to a monkey
* @param user_data pointer to the new user_data.
*/
void lv_monkey_set_user_data(lv_monkey_t * monkey, void * user_data);
/**
* Get the user_data field of the monkey
* @param monkey pointer to a monkey
* @return the pointer to the user_data of the monkey
*/
void * lv_monkey_get_user_data(lv_monkey_t * monkey);
/**
* Delete monkey
* @param monkey pointer to monkey
*/
void lv_monkey_delete(lv_monkey_t * monkey);
/**********************
* MACROS
**********************/
#endif /*LV_USE_MONKEY*/
#ifdef __cplusplus
} /*extern "C"*/
#endif
#endif /*LV_MONKEY_H*/

View File

@@ -0,0 +1,43 @@
/**
* @file lv_monkey_private.h
*
*/
#ifndef LV_MONKEY_PRIVATE_H
#define LV_MONKEY_PRIVATE_H
#ifdef __cplusplus
extern "C" {
#endif
/*********************
* INCLUDES
*********************/
#include "lv_monkey.h"
#if LV_USE_MONKEY != 0
/*********************
* DEFINES
*********************/
/**********************
* TYPEDEFS
**********************/
/**********************
* GLOBAL PROTOTYPES
**********************/
/**********************
* MACROS
**********************/
#endif /* LV_USE_MONKEY != 0 */
#ifdef __cplusplus
} /*extern "C"*/
#endif
#endif /*LV_MONKEY_PRIVATE_H*/

View File

@@ -0,0 +1,412 @@
/**
* @file lv_sysmon.c
*
*/
/*********************
* INCLUDES
*********************/
#include "lv_sysmon_private.h"
#include "../../misc/lv_timer_private.h"
#if LV_USE_SYSMON
#include "../../core/lv_global.h"
#include "../../misc/lv_async.h"
#include "../../stdlib/lv_string.h"
#include "../../widgets/label/lv_label.h"
#include "../../display/lv_display_private.h"
/*********************
* DEFINES
*********************/
#ifndef LV_SYSMON_REFR_PERIOD_DEF
#define LV_SYSMON_REFR_PERIOD_DEF 300 /* ms */
#endif
#if LV_USE_MEM_MONITOR
#define sysmon_mem LV_GLOBAL_DEFAULT()->sysmon_mem
#endif
/**********************
* TYPEDEFS
**********************/
/**********************
* STATIC PROTOTYPES
**********************/
#if LV_USE_PERF_MONITOR
static void perf_update_timer_cb(lv_timer_t * t);
static void perf_observer_cb(lv_observer_t * observer, lv_subject_t * subject);
static void perf_monitor_disp_event_cb(lv_event_t * e);
static void perf_dump_info(lv_display_t * disp);
static void perf_control(lv_display_t * disp, bool start);
#endif
#if LV_USE_MEM_MONITOR
static void mem_update_timer_cb(lv_timer_t * t);
static void mem_observer_cb(lv_observer_t * observer, lv_subject_t * subject);
#endif
/**********************
* STATIC VARIABLES
**********************/
/**********************
* MACROS
**********************/
/**********************
* GLOBAL FUNCTIONS
**********************/
void lv_sysmon_builtin_init(void)
{
#if LV_USE_MEM_MONITOR
static lv_mem_monitor_t mem_info;
lv_subject_init_pointer(&sysmon_mem.subject, &mem_info);
sysmon_mem.timer = lv_timer_create(mem_update_timer_cb, LV_SYSMON_REFR_PERIOD_DEF, &mem_info);
#endif
}
void lv_sysmon_builtin_deinit(void)
{
#if LV_USE_MEM_MONITOR
lv_timer_delete(sysmon_mem.timer);
#endif
}
lv_obj_t * lv_sysmon_create(lv_display_t * disp)
{
LV_LOG_INFO("begin");
if(disp == NULL) disp = lv_display_get_default();
if(disp == NULL) {
LV_LOG_WARN("There is no default display");
return NULL;
}
lv_obj_t * label = lv_label_create(lv_display_get_layer_sys(disp));
lv_obj_set_style_bg_opa(label, LV_OPA_50, 0);
lv_obj_set_style_bg_color(label, lv_color_black(), 0);
lv_obj_set_style_text_color(label, lv_color_white(), 0);
lv_obj_set_style_pad_all(label, 3, 0);
lv_label_set_text(label, "?");
return label;
}
#if LV_USE_PERF_MONITOR
void lv_sysmon_show_performance(lv_display_t * disp)
{
if(disp == NULL) disp = lv_display_get_default();
if(disp == NULL) {
LV_LOG_WARN("There is no default display");
return;
}
if(disp->perf_label == NULL) {
disp->perf_label = lv_sysmon_create(disp);
if(disp->perf_label == NULL) {
LV_LOG_WARN("Couldn't create sysmon");
return;
}
lv_subject_init_pointer(&disp->perf_sysmon_backend.subject, &disp->perf_sysmon_info);
lv_obj_align(disp->perf_label, LV_USE_PERF_MONITOR_POS, 0, 0);
lv_subject_add_observer_obj(&disp->perf_sysmon_backend.subject, perf_observer_cb, disp->perf_label, NULL);
disp->perf_sysmon_backend.timer = lv_timer_create(perf_update_timer_cb, LV_SYSMON_REFR_PERIOD_DEF, disp);
lv_display_add_event_cb(disp, perf_monitor_disp_event_cb, LV_EVENT_ALL, NULL);
}
#if LV_USE_PERF_MONITOR_LOG_MODE
lv_obj_add_flag(disp->perf_label, LV_OBJ_FLAG_HIDDEN);
#else
lv_obj_remove_flag(disp->perf_label, LV_OBJ_FLAG_HIDDEN);
#endif
}
void lv_sysmon_hide_performance(lv_display_t * disp)
{
if(disp == NULL) disp = lv_display_get_default();
if(disp == NULL) {
LV_LOG_WARN("There is no default display");
return;
}
lv_obj_add_flag(disp->perf_label, LV_OBJ_FLAG_HIDDEN);
}
void lv_sysmon_performance_dump(lv_display_t * disp)
{
if(disp == NULL) disp = lv_display_get_default();
if(disp == NULL) {
LV_LOG_WARN("There is no default display");
return;
}
perf_dump_info(disp);
}
void lv_sysmon_performance_resume(lv_display_t * disp)
{
perf_control(disp, true);
}
void lv_sysmon_performance_pause(lv_display_t * disp)
{
perf_control(disp, false);
}
#endif
#if LV_USE_MEM_MONITOR
void lv_sysmon_show_memory(lv_display_t * disp)
{
if(disp == NULL) disp = lv_display_get_default();
if(disp == NULL) {
LV_LOG_WARN("There is no default display");
return;
}
if(disp->mem_label == NULL) {
disp->mem_label = lv_sysmon_create(disp);
if(disp->mem_label == NULL) {
LV_LOG_WARN("Couldn't create sysmon");
return;
}
lv_obj_align(disp->mem_label, LV_USE_MEM_MONITOR_POS, 0, 0);
lv_subject_add_observer_obj(&sysmon_mem.subject, mem_observer_cb, disp->mem_label, NULL);
}
lv_obj_remove_flag(disp->mem_label, LV_OBJ_FLAG_HIDDEN);
}
void lv_sysmon_hide_memory(lv_display_t * disp)
{
if(disp == NULL) disp = lv_display_get_default();
if(disp == NULL) {
LV_LOG_WARN("There is no default display");
return;
}
lv_obj_add_flag(disp->mem_label, LV_OBJ_FLAG_HIDDEN);
}
#endif
/**********************
* STATIC FUNCTIONS
**********************/
#if LV_USE_PERF_MONITOR
static void perf_monitor_disp_event_cb(lv_event_t * e)
{
lv_display_t * disp = lv_event_get_target(e);
lv_event_code_t code = lv_event_get_code(e);
lv_sysmon_perf_info_t * info = &disp->perf_sysmon_info;
switch(code) {
case LV_EVENT_REFR_START:
info->measured.refr_interval_sum += lv_tick_elaps(info->measured.refr_start);
info->measured.refr_start = lv_tick_get();
break;
case LV_EVENT_REFR_READY:
info->measured.refr_elaps_sum += lv_tick_elaps(info->measured.refr_start);
info->measured.refr_cnt++;
break;
case LV_EVENT_RENDER_START:
info->measured.render_in_progress = 1;
info->measured.render_start = lv_tick_get();
break;
case LV_EVENT_RENDER_READY:
info->measured.render_in_progress = 0;
info->measured.render_elaps_sum += lv_tick_elaps(info->measured.render_start);
info->measured.render_cnt++;
break;
case LV_EVENT_FLUSH_START:
case LV_EVENT_FLUSH_WAIT_START:
if(info->measured.render_in_progress) {
info->measured.flush_in_render_start = lv_tick_get();
}
else {
info->measured.flush_not_in_render_start = lv_tick_get();
}
break;
case LV_EVENT_FLUSH_FINISH:
case LV_EVENT_FLUSH_WAIT_FINISH:
if(info->measured.render_in_progress) {
info->measured.flush_in_render_elaps_sum += lv_tick_elaps(info->measured.flush_in_render_start);
}
else {
info->measured.flush_not_in_render_elaps_sum += lv_tick_elaps(info->measured.flush_not_in_render_start);
}
break;
case LV_EVENT_DELETE:
lv_timer_delete(disp->perf_sysmon_backend.timer);
lv_subject_deinit(&disp->perf_sysmon_backend.subject);
break;
default:
break;
}
}
static void perf_dump_info(lv_display_t * disp)
{
uint32_t LV_SYSMON_GET_IDLE(void);
lv_sysmon_perf_info_t * info = &disp->perf_sysmon_info;
info->calculated.run_cnt++;
uint32_t time_since_last_report = lv_tick_elaps(info->measured.last_report_timestamp);
lv_timer_t * disp_refr_timer = lv_display_get_refr_timer(NULL);
uint32_t disp_refr_period = disp_refr_timer ? disp_refr_timer->period : LV_DEF_REFR_PERIOD;
info->calculated.fps = time_since_last_report ? (1000 * info->measured.refr_cnt / time_since_last_report) : 0;
info->calculated.fps = LV_MIN(info->calculated.fps,
1000 / disp_refr_period); /*Limit due to possible off-by-one error*/
info->calculated.cpu = 100 - LV_SYSMON_GET_IDLE();
#if LV_SYSMON_PROC_IDLE_AVAILABLE
uint32_t LV_SYSMON_GET_PROC_IDLE(void);
info->calculated.cpu_proc = 100 - LV_SYSMON_GET_PROC_IDLE();
#endif /*LV_SYSMON_PROC_IDLE_AVAILABLE*/
info->calculated.refr_avg_time = info->measured.refr_cnt ? (info->measured.refr_elaps_sum / info->measured.refr_cnt) :
0;
info->calculated.flush_avg_time = info->measured.render_cnt ?
((info->measured.flush_in_render_elaps_sum + info->measured.flush_not_in_render_elaps_sum)
/ info->measured.render_cnt) : 0;
/*Flush time was measured in rendering time so subtract it*/
info->calculated.render_avg_time = info->measured.render_cnt ? ((info->measured.render_elaps_sum -
info->measured.flush_in_render_elaps_sum) /
info->measured.render_cnt) : 0;
info->calculated.cpu_avg_total = ((info->calculated.cpu_avg_total * (info->calculated.run_cnt - 1)) +
info->calculated.cpu) / info->calculated.run_cnt;
info->calculated.fps_avg_total = ((info->calculated.fps_avg_total * (info->calculated.run_cnt - 1)) +
info->calculated.fps) / info->calculated.run_cnt;
lv_subject_set_pointer(&disp->perf_sysmon_backend.subject, info);
lv_sysmon_perf_info_t prev_info = *info;
lv_memzero(info, sizeof(lv_sysmon_perf_info_t));
info->measured.refr_start = prev_info.measured.refr_start;
info->calculated.cpu_avg_total = prev_info.calculated.cpu_avg_total;
#if LV_SYSMON_PROC_IDLE_AVAILABLE
info->calculated.cpu_proc = prev_info.calculated.cpu_proc;
#endif /*LV_SYSMON_PROC_IDLE_AVAILABLE*/
info->calculated.fps_avg_total = prev_info.calculated.fps_avg_total;
info->calculated.run_cnt = prev_info.calculated.run_cnt;
info->measured.last_report_timestamp = lv_tick_get();
}
static void perf_update_timer_cb(lv_timer_t * t)
{
lv_display_t * disp = lv_timer_get_user_data(t);
perf_dump_info(disp);
}
static void perf_observer_cb(lv_observer_t * observer, lv_subject_t * subject)
{
const lv_sysmon_perf_info_t * perf = lv_subject_get_pointer(subject);
#if LV_USE_PERF_MONITOR_LOG_MODE
LV_UNUSED(observer);
#if LV_SYSMON_PROC_IDLE_AVAILABLE
LV_LOG("sysmon: "
"%" LV_PRIu32 " FPS (refr_cnt: %" LV_PRIu32 " | redraw_cnt: %" LV_PRIu32"), "
"refr %" LV_PRIu32 "ms (render %" LV_PRIu32 "ms | flush %" LV_PRIu32 "ms), "
"CPU (total %" LV_PRIu32 "%% proc %" LV_PRIu32 "%%)\n",
perf->calculated.fps, perf->measured.refr_cnt, perf->measured.render_cnt,
perf->calculated.refr_avg_time, perf->calculated.render_avg_time, perf->calculated.flush_avg_time,
perf->calculated.cpu, perf->calculated.cpu_proc);
#else
LV_LOG("sysmon: "
"%" LV_PRIu32 " FPS (refr_cnt: %" LV_PRIu32 " | redraw_cnt: %" LV_PRIu32"), "
"refr %" LV_PRIu32 "ms (render %" LV_PRIu32 "ms | flush %" LV_PRIu32 "ms), "
"CPU %" LV_PRIu32 "%%\n",
perf->calculated.fps, perf->measured.refr_cnt, perf->measured.render_cnt,
perf->calculated.refr_avg_time, perf->calculated.render_avg_time, perf->calculated.flush_avg_time,
perf->calculated.cpu);
#endif
#else
lv_obj_t * label = lv_observer_get_target(observer);
#if LV_SYSMON_PROC_IDLE_AVAILABLE
lv_label_set_text_fmt(
label,
"%" LV_PRIu32" FPS | CPU (%" LV_PRIu32 "%% | %" LV_PRIu32 "%%)\n"
"%" LV_PRIu32" ms (%" LV_PRIu32" | %" LV_PRIu32")",
perf->calculated.fps, perf->calculated.cpu, perf->calculated.cpu_proc,
perf->calculated.render_avg_time + perf->calculated.flush_avg_time,
perf->calculated.render_avg_time, perf->calculated.flush_avg_time
);
#else
lv_label_set_text_fmt(
label,
"%" LV_PRIu32" FPS, %" LV_PRIu32 "%% CPU\n"
"%" LV_PRIu32" ms (%" LV_PRIu32" | %" LV_PRIu32")",
perf->calculated.fps, perf->calculated.cpu,
perf->calculated.render_avg_time + perf->calculated.flush_avg_time,
perf->calculated.render_avg_time, perf->calculated.flush_avg_time
);
#endif /*LV_SYSMON_PROC_IDLE_AVAILABLE*/
#endif /*LV_USE_PERF_MONITOR_LOG_MODE*/
}
static void perf_control(lv_display_t * disp, bool start)
{
if(disp == NULL) disp = lv_display_get_default();
if(disp == NULL) {
LV_LOG_WARN("There is no default display");
return;
}
if(disp->perf_sysmon_backend.timer == NULL) return;
if(start) {
lv_timer_resume(disp->perf_sysmon_backend.timer);
}
else {
lv_timer_pause(disp->perf_sysmon_backend.timer);
}
}
#endif
#if LV_USE_MEM_MONITOR
static void mem_update_timer_cb(lv_timer_t * t)
{
lv_mem_monitor_t * mem_mon = lv_timer_get_user_data(t);
lv_mem_monitor(mem_mon);
lv_subject_set_pointer(&sysmon_mem.subject, mem_mon);
}
static void mem_observer_cb(lv_observer_t * observer, lv_subject_t * subject)
{
lv_obj_t * label = lv_observer_get_target(observer);
const lv_mem_monitor_t * mon = lv_subject_get_pointer(subject);
size_t used_size = mon->total_size - mon->free_size;
size_t used_kb = used_size / 1024;
size_t used_kb_tenth = (used_size - (used_kb * 1024)) / 102;
size_t max_used_kb = mon->max_used / 1024;
size_t max_used_kb_tenth = (mon->max_used - (max_used_kb * 1024)) / 102;
lv_label_set_text_fmt(label,
"%zu.%zu kB (%d%%)\n"
"%zu.%zu kB max, %d%% frag.",
used_kb, used_kb_tenth, mon->used_pct,
max_used_kb, max_used_kb_tenth,
mon->frag_pct);
}
#endif
#endif /*LV_USE_SYSMON*/

View File

@@ -0,0 +1,113 @@
/**
* @file lv_sysmon.h
*
*/
#ifndef LV_SYSMON_H
#define LV_SYSMON_H
#ifdef __cplusplus
extern "C" {
#endif
/*********************
* INCLUDES
*********************/
#include "../../misc/lv_timer.h"
#include "../../core/lv_observer.h"
#if LV_USE_SYSMON
#if LV_USE_LABEL == 0
#error "lv_sysmon: lv_label is required. Enable it in lv_conf.h (LV_USE_LABEL 1) "
#endif
#if LV_USE_OBSERVER == 0
#error "lv_observer: lv_observer is required. Enable it in lv_conf.h (LV_USE_OBSERVER 1) "
#endif
/*********************
* DEFINES
*********************/
/**********************
* TYPEDEFS
**********************/
/**********************
* GLOBAL PROTOTYPES
**********************/
/**
* Create a new system monitor label
* @param disp create the sys. mon. on this display's system layer
* @return the create label
*/
lv_obj_t * lv_sysmon_create(lv_display_t * disp);
#if LV_USE_PERF_MONITOR
/**
* Show system performance monitor: CPU usage and FPS count
* @param disp target display, NULL: use the default displays
*/
void lv_sysmon_show_performance(lv_display_t * disp);
/**
* Hide system performance monitor
* @param disp target display, NULL: use the default
*/
void lv_sysmon_hide_performance(lv_display_t * disp);
/**
* Dump the FPS data recorded between the last and current dump call.
* @param disp target display, NULL: use the default
*/
void lv_sysmon_performance_dump(lv_display_t * disp);
/**
* Resume the system performance monitor.
* @param disp target display, NULL: use the default
*/
void lv_sysmon_performance_resume(lv_display_t * disp);
/**
* Pause the system performance monitor.
*
* @param disp target display, NULL: use the default
* @note When the sysmon is stopped you can use `lv_sysmon_dump_performance` to
* get performance information. See `lv_sysmon_dump_performance` for more information.
*/
void lv_sysmon_performance_pause(lv_display_t * disp);
#endif /*LV_USE_PERF_MONITOR*/
#if LV_USE_MEM_MONITOR
/**
* Show system memory monitor: used memory and the memory fragmentation
* @param disp target display, NULL: use the default displays
*/
void lv_sysmon_show_memory(lv_display_t * disp);
/**
* Hide system memory monitor
* @param disp target display, NULL: use the default displays
*/
void lv_sysmon_hide_memory(lv_display_t * disp);
#endif /*LV_USE_MEM_MONITOR*/
/**********************
* MACROS
**********************/
#endif /*LV_USE_SYSMON*/
#ifdef __cplusplus
} /*extern "C"*/
#endif
#endif /*LV_SYSMON_H*/

View File

@@ -0,0 +1,94 @@
/**
* @file lv_sysmon_private.h
*
*/
#ifndef LV_SYSMON_PRIVATE_H
#define LV_SYSMON_PRIVATE_H
#ifdef __cplusplus
extern "C" {
#endif
/*********************
* INCLUDES
*********************/
#include "lv_sysmon.h"
#if LV_USE_SYSMON
/*********************
* DEFINES
*********************/
/**********************
* TYPEDEFS
**********************/
struct _lv_sysmon_backend_data_t {
lv_subject_t subject;
lv_timer_t * timer;
};
#if LV_USE_PERF_MONITOR
struct _lv_sysmon_perf_info_t {
struct {
bool inited;
uint32_t refr_start;
uint32_t refr_interval_sum;
uint32_t refr_elaps_sum;
uint32_t refr_cnt;
uint32_t render_start;
uint32_t render_elaps_sum; /*Contains the flush time too*/
uint32_t render_cnt;
uint32_t flush_in_render_start;
uint32_t flush_in_render_elaps_sum;
uint32_t flush_not_in_render_start;
uint32_t flush_not_in_render_elaps_sum;
uint32_t last_report_timestamp;
uint32_t render_in_progress : 1;
} measured;
struct {
uint32_t fps;
uint32_t cpu;
#if LV_SYSMON_PROC_IDLE_AVAILABLE
uint32_t cpu_proc; /** The applications idle time percentage */
#endif
uint32_t refr_avg_time;
uint32_t render_avg_time; /**< Pure rendering time without flush time*/
uint32_t flush_avg_time; /**< Pure flushing time without rendering time*/
uint32_t cpu_avg_total;
uint32_t fps_avg_total;
uint32_t run_cnt;
} calculated;
};
#endif
/**********************
* GLOBAL PROTOTYPES
**********************/
/**
* Initialize built-in system monitor, such as performance and memory monitor.
*/
void lv_sysmon_builtin_init(void);
/**
* DeInitialize built-in system monitor, such as performance and memory monitor.
*/
void lv_sysmon_builtin_deinit(void);
/**********************
* MACROS
**********************/
#endif /* LV_USE_SYSMON */
#ifdef __cplusplus
} /*extern "C"*/
#endif
#endif /*LV_SYSMON_PRIVATE_H*/

View File

@@ -0,0 +1,47 @@
/**
* @file lv_test.h
*
*/
#ifndef LV_TEST_H
#define LV_TEST_H
#ifdef __cplusplus
extern "C" {
#endif
/*********************
* INCLUDES
*********************/
#include "../../lv_conf_internal.h"
#if LV_USE_TEST
/*********************
* DEFINES
*********************/
#include "lv_test_indev.h"
#include "lv_test_display.h"
#include "lv_test_fs.h"
#include "lv_test_helpers.h"
#include "lv_test_screenshot_compare.h"
#include "lv_test_indev_gesture.h"
/**********************
* TYPEDEFS
**********************/
/**********************
* GLOBAL PROTOTYPES
**********************/
/**********************
* MACROS
**********************/
#endif /*LV_USE TEST*/
#ifdef __cplusplus
} /*extern "C"*/
#endif
#endif /*LV_TEST_H*/

View File

@@ -0,0 +1,108 @@
/**
* @file lv_test_display.c
*
*/
/*********************
* INCLUDES
*********************/
#include "lv_test_display.h"
#if LV_USE_TEST
#include "../../core/lv_global.h"
#include "../../lvgl_private.h"
#include <stdlib.h>
/*********************
* DEFINES
*********************/
/**********************
* TYPEDEFS
**********************/
/**********************
* STATIC PROTOTYPES
**********************/
static void dummy_flush_cb(lv_display_t * disp, const lv_area_t * area, uint8_t * color_p);
static void buf_changed_event_cb(lv_event_t * e);
static void delete_event_cb(lv_event_t * e);
/**********************
* STATIC VARIABLES
**********************/
/**********************
* MACROS
**********************/
#define _state LV_GLOBAL_DEFAULT()->test_state
/**********************
* GLOBAL FUNCTIONS
**********************/
lv_display_t * lv_test_display_create(int32_t hor_res, int32_t ver_res)
{
lv_display_t * disp = lv_display_create(hor_res, ver_res);
lv_display_set_color_format(disp, LV_COLOR_FORMAT_XRGB8888);
size_t buf_size = 4 * (hor_res + LV_DRAW_BUF_STRIDE_ALIGN - 1) * ver_res + LV_DRAW_BUF_ALIGN;
uint8_t * buf = malloc(buf_size);
LV_ASSERT_MALLOC(buf);
lv_draw_buf_init(&_state.draw_buf, hor_res, ver_res, LV_COLOR_FORMAT_XRGB8888, LV_STRIDE_AUTO, lv_draw_buf_align(buf,
LV_COLOR_FORMAT_XRGB8888), buf_size);
_state.draw_buf.unaligned_data = buf;
lv_display_set_draw_buffers(disp, &_state.draw_buf, NULL);
lv_display_set_render_mode(disp, LV_DISPLAY_RENDER_MODE_DIRECT);
lv_display_set_flush_cb(disp, dummy_flush_cb);
lv_display_add_event_cb(disp, buf_changed_event_cb, LV_EVENT_COLOR_FORMAT_CHANGED, NULL);
lv_display_add_event_cb(disp, buf_changed_event_cb, LV_EVENT_RESOLUTION_CHANGED, NULL);
lv_display_add_event_cb(disp, delete_event_cb, LV_EVENT_DELETE, NULL);
return disp;
}
/**********************
* STATIC FUNCTIONS
**********************/
static void buf_changed_event_cb(lv_event_t * e)
{
lv_display_t * disp = lv_event_get_target(e);
lv_color_format_t cf = lv_display_get_color_format(disp);
int32_t hor_res = lv_display_get_original_horizontal_resolution(disp);
int32_t ver_res = lv_display_get_original_vertical_resolution(disp);
free(_state.draw_buf.unaligned_data);
size_t buf_size = 4 * (hor_res + LV_DRAW_BUF_STRIDE_ALIGN - 1) * ver_res + LV_DRAW_BUF_ALIGN;
uint8_t * buf = malloc(buf_size);
LV_ASSERT_MALLOC(buf);
lv_draw_buf_init(&_state.draw_buf, hor_res, ver_res, cf, LV_STRIDE_AUTO, lv_draw_buf_align(buf, cf), buf_size);
_state.draw_buf.unaligned_data = buf;
lv_display_set_draw_buffers(disp, &_state.draw_buf, NULL);
}
static void delete_event_cb(lv_event_t * e)
{
LV_UNUSED(e);
lv_display_t * disp = lv_event_get_target(e);
lv_draw_buf_t * draw_buf = lv_display_get_buf_active(disp);
free(draw_buf->unaligned_data);
}
static void dummy_flush_cb(lv_display_t * disp, const lv_area_t * area, uint8_t * color_p)
{
LV_UNUSED(area);
LV_UNUSED(color_p);
lv_display_flush_ready(disp);
}
#endif /*LV_USE_TEST*/

View File

@@ -0,0 +1,55 @@
/**
* @file lv_test_display.h
*
*/
#ifndef LV_TEST_DISPLAY_H
#define LV_TEST_DISPLAY_H
#ifdef __cplusplus
extern "C" {
#endif
/*********************
* INCLUDES
*********************/
#include "../../lv_conf_internal.h"
#if LV_USE_TEST
#include "../../misc/lv_types.h"
/*********************
* DEFINES
*********************/
/**********************
* TYPEDEFS
**********************/
/**********************
* GLOBAL PROTOTYPES
**********************/
/***
* Create a dummy display for for the tests
* @param hor_res the maximal horizontal resolution
* @param ver_res the maximal vertical resolution
* @return the created display
*
* @note The resolution can be changed to any smaller values later
* using `lv_display_set_resolution`
* The color format can be freely changed later using `lv_display_set_color_format`
*/
lv_display_t * lv_test_display_create(int32_t hor_res, int32_t ver_res);
/**********************
* MACROS
**********************/
#endif /*LV_USE_TEST*/
#ifdef __cplusplus
} /*extern "C"*/
#endif
#endif /*LV_TEST_DISPLAY_H*/

View File

@@ -0,0 +1,193 @@
/**
* @file lv_test_fs.c
*
*/
/*********************
* INCLUDES
*********************/
#include "lv_test_fs.h"
#if LV_USE_TEST
#include "../../misc/lv_assert.h"
#include "../../misc/lv_fs.h"
#include "../../stdlib/lv_mem.h"
/*********************
* DEFINES
*********************/
#define LV_TEST_FS_LETTER 'T'
#define LV_TEST_FS_CACHE_SIZE (512)
/**********************
* TYPEDEFS
**********************/
/**********************
* STATIC PROTOTYPES
**********************/
static void * fs_open(lv_fs_drv_t * drv, const char * path, lv_fs_mode_t mode);
static lv_fs_res_t fs_close(lv_fs_drv_t * drv, void * file_p);
static lv_fs_res_t fs_read(lv_fs_drv_t * drv, void * file_p, void * buf, uint32_t btr, uint32_t * br);
static lv_fs_res_t fs_write(lv_fs_drv_t * drv, void * file_p, const void * buf, uint32_t btw, uint32_t * bw);
static lv_fs_res_t fs_seek(lv_fs_drv_t * drv, void * file_p, uint32_t pos, lv_fs_whence_t whence);
static lv_fs_res_t fs_tell(lv_fs_drv_t * drv, void * file_p, uint32_t * pos_p);
static void * fs_dir_open(lv_fs_drv_t * drv, const char * path);
static lv_fs_res_t fs_dir_read(lv_fs_drv_t * drv, void * rddir_p, char * fn, uint32_t fn_len);
static lv_fs_res_t fs_dir_close(lv_fs_drv_t * drv, void * rddir_p);
static bool fs_ready(lv_fs_drv_t * drv);
/**********************
* STATIC VARIABLES
**********************/
static bool is_ready = true;
static lv_fs_drv_t fs_drv;
/**********************
* MACROS
**********************/
/**********************
* GLOBAL FUNCTIONS
**********************/
/**********************
* STATIC FUNCTIONS
**********************/
void lv_test_fs_init(void)
{
lv_fs_drv_init(&fs_drv);
/*Set up fields...*/
fs_drv.letter = LV_TEST_FS_LETTER;
fs_drv.cache_size = LV_TEST_FS_CACHE_SIZE;
fs_drv.open_cb = fs_open;
fs_drv.close_cb = fs_close;
fs_drv.read_cb = fs_read;
fs_drv.write_cb = fs_write;
fs_drv.seek_cb = fs_seek;
fs_drv.tell_cb = fs_tell;
fs_drv.dir_close_cb = fs_dir_close;
fs_drv.dir_open_cb = fs_dir_open;
fs_drv.dir_read_cb = fs_dir_read;
fs_drv.ready_cb = fs_ready;
lv_fs_drv_register(&fs_drv);
}
void lv_test_fs_set_ready(bool ready)
{
is_ready = ready;
}
void lv_test_fs_clear_open_cb(bool is_clear)
{
fs_drv.open_cb = is_clear ? NULL : fs_open;
}
void lv_test_fs_clear_close_cb(bool is_clear)
{
fs_drv.close_cb = is_clear ? NULL : fs_close;
}
/**********************
* STATIC FUNCTIONS
**********************/
static void * fs_open(lv_fs_drv_t * drv, const char * path, lv_fs_mode_t mode)
{
LV_UNUSED(drv);
lv_fs_file_t * file_p = lv_malloc(sizeof(lv_fs_file_t));
LV_ASSERT_MALLOC(file_p);
if(file_p == NULL) {
return NULL;
}
lv_fs_res_t res = lv_fs_open(file_p, path, mode);
if(res != LV_FS_RES_OK) {
lv_free(file_p);
return NULL;
}
return file_p;
}
static lv_fs_res_t fs_close(lv_fs_drv_t * drv, void * file_p)
{
LV_UNUSED(drv);
lv_fs_res_t res = lv_fs_close(file_p);
lv_free(file_p);
return res;
}
static lv_fs_res_t fs_read(lv_fs_drv_t * drv, void * file_p, void * buf, uint32_t btr, uint32_t * br)
{
LV_UNUSED(drv);
return lv_fs_read(file_p, buf, btr, br);
}
static lv_fs_res_t fs_write(lv_fs_drv_t * drv, void * file_p, const void * buf, uint32_t btw, uint32_t * bw)
{
LV_UNUSED(drv);
return lv_fs_write(file_p, buf, btw, bw);
}
static lv_fs_res_t fs_seek(lv_fs_drv_t * drv, void * file_p, uint32_t pos, lv_fs_whence_t whence)
{
LV_UNUSED(drv);
return lv_fs_seek(file_p, pos, whence);
}
static lv_fs_res_t fs_tell(lv_fs_drv_t * drv, void * file_p, uint32_t * pos_p)
{
LV_UNUSED(drv);
return lv_fs_tell(file_p, pos_p);
}
static void * fs_dir_open(lv_fs_drv_t * drv, const char * path)
{
LV_UNUSED(drv);
lv_fs_dir_t * rddir_p = lv_malloc(sizeof(lv_fs_dir_t));
if(rddir_p == NULL) {
return NULL;
}
lv_fs_res_t res = lv_fs_dir_open(rddir_p, path);
if(res != LV_FS_RES_OK) {
lv_free(rddir_p);
return NULL;
}
return rddir_p;
}
static lv_fs_res_t fs_dir_read(lv_fs_drv_t * drv, void * rddir_p, char * fn, uint32_t fn_len)
{
LV_UNUSED(drv);
return lv_fs_dir_read(rddir_p, fn, fn_len);
}
static lv_fs_res_t fs_dir_close(lv_fs_drv_t * drv, void * rddir_p)
{
LV_UNUSED(drv);
lv_fs_res_t res = lv_fs_dir_close(rddir_p);
lv_free(rddir_p);
return res;
}
static bool fs_ready(lv_fs_drv_t * drv)
{
LV_UNUSED(drv);
return is_ready;
}
#endif/*LV_USE_TEST*/

View File

@@ -0,0 +1,69 @@
/**
* @file lv_test_fs.h
*
*/
#ifndef LV_TEST_FS_H
#define LV_TEST_FS_H
#ifdef __cplusplus
extern "C" {
#endif
/*********************
* INCLUDES
*********************/
#include "../../lv_conf_internal.h"
#if LV_USE_TEST
#include "../../misc/lv_types.h"
/*********************
* DEFINES
*********************/
/**********************
* TYPEDEFS
**********************/
/**********************
* GLOBAL PROTOTYPES
**********************/
/**
* Initialize the test file system driver
*/
void lv_test_fs_init(void);
/**
* Set whether the test file system is ready
* @param ready true: ready, false: not ready
*/
void lv_test_fs_set_ready(bool ready);
/**
* Set whether the open callback of the test file system is cleared
* @param is_clear true: clear, false: not clear
*/
void lv_test_fs_clear_open_cb(bool is_clear);
/**
* Set whether the close callback of the test file system is cleared
* @param is_clear true: clear, false: not clear
*/
void lv_test_fs_clear_close_cb(bool is_clear);
/**********************
* MACROS
**********************/
#endif /*LV_USE_TEST*/
#ifdef __cplusplus
} /*extern "C"*/
#endif
#endif /*LV_TEST_FS_H*/

View File

@@ -0,0 +1,60 @@
/**
* @file lv_test_helpers.c
*
*/
/*********************
* INCLUDES
*********************/
#include "lv_test_helpers.h"
#if LV_USE_TEST
#include "../../lvgl.h"
/*********************
* DEFINES
*********************/
/**********************
* TYPEDEFS
**********************/
/**********************
* STATIC PROTOTYPES
**********************/
/**********************
* STATIC VARIABLES
**********************/
/**********************
* MACROS
**********************/
/**********************
* GLOBAL FUNCTIONS
**********************/
void lv_test_wait(uint32_t ms)
{
while(ms) {
lv_tick_inc(1);
lv_timer_handler();
ms--;
}
lv_refr_now(NULL);
}
void lv_test_fast_forward(uint32_t ms)
{
lv_tick_inc(ms);
lv_timer_handler();
lv_refr_now(NULL);
}
/**********************
* STATIC FUNCTIONS
**********************/
#endif /*LV_USE_TEST*/

View File

@@ -0,0 +1,80 @@
/**
* @file lv_test_helpers.h
*
*/
#ifndef LV_TEST_HELPERS_H
#define LV_TEST_HELPERS_H
#ifdef __cplusplus
extern "C" {
#endif
/*********************
* INCLUDES
*********************/
#include "../../lv_conf_internal.h"
#if LV_USE_TEST
#include "../../misc/lv_types.h"
#include "../../stdlib/lv_mem.h"
/*********************
* DEFINES
*********************/
/**********************
* TYPEDEFS
**********************/
/**********************
* GLOBAL PROTOTYPES
**********************/
/**
* Emulate a delay. It's not real delay, but it tricks LVGL to think that the
* required time has been elapsed.
* `lv_timer_handler` is called after each millisecond, meaning all the events
* will be fired inside this function.
* At the end the animations and display will be also updated.
* @param ms the number of milliseconds to pass
*/
void lv_test_wait(uint32_t ms);
/**
* Emulates some time passing.
* Update the animations and the display only once at the end.
* @param ms the number of milliseconds to pass
*/
void lv_test_fast_forward(uint32_t ms);
#if LV_USE_STDLIB_MALLOC != LV_STDLIB_BUILTIN
/* Skip checking heap as we don't have the info available */
#define LV_HEAP_CHECK(x) do {} while(0)
/* Pick a non-zero value */
#define lv_test_get_free_mem() (65536)
#else
#define LV_HEAP_CHECK(x) x
static inline size_t lv_test_get_free_mem(void)
{
lv_mem_monitor_t m1;
lv_mem_monitor(&m1);
return m1.free_size;
}
#endif /* LV_USE_STDLIB_MALLOC == LV_STDLIB_BUILTIN */
/**********************
* MACROS
**********************/
#define LV_TEST_WIDTH_TO_STRIDE(w, px_size) ((((w) * (px_size) + (LV_DRAW_BUF_STRIDE_ALIGN - 1)) / LV_DRAW_BUF_STRIDE_ALIGN) * LV_DRAW_BUF_STRIDE_ALIGN)
#endif /*LV_USE_TEST*/
#ifdef __cplusplus
} /*extern "C"*/
#endif
#endif /*LV_TEST_HELPERS_H*/

View File

@@ -0,0 +1,212 @@
/**
* @file lv_test_indev.c
*
*/
/*********************
* INCLUDES
*********************/
#include "lv_test.h"
#if LV_USE_TEST
#include "../../core/lv_global.h"
#include "../../lvgl_private.h"
/*********************
* DEFINES
*********************/
/**********************
* TYPEDEFS
**********************/
/**********************
* STATIC PROTOTYPES
**********************/
static void lv_test_mouse_read_cb(lv_indev_t * indev, lv_indev_data_t * data);
static void lv_test_keypad_read_cb(lv_indev_t * indev, lv_indev_data_t * data);
static void lv_test_encoder_read_cb(lv_indev_t * indev, lv_indev_data_t * data);
/**********************
* STATIC VARIABLES
**********************/
/**********************
* MACROS
**********************/
#define _state LV_GLOBAL_DEFAULT()->test_state
/**********************
* GLOBAL FUNCTIONS
**********************/
void lv_test_indev_create_all(void)
{
_state.mouse_indev = lv_indev_create();
lv_indev_set_type(_state.mouse_indev, LV_INDEV_TYPE_POINTER);
lv_indev_set_read_cb(_state.mouse_indev, lv_test_mouse_read_cb);
_state.keypad_indev = lv_indev_create();
lv_indev_set_type(_state.keypad_indev, LV_INDEV_TYPE_KEYPAD);
lv_indev_set_read_cb(_state.keypad_indev, lv_test_keypad_read_cb);
_state.encoder_indev = lv_indev_create();
lv_indev_set_type(_state.encoder_indev, LV_INDEV_TYPE_ENCODER);
lv_indev_set_read_cb(_state.encoder_indev, lv_test_encoder_read_cb);
}
void lv_test_indev_delete_all(void)
{
if(_state.mouse_indev) {
lv_indev_delete(_state.mouse_indev);
_state.mouse_indev = NULL;
}
if(_state.keypad_indev) {
lv_indev_delete(_state.keypad_indev);
_state.keypad_indev = NULL;
}
if(_state.encoder_indev) {
lv_indev_delete(_state.encoder_indev);
_state.encoder_indev = NULL;
}
}
lv_indev_t * lv_test_indev_get_indev(lv_indev_type_t type)
{
switch(type) {
case LV_INDEV_TYPE_POINTER:
return _state.mouse_indev;
case LV_INDEV_TYPE_KEYPAD:
return _state.keypad_indev;
case LV_INDEV_TYPE_ENCODER:
return _state.encoder_indev;
default:
return NULL;
}
}
void lv_test_mouse_move_to(int32_t x, int32_t y)
{
_state.x_act = x;
_state.y_act = y;
}
void lv_test_mouse_move_to_obj(lv_obj_t * obj)
{
int32_t x = obj->coords.x1 + lv_obj_get_width(obj) / 2;
int32_t y = obj->coords.y1 + lv_obj_get_height(obj) / 2;
lv_test_mouse_move_to(x, y);
}
void lv_test_mouse_move_by(int32_t x, int32_t y)
{
_state.x_act += x;
_state.y_act += y;
}
void lv_test_mouse_press(void)
{
_state.mouse_pressed = true;
}
void lv_test_mouse_release(void)
{
_state.mouse_pressed = false;
}
void lv_test_mouse_click_at(int32_t x, int32_t y)
{
lv_test_mouse_release();
lv_test_wait(50);
lv_test_mouse_move_to(x, y);
lv_test_mouse_press();
lv_test_wait(50);
lv_test_mouse_release();
lv_test_wait(50);
}
void lv_test_key_press(uint32_t k)
{
_state.key_act = k;
_state.key_pressed = true;
}
void lv_test_key_release(void)
{
_state.key_pressed = false;
}
void lv_test_key_hit(uint32_t k)
{
lv_test_key_release();
lv_test_wait(50);
lv_test_key_press(k);
lv_test_wait(50);
lv_test_key_release();
lv_test_wait(50);
}
void lv_test_encoder_add_diff(int32_t d)
{
_state.diff_act += d;
}
void lv_test_encoder_turn(int32_t d)
{
_state.diff_act += d;
lv_test_wait(50);
}
void lv_test_encoder_press(void)
{
_state.enc_pressed = true;
}
void lv_test_encoder_release(void)
{
_state.enc_pressed = false;
}
void lv_test_encoder_click(void)
{
lv_test_encoder_release();
lv_test_wait(50);
lv_test_encoder_press();
lv_test_wait(50);
lv_test_encoder_release();
lv_test_wait(50);
}
/**********************
* STATIC FUNCTIONS
**********************/
static void lv_test_mouse_read_cb(lv_indev_t * indev, lv_indev_data_t * data)
{
LV_UNUSED(indev);
lv_point_set(&data->point, _state.x_act, _state.y_act);
data->state = _state.mouse_pressed ? LV_INDEV_STATE_PRESSED : LV_INDEV_STATE_RELEASED;
}
static void lv_test_keypad_read_cb(lv_indev_t * indev, lv_indev_data_t * data)
{
LV_UNUSED(indev);
data->key = _state.key_act;
data->state = _state.key_pressed ? LV_INDEV_STATE_PRESSED : LV_INDEV_STATE_RELEASED;
}
static void lv_test_encoder_read_cb(lv_indev_t * indev, lv_indev_data_t * data)
{
LV_UNUSED(indev);
data->enc_diff = _state.diff_act;
data->state = _state.enc_pressed ? LV_INDEV_STATE_PRESSED : LV_INDEV_STATE_RELEASED;
_state.diff_act = 0;
}
#endif /*LV_USE_TEST*/

View File

@@ -0,0 +1,171 @@
/**
* @file lv_test_indev.h
*
*/
#ifndef LV_TEST_INDEV_H
#define LV_TEST_INDEV_H
#ifdef __cplusplus
extern "C" {
#endif
/*********************
* INCLUDES
*********************/
#include "../../lv_conf_internal.h"
#if LV_USE_TEST
#include "../../misc/lv_types.h"
#include "../../indev/lv_indev.h"
/*********************
* DEFINES
*********************/
/**********************
* TYPEDEFS
**********************/
/**********************
* GLOBAL PROTOTYPES
**********************/
/**
* Create a mouse (pointer), keypad, and encoder indevs.
* They can be controlled via function calls during the test
*/
void lv_test_indev_create_all(void);
/**
* Delete all test input devices
*/
void lv_test_indev_delete_all(void);
/**
* Get one of the indev created in `lv_test_indev_create_all`
* @param type type of the indev to get
* @return the indev
*/
lv_indev_t * lv_test_indev_get_indev(lv_indev_type_t type);
/**
* Move the mouse to the given coordinates.
* This function doesn't wait, but just changes the state and returns immediately.
* @param x the target absolute X coordinate
* @param y the target absolute Y coordinate
*/
void lv_test_mouse_move_to(int32_t x, int32_t y);
/**
* Move the mouse to the center of a widget
* This function doesn't wait, but just changes the state and returns immediately.
* @param obj pointer to an widget
*/
void lv_test_mouse_move_to_obj(lv_obj_t * obj);
/**
* Move the mouse cursor. Keep the pressed or released state
* This function doesn't wait, but just changes the state and returns immediately.
* @param x the difference in X to move
* @param y the difference in Y to move
*/
void lv_test_mouse_move_by(int32_t x, int32_t y);
/**
* Make the mouse button pressed.
* This function doesn't wait, but just changes the state and returns immediately.
*/
void lv_test_mouse_press(void);
/**
* Make the mouse button released.
* This function doesn't wait, but just changes the state and returns immediately.
*/
void lv_test_mouse_release(void);
/**
* Emulate a click on a given point.
* First set the released state, wait a little, press, wait, and release again.
* The wait time is 50ms.
* Internally `lv_timer_handler` is called, meaning all the events will be fired inside this function.
* @param x the target absolute X coordinate
* @param y the target absolute Y coordinate
*/
void lv_test_mouse_click_at(int32_t x, int32_t y);
/**
* Emulate a key press.
* This function doesn't wait, but just changes the state and returns immediately.
* @param k the key to press
*/
void lv_test_key_press(uint32_t k);
/**
* Release the previously press key.
* This function doesn't wait, but just changes the state and returns immediately.
* @param k the key to press
*/
void lv_test_key_release(void);
/**
* Emulate a key hit.
* First set the released state, wait a little, press, wait, and release again.
* The wait time is 50ms.
* Internally `lv_timer_handler` is called, meaning all the events will be fired inside this function.
* @param k the key to hit
*/
void lv_test_key_hit(uint32_t k);
/**
* Emulate encoder rotation, use positive parameter to rotate to the right
* and negative to rotate to the left.
* This function doesn't wait, but just changes the state and returns immediately.
* @param d number of encoder ticks to emulate
*/
void lv_test_encoder_add_diff(int32_t d);
/**
* Emulate an encoder turn a wait 50ms. Use positive parameter to rotate to the right
* and negative to rotate to the left.
* Internally `lv_timer_handler` is called, meaning all the events will be fired inside this function.
* @param d number of encoder ticks to emulate
*/
void lv_test_encoder_turn(int32_t d);
/**
* Emulate an encoder press.
* This function doesn't wait, but just changes the state and returns immediately.
*/
void lv_test_encoder_press(void);
/**
* Emulate an encoder release.
* This function doesn't wait, but just changes the state and returns immediately.
*/
void lv_test_encoder_release(void);
/**
* Emulate am encoder click.
* First set the released state, wait a little, press, wait, and release again.
* The wait time is 50ms.
* Internally `lv_timer_handler` is called, meaning all the events will be fired inside this function.
*/
void lv_test_encoder_click(void);
/**********************
* MACROS
**********************/
#endif /*LV_USE_TEST*/
#ifdef __cplusplus
} /*extern "C"*/
#endif
#endif /*LV_TEST_INDEV_H*/

View File

@@ -0,0 +1,141 @@
/**
* @file lv_test_indev_gesture.c
*
*/
/*********************
* INCLUDES
*********************/
#include "lv_test.h"
#if LV_USE_TEST && LV_USE_GESTURE_RECOGNITION
#include "../../core/lv_global.h"
#include "../../lvgl_private.h"
/*********************
* DEFINES
*********************/
#define MAX_TOUCH_CNT 2
/**********************
* TYPEDEFS
**********************/
/**********************
* STATIC PROTOTYPES
**********************/
static void lv_test_gesture_read_cb(lv_indev_t * indev, lv_indev_data_t * data);
/**********************
* STATIC VARIABLES
**********************/
/**********************
* MACROS
**********************/
#define _state LV_GLOBAL_DEFAULT()->test_state
/**********************
* GLOBAL FUNCTIONS
**********************/
void lv_test_indev_gesture_create(void)
{
_state.max_touch_cnt = MAX_TOUCH_CNT;
_state.touch_data =
lv_malloc_zeroed(sizeof(lv_indev_touch_data_t) * _state.max_touch_cnt);
if(_state.touch_data == NULL) {
LV_LOG_ERROR("lv_indev_touch_data_t malloc failed");
}
_state.gesture_indev = lv_indev_create();
lv_indev_set_type(_state.gesture_indev, LV_INDEV_TYPE_POINTER);
lv_indev_set_read_cb(_state.gesture_indev, lv_test_gesture_read_cb);
}
void lv_test_indev_gesture_delete(void)
{
if(_state.gesture_indev) {
lv_indev_delete(_state.gesture_indev);
_state.gesture_indev = NULL;
}
if(_state.touch_data) {
lv_free(_state.touch_data);
_state.touch_data = NULL;
}
_state.max_touch_cnt = 0;
}
lv_indev_t * lv_test_indev_get_gesture_indev(lv_indev_type_t type)
{
switch(type) {
case LV_INDEV_TYPE_POINTER:
return _state.gesture_indev;
default:
return NULL;
}
}
void lv_test_gesture_set_pinch_data(lv_point_t point_0, lv_point_t point_1)
{
_state.touch_data[0].id = 0;
_state.touch_data[0].point = point_0;
_state.touch_data[1].id = 1;
_state.touch_data[1].point = point_1;
}
void lv_test_gesture_pinch_press(void)
{
_state.touch_data[0].state = LV_INDEV_STATE_PRESSED;
_state.touch_data[1].state = LV_INDEV_STATE_PRESSED;
}
void lv_test_gesture_pinch_release(void)
{
_state.touch_data[0].state = LV_INDEV_STATE_RELEASED;
_state.touch_data[1].state = LV_INDEV_STATE_RELEASED;
}
void lv_test_gesture_pinch(lv_point_t point_begin_0, lv_point_t point_begin_1,
lv_point_t point_end_0, lv_point_t point_end_1)
{
lv_test_gesture_pinch_release();
lv_test_wait(50);
lv_test_gesture_set_pinch_data(point_begin_0, point_begin_1);
lv_test_gesture_pinch_press();
lv_test_wait(50);
lv_test_gesture_set_pinch_data(point_end_0, point_end_1);
lv_test_wait(80);
lv_test_gesture_set_pinch_data(point_end_0, point_end_1);
lv_test_wait(80);
lv_test_gesture_pinch_release();
lv_test_wait(50);
}
/**********************
* STATIC FUNCTIONS
**********************/
static void lv_test_gesture_read_cb(lv_indev_t * indev, lv_indev_data_t * data)
{
LV_UNUSED(indev);
lv_indev_gesture_recognizers_update(indev,
_state.touch_data,
_state.max_touch_cnt);
lv_indev_gesture_recognizers_set_data(indev, data);
if(_state.touch_data != NULL && _state.max_touch_cnt > 0) {
data->point.x = _state.touch_data[0].point.x;
data->point.y = _state.touch_data[0].point.y;
}
else {
LV_LOG_ERROR("Invalid touch data or max touch count");
data->point.x = 0;
data->point.y = 0;
}
}
#endif /*LV_USE_TEST*/

View File

@@ -0,0 +1,93 @@
/**
* @file lv_test_indev_gesture.h
*
*/
#ifndef LV_TEST_INDEV_GESTURE_H
#define LV_TEST_INDEV_GESTURE_H
#ifdef __cplusplus
extern "C" {
#endif
/*********************
* INCLUDES
*********************/
#include "../../lv_conf_internal.h"
#if LV_USE_TEST && LV_USE_GESTURE_RECOGNITION
#include "../../misc/lv_types.h"
#include "../../indev/lv_indev.h"
/*********************
* DEFINES
*********************/
/**********************
* TYPEDEFS
**********************/
/**********************
* GLOBAL PROTOTYPES
**********************/
/**
* Create a touch (pointer) indevs.
* They can be controlled via function calls during the test
*/
void lv_test_indev_gesture_create(void);
/**
* Delete the touch (pointer) indevs.
*/
void lv_test_indev_gesture_delete(void);
/**
* Get one of the indev created in `lv_test_indev_gesture_create`
* @param type type of the indev to get
* @return the indev
*/
lv_indev_t * lv_test_indev_get_gesture_indev(lv_indev_type_t type);
/**
* Set two touch points data for pinch gesture
* @param point_0 First touch point coordinates
* @param point_1 Second touch point coordinates
*/
void lv_test_gesture_set_pinch_data(lv_point_t point_0, lv_point_t point_1);
/**
* Trigger press state of pinch gesture (both touch points pressed)
*/
void lv_test_gesture_pinch_press(void);
/**
* Trigger release state of pinch gesture (both touch points released)
*/
void lv_test_gesture_pinch_release(void);
/**
* Simulate a complete pinch gesture operation
* @param point_begin_0 Starting coordinates of first touch point
* @param point_begin_1 Starting coordinates of second touch point
* @param point_end_0 Ending coordinates of first touch point
* @param point_end_1 Ending coordinates of second touch point
*/
void lv_test_gesture_pinch(lv_point_t point_begin_0, lv_point_t point_begin_1,
lv_point_t point_end_0, lv_point_t point_end_1);
/**********************
* MACROS
**********************/
#endif /*LV_USE_TEST && LV_USE_GESTURE_RECOGNITION*/
#ifdef __cplusplus
} /*extern "C"*/
#endif
#endif /*LV_TEST_INDEV_GESTURE_H*/

View File

@@ -0,0 +1,67 @@
/**
* @file lv_test_private.h
*
*/
#ifndef LV_TEST_PRIVATE_H
#define LV_TEST_PRIVATE_H
#ifdef __cplusplus
extern "C" {
#endif
/*********************
* INCLUDES
*********************/
#include "../../lv_conf_internal.h"
#if LV_USE_TEST
#include "../../misc/lv_types.h"
#include "../../indev/lv_indev_gesture.h"
/*********************
* DEFINES
*********************/
/**********************
* TYPEDEFS
**********************/
typedef struct {
lv_indev_t * mouse_indev;
lv_indev_t * keypad_indev;
lv_indev_t * encoder_indev;
lv_draw_buf_t draw_buf;
int32_t x_act;
int32_t y_act;
uint32_t key_act;
int32_t diff_act;
bool mouse_pressed;
bool key_pressed;
bool enc_pressed;
#if LV_USE_GESTURE_RECOGNITION
lv_indev_t * gesture_indev;
lv_indev_touch_data_t * touch_data;
uint8_t max_touch_cnt;
#endif
} lv_test_state_t;
/**********************
* GLOBAL PROTOTYPES
**********************/
/**********************
* MACROS
**********************/
#endif /*LV_USE_TEST*/
#ifdef __cplusplus
} /*extern "C"*/
#endif
#endif /*LV_TEST_PRIVATE_H*/

View File

@@ -0,0 +1,348 @@
/**
* @file lv_test_screenshot_compare.c
*
* Copyright 2002-2010 Guillaume Cottenceau.
*
* This software may be freely redistributed under the terms
* of the X11 license.
*
*/
/*********************
* INCLUDES
*********************/
#include "../../lv_conf_internal.h"
#if LV_USE_TEST && defined(LV_USE_TEST_SCREENSHOT_COMPARE) && LV_USE_TEST_SCREENSHOT_COMPARE
#if LV_USE_LODEPNG == 0
#error "lodepng is required for screenshot compare. Enable it in lv_conf.h (LV_USE_LODEPNG 1)"
#endif
#include "../../lvgl.h"
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <stdarg.h>
#include <errno.h>
#include "../../libs/lodepng/lodepng.h"
#ifdef _WIN32
#include <direct.h>
#define mkdir(pathname, mode) _mkdir(pathname)
#define strtok_r strtok_s
#else
#include <sys/stat.h>
#endif
/*********************
* DEFINES
*********************/
#ifndef REF_IMGS_PATH
#define REF_IMGS_PATH ""
#endif
#ifndef REF_IMG_TOLERANCE
#define REF_IMG_TOLERANCE 0
#endif
#define ERR_FILE_NOT_FOUND -1
#define ERR_PNG -2
/**********************
* TYPEDEFS
**********************/
/**********************
* STATIC PROTOTYPES
**********************/
static lv_test_screenshot_result_t screenshot_compare(const char * fn_ref, uint8_t tolerance);
static unsigned read_png_file(lv_draw_buf_t ** refr_draw_buf, unsigned * width, unsigned * height,
const char * file_name);
static unsigned write_png_file(void * raw_img, uint32_t width, uint32_t height, char * file_name);
static void buf_to_xrgb8888(const lv_draw_buf_t * draw_buf, uint8_t * buf_out);
static void create_folders_if_needed(const char * path) ;
/**********************
* STATIC VARIABLES
**********************/
/**********************
* MACROS
**********************/
/**********************
* GLOBAL FUNCTIONS
**********************/
lv_test_screenshot_result_t lv_test_screenshot_compare(const char * fn_ref)
{
lv_obj_t * scr = lv_screen_active();
lv_obj_invalidate(scr);
lv_refr_now(NULL);
lv_test_screenshot_result_t res;
res = screenshot_compare(fn_ref, REF_IMG_TOLERANCE);
return res;
}
/**********************
* STATIC FUNCTIONS
**********************/
/**
* Compare the content of the frame buffer with a reference image
* @param fn_ref reference image path
* @return An element of lv_test_screenshot_result_t
*/
static lv_test_screenshot_result_t screenshot_compare(const char * fn_ref, uint8_t tolerance)
{
char fn_ref_full[256];
lv_snprintf(fn_ref_full, sizeof(fn_ref_full), "%s%s", REF_IMGS_PATH, fn_ref);
#if LV_TEST_SCREENSHOT_CREATE_REFERENCE_IMAGE
create_folders_if_needed(fn_ref_full);
#endif
lv_draw_buf_t * draw_buf = lv_display_get_buf_active(NULL);
uint8_t * screen_buf_xrgb8888 = lv_malloc(draw_buf->header.w * draw_buf->header.h * 4);
if(!screen_buf_xrgb8888) {
LV_LOG_ERROR("Not enough memory to compare display with screenshot");
return LV_TEST_SCREENSHOT_RESULT_FAILED;
}
buf_to_xrgb8888(draw_buf, screen_buf_xrgb8888);
lv_draw_buf_t * ref_draw_buf = NULL;
unsigned ref_img_width = 0;
unsigned ref_img_height = 0;
unsigned res = read_png_file(&ref_draw_buf, &ref_img_width, &ref_img_height, fn_ref_full);
if(res) {
lv_test_screenshot_result_t comp_res;
#if LV_TEST_SCREENSHOT_CREATE_REFERENCE_IMAGE
LV_LOG_WARN("%s%s", fn_ref_full, " was not found, creating it now from the rendered screen");
write_png_file(screen_buf_xrgb8888, draw_buf->header.w, draw_buf->header.h, fn_ref_full);
comp_res = LV_TEST_SCREENSHOT_RESULT_PASSED;
#else
comp_res = LV_TEST_SCREENSHOT_RESULT_NO_REFERENCE_IMAGE;
#endif
lv_free(screen_buf_xrgb8888);
if(ref_draw_buf) lv_draw_buf_destroy(ref_draw_buf);
return comp_res;
}
if(ref_img_width != draw_buf->header.w || ref_img_height != draw_buf->header.h) {
LV_LOG_WARN("The dimensions of the rendered and the %s reference image don't match", fn_ref);
lv_free(screen_buf_xrgb8888);
if(ref_draw_buf) lv_draw_buf_destroy(ref_draw_buf);
return LV_TEST_SCREENSHOT_RESULT_FAILED;
}
unsigned x, y;
bool err = false;
for(y = 0; y < ref_img_height; y++) {
uint8_t * screen_buf_tmp = screen_buf_xrgb8888 + draw_buf->header.w * 4 * y;
uint8_t * ref_row = (uint8_t *)ref_draw_buf->data + y * ref_draw_buf->header.stride;
for(x = 0; x < ref_img_width; x++) {
uint8_t * ptr_ref = &(ref_row[x * 4]);
uint8_t * ptr_act = &screen_buf_tmp[x * 4];
if(LV_ABS((int32_t) ptr_act[0] - (int32_t) ptr_ref[0]) > tolerance ||
LV_ABS((int32_t) ptr_act[1] - (int32_t) ptr_ref[1]) > tolerance ||
LV_ABS((int32_t) ptr_act[2] - (int32_t) ptr_ref[2]) > tolerance) {
uint32_t act_px = (ptr_act[2] << 16) + (ptr_act[1] << 8) + (ptr_act[0] << 0);
uint32_t ref_px = 0;
memcpy(&ref_px, ptr_ref, 3);
LV_LOG("\nScreenshot compare error\n"
" - File: %s\n"
" - At x:%d, y:%d.\n"
" - Expected: %X\n"
" - Actual: %X\n"
" - Tolerance: %d\n",
fn_ref_full, x, y, ref_px, act_px, tolerance);
err = true;
break;
}
}
if(err) break;
}
if(err) {
char fn_ref_no_ext[128];
lv_strlcpy(fn_ref_no_ext, fn_ref, sizeof(fn_ref_no_ext));
fn_ref_no_ext[strlen(fn_ref_no_ext) - 4] = '\0';
char fn_err_full[256];
lv_snprintf(fn_err_full, sizeof(fn_err_full), "%s%s_err.png", REF_IMGS_PATH, fn_ref_no_ext);
write_png_file(screen_buf_xrgb8888, draw_buf->header.w, draw_buf->header.h, fn_err_full);
}
fflush(stdout);
lv_free(screen_buf_xrgb8888);
if(ref_draw_buf) lv_draw_buf_destroy(ref_draw_buf);
return err ? LV_TEST_SCREENSHOT_RESULT_FAILED : LV_TEST_SCREENSHOT_RESULT_PASSED;
}
static unsigned read_png_file(lv_draw_buf_t ** refr_draw_buf, unsigned * width, unsigned * height,
const char * file_name)
{
unsigned error = lodepng_decode32_file((void *)refr_draw_buf, width, height, file_name);
if(error) LV_LOG_WARN("error %u: %s\n", error, lodepng_error_text(error));
return error;
}
static unsigned write_png_file(void * raw_img, uint32_t width, uint32_t height, char * file_name)
{
unsigned error = lodepng_encode32_file(file_name, raw_img, width, height);
if(error) LV_LOG_WARN("error %u: %s\n", error, lodepng_error_text(error));
return error;
}
static void buf_to_xrgb8888(const lv_draw_buf_t * draw_buf, uint8_t * buf_out)
{
uint32_t stride = draw_buf->header.stride;
lv_color_format_t cf_in = draw_buf->header.cf;
const uint8_t * buf_in = draw_buf->data;
if(cf_in == LV_COLOR_FORMAT_RGB565 || cf_in == LV_COLOR_FORMAT_RGB565_SWAPPED) {
if(cf_in == LV_COLOR_FORMAT_RGB565_SWAPPED) {
lv_draw_sw_rgb565_swap(draw_buf->data, draw_buf->header.w * draw_buf->header.h);
}
uint32_t y;
for(y = 0; y < draw_buf->header.h; y++) {
uint32_t x;
for(x = 0; x < draw_buf->header.w; x++) {
const lv_color16_t * c16 = (const lv_color16_t *)&buf_in[x * 2];
buf_out[x * 4 + 3] = 0xff;
buf_out[x * 4 + 2] = (c16->blue * 2106) >> 8; /*To make it rounded*/
buf_out[x * 4 + 1] = (c16->green * 1037) >> 8;
buf_out[x * 4 + 0] = (c16->red * 2106) >> 8;
}
buf_in += stride;
buf_out += draw_buf->header.w * 4;
}
}
else if(cf_in == LV_COLOR_FORMAT_ARGB8888 || cf_in == LV_COLOR_FORMAT_XRGB8888 ||
cf_in == LV_COLOR_FORMAT_ARGB8888_PREMULTIPLIED) {
uint32_t y;
for(y = 0; y < draw_buf->header.h; y++) {
uint32_t x;
for(x = 0; x < draw_buf->header.w; x++) {
buf_out[x * 4 + 3] = buf_in[x * 4 + 3];
buf_out[x * 4 + 2] = buf_in[x * 4 + 0];
buf_out[x * 4 + 1] = buf_in[x * 4 + 1];
buf_out[x * 4 + 0] = buf_in[x * 4 + 2];
}
buf_in += stride;
buf_out += draw_buf->header.w * 4;
}
}
else if(cf_in == LV_COLOR_FORMAT_RGB888) {
uint32_t y;
for(y = 0; y < draw_buf->header.h; y++) {
uint32_t x;
for(x = 0; x < draw_buf->header.w; x++) {
buf_out[x * 4 + 3] = 0xff;
buf_out[x * 4 + 2] = buf_in[x * 3 + 0];
buf_out[x * 4 + 1] = buf_in[x * 3 + 1];
buf_out[x * 4 + 0] = buf_in[x * 3 + 2];
}
buf_in += stride;
buf_out += draw_buf->header.w * 4;
}
}
else if(cf_in == LV_COLOR_FORMAT_L8) {
uint32_t y;
for(y = 0; y < draw_buf->header.h; y++) {
uint32_t x;
for(x = 0; x < draw_buf->header.w; x++) {
buf_out[x * 4 + 3] = 0xff;
buf_out[x * 4 + 2] = buf_in[x];
buf_out[x * 4 + 1] = buf_in[x];
buf_out[x * 4 + 0] = buf_in[x];
}
buf_in += stride;
buf_out += draw_buf->header.w * 4;
}
}
else if(cf_in == LV_COLOR_FORMAT_AL88) {
uint32_t y;
for(y = 0; y < draw_buf->header.h; y++) {
uint32_t x;
for(x = 0; x < draw_buf->header.w; x++) {
buf_out[x * 4 + 3] = buf_in[x * 2 + 1];
buf_out[x * 4 + 2] = buf_in[x * 2 + 0];
buf_out[x * 4 + 1] = buf_in[x * 2 + 0];
buf_out[x * 4 + 0] = buf_in[x * 2 + 0];
}
buf_in += stride;
buf_out += draw_buf->header.w * 4;
}
}
else if(cf_in == LV_COLOR_FORMAT_I1) {
buf_in += 8;
uint32_t y;
for(y = 0; y < draw_buf->header.h; y++) {
uint32_t x;
for(x = 0; x < draw_buf->header.w; x++) {
const uint8_t byte = buf_in[x / 8] ;
const uint8_t bit_pos = x % 8;
const uint8_t pixel = (byte >> (7 - bit_pos)) & 0x01;
buf_out[x * 4 + 3] = 0xff;
buf_out[x * 4 + 2] = pixel ? 0xff : 0x00;
buf_out[x * 4 + 1] = pixel ? 0xff : 0x00;
buf_out[x * 4 + 0] = pixel ? 0xff : 0x00;
}
buf_in += stride;
buf_out += draw_buf->header.w * 4;
}
}
}
static void create_folders_if_needed(const char * path)
{
char * ptr;
char * path_copy = lv_strdup(path);
if(path_copy == NULL) {
LV_LOG_ERROR("Error duplicating path");
exit(EXIT_FAILURE);
}
char * token = strtok_r(path_copy, "/", &ptr);
char current_path[1024] = {'\0'}; /* Adjust the size as needed */
while(token && ptr && *ptr != '\0') {
lv_strcat(current_path, token);
lv_strcat(current_path, "/");
int mkdir_retval = mkdir(current_path, 0777);
if(mkdir_retval == 0) {
LV_LOG_INFO("Created folder: %s\n", current_path);
}
else if(errno != EEXIST) {
perror("Error creating folder");
lv_free(path_copy);
exit(EXIT_FAILURE);
}
token = strtok_r(NULL, "/", &ptr);
}
lv_free(path_copy);
}
#endif

View File

@@ -0,0 +1,79 @@
/**
* @file lv_test_screenshot_compare.h
*
*/
#ifndef LV_TEST_SCREENSHOT_COMPARE_H
#define LV_TEST_SCREENSHOT_COMPARE_H
#ifdef __cplusplus
extern "C" {
#endif
/*********************
* INCLUDES
*********************/
#include "../../lv_conf_internal.h"
#if LV_USE_TEST && defined(LV_USE_TEST_SCREENSHOT_COMPARE) && LV_USE_TEST_SCREENSHOT_COMPARE
/*********************
* DEFINES
*********************/
/**********************
* TYPEDEFS
**********************/
/**
* Return value of `lv_test_screenshot_compare`
*/
typedef enum {
/**
* The screenshot is different than the reference image
*/
LV_TEST_SCREENSHOT_RESULT_FAILED,
/**
* The screenshot is the same as the reference image.
* It is also returned if `LV_TEST_SCREENSHOT_CREATE_REFERENCE_IMAGE` is enabled
* and the reference image was missing.
*/
LV_TEST_SCREENSHOT_RESULT_PASSED,
/**
* If `LV_TEST_SCREENSHOT_CREATE_REFERENCE_IMAGE` is not enabled
* and the reference image is missing.
*/
LV_TEST_SCREENSHOT_RESULT_NO_REFERENCE_IMAGE,
} lv_test_screenshot_result_t;
/**********************
* GLOBAL PROTOTYPES
**********************/
/**
* Compare the current content of the test screen with a reference PNG image
* - If the reference image is not found it will be created automatically from the rendered screen.
* - If the compare fails an `<image_name>_err.png` file will be created with the rendered content next to the reference image.
*
* It requires lodepng.
*
* @param fn_ref path to the reference image. Will be appended to REF_IMGS_PATH if set.
* @return An element of `lv_test_screenshot_result_t`
* @note This function assumes that the default display is the test display that was created by
* `lv_test_display_create()`
*/
lv_test_screenshot_result_t lv_test_screenshot_compare(const char * fn_ref);
/**********************
* MACROS
**********************/
#endif /*LV_USE_TEST_SCREENSHOT_COMPARE*/
#ifdef __cplusplus
} /*extern "C"*/
#endif
#endif /*LV_TEST_SCREENSHOT_COMPARE_H*/

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,162 @@
/**
* @file vg_lite_matrix.c
*
*/
/*********************
* INCLUDES
*********************/
#include "../../lv_conf_internal.h"
#if LV_USE_DRAW_VG_LITE && LV_USE_VG_LITE_THORVG
#include <math.h>
#include <string.h>
#include "vg_lite.h"
/*********************
* DEFINES
*********************/
#define VG_SW_BLIT_PRECISION_OPT 1
/**********************
* TYPEDEFS
**********************/
/**********************
* STATIC PROTOTYPES
**********************/
/**********************
* STATIC VARIABLES
**********************/
/**********************
* MACROS
**********************/
/**********************
* GLOBAL FUNCTIONS
**********************/
vg_lite_error_t vg_lite_identity(vg_lite_matrix_t * matrix)
{
/* Set identify matrix. */
matrix->m[0][0] = 1.0f;
matrix->m[0][1] = 0.0f;
matrix->m[0][2] = 0.0f;
matrix->m[1][0] = 0.0f;
matrix->m[1][1] = 1.0f;
matrix->m[1][2] = 0.0f;
matrix->m[2][0] = 0.0f;
matrix->m[2][1] = 0.0f;
matrix->m[2][2] = 1.0f;
#if VG_SW_BLIT_PRECISION_OPT
matrix->scaleX = 1.0f;
matrix->scaleY = 1.0f;
matrix->angle = 0.0f;
#endif /* VG_SW_BLIT_PRECISION_OPT */
return VG_LITE_SUCCESS;
}
static void multiply(vg_lite_matrix_t * matrix, vg_lite_matrix_t * mult)
{
vg_lite_matrix_t temp;
int row, column;
/* Process all rows. */
for(row = 0; row < 3; row++) {
/* Process all columns. */
for(column = 0; column < 3; column++) {
/* Compute matrix entry. */
temp.m[row][column] = (matrix->m[row][0] * mult->m[0][column])
+ (matrix->m[row][1] * mult->m[1][column])
+ (matrix->m[row][2] * mult->m[2][column]);
}
}
/* Copy temporary matrix into result. */
#if VG_SW_BLIT_PRECISION_OPT
memcpy(matrix, &temp, sizeof(vg_lite_float_t) * 9);
#else
memcpy(matrix, &temp, sizeof(temp));
#endif /* VG_SW_BLIT_PRECISION_OPT */
}
vg_lite_error_t vg_lite_translate(vg_lite_float_t x, vg_lite_float_t y, vg_lite_matrix_t * matrix)
{
/* Set translation matrix. */
vg_lite_matrix_t t = { { {1.0f, 0.0f, x},
{0.0f, 1.0f, y},
{0.0f, 0.0f, 1.0f},
},
0.0f, 0.0f, 0.0f
};
/* Multiply with current matrix. */
multiply(matrix, &t);
return VG_LITE_SUCCESS;
}
vg_lite_error_t vg_lite_scale(vg_lite_float_t scale_x, vg_lite_float_t scale_y, vg_lite_matrix_t * matrix)
{
/* Set scale matrix. */
vg_lite_matrix_t s = { { {scale_x, 0.0f, 0.0f},
{0.0f, scale_y, 0.0f},
{0.0f, 0.0f, 1.0f},
},
0.0f, 0.0f, 0.0f
};
/* Multiply with current matrix. */
multiply(matrix, &s);
#if VG_SW_BLIT_PRECISION_OPT
matrix->scaleX = matrix->scaleX * scale_x;
matrix->scaleY = matrix->scaleY * scale_y;
#endif /* VG_SW_BLIT_PRECISION_OPT */
return VG_LITE_SUCCESS;
}
vg_lite_error_t vg_lite_rotate(vg_lite_float_t degrees, vg_lite_matrix_t * matrix)
{
/* Convert degrees into radians. */
vg_lite_float_t angle = (degrees / 180.0f) * 3.141592654f;
/* Compute cosine and sine values. */
vg_lite_float_t cos_angle = cosf(angle);
vg_lite_float_t sin_angle = sinf(angle);
/* Set rotation matrix. */
vg_lite_matrix_t r = { { {cos_angle, -sin_angle, 0.0f},
{sin_angle, cos_angle, 0.0f},
{0.0f, 0.0f, 1.0f},
},
0.0f, 0.0f, 0.0f
};
/* Multiply with current matrix. */
multiply(matrix, &r);
#if VG_SW_BLIT_PRECISION_OPT
matrix->angle = matrix->angle + degrees;
if(matrix->angle >= 360) {
vg_lite_uint32_t count = (vg_lite_uint32_t)matrix->angle / 360;
matrix->angle = matrix->angle - count * 360;
}
#endif /* VG_SW_BLIT_PRECISION_OPT */
return VG_LITE_SUCCESS;
}
/**********************
* STATIC FUNCTIONS
**********************/
#endif /*LV_USE_VG_LITE_THORVG*/

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -0,0 +1,299 @@
/**
* @file lv_draw_eve_image.c
*
*/
/* Created on: 17 jun 2023
* Author: juanj
*
* Modified by LVGL
*/
/*********************
* INCLUDES
*********************/
#include "lv_draw_eve_private.h"
#if LV_USE_DRAW_EVE
#include "../lv_draw_image_private.h"
#include "lv_draw_eve_ram_g.h"
#include "lv_eve.h"
/*********************
* DEFINES
*********************/
/**********************
* TYPEDEFS
**********************/
/**********************
* STATIC PROTOTYPES
**********************/
static void convert_row_rgb565a8_to_argb4444(const uint8_t * src, const uint8_t * src_alpha, uint8_t * dst,
uint32_t width);
static void convert_row_argb8888_to_argb4444(const uint8_t * src, uint8_t * dst, uint32_t width);
/**********************
* STATIC VARIABLES
**********************/
/**********************
* MACROS
**********************/
#define F16_PIVOT_SHIFT(x) ((int32_t)((((x) >> 1)) * 65536L))
#define F16_SCALE_DIV_256(x) ((int32_t)(((x) / 256.0f) * 65536L))
/**********************
* GLOBAL FUNCTIONS
**********************/
void lv_draw_eve_image(lv_draw_task_t * t, const lv_draw_image_dsc_t * draw_dsc, const lv_area_t * coords)
{
if(!lv_draw_eve_image_src_check(draw_dsc->src)) {
return;
}
const lv_image_dsc_t * img_dsc = draw_dsc->src;
int32_t src_w = img_dsc->header.w;
int32_t src_h = img_dsc->header.h;
int32_t src_stride = img_dsc->header.stride;
lv_color_format_t src_cf = img_dsc->header.cf;
if(src_stride == 0) {
src_stride = src_w * lv_color_format_get_size(src_cf);
}
uint8_t eve_format;
int32_t eve_stride;
switch(src_cf) {
case LV_COLOR_FORMAT_L8:
eve_format = EVE_L8;
eve_stride = src_stride;
break;
case LV_COLOR_FORMAT_RGB565:
eve_format = EVE_RGB565;
eve_stride = src_stride;
break;
case LV_COLOR_FORMAT_RGB565A8:
case LV_COLOR_FORMAT_ARGB8888:
eve_format = EVE_ARGB4;
eve_stride = src_w * 2;
break;
default :
LV_ASSERT(0);
}
uint32_t ramg_addr = lv_draw_eve_image_upload_image(true, img_dsc);
if(ramg_addr == LV_DRAW_EVE_RAMG_OUT_OF_RAMG) {
LV_LOG_WARN("Could not load image because space could not be allocated in RAM_G.");
return;
}
lv_eve_scissor(t->clip_area.x1, t->clip_area.y1, t->clip_area.x2, t->clip_area.y2);
lv_eve_save_context();
lv_eve_color_opa(draw_dsc->opa);
if(draw_dsc->recolor_opa > LV_OPA_MIN) {
lv_eve_color(lv_color_mix(draw_dsc->recolor, lv_color_white(), draw_dsc->recolor_opa));
}
lv_eve_primitive(LV_EVE_PRIMITIVE_BITMAPS);
lv_eve_bitmap_source(ramg_addr);
/*real height and width is mandatory for rotation and scale (Clip Area)*/
lv_eve_bitmap_size(EVE_NEAREST, EVE_BORDER, EVE_BORDER, src_w, src_h);
lv_eve_bitmap_layout(eve_format, eve_stride, src_h);
if(draw_dsc->rotation || draw_dsc->scale_x != LV_SCALE_NONE || draw_dsc->scale_y != LV_SCALE_NONE) {
EVE_cmd_dl_burst(CMD_LOADIDENTITY);
EVE_cmd_translate_burst(F16(coords->x1 - t->clip_area.x1 + draw_dsc->pivot.x),
F16(coords->y1 - t->clip_area.y1 + draw_dsc->pivot.y));
if(draw_dsc->scale_x != LV_SCALE_NONE || draw_dsc->scale_y != LV_SCALE_NONE) {
/*Image Scale*/
EVE_cmd_scale_burst(F16_SCALE_DIV_256(draw_dsc->scale_x), F16_SCALE_DIV_256(draw_dsc->scale_y));
}
if(draw_dsc->rotation != 0) {
/*Image Rotate*/
EVE_cmd_rotate_burst(DEGREES(draw_dsc->rotation));
}
EVE_cmd_translate_burst(-F16(draw_dsc->pivot.x), -F16(draw_dsc->pivot.y));
EVE_cmd_dl_burst(CMD_SETMATRIX);
EVE_cmd_dl_burst(CMD_LOADIDENTITY);
lv_eve_vertex_2f(t->clip_area.x1, t->clip_area.y1);
}
else {
lv_eve_vertex_2f(coords->x1, coords->y1);
}
lv_eve_restore_context();
}
bool lv_draw_eve_image_src_check(const void * src)
{
if(lv_image_src_get_type(src) != LV_IMAGE_SRC_VARIABLE) {
LV_LOG_WARN("lv_draw_eve can only render images from variables (not files or symbols) for now.");
return false;
}
const lv_image_dsc_t * img_dsc = src;
switch(img_dsc->header.cf) {
case LV_COLOR_FORMAT_L8:
case LV_COLOR_FORMAT_RGB565:
case LV_COLOR_FORMAT_RGB565A8:
case LV_COLOR_FORMAT_ARGB8888:
break;
default :
LV_LOG_WARN("lv_draw_eve can only render L8, RGB565, RGB565A8, and ARGB8888 images for now.");
return false;
}
return true;
}
uint32_t lv_draw_eve_image_upload_image(bool burst_is_active, const lv_image_dsc_t * img_dsc)
{
const uint8_t * src_buf = img_dsc->data;
int32_t src_w = img_dsc->header.w;
int32_t src_h = img_dsc->header.h;
int32_t src_stride = img_dsc->header.stride;
lv_color_format_t src_cf = img_dsc->header.cf;
if(src_stride == 0) {
src_stride = src_w * lv_color_format_get_size(src_cf);
}
int32_t eve_stride;
uint8_t eve_alignment;
switch(src_cf) {
case LV_COLOR_FORMAT_L8:
eve_stride = src_stride;
eve_alignment = 1;
break;
case LV_COLOR_FORMAT_RGB565:
eve_stride = src_stride;
eve_alignment = 2;
break;
case LV_COLOR_FORMAT_RGB565A8:
case LV_COLOR_FORMAT_ARGB8888:
eve_stride = src_w * 2;
eve_alignment = 2;
break;
default :
LV_ASSERT(0);
}
int32_t eve_size = eve_stride * src_h;
uint32_t ramg_addr;
bool img_is_loaded = lv_draw_eve_ramg_get_addr(&ramg_addr, (uintptr_t) src_buf, eve_size, eve_alignment);
/* New image to load */
if(!img_is_loaded && ramg_addr != LV_DRAW_EVE_RAMG_OUT_OF_RAMG) {
/* Load image to RAM_G */
if(burst_is_active) {
EVE_end_cmd_burst();
}
switch(src_cf) {
case LV_COLOR_FORMAT_L8:
case LV_COLOR_FORMAT_RGB565:
EVE_memWrite_flash_buffer(ramg_addr, src_buf, eve_size);
break;
case LV_COLOR_FORMAT_RGB565A8: {
uint8_t * tmp_buf = lv_malloc(eve_stride);
LV_ASSERT_MALLOC(tmp_buf);
const uint8_t * src_alpha_buf = src_buf + src_h * src_stride;
int32_t src_alpha_stride = src_stride / 2;
for(uint32_t y = 0; y < src_h; y++) {
convert_row_rgb565a8_to_argb4444(src_buf + y * src_stride, src_alpha_buf + y * src_alpha_stride, tmp_buf, src_w);
EVE_memWrite_flash_buffer(ramg_addr + y * eve_stride, tmp_buf, eve_stride);
}
lv_free(tmp_buf);
break;
}
case LV_COLOR_FORMAT_ARGB8888: {
uint8_t * tmp_buf = lv_malloc(eve_stride);
LV_ASSERT_MALLOC(tmp_buf);
for(uint32_t y = 0; y < src_h; y++) {
convert_row_argb8888_to_argb4444(src_buf + y * src_stride, tmp_buf, src_w);
EVE_memWrite_flash_buffer(ramg_addr + y * eve_stride, tmp_buf, eve_stride);
}
lv_free(tmp_buf);
break;
}
default:
LV_ASSERT(0);
}
if(burst_is_active) {
EVE_start_cmd_burst();
}
}
return ramg_addr;
}
/**********************
* STATIC FUNCTIONS
**********************/
static void convert_row_rgb565a8_to_argb4444(const uint8_t * src, const uint8_t * src_alpha, uint8_t * dst,
uint32_t width)
{
for(uint32_t x = 0; x < width; x++) {
uint16_t rgb565 = ((const uint16_t *) src)[x];
uint8_t r5 = (rgb565 >> 11) & 0x1F;
uint8_t g6 = (rgb565 >> 5) & 0x3F;
uint8_t b5 = rgb565 & 0x1F;
uint8_t alpha = src_alpha[x];
uint8_t r4 = r5 >> 1;
uint8_t g4 = g6 >> 2;
uint8_t b4 = b5 >> 1;
uint8_t a4 = alpha >> 4;
uint16_t argb4444 = (a4 << 12) | (r4 << 8) | (g4 << 4) | b4;
dst[2 * x] = argb4444 & 0xFF;
dst[2 * x + 1] = (argb4444 >> 8) & 0xFF;
}
}
static void convert_row_argb8888_to_argb4444(const uint8_t * src, uint8_t * dst, uint32_t width)
{
for(uint32_t x = 0; x < width; x++) {
uint8_t blue = src[4 * x];
uint8_t green = src[4 * x + 1];
uint8_t red = src[4 * x + 2];
uint8_t alpha = src[4 * x + 3];
uint8_t r4 = red >> 4;
uint8_t g4 = green >> 4;
uint8_t b4 = blue >> 4;
uint8_t a4 = alpha >> 4;
uint16_t argb4444 = (a4 << 12) | (r4 << 8) | (g4 << 4) | b4;
dst[2 * x] = argb4444 & 0xFF;
dst[2 * x + 1] = (argb4444 >> 8) & 0xFF;
}
}
#endif /*LV_USE_DRAW_EVE*/

View File

@@ -0,0 +1,233 @@
/**
* @file lv_draw_eve_letter.c
*
*/
/* Author: juanj
*
* Modified by LVGL
*/
/*********************
* INCLUDES
*********************/
#include "lv_draw_eve_private.h"
#if LV_USE_DRAW_EVE
#include "../lv_draw_private.h"
#include "../lv_draw_label_private.h"
#include "../lv_draw_rect.h"
#include "lv_eve.h"
/*********************
* DEFINES
*********************/
/**********************
* TYPEDEFS
**********************/
/**********************
* STATIC PROTOTYPES
**********************/
static void lv_draw_eve_letter_cb(lv_draw_task_t * t, lv_draw_glyph_dsc_t * glyph_draw_dsc,
lv_draw_fill_dsc_t * fill_draw_dsc, const lv_area_t * fill_area);
static void font_bitmap_to_ramg(uint32_t addr, const uint8_t * src, uint32_t width,
uint32_t height, uint8_t src_stride_align);
/**********************
* STATIC VARIABLES
**********************/
/**********************
* MACROS
**********************/
#define GET_NIBBLE_1(w) ((uint8_t) ((w) >> 4))
#define GET_NIBBLE_2(w) ((uint8_t) ((w) & 0xf))
/**********************
* GLOBAL FUNCTIONS
**********************/
void lv_draw_eve_label(lv_draw_task_t * t, const lv_draw_label_dsc_t * dsc, const lv_area_t * coords)
{
if(dsc->opa <= LV_OPA_MIN) return;
lv_eve_scissor(t->clip_area.x1, t->clip_area.y1, t->clip_area.x2, t->clip_area.y2);
lv_eve_save_context();
lv_eve_primitive(LV_EVE_PRIMITIVE_BITMAPS);
lv_draw_label_iterate_characters(t, dsc, coords, lv_draw_eve_letter_cb);
lv_eve_restore_context();
}
bool lv_draw_eve_label_font_check(const lv_font_t * font)
{
if(font->get_glyph_bitmap != lv_font_get_bitmap_fmt_txt) {
LV_LOG_WARN("lv_draw_eve can only render static fonts for now.");
return false;
}
const lv_font_fmt_txt_dsc_t * font_dsc = font->dsc;
/* Only 4 bpp is supported for now. Support for 1 and 8 bpp can be added. (EVE_L1, EVE_L8) */
if(font_dsc->bpp != 4) {
LV_LOG_WARN("lv_draw_eve can only render static fonts for now.");
return false;
}
return true;
}
uint32_t lv_draw_eve_label_upload_glyph(bool burst_is_active, const lv_font_fmt_txt_dsc_t * font_dsc,
uint32_t gid_index)
{
const lv_font_fmt_txt_glyph_dsc_t * glyph_dsc = &font_dsc->glyph_dsc[gid_index];
const uint8_t * glyph_bitmap = &font_dsc->glyph_bitmap[glyph_dsc->bitmap_index];
uint16_t g_box_w = glyph_dsc->box_w;
uint16_t g_box_h = glyph_dsc->box_h;
uint16_t g_aligned_stride = (g_box_w + 1) / 2;
uint32_t glyph_ramg_size = g_aligned_stride * g_box_h;
uint32_t ramg_addr;
uintptr_t glyph_ramg_key = (uintptr_t) glyph_bitmap;
bool font_is_loaded = lv_draw_eve_ramg_get_addr(&ramg_addr, glyph_ramg_key, glyph_ramg_size, 1);
/* If the font is not yet loaded in ramG, load it */
if(!font_is_loaded && ramg_addr != LV_DRAW_EVE_RAMG_OUT_OF_RAMG) {
if(burst_is_active) {
EVE_end_cmd_burst();
}
uint8_t glyph_bitmap_stride_align = font_dsc->stride;
font_bitmap_to_ramg(ramg_addr, glyph_bitmap, g_box_w, g_box_h, glyph_bitmap_stride_align);
if(burst_is_active) {
EVE_start_cmd_burst();
}
}
return ramg_addr;
}
/**********************
* STATIC FUNCTIONS
**********************/
static void lv_draw_eve_letter_cb(lv_draw_task_t * t, lv_draw_glyph_dsc_t * glyph_draw_dsc,
lv_draw_fill_dsc_t * fill_draw_dsc, const lv_area_t * fill_area)
{
if(fill_draw_dsc && fill_area) {
/* draw UNDERLINE and STRIKETHROUGH */
lv_eve_draw_rect_simple(fill_area->x1, fill_area->y1, fill_area->x2, fill_area->y2, 0);
}
if(glyph_draw_dsc == NULL)
return; /* Important */
const lv_font_t * font = glyph_draw_dsc->g->resolved_font;
if(!lv_draw_eve_label_font_check(font)) {
return;
}
const lv_font_fmt_txt_dsc_t * font_dsc = font->dsc;
uint32_t gid_index = glyph_draw_dsc->g->gid.index;
const lv_font_fmt_txt_glyph_dsc_t * glyph_dsc = &font_dsc->glyph_dsc[gid_index];
uint16_t g_box_w = glyph_dsc->box_w;
uint16_t g_box_h = glyph_dsc->box_h;
uint16_t g_aligned_stride = (g_box_w + 1) / 2;
uint8_t bpp_eve = EVE_L4;
uint32_t ramg_addr = lv_draw_eve_label_upload_glyph(true, font_dsc, gid_index);
if(ramg_addr == LV_DRAW_EVE_RAMG_OUT_OF_RAMG) {
LV_LOG_WARN("Could not load glyph because space could not be allocated in RAM_G.");
return;
}
lv_eve_color_opa(glyph_draw_dsc->opa);
lv_eve_color(glyph_draw_dsc->color);
lv_eve_bitmap_source(ramg_addr);
lv_eve_bitmap_size(EVE_NEAREST, EVE_BORDER, EVE_BORDER, g_box_w, g_box_h);
lv_eve_bitmap_layout(bpp_eve, g_aligned_stride, g_box_h);
lv_eve_vertex_2f(glyph_draw_dsc->letter_coords->x1, glyph_draw_dsc->letter_coords->y1);
}
static void font_bitmap_to_ramg(uint32_t addr, const uint8_t * src, uint32_t width,
uint32_t height, uint8_t src_stride_align)
{
uint32_t stride = (width + 1) / 2;
if(src_stride_align == 1 || (src_stride_align == 0 && width % 2 == 0)) {
uint32_t size = stride * height;
EVE_memWrite_flash_buffer(addr, src, size);
return;
}
if(src_stride_align > 0) {
uint32_t src_stride = LV_ALIGN_UP(stride, src_stride_align);
for(uint32_t y = 0; y < height; y++) {
EVE_memWrite_sram_buffer(addr, src, stride);
addr += stride;
src += src_stride;
}
return;
}
uint8_t * row_buf = lv_malloc(stride);
LV_ASSERT_MALLOC(row_buf);
uint32_t src_i = 0;
uint8_t nibble_1;
uint8_t nibble_2;
uint8_t key = 0;
/* Iterate through each row of the bitmap*/
for(uint32_t y = 0; y < height; y++) {
/* Iterate through each byte of the row*/
uint32_t row_i;
for(row_i = 0; row_i < (width / 2); ++row_i) {
/*Get the two nibbles from the current byte*/
if(key == 0) {
nibble_1 = GET_NIBBLE_1(src[src_i]);
nibble_2 = GET_NIBBLE_2(src[src_i]);
}
else {
nibble_1 = GET_NIBBLE_2(src[src_i - 1]);
nibble_2 = GET_NIBBLE_1(src[src_i]);
}
/*Combine the nibbles and assign the result to the output byte*/
row_buf[row_i] = (nibble_1 << 4) | nibble_2;
src_i++;
}
/*process the last remaining nibble*/
row_buf[row_i] =
(key == 0) ?
(GET_NIBBLE_1(src[src_i])) << 4 | 0x0 : (GET_NIBBLE_2(src[src_i - 1])) << 4 | 0x0;
key = (key == 0) ? 1 : 0;
src_i += (key == 1) ? 1 : 0;
EVE_memWrite_sram_buffer(addr, row_buf, stride);
addr += stride;
}
lv_free(row_buf);
}
#endif /*LV_USE_DRAW_EVE*/

View File

@@ -0,0 +1,63 @@
/**
* @file lv_draw_eve_line.c
*
*/
/* Created on: 8 abr 2023
* Author: juanj
*
* Modified by LVGL
*/
/*********************
* INCLUDES
*********************/
#include "lv_draw_eve_private.h"
#if LV_USE_DRAW_EVE
#include "lv_eve.h"
void lv_draw_eve_line(lv_draw_task_t * t, const lv_draw_line_dsc_t * dsc)
{
if(dsc->width == 0)
return;
if(dsc->opa <= LV_OPA_MIN)
return;
if(dsc->p1.x == dsc->p2.x && dsc->p1.y == dsc->p2.y)
return;
uint32_t line_w = dsc->width * 8;
lv_eve_scissor(t->clip_area.x1, t->clip_area.y1, t->clip_area.x2, t->clip_area.y2);
lv_eve_save_context();
lv_eve_color_opa(dsc->opa);
lv_eve_color(dsc->color);
if(dsc->dash_gap || dsc->dash_width) {
LV_LOG_WARN("line dash_gap and dash_width not implemented by EVE yet.");
}
/* Check if it's a vertical or horizontal line without rounding */
bool is_vertical = (dsc->p1.x == dsc->p2.x);
bool is_horizontal = (dsc->p1.y == dsc->p2.y);
bool no_round = (!dsc->round_end || !dsc->round_start);
if((is_vertical || is_horizontal) && no_round) {
lv_eve_primitive(LV_EVE_PRIMITIVE_RECTS);
lv_eve_vertex_2f(dsc->p1.x, dsc->p1.y);
lv_eve_vertex_2f(dsc->p2.x, dsc->p2.y);
}
else {
/* Draw inclined line or line with rounding (not possible without rounding)*/
lv_eve_primitive(LV_EVE_PRIMITIVE_LINE_STRIP);
lv_eve_line_width(line_w);
lv_eve_vertex_2f(dsc->p1.x, dsc->p1.y);
lv_eve_vertex_2f(dsc->p2.x, dsc->p2.y);
}
lv_eve_restore_context();
}
#endif /*LV_USE_DRAW_EVE*/

View File

@@ -0,0 +1,117 @@
/**
* @file lv_draw_eve_private.h
*
*/
/* Author: juanj
*
* Modified by LVGL
*/
#ifndef LV_DRAW_EVE_PRIVATE_H
#define LV_DRAW_EVE_PRIVATE_H
#ifdef __cplusplus
extern "C" {
#endif
/*********************
* INCLUDES
*********************/
#include "lv_draw_eve.h"
#if LV_USE_DRAW_EVE
#include "lv_draw_eve_target.h"
#include "lv_draw_eve_ram_g.h"
#include "../lv_draw_private.h"
#include "../../misc/lv_types.h"
#include "../../core/lv_global.h"
#include "../lv_draw_triangle.h"
#include "../lv_draw_line.h"
#include "../lv_draw_label.h"
#include "../../font/fmt_txt/lv_font_fmt_txt.h"
#include "../lv_draw_arc.h"
#if LV_DRAW_EVE_WRITE_BUFFER_SIZE != 0 && LV_DRAW_EVE_WRITE_BUFFER_SIZE < 4
#warning LV_DRAW_EVE_WRITE_BUFFER_SIZE cannot be less than 4. Using 0 (buffering disabled).
#define LV_DRAW_EVE_WRITE_BUFFER_SIZE_INTERNAL 0
#else
#define LV_DRAW_EVE_WRITE_BUFFER_SIZE_INTERNAL LV_DRAW_EVE_WRITE_BUFFER_SIZE
#endif
/*********************
* DEFINES
*********************/
/**********************
* TYPEDEFS
**********************/
typedef struct {
uintptr_t key;
uint32_t addr;
} lv_draw_eve_ramg_hash_table_cell_t;
typedef struct {
uint32_t ramg_addr_end;
uint32_t hash_table_cell_count;
uint32_t hash_table_cells_occupied;
lv_draw_eve_ramg_hash_table_cell_t * hash_table;
} lv_draw_eve_ramg_t;
struct _lv_draw_eve_unit_t {
lv_draw_unit_t base_unit;
lv_draw_task_t * task_act;
lv_display_t * disp;
lv_draw_eve_ramg_t ramg;
lv_draw_eve_parameters_t params;
lv_draw_eve_operation_cb_t op_cb;
#if LV_DRAW_EVE_WRITE_BUFFER_SIZE_INTERNAL
uint32_t lv_eve_write_buf_len;
uint8_t lv_eve_write_buf[LV_DRAW_EVE_WRITE_BUFFER_SIZE_INTERNAL];
#endif
};
/**********************
* GLOBAL PROTOTYPES
**********************/
void lv_draw_eve_image(lv_draw_task_t * t, const lv_draw_image_dsc_t * draw_dsc,
const lv_area_t * coords);
bool lv_draw_eve_image_src_check(const void * src);
uint32_t lv_draw_eve_image_upload_image(bool burst_is_active, const lv_image_dsc_t * img_dsc);
void lv_draw_eve_fill(lv_draw_task_t * t, const lv_draw_fill_dsc_t * dsc, const lv_area_t * coords);
void lv_draw_eve_border(lv_draw_task_t * t, const lv_draw_border_dsc_t * dsc,
const lv_area_t * coords);
void lv_draw_eve_line(lv_draw_task_t * t, const lv_draw_line_dsc_t * dsc);
void lv_draw_eve_label(lv_draw_task_t * t, const lv_draw_label_dsc_t * dsc,
const lv_area_t * coords);
bool lv_draw_eve_label_font_check(const lv_font_t * font);
uint32_t lv_draw_eve_label_upload_glyph(bool burst_is_active, const lv_font_fmt_txt_dsc_t * font_dsc,
uint32_t gid_index);
void lv_draw_eve_arc(lv_draw_task_t * t, const lv_draw_arc_dsc_t * dsc, const lv_area_t * coords);
void lv_draw_eve_triangle(lv_draw_task_t * t, const lv_draw_triangle_dsc_t * dsc);
/**********************
* MACROS
**********************/
#define DEGREES(n) ((65536UL * (n)) / 3600)
#define F16(x) ((int32_t)((x) * 65536L))
#define lv_draw_eve_unit_g (LV_GLOBAL_DEFAULT()->draw_eve_unit)
#endif /*LV_USE_DRAW_EVE*/
#ifdef __cplusplus
} /*extern "C"*/
#endif
#endif /*LV_DRAW_EVE_PRIVATE_H*/

View File

@@ -0,0 +1,221 @@
/**
* @file lv_draw_eve_ram_g.c
*
*/
/* Created on: 19 nov 2023
* Author: juanj
*
* Modified by LVGL
*/
/*********************
* INCLUDES
*********************/
#include "lv_draw_eve_private.h"
#if LV_USE_DRAW_EVE
#include "lv_draw_eve_ram_g.h"
#include "lv_eve.h"
/*********************
* DEFINES
*********************/
#define RAMG_DEBUG 0
/**********************
* TYPEDEFS
**********************/
/**********************
* STATIC PROTOTYPES
**********************/
static void ramg_init(lv_draw_eve_ramg_t * ramg);
static uint32_t hash_key(uintptr_t key);
static uint32_t fnv_1a_hash(const void * src, size_t len);
static void grow_hash_table(lv_draw_eve_ramg_t * ramg);
#if RAMG_DEBUG
static void ramg_debug(lv_draw_eve_ramg_t * ramg, uint32_t key_hash, uint32_t table_index);
#endif
/**********************
* STATIC VARIABLES
**********************/
/**********************
* MACROS
**********************/
/**********************
* GLOBAL FUNCTIONS
**********************/
bool lv_draw_eve_ramg_get_addr(uint32_t * addr_dst, uintptr_t key,
uint32_t addr_size, uint32_t addr_align)
{
LV_ASSERT(key != 0);
lv_draw_eve_ramg_t * ramg = &lv_draw_eve_unit_g->ramg;
if(ramg->hash_table_cell_count == 0) {
ramg_init(ramg);
}
uint32_t key_hash = hash_key(key);
uint32_t table_index = key_hash % ramg->hash_table_cell_count;
lv_draw_eve_ramg_hash_table_cell_t * cell;
while(1) {
cell = &ramg->hash_table[table_index];
if(cell->key == key) {
#if RAMG_DEBUG
ramg_debug(ramg, key_hash, table_index);
#endif
*addr_dst = cell->addr;
return true;
}
if(cell->key == 0) {
break;
}
table_index++;
if(table_index >= ramg->hash_table_cell_count) table_index = 0;
}
uint32_t addr_ret = LV_ALIGN_UP(ramg->ramg_addr_end, addr_align);
uint32_t addr_new_end = addr_ret + addr_size;
if(addr_new_end > 1024 * 1024) {
LV_LOG_WARN("EVE on-chip 1 MB RAM_G for images and fonts has run out.");
*addr_dst = LV_DRAW_EVE_RAMG_OUT_OF_RAMG;
return false;
}
ramg->ramg_addr_end = addr_new_end;
ramg->hash_table_cells_occupied++;
cell->key = key;
cell->addr = addr_ret;
*addr_dst = addr_ret;
#if RAMG_DEBUG
ramg_debug(ramg, key_hash, table_index);
#endif
if(ramg->hash_table_cells_occupied > ramg->hash_table_cell_count / 4 * 3) {
grow_hash_table(ramg);
}
return false;
}
/**********************
* STATIC FUNCTIONS
**********************/
static void ramg_init(lv_draw_eve_ramg_t * ramg)
{
ramg->hash_table_cell_count = 32;
ramg->hash_table = lv_calloc(32, sizeof(lv_draw_eve_ramg_hash_table_cell_t));
LV_ASSERT_MALLOC(ramg->hash_table);
}
static uint32_t hash_key(uintptr_t key)
{
return fnv_1a_hash(&key, sizeof(key));
}
static uint32_t fnv_1a_hash(const void * src, size_t len)
{
const uint8_t * src_u8 = src;
uint32_t hash = 2166136261u;
for(size_t i = 0; i < len; i++) {
hash ^= src_u8[i];
hash *= 16777619u;
}
return hash;
}
static void grow_hash_table(lv_draw_eve_ramg_t * ramg)
{
uint32_t old_cell_count = ramg->hash_table_cell_count;
lv_draw_eve_ramg_hash_table_cell_t * old_hash_table = ramg->hash_table;
ramg->hash_table_cell_count += ramg->hash_table_cell_count / 2;
ramg->hash_table = lv_calloc(ramg->hash_table_cell_count,
sizeof(lv_draw_eve_ramg_hash_table_cell_t));
LV_ASSERT_MALLOC(ramg->hash_table);
for(uint32_t i = 0; i < old_cell_count; i++) {
lv_draw_eve_ramg_hash_table_cell_t * old_cell = &old_hash_table[i];
if(old_cell->key == 0) continue;
uint32_t key_hash = hash_key(old_cell->key);
uint32_t new_table_index = key_hash % ramg->hash_table_cell_count;
lv_draw_eve_ramg_hash_table_cell_t * new_cell_dst = &ramg->hash_table[new_table_index];
while(new_cell_dst->key != 0) {
new_table_index++;
if(new_table_index >= ramg->hash_table_cell_count) new_table_index = 0;
new_cell_dst = &ramg->hash_table[new_table_index];
}
*new_cell_dst = *old_cell;
}
lv_free(old_hash_table);
}
#if RAMG_DEBUG
/*
Print tables like this:
113 kB of RAM_G used
================================-==-=====---=---===---=====-=-=-=-==--=======----=-==-==---=--===--=-=-=========================
========^$==========--=-==-=-=--=--=========---=----========-------===--=====----=======--=====--====--=====-=--=-=
'-' unoccupied cells
'=' occupied cells
'^' where the hash pointed to in the table initially and linear probing started
'$' where linear probing ended because a matching or unoccupied cell
was found. This symbol is not shown if the initial guess was correct.
This example has 244 cells. Each cell uses 8 bytes of local RAM, so just under 2 kB.
Each cell represents an allocation in EVE RAM_G. The RAM_G allocation sizes are not
represented in this table, except for the overall "113 kB of RAM_G used" message.
*/
static void ramg_debug(lv_draw_eve_ramg_t * ramg, uint32_t key_hash, uint32_t table_index)
{
uint32_t table_index_initial_guess = key_hash % ramg->hash_table_cell_count;
lv_log("%u kB of RAM_G used\n", (unsigned) ramg->ramg_addr_end / 1024);
for(uint32_t i = 0; i < ramg->hash_table_cell_count; i++) {
if(i != 0 && i % 128 == 0) {
lv_log("\n");
}
if(i == table_index_initial_guess) {
lv_log("^");
}
else if(i == table_index) {
lv_log("$");
}
else if(ramg->hash_table[i].key) {
lv_log("=");
}
else {
lv_log("-");
}
}
lv_log("\n\n");
}
#endif
#endif/*LV_USE_EVE_DRAW*/

View File

@@ -0,0 +1,48 @@
/**
* @file lv_draw_eve_ram_g.h
*
*/
/* Created on: 19 nov 2023
* Author: juanj
*
* Modified by LVGL
*/
#ifndef LV_DRAW_EVE_RAM_G_H
#define LV_DRAW_EVE_RAM_G_H
#ifdef __cplusplus
extern "C" {
#endif
/*********************
* INCLUDES
*********************/
#include "lv_draw_eve.h"
#if LV_USE_DRAW_EVE
/*********************
* DEFINES
*********************/
#define LV_DRAW_EVE_RAMG_OUT_OF_RAMG UINT32_MAX
/**********************
* TYPEDEFS
**********************/
/**********************
* GLOBAL PROTOTYPES
**********************/
bool lv_draw_eve_ramg_get_addr(uint32_t * addr_dst, uintptr_t key,
uint32_t addr_size, uint32_t addr_align);
#endif/*LV_USE_DRAW_EVE*/
#ifdef __cplusplus
} /*extern "C"*/
#endif
#endif /* LV_DRAW_EVE_RAM_G_H */

View File

@@ -0,0 +1,79 @@
/**
* @file lv_draw_eve_target.h
*
*/
#ifndef LV_DRAW_EVE_TARGET_H
#define LV_DRAW_EVE_TARGET_H
#ifdef __cplusplus
extern "C" {
#endif
/*********************
* INCLUDES
*********************/
#include "../../lv_conf_internal.h"
#if LV_USE_DRAW_EVE
#include "../../misc/lv_types.h"
#include LV_STDBOOL_INCLUDE
#include LV_STDINT_INCLUDE
typedef struct {
uint16_t hor_res; /**< active display width */
uint16_t ver_res; /**< active display height */
uint16_t hcycle; /**< total number of clocks per line, incl front/back porch */
uint16_t hoffset; /**< start of active line */
uint16_t hsync0; /**< start of horizontal sync pulse */
uint16_t hsync1; /**< end of horizontal sync pulse */
uint16_t vcycle; /**< total number of lines per screen, including pre/post */
uint16_t voffset; /**< start of active screen */
uint16_t vsync0; /**< start of vertical sync pulse */
uint16_t vsync1; /**< end of vertical sync pulse */
uint8_t swizzle; /**< FT8xx output to LCD - pin order */
uint8_t pclkpol; /**< LCD data is clocked in on this PCLK edge */
uint8_t cspread; /**< helps with noise, when set to 1 fewer signals are changed simultaneously, reset-default: 1 */
uint8_t pclk; /**< 60MHz / pclk = pclk frequency */
bool has_crystal; /**< has an external clock crystal */
bool has_gt911; /**< has a touch controller */
uint8_t backlight_pwm; /**< backlight PWM duty cycle 0 = off, 128 = max */
uint16_t backlight_freq; /**< backlight PWM frequency. try 4000 if unsure */
} lv_draw_eve_parameters_t;
typedef enum {
LV_DRAW_EVE_OPERATION_POWERDOWN_SET, /**< set the "PD_N" pin low */
LV_DRAW_EVE_OPERATION_POWERDOWN_CLEAR, /**< set the "PD_N" pin high */
LV_DRAW_EVE_OPERATION_CS_ASSERT, /**< set the "CS_N" pin low */
LV_DRAW_EVE_OPERATION_CS_DEASSERT, /**< set the "CS_N" pin high */
LV_DRAW_EVE_OPERATION_SPI_SEND, /**< send `length` bytes of `data` over SPI */
LV_DRAW_EVE_OPERATION_SPI_RECEIVE /**< receive `length` bytes into `data` from SPI */
} lv_draw_eve_operation_t;
typedef void (*lv_draw_eve_operation_cb_t)(lv_display_t * disp, lv_draw_eve_operation_t operation, void * data,
uint32_t length);
/*********************
* DEFINES
*********************/
/**********************
* TYPEDEFS
**********************/
/**********************
* GLOBAL PROTOTYPES
**********************/
/**********************
* MACROS
**********************/
#endif /*LV_USE_DRAW_EVE*/
#ifdef __cplusplus
} /*extern "C"*/
#endif
#endif /*LV_DRAW_EVE_TARGET_H*/

View File

@@ -0,0 +1,127 @@
/**
* @file lv_draw_eve_triangle.c
*
*/
/* Created on: 10 ene 2024
* Author: juanj
*
* Modified by LVGL
*/
/*********************
* INCLUDES
*********************/
#include "lv_draw_eve_private.h"
#if LV_USE_DRAW_EVE
#include "../../misc/lv_math.h"
#include "../../stdlib/lv_mem.h"
#include "../../misc/lv_area_private.h"
#include "../../misc/lv_color.h"
#include "../../stdlib/lv_string.h"
#include "lv_eve.h"
/*********************
* DEFINES
*********************/
/**********************
* TYPEDEFS
**********************/
/**********************
* STATIC PROTOTYPES
**********************/
/**********************
* STATIC VARIABLES
**********************/
/**********************
* MACROS
**********************/
/**********************
* GLOBAL FUNCTIONS
**********************/
void lv_draw_eve_triangle(lv_draw_task_t * t, const lv_draw_triangle_dsc_t * dsc)
{
lv_area_t tri_area;
tri_area.x1 = (int32_t)LV_MIN3(dsc->p[0].x, dsc->p[1].x, dsc->p[2].x);
tri_area.y1 = (int32_t)LV_MIN3(dsc->p[0].y, dsc->p[1].y, dsc->p[2].y);
tri_area.x2 = (int32_t)LV_MAX3(dsc->p[0].x, dsc->p[1].x, dsc->p[2].x);
tri_area.y2 = (int32_t)LV_MAX3(dsc->p[0].y, dsc->p[1].y, dsc->p[2].y);
bool is_common;
lv_area_t draw_area;
is_common = lv_area_intersect(&draw_area, &tri_area, &t->clip_area);
if(!is_common) return;
lv_point_t p[3];
/*If there is a vertical side use it as p[0] and p[1]*/
if(dsc->p[0].x == dsc->p[1].x) {
p[0] = lv_point_from_precise(&dsc->p[0]);
p[1] = lv_point_from_precise(&dsc->p[1]);
p[2] = lv_point_from_precise(&dsc->p[2]);
}
else if(dsc->p[0].x == dsc->p[2].x) {
p[0] = lv_point_from_precise(&dsc->p[0]);
p[1] = lv_point_from_precise(&dsc->p[2]);
p[2] = lv_point_from_precise(&dsc->p[1]);
}
else if(dsc->p[1].x == dsc->p[2].x) {
p[0] = lv_point_from_precise(&dsc->p[1]);
p[1] = lv_point_from_precise(&dsc->p[2]);
p[2] = lv_point_from_precise(&dsc->p[0]);
}
else {
p[0] = lv_point_from_precise(&dsc->p[0]);
p[1] = lv_point_from_precise(&dsc->p[1]);
p[2] = lv_point_from_precise(&dsc->p[2]);
/*Set the smallest y as p[0]*/
if(p[0].y > p[1].y) lv_point_swap(&p[0], &p[1]);
if(p[0].y > p[2].y) lv_point_swap(&p[0], &p[2]);
/*Set the greatest y as p[1]*/
if(p[1].y < p[2].y) lv_point_swap(&p[1], &p[2]);
}
/*Be sure p[0] is on the top*/
if(p[0].y > p[1].y) lv_point_swap(&p[0], &p[1]);
lv_eve_save_context();
lv_eve_scissor(t->clip_area.x1, t->clip_area.y1, t->clip_area.x2, t->clip_area.y2);
lv_eve_color(dsc->color);
lv_eve_color_opa(dsc->opa);
lv_eve_color_mask(0, 0, 0, 0);
lv_eve_stencil_op(EVE_KEEP, EVE_INVERT);
lv_eve_stencil_func(EVE_ALWAYS, 255, 255);
lv_eve_primitive(LV_EVE_PRIMITIVE_EDGE_STRIP_A);
lv_eve_vertex_2f(p[0].x, p[0].y);
lv_eve_vertex_2f(p[1].x, p[1].y);
lv_eve_vertex_2f(p[2].x, p[2].y);
lv_eve_color_mask(1, 1, 1, 1);
lv_eve_stencil_func(EVE_EQUAL, 255, 255) ;
lv_eve_vertex_2f(0, 0);
lv_eve_vertex_2f(1022, 0);
lv_eve_restore_context();
}
/**********************
* STATIC FUNCTIONS
**********************/
#endif /*LV_USE_DRAW_EVE*/

View File

@@ -0,0 +1,265 @@
/**
* @file lv_eve.c
*
*/
/* Created on: 8 jun 2023
* Author: juanj
*
* Modified by LVGL
*/
/*********************
* INCLUDES
*********************/
#include "lv_draw_eve.h"
#if LV_USE_DRAW_EVE
#include "lv_eve.h"
#include "../../libs/FT800-FT813/EVE_commands.h"
/*********************
* DEFINES
*********************/
/**********************
* TYPEDEFS
**********************/
/**********************
* STATIC PROTOTYPES
**********************/
/**********************
* STATIC VARIABLES
**********************/
static uint16_t scissor_x1 = 0;
static uint16_t scissor_y1 = 0;
static uint16_t scissor_x2 = 0;
static uint16_t scissor_y2 = 0;
static lv_eve_drawing_context_t ct = {
.primitive = LV_EVE_PRIMITIVE_ZERO_VALUE,
.color = {0xff, 0xff, 0xff},
.opa = 255,
.line_width = 1, /* for format(0) */
.point_size = 1,
.color_mask = {1, 1, 1, 1},
.stencil_func = {EVE_ALWAYS, 0, 255},
.stencil_op = {EVE_KEEP, EVE_KEEP},
.blend_func = {EVE_SRC_ALPHA, EVE_ONE_MINUS_SRC_ALPHA},
.scx = 0,
.scy = 0,
};
static lv_eve_drawing_context_t ct_temp;
static lv_eve_drawing_state_t st;
/**********************
* MACROS
**********************/
/**********************
* GLOBAL FUNCTIONS
**********************/
void lv_eve_save_context(void)
{
EVE_cmd_dl_burst(DL_SAVE_CONTEXT);
ct_temp = ct;
}
void lv_eve_restore_context(void)
{
EVE_cmd_dl_burst(DL_RESTORE_CONTEXT);
ct = ct_temp;
}
void lv_eve_primitive(uint8_t context)
{
if(context != ct.primitive && context != LV_EVE_PRIMITIVE_ZERO_VALUE) {
EVE_cmd_dl_burst(DL_BEGIN | context);
ct.primitive = context;
}
}
void lv_eve_scissor(uint16_t x1, uint16_t y1, uint16_t x2, uint16_t y2)
{
if(x1 != scissor_x1 || y1 != scissor_y1) {
int16_t adjusted_x1 = x1 > 0 ? x1 - 1 : 0;
int16_t adjusted_y1 = y1 > 0 ? y1 - 1 : 0;
EVE_cmd_dl_burst(SCISSOR_XY(adjusted_x1, adjusted_y1));
scissor_x1 = x1;
scissor_y1 = y1;
}
if(x2 != scissor_x2 || y2 != scissor_y2) {
uint16_t w = x2 - x1 + 3;
uint16_t h = y2 - y1 + 3;
EVE_cmd_dl_burst(SCISSOR_SIZE(w, h));
scissor_x2 = x2;
scissor_y2 = y2;
}
}
void lv_eve_color(lv_color_t color)
{
if((ct.color.red != color.red) || (ct.color.green != color.green) || (ct.color.blue != color.blue)) {
EVE_cmd_dl_burst(COLOR_RGB(color.red, color.green, color.blue));
ct.color = color;
}
}
void lv_eve_color_mask(uint8_t r, uint8_t g, uint8_t b, uint8_t a)
{
if((ct.color_mask[0] != r) ||
(ct.color_mask[1] != g) ||
(ct.color_mask[2] != b) ||
(ct.color_mask[3] != a)) {
EVE_cmd_dl_burst(COLOR_MASK(r, g, b, a));
ct.color_mask[0] = r;
ct.color_mask[1] = g;
ct.color_mask[2] = b;
ct.color_mask[3] = a;
}
}
void lv_eve_stencil_func(uint8_t func, uint8_t ref, uint8_t mask)
{
if(func != ct.stencil_func[0] || ref != ct.stencil_func[1] || mask != ct.stencil_func[2]) {
EVE_cmd_dl_burst(STENCIL_FUNC(func, ref, mask));
ct.stencil_func[0] = func;
ct.stencil_func[1] = ref;
ct.stencil_func[2] = mask;
}
}
void lv_eve_stencil_op(uint8_t sfail, uint8_t spass)
{
if(sfail != ct.stencil_op[0] || spass != ct.stencil_op[1]) {
EVE_cmd_dl_burst(STENCIL_OP(sfail, spass));
ct.stencil_op[0] = sfail;
ct.stencil_op[1] = spass;
}
}
void lv_eve_blend_func(uint8_t src, uint8_t dst)
{
if(src != ct.blend_func[0] || dst != ct.blend_func[1]) {
EVE_cmd_dl_burst(BLEND_FUNC(src, dst));
ct.blend_func[0] = src;
ct.blend_func[1] = dst;
}
}
void lv_eve_color_opa(lv_opa_t opa)
{
if(opa != ct.opa) {
EVE_cmd_dl_burst(COLOR_A(opa));
ct.opa = opa;
}
}
void lv_eve_line_width(int32_t width)
{
if(width != ct.line_width) {
EVE_cmd_dl_burst(LINE_WIDTH(width));
ct.line_width = width;
}
}
void lv_eve_point_size(uint16_t radius)
{
if(radius != ct.point_size) {
EVE_cmd_dl_burst(POINT_SIZE(radius * 16));
ct.point_size = radius;
}
}
void lv_eve_vertex_2f(int16_t x, int16_t y)
{
EVE_cmd_dl_burst(VERTEX2F(x, y));
}
void lv_eve_draw_circle_simple(int16_t coord_x1, int16_t coord_y1, uint16_t radius_t)
{
lv_eve_primitive(LV_EVE_PRIMITIVE_POINTS);
lv_eve_point_size(radius_t);
lv_eve_vertex_2f(coord_x1, coord_y1);
}
void lv_eve_draw_rect_simple(int16_t coord_x1, int16_t coord_y1, int16_t coord_x2, int16_t coord_y2, uint16_t radius)
{
lv_eve_primitive(LV_EVE_PRIMITIVE_RECTS);
if(radius > 1) {
lv_eve_line_width(radius * 16);
}
lv_eve_vertex_2f(coord_x1 + radius, coord_y1 + radius);
lv_eve_vertex_2f(coord_x2 - radius, coord_y2 - radius);
}
void lv_eve_mask_round(int16_t coord_x1, int16_t coord_y1, int16_t coord_x2, int16_t coord_y2, int16_t radius)
{
lv_eve_color_mask(0, 0, 0, 1);
EVE_cmd_dl_burst(CLEAR(1, 1, 1));
lv_eve_draw_rect_simple(coord_x1, coord_y1, coord_x2, coord_y2, radius);
lv_eve_color_mask(1, 1, 1, 0);
lv_eve_blend_func(EVE_DST_ALPHA, EVE_ONE_MINUS_DST_ALPHA);
}
void lv_eve_bitmap_source(uint32_t addr)
{
uint32_t bitmap_source = BITMAP_SOURCE(addr);
if(st.bitmap_source != bitmap_source) {
EVE_cmd_dl_burst(bitmap_source);
st.bitmap_source = bitmap_source;
}
}
void lv_eve_bitmap_size(uint8_t filter, uint8_t wrapx, uint8_t wrapy, uint16_t width, uint16_t height)
{
uint32_t bitmap_size = BITMAP_SIZE(filter, wrapx, wrapy, width, height);
if(st.bitmap_size != bitmap_size) {
EVE_cmd_dl_burst(bitmap_size);
st.bitmap_size = bitmap_size;
}
/* set the high bits too, of the width and height */
uint32_t bitmap_size_h = BITMAP_SIZE_H(width, height);
if(st.bitmap_size_h != bitmap_size_h) {
EVE_cmd_dl_burst(bitmap_size_h);
st.bitmap_size_h = bitmap_size_h;
}
}
void lv_eve_bitmap_layout(uint8_t format, uint16_t linestride, uint16_t height)
{
uint32_t bitmap_layout = BITMAP_LAYOUT(format, linestride, height);
if(st.bitmap_layout != bitmap_layout) {
EVE_cmd_dl_burst(bitmap_layout);
st.bitmap_layout = bitmap_layout;
}
/* set the high bits too, of the linestride and height */
uint32_t bitmap_layout_h = BITMAP_LAYOUT_H(linestride, height);
if(st.bitmap_layout_h != bitmap_layout_h) {
EVE_cmd_dl_burst(bitmap_layout_h);
st.bitmap_layout_h = bitmap_layout_h;
}
}
/**********************
* STATIC FUNCTIONS
**********************/
#endif /*LV_USE_DRAW_EVE*/

View File

@@ -0,0 +1,151 @@
/**
* @file lv_eve.h
*
*/
/* Created on: 8 jun 2023
* Author: juanj
*
* Modified by LVGL
*/
#ifndef LV_EVE_H
#define LV_EVE_H
#ifdef __cplusplus
extern "C" {
#endif
/*********************
* INCLUDES
*********************/
#include "lv_draw_eve.h"
#if LV_USE_DRAW_EVE
#include "../../misc/lv_types.h"
#include "../../misc/lv_color.h"
#include "../../libs/FT800-FT813/EVE.h"
/*********************
* DEFINES
*********************/
/**********************
* TYPEDEFS
**********************/
typedef enum {
LV_EVE_PRIMITIVE_ZERO_VALUE,
LV_EVE_PRIMITIVE_BITMAPS = 1UL, /* Bitmap drawing primitive */
LV_EVE_PRIMITIVE_POINTS = 2UL, /* Point drawing primitive */
LV_EVE_PRIMITIVE_LINES = 3UL, /* Line drawing primitive */
LV_EVE_PRIMITIVE_LINE_STRIP = 4UL, /* Line strip drawing primitive */
LV_EVE_PRIMITIVE_EDGE_STRIP_R = 5UL, /* Edge strip right side drawing primitive */
LV_EVE_PRIMITIVE_EDGE_STRIP_L = 6UL, /* Edge strip left side drawing primitive */
LV_EVE_PRIMITIVE_EDGE_STRIP_A = 7UL, /* Edge strip above drawing primitive */
LV_EVE_PRIMITIVE_EDGE_STRIP_B = 8UL, /* Edge strip below side drawing primitive */
LV_EVE_PRIMITIVE_RECTS = 9UL, /* Rectangle drawing primitive */
} lv_eve_primitive_t;
typedef struct {
lv_eve_primitive_t primitive;
lv_color_t color;
lv_opa_t opa;
int32_t line_width;
uint16_t point_size;
uint8_t color_mask[4];
uint8_t stencil_func[3];
uint8_t stencil_op[2];
uint8_t blend_func[2];
uint16_t scx;
uint16_t scy;
} lv_eve_drawing_context_t;
/* drawing context that is not saved and restored
* by SAVE_CONTEXT and RESTORE_CONTEXT
*/
typedef struct {
uint32_t bitmap_source;
uint32_t bitmap_size;
uint32_t bitmap_size_h;
uint32_t bitmap_layout;
uint32_t bitmap_layout_h;
} lv_eve_drawing_state_t;
/**********************
* GLOBAL PROTOTYPES
**********************/
void lv_eve_save_context(void);
void lv_eve_restore_context(void);
void lv_eve_scissor(uint16_t x1, uint16_t y1, uint16_t x2, uint16_t y2);
void lv_eve_primitive(uint8_t context);
void lv_eve_color(lv_color_t color);
void lv_eve_color_opa(lv_opa_t opa);
void lv_eve_line_width(int32_t width);
void lv_eve_point_size(uint16_t radius);
void lv_eve_vertex_2f(int16_t x, int16_t y);
void lv_eve_color_mask(uint8_t r, uint8_t g, uint8_t b, uint8_t a);
void lv_eve_stencil_func(uint8_t func, uint8_t ref, uint8_t mask);
void lv_eve_stencil_op(uint8_t sfail, uint8_t spass);
void lv_eve_blend_func(uint8_t src, uint8_t dst);
void lv_eve_draw_circle_simple(int16_t coord_x1, int16_t coord_y1, uint16_t radius_t);
void lv_eve_draw_rect_simple(int16_t coord_x1, int16_t coord_y1, int16_t coord_x2, int16_t coord_y2,
uint16_t radius);
void lv_eve_mask_round(int16_t coord_x1, int16_t coord_y1, int16_t coord_x2, int16_t coord_y2, int16_t radius);
/**
* Set the bitmap source to `addr`. SPI transmission will occur unless it is already set to this value.
* The bitmap source is not part of the saved and restored context.
* @param addr the remote EVE memory address to set as the bitmap source
*/
void lv_eve_bitmap_source(uint32_t addr);
/**
* Set the bitmap size and sampling parameters. SPI transmission will occur unless the currently set parameters are already these.
* The bitmap size is not part of the saved and restored context.
* @param filter the sampling method. Either EVE_NEAREST or EVE_BILINEAR
* @param wrapx the out of bounds sampling behavior in the X direction. Either EVE_BORDER or EVE_REPEAT
* @param wrapy the out of bounds sampling behavior in the Y direction. Either EVE_BORDER or EVE_REPEAT
* @param width the width of the bitmap in pixels
* @param height the height of the bitmap in pixels
*/
void lv_eve_bitmap_size(uint8_t filter, uint8_t wrapx, uint8_t wrapy, uint16_t width, uint16_t height);
/**
* Set the bitmap format/layout parameters. SPI transmission will occur unless the currently set parameters are already these.
* The bitmap layout is not part of the saved and restored context.
* @param format an eve color format value like EVE_RGB565
* @param linestride the stride of the bitmap rows in bytes
* @param height the number of rows in the bitmap
*/
void lv_eve_bitmap_layout(uint8_t format, uint16_t linestride, uint16_t height);
/**********************
* EXTERN VARIABLES
**********************/
/**********************
* MACROS
**********************/
/**********************
* INLINE FUNCTIONS
**********************/
/**********************
* STATIC FUNCTIONS
**********************/
#endif /*LV_USE_DRAW_EVE*/
#ifdef __cplusplus
} /*extern "C"*/
#endif
#endif /* LV_EVE_H */

View File

@@ -0,0 +1,762 @@
/**
* @file lv_draw.c
*
*/
/**
* Modified by NXP in 2024
*/
/*********************
* INCLUDES
*********************/
#include "../misc/lv_area_private.h"
#include "../misc/lv_assert.h"
#include "../misc/lv_event_private.h"
#include "lv_draw_private.h"
#include "lv_draw_mask.h"
#include "lv_draw_vector_private.h"
#include "lv_draw_3d.h"
#include "sw/lv_draw_sw.h"
#include "../display/lv_display_private.h"
#include "../core/lv_global.h"
#include "../core/lv_refr_private.h"
#include "../stdlib/lv_string.h"
/*********************
* DEFINES
*********************/
#define _draw_info LV_GLOBAL_DEFAULT()->draw_info
/**********************
* TYPEDEFS
**********************/
/**********************
* STATIC PROTOTYPES
**********************/
static bool is_independent(lv_layer_t * layer, lv_draw_task_t * t_check, uint8_t draw_unit_id);
static void cleanup_task(lv_draw_task_t * t, lv_display_t * disp);
static inline size_t get_draw_dsc_size(lv_draw_task_type_t type);
static lv_draw_task_t * get_first_available_task(lv_layer_t * layer);
#if LV_LOG_LEVEL <= LV_LOG_LEVEL_INFO
static inline uint32_t get_layer_size_kb(uint32_t size_byte)
{
return (size_byte + 1023) >> 10;
}
#endif
/**********************
* STATIC VARIABLES
**********************/
/**********************
* MACROS
**********************/
/**********************
* GLOBAL FUNCTIONS
**********************/
void lv_draw_init(void)
{
#if LV_USE_OS
lv_thread_sync_init(&_draw_info.sync);
#endif
}
void lv_draw_deinit(void)
{
#if LV_USE_OS
lv_thread_sync_delete(&_draw_info.sync);
#endif
lv_draw_unit_t * u = _draw_info.unit_head;
while(u) {
lv_draw_unit_t * cur_unit = u;
u = u->next;
if(cur_unit->delete_cb) cur_unit->delete_cb(cur_unit);
lv_free(cur_unit);
}
_draw_info.unit_head = NULL;
}
void * lv_draw_create_unit(size_t size)
{
lv_draw_unit_t * new_unit = lv_malloc_zeroed(size);
LV_ASSERT_MALLOC(new_unit);
new_unit->next = _draw_info.unit_head;
_draw_info.unit_head = new_unit;
_draw_info.unit_cnt++;
new_unit->idx = _draw_info.unit_cnt;
return new_unit;
}
lv_draw_task_t * lv_draw_add_task(lv_layer_t * layer, const lv_area_t * coords, lv_draw_task_type_t type)
{
LV_PROFILER_DRAW_BEGIN;
size_t dsc_size = get_draw_dsc_size(type);
LV_ASSERT_FORMAT_MSG(dsc_size > 0, "Draw task size is 0 for type %d", type);
lv_draw_task_t * new_task = lv_malloc_zeroed(LV_ALIGN_UP(sizeof(lv_draw_task_t), 8) + dsc_size);
LV_ASSERT_MALLOC(new_task);
new_task->area = *coords;
new_task->_real_area = *coords;
new_task->target_layer = layer;
new_task->clip_area = layer->_clip_area;
#if LV_DRAW_TRANSFORM_USE_MATRIX
new_task->matrix = layer->matrix;
#endif
new_task->opa = layer->opa;
new_task->type = type;
new_task->draw_dsc = (uint8_t *)new_task + LV_ALIGN_UP(sizeof(lv_draw_task_t), 8);
new_task->state = LV_DRAW_TASK_STATE_WAITING;
/*Find the tail*/
if(layer->draw_task_head == NULL) {
layer->draw_task_head = new_task;
}
else {
lv_draw_task_t * tail = layer->draw_task_head;
while(tail->next) tail = tail->next;
tail->next = new_task;
}
LV_PROFILER_DRAW_END;
return new_task;
}
void lv_draw_finalize_task_creation(lv_layer_t * layer, lv_draw_task_t * t)
{
LV_PROFILER_DRAW_BEGIN;
lv_draw_dsc_base_t * base_dsc = t->draw_dsc;
base_dsc->layer = layer;
lv_draw_global_info_t * info = &_draw_info;
/*Send LV_EVENT_DRAW_TASK_ADDED and dispatch only on the "main" draw_task
*and not on the draw tasks added in the event.
*Sending LV_EVENT_DRAW_TASK_ADDED events might cause recursive event sends and besides
*dispatching might remove the "main" draw task while it's still being used in the event*/
if(info->task_running == false) {
if(base_dsc->obj && lv_obj_has_flag(base_dsc->obj, LV_OBJ_FLAG_SEND_DRAW_TASK_EVENTS)) {
info->task_running = true;
lv_obj_send_event(base_dsc->obj, LV_EVENT_DRAW_TASK_ADDED, t);
info->task_running = false;
}
/*Let the draw units set their preference score*/
t->preference_score = 100;
t->preferred_draw_unit_id = 0;
lv_draw_unit_t * u = info->unit_head;
while(u) {
if(u->evaluate_cb) {
LV_PROFILER_DRAW_BEGIN_TAG("evaluate_cb");
LV_PROFILER_DRAW_BEGIN_TAG(u->name);
u->evaluate_cb(u, t);
LV_PROFILER_DRAW_END_TAG(u->name);
LV_PROFILER_DRAW_END_TAG("evaluate_cb");
}
u = u->next;
}
if(t->preferred_draw_unit_id == LV_DRAW_UNIT_NONE) {
LV_LOG_WARN("the draw task was not taken by any units");
t->state = LV_DRAW_TASK_STATE_FINISHED;
}
else {
lv_draw_dispatch();
}
}
else {
/*Let the draw units set their preference score*/
t->preference_score = 100;
t->preferred_draw_unit_id = 0;
lv_draw_unit_t * u = info->unit_head;
while(u) {
if(u->evaluate_cb) {
LV_PROFILER_DRAW_BEGIN_TAG("evaluate_cb");
LV_PROFILER_DRAW_BEGIN_TAG(u->name);
u->evaluate_cb(u, t);
LV_PROFILER_DRAW_END_TAG(u->name);
LV_PROFILER_DRAW_END_TAG("evaluate_cb");
}
u = u->next;
}
}
LV_PROFILER_DRAW_END;
}
void lv_draw_wait_for_finish(void)
{
#if LV_USE_OS
LV_PROFILER_DRAW_BEGIN;
lv_draw_unit_t * u = _draw_info.unit_head;
while(u) {
if(u->wait_for_finish_cb) {
LV_PROFILER_DRAW_BEGIN_TAG("wait_for_finish_cb");
LV_PROFILER_DRAW_BEGIN_TAG(u->name);
u->wait_for_finish_cb(u);
LV_PROFILER_DRAW_END_TAG(u->name);
LV_PROFILER_DRAW_END_TAG("wait_for_finish_cb");
}
u = u->next;
}
LV_PROFILER_DRAW_END;
#endif
}
void lv_draw_dispatch(void)
{
LV_PROFILER_DRAW_BEGIN;
bool task_dispatched = false;
lv_display_t * disp = lv_refr_get_disp_refreshing();
if(disp != NULL) {
lv_layer_t * layer = disp->layer_head;
while(layer) {
if(lv_draw_dispatch_layer(disp, layer))
task_dispatched = true;
layer = layer->next;
}
}
if(!task_dispatched) {
lv_draw_wait_for_finish();
lv_draw_dispatch_request();
}
LV_PROFILER_DRAW_END;
}
bool lv_draw_dispatch_layer(lv_display_t * disp, lv_layer_t * layer)
{
LV_PROFILER_DRAW_BEGIN;
/*Remove the finished tasks first*/
lv_draw_task_t * t_prev = NULL;
lv_draw_task_t * t = layer->draw_task_head;
lv_draw_task_t * t_next;
bool remove_task = false;
while(t) {
t_next = t->next;
if(t->state == LV_DRAW_TASK_STATE_FINISHED) {
cleanup_task(t, disp);
remove_task = true;
if(t_prev != NULL)
t_prev->next = t_next;
else
layer->draw_task_head = t_next;
}
else {
t_prev = t;
}
t = t_next;
}
bool task_dispatched = false;
/*This layer is ready, enable blending its buffer*/
if(layer->parent && layer->all_tasks_added && layer->draw_task_head == NULL) {
/*Find a draw task with TYPE_LAYER in the layer where the src is this layer*/
lv_draw_task_t * t_src = layer->parent->draw_task_head;
while(t_src) {
if(t_src->type == LV_DRAW_TASK_TYPE_LAYER && t_src->state == LV_DRAW_TASK_STATE_BLOCKED) {
lv_draw_image_dsc_t * draw_dsc = t_src->draw_dsc;
if(draw_dsc->src == layer) {
t_src->state = LV_DRAW_TASK_STATE_WAITING;
lv_draw_dispatch_request();
break;
}
}
t_src = t_src->next;
}
}
/*Assign draw tasks to the draw_units*/
else if(remove_task || layer->draw_task_head) {
/*Find a draw unit which is not busy and can take at least one task*/
/*Let all draw units to pick draw tasks*/
lv_draw_unit_t * u = _draw_info.unit_head;
while(u) {
LV_PROFILER_DRAW_BEGIN_TAG("dispatch_cb");
LV_PROFILER_DRAW_BEGIN_TAG(u->name);
int32_t taken_cnt = u->dispatch_cb(u, layer);
LV_PROFILER_DRAW_END_TAG(u->name);
LV_PROFILER_DRAW_END_TAG("dispatch_cb");
if(taken_cnt != LV_DRAW_UNIT_IDLE) task_dispatched = true;
u = u->next;
}
}
LV_PROFILER_DRAW_END;
return task_dispatched;
}
void lv_draw_dispatch_wait_for_request(void)
{
LV_PROFILER_DRAW_BEGIN;
#if LV_USE_OS
lv_thread_sync_wait(&_draw_info.sync);
#else
while(!_draw_info.dispatch_req);
_draw_info.dispatch_req = 0;
#endif
LV_PROFILER_DRAW_END;
}
void lv_draw_dispatch_request(void)
{
LV_PROFILER_DRAW_BEGIN;
#if LV_USE_OS
lv_thread_sync_signal(&_draw_info.sync);
#else
_draw_info.dispatch_req = 1;
#endif
LV_PROFILER_DRAW_END;
}
uint32_t lv_draw_get_unit_count(void)
{
return _draw_info.unit_cnt;
}
lv_draw_task_t * lv_draw_get_available_task(lv_layer_t * layer, lv_draw_task_t * t_prev, uint8_t draw_unit_id)
{
if(_draw_info.unit_cnt == 1) {
return get_first_available_task(layer);
}
else {
return lv_draw_get_next_available_task(layer, t_prev, draw_unit_id);
}
}
lv_draw_task_t * lv_draw_get_next_available_task(lv_layer_t * layer, lv_draw_task_t * t_prev, uint8_t draw_unit_id)
{
LV_PROFILER_DRAW_BEGIN;
/*If the first task is screen sized, there cannot be independent areas*/
if(layer->draw_task_head) {
int32_t hor_res = lv_display_get_horizontal_resolution(lv_refr_get_disp_refreshing());
int32_t ver_res = lv_display_get_vertical_resolution(lv_refr_get_disp_refreshing());
lv_draw_task_t * t = layer->draw_task_head;
if(t->state != LV_DRAW_TASK_STATE_WAITING &&
t->area.x1 <= 0 && t->area.x2 >= hor_res - 1 &&
t->area.y1 <= 0 && t->area.y2 >= ver_res - 1) {
LV_PROFILER_DRAW_END;
return NULL;
}
}
lv_draw_task_t * t = t_prev ? t_prev->next : layer->draw_task_head;
while(t) {
/*Find a draw task for this draw unit which is waiting and independent?*/
if((t->preferred_draw_unit_id == draw_unit_id || t->preferred_draw_unit_id == LV_DRAW_UNIT_NONE) &&
t->state == LV_DRAW_TASK_STATE_WAITING &&
is_independent(layer, t, draw_unit_id)) {
LV_PROFILER_DRAW_END;
return t;
}
t = t->next;
}
LV_PROFILER_DRAW_END;
return NULL;
}
uint32_t lv_draw_get_dependent_count(lv_draw_task_t * t_check)
{
if(t_check == NULL) return 0;
if(t_check->next == NULL) return 0;
LV_PROFILER_DRAW_BEGIN;
uint32_t cnt = 0;
lv_draw_task_t * t = t_check->next;
while(t) {
if((t->state == LV_DRAW_TASK_STATE_WAITING || t->state == LV_DRAW_TASK_STATE_BLOCKED) &&
lv_area_is_on(&t_check->area, &t->area)) {
cnt++;
}
t = t->next;
}
LV_PROFILER_DRAW_END;
return cnt;
}
void lv_draw_unit_send_event(const char * name, lv_event_code_t code, void * param)
{
LV_PROFILER_DRAW_BEGIN;
lv_event_t event = { 0 };
event.code = code;
event.param = param;
lv_draw_unit_t * u = _draw_info.unit_head;
while(u) {
if(u->event_cb && (!name || lv_strcmp(name, u->name) == 0)) {
event.current_target = event.original_target = u;
LV_PROFILER_DRAW_BEGIN_TAG("event_cb");
LV_PROFILER_DRAW_BEGIN_TAG(u->name);
u->event_cb(&event);
LV_PROFILER_DRAW_END_TAG(u->name);
LV_PROFILER_DRAW_END_TAG("event_cb");
}
u = u->next;
}
LV_PROFILER_DRAW_END;
}
void lv_layer_init(lv_layer_t * layer)
{
LV_ASSERT_NULL(layer);
lv_memzero(layer, sizeof(lv_layer_t));
lv_layer_reset(layer);
}
void lv_layer_reset(lv_layer_t * layer)
{
LV_ASSERT_NULL(layer);
#if LV_DRAW_TRANSFORM_USE_MATRIX
lv_matrix_identity(&layer->matrix);
#endif
layer->opa = LV_OPA_COVER;
layer->recolor = lv_color32_make(0, 0, 0, 0);
layer->all_tasks_added = false;
}
lv_layer_t * lv_draw_layer_create(lv_layer_t * parent_layer, lv_color_format_t color_format, const lv_area_t * area)
{
LV_PROFILER_DRAW_BEGIN;
lv_layer_t * new_layer = lv_malloc_zeroed(sizeof(lv_layer_t));
LV_ASSERT_MALLOC(new_layer);
if(new_layer == NULL) {
LV_PROFILER_DRAW_END;
return NULL;
}
lv_draw_layer_init(new_layer, parent_layer, color_format, area);
/*Inherits transparency from parent*/
if(parent_layer) {
new_layer->opa = parent_layer->opa;
new_layer->recolor = parent_layer->recolor;
}
LV_PROFILER_DRAW_END;
return new_layer;
}
void lv_draw_layer_init(lv_layer_t * layer, lv_layer_t * parent_layer, lv_color_format_t color_format,
const lv_area_t * area)
{
LV_PROFILER_DRAW_BEGIN;
lv_layer_init(layer);
lv_display_t * disp = lv_refr_get_disp_refreshing();
layer->parent = parent_layer;
layer->_clip_area = *area;
layer->buf_area = *area;
layer->phy_clip_area = *area;
layer->color_format = color_format;
if(disp->layer_init) disp->layer_init(disp, layer);
lv_draw_unit_send_event(NULL, LV_EVENT_CHILD_CREATED, layer);
if(disp->layer_head) {
lv_layer_t * tail = disp->layer_head;
while(tail->next) tail = tail->next;
tail->next = layer;
}
else {
disp->layer_head = layer;
}
LV_PROFILER_DRAW_END;
}
void * lv_draw_layer_alloc_buf(lv_layer_t * layer)
{
LV_PROFILER_DRAW_BEGIN;
/*If the buffer of the layer is already allocated return it*/
if(layer->draw_buf != NULL) {
LV_PROFILER_DRAW_END;
return layer->draw_buf->data;
}
/*If the buffer of the layer is not allocated yet, allocate it now*/
int32_t w = lv_area_get_width(&layer->buf_area);
int32_t h = lv_area_get_height(&layer->buf_area);
uint32_t layer_size_byte = h * lv_draw_buf_width_to_stride(w, layer->color_format);
#if LV_DRAW_LAYER_MAX_MEMORY > 0
/* Do not allocate the layer if the sum of allocated layer sizes
* will exceed `LV_DRAW_LAYER_MAX_MEMORY` */
if((_draw_info.used_memory_for_layers + layer_size_byte) > LV_DRAW_LAYER_MAX_MEMORY) {
LV_LOG_WARN("LV_DRAW_LAYER_MAX_MEMORY was reached when allocating the layer.");
return NULL;
}
#endif
layer->draw_buf = lv_draw_buf_create(w, h, layer->color_format, 0);
if(layer->draw_buf == NULL) {
LV_LOG_WARN("Allocating layer buffer failed. Try later");
LV_PROFILER_DRAW_END;
return NULL;
}
_draw_info.used_memory_for_layers += layer_size_byte;
LV_LOG_INFO("Layer memory used: %" LV_PRIu32 " kB", get_layer_size_kb(_draw_info.used_memory_for_layers));
if(lv_color_format_has_alpha(layer->color_format)) {
lv_draw_buf_clear(layer->draw_buf, NULL);
}
LV_PROFILER_DRAW_END;
return layer->draw_buf->data;
}
void * lv_draw_layer_go_to_xy(lv_layer_t * layer, int32_t x, int32_t y)
{
return lv_draw_buf_goto_xy(layer->draw_buf, x, y);
}
lv_draw_task_type_t lv_draw_task_get_type(const lv_draw_task_t * t)
{
return t->type;
}
void * lv_draw_task_get_draw_dsc(const lv_draw_task_t * t)
{
return t->draw_dsc;
}
void lv_draw_task_get_area(const lv_draw_task_t * t, lv_area_t * area)
{
*area = t->area;
}
lv_layer_t * lv_draw_layer_create_drop_shadow(lv_layer_t * parent_layer, const lv_draw_dsc_base_t * base,
const lv_area_t * area)
{
lv_area_t drop_shadow_area = *area;
int32_t blur_radius = base->drop_shadow_blur_radius;
/* x2 to have some extra space for cleaner blurring */
lv_area_increase(&drop_shadow_area, blur_radius * 2, blur_radius * 2);
lv_layer_t * ds_layer = lv_draw_layer_create(parent_layer, LV_COLOR_FORMAT_A8, &drop_shadow_area);
if(ds_layer == NULL) {
LV_LOG_WARN("Failed to create a layer for the drop shadow");
}
return ds_layer;
}
void lv_draw_layer_finish_drop_shadow(lv_layer_t * drop_shadow_layer, const lv_draw_dsc_base_t * base)
{
lv_area_t drop_shadow_area = drop_shadow_layer->buf_area;
lv_draw_blur_dsc_t blur_dsc;
lv_draw_blur_dsc_init(&blur_dsc);
blur_dsc.blur_radius = base->drop_shadow_blur_radius;
blur_dsc.quality = base->drop_shadow_quality;
lv_draw_blur(drop_shadow_layer, &blur_dsc, &drop_shadow_layer->buf_area);
lv_area_move(&drop_shadow_area, base->drop_shadow_ofs_x, base->drop_shadow_ofs_y);
lv_draw_image_dsc_t layer_draw_dsc;
lv_draw_image_dsc_init(&layer_draw_dsc);
layer_draw_dsc.src = drop_shadow_layer;
layer_draw_dsc.recolor = base->drop_shadow_color;
layer_draw_dsc.opa = base->drop_shadow_opa;
lv_draw_layer(drop_shadow_layer->parent, &layer_draw_dsc, &drop_shadow_area);
}
/**********************
* STATIC FUNCTIONS
**********************/
/**
* Check if there are older draw task overlapping the area of `t_check`
* @param layer the draw ctx to search in
* @param t_check check this task if it overlaps with the older ones
* @param draw_unit_id draw unit ID for which the independence check is called
* @return true: `t_check` is not overlapping with older tasks so it's independent
*/
static bool is_independent(lv_layer_t * layer, lv_draw_task_t * t_check, uint8_t draw_unit_id)
{
LV_PROFILER_DRAW_BEGIN;
lv_draw_task_t * t = layer->draw_task_head;
/*If t_check is outside of the older tasks then it's independent*/
while(t && t != t_check) {
/*It's independent of finished draw tasks, and queued draw tasks of the same draw unit,
*so no need to check it*/
if(t->state == LV_DRAW_TASK_STATE_FINISHED ||
(t->state == LV_DRAW_TASK_STATE_QUEUED && t->preferred_draw_unit_id == draw_unit_id)) {
t = t->next;
continue;
}
lv_area_t a;
if(lv_area_intersect(&a, &t->_real_area, &t_check->_real_area)) {
LV_PROFILER_DRAW_END;
return false;
}
t = t->next;
}
LV_PROFILER_DRAW_END;
return true;
}
/**
* Get the size of the draw descriptor of a draw task
* @param type type of the draw task
* @return size of the draw descriptor in bytes
*/
static inline size_t get_draw_dsc_size(lv_draw_task_type_t type)
{
switch(type) {
case LV_DRAW_TASK_TYPE_NONE:
return 0;
case LV_DRAW_TASK_TYPE_FILL:
return sizeof(lv_draw_fill_dsc_t);
case LV_DRAW_TASK_TYPE_BORDER:
return sizeof(lv_draw_border_dsc_t);
case LV_DRAW_TASK_TYPE_BOX_SHADOW:
return sizeof(lv_draw_box_shadow_dsc_t);
case LV_DRAW_TASK_TYPE_LETTER:
return sizeof(lv_draw_letter_dsc_t);
case LV_DRAW_TASK_TYPE_LABEL:
return sizeof(lv_draw_label_dsc_t);
case LV_DRAW_TASK_TYPE_IMAGE:
return sizeof(lv_draw_image_dsc_t);
case LV_DRAW_TASK_TYPE_LAYER:
return sizeof(lv_draw_image_dsc_t);
case LV_DRAW_TASK_TYPE_LINE:
return sizeof(lv_draw_line_dsc_t);
case LV_DRAW_TASK_TYPE_ARC:
return sizeof(lv_draw_arc_dsc_t);
case LV_DRAW_TASK_TYPE_TRIANGLE:
return sizeof(lv_draw_triangle_dsc_t);
case LV_DRAW_TASK_TYPE_BLUR:
return sizeof(lv_draw_blur_dsc_t);
case LV_DRAW_TASK_TYPE_MASK_RECTANGLE:
return sizeof(lv_draw_mask_rect_dsc_t);
/* no struct match for LV_DRAW_TASK_TYPE_MASK_BITMAP, set it to zero now */
case LV_DRAW_TASK_TYPE_MASK_BITMAP:
return 0;
#if LV_USE_VECTOR_GRAPHIC
case LV_DRAW_TASK_TYPE_VECTOR:
return sizeof(lv_draw_vector_dsc_t);
#endif
#if LV_USE_3DTEXTURE
case LV_DRAW_TASK_TYPE_3D:
return sizeof(lv_draw_3d_dsc_t);
#endif
/* Note that default is not added here because when adding new draw task type,
* if forget to add case, the compiler will automatically report a warning.
*/
}
return 0;
}
/**
* Clean-up resources allocated by a finished task
* @param t pointer to a draw task
* @param disp pointer to a display on which the task was drawn
*/
static void cleanup_task(lv_draw_task_t * t, lv_display_t * disp)
{
LV_PROFILER_DRAW_BEGIN;
if(t->type == LV_DRAW_TASK_TYPE_LINE) {
lv_draw_line_dsc_t * draw_line_dsc = t->draw_dsc;
if(draw_line_dsc->points) {
lv_free(draw_line_dsc->points);
draw_line_dsc->points = NULL;
}
}
/*If it was layer drawing free the layer too*/
else if(t->type == LV_DRAW_TASK_TYPE_LAYER) {
lv_draw_image_dsc_t * draw_image_dsc = t->draw_dsc;
lv_layer_t * layer_drawn = (lv_layer_t *)draw_image_dsc->src;
if(layer_drawn->draw_buf) {
int32_t h = lv_area_get_height(&layer_drawn->buf_area);
uint32_t layer_size_byte = h * layer_drawn->draw_buf->header.stride;
if(_draw_info.used_memory_for_layers >= layer_size_byte) {
_draw_info.used_memory_for_layers -= layer_size_byte;
}
else {
_draw_info.used_memory_for_layers = 0;
LV_LOG_WARN("More layers were freed than allocated");
}
LV_LOG_INFO("Layer memory used: %" LV_PRIu32 " kB", get_layer_size_kb(_draw_info.used_memory_for_layers));
lv_draw_buf_destroy(layer_drawn->draw_buf);
layer_drawn->draw_buf = NULL;
}
/*Remove the layer from the display's*/
if(disp) {
lv_layer_t * l2 = disp->layer_head;
while(l2) {
if(l2->next == layer_drawn) {
l2->next = layer_drawn->next;
break;
}
l2 = l2->next;
}
lv_draw_unit_send_event(NULL, LV_EVENT_CHILD_DELETED, layer_drawn);
if(disp->layer_deinit) {
LV_PROFILER_DRAW_BEGIN_TAG("layer_deinit");
disp->layer_deinit(disp, layer_drawn);
LV_PROFILER_DRAW_END_TAG("layer_deinit");
}
lv_free(layer_drawn);
}
}
lv_draw_label_dsc_t * draw_label_dsc = lv_draw_task_get_label_dsc(t);
if(draw_label_dsc && draw_label_dsc->text_local) {
lv_free((void *)draw_label_dsc->text);
draw_label_dsc->text = NULL;
}
lv_free(t);
LV_PROFILER_DRAW_END;
}
static lv_draw_task_t * get_first_available_task(lv_layer_t * layer)
{
LV_PROFILER_DRAW_BEGIN;
/* If there is only 1 draw unit the task can be consumed linearly as
* they are added in the correct order. However, it can happen that
* there is a `LV_DRAW_TASK_TYPE_LAYER` which can be blended only when
* all its tasks are ready. As other areas might be on top of that
* layer-to-blend don't skip it. Instead stop there, so that the
* draw tasks of that layer can be consumed and can be finished.
* After that this layer-to-blenf will have `LV_DRAW_TASK_STATE_WAITING`
* so it can be blended normally.*/
lv_draw_task_t * t = layer->draw_task_head;
while(t) {
/*Not waiting to be rendered, leave this layer while the first task is ready (i.e. not blocked)*/
if(t->state != LV_DRAW_TASK_STATE_WAITING) {
t = NULL;
break;
}
/*Waiting to be rendered, use it*/
else {
break;
}
t = t->next;
}
LV_PROFILER_DRAW_END;
return t;
}

View File

@@ -0,0 +1,394 @@
/**
* @file lv_draw.h
*
*/
/**
* Modified by NXP in 2024
*/
#ifndef LV_DRAW_H
#define LV_DRAW_H
#ifdef __cplusplus
extern "C" {
#endif
/*********************
* INCLUDES
*********************/
#include "../lv_conf_internal.h"
#include "../misc/lv_types.h"
#include "../misc/lv_style.h"
#include "../misc/lv_text.h"
#include "../misc/lv_profiler.h"
#include "../misc/lv_matrix.h"
#include "../misc/lv_event.h"
#include "lv_image_decoder.h"
#include "lv_draw_buf.h"
/*********************
* DEFINES
*********************/
#define LV_DRAW_UNIT_NONE 0
#define LV_DRAW_UNIT_IDLE -1 /**< The draw unit is idle, new dispatching might be requested to try again */
#if LV_DRAW_TRANSFORM_USE_MATRIX
#if !LV_USE_MATRIX
#error "LV_DRAW_TRANSFORM_USE_MATRIX requires LV_USE_MATRIX = 1"
#endif
#endif
/**********************
* TYPEDEFS
**********************/
typedef enum {
LV_DRAW_TASK_TYPE_NONE = 0,
LV_DRAW_TASK_TYPE_FILL,
LV_DRAW_TASK_TYPE_BORDER,
LV_DRAW_TASK_TYPE_BOX_SHADOW,
LV_DRAW_TASK_TYPE_LETTER,
LV_DRAW_TASK_TYPE_LABEL,
LV_DRAW_TASK_TYPE_IMAGE,
LV_DRAW_TASK_TYPE_LAYER,
LV_DRAW_TASK_TYPE_LINE,
LV_DRAW_TASK_TYPE_ARC,
LV_DRAW_TASK_TYPE_TRIANGLE,
LV_DRAW_TASK_TYPE_MASK_RECTANGLE,
LV_DRAW_TASK_TYPE_MASK_BITMAP,
LV_DRAW_TASK_TYPE_BLUR,
#if LV_USE_VECTOR_GRAPHIC
LV_DRAW_TASK_TYPE_VECTOR,
#endif
#if LV_USE_3DTEXTURE
LV_DRAW_TASK_TYPE_3D,
#endif
} lv_draw_task_type_t;
typedef enum {
/** Waiting for an other task to be finished.
* For example in case of `LV_DRAW_TASK_TYPE_LAYER` (used to blend a layer)
* is blocked until all the draw tasks of the layer is rendered. */
LV_DRAW_TASK_STATE_BLOCKED,
/** The draw task is added to the layers list and waits to be rendered. */
LV_DRAW_TASK_STATE_WAITING,
/** The draw task is added to the command queue of the draw unit.
* As the queued task are executed in order it's possible to queue multiple draw task
* (for the same draw unit) even if they are depending on each other.
* Therefore `lv_draw_get_available_task` and `lv_draw_get_next_available_task` can return
* draw task for the same draw unit even if a dependent draw task is not finished ready yet.*/
LV_DRAW_TASK_STATE_QUEUED,
/** The draw task is being rendered. This draw task needs to be finished before
* `lv_draw_get_available_task` and `lv_draw_get_next_available_task` would
* return any depending draw tasks.*/
LV_DRAW_TASK_STATE_IN_PROGRESS,
/** The draw task is rendered. It will be removed from the draw task list of the layer
* and freed automatically. */
LV_DRAW_TASK_STATE_FINISHED,
} lv_draw_task_state_t;
struct _lv_layer_t {
/** Target draw buffer of the layer */
lv_draw_buf_t * draw_buf;
/** Linked list of draw tasks */
lv_draw_task_t * draw_task_head;
/** Parent layer */
lv_layer_t * parent;
/** Next layer */
lv_layer_t * next;
/** User data */
void * user_data;
/** The absolute coordinates of the buffer */
lv_area_t buf_area;
/** The physical clipping area relative to the display */
lv_area_t phy_clip_area;
/**
* NEVER USE IT DRAW UNITS. USED INTERNALLY DURING DRAW TASK CREATION.
* The current clip area with absolute coordinates, always the same or smaller than `buf_area`
* Can be set before new draw tasks are added to indicate the clip area of the draw tasks.
* Therefore `lv_draw_add_task()` always saves it in the new draw task to know the clip area when the draw task was added.
* During drawing the draw units also sees the saved clip_area and should use it during drawing.
* During drawing the layer's clip area shouldn't be used as it might be already changed for other draw tasks.
*/
lv_area_t _clip_area;
#if LV_DRAW_TRANSFORM_USE_MATRIX
/** Transform matrix to be applied when rendering the layer */
lv_matrix_t matrix;
#endif
/** Partial y offset */
int32_t partial_y_offset;
/** Recolor of the layer */
lv_color32_t recolor;
/** The color format of the layer. LV_COLOR_FORMAT_... */
lv_color_format_t color_format;
/** Flag indicating all tasks are added */
bool all_tasks_added;
/** Opacity of the layer */
lv_opa_t opa;
};
typedef struct {
/**The widget for which draw descriptor was created */
lv_obj_t * obj;
/**The widget part for which draw descriptor was created */
uint32_t part;
/**A widget type specific ID (e.g. table row index). See the docs of the given widget.*/
uint32_t id1;
/**A widget type specific ID (e.g. table column index). See the docs of the given widget.*/
uint32_t id2;
/**The target layer */
lv_layer_t * layer;
/*Drop shadow is part of every draw dsc as anything can have drop shadow*/
/**Drop shadow offset in X*/
int16_t drop_shadow_ofs_x;
/**Drop shadow offset in Y*/
int16_t drop_shadow_ofs_y;
/**Drop shadow color*/
lv_color_t drop_shadow_color;
/**Drop shadow opacity*/
lv_opa_t drop_shadow_opa;
/**Drop shadow blur radius*/
int32_t drop_shadow_blur_radius: 20;
/**Drop shadow blur quality*/
lv_blur_quality_t drop_shadow_quality : 3;
/**Size of the specific draw descriptor into which this base descriptor is embedded*/
size_t dsc_size;
/**Any custom user data*/
void * user_data;
} lv_draw_dsc_base_t;
/**********************
* GLOBAL PROTOTYPES
**********************/
/**
* Used internally to initialize the drawing module
*/
void lv_draw_init(void);
/**
* Deinitialize the drawing module
*/
void lv_draw_deinit(void);
/**
* Allocate a new draw unit with the given size and appends it to the list of draw units
* @param size the size to allocate. E.g. `sizeof(my_draw_unit_t)`,
* where the first element of `my_draw_unit_t` is `lv_draw_unit_t`.
*/
void * lv_draw_create_unit(size_t size);
/**
* Add an empty draw task to the draw task list of a layer.
* @param layer pointer to a layer
* @param coords the coordinates of the draw task
* @return the created draw task which needs to be
* further configured e.g. by added a draw descriptor
*/
lv_draw_task_t * lv_draw_add_task(lv_layer_t * layer, const lv_area_t * coords, lv_draw_task_type_t type);
/**
* Needs to be called when a draw task is created and configured.
* It will send an event about the new draw task to the widget
* and assign it to a draw unit.
* @param layer pointer to a layer
* @param t pointer to a draw task
*/
void lv_draw_finalize_task_creation(lv_layer_t * layer, lv_draw_task_t * t);
/**
* Try dispatching draw tasks to draw units
*/
void lv_draw_dispatch(void);
/**
* Used internally to try dispatching draw tasks of a specific layer
* @param disp pointer to a display on which the dispatching was requested
* @param layer pointer to a layer
* @return at least one draw task is being rendered (maybe it was taken earlier)
*/
bool lv_draw_dispatch_layer(lv_display_t * disp, lv_layer_t * layer);
/**
* Wait for a new dispatch request.
* It's blocking if `LV_USE_OS == 0` else it yields
*/
void lv_draw_dispatch_wait_for_request(void);
/**
* Wait for draw finish in case of asynchronous task execution.
* If `LV_USE_OS == 0` it just return.
*/
void lv_draw_wait_for_finish(void);
/**
* When a draw unit finished a draw task it needs to request dispatching
* to let LVGL assign a new draw task to it
*/
void lv_draw_dispatch_request(void);
/**
* Get the total number of draw units.
*/
uint32_t lv_draw_get_unit_count(void);
/**
* If there is only one draw unit check the first draw task if it's available.
* If there are multiple draw units call `lv_draw_get_next_available_task` to find a task.
* @param layer the draw layer to search in
* @param t_prev continue searching from this task
* @param draw_unit_id check the task where `preferred_draw_unit_id` equals this value or `LV_DRAW_UNIT_NONE`
* @return an available draw task or NULL if there is not any
*/
lv_draw_task_t * lv_draw_get_available_task(lv_layer_t * layer, lv_draw_task_t * t_prev, uint8_t draw_unit_id);
/**
* Find and available draw task
* @param layer the draw layer to search in
* @param t_prev continue searching from this task
* @param draw_unit_id check the task where `preferred_draw_unit_id` equals this value or `LV_DRAW_UNIT_NONE`
* @return an available draw task or NULL if there is not any
*/
lv_draw_task_t * lv_draw_get_next_available_task(lv_layer_t * layer, lv_draw_task_t * t_prev, uint8_t draw_unit_id);
/**
* Tell how many draw task are waiting to be drawn on the area of `t_check`.
* It can be used to determine if a GPU shall combine many draw tasks into one or not.
* If a lot of tasks are waiting for the current ones it makes sense to draw them one-by-one
* to not block the dependent tasks' rendering
* @param t_check the task whose dependent tasks shall be counted
* @return number of tasks depending on `t_check`
*/
uint32_t lv_draw_get_dependent_count(lv_draw_task_t * t_check);
/**
* Send an event to the draw units
* @param name the name of the draw unit to send the event to
* @param code the event code
* @param param the event parameter
*/
void lv_draw_unit_send_event(const char * name, lv_event_code_t code, void * param);
/**
* Initialize a layer
* @param layer pointer to a layer to initialize
*/
void lv_layer_init(lv_layer_t * layer);
/**
* Reset the layer to a drawable state
* @param layer pointer to a layer to reset
*/
void lv_layer_reset(lv_layer_t * layer);
/**
* Create (allocate) a new layer on a parent layer
* @param parent_layer the parent layer to which the layer will be merged when it's rendered
* @param color_format the color format of the layer
* @param area the areas of the layer (absolute coordinates)
* @return the new target_layer or NULL on error
*/
lv_layer_t * lv_draw_layer_create(lv_layer_t * parent_layer, lv_color_format_t color_format, const lv_area_t * area);
/**
* Initialize a layer which is allocated by the user
* @param layer pointer the layer to initialize (its lifetime needs to be managed by the user)
* @param parent_layer the parent layer to which the layer will be merged when it's rendered
* @param color_format the color format of the layer
* @param area the areas of the layer (absolute coordinates)
* @return the new target_layer or NULL on error
*/
void lv_draw_layer_init(lv_layer_t * layer, lv_layer_t * parent_layer, lv_color_format_t color_format,
const lv_area_t * area);
/**
* Try to allocate a buffer for the layer.
* @param layer pointer to a layer
* @return pointer to the allocated aligned buffer or NULL on failure
*/
void * lv_draw_layer_alloc_buf(lv_layer_t * layer);
/**
* Got to a pixel at X and Y coordinate on a layer
* @param layer pointer to a layer
* @param x the target X coordinate
* @param y the target X coordinate
* @return `buf` offset to point to the given X and Y coordinate
*/
void * lv_draw_layer_go_to_xy(lv_layer_t * layer, int32_t x, int32_t y);
/**
* Get the type of a draw task
* @param t the draw task to get the type of
* @return the draw task type
*/
lv_draw_task_type_t lv_draw_task_get_type(const lv_draw_task_t * t);
/**
* Get the draw descriptor of a draw task
* @param t the draw task to get the draw descriptor of
* @return a void pointer to the draw descriptor
*/
void * lv_draw_task_get_draw_dsc(const lv_draw_task_t * t);
/**
* Get the draw area of a draw task
* @param t the draw task to get the draw area of
* @param area the destination where the draw area will be stored
*/
void lv_draw_task_get_area(const lv_draw_task_t * t, lv_area_t * area);
lv_layer_t * lv_draw_layer_create_drop_shadow(lv_layer_t * parent_layer, const lv_draw_dsc_base_t * base,
const lv_area_t * area);
void lv_draw_layer_finish_drop_shadow(lv_layer_t * drop_shadow_layer, const lv_draw_dsc_base_t * base);
/**********************
* GLOBAL VARIABLES
**********************/
/**********************
* MACROS
**********************/
#ifdef __cplusplus
} /*extern "C"*/
#endif
#endif /*LV_DRAW_H*/

View File

@@ -0,0 +1,71 @@
/**
* @file lv_draw_3d.c
*
*/
/*********************
* INCLUDES
*********************/
#include "lv_draw_3d.h"
#if LV_USE_3DTEXTURE
#include "lv_draw_private.h"
/*********************
* DEFINES
*********************/
/**********************
* TYPEDEFS
**********************/
/**********************
* STATIC PROTOTYPES
**********************/
/**********************
* STATIC VARIABLES
**********************/
/**********************
* MACROS
**********************/
/**********************
* GLOBAL FUNCTIONS
**********************/
void lv_draw_3d_dsc_init(lv_draw_3d_dsc_t * dsc)
{
lv_memzero(dsc, sizeof(lv_draw_3d_dsc_t));
dsc->base.dsc_size = sizeof(lv_draw_3d_dsc_t);
dsc->tex_id = LV_3DTEXTURE_ID_NULL;
dsc->h_flip = false;
dsc->v_flip = false;
dsc->opa = LV_OPA_COVER;
}
lv_draw_3d_dsc_t * lv_draw_task_get_3d_dsc(lv_draw_task_t * task)
{
return task->type == LV_DRAW_TASK_TYPE_3D ? (lv_draw_3d_dsc_t *)task->draw_dsc : NULL;
}
void lv_draw_3d(lv_layer_t * layer, const lv_draw_3d_dsc_t * dsc, const lv_area_t * coords)
{
LV_PROFILER_DRAW_BEGIN;
lv_draw_task_t * t = lv_draw_add_task(layer, coords, LV_DRAW_TASK_TYPE_3D);
lv_memcpy(t->draw_dsc, dsc, sizeof(*dsc));
lv_draw_finalize_task_creation(layer, t);
LV_PROFILER_DRAW_END;
}
/**********************
* STATIC FUNCTIONS
**********************/
#endif /*LV_USE_3DTEXTURE*/

View File

@@ -0,0 +1,72 @@
/**
* @file lv_draw_3d.h
*
*/
#ifndef LV_DRAW_3D_H
#define LV_DRAW_3D_H
#ifdef __cplusplus
extern "C" {
#endif
/*********************
* INCLUDES
*********************/
#include "../lv_conf_internal.h"
#if LV_USE_3DTEXTURE
#include "lv_draw.h"
/*********************
* DEFINES
*********************/
/**********************
* TYPEDEFS
**********************/
typedef struct {
lv_draw_dsc_base_t base;
lv_3dtexture_id_t tex_id;
bool h_flip;
bool v_flip;
lv_opa_t opa;
} lv_draw_3d_dsc_t;
/**********************
* GLOBAL PROTOTYPES
**********************/
/**
* Initialize a 3D draw descriptor
* @param dsc pointer to a draw descriptor
*/
void lv_draw_3d_dsc_init(lv_draw_3d_dsc_t * dsc);
/**
* Try to get a 3D draw descriptor from a draw task.
* @param task draw task
* @return the task's draw descriptor or NULL if the task is not of type LV_DRAW_TASK_TYPE_3D
*/
lv_draw_3d_dsc_t * lv_draw_task_get_3d_dsc(lv_draw_task_t * task);
/**
* Create a 3D draw task
* @param layer pointer to a layer
* @param dsc pointer to an initialized `lv_draw_3d_dsc_t` variable
*/
void lv_draw_3d(lv_layer_t * layer, const lv_draw_3d_dsc_t * dsc, const lv_area_t * coords);
/**********************
* MACROS
**********************/
#endif /*LV_USE_3DTEXTURE*/
#ifdef __cplusplus
} /*extern "C"*/
#endif
#endif /*LV_DRAW_3D_H*/

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