mirror of
https://github.com/esphome/esphome.git
synced 2024-11-21 22:48:10 +01:00
[lvgl] Implement better software rotation (#7595)
This commit is contained in:
parent
b274d6901a
commit
6a86d92781
7 changed files with 201 additions and 58 deletions
|
@ -48,6 +48,7 @@ from .types import (
|
||||||
FontEngine,
|
FontEngine,
|
||||||
IdleTrigger,
|
IdleTrigger,
|
||||||
ObjUpdateAction,
|
ObjUpdateAction,
|
||||||
|
PauseTrigger,
|
||||||
lv_font_t,
|
lv_font_t,
|
||||||
lv_group_t,
|
lv_group_t,
|
||||||
lv_style_t,
|
lv_style_t,
|
||||||
|
@ -233,6 +234,8 @@ async def to_code(config):
|
||||||
frac = 8
|
frac = 8
|
||||||
cg.add(lv_component.set_buffer_frac(int(frac)))
|
cg.add(lv_component.set_buffer_frac(int(frac)))
|
||||||
cg.add(lv_component.set_full_refresh(config[df.CONF_FULL_REFRESH]))
|
cg.add(lv_component.set_full_refresh(config[df.CONF_FULL_REFRESH]))
|
||||||
|
cg.add(lv_component.set_draw_rounding(config[df.CONF_DRAW_ROUNDING]))
|
||||||
|
cg.add(lv_component.set_resume_on_input(config[df.CONF_RESUME_ON_INPUT]))
|
||||||
|
|
||||||
for font in helpers.esphome_fonts_used:
|
for font in helpers.esphome_fonts_used:
|
||||||
await cg.get_variable(font)
|
await cg.get_variable(font)
|
||||||
|
@ -272,11 +275,19 @@ async def to_code(config):
|
||||||
async with LvContext(lv_component):
|
async with LvContext(lv_component):
|
||||||
await generate_triggers(lv_component)
|
await generate_triggers(lv_component)
|
||||||
await generate_page_triggers(lv_component, config)
|
await generate_page_triggers(lv_component, config)
|
||||||
|
await initial_focus_to_code(config)
|
||||||
for conf in config.get(CONF_ON_IDLE, ()):
|
for conf in config.get(CONF_ON_IDLE, ()):
|
||||||
templ = await cg.templatable(conf[CONF_TIMEOUT], [], cg.uint32)
|
templ = await cg.templatable(conf[CONF_TIMEOUT], [], cg.uint32)
|
||||||
idle_trigger = cg.new_Pvariable(conf[CONF_TRIGGER_ID], lv_component, templ)
|
idle_trigger = cg.new_Pvariable(conf[CONF_TRIGGER_ID], lv_component, templ)
|
||||||
await build_automation(idle_trigger, [], conf)
|
await build_automation(idle_trigger, [], conf)
|
||||||
await initial_focus_to_code(config)
|
for conf in config.get(df.CONF_ON_PAUSE, ()):
|
||||||
|
pause_trigger = cg.new_Pvariable(conf[CONF_TRIGGER_ID], lv_component, True)
|
||||||
|
await build_automation(pause_trigger, [], conf)
|
||||||
|
for conf in config.get(df.CONF_ON_RESUME, ()):
|
||||||
|
resume_trigger = cg.new_Pvariable(
|
||||||
|
conf[CONF_TRIGGER_ID], lv_component, False
|
||||||
|
)
|
||||||
|
await build_automation(resume_trigger, [], conf)
|
||||||
|
|
||||||
for comp in helpers.lvgl_components_required:
|
for comp in helpers.lvgl_components_required:
|
||||||
CORE.add_define(f"USE_LVGL_{comp.upper()}")
|
CORE.add_define(f"USE_LVGL_{comp.upper()}")
|
||||||
|
@ -314,6 +325,7 @@ CONFIG_SCHEMA = (
|
||||||
cv.Optional(df.CONF_COLOR_DEPTH, default=16): cv.one_of(16),
|
cv.Optional(df.CONF_COLOR_DEPTH, default=16): cv.one_of(16),
|
||||||
cv.Optional(df.CONF_DEFAULT_FONT, default="montserrat_14"): lvalid.lv_font,
|
cv.Optional(df.CONF_DEFAULT_FONT, default="montserrat_14"): lvalid.lv_font,
|
||||||
cv.Optional(df.CONF_FULL_REFRESH, default=False): cv.boolean,
|
cv.Optional(df.CONF_FULL_REFRESH, default=False): cv.boolean,
|
||||||
|
cv.Optional(df.CONF_DRAW_ROUNDING, default=2): cv.positive_int,
|
||||||
cv.Optional(CONF_BUFFER_SIZE, default="100%"): cv.percentage,
|
cv.Optional(CONF_BUFFER_SIZE, default="100%"): cv.percentage,
|
||||||
cv.Optional(df.CONF_LOG_LEVEL, default="WARN"): cv.one_of(
|
cv.Optional(df.CONF_LOG_LEVEL, default="WARN"): cv.one_of(
|
||||||
*df.LOG_LEVELS, upper=True
|
*df.LOG_LEVELS, upper=True
|
||||||
|
@ -341,6 +353,16 @@ CONFIG_SCHEMA = (
|
||||||
),
|
),
|
||||||
}
|
}
|
||||||
),
|
),
|
||||||
|
cv.Optional(df.CONF_ON_PAUSE): validate_automation(
|
||||||
|
{
|
||||||
|
cv.GenerateID(CONF_TRIGGER_ID): cv.declare_id(PauseTrigger),
|
||||||
|
}
|
||||||
|
),
|
||||||
|
cv.Optional(df.CONF_ON_RESUME): validate_automation(
|
||||||
|
{
|
||||||
|
cv.GenerateID(CONF_TRIGGER_ID): cv.declare_id(PauseTrigger),
|
||||||
|
}
|
||||||
|
),
|
||||||
cv.Exclusive(df.CONF_WIDGETS, CONF_PAGES): cv.ensure_list(WIDGET_SCHEMA),
|
cv.Exclusive(df.CONF_WIDGETS, CONF_PAGES): cv.ensure_list(WIDGET_SCHEMA),
|
||||||
cv.Exclusive(CONF_PAGES, CONF_PAGES): cv.ensure_list(
|
cv.Exclusive(CONF_PAGES, CONF_PAGES): cv.ensure_list(
|
||||||
container_schema(page_spec)
|
container_schema(page_spec)
|
||||||
|
@ -356,6 +378,7 @@ CONFIG_SCHEMA = (
|
||||||
cv.Optional(df.CONF_TOUCHSCREENS, default=None): touchscreen_schema,
|
cv.Optional(df.CONF_TOUCHSCREENS, default=None): touchscreen_schema,
|
||||||
cv.Optional(df.CONF_ENCODERS, default=None): ENCODERS_CONFIG,
|
cv.Optional(df.CONF_ENCODERS, default=None): ENCODERS_CONFIG,
|
||||||
cv.GenerateID(df.CONF_DEFAULT_GROUP): cv.declare_id(lv_group_t),
|
cv.GenerateID(df.CONF_DEFAULT_GROUP): cv.declare_id(lv_group_t),
|
||||||
|
cv.Optional(df.CONF_RESUME_ON_INPUT, default=True): cv.boolean,
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
.extend(DISP_BG_SCHEMA)
|
.extend(DISP_BG_SCHEMA)
|
||||||
|
|
|
@ -408,6 +408,7 @@ CONF_DEFAULT_FONT = "default_font"
|
||||||
CONF_DEFAULT_GROUP = "default_group"
|
CONF_DEFAULT_GROUP = "default_group"
|
||||||
CONF_DIR = "dir"
|
CONF_DIR = "dir"
|
||||||
CONF_DISPLAYS = "displays"
|
CONF_DISPLAYS = "displays"
|
||||||
|
CONF_DRAW_ROUNDING = "draw_rounding"
|
||||||
CONF_EDITING = "editing"
|
CONF_EDITING = "editing"
|
||||||
CONF_ENCODERS = "encoders"
|
CONF_ENCODERS = "encoders"
|
||||||
CONF_END_ANGLE = "end_angle"
|
CONF_END_ANGLE = "end_angle"
|
||||||
|
@ -451,6 +452,8 @@ CONF_OFFSET_X = "offset_x"
|
||||||
CONF_OFFSET_Y = "offset_y"
|
CONF_OFFSET_Y = "offset_y"
|
||||||
CONF_ONE_CHECKED = "one_checked"
|
CONF_ONE_CHECKED = "one_checked"
|
||||||
CONF_ONE_LINE = "one_line"
|
CONF_ONE_LINE = "one_line"
|
||||||
|
CONF_ON_PAUSE = "on_pause"
|
||||||
|
CONF_ON_RESUME = "on_resume"
|
||||||
CONF_ON_SELECT = "on_select"
|
CONF_ON_SELECT = "on_select"
|
||||||
CONF_OPA = "opa"
|
CONF_OPA = "opa"
|
||||||
CONF_NEXT = "next"
|
CONF_NEXT = "next"
|
||||||
|
@ -466,6 +469,7 @@ CONF_POINTS = "points"
|
||||||
CONF_PREVIOUS = "previous"
|
CONF_PREVIOUS = "previous"
|
||||||
CONF_REPEAT_COUNT = "repeat_count"
|
CONF_REPEAT_COUNT = "repeat_count"
|
||||||
CONF_RECOLOR = "recolor"
|
CONF_RECOLOR = "recolor"
|
||||||
|
CONF_RESUME_ON_INPUT = "resume_on_input"
|
||||||
CONF_RIGHT_BUTTON = "right_button"
|
CONF_RIGHT_BUTTON = "right_button"
|
||||||
CONF_ROLLOVER = "rollover"
|
CONF_ROLLOVER = "rollover"
|
||||||
CONF_ROOT_BACK_BTN = "root_back_btn"
|
CONF_ROOT_BACK_BTN = "root_back_btn"
|
||||||
|
|
|
@ -69,30 +69,38 @@ std::string lv_event_code_name_for(uint8_t event_code) {
|
||||||
}
|
}
|
||||||
return str_sprintf("%2d", event_code);
|
return str_sprintf("%2d", event_code);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void rounder_cb(lv_disp_drv_t *disp_drv, lv_area_t *area) {
|
static void rounder_cb(lv_disp_drv_t *disp_drv, lv_area_t *area) {
|
||||||
// make sure all coordinates are even
|
// cater for display driver chips with special requirements for bounds of partial
|
||||||
if (area->x1 & 1)
|
// draw areas. Extend the draw area to satisfy:
|
||||||
area->x1--;
|
// * Coordinates must be a multiple of draw_rounding
|
||||||
if (!(area->x2 & 1))
|
auto *comp = static_cast<LvglComponent *>(disp_drv->user_data);
|
||||||
area->x2++;
|
auto draw_rounding = comp->draw_rounding;
|
||||||
if (area->y1 & 1)
|
// round down the start coordinates
|
||||||
area->y1--;
|
area->x1 = area->x1 / draw_rounding * draw_rounding;
|
||||||
if (!(area->y2 & 1))
|
area->y1 = area->y1 / draw_rounding * draw_rounding;
|
||||||
area->y2++;
|
// round up the end coordinates
|
||||||
|
area->x2 = (area->x2 + draw_rounding) / draw_rounding * draw_rounding - 1;
|
||||||
|
area->y2 = (area->y2 + draw_rounding) / draw_rounding * draw_rounding - 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
lv_event_code_t lv_api_event; // NOLINT
|
lv_event_code_t lv_api_event; // NOLINT
|
||||||
lv_event_code_t lv_update_event; // NOLINT
|
lv_event_code_t lv_update_event; // NOLINT
|
||||||
void LvglComponent::dump_config() { ESP_LOGCONFIG(TAG, "LVGL:"); }
|
void LvglComponent::dump_config() {
|
||||||
|
ESP_LOGCONFIG(TAG, "LVGL:");
|
||||||
|
ESP_LOGCONFIG(TAG, " Rotation: %d", this->rotation);
|
||||||
|
ESP_LOGCONFIG(TAG, " Draw rounding: %d", (int) this->draw_rounding);
|
||||||
|
}
|
||||||
void LvglComponent::set_paused(bool paused, bool show_snow) {
|
void LvglComponent::set_paused(bool paused, bool show_snow) {
|
||||||
this->paused_ = paused;
|
this->paused_ = paused;
|
||||||
this->show_snow_ = show_snow;
|
this->show_snow_ = show_snow;
|
||||||
this->snow_line_ = 0;
|
|
||||||
if (!paused && lv_scr_act() != nullptr) {
|
if (!paused && lv_scr_act() != nullptr) {
|
||||||
lv_disp_trig_activity(this->disp_); // resets the inactivity time
|
lv_disp_trig_activity(this->disp_); // resets the inactivity time
|
||||||
lv_obj_invalidate(lv_scr_act());
|
lv_obj_invalidate(lv_scr_act());
|
||||||
}
|
}
|
||||||
|
this->pause_callbacks_.call(paused);
|
||||||
}
|
}
|
||||||
|
|
||||||
void LvglComponent::add_event_cb(lv_obj_t *obj, event_callback_t callback, lv_event_code_t event) {
|
void LvglComponent::add_event_cb(lv_obj_t *obj, event_callback_t callback, lv_event_code_t event) {
|
||||||
lv_obj_add_event_cb(obj, callback, event, this);
|
lv_obj_add_event_cb(obj, callback, event, this);
|
||||||
}
|
}
|
||||||
|
@ -133,19 +141,64 @@ void LvglComponent::show_prev_page(lv_scr_load_anim_t anim, uint32_t time) {
|
||||||
} while (this->pages_[this->current_page_]->skip); // skip empty pages()
|
} while (this->pages_[this->current_page_]->skip); // skip empty pages()
|
||||||
this->show_page(this->current_page_, anim, time);
|
this->show_page(this->current_page_, anim, time);
|
||||||
}
|
}
|
||||||
void LvglComponent::draw_buffer_(const lv_area_t *area, const uint8_t *ptr) {
|
void LvglComponent::draw_buffer_(const lv_area_t *area, lv_color_t *ptr) {
|
||||||
|
auto width = lv_area_get_width(area);
|
||||||
|
auto height = lv_area_get_height(area);
|
||||||
|
auto x1 = area->x1;
|
||||||
|
auto y1 = area->y1;
|
||||||
|
lv_color_t *dst = this->rotate_buf_;
|
||||||
|
switch (this->rotation) {
|
||||||
|
case display::DISPLAY_ROTATION_90_DEGREES:
|
||||||
|
for (lv_coord_t x = height - 1; x-- != 0;) {
|
||||||
|
for (lv_coord_t y = 0; y != width; y++) {
|
||||||
|
dst[y * height + x] = *ptr++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
y1 = x1;
|
||||||
|
x1 = this->disp_drv_.ver_res - area->y1 - height;
|
||||||
|
width = height;
|
||||||
|
height = lv_area_get_width(area);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case display::DISPLAY_ROTATION_180_DEGREES:
|
||||||
|
for (lv_coord_t y = height; y-- != 0;) {
|
||||||
|
for (lv_coord_t x = width; x-- != 0;) {
|
||||||
|
dst[y * width + x] = *ptr++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
x1 = this->disp_drv_.hor_res - x1 - width;
|
||||||
|
y1 = this->disp_drv_.ver_res - y1 - height;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case display::DISPLAY_ROTATION_270_DEGREES:
|
||||||
|
for (lv_coord_t x = 0; x != height; x++) {
|
||||||
|
for (lv_coord_t y = width; y-- != 0;) {
|
||||||
|
dst[y * height + x] = *ptr++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
x1 = y1;
|
||||||
|
y1 = this->disp_drv_.hor_res - area->x1 - width;
|
||||||
|
width = height;
|
||||||
|
height = lv_area_get_width(area);
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
dst = ptr;
|
||||||
|
break;
|
||||||
|
}
|
||||||
for (auto *display : this->displays_) {
|
for (auto *display : this->displays_) {
|
||||||
display->draw_pixels_at(area->x1, area->y1, lv_area_get_width(area), lv_area_get_height(area), ptr,
|
ESP_LOGV(TAG, "draw buffer x1=%d, y1=%d, width=%d, height=%d", x1, y1, width, height);
|
||||||
display::COLOR_ORDER_RGB, LV_BITNESS, LV_COLOR_16_SWAP);
|
display->draw_pixels_at(x1, y1, width, height, (const uint8_t *) dst, display::COLOR_ORDER_RGB, LV_BITNESS,
|
||||||
|
LV_COLOR_16_SWAP);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void LvglComponent::flush_cb_(lv_disp_drv_t *disp_drv, const lv_area_t *area, lv_color_t *color_p) {
|
void LvglComponent::flush_cb_(lv_disp_drv_t *disp_drv, const lv_area_t *area, lv_color_t *color_p) {
|
||||||
if (!this->paused_) {
|
if (!this->paused_) {
|
||||||
auto now = millis();
|
auto now = millis();
|
||||||
this->draw_buffer_(area, (const uint8_t *) color_p);
|
this->draw_buffer_(area, color_p);
|
||||||
ESP_LOGV(TAG, "flush_cb, area=%d/%d, %d/%d took %dms", area->x1, area->y1, lv_area_get_width(area),
|
ESP_LOGVV(TAG, "flush_cb, area=%d/%d, %d/%d took %dms", area->x1, area->y1, lv_area_get_width(area),
|
||||||
lv_area_get_height(area), (int) (millis() - now));
|
lv_area_get_height(area), (int) (millis() - now));
|
||||||
}
|
}
|
||||||
lv_disp_flush_ready(disp_drv);
|
lv_disp_flush_ready(disp_drv);
|
||||||
}
|
}
|
||||||
|
@ -160,6 +213,13 @@ IdleTrigger::IdleTrigger(LvglComponent *parent, TemplatableValue<uint32_t> timeo
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
PauseTrigger::PauseTrigger(LvglComponent *parent, TemplatableValue<bool> paused) : paused_(std::move(paused)) {
|
||||||
|
parent->add_on_pause_callback([this](bool pausing) {
|
||||||
|
if (this->paused_.value() == pausing)
|
||||||
|
this->trigger();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
#ifdef USE_LVGL_TOUCHSCREEN
|
#ifdef USE_LVGL_TOUCHSCREEN
|
||||||
LVTouchListener::LVTouchListener(uint16_t long_press_time, uint16_t long_press_repeat_time) {
|
LVTouchListener::LVTouchListener(uint16_t long_press_time, uint16_t long_press_repeat_time) {
|
||||||
lv_indev_drv_init(&this->drv_);
|
lv_indev_drv_init(&this->drv_);
|
||||||
|
@ -261,23 +321,31 @@ void LvKeyboardType::set_obj(lv_obj_t *lv_obj) {
|
||||||
#endif // USE_LVGL_KEYBOARD
|
#endif // USE_LVGL_KEYBOARD
|
||||||
|
|
||||||
void LvglComponent::write_random_() {
|
void LvglComponent::write_random_() {
|
||||||
// length of 2 lines in 32 bit units
|
int iterations = 6 - lv_disp_get_inactive_time(this->disp_) / 60000;
|
||||||
// we write 2 lines for the benefit of displays that won't write one line at a time.
|
if (iterations <= 0)
|
||||||
size_t line_len = this->disp_drv_.hor_res * LV_COLOR_DEPTH / 8 / 4 * 2;
|
iterations = 1;
|
||||||
for (size_t i = 0; i != line_len; i++) {
|
while (iterations-- != 0) {
|
||||||
((uint32_t *) (this->draw_buf_.buf1))[i] = random_uint32();
|
auto col = random_uint32() % this->disp_drv_.hor_res;
|
||||||
|
col = col / this->draw_rounding * this->draw_rounding;
|
||||||
|
auto row = random_uint32() % this->disp_drv_.ver_res;
|
||||||
|
row = row / this->draw_rounding * this->draw_rounding;
|
||||||
|
auto size = (random_uint32() % 32) / this->draw_rounding * this->draw_rounding - 1;
|
||||||
|
lv_area_t area;
|
||||||
|
area.x1 = col;
|
||||||
|
area.y1 = row;
|
||||||
|
area.x2 = col + size;
|
||||||
|
area.y2 = row + size;
|
||||||
|
if (area.x2 >= this->disp_drv_.hor_res)
|
||||||
|
area.x2 = this->disp_drv_.hor_res - 1;
|
||||||
|
if (area.y2 >= this->disp_drv_.ver_res)
|
||||||
|
area.y2 = this->disp_drv_.ver_res - 1;
|
||||||
|
|
||||||
|
size_t line_len = lv_area_get_width(&area) * lv_area_get_height(&area) / 2;
|
||||||
|
for (size_t i = 0; i != line_len; i++) {
|
||||||
|
((uint32_t *) (this->draw_buf_.buf1))[i] = random_uint32();
|
||||||
|
}
|
||||||
|
this->draw_buffer_(&area, (lv_color_t *) this->draw_buf_.buf1);
|
||||||
}
|
}
|
||||||
lv_area_t area;
|
|
||||||
area.x1 = 0;
|
|
||||||
area.x2 = this->disp_drv_.hor_res - 1;
|
|
||||||
if (this->snow_line_ == this->disp_drv_.ver_res / 2) {
|
|
||||||
area.y1 = static_cast<lv_coord_t>(random_uint32() % (this->disp_drv_.ver_res / 2) * 2);
|
|
||||||
} else {
|
|
||||||
area.y1 = this->snow_line_++ * 2;
|
|
||||||
}
|
|
||||||
// write 2 lines
|
|
||||||
area.y2 = area.y1 + 1;
|
|
||||||
this->draw_buffer_(&area, (const uint8_t *) this->draw_buf_.buf1);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void LvglComponent::setup() {
|
void LvglComponent::setup() {
|
||||||
|
@ -291,7 +359,7 @@ void LvglComponent::setup() {
|
||||||
auto *display = this->displays_[0];
|
auto *display = this->displays_[0];
|
||||||
size_t buffer_pixels = display->get_width() * display->get_height() / this->buffer_frac_;
|
size_t buffer_pixels = display->get_width() * display->get_height() / this->buffer_frac_;
|
||||||
auto buf_bytes = buffer_pixels * LV_COLOR_DEPTH / 8;
|
auto buf_bytes = buffer_pixels * LV_COLOR_DEPTH / 8;
|
||||||
auto *buf = lv_custom_mem_alloc(buf_bytes);
|
auto *buf = lv_custom_mem_alloc(buf_bytes); // NOLINT
|
||||||
if (buf == nullptr) {
|
if (buf == nullptr) {
|
||||||
#if ESPHOME_LOG_LEVEL >= ESPHOME_LOG_LEVEL_ERROR
|
#if ESPHOME_LOG_LEVEL >= ESPHOME_LOG_LEVEL_ERROR
|
||||||
ESP_LOGE(TAG, "Malloc failed to allocate %zu bytes", buf_bytes);
|
ESP_LOGE(TAG, "Malloc failed to allocate %zu bytes", buf_bytes);
|
||||||
|
@ -307,26 +375,30 @@ void LvglComponent::setup() {
|
||||||
this->disp_drv_.full_refresh = this->full_refresh_;
|
this->disp_drv_.full_refresh = this->full_refresh_;
|
||||||
this->disp_drv_.flush_cb = static_flush_cb;
|
this->disp_drv_.flush_cb = static_flush_cb;
|
||||||
this->disp_drv_.rounder_cb = rounder_cb;
|
this->disp_drv_.rounder_cb = rounder_cb;
|
||||||
switch (display->get_rotation()) {
|
this->rotation = display->get_rotation();
|
||||||
case display::DISPLAY_ROTATION_0_DEGREES:
|
if (this->rotation != display::DISPLAY_ROTATION_0_DEGREES) {
|
||||||
break;
|
this->rotate_buf_ = static_cast<lv_color_t *>(lv_custom_mem_alloc(buf_bytes)); // NOLINT
|
||||||
case display::DISPLAY_ROTATION_90_DEGREES:
|
if (this->rotate_buf_ == nullptr) {
|
||||||
this->disp_drv_.sw_rotate = true;
|
#if ESPHOME_LOG_LEVEL >= ESPHOME_LOG_LEVEL_ERROR
|
||||||
this->disp_drv_.rotated = LV_DISP_ROT_90;
|
ESP_LOGE(TAG, "Malloc failed to allocate %zu bytes", buf_bytes);
|
||||||
break;
|
#endif
|
||||||
case display::DISPLAY_ROTATION_180_DEGREES:
|
this->mark_failed();
|
||||||
this->disp_drv_.sw_rotate = true;
|
this->status_set_error("Memory allocation failure");
|
||||||
this->disp_drv_.rotated = LV_DISP_ROT_180;
|
return;
|
||||||
break;
|
}
|
||||||
case display::DISPLAY_ROTATION_270_DEGREES:
|
|
||||||
this->disp_drv_.sw_rotate = true;
|
|
||||||
this->disp_drv_.rotated = LV_DISP_ROT_270;
|
|
||||||
break;
|
|
||||||
}
|
}
|
||||||
display->set_rotation(display::DISPLAY_ROTATION_0_DEGREES);
|
display->set_rotation(display::DISPLAY_ROTATION_0_DEGREES);
|
||||||
this->disp_drv_.hor_res = (lv_coord_t) display->get_width();
|
switch (this->rotation) {
|
||||||
this->disp_drv_.ver_res = (lv_coord_t) display->get_height();
|
default:
|
||||||
ESP_LOGV(TAG, "sw_rotate = %d, rotated=%d", this->disp_drv_.sw_rotate, this->disp_drv_.rotated);
|
this->disp_drv_.hor_res = (lv_coord_t) display->get_width();
|
||||||
|
this->disp_drv_.ver_res = (lv_coord_t) display->get_height();
|
||||||
|
break;
|
||||||
|
case display::DISPLAY_ROTATION_90_DEGREES:
|
||||||
|
case display::DISPLAY_ROTATION_270_DEGREES:
|
||||||
|
this->disp_drv_.ver_res = (lv_coord_t) display->get_width();
|
||||||
|
this->disp_drv_.hor_res = (lv_coord_t) display->get_height();
|
||||||
|
break;
|
||||||
|
}
|
||||||
this->disp_ = lv_disp_drv_register(&this->disp_drv_);
|
this->disp_ = lv_disp_drv_register(&this->disp_drv_);
|
||||||
for (const auto &v : this->init_lambdas_)
|
for (const auto &v : this->init_lambdas_)
|
||||||
v(this);
|
v(this);
|
||||||
|
|
|
@ -119,6 +119,7 @@ class LvglComponent : public PollingComponent {
|
||||||
void add_on_idle_callback(std::function<void(uint32_t)> &&callback) {
|
void add_on_idle_callback(std::function<void(uint32_t)> &&callback) {
|
||||||
this->idle_callbacks_.add(std::move(callback));
|
this->idle_callbacks_.add(std::move(callback));
|
||||||
}
|
}
|
||||||
|
void add_on_pause_callback(std::function<void(bool)> &&callback) { this->pause_callbacks_.add(std::move(callback)); }
|
||||||
void add_display(display::Display *display) { this->displays_.push_back(display); }
|
void add_display(display::Display *display) { this->displays_.push_back(display); }
|
||||||
void add_init_lambda(const std::function<void(LvglComponent *)> &lamb) { this->init_lambdas_.push_back(lamb); }
|
void add_init_lambda(const std::function<void(LvglComponent *)> &lamb) { this->init_lambdas_.push_back(lamb); }
|
||||||
void dump_config() override;
|
void dump_config() override;
|
||||||
|
@ -126,12 +127,22 @@ class LvglComponent : public PollingComponent {
|
||||||
bool is_idle(uint32_t idle_ms) { return lv_disp_get_inactive_time(this->disp_) > idle_ms; }
|
bool is_idle(uint32_t idle_ms) { return lv_disp_get_inactive_time(this->disp_) > idle_ms; }
|
||||||
void set_buffer_frac(size_t frac) { this->buffer_frac_ = frac; }
|
void set_buffer_frac(size_t frac) { this->buffer_frac_ = frac; }
|
||||||
lv_disp_t *get_disp() { return this->disp_; }
|
lv_disp_t *get_disp() { return this->disp_; }
|
||||||
|
// Pause or resume the display.
|
||||||
|
// @param paused If true, pause the display. If false, resume the display.
|
||||||
|
// @param show_snow If true, show the snow effect when paused.
|
||||||
void set_paused(bool paused, bool show_snow);
|
void set_paused(bool paused, bool show_snow);
|
||||||
|
bool is_paused() const { return this->paused_; }
|
||||||
|
// If the display is paused and we have resume_on_input_ set to true, resume the display.
|
||||||
|
void maybe_wakeup() {
|
||||||
|
if (this->paused_ && this->resume_on_input_) {
|
||||||
|
this->set_paused(false, false);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void add_event_cb(lv_obj_t *obj, event_callback_t callback, lv_event_code_t event);
|
void add_event_cb(lv_obj_t *obj, event_callback_t callback, lv_event_code_t event);
|
||||||
void add_event_cb(lv_obj_t *obj, event_callback_t callback, lv_event_code_t event1, lv_event_code_t event2);
|
void add_event_cb(lv_obj_t *obj, event_callback_t callback, lv_event_code_t event1, lv_event_code_t event2);
|
||||||
void add_event_cb(lv_obj_t *obj, event_callback_t callback, lv_event_code_t event1, lv_event_code_t event2,
|
void add_event_cb(lv_obj_t *obj, event_callback_t callback, lv_event_code_t event1, lv_event_code_t event2,
|
||||||
lv_event_code_t event3);
|
lv_event_code_t event3);
|
||||||
bool is_paused() const { return this->paused_; }
|
|
||||||
void add_page(LvPageType *page);
|
void add_page(LvPageType *page);
|
||||||
void show_page(size_t index, lv_scr_load_anim_t anim, uint32_t time);
|
void show_page(size_t index, lv_scr_load_anim_t anim, uint32_t time);
|
||||||
void show_next_page(lv_scr_load_anim_t anim, uint32_t time);
|
void show_next_page(lv_scr_load_anim_t anim, uint32_t time);
|
||||||
|
@ -144,10 +155,17 @@ class LvglComponent : public PollingComponent {
|
||||||
lv_group_focus_obj(mark);
|
lv_group_focus_obj(mark);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
// rounding factor to align bounds of update area when drawing
|
||||||
|
size_t draw_rounding{2};
|
||||||
|
void set_draw_rounding(size_t rounding) { this->draw_rounding = rounding; }
|
||||||
|
void set_resume_on_input(bool resume_on_input) { this->resume_on_input_ = resume_on_input; }
|
||||||
|
|
||||||
|
// if set to true, the bounds of the update area will always start at 0,0
|
||||||
|
display::DisplayRotation rotation{display::DISPLAY_ROTATION_0_DEGREES};
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
void write_random_();
|
void write_random_();
|
||||||
void draw_buffer_(const lv_area_t *area, const uint8_t *ptr);
|
void draw_buffer_(const lv_area_t *area, lv_color_t *ptr);
|
||||||
void flush_cb_(lv_disp_drv_t *disp_drv, const lv_area_t *area, lv_color_t *color_p);
|
void flush_cb_(lv_disp_drv_t *disp_drv, const lv_area_t *area, lv_color_t *color_p);
|
||||||
std::vector<display::Display *> displays_{};
|
std::vector<display::Display *> displays_{};
|
||||||
lv_disp_draw_buf_t draw_buf_{};
|
lv_disp_draw_buf_t draw_buf_{};
|
||||||
|
@ -157,14 +175,16 @@ class LvglComponent : public PollingComponent {
|
||||||
std::vector<LvPageType *> pages_{};
|
std::vector<LvPageType *> pages_{};
|
||||||
size_t current_page_{0};
|
size_t current_page_{0};
|
||||||
bool show_snow_{};
|
bool show_snow_{};
|
||||||
lv_coord_t snow_line_{};
|
|
||||||
bool page_wrap_{true};
|
bool page_wrap_{true};
|
||||||
|
bool resume_on_input_{};
|
||||||
std::map<lv_group_t *, lv_obj_t *> focus_marks_{};
|
std::map<lv_group_t *, lv_obj_t *> focus_marks_{};
|
||||||
|
|
||||||
std::vector<std::function<void(LvglComponent *lv_component)>> init_lambdas_;
|
std::vector<std::function<void(LvglComponent *lv_component)>> init_lambdas_;
|
||||||
CallbackManager<void(uint32_t)> idle_callbacks_{};
|
CallbackManager<void(uint32_t)> idle_callbacks_{};
|
||||||
|
CallbackManager<void(bool)> pause_callbacks_{};
|
||||||
size_t buffer_frac_{1};
|
size_t buffer_frac_{1};
|
||||||
bool full_refresh_{};
|
bool full_refresh_{};
|
||||||
|
lv_color_t *rotate_buf_{};
|
||||||
};
|
};
|
||||||
|
|
||||||
class IdleTrigger : public Trigger<> {
|
class IdleTrigger : public Trigger<> {
|
||||||
|
@ -176,6 +196,14 @@ class IdleTrigger : public Trigger<> {
|
||||||
bool is_idle_{};
|
bool is_idle_{};
|
||||||
};
|
};
|
||||||
|
|
||||||
|
class PauseTrigger : public Trigger<> {
|
||||||
|
public:
|
||||||
|
explicit PauseTrigger(LvglComponent *parent, TemplatableValue<bool> paused);
|
||||||
|
|
||||||
|
protected:
|
||||||
|
TemplatableValue<bool> paused_;
|
||||||
|
};
|
||||||
|
|
||||||
template<typename... Ts> class LvglAction : public Action<Ts...>, public Parented<LvglComponent> {
|
template<typename... Ts> class LvglAction : public Action<Ts...>, public Parented<LvglComponent> {
|
||||||
public:
|
public:
|
||||||
explicit LvglAction(std::function<void(LvglComponent *)> &&lamb) : action_(std::move(lamb)) {}
|
explicit LvglAction(std::function<void(LvglComponent *)> &&lamb) : action_(std::move(lamb)) {}
|
||||||
|
@ -200,7 +228,10 @@ class LVTouchListener : public touchscreen::TouchListener, public Parented<LvglC
|
||||||
public:
|
public:
|
||||||
LVTouchListener(uint16_t long_press_time, uint16_t long_press_repeat_time);
|
LVTouchListener(uint16_t long_press_time, uint16_t long_press_repeat_time);
|
||||||
void update(const touchscreen::TouchPoints_t &tpoints) override;
|
void update(const touchscreen::TouchPoints_t &tpoints) override;
|
||||||
void release() override { touch_pressed_ = false; }
|
void release() override {
|
||||||
|
touch_pressed_ = false;
|
||||||
|
this->parent_->maybe_wakeup();
|
||||||
|
}
|
||||||
lv_indev_drv_t *get_drv() { return &this->drv_; }
|
lv_indev_drv_t *get_drv() { return &this->drv_; }
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
|
@ -236,12 +267,18 @@ class LVEncoderListener : public Parented<LvglComponent> {
|
||||||
if (!this->parent_->is_paused()) {
|
if (!this->parent_->is_paused()) {
|
||||||
this->pressed_ = pressed;
|
this->pressed_ = pressed;
|
||||||
this->key_ = key;
|
this->key_ = key;
|
||||||
|
} else if (!pressed) {
|
||||||
|
// maybe wakeup on release if paused
|
||||||
|
this->parent_->maybe_wakeup();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void set_count(int32_t count) {
|
void set_count(int32_t count) {
|
||||||
if (!this->parent_->is_paused())
|
if (!this->parent_->is_paused()) {
|
||||||
this->count_ = count;
|
this->count_ = count;
|
||||||
|
} else {
|
||||||
|
this->parent_->maybe_wakeup();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
lv_indev_drv_t *get_drv() { return &this->drv_; }
|
lv_indev_drv_t *get_drv() { return &this->drv_; }
|
||||||
|
|
|
@ -40,6 +40,7 @@ lv_event_code_t = cg.global_ns.enum("lv_event_code_t")
|
||||||
lv_indev_type_t = cg.global_ns.enum("lv_indev_type_t")
|
lv_indev_type_t = cg.global_ns.enum("lv_indev_type_t")
|
||||||
FontEngine = lvgl_ns.class_("FontEngine")
|
FontEngine = lvgl_ns.class_("FontEngine")
|
||||||
IdleTrigger = lvgl_ns.class_("IdleTrigger", automation.Trigger.template())
|
IdleTrigger = lvgl_ns.class_("IdleTrigger", automation.Trigger.template())
|
||||||
|
PauseTrigger = lvgl_ns.class_("PauseTrigger", automation.Trigger.template())
|
||||||
ObjUpdateAction = lvgl_ns.class_("ObjUpdateAction", automation.Action)
|
ObjUpdateAction = lvgl_ns.class_("ObjUpdateAction", automation.Action)
|
||||||
LvglCondition = lvgl_ns.class_("LvglCondition", automation.Condition)
|
LvglCondition = lvgl_ns.class_("LvglCondition", automation.Condition)
|
||||||
LvglAction = lvgl_ns.class_("LvglAction", automation.Action)
|
LvglAction = lvgl_ns.class_("LvglAction", automation.Action)
|
||||||
|
|
|
@ -12,6 +12,11 @@ substitutions:
|
||||||
arrow_down: "\U000F004B"
|
arrow_down: "\U000F004B"
|
||||||
|
|
||||||
lvgl:
|
lvgl:
|
||||||
|
resume_on_input: true
|
||||||
|
on_pause:
|
||||||
|
logger.log: LVGL is Paused
|
||||||
|
on_resume:
|
||||||
|
logger.log: LVGL has resumed
|
||||||
log_level: TRACE
|
log_level: TRACE
|
||||||
bg_color: light_blue
|
bg_color: light_blue
|
||||||
disp_bg_color: color_id
|
disp_bg_color: color_id
|
||||||
|
|
|
@ -44,6 +44,7 @@ binary_sensor:
|
||||||
number: GPIO39
|
number: GPIO39
|
||||||
inverted: true
|
inverted: true
|
||||||
lvgl:
|
lvgl:
|
||||||
|
draw_rounding: 8
|
||||||
encoders:
|
encoders:
|
||||||
group: switches
|
group: switches
|
||||||
initial_focus: button_button
|
initial_focus: button_button
|
||||||
|
|
Loading…
Reference in a new issue