[lvgl] Add option to sync updates with display (#11896)

Co-authored-by: J. Nick Koston <nick@koston.org>
This commit is contained in:
Clyde Stubbs
2025-11-27 09:06:32 +10:00
committed by GitHub
parent caaa08d678
commit a2d9941c62
5 changed files with 55 additions and 12 deletions

View File

@@ -276,6 +276,7 @@ async def to_code(configs):
config[df.CONF_FULL_REFRESH],
config[CONF_DRAW_ROUNDING],
config[df.CONF_RESUME_ON_INPUT],
config[df.CONF_UPDATE_WHEN_DISPLAY_IDLE],
)
await cg.register_component(lv_component, config)
Widget.create(config[CONF_ID], lv_component, LvScrActType(), config)
@@ -373,6 +374,9 @@ LVGL_SCHEMA = cv.All(
df.CONF_DEFAULT_FONT, default="montserrat_14"
): lvalid.lv_font,
cv.Optional(df.CONF_FULL_REFRESH, default=False): cv.boolean,
cv.Optional(
df.CONF_UPDATE_WHEN_DISPLAY_IDLE, default=False
): cv.boolean,
cv.Optional(CONF_DRAW_ROUNDING, default=2): cv.positive_int,
cv.Optional(CONF_BUFFER_SIZE, default=0): cv.percentage,
cv.Optional(CONF_LOG_LEVEL, default="WARN"): cv.one_of(

View File

@@ -542,6 +542,7 @@ CONF_TOUCHSCREENS = "touchscreens"
CONF_TRANSPARENCY_KEY = "transparency_key"
CONF_THEME = "theme"
CONF_UPDATE_ON_RELEASE = "update_on_release"
CONF_UPDATE_WHEN_DISPLAY_IDLE = "update_when_display_idle"
CONF_VISIBLE_ROW_COUNT = "visible_row_count"
CONF_WIDGET = "widget"
CONF_WIDGETS = "widgets"

View File

@@ -106,6 +106,7 @@ void LvglComponent::dump_config() {
this->disp_drv_.hor_res, this->disp_drv_.ver_res, 100 / this->buffer_frac_, this->rotation,
(int) this->draw_rounding);
}
void LvglComponent::set_paused(bool paused, bool show_snow) {
this->paused_ = paused;
this->show_snow_ = show_snow;
@@ -124,32 +125,38 @@ void LvglComponent::esphome_lvgl_init() {
lv_update_event = static_cast<lv_event_code_t>(lv_event_register_id());
lv_api_event = static_cast<lv_event_code_t>(lv_event_register_id());
}
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, nullptr);
}
void LvglComponent::add_event_cb(lv_obj_t *obj, event_callback_t callback, lv_event_code_t event1,
lv_event_code_t event2) {
add_event_cb(obj, callback, event1);
add_event_cb(obj, callback, event2);
}
void LvglComponent::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) {
add_event_cb(obj, callback, event1);
add_event_cb(obj, callback, event2);
add_event_cb(obj, callback, event3);
}
void LvglComponent::add_page(LvPageType *page) {
this->pages_.push_back(page);
page->set_parent(this);
lv_disp_set_default(this->disp_);
page->setup(this->pages_.size() - 1);
}
void LvglComponent::show_page(size_t index, lv_scr_load_anim_t anim, uint32_t time) {
if (index >= this->pages_.size())
return;
this->current_page_ = index;
lv_scr_load_anim(this->pages_[this->current_page_]->obj, anim, time, 0, false);
}
void LvglComponent::show_next_page(lv_scr_load_anim_t anim, uint32_t time) {
if (this->pages_.empty() || (this->current_page_ == this->pages_.size() - 1 && !this->page_wrap_))
return;
@@ -158,6 +165,7 @@ void LvglComponent::show_next_page(lv_scr_load_anim_t anim, uint32_t time) {
} while (this->pages_[this->current_page_]->skip); // skip empty pages()
this->show_page(this->current_page_, anim, time);
}
void LvglComponent::show_prev_page(lv_scr_load_anim_t anim, uint32_t time) {
if (this->pages_.empty() || (this->current_page_ == 0 && !this->page_wrap_))
return;
@@ -166,8 +174,10 @@ void LvglComponent::show_prev_page(lv_scr_load_anim_t anim, uint32_t time) {
} while (this->pages_[this->current_page_]->skip); // skip empty pages()
this->show_page(this->current_page_, anim, time);
}
size_t LvglComponent::get_current_page() const { return this->current_page_; }
bool LvPageType::is_showing() const { return this->parent_->get_current_page() == this->index; }
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);
@@ -222,7 +232,7 @@ void LvglComponent::draw_buffer_(const lv_area_t *area, lv_color_t *ptr) {
}
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->is_paused()) {
auto now = millis();
this->draw_buffer_(area, color_p);
ESP_LOGVV(TAG, "flush_cb, area=%d/%d, %d/%d took %dms", area->x1, area->y1, lv_area_get_width(area),
@@ -230,6 +240,7 @@ void LvglComponent::flush_cb_(lv_disp_drv_t *disp_drv, const lv_area_t *area, lv
}
lv_disp_flush_ready(disp_drv);
}
IdleTrigger::IdleTrigger(LvglComponent *parent, TemplatableValue<uint32_t> timeout) : timeout_(std::move(timeout)) {
parent->add_on_idle_callback([this](uint32_t idle_time) {
if (!this->is_idle_ && idle_time > this->timeout_.value()) {
@@ -377,6 +388,27 @@ void LvKeyboardType::set_obj(lv_obj_t *lv_obj) {
}
#endif // USE_LVGL_KEYBOARD
void LvglComponent::draw_end_() {
if (this->draw_end_callback_ != nullptr)
this->draw_end_callback_->trigger();
if (this->update_when_display_idle_) {
for (auto *disp : this->displays_)
disp->update();
}
}
bool LvglComponent::is_paused() const {
if (this->paused_)
return true;
if (this->update_when_display_idle_) {
for (auto *disp : this->displays_) {
if (!disp->is_idle())
return true;
}
}
return false;
}
void LvglComponent::write_random_() {
int iterations = 6 - lv_disp_get_inactive_time(this->disp_) / 60000;
if (iterations <= 0)
@@ -426,12 +458,13 @@ void LvglComponent::write_random_() {
* presses a key or clicks on the screen.
*/
LvglComponent::LvglComponent(std::vector<display::Display *> displays, float buffer_frac, bool full_refresh,
int draw_rounding, bool resume_on_input)
int draw_rounding, bool resume_on_input, bool update_when_display_idle)
: draw_rounding(draw_rounding),
displays_(std::move(displays)),
buffer_frac_(buffer_frac),
full_refresh_(full_refresh),
resume_on_input_(resume_on_input) {
resume_on_input_(resume_on_input),
update_when_display_idle_(update_when_display_idle) {
lv_disp_draw_buf_init(&this->draw_buf_, nullptr, nullptr, 0);
lv_disp_drv_init(&this->disp_drv_);
this->disp_drv_.draw_buf = &this->draw_buf_;
@@ -487,7 +520,7 @@ void LvglComponent::setup() {
if (this->draw_start_callback_ != nullptr) {
this->disp_drv_.render_start_cb = render_start_cb;
}
if (this->draw_end_callback_ != nullptr) {
if (this->draw_end_callback_ != nullptr || this->update_when_display_idle_) {
this->disp_drv_.monitor_cb = monitor_cb;
}
#if LV_USE_LOG
@@ -509,14 +542,15 @@ void LvglComponent::setup() {
void LvglComponent::update() {
// update indicators
if (this->paused_) {
if (this->is_paused()) {
return;
}
this->idle_callbacks_.call(lv_disp_get_inactive_time(this->disp_));
}
void LvglComponent::loop() {
if (this->paused_) {
if (this->show_snow_)
if (this->is_paused()) {
if (this->paused_ && this->show_snow_)
this->write_random_();
} else {
lv_timer_handler_run_in_period(5);

View File

@@ -151,7 +151,7 @@ class LvglComponent : public PollingComponent {
public:
LvglComponent(std::vector<display::Display *> displays, float buffer_frac, bool full_refresh, int draw_rounding,
bool resume_on_input);
bool resume_on_input, bool update_when_display_idle);
static void static_flush_cb(lv_disp_drv_t *disp_drv, const lv_area_t *area, lv_color_t *color_p);
float get_setup_priority() const override { return setup_priority::PROCESSOR; }
@@ -171,7 +171,9 @@ class LvglComponent : public PollingComponent {
// @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);
bool is_paused() const { return this->paused_; }
// Returns true if the display is explicitly paused, or a blocking display update is in progress.
bool is_paused() const;
// 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_) {
@@ -210,10 +212,10 @@ class LvglComponent : public PollingComponent {
void set_draw_end_trigger(Trigger<> *trigger) { this->draw_end_callback_ = trigger; }
protected:
// these functions are never called unless the callbacks are non-null since the
// LVGL callbacks that call them are not set unless the start/end callbacks are non-null
void draw_end_();
// Not checking for non-null callback since the
// LVGL callback that calls it is not set in that case
void draw_start_() const { this->draw_start_callback_->trigger(); }
void draw_end_() const { this->draw_end_callback_->trigger(); }
void write_random_();
void draw_buffer_(const lv_area_t *area, lv_color_t *ptr);
@@ -222,6 +224,7 @@ class LvglComponent : public PollingComponent {
size_t buffer_frac_{1};
bool full_refresh_{};
bool resume_on_input_{};
bool update_when_display_idle_{};
lv_disp_draw_buf_t draw_buf_{};
lv_disp_drv_t disp_drv_{};

View File

@@ -60,6 +60,7 @@ display:
update_interval: never
lvgl:
update_when_display_idle: true
displays:
- tft_display
- second_display