9 Commits

Author SHA1 Message Date
TuxMonkey
0602520e4f feat: redesign PRTS main menu cards with large icons, black boot screen 2026-07-21 22:23:40 +08:00
TuxMonkey
dc57b5152e feat: refine remote states and PRTS LCD refresh
- add remote NOT_READY/READY/PROTECT state flow and matching LED/status display

- speed up TFT hardware SPI DMA flush path and larger LVGL partial refresh buffer

- reduce page/menu redraw artifacts and center the PRTS status title
2026-07-21 00:40:01 +08:00
TuxMonkey
8671f84139 feat: speed up PRTS LCD refresh 2026-07-20 23:34:13 +08:00
TuxMonkey
b2169513e1 tft lvgl test ok 2026-07-20 21:59:16 +08:00
TuxMonkey
8928b7c7db ioc changed hard spi tft 2026-07-20 18:09:56 +08:00
TuxMonkey
9ea9ddf310 ioc changed hard spi tft 2026-07-20 16:41:16 +08:00
TuxMonkey
f92585831a ioc changed soft spi tft 2026-07-20 16:29:06 +08:00
TuxMonkey
cda936d7e0 ioc changed but not tested 2026-07-20 15:39:14 +08:00
TuxMonkey
d13729b162 ioc changed but not tested 2026-07-20 15:39:06 +08:00
785 changed files with 447104 additions and 214 deletions

View File

@@ -155,7 +155,7 @@ link_directories(
# A. 头文件管理:全自动模式,扫描 User_Code 下的所有头文件。
# ------------------------------------------------------------------------------
message(NOTICE ">> [Searching All Header] 正在全自动递归扫描 User_Code 下所有头文件路径...")
file(GLOB_RECURSE USER_HEADERS "${CMAKE_SOURCE_DIR}/User_Code/*.h" "${CMAKE_SOURCE_DIR}/User_Code/*.hpp")
file(GLOB_RECURSE USER_HEADERS CONFIGURE_DEPENDS "${CMAKE_SOURCE_DIR}/User_Code/*.h" "${CMAKE_SOURCE_DIR}/User_Code/*.hpp")
foreach (header ${USER_HEADERS})
get_filename_component(dir ${header} DIRECTORY) # 获取头文件所在目录
@@ -174,7 +174,7 @@ message(NOTICE ">> [INFO] 正在全自动递归扫描 User_Code/application下
# 1. 添加通用代码 (BSP & Module & Application 通用框架)
# 包含 bsp, module 以及 application 下的通用组件
file(GLOB_RECURSE COMMON_SOURCES
file(GLOB_RECURSE COMMON_SOURCES CONFIGURE_DEPENDS
"${PROJECT_SOURCE_DIR}/User_Code/bsp/*.c"
"${PROJECT_SOURCE_DIR}/User_Code/bsp/*.cpp"
"${PROJECT_SOURCE_DIR}/User_Code/module/*.c"
@@ -269,7 +269,7 @@ endif()
if(ACTIVE_APP_DIR)
if(EXISTS "${ACTIVE_APP_DIR}")
# 添加该兵种目录下的所有源码
file(GLOB_RECURSE APP_SOURCES "${ACTIVE_APP_DIR}/*.c" "${ACTIVE_APP_DIR}/*.cpp")
file(GLOB_RECURSE APP_SOURCES CONFIGURE_DEPENDS "${ACTIVE_APP_DIR}/*.c" "${ACTIVE_APP_DIR}/*.cpp")
# --- 新增:统计文件数量 ---
list(LENGTH APP_SOURCES APP_SOURCES_COUNT)
@@ -294,4 +294,4 @@ target_include_directories(${CMAKE_PROJECT_NAME} PRIVATE
target_compile_definitions(${CMAKE_PROJECT_NAME} PRIVATE
# RM_REFEREE
)
)

View File

@@ -167,6 +167,34 @@ standard names. */
/* USER CODE BEGIN Defines */
/* Section where parameter definitions can be added (for instance, to override default ones in FreeRTOS.h) */
#include CMSIS_device_header
#define INCLUDE_xTaskGetIdleTaskHandle 1
#define configGENERATE_RUN_TIME_STATS 1
#define configUSE_STATS_FORMATTING_FUNCTIONS 0
#define portCONFIGURE_TIMER_FOR_RUN_TIME_STATS() \
do { \
CoreDebug->DEMCR |= CoreDebug_DEMCR_TRCENA_Msk; \
DWT->CYCCNT = 0U; \
DWT->CTRL |= DWT_CTRL_CYCCNTENA_Msk; \
} while (0)
#define portGET_RUN_TIME_COUNTER_VALUE() DWT->CYCCNT
#ifndef traceTASK_SWITCHED_IN
#define traceTASK_SWITCHED_IN() \
do { \
extern void lv_freertos_task_switch_in(const char * name); \
lv_freertos_task_switch_in(pxCurrentTCB->pcTaskName); \
} while (0)
#endif
#ifndef traceTASK_SWITCHED_OUT
#define traceTASK_SWITCHED_OUT() \
do { \
extern void lv_freertos_task_switch_out(void); \
lv_freertos_task_switch_out(); \
} while (0)
#endif
/* USER CODE END Defines */
#endif /* FREERTOS_CONFIG_H */

View File

@@ -36,15 +36,12 @@ extern SPI_HandleTypeDef hspi1;
extern SPI_HandleTypeDef hspi2;
extern SPI_HandleTypeDef hspi6;
/* USER CODE BEGIN Private defines */
/* USER CODE END Private defines */
void MX_SPI1_Init(void);
void MX_SPI2_Init(void);
void MX_SPI6_Init(void);
/* USER CODE BEGIN Prototypes */

View File

@@ -78,7 +78,7 @@ void MX_ADC1_Init(void)
*/
sConfig.Channel = ADC_CHANNEL_4;
sConfig.Rank = ADC_REGULAR_RANK_1;
sConfig.SamplingTime = ADC_SAMPLETIME_1CYCLE_5;
sConfig.SamplingTime = ADC_SAMPLETIME_64CYCLES_5;
sConfig.SingleDiff = ADC_SINGLE_ENDED;
sConfig.OffsetNumber = ADC_OFFSET_NONE;
sConfig.Offset = 0;
@@ -90,7 +90,9 @@ void MX_ADC1_Init(void)
/** Configure Regular Channel
*/
sConfig.Channel = ADC_CHANNEL_19;
sConfig.Rank = ADC_REGULAR_RANK_2;
sConfig.SamplingTime = ADC_SAMPLETIME_64CYCLES_5;
if (HAL_ADC_ConfigChannel(&hadc1, &sConfig) != HAL_OK)
{
Error_Handler();
@@ -171,10 +173,17 @@ void HAL_ADC_MspInit(ADC_HandleTypeDef* adcHandle)
/* ADC1 clock enable */
__HAL_RCC_ADC12_CLK_ENABLE();
__HAL_RCC_GPIOA_CLK_ENABLE();
__HAL_RCC_GPIOC_CLK_ENABLE();
/**ADC1 GPIO Configuration
PA5 ------> ADC1_INP19
PC4 ------> ADC1_INP4
*/
GPIO_InitStruct.Pin = GPIO_PIN_5;
GPIO_InitStruct.Mode = GPIO_MODE_ANALOG;
GPIO_InitStruct.Pull = GPIO_NOPULL;
HAL_GPIO_Init(GPIOA, &GPIO_InitStruct);
GPIO_InitStruct.Pin = GPIO_PIN_4;
GPIO_InitStruct.Mode = GPIO_MODE_ANALOG;
GPIO_InitStruct.Pull = GPIO_NOPULL;
@@ -231,8 +240,11 @@ void HAL_ADC_MspDeInit(ADC_HandleTypeDef* adcHandle)
__HAL_RCC_ADC12_CLK_DISABLE();
/**ADC1 GPIO Configuration
PA5 ------> ADC1_INP19
PC4 ------> ADC1_INP4
*/
HAL_GPIO_DeInit(GPIOA, GPIO_PIN_5);
HAL_GPIO_DeInit(GPIOC, GPIO_PIN_4);
/* ADC1 DMA DeInit */

View File

@@ -28,6 +28,8 @@
#include "ws2812status.h"
#include "ins_task.h"
#include "buzzer.h"
#include "tft.h"
#include "prts.h"
// #include "remoteTask.h"
//
// #include "bsp_dwt.h"
@@ -45,6 +47,8 @@
/* Private define ------------------------------------------------------------*/
/* USER CODE BEGIN PD */
#define PRTS_STATUS_UPDATE_PERIOD_MS 50U
#define PRTS_TASK_SLEEP_CAP_MS 1U
/* USER CODE END PD */
@@ -61,7 +65,7 @@
osThreadId_t BeginTaskHandle;
const osThreadAttr_t BeginTask_attributes = {
.name = "BeginTask",
.stack_size = 256 * 4,
.stack_size = 512 * 4,
.priority = (osPriority_t) osPriorityNormal,
};
/* Definitions for shoot */
@@ -157,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 = osPriorityNormal
};
osThreadId_t prtsHandle;
void StartPRTSTask(void *argument);
/* USER CODE END FunctionPrototypes */
void StartDefaultTask(void *argument);
@@ -267,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 */
@@ -290,7 +308,7 @@ __weak void StartDefaultTask(void *argument)
/* Infinite loop */
for (;;)
{
osDelay(1);
osDelay(1000);
}
/* USER CODE END StartDefaultTask */
}
@@ -472,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

@@ -38,6 +38,7 @@
* Output
* EVENT_OUT
* EXTI
PA7 ------> SPI6_MOSI
PA8 ------> RCC_MCO_1
PC10 ------> SPI3_SCK
PC11 ------> SPI3_MISO
@@ -56,10 +57,7 @@ void MX_GPIO_Init(void)
__HAL_RCC_GPIOD_CLK_ENABLE();
/*Configure GPIO pin Output Level */
HAL_GPIO_WritePin(GPIOC, Power2_Pin|Power1_Pin|Power_5V_EN_Pin|GYRO_CS_Pin, GPIO_PIN_SET);
/*Configure GPIO pin Output Level */
HAL_GPIO_WritePin(ACC_CS_GPIO_Port, ACC_CS_Pin, GPIO_PIN_RESET);
HAL_GPIO_WritePin(GPIOC, Power2_Pin|Power1_Pin|Power_5V_EN_Pin|ACC_CS_Pin|GYRO_CS_Pin, GPIO_PIN_SET);
/*Configure GPIO pin Output Level */
HAL_GPIO_WritePin(GPIOA, power2_Pin|power1_Pin, GPIO_PIN_SET);
@@ -97,6 +95,20 @@ void MX_GPIO_Init(void)
GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_HIGH;
HAL_GPIO_Init(GPIOA, &GPIO_InitStruct);
/*Configure GPIO pin : PA6 */
GPIO_InitStruct.Pin = GPIO_PIN_6;
GPIO_InitStruct.Mode = GPIO_MODE_INPUT;
GPIO_InitStruct.Pull = GPIO_NOPULL;
HAL_GPIO_Init(GPIOA, &GPIO_InitStruct);
/*Configure GPIO pin : PA7 */
GPIO_InitStruct.Pin = GPIO_PIN_7;
GPIO_InitStruct.Mode = GPIO_MODE_AF_PP;
GPIO_InitStruct.Pull = GPIO_NOPULL;
GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_LOW;
GPIO_InitStruct.Alternate = GPIO_AF8_SPI6;
HAL_GPIO_Init(GPIOA, &GPIO_InitStruct);
/*Configure GPIO pins : pump3_Pin pump4_Pin */
GPIO_InitStruct.Pin = pump3_Pin|pump4_Pin;
GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP;

View File

@@ -35,6 +35,7 @@
#include "buzzer.h"
#include "ws2812status.h"
#include "robot.h"
#include "tft.h"
/* USER CODE END Includes */
@@ -133,10 +134,10 @@ int main(void)
MX_SPI1_Init();
MX_UART5_Init();
MX_TIM3_Init();
MX_SPI6_Init();
MX_ADC1_Init();
/* USER CODE BEGIN 2 */
TFT_ShowBootScreen();
robotSelfCheck(); //自检LED
RobotInit();
systemstart_song(); //开机音乐

View File

@@ -26,7 +26,6 @@
SPI_HandleTypeDef hspi1;
SPI_HandleTypeDef hspi2;
SPI_HandleTypeDef hspi6;
DMA_HandleTypeDef hdma_spi1_rx;
DMA_HandleTypeDef hdma_spi1_tx;
DMA_HandleTypeDef hdma_spi2_rx;
@@ -46,11 +45,11 @@ void MX_SPI1_Init(void)
hspi1.Instance = SPI1;
hspi1.Init.Mode = SPI_MODE_MASTER;
hspi1.Init.Direction = SPI_DIRECTION_2LINES;
hspi1.Init.DataSize = SPI_DATASIZE_16BIT;
hspi1.Init.DataSize = SPI_DATASIZE_8BIT;
hspi1.Init.CLKPolarity = SPI_POLARITY_LOW;
hspi1.Init.CLKPhase = SPI_PHASE_1EDGE;
hspi1.Init.NSS = SPI_NSS_SOFT;
hspi1.Init.BaudRatePrescaler = SPI_BAUDRATEPRESCALER_16;
hspi1.Init.BaudRatePrescaler = SPI_BAUDRATEPRESCALER_2;
hspi1.Init.FirstBit = SPI_FIRSTBIT_MSB;
hspi1.Init.TIMode = SPI_TIMODE_DISABLE;
hspi1.Init.CRCCalculation = SPI_CRCCALCULATION_DISABLE;
@@ -115,48 +114,6 @@ void MX_SPI2_Init(void)
/* USER CODE END SPI2_Init 2 */
}
/* SPI6 init function */
void MX_SPI6_Init(void)
{
/* USER CODE BEGIN SPI6_Init 0 */
/* USER CODE END SPI6_Init 0 */
/* USER CODE BEGIN SPI6_Init 1 */
/* USER CODE END SPI6_Init 1 */
hspi6.Instance = SPI6;
hspi6.Init.Mode = SPI_MODE_MASTER;
hspi6.Init.Direction = SPI_DIRECTION_2LINES_TXONLY;
hspi6.Init.DataSize = SPI_DATASIZE_8BIT;
hspi6.Init.CLKPolarity = SPI_POLARITY_LOW;
hspi6.Init.CLKPhase = SPI_PHASE_2EDGE;
hspi6.Init.NSS = SPI_NSS_SOFT;
hspi6.Init.BaudRatePrescaler = SPI_BAUDRATEPRESCALER_4;
hspi6.Init.FirstBit = SPI_FIRSTBIT_MSB;
hspi6.Init.TIMode = SPI_TIMODE_DISABLE;
hspi6.Init.CRCCalculation = SPI_CRCCALCULATION_DISABLE;
hspi6.Init.CRCPolynomial = 0x0;
hspi6.Init.NSSPMode = SPI_NSS_PULSE_ENABLE;
hspi6.Init.NSSPolarity = SPI_NSS_POLARITY_LOW;
hspi6.Init.FifoThreshold = SPI_FIFO_THRESHOLD_01DATA;
hspi6.Init.TxCRCInitializationPattern = SPI_CRC_INITIALIZATION_ALL_ZERO_PATTERN;
hspi6.Init.RxCRCInitializationPattern = SPI_CRC_INITIALIZATION_ALL_ZERO_PATTERN;
hspi6.Init.MasterSSIdleness = SPI_MASTER_SS_IDLENESS_00CYCLE;
hspi6.Init.MasterInterDataIdleness = SPI_MASTER_INTERDATA_IDLENESS_00CYCLE;
hspi6.Init.MasterReceiverAutoSusp = SPI_MASTER_RX_AUTOSUSP_DISABLE;
hspi6.Init.MasterKeepIOState = SPI_MASTER_KEEP_IO_STATE_DISABLE;
hspi6.Init.IOSwap = SPI_IO_SWAP_DISABLE;
if (HAL_SPI_Init(&hspi6) != HAL_OK)
{
Error_Handler();
}
/* USER CODE BEGIN SPI6_Init 2 */
/* USER CODE END SPI6_Init 2 */
}
void HAL_SPI_MspInit(SPI_HandleTypeDef* spiHandle)
@@ -192,14 +149,14 @@ void HAL_SPI_MspInit(SPI_HandleTypeDef* spiHandle)
GPIO_InitStruct.Pin = GPIO_PIN_7;
GPIO_InitStruct.Mode = GPIO_MODE_AF_PP;
GPIO_InitStruct.Pull = GPIO_NOPULL;
GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_LOW;
GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_VERY_HIGH;
GPIO_InitStruct.Alternate = GPIO_AF5_SPI1;
HAL_GPIO_Init(GPIOD, &GPIO_InitStruct);
GPIO_InitStruct.Pin = GPIO_PIN_3|GPIO_PIN_4;
GPIO_InitStruct.Mode = GPIO_MODE_AF_PP;
GPIO_InitStruct.Pull = GPIO_NOPULL;
GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_LOW;
GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_VERY_HIGH;
GPIO_InitStruct.Alternate = GPIO_AF5_SPI1;
HAL_GPIO_Init(GPIOB, &GPIO_InitStruct);
@@ -210,8 +167,8 @@ void HAL_SPI_MspInit(SPI_HandleTypeDef* spiHandle)
hdma_spi1_rx.Init.Direction = DMA_PERIPH_TO_MEMORY;
hdma_spi1_rx.Init.PeriphInc = DMA_PINC_DISABLE;
hdma_spi1_rx.Init.MemInc = DMA_MINC_ENABLE;
hdma_spi1_rx.Init.PeriphDataAlignment = DMA_PDATAALIGN_HALFWORD;
hdma_spi1_rx.Init.MemDataAlignment = DMA_MDATAALIGN_HALFWORD;
hdma_spi1_rx.Init.PeriphDataAlignment = DMA_PDATAALIGN_BYTE;
hdma_spi1_rx.Init.MemDataAlignment = DMA_MDATAALIGN_BYTE;
hdma_spi1_rx.Init.Mode = DMA_NORMAL;
hdma_spi1_rx.Init.Priority = DMA_PRIORITY_VERY_HIGH;
hdma_spi1_rx.Init.FIFOMode = DMA_FIFOMODE_DISABLE;
@@ -228,8 +185,8 @@ void HAL_SPI_MspInit(SPI_HandleTypeDef* spiHandle)
hdma_spi1_tx.Init.Direction = DMA_MEMORY_TO_PERIPH;
hdma_spi1_tx.Init.PeriphInc = DMA_PINC_DISABLE;
hdma_spi1_tx.Init.MemInc = DMA_MINC_ENABLE;
hdma_spi1_tx.Init.PeriphDataAlignment = DMA_PDATAALIGN_HALFWORD;
hdma_spi1_tx.Init.MemDataAlignment = DMA_MDATAALIGN_HALFWORD;
hdma_spi1_tx.Init.PeriphDataAlignment = DMA_PDATAALIGN_BYTE;
hdma_spi1_tx.Init.MemDataAlignment = DMA_MDATAALIGN_BYTE;
hdma_spi1_tx.Init.Mode = DMA_NORMAL;
hdma_spi1_tx.Init.Priority = DMA_PRIORITY_VERY_HIGH;
hdma_spi1_tx.Init.FIFOMode = DMA_FIFOMODE_DISABLE;
@@ -330,41 +287,6 @@ void HAL_SPI_MspInit(SPI_HandleTypeDef* spiHandle)
/* USER CODE END SPI2_MspInit 1 */
}
else if(spiHandle->Instance==SPI6)
{
/* USER CODE BEGIN SPI6_MspInit 0 */
/* USER CODE END SPI6_MspInit 0 */
/** Initializes the peripherals clock
*/
PeriphClkInitStruct.PeriphClockSelection = RCC_PERIPHCLK_SPI6;
PeriphClkInitStruct.Spi6ClockSelection = RCC_SPI6CLKSOURCE_HSE;
if (HAL_RCCEx_PeriphCLKConfig(&PeriphClkInitStruct) != HAL_OK)
{
Error_Handler();
}
/* SPI6 clock enable */
__HAL_RCC_SPI6_CLK_ENABLE();
__HAL_RCC_GPIOA_CLK_ENABLE();
/**SPI6 GPIO Configuration
PA5 ------> SPI6_SCK
PA6 ------> SPI6_MISO
PA7 ------> SPI6_MOSI
*/
GPIO_InitStruct.Pin = GPIO_PIN_5|GPIO_PIN_6|GPIO_PIN_7;
GPIO_InitStruct.Mode = GPIO_MODE_AF_PP;
GPIO_InitStruct.Pull = GPIO_NOPULL;
GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_LOW;
GPIO_InitStruct.Alternate = GPIO_AF8_SPI6;
HAL_GPIO_Init(GPIOA, &GPIO_InitStruct);
/* USER CODE BEGIN SPI6_MspInit 1 */
/* USER CODE END SPI6_MspInit 1 */
}
}
void HAL_SPI_MspDeInit(SPI_HandleTypeDef* spiHandle)
@@ -424,25 +346,6 @@ void HAL_SPI_MspDeInit(SPI_HandleTypeDef* spiHandle)
/* USER CODE END SPI2_MspDeInit 1 */
}
else if(spiHandle->Instance==SPI6)
{
/* USER CODE BEGIN SPI6_MspDeInit 0 */
/* USER CODE END SPI6_MspDeInit 0 */
/* Peripheral clock disable */
__HAL_RCC_SPI6_CLK_DISABLE();
/**SPI6 GPIO Configuration
PA5 ------> SPI6_SCK
PA6 ------> SPI6_MISO
PA7 ------> SPI6_MOSI
*/
HAL_GPIO_DeInit(GPIOA, GPIO_PIN_5|GPIO_PIN_6|GPIO_PIN_7);
/* USER CODE BEGIN SPI6_MspDeInit 1 */
/* USER CODE END SPI6_MspDeInit 1 */
}
}
/* USER CODE BEGIN 1 */

View File

@@ -1,6 +1,6 @@
#MicroXplorer Configuration settings - do not modify
ADC1.Channel-0\#ChannelRegularConversion=ADC_CHANNEL_4
ADC1.Channel-1\#ChannelRegularConversion=ADC_CHANNEL_4
ADC1.Channel-1\#ChannelRegularConversion=ADC_CHANNEL_19
ADC1.ClockPrescaler=ADC_CLOCK_ASYNC_DIV64
ADC1.ContinuousConvMode=ENABLE
ADC1.ConversionDataManagement=ADC_CONVERSIONDATA_DMA_CIRCULAR
@@ -13,8 +13,8 @@ ADC1.OffsetSignedSaturation-0\#ChannelRegularConversion=DISABLE
ADC1.OffsetSignedSaturation-1\#ChannelRegularConversion=DISABLE
ADC1.Rank-0\#ChannelRegularConversion=1
ADC1.Rank-1\#ChannelRegularConversion=2
ADC1.SamplingTime-0\#ChannelRegularConversion=ADC_SAMPLETIME_1CYCLE_5
ADC1.SamplingTime-1\#ChannelRegularConversion=ADC_SAMPLETIME_1CYCLE_5
ADC1.SamplingTime-0\#ChannelRegularConversion=ADC_SAMPLETIME_64CYCLES_5
ADC1.SamplingTime-1\#ChannelRegularConversion=ADC_SAMPLETIME_64CYCLES_5
ADC1.master=1
ADC3.Channel-0\#ChannelRegularConversion=ADC_CHANNEL_TEMPSENSOR
ADC3.ClockPrescaler=ADC_CLOCK_ASYNC_DIV64
@@ -82,10 +82,10 @@ Dma.SPI1_RX.11.Direction=DMA_PERIPH_TO_MEMORY
Dma.SPI1_RX.11.EventEnable=DISABLE
Dma.SPI1_RX.11.FIFOMode=DMA_FIFOMODE_DISABLE
Dma.SPI1_RX.11.Instance=DMA2_Stream7
Dma.SPI1_RX.11.MemDataAlignment=DMA_MDATAALIGN_HALFWORD
Dma.SPI1_RX.11.MemDataAlignment=DMA_MDATAALIGN_BYTE
Dma.SPI1_RX.11.MemInc=DMA_MINC_ENABLE
Dma.SPI1_RX.11.Mode=DMA_NORMAL
Dma.SPI1_RX.11.PeriphDataAlignment=DMA_PDATAALIGN_HALFWORD
Dma.SPI1_RX.11.PeriphDataAlignment=DMA_PDATAALIGN_BYTE
Dma.SPI1_RX.11.PeriphInc=DMA_PINC_DISABLE
Dma.SPI1_RX.11.Polarity=HAL_DMAMUX_REQ_GEN_RISING
Dma.SPI1_RX.11.Priority=DMA_PRIORITY_VERY_HIGH
@@ -100,10 +100,10 @@ Dma.SPI1_TX.12.Direction=DMA_MEMORY_TO_PERIPH
Dma.SPI1_TX.12.EventEnable=DISABLE
Dma.SPI1_TX.12.FIFOMode=DMA_FIFOMODE_DISABLE
Dma.SPI1_TX.12.Instance=DMA1_Stream3
Dma.SPI1_TX.12.MemDataAlignment=DMA_MDATAALIGN_HALFWORD
Dma.SPI1_TX.12.MemDataAlignment=DMA_MDATAALIGN_BYTE
Dma.SPI1_TX.12.MemInc=DMA_MINC_ENABLE
Dma.SPI1_TX.12.Mode=DMA_NORMAL
Dma.SPI1_TX.12.PeriphDataAlignment=DMA_PDATAALIGN_HALFWORD
Dma.SPI1_TX.12.PeriphDataAlignment=DMA_PDATAALIGN_BYTE
Dma.SPI1_TX.12.PeriphInc=DMA_PINC_DISABLE
Dma.SPI1_TX.12.Polarity=HAL_DMAMUX_REQ_GEN_RISING
Dma.SPI1_TX.12.Priority=DMA_PRIORITY_VERY_HIGH
@@ -461,20 +461,19 @@ Mcu.IP10=NVIC
Mcu.IP11=RCC
Mcu.IP12=SPI1
Mcu.IP13=SPI2
Mcu.IP14=SPI6
Mcu.IP15=SYS
Mcu.IP16=TIM1
Mcu.IP17=TIM3
Mcu.IP18=TIM12
Mcu.IP19=UART5
Mcu.IP14=SYS
Mcu.IP15=TIM1
Mcu.IP16=TIM3
Mcu.IP17=TIM12
Mcu.IP18=UART5
Mcu.IP19=UART7
Mcu.IP2=CORTEX_M7
Mcu.IP20=UART7
Mcu.IP21=USART1
Mcu.IP22=USART2
Mcu.IP23=USART3
Mcu.IP24=USART10
Mcu.IP25=USB_DEVICE
Mcu.IP26=USB_OTG_HS
Mcu.IP20=USART1
Mcu.IP21=USART2
Mcu.IP22=USART3
Mcu.IP23=USART10
Mcu.IP24=USB_DEVICE
Mcu.IP25=USB_OTG_HS
Mcu.IP3=CRC
Mcu.IP4=DMA
Mcu.IP5=FDCAN1
@@ -482,7 +481,7 @@ Mcu.IP6=FDCAN2
Mcu.IP7=FDCAN3
Mcu.IP8=FREERTOS
Mcu.IP9=MEMORYMAP
Mcu.IPNb=27
Mcu.IPNb=26
Mcu.Name=STM32H723VGTx
Mcu.Package=LQFP100
Mcu.Pin0=PE2
@@ -624,10 +623,8 @@ PA2.GPIO_Speed=GPIO_SPEED_FREQ_HIGH
PA2.Locked=true
PA2.PinState=GPIO_PIN_SET
PA2.Signal=GPIO_Output
PA5.Mode=TX_Only_Simplex_Unidirect_Master
PA5.Signal=SPI6_SCK
PA6.Mode=Full_Duplex_Master
PA6.Signal=SPI6_MISO
PA5.Signal=ADCx_INP19
PA6.Signal=GPIO_Input
PA7.Mode=TX_Only_Simplex_Unidirect_Master
PA7.Signal=SPI6_MOSI
PA8.Mode=Clock-out-1
@@ -656,9 +653,13 @@ PB13.Locked=true
PB13.Mode=Full_Duplex_Master
PB13.Signal=SPI2_SCK
PB15.Signal=S_TIM12_CH2
PB3(JTDO/TRACESWO).GPIOParameters=GPIO_Speed
PB3(JTDO/TRACESWO).GPIO_Speed=GPIO_SPEED_FREQ_VERY_HIGH
PB3(JTDO/TRACESWO).Locked=true
PB3(JTDO/TRACESWO).Mode=Full_Duplex_Master
PB3(JTDO/TRACESWO).Signal=SPI1_SCK
PB4(NJTRST).GPIOParameters=GPIO_Speed
PB4(NJTRST).GPIO_Speed=GPIO_SPEED_FREQ_VERY_HIGH
PB4(NJTRST).Locked=true
PB4(NJTRST).Mode=Full_Duplex_Master
PB4(NJTRST).Signal=SPI1_MISO
@@ -668,10 +669,11 @@ PB5.Signal=FDCAN2_RX
PB6.Locked=true
PB6.Mode=FDCAN_Activate
PB6.Signal=FDCAN2_TX
PC0.GPIOParameters=GPIO_Speed,GPIO_Label
PC0.GPIOParameters=GPIO_Speed,PinState,GPIO_Label
PC0.GPIO_Label=ACC_CS
PC0.GPIO_Speed=GPIO_SPEED_FREQ_VERY_HIGH
PC0.Locked=true
PC0.PinState=GPIO_PIN_SET
PC0.Signal=GPIO_Output
PC1.GPIOParameters=GPIO_Speed
PC1.GPIO_Speed=GPIO_SPEED_FREQ_VERY_HIGH
@@ -738,6 +740,8 @@ PD5.Signal=USART2_TX
PD6.Locked=true
PD6.Mode=Asynchronous
PD6.Signal=USART2_RX
PD7.GPIOParameters=GPIO_Speed
PD7.GPIO_Speed=GPIO_SPEED_FREQ_VERY_HIGH
PD7.Locked=true
PD7.Mode=Full_Duplex_Master
PD7.Signal=SPI1_MOSI
@@ -817,7 +821,7 @@ ProjectManager.ToolChainLocation=
ProjectManager.UAScriptAfterPath=
ProjectManager.UAScriptBeforePath=
ProjectManager.UnderRoot=false
ProjectManager.functionlistsort=1-SystemClock_Config-RCC-false-HAL-false,2-MX_GPIO_Init-GPIO-false-HAL-true,3-MX_DMA_Init-DMA-false-HAL-true,4-MX_FDCAN2_Init-FDCAN2-false-HAL-true,5-MX_FDCAN3_Init-FDCAN3-false-HAL-true,6-MX_CRC_Init-CRC-false-HAL-true,7-MX_USB_DEVICE_Init-USB_DEVICE-false-HAL-false,8-MX_FDCAN1_Init-FDCAN1-false-HAL-true,9-MX_USART10_UART_Init-USART10-false-HAL-true,10-MX_SPI2_Init-SPI2-false-HAL-true,11-MX_TIM12_Init-TIM12-false-HAL-true,12-MX_UART7_Init-UART7-false-HAL-true,13-MX_TIM1_Init-TIM1-false-HAL-true,14-MX_USART1_UART_Init-USART1-false-HAL-true,15-MX_ADC3_Init-ADC3-false-HAL-true,16-MX_USART2_UART_Init-USART2-false-HAL-true,17-MX_USART3_UART_Init-USART3-false-HAL-true,18-MX_SPI1_Init-SPI1-false-HAL-true,19-MX_UART5_Init-UART5-false-HAL-true,20-MX_TIM3_Init-TIM3-false-HAL-true,21-MX_SPI6_Init-SPI6-false-HAL-true,22-MX_ADC1_Init-ADC1-false-HAL-true,0-MX_CORTEX_M7_Init-CORTEX_M7-false-HAL-true
ProjectManager.functionlistsort=1-SystemClock_Config-RCC-false-HAL-false,2-MX_GPIO_Init-GPIO-false-HAL-true,3-MX_DMA_Init-DMA-false-HAL-true,4-MX_FDCAN2_Init-FDCAN2-false-HAL-true,5-MX_FDCAN3_Init-FDCAN3-false-HAL-true,6-MX_CRC_Init-CRC-false-HAL-true,7-MX_USB_DEVICE_Init-USB_DEVICE-false-HAL-false,8-MX_FDCAN1_Init-FDCAN1-false-HAL-true,9-MX_USART10_UART_Init-USART10-false-HAL-true,10-MX_SPI2_Init-SPI2-false-HAL-true,11-MX_TIM12_Init-TIM12-false-HAL-true,12-MX_UART7_Init-UART7-false-HAL-true,13-MX_TIM1_Init-TIM1-false-HAL-true,14-MX_USART1_UART_Init-USART1-false-HAL-true,15-MX_ADC3_Init-ADC3-false-HAL-true,16-MX_USART2_UART_Init-USART2-false-HAL-true,17-MX_USART3_UART_Init-USART3-false-HAL-true,18-MX_SPI1_Init-SPI1-false-HAL-true,19-MX_UART5_Init-UART5-false-HAL-true,20-MX_TIM3_Init-TIM3-false-HAL-true,21-MX_ADC1_Init-ADC1-false-HAL-true,0-MX_CORTEX_M7_Init-CORTEX_M7-false-HAL-true
RCC.ADCFreq_Value=96000000
RCC.AHB12Freq_Value=240000000
RCC.AHB4Freq_Value=240000000
@@ -901,6 +905,8 @@ RCC.VCO3OutputFreq_Value=96750000
RCC.VCOInput1Freq_Value=12000000
RCC.VCOInput2Freq_Value=12000000
RCC.VCOInput3Freq_Value=750000
SH.ADCx_INP19.0=ADC1_INP19,IN19-Single-Ended
SH.ADCx_INP19.ConfNb=1
SH.ADCx_INP4.0=ADC1_INP4,IN4-Single-Ended
SH.ADCx_INP4.ConfNb=1
SH.GPXTI10.0=GPIO_EXTI10
@@ -914,11 +920,12 @@ SH.S_TIM3_CH4.ConfNb=1
SH.SharedStack_PC12.0=SPI3_MOSI
SH.SharedStack_PC12.1=UART5_TX,Asynchronous
SH.SharedStack_PC12.ConfNb=2
SPI1.BaudRatePrescaler=SPI_BAUDRATEPRESCALER_16
SPI1.CalculateBaudRate=7.5 MBits/s
SPI1.DataSize=SPI_DATASIZE_16BIT
SPI1.BaudRatePrescaler=SPI_BAUDRATEPRESCALER_2
SPI1.CLKPolarity=SPI_POLARITY_LOW
SPI1.CalculateBaudRate=60 MBits/s
SPI1.DataSize=SPI_DATASIZE_8BIT
SPI1.Direction=SPI_DIRECTION_2LINES
SPI1.IPParameters=VirtualType,Mode,Direction,CalculateBaudRate,DataSize,BaudRatePrescaler
SPI1.IPParameters=VirtualType,Mode,Direction,CalculateBaudRate,DataSize,BaudRatePrescaler,CLKPolarity
SPI1.Mode=SPI_MODE_MASTER
SPI1.VirtualType=VM_MASTER
SPI2.BaudRatePrescaler=SPI_BAUDRATEPRESCALER_32
@@ -931,14 +938,6 @@ SPI2.IPParameters=VirtualType,Mode,Direction,CalculateBaudRate,DataSize,BaudRate
SPI2.Mode=SPI_MODE_MASTER
SPI2.NSSPMode=SPI_NSS_PULSE_ENABLE
SPI2.VirtualType=VM_MASTER
SPI6.BaudRatePrescaler=SPI_BAUDRATEPRESCALER_4
SPI6.CLKPhase=SPI_PHASE_2EDGE
SPI6.CalculateBaudRate=6.0 MBits/s
SPI6.DataSize=SPI_DATASIZE_8BIT
SPI6.Direction=SPI_DIRECTION_2LINES_TXONLY
SPI6.IPParameters=VirtualType,Mode,Direction,CalculateBaudRate,DataSize,BaudRatePrescaler,CLKPhase
SPI6.Mode=SPI_MODE_MASTER
SPI6.VirtualType=VM_MASTER
STMicroelectronics.X-CUBE-ALGOBUILD.1.4.0.DSPOoLibraryJjLibrary_Checked=true
STMicroelectronics.X-CUBE-ALGOBUILD.1.4.0.IPParameters=LibraryCcDSPOoLibraryJjDSPOoLibrary
STMicroelectronics.X-CUBE-ALGOBUILD.1.4.0.LibraryCcDSPOoLibraryJjDSPOoLibrary=true

View File

@@ -54,20 +54,16 @@ void robotSelfCheck(void)
void ws2812Task(void *argument)
{
(void) argument;
RC_ctrl_t rc_data;
while (1)
{
RobotMode_t display_mode = RobotMode;
if (display_mode != SYS_ERROR_OCCURRED && !RemoteControlIsOnline())
display_mode = REMOTE_NOT_CONNECTED;
if (RemoteControlReadSnapshot(&rc_data) && switch_is_down(rc_data.sw_d))
display_mode = NORMAL_MODE;
switch (display_mode)
{
case NORMAL_MODE:
BlinkBlue();
BlinkGreen();
break;
case SYS_ERROR_OCCURRED:
BlinkRed();
@@ -75,7 +71,13 @@ void ws2812Task(void *argument)
case REMOTE_NOT_CONNECTED:
BlinkRed();
break;
case REMOTE_CONNECTED:
case REMOTE_NOT_READY:
BlinkWhite();
break;
case REMOTE_READY:
BlinkPurple();
break;
case REMOTE_PROTECT:
BlinkYellow();
break;
case AUTO_SHOOTING_MODE:

View File

@@ -15,11 +15,14 @@
/* Includes ------------------------------------------------------------------*/
#include "bsp_adc.h"
#include "adc.h"
#include <stdbool.h>
/**
* @brief ADC sampling voltage array
*/
__attribute__((section (".AXI_SRAM"))) uint16_t ADC_Voltage_Val[2];
__attribute__((section(".dma_buffer"), aligned(32))) volatile uint16_t ADC_Voltage_Val[BSP_ADC_CHANNEL_COUNT];
static bool bsp_adc_started = false;
/**
* @brief Configures the ADC.
@@ -28,8 +31,16 @@ __attribute__((section (".AXI_SRAM"))) uint16_t ADC_Voltage_Val[2];
*/
void BSP_ADC_Init(void)
{
HAL_ADCEx_Calibration_Start(&hadc1, ADC_CALIB_OFFSET, ADC_SINGLE_ENDED);
HAL_ADC_Start_DMA(&hadc1, (uint32_t *) ADC_Voltage_Val, 2);
if (bsp_adc_started)
{
return;
}
(void) HAL_ADCEx_Calibration_Start(&hadc1, ADC_CALIB_OFFSET, ADC_SINGLE_ENDED);
if (HAL_ADC_Start_DMA(&hadc1, (uint32_t *) ADC_Voltage_Val, BSP_ADC_CHANNEL_COUNT) == HAL_OK)
{
bsp_adc_started = true;
}
}
//------------------------------------------------------------------------------
@@ -41,8 +52,33 @@ void BSP_ADC_Init(void)
*/
float USER_ADC_Voltage_Update(void)
{
auto Voltage = (ADC_Voltage_Val[0] * 3.3f / 65535) * 11.0f;
return Voltage;
float voltage = ((float) ADC_Voltage_Val[BSP_ADC_BOARD_VOLTAGE_INDEX] * 3.3f / 65535.0f) * 11.0f;
return voltage;
}
//------------------------------------------------------------------------------
uint16_t BSP_ADC_GetRaw(uint32_t index)
{
if (index >= BSP_ADC_CHANNEL_COUNT)
{
return 0U;
}
return ADC_Voltage_Val[index];
}
uint16_t BSP_ADC_GetJoystickRaw(void)
{
return BSP_ADC_GetRaw(BSP_ADC_JOYSTICK_INDEX);
}
uint16_t BSP_ADC_GetJoystickRaw12(void)
{
return (uint16_t) (BSP_ADC_GetJoystickRaw() >> 4U);
}
float BSP_ADC_GetJoystickVoltage(void)
{
return (float) BSP_ADC_GetJoystickRaw() * 3.3f / 65535.0f;
}

View File

@@ -26,9 +26,20 @@ extern "C"
/* Includes ------------------------------------------------------------------*/
#include "adc.h"
#include <stdint.h>
#define BSP_ADC_CHANNEL_COUNT 2U
#define BSP_ADC_BOARD_VOLTAGE_INDEX 0U
#define BSP_ADC_JOYSTICK_INDEX 1U
extern volatile uint16_t ADC_Voltage_Val[BSP_ADC_CHANNEL_COUNT];
/* Externs ---------------------------------------------*/
void BSP_ADC_Init(void);
float USER_ADC_Voltage_Update(void);
uint16_t BSP_ADC_GetRaw(uint32_t index);
uint16_t BSP_ADC_GetJoystickRaw(void);
uint16_t BSP_ADC_GetJoystickRaw12(void);
float BSP_ADC_GetJoystickVoltage(void);
#endif //BSP_ADC_H

View File

@@ -1,21 +1,18 @@
// #pragma once // 可以用#pragma once代替#ifndef ROBOT_DEF_H(header guard)
#ifndef ROBOT_DEF_H
#define ROBOT_DEF_H
typedef enum
{
NORMAL_MODE = 0, // 正常模式对应数值0
SYS_ERROR_OCCURRED = 1, // 系统错误对应数值1
REMOTE_NOT_CONNECTED = 2, // 遥控器未连接对应数值2
REMOTE_CONNECTED = 3, // 遥控器已连接对应数值3
AUTO_SHOOTING_MODE = 4, // 成功进入自动瞄准模式对应数值4
IMU_CALIBERATION_MODE = 5 // IMU校准模式对应数值5
// 添加其他模式
} RobotMode_t; //机器人控制模式
NORMAL_MODE = 0,
SYS_ERROR_OCCURRED = 1,
REMOTE_NOT_CONNECTED = 2,
REMOTE_NOT_READY = 3,
REMOTE_READY = 4,
REMOTE_PROTECT = 5,
AUTO_SHOOTING_MODE = 6,
IMU_CALIBERATION_MODE = 7
} RobotMode_t;
extern volatile RobotMode_t RobotMode;
// #pragma pack() // 开启字节对齐,结束前面的#pragma pack(1)
#endif

View File

@@ -102,6 +102,49 @@ static uint8_t SBusEndByteIsValid(uint8_t value)
return value == SBUS_END || value == 0x04u || value == 0x14u || value == 0x24u || value == 0x34u;
}
static uint8_t RemoteControlAllSwitchesDown(const RC_ctrl_t *rc)
{
return rc != NULL && switch_is_down(rc->sw_a) && switch_is_down(rc->sw_b) && switch_is_down(rc->sw_c) &&
switch_is_down(rc->sw_d);
}
static void RemoteControlSetMode(RobotMode_t mode)
{
if (RobotMode != SYS_ERROR_OCCURRED)
RobotMode = mode;
}
static void RemoteControlUpdateMode(const RC_ctrl_t *rc)
{
if (rc == NULL || RobotMode == SYS_ERROR_OCCURRED)
return;
switch (RobotMode)
{
case REMOTE_NOT_CONNECTED:
RemoteControlSetMode(REMOTE_NOT_READY);
break;
case REMOTE_NOT_READY:
if (RemoteControlAllSwitchesDown(rc))
RemoteControlSetMode(REMOTE_READY);
break;
case REMOTE_READY:
if (switch_is_up(rc->sw_d))
RemoteControlSetMode(NORMAL_MODE);
break;
case NORMAL_MODE:
if (switch_is_down(rc->sw_d))
RemoteControlSetMode(REMOTE_PROTECT);
break;
case REMOTE_PROTECT:
if (switch_is_up(rc->sw_d))
RemoteControlSetMode(NORMAL_MODE);
break;
default:
break;
}
}
#ifdef REMOTE_DJI_DT7
/**
@@ -364,8 +407,7 @@ static void RemoteControlPublishOffline(uint8_t force)
{
rc_data_valid = 0;
memset(rc_ctrl, 0, sizeof(rc_ctrl));
if (RobotMode != SYS_ERROR_OCCURRED)
RobotMode = REMOTE_NOT_CONNECTED;
RemoteControlSetMode(REMOTE_NOT_CONNECTED);
}
__set_PRIMASK(primask);
@@ -396,8 +438,7 @@ static void RemoteControlRxCallback(USART_Instance *instance, const uint8_t *rec
rc_valid_frame_count++;
rc_data_valid = 1;
DaemonReload(rc_daemon_instance);
if (RobotMode == REMOTE_NOT_CONNECTED)
RobotMode = REMOTE_CONNECTED;
RemoteControlUpdateMode(&rc_ctrl[TEMP]);
}
/**

View File

@@ -0,0 +1,822 @@
#include "tft.h"
#include <stdbool.h>
#include <stddef.h>
#include <string.h>
#define TFT_SPI_TIMEOUT_MS 100U
#define TFT_FONT_W 5U
#define TFT_FONT_H 7U
#define TFT_MAX_SCALE 4U
#if (TFT_USE_HORIZONTAL == 0U) || (TFT_USE_HORIZONTAL == 1U)
#define TFT_X_OFFSET 0U
#define TFT_Y_OFFSET 20U
#else
#define TFT_X_OFFSET 20U
#define TFT_Y_OFFSET 0U
#endif
typedef enum
{
TFT_GLYPH_SPACE = 0,
TFT_GLYPH_DASH,
TFT_GLYPH_DOT,
TFT_GLYPH_COLON,
TFT_GLYPH_0,
TFT_GLYPH_A = TFT_GLYPH_0 + 10
} TFT_GlyphIndex_e;
static bool tft_initialized = false;
static bool tft_transfer_ok = true;
static volatile bool tft_dma_active = false;
static TFT_TransferDoneCallback tft_dma_callback = NULL;
static void *tft_dma_context = NULL;
static const uint8_t tft_font5x7[][TFT_FONT_H] = {
{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, /* space */
{0x00, 0x00, 0x00, 0x1F, 0x00, 0x00, 0x00}, /* - */
{0x00, 0x00, 0x00, 0x00, 0x00, 0x0C, 0x0C}, /* . */
{0x00, 0x0C, 0x0C, 0x00, 0x0C, 0x0C, 0x00}, /* : */
{0x0E, 0x11, 0x13, 0x15, 0x19, 0x11, 0x0E}, /* 0 */
{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 */
{0x1F, 0x01, 0x0F, 0x10, 0x10, 0x11, 0x0E}, /* 5 */
{0x0C, 0x02, 0x01, 0x0F, 0x11, 0x11, 0x0E}, /* 6 */
{0x1F, 0x10, 0x08, 0x04, 0x02, 0x02, 0x02}, /* 7 */
{0x0E, 0x11, 0x11, 0x0E, 0x11, 0x11, 0x0E}, /* 8 */
{0x0E, 0x11, 0x11, 0x1E, 0x10, 0x08, 0x06}, /* 9 */
{0x0E, 0x11, 0x11, 0x1F, 0x11, 0x11, 0x11}, /* A */
{0x0F, 0x11, 0x11, 0x0F, 0x11, 0x11, 0x0F}, /* B */
{0x0E, 0x11, 0x01, 0x01, 0x01, 0x11, 0x0E}, /* C */
{0x0F, 0x11, 0x11, 0x11, 0x11, 0x11, 0x0F}, /* D */
{0x1F, 0x01, 0x01, 0x0F, 0x01, 0x01, 0x1F}, /* E */
{0x1F, 0x01, 0x01, 0x0F, 0x01, 0x01, 0x01}, /* F */
{0x0E, 0x11, 0x01, 0x1D, 0x11, 0x11, 0x0E}, /* G */
{0x11, 0x11, 0x11, 0x1F, 0x11, 0x11, 0x11}, /* H */
{0x0E, 0x04, 0x04, 0x04, 0x04, 0x04, 0x0E}, /* I */
{0x1C, 0x08, 0x08, 0x08, 0x08, 0x09, 0x06}, /* J */
{0x11, 0x09, 0x05, 0x03, 0x05, 0x09, 0x11}, /* K */
{0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x1F}, /* L */
{0x11, 0x1B, 0x15, 0x15, 0x11, 0x11, 0x11}, /* M */
{0x11, 0x13, 0x15, 0x19, 0x11, 0x11, 0x11}, /* N */
{0x0E, 0x11, 0x11, 0x11, 0x11, 0x11, 0x0E}, /* O */
{0x0F, 0x11, 0x11, 0x0F, 0x01, 0x01, 0x01}, /* P */
{0x0E, 0x11, 0x11, 0x11, 0x15, 0x09, 0x16}, /* Q */
{0x0F, 0x11, 0x11, 0x0F, 0x05, 0x09, 0x11}, /* R */
{0x1E, 0x01, 0x01, 0x0E, 0x10, 0x10, 0x0F}, /* S */
{0x1F, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04}, /* T */
{0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x0E}, /* U */
{0x11, 0x11, 0x11, 0x11, 0x11, 0x0A, 0x04}, /* V */
{0x11, 0x11, 0x11, 0x15, 0x15, 0x15, 0x0A}, /* W */
{0x11, 0x11, 0x0A, 0x04, 0x0A, 0x11, 0x11}, /* X */
{0x11, 0x11, 0x0A, 0x04, 0x04, 0x04, 0x04}, /* Y */
{0x1F, 0x10, 0x08, 0x04, 0x02, 0x01, 0x1F}, /* Z */
};
static void tft_cs_low(void)
{
HAL_GPIO_WritePin(TFT_CS_GPIO_Port, TFT_CS_Pin, GPIO_PIN_RESET);
}
static void tft_cs_high(void)
{
HAL_GPIO_WritePin(TFT_CS_GPIO_Port, TFT_CS_Pin, GPIO_PIN_SET);
}
static void tft_dc_command(void)
{
HAL_GPIO_WritePin(TFT_DC_GPIO_Port, TFT_DC_Pin, GPIO_PIN_RESET);
}
static void tft_dc_data(void)
{
HAL_GPIO_WritePin(TFT_DC_GPIO_Port, TFT_DC_Pin, GPIO_PIN_SET);
}
static void tft_reset_low(void)
{
HAL_GPIO_WritePin(TFT_RES_GPIO_Port, TFT_RES_Pin, GPIO_PIN_RESET);
}
static void tft_reset_high(void)
{
HAL_GPIO_WritePin(TFT_RES_GPIO_Port, TFT_RES_Pin, GPIO_PIN_SET);
}
static void tft_backlight_on(void)
{
HAL_GPIO_WritePin(TFT_BLK_GPIO_Port, TFT_BLK_Pin, GPIO_PIN_SET);
}
static void tft_dma_finish(bool ok)
{
TFT_TransferDoneCallback callback = tft_dma_callback;
void *context = tft_dma_context;
if (!tft_dma_active)
{
return;
}
tft_dma_active = false;
tft_dma_callback = NULL;
tft_dma_context = NULL;
if (!ok)
{
tft_transfer_ok = false;
}
tft_cs_high();
if (callback != NULL)
{
callback(context, ok);
}
}
static void tft_bus_init(void)
{
#if TFT_USE_SOFT_SPI
GPIO_InitTypeDef GPIO_InitStruct = {0};
__HAL_RCC_GPIOB_CLK_ENABLE();
__HAL_RCC_GPIOD_CLK_ENABLE();
GPIO_InitStruct.Pin = TFT_SCK_Pin;
GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP;
GPIO_InitStruct.Pull = GPIO_NOPULL;
GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_LOW;
HAL_GPIO_Init(TFT_SCK_GPIO_Port, &GPIO_InitStruct);
GPIO_InitStruct.Pin = TFT_MOSI_Pin;
GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP;
GPIO_InitStruct.Pull = GPIO_NOPULL;
GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_LOW;
HAL_GPIO_Init(TFT_MOSI_GPIO_Port, &GPIO_InitStruct);
HAL_GPIO_WritePin(TFT_SCK_GPIO_Port, TFT_SCK_Pin, GPIO_PIN_SET);
HAL_GPIO_WritePin(TFT_MOSI_GPIO_Port, TFT_MOSI_Pin, GPIO_PIN_RESET);
#endif
}
#if TFT_USE_SOFT_SPI
static void tft_write_byte(uint8_t data)
{
for (uint8_t i = 0U; i < 8U; i++)
{
HAL_GPIO_WritePin(TFT_SCK_GPIO_Port, TFT_SCK_Pin, GPIO_PIN_RESET);
HAL_GPIO_WritePin(TFT_MOSI_GPIO_Port, TFT_MOSI_Pin,
(data & 0x80U) != 0U ? GPIO_PIN_SET : GPIO_PIN_RESET);
HAL_GPIO_WritePin(TFT_SCK_GPIO_Port, TFT_SCK_Pin, GPIO_PIN_SET);
data <<= 1U;
}
}
#endif
static void tft_write_bytes(const uint8_t *data, uint32_t len)
{
#if TFT_USE_SOFT_SPI
while (len > 0U)
{
tft_write_byte(*data);
data++;
len--;
}
#else
while (len > 0U)
{
#if TFT_HW_SPI_BYTE_MODE && TFT_HW_SPI_PULSE_CS
tft_cs_low();
if (HAL_SPI_Transmit(&TFT_SPI_UNIT, (uint8_t *) data, 1U, TFT_SPI_TIMEOUT_MS) != HAL_OK)
{
tft_transfer_ok = false;
tft_cs_high();
return;
}
tft_cs_high();
data++;
len--;
#else
#if TFT_HW_SPI_BYTE_MODE
uint16_t chunk = 1U;
#else
uint16_t chunk = len > UINT16_MAX ? UINT16_MAX : (uint16_t) len;
#endif
if (HAL_SPI_Transmit(&TFT_SPI_UNIT, (uint8_t *) data, chunk, TFT_SPI_TIMEOUT_MS) != HAL_OK)
{
tft_transfer_ok = false;
tft_cs_high();
return;
}
data += chunk;
len -= chunk;
#endif
}
#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();
tft_dc_command();
tft_write_bytes(&reg, 1U);
tft_cs_high();
tft_dc_data();
}
static void lcd_write_data8(uint8_t data)
{
tft_cs_low();
tft_dc_data();
tft_write_bytes(&data, 1U);
tft_cs_high();
}
static void lcd_write_reg_data(uint8_t reg, const uint8_t *data, uint32_t len)
{
tft_cs_low();
tft_dc_command();
tft_write_bytes(&reg, 1U);
if (data != NULL && len > 0U && tft_transfer_ok)
{
tft_dc_data();
tft_write_bytes(data, len);
}
tft_cs_high();
tft_dc_data();
}
static void tft_write_color_block(uint16_t color, uint32_t count)
{
uint8_t buf[64];
uint8_t high = (uint8_t) (color >> 8);
uint8_t low = (uint8_t) color;
for (uint32_t i = 0U; i < sizeof(buf); i += 2U)
{
buf[i] = high;
buf[i + 1U] = low;
}
tft_cs_low();
tft_dc_data();
while (count > 0U && tft_transfer_ok)
{
uint32_t pixels = count > (sizeof(buf) / 2U) ? (sizeof(buf) / 2U) : count;
tft_write_bytes(buf, pixels * 2U);
count -= pixels;
}
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_WriteColorDataDMA(const uint8_t *data,
uint32_t len,
TFT_TransferDoneCallback callback,
void *context)
{
if (data == NULL || len == 0U || len > UINT16_MAX || tft_dma_active || TFT_SPI_UNIT.hdmatx == NULL)
{
return false;
}
tft_cs_low();
tft_dc_data();
tft_dma_callback = callback;
tft_dma_context = context;
tft_dma_active = true;
if (HAL_SPI_Transmit_DMA(&TFT_SPI_UNIT, (uint8_t *) data, (uint16_t) len) != HAL_OK)
{
tft_dma_active = false;
tft_dma_callback = NULL;
tft_dma_context = NULL;
tft_transfer_ok = false;
tft_cs_high();
return false;
}
return true;
}
void HAL_SPI_TxCpltCallback(SPI_HandleTypeDef *hspi)
{
if (hspi == &TFT_SPI_UNIT)
{
tft_dma_finish(true);
}
}
void HAL_SPI_ErrorCallback(SPI_HandleTypeDef *hspi)
{
if (hspi == &TFT_SPI_UNIT)
{
tft_dma_finish(false);
}
}
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;
uint16_t xe = x2 + TFT_X_OFFSET;
uint16_t ys = y1 + TFT_Y_OFFSET;
uint16_t ye = y2 + TFT_Y_OFFSET;
uint8_t col_data[4] = {
(uint8_t) (xs >> 8),
(uint8_t) xs,
(uint8_t) (xe >> 8),
(uint8_t) xe
};
uint8_t row_data[4] = {
(uint8_t) (ys >> 8),
(uint8_t) ys,
(uint8_t) (ye >> 8),
(uint8_t) ye
};
lcd_write_reg_data(0x2A, col_data, sizeof(col_data));
lcd_write_reg_data(0x2B, row_data, sizeof(row_data));
lcd_write_reg_data(0x2C, NULL, 0U);
}
void TFT_FillRect(uint16_t x, uint16_t y, uint16_t width, uint16_t height, uint16_t color)
{
if (x >= TFT_LCD_W || y >= TFT_LCD_H || width == 0U || height == 0U)
{
return;
}
if ((uint32_t) x + width > TFT_LCD_W)
{
width = (uint16_t) (TFT_LCD_W - x);
}
if ((uint32_t) y + height > TFT_LCD_H)
{
height = (uint16_t) (TFT_LCD_H - y);
}
LCD_Address_Set(x, y, (uint16_t) (x + width - 1U), (uint16_t) (y + height - 1U));
tft_write_color_block(color, (uint32_t) width * height);
}
void LCD_Fill(uint16_t xsta, uint16_t ysta, uint16_t xend, uint16_t yend, uint16_t color)
{
if (xend <= xsta || yend <= ysta)
{
return;
}
TFT_FillRect(xsta, ysta, (uint16_t) (xend - xsta), (uint16_t) (yend - ysta), color);
}
void TFT_DrawPoint(uint16_t x, uint16_t y, uint16_t color)
{
TFT_FillRect(x, y, 1U, 1U, color);
}
static const uint8_t *tft_get_glyph(char ch)
{
if (ch >= 'a' && ch <= 'z')
{
ch = (char) (ch - ('a' - 'A'));
}
if (ch >= '0' && ch <= '9')
{
return tft_font5x7[TFT_GLYPH_0 + (uint8_t) (ch - '0')];
}
if (ch >= 'A' && ch <= 'Z')
{
return tft_font5x7[TFT_GLYPH_A + (uint8_t) (ch - 'A')];
}
switch (ch)
{
case '-':
return tft_font5x7[TFT_GLYPH_DASH];
case '.':
return tft_font5x7[TFT_GLYPH_DOT];
case ':':
return tft_font5x7[TFT_GLYPH_COLON];
default:
return tft_font5x7[TFT_GLYPH_SPACE];
}
}
static void tft_draw_char(uint16_t x, uint16_t y, char ch, uint16_t fc, uint16_t bc, uint8_t scale)
{
uint8_t line[(TFT_FONT_W * TFT_MAX_SCALE) * 2U];
const uint8_t *glyph;
uint16_t char_w;
uint16_t char_h;
if (scale == 0U)
{
scale = 1U;
}
if (scale > TFT_MAX_SCALE)
{
scale = TFT_MAX_SCALE;
}
char_w = TFT_FONT_W * scale;
char_h = TFT_FONT_H * scale;
if (x >= TFT_LCD_W || y >= TFT_LCD_H)
{
return;
}
glyph = tft_get_glyph(ch);
LCD_Address_Set(x, y, (uint16_t) (x + char_w - 1U), (uint16_t) (y + char_h - 1U));
tft_cs_low();
tft_dc_data();
for (uint8_t row = 0U; row < TFT_FONT_H; row++)
{
for (uint8_t repeat_y = 0U; repeat_y < scale; repeat_y++)
{
uint32_t idx = 0U;
for (uint8_t col = 0U; col < TFT_FONT_W; col++)
{
uint16_t color = (glyph[row] & (1U << col)) ? fc : bc;
for (uint8_t repeat_x = 0U; repeat_x < scale; repeat_x++)
{
line[idx++] = (uint8_t) (color >> 8);
line[idx++] = (uint8_t) color;
}
}
tft_write_bytes(line, idx);
}
}
tft_cs_high();
}
void TFT_DrawString(uint16_t x, uint16_t y, const char *text, uint16_t fc, uint16_t bc, uint8_t scale)
{
uint16_t cursor = x;
uint16_t advance;
if (text == NULL)
{
return;
}
if (scale == 0U)
{
scale = 1U;
}
if (scale > TFT_MAX_SCALE)
{
scale = TFT_MAX_SCALE;
}
advance = (TFT_FONT_W + 1U) * scale;
while (*text != '\0')
{
if (cursor + (TFT_FONT_W * scale) >= TFT_LCD_W)
{
break;
}
tft_draw_char(cursor, y, *text, fc, bc, scale);
cursor = (uint16_t) (cursor + advance);
text++;
}
}
void TFT_Clear(uint16_t color)
{
LCD_Fill(0U, 0U, TFT_LCD_W, TFT_LCD_H, color);
}
void LCD_Init(void)
{
if (tft_initialized)
{
return;
}
tft_transfer_ok = true;
tft_bus_init();
tft_cs_high();
tft_reset_low();
HAL_Delay(100);
tft_reset_high();
HAL_Delay(100);
tft_backlight_on();
HAL_Delay(100);
lcd_write_reg(0x11);
HAL_Delay(120);
lcd_write_reg(0x36);
#if TFT_USE_HORIZONTAL == 0U
lcd_write_data8(0x00);
#elif TFT_USE_HORIZONTAL == 1U
lcd_write_data8(0xC0);
#elif TFT_USE_HORIZONTAL == 2U
lcd_write_data8(0x70);
#else
lcd_write_data8(0xA0);
#endif
lcd_write_reg(0x3A);
lcd_write_data8(0x05);
lcd_write_reg(0xB2);
lcd_write_data8(0x0C);
lcd_write_data8(0x0C);
lcd_write_data8(0x00);
lcd_write_data8(0x33);
lcd_write_data8(0x33);
lcd_write_reg(0xB7);
lcd_write_data8(0x35);
lcd_write_reg(0xBB);
lcd_write_data8(0x32);
lcd_write_reg(0xC2);
lcd_write_data8(0x01);
lcd_write_reg(0xC3);
lcd_write_data8(0x15);
lcd_write_reg(0xC4);
lcd_write_data8(0x20);
lcd_write_reg(0xC6);
lcd_write_data8(0x0F);
lcd_write_reg(0xD0);
lcd_write_data8(0xA4);
lcd_write_data8(0xA1);
lcd_write_reg(0xE0);
lcd_write_data8(0xD0);
lcd_write_data8(0x08);
lcd_write_data8(0x0E);
lcd_write_data8(0x09);
lcd_write_data8(0x09);
lcd_write_data8(0x05);
lcd_write_data8(0x31);
lcd_write_data8(0x33);
lcd_write_data8(0x48);
lcd_write_data8(0x17);
lcd_write_data8(0x14);
lcd_write_data8(0x15);
lcd_write_data8(0x31);
lcd_write_data8(0x34);
lcd_write_reg(0xE1);
lcd_write_data8(0xD0);
lcd_write_data8(0x08);
lcd_write_data8(0x0E);
lcd_write_data8(0x09);
lcd_write_data8(0x09);
lcd_write_data8(0x15);
lcd_write_data8(0x31);
lcd_write_data8(0x33);
lcd_write_data8(0x48);
lcd_write_data8(0x17);
lcd_write_data8(0x14);
lcd_write_data8(0x15);
lcd_write_data8(0x31);
lcd_write_data8(0x34);
lcd_write_reg(0x21);
lcd_write_reg(0x29);
TFT_Clear(TFT_COLOR_BLACK);
if (tft_transfer_ok)
{
tft_initialized = true;
}
}
void TFT_Init(void)
{
LCD_Init();
}
void TFT_ShowBootScreen(void)
{
if (!tft_initialized)
{
TFT_Init();
if (!tft_initialized)
{
return;
}
}
tft_transfer_ok = true;
TFT_Clear(TFT_COLOR_BLACK);
TFT_DrawString(20U, 80U, "TronOneH7-Scaffold", TFT_COLOR_WHITE, TFT_COLOR_BLACK, 2U);
TFT_DrawString(107U, 130U, "Starting...", TFT_COLOR_WHITE, TFT_COLOR_BLACK, 1U);
}
const char *TFT_RobotModeText(RobotMode_t mode)
{
switch (mode)
{
case NORMAL_MODE:
return "NORMAL";
case SYS_ERROR_OCCURRED:
return "ERROR";
case REMOTE_NOT_CONNECTED:
return "REMOTE OFF";
case REMOTE_NOT_READY:
return "NOT READY";
case REMOTE_READY:
return "READY";
case REMOTE_PROTECT:
return "PROTECT";
case AUTO_SHOOTING_MODE:
return "AUTO SHOOT";
case IMU_CALIBERATION_MODE:
return "IMU CAL";
default:
return "UNKNOWN";
}
}
static uint16_t tft_mode_color(RobotMode_t mode)
{
switch (mode)
{
case NORMAL_MODE:
return TFT_COLOR_GREEN;
case SYS_ERROR_OCCURRED:
case REMOTE_NOT_CONNECTED:
return TFT_COLOR_RED;
case REMOTE_NOT_READY:
return TFT_COLOR_WHITE;
case REMOTE_READY:
return TFT_COLOR_MAGENTA;
case REMOTE_PROTECT:
return TFT_COLOR_YELLOW;
case AUTO_SHOOTING_MODE:
return TFT_COLOR_CYAN;
case IMU_CALIBERATION_MODE:
return TFT_COLOR_MAGENTA;
default:
return TFT_COLOR_LIGHT_GRAY;
}
}
static int16_t tft_temperature_to_tenths(float temperature_c)
{
return (int16_t) ((temperature_c * 10.0f) + (temperature_c >= 0.0f ? 0.5f : -0.5f));
}
static void tft_format_temperature(char *buf, size_t len, int16_t temperature_tenths)
{
uint16_t abs_temp;
uint16_t integer;
uint8_t decimal;
size_t idx = 0U;
char digits[5];
uint8_t digits_len = 0U;
if (buf == NULL || len < 6U)
{
return;
}
if (temperature_tenths < 0)
{
abs_temp = (uint16_t) (-temperature_tenths);
buf[idx++] = '-';
}
else
{
abs_temp = (uint16_t) temperature_tenths;
}
integer = abs_temp / 10U;
decimal = (uint8_t) (abs_temp % 10U);
do
{
digits[digits_len++] = (char) ('0' + (integer % 10U));
integer /= 10U;
} while (integer > 0U && digits_len < sizeof(digits));
while (digits_len > 0U && idx + 1U < len)
{
buf[idx++] = digits[--digits_len];
}
if (idx + 4U < len)
{
buf[idx++] = '.';
buf[idx++] = (char) ('0' + decimal);
buf[idx++] = ' ';
buf[idx++] = 'C';
}
buf[idx] = '\0';
}
static void tft_draw_status_layout(void)
{
TFT_Clear(TFT_COLOR_BLACK);
TFT_DrawString(16U, 16U, "DM LCD", TFT_COLOR_CYAN, TFT_COLOR_BLACK, 3U);
TFT_FillRect(16U, 54U, 248U, 2U, TFT_COLOR_DARK_GRAY);
TFT_DrawString(16U, 76U, "TEMP:", TFT_COLOR_YELLOW, TFT_COLOR_BLACK, 2U);
TFT_DrawString(16U, 132U, "MODE:", TFT_COLOR_YELLOW, TFT_COLOR_BLACK, 2U);
}
void TFT_ShowStatus(float temperature_c, RobotMode_t mode)
{
static bool layout_ready = false;
static int16_t last_temperature_tenths = INT16_MIN;
static RobotMode_t last_mode = (RobotMode_t) 0xFF;
int16_t temperature_tenths = tft_temperature_to_tenths(temperature_c);
char temperature_text[16];
const char *mode_text = TFT_RobotModeText(mode);
if (!tft_initialized)
{
TFT_Init();
if (!tft_initialized)
{
return;
}
}
if (!layout_ready)
{
tft_transfer_ok = true;
tft_draw_status_layout();
if (!tft_transfer_ok)
{
return;
}
layout_ready = true;
}
if (temperature_tenths != last_temperature_tenths)
{
tft_transfer_ok = true;
uint16_t temperature_color = temperature_tenths >= 550 ? TFT_COLOR_RED : TFT_COLOR_WHITE;
tft_format_temperature(temperature_text, sizeof(temperature_text), temperature_tenths);
TFT_FillRect(96U, 68U, 168U, 32U, TFT_COLOR_BLACK);
TFT_DrawString(100U, 70U, temperature_text, temperature_color, TFT_COLOR_BLACK, 3U);
if (tft_transfer_ok)
{
last_temperature_tenths = temperature_tenths;
}
}
if (mode != last_mode)
{
tft_transfer_ok = true;
TFT_FillRect(96U, 124U, 184U, 32U, TFT_COLOR_BLACK);
TFT_DrawString(100U, 126U, mode_text, tft_mode_color(mode), TFT_COLOR_BLACK, 3U);
if (tft_transfer_ok)
{
last_mode = mode;
}
}
}

View File

@@ -0,0 +1,110 @@
#ifndef TFT_H
#define TFT_H
#include "main.h"
#include "robot_def.h"
#include "spi.h"
#include <stdbool.h>
#include <stdint.h>
#ifdef __cplusplus
extern "C" {
#endif
typedef void (*TFT_TransferDoneCallback)(void *context, bool ok);
#define TFT_USE_HORIZONTAL 2U
#define TFT_USE_SOFT_SPI 0U
#define TFT_HW_SPI_BYTE_MODE 0U
#define TFT_HW_SPI_PULSE_CS 0U
#define TFT_HW_SPI_STREAM_COLOR 1U
#if (TFT_USE_HORIZONTAL == 0U) || (TFT_USE_HORIZONTAL == 1U)
#define TFT_LCD_W 240U
#define TFT_LCD_H 280U
#else
#define TFT_LCD_W 280U
#define TFT_LCD_H 240U
#endif
#ifndef TFT_SPI_UNIT
#define TFT_SPI_UNIT hspi1
#endif
#ifndef TFT_SCK_GPIO_Port
#define TFT_SCK_GPIO_Port GPIOB
#define TFT_SCK_Pin GPIO_PIN_3
#endif
#ifndef TFT_MOSI_GPIO_Port
#define TFT_MOSI_GPIO_Port GPIOD
#define TFT_MOSI_Pin GPIO_PIN_7
#endif
#if defined(LCD_CS_GPIO_Port) && defined(LCD_CS_Pin)
#define TFT_CS_GPIO_Port LCD_CS_GPIO_Port
#define TFT_CS_Pin LCD_CS_Pin
#else
#define TFT_CS_GPIO_Port cs2_GPIO_Port
#define TFT_CS_Pin cs2_Pin
#endif
#if defined(LCD_BLK_GPIO_Port) && defined(LCD_BLK_Pin)
#define TFT_BLK_GPIO_Port LCD_BLK_GPIO_Port
#define TFT_BLK_Pin LCD_BLK_Pin
#else
#define TFT_BLK_GPIO_Port cs1_GPIO_Port
#define TFT_BLK_Pin cs1_Pin
#endif
#if defined(LCD_RES_GPIO_Port) && defined(LCD_RES_Pin)
#define TFT_RES_GPIO_Port LCD_RES_GPIO_Port
#define TFT_RES_Pin LCD_RES_Pin
#else
#define TFT_RES_GPIO_Port cs4_GPIO_Port
#define TFT_RES_Pin cs4_Pin
#endif
#if defined(LCD_DC_GPIO_Port) && defined(LCD_DC_Pin)
#define TFT_DC_GPIO_Port LCD_DC_GPIO_Port
#define TFT_DC_Pin LCD_DC_Pin
#else
#define TFT_DC_GPIO_Port cs3_GPIO_Port
#define TFT_DC_Pin cs3_Pin
#endif
#define TFT_COLOR_WHITE 0xFFFFU
#define TFT_COLOR_BLACK 0x0000U
#define TFT_COLOR_BLUE 0x001FU
#define TFT_COLOR_RED 0xF800U
#define TFT_COLOR_GREEN 0x07E0U
#define TFT_COLOR_CYAN 0x7FFFU
#define TFT_COLOR_YELLOW 0xFFE0U
#define TFT_COLOR_MAGENTA 0xF81FU
#define TFT_COLOR_DARK_GRAY 0x4208U
#define TFT_COLOR_LIGHT_GRAY 0xC618U
void TFT_Init(void);
void TFT_ShowBootScreen(void);
void TFT_Clear(uint16_t color);
void TFT_FillRect(uint16_t x, uint16_t y, uint16_t width, uint16_t height, uint16_t color);
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);
bool TFT_WriteColorDataDMA(const uint8_t *data,
uint32_t len,
TFT_TransferDoneCallback callback,
void *context);
const char *TFT_RobotModeText(RobotMode_t mode);
void LCD_Init(void);
void LCD_Address_Set(uint16_t x1, uint16_t y1, uint16_t x2, uint16_t y2);
void LCD_Fill(uint16_t xsta, uint16_t ysta, uint16_t xend, uint16_t yend, uint16_t color);
#ifdef __cplusplus
}
#endif
#endif

View File

@@ -0,0 +1,366 @@
#include "w25q64.h"
#include <string.h>
static W25Q64_Handle_t w25q64 = {0};
static bool w25q64_attached = false;
static bool w25q64_ready = false;
static void w25q64_cs_low(void)
{
if (w25q64_attached)
{
HAL_GPIO_WritePin(w25q64.cs_port, w25q64.cs_pin, GPIO_PIN_RESET);
}
}
static void w25q64_cs_high(void)
{
if (w25q64_attached)
{
HAL_GPIO_WritePin(w25q64.cs_port, w25q64.cs_pin, GPIO_PIN_SET);
}
}
static W25Q64_Status_t w25q64_spi_tx(const uint8_t *data, uint16_t len)
{
if (!w25q64_attached || w25q64.hspi == NULL)
{
return W25Q64_ERR_NOT_ATTACHED;
}
if (HAL_SPI_Transmit(w25q64.hspi, (uint8_t *) data, len, w25q64.timeout_ms) != HAL_OK)
{
return W25Q64_ERR_HAL;
}
return W25Q64_OK;
}
static W25Q64_Status_t w25q64_spi_rx(uint8_t *data, uint16_t len)
{
if (!w25q64_attached || w25q64.hspi == NULL)
{
return W25Q64_ERR_NOT_ATTACHED;
}
if (HAL_SPI_Receive(w25q64.hspi, data, len, w25q64.timeout_ms) != HAL_OK)
{
return W25Q64_ERR_HAL;
}
return W25Q64_OK;
}
static W25Q64_Status_t w25q64_write_enable(void)
{
uint8_t cmd = W25Q64_CMD_WRITE_ENABLE;
w25q64_cs_low();
if (w25q64_spi_tx(&cmd, 1U) != W25Q64_OK)
{
w25q64_cs_high();
return W25Q64_ERR_HAL;
}
w25q64_cs_high();
return W25Q64_OK;
}
static W25Q64_Status_t w25q64_read_status(uint8_t *status)
{
uint8_t cmd = W25Q64_CMD_READ_STATUS_1;
if (status == NULL)
{
return W25Q64_ERR_PARAM;
}
w25q64_cs_low();
if (w25q64_spi_tx(&cmd, 1U) != W25Q64_OK)
{
w25q64_cs_high();
return W25Q64_ERR_HAL;
}
if (w25q64_spi_rx(status, 1U) != W25Q64_OK)
{
w25q64_cs_high();
return W25Q64_ERR_HAL;
}
w25q64_cs_high();
return W25Q64_OK;
}
static W25Q64_Status_t w25q64_wait_ready(uint32_t timeout_ms)
{
uint8_t status = 0;
uint32_t start = HAL_GetTick();
do
{
if (w25q64_read_status(&status) != W25Q64_OK)
{
return W25Q64_ERR_HAL;
}
if ((status & 0x01U) == 0U)
{
return W25Q64_OK;
}
} while ((HAL_GetTick() - start) < timeout_ms);
return W25Q64_ERR_BUSY;
}
static W25Q64_Status_t w25q64_command_addr(uint8_t cmd, uint32_t addr)
{
uint8_t header[4] = {
cmd,
(uint8_t) (addr >> 16),
(uint8_t) (addr >> 8),
(uint8_t) addr
};
w25q64_cs_low();
if (w25q64_spi_tx(header, sizeof(header)) != W25Q64_OK)
{
w25q64_cs_high();
return W25Q64_ERR_HAL;
}
return W25Q64_OK;
}
void W25Q64_Attach(W25Q64_Handle_t *handle)
{
if (handle == NULL)
{
W25Q64_Detach();
return;
}
w25q64 = *handle;
w25q64_attached = (w25q64.hspi != NULL) && (w25q64.cs_port != NULL);
w25q64.timeout_ms = (w25q64.timeout_ms == 0U) ? 100U : w25q64.timeout_ms;
if (w25q64_attached)
{
HAL_GPIO_WritePin(w25q64.cs_port, w25q64.cs_pin, GPIO_PIN_SET);
}
w25q64_ready = false;
}
void W25Q64_Detach(void)
{
memset(&w25q64, 0, sizeof(w25q64));
w25q64_attached = false;
w25q64_ready = false;
}
W25Q64_Status_t W25Q64_ReadID(uint32_t *jedec_id)
{
uint8_t cmd = W25Q64_CMD_READ_ID;
uint8_t id[3] = {0};
if (jedec_id == NULL)
{
return W25Q64_ERR_PARAM;
}
if (!w25q64_attached)
{
return W25Q64_ERR_NOT_ATTACHED;
}
w25q64_cs_low();
if (w25q64_spi_tx(&cmd, 1U) != W25Q64_OK)
{
w25q64_cs_high();
return W25Q64_ERR_HAL;
}
if (w25q64_spi_rx(id, 3U) != W25Q64_OK)
{
w25q64_cs_high();
return W25Q64_ERR_HAL;
}
w25q64_cs_high();
*jedec_id = ((uint32_t) id[0] << 16) | ((uint32_t) id[1] << 8) | id[2];
return W25Q64_OK;
}
W25Q64_Status_t W25Q64_Init(void)
{
uint32_t jedec_id = 0;
if (!w25q64_attached)
{
return W25Q64_ERR_NOT_ATTACHED;
}
if (W25Q64_ReadID(&jedec_id) != W25Q64_OK)
{
w25q64_ready = false;
return W25Q64_ERR_HAL;
}
w25q64_ready = (jedec_id == W25Q64_JEDEC_ID);
return w25q64_ready ? W25Q64_OK : W25Q64_ERR_ID;
}
W25Q64_Status_t W25Q64_Read(uint32_t addr, uint8_t *data, uint32_t len)
{
W25Q64_Status_t ret;
uint32_t offset = 0U;
if (data == NULL || len == 0U)
{
return W25Q64_ERR_PARAM;
}
if (!w25q64_ready)
{
return W25Q64_ERR_NOT_ATTACHED;
}
ret = w25q64_command_addr(W25Q64_CMD_READ_DATA, addr);
if (ret != W25Q64_OK)
{
return ret;
}
while (offset < len)
{
uint16_t chunk = (uint16_t) ((len - offset) > UINT16_MAX ? UINT16_MAX : (len - offset));
ret = w25q64_spi_rx(data + offset, chunk);
if (ret != W25Q64_OK)
{
w25q64_cs_high();
return ret;
}
offset += chunk;
}
w25q64_cs_high();
return W25Q64_OK;
}
static W25Q64_Status_t w25q64_page_program(uint32_t addr, const uint8_t *data, uint16_t len)
{
W25Q64_Status_t ret;
ret = w25q64_write_enable();
if (ret != W25Q64_OK)
{
return ret;
}
ret = w25q64_command_addr(W25Q64_CMD_PAGE_PROGRAM, addr);
if (ret != W25Q64_OK)
{
return ret;
}
if (w25q64_spi_tx(data, len) != W25Q64_OK)
{
w25q64_cs_high();
return W25Q64_ERR_HAL;
}
w25q64_cs_high();
return w25q64_wait_ready(w25q64.timeout_ms);
}
W25Q64_Status_t W25Q64_Write(uint32_t addr, const uint8_t *data, uint32_t len)
{
uint32_t offset = 0U;
if (data == NULL || len == 0U)
{
return W25Q64_ERR_PARAM;
}
if (!w25q64_ready)
{
return W25Q64_ERR_NOT_ATTACHED;
}
while (offset < len)
{
uint32_t page_offset = (addr + offset) % W25Q64_PAGE_SIZE;
uint32_t room = W25Q64_PAGE_SIZE - page_offset;
uint16_t chunk = (uint16_t) ((len - offset) < room ? (len - offset) : room);
W25Q64_Status_t ret = w25q64_page_program(addr + offset, data + offset, chunk);
if (ret != W25Q64_OK)
{
return ret;
}
offset += chunk;
}
return W25Q64_OK;
}
W25Q64_Status_t W25Q64_EraseSector(uint32_t addr)
{
W25Q64_Status_t ret;
if (!w25q64_ready)
{
return W25Q64_ERR_NOT_ATTACHED;
}
ret = w25q64_write_enable();
if (ret != W25Q64_OK)
{
return ret;
}
ret = w25q64_command_addr(W25Q64_CMD_SECTOR_ERASE, addr);
if (ret != W25Q64_OK)
{
return ret;
}
w25q64_cs_high();
return w25q64_wait_ready(400U);
}
W25Q64_Status_t W25Q64_Erase64K(uint32_t addr)
{
W25Q64_Status_t ret;
if (!w25q64_ready)
{
return W25Q64_ERR_NOT_ATTACHED;
}
ret = w25q64_write_enable();
if (ret != W25Q64_OK)
{
return ret;
}
ret = w25q64_command_addr(W25Q64_CMD_BLOCK_ERASE_64K, addr);
if (ret != W25Q64_OK)
{
return ret;
}
w25q64_cs_high();
return w25q64_wait_ready(2000U);
}
W25Q64_Status_t W25Q64_ChipErase(void)
{
uint8_t cmd = W25Q64_CMD_CHIP_ERASE;
if (!w25q64_ready)
{
return W25Q64_ERR_NOT_ATTACHED;
}
if (w25q64_write_enable() != W25Q64_OK)
{
return W25Q64_ERR_HAL;
}
w25q64_cs_low();
if (w25q64_spi_tx(&cmd, 1U) != W25Q64_OK)
{
w25q64_cs_high();
return W25Q64_ERR_HAL;
}
w25q64_cs_high();
return w25q64_wait_ready(100000U);
}

View File

@@ -0,0 +1,59 @@
#ifndef W25Q64_H
#define W25Q64_H
#include "main.h"
#include <stdbool.h>
#include <stdint.h>
#ifdef __cplusplus
extern "C" {
#endif
#define W25Q64_PAGE_SIZE 256U
#define W25Q64_SECTOR_SIZE 4096U
#define W25Q64_FLASH_SIZE 0x800000U
#define W25Q64_JEDEC_ID 0xEF4017U
#define W25Q64_CMD_WRITE_ENABLE 0x06U
#define W25Q64_CMD_READ_STATUS_1 0x05U
#define W25Q64_CMD_READ_ID 0x9FU
#define W25Q64_CMD_READ_DATA 0x03U
#define W25Q64_CMD_PAGE_PROGRAM 0x02U
#define W25Q64_CMD_SECTOR_ERASE 0x20U
#define W25Q64_CMD_BLOCK_ERASE_64K 0xD8U
#define W25Q64_CMD_CHIP_ERASE 0xC7U
typedef enum
{
W25Q64_OK = 0,
W25Q64_ERR_PARAM = -1,
W25Q64_ERR_NOT_ATTACHED = -2,
W25Q64_ERR_HAL = -3,
W25Q64_ERR_ID = -4,
W25Q64_ERR_BUSY = -5
} W25Q64_Status_t;
typedef struct
{
SPI_HandleTypeDef *hspi;
GPIO_TypeDef *cs_port;
uint16_t cs_pin;
uint32_t timeout_ms;
} W25Q64_Handle_t;
void W25Q64_Attach(W25Q64_Handle_t *handle);
void W25Q64_Detach(void);
W25Q64_Status_t W25Q64_Init(void);
W25Q64_Status_t W25Q64_ReadID(uint32_t *jedec_id);
W25Q64_Status_t W25Q64_Read(uint32_t addr, uint8_t *data, uint32_t len);
W25Q64_Status_t W25Q64_Write(uint32_t addr, const uint8_t *data, uint32_t len);
W25Q64_Status_t W25Q64_EraseSector(uint32_t addr);
W25Q64_Status_t W25Q64_Erase64K(uint32_t addr);
W25Q64_Status_t W25Q64_ChipErase(void);
#ifdef __cplusplus
}
#endif
#endif

View File

@@ -16,9 +16,74 @@
#include "ws2812.h"
#define WS2812_SPI_TIMEOUT 2U
#define WS2812_RESET_BYTES 100U
#define WS2812_LowLevel 0xC0 // 0码
#define WS2812_HighLevel 0xF0 // 1码
static SPI_HandleTypeDef ws2812_spi;
static uint8_t ws2812_spi_initialized = 0U;
static uint8_t WS2812_SPI_Init(void)
{
GPIO_InitTypeDef GPIO_InitStruct = {0};
RCC_PeriphCLKInitTypeDef PeriphClkInitStruct = {0};
if (ws2812_spi_initialized != 0U)
{
return 1U;
}
PeriphClkInitStruct.PeriphClockSelection = RCC_PERIPHCLK_SPI6;
PeriphClkInitStruct.Spi6ClockSelection = RCC_SPI6CLKSOURCE_HSE;
if (HAL_RCCEx_PeriphCLKConfig(&PeriphClkInitStruct) != HAL_OK)
{
return 0U;
}
__HAL_RCC_GPIOA_CLK_ENABLE();
__HAL_RCC_SPI6_CLK_ENABLE();
GPIO_InitStruct.Pin = GPIO_PIN_7;
GPIO_InitStruct.Mode = GPIO_MODE_AF_PP;
GPIO_InitStruct.Pull = GPIO_NOPULL;
GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_LOW;
GPIO_InitStruct.Alternate = GPIO_AF8_SPI6;
HAL_GPIO_Init(GPIOA, &GPIO_InitStruct);
ws2812_spi.Instance = SPI6;
ws2812_spi.Init.Mode = SPI_MODE_MASTER;
ws2812_spi.Init.Direction = SPI_DIRECTION_2LINES_TXONLY;
ws2812_spi.Init.DataSize = SPI_DATASIZE_8BIT;
ws2812_spi.Init.CLKPolarity = SPI_POLARITY_LOW;
ws2812_spi.Init.CLKPhase = SPI_PHASE_2EDGE;
ws2812_spi.Init.NSS = SPI_NSS_SOFT;
ws2812_spi.Init.BaudRatePrescaler = SPI_BAUDRATEPRESCALER_4;
ws2812_spi.Init.FirstBit = SPI_FIRSTBIT_MSB;
ws2812_spi.Init.TIMode = SPI_TIMODE_DISABLE;
ws2812_spi.Init.CRCCalculation = SPI_CRCCALCULATION_DISABLE;
ws2812_spi.Init.CRCPolynomial = 0x0;
ws2812_spi.Init.NSSPMode = SPI_NSS_PULSE_ENABLE;
ws2812_spi.Init.NSSPolarity = SPI_NSS_POLARITY_LOW;
ws2812_spi.Init.FifoThreshold = SPI_FIFO_THRESHOLD_01DATA;
ws2812_spi.Init.TxCRCInitializationPattern = SPI_CRC_INITIALIZATION_ALL_ZERO_PATTERN;
ws2812_spi.Init.RxCRCInitializationPattern = SPI_CRC_INITIALIZATION_ALL_ZERO_PATTERN;
ws2812_spi.Init.MasterSSIdleness = SPI_MASTER_SS_IDLENESS_00CYCLE;
ws2812_spi.Init.MasterInterDataIdleness = SPI_MASTER_INTERDATA_IDLENESS_00CYCLE;
ws2812_spi.Init.MasterReceiverAutoSusp = SPI_MASTER_RX_AUTOSUSP_DISABLE;
ws2812_spi.Init.MasterKeepIOState = SPI_MASTER_KEEP_IO_STATE_DISABLE;
ws2812_spi.Init.IOSwap = SPI_IO_SWAP_DISABLE;
if (HAL_SPI_Init(&ws2812_spi) != HAL_OK)
{
return 0U;
}
ws2812_spi_initialized = 1U;
return 1U;
}
/**
* @brief
* @param [in] r My Param doc
@@ -29,17 +94,28 @@ void WS2812_Ctrl(uint8_t r, uint8_t g, uint8_t b)
{
uint8_t txbuf[24];
uint8_t res = 0;
for (auto i = 0; i < 8; i++)
if (WS2812_SPI_Init() == 0U)
{
return;
}
for (uint8_t i = 0; i < 8U; i++)
{
txbuf[7 - i] = (((g >> i) & 0x01) ? WS2812_HighLevel : WS2812_LowLevel) >> 1;
txbuf[15 - i] = (((r >> i) & 0x01) ? WS2812_HighLevel : WS2812_LowLevel) >> 1;
txbuf[23 - i] = (((b >> i) & 0x01) ? WS2812_HighLevel : WS2812_LowLevel) >> 1;
}
HAL_SPI_Transmit(&WS2812_SPI_UNIT, &res, 0, 0xFFFF);
while (WS2812_SPI_UNIT.State != HAL_SPI_STATE_READY);
HAL_SPI_Transmit(&WS2812_SPI_UNIT, txbuf, 24, 0xFFFF);
for (auto i = 0; i < 100; i++)
if (HAL_SPI_Transmit(&ws2812_spi, txbuf, sizeof(txbuf), WS2812_SPI_TIMEOUT) != HAL_OK)
{
HAL_SPI_Transmit(&WS2812_SPI_UNIT, &res, 1, 0xFFFF);
return;
}
for (uint8_t i = 0; i < WS2812_RESET_BYTES; i++)
{
if (HAL_SPI_Transmit(&ws2812_spi, &res, 1U, WS2812_SPI_TIMEOUT) != HAL_OK)
{
return;
}
}
}

View File

@@ -3,8 +3,5 @@
#include "main.h"
#define WS2812_SPI_UNIT hspi6
extern SPI_HandleTypeDef WS2812_SPI_UNIT;
void WS2812_Ctrl(uint8_t r, uint8_t g, uint8_t b);
#endif

File diff suppressed because it is too large Load Diff

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*/

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