ESPHome 2026.11.0-dev
Loading...
Searching...
No Matches
lvgl_esphome.cpp
Go to the documentation of this file.
2#include "esphome/core/hal.h"
4#include "esphome/core/log.h"
5#include "lvgl_esphome.h"
6
7#include "core/lv_global.h"
8#include "core/lv_obj_class_private.h"
9#include "core/lv_obj_private.h"
10
11#include <numeric>
12
13static void *lv_alloc_draw_buf(size_t size, bool internal);
14static void *draw_buf_alloc_cb(size_t size, lv_color_format_t color_format) { return lv_alloc_draw_buf(size, false); };
15
16namespace esphome::lvgl {
17ESPHOME_LOG_TAG(TAG, "lvgl");
18
19static const size_t MIN_BUFFER_FRAC = 8; // buffer must be at least 1/8 of the display size
20static const size_t MIN_BUFFER_SIZE = 2048; // Sensible minimum buffer size
21
22static const char *const EVENT_NAMES[] = {
23 "NONE",
24 "PRESSED",
25 "PRESSING",
26 "PRESS_LOST",
27 "SHORT_CLICKED",
28 "LONG_PRESSED",
29 "LONG_PRESSED_REPEAT",
30 "CLICKED",
31 "RELEASED",
32 "SCROLL_BEGIN",
33 "SCROLL_END",
34 "SCROLL",
35 "GESTURE",
36 "KEY",
37 "FOCUSED",
38 "DEFOCUSED",
39 "LEAVE",
40 "HIT_TEST",
41 "COVER_CHECK",
42 "REFR_EXT_DRAW_SIZE",
43 "DRAW_MAIN_BEGIN",
44 "DRAW_MAIN",
45 "DRAW_MAIN_END",
46 "DRAW_POST_BEGIN",
47 "DRAW_POST",
48 "DRAW_POST_END",
49 "DRAW_PART_BEGIN",
50 "DRAW_PART_END",
51 "VALUE_CHANGED",
52 "INSERT",
53 "REFRESH",
54 "READY",
55 "CANCEL",
56 "DELETE",
57 "CHILD_CHANGED",
58 "CHILD_CREATED",
59 "CHILD_DELETED",
60 "SCREEN_UNLOAD_START",
61 "SCREEN_LOAD_START",
62 "SCREEN_LOADED",
63 "SCREEN_UNLOADED",
64 "SIZE_CHANGED",
65 "STYLE_CHANGED",
66 "LAYOUT_CHANGED",
67 "GET_SELF_SIZE",
68};
69
70static const unsigned LOG_LEVEL_MAP[] = {
71 ESPHOME_LOG_LEVEL_DEBUG, ESPHOME_LOG_LEVEL_INFO, ESPHOME_LOG_LEVEL_WARN,
72 ESPHOME_LOG_LEVEL_ERROR, ESPHOME_LOG_LEVEL_ERROR, ESPHOME_LOG_LEVEL_NONE,
73
74};
75
76std::string lv_event_code_name_for(lv_event_t *event) {
77 auto event_code = lv_event_get_code(event);
78 if (event_code < sizeof(EVENT_NAMES) / sizeof(EVENT_NAMES[0])) {
79 return EVENT_NAMES[event_code];
80 }
81 // max 4 bytes: "%u" with uint8_t (max 255, 3 digits) + null
82 char buf[4];
83 snprintf(buf, sizeof(buf), "%u", event_code);
84 return buf;
85}
86
89 ESP_LOGW(TAG, "Display rotation cannot be changed unless rotation was enabled during setup.");
90 return;
91 }
92 this->rotation_ = rotation;
93 if (this->is_ready()) {
94 this->set_resolution_();
95 this->update_orientation_();
96 lv_obj_update_layout(this->get_screen_active());
97 lv_obj_invalidate(this->get_screen_active());
98 }
99}
100
102 // Normalize to [0, 360). The DisplayRotation enum values are the angles in degrees.
103 angle %= 360;
104 if (angle < 0)
105 angle += 360;
106 if (angle % 90 != 0) {
107 ESP_LOGW(TAG, "Invalid rotation angle %d; must be a multiple of 90 degrees.", angle);
108 return;
109 }
110 this->set_rotation(static_cast<display::DisplayRotation>(angle));
111}
112
113void LvglComponent::rotate_coordinates(int32_t &x, int32_t &y) const {
114 switch (this->rotation_) {
115 default:
116 break;
117
119 x = this->width_ - x - 1;
120 y = this->height_ - y - 1;
121 break;
122 }
124 auto tmp = x;
125 x = this->height_ - y - 1;
126 y = tmp;
127 break;
128 }
130 auto tmp = y;
131 y = this->width_ - x - 1;
132 x = tmp;
133 break;
134 }
135 }
136}
137
138static void rounder_cb(lv_event_t *event) {
139 auto *comp = static_cast<LvglComponent *>(lv_event_get_user_data(event));
140 auto *area = static_cast<lv_area_t *>(lv_event_get_param(event));
141 // cater for display driver chips with special requirements for bounds of partial
142 // draw areas. Extend the draw area to satisfy:
143 // * Coordinates must be a multiple of draw_rounding
144 auto draw_rounding = comp->draw_rounding;
145 // round down the start coordinates
146 area->x1 = area->x1 / draw_rounding * draw_rounding;
147 area->y1 = area->y1 / draw_rounding * draw_rounding;
148 // round up the end coordinates
149 area->x2 = (area->x2 + draw_rounding) / draw_rounding * draw_rounding - 1;
150 area->y2 = (area->y2 + draw_rounding) / draw_rounding * draw_rounding - 1;
151}
152
153void LvglComponent::render_end_cb(lv_event_t *event) {
154 auto *comp = static_cast<LvglComponent *>(lv_event_get_user_data(event));
155 comp->draw_end_();
156}
157
158void LvglComponent::render_start_cb(lv_event_t *event) {
159 ESP_LOGVV(TAG, "Draw start");
160 auto *comp = static_cast<LvglComponent *>(lv_event_get_user_data(event));
161 comp->draw_start_();
162}
163
164lv_event_code_t lv_update_event; // NOLINT
166 ESP_LOGCONFIG(TAG,
167 "LVGL:\n"
168 " Display width/height: %d x %d\n"
169 " Buffer size: %zu%%\n"
170 " Rotation: %d\n"
171 " Draw rounding: %d",
172 this->width_, this->height_, 100 / this->buffer_frac_, this->rotation_, (int) this->draw_rounding);
173 if (this->rotation_type_ != ROTATION_UNUSED) {
174 const char *rot_type = "hardware via display driver";
176#ifdef USE_ESP32_VARIANT_ESP32P4
177 rot_type = this->ppa_client_ != nullptr ? "software (PPA accelerated)" : "software";
178#else
179 rot_type = "software";
180#endif
181 }
182 ESP_LOGCONFIG(TAG, " Rotation type: %s", rot_type);
183 }
184}
185
186void LvglComponent::set_paused(bool paused, bool show_snow) {
187 this->paused_ = paused;
188 this->show_snow_ = show_snow;
189 if (!paused && lv_screen_active() != nullptr) {
190 lv_display_trigger_activity(this->disp_); // resets the inactivity time
191 lv_obj_invalidate(lv_screen_active());
192 }
193 if (paused && this->pause_callback_ != nullptr)
194 this->pause_callback_->trigger();
195 if (!paused && this->resume_callback_ != nullptr)
196 this->resume_callback_->trigger();
197}
198
200 lv_init();
201 // override draw buf alloc to ensure proper alignment for PPA
202 LV_GLOBAL_DEFAULT()->draw_buf_handlers.buf_malloc_cb = draw_buf_alloc_cb;
203 LV_GLOBAL_DEFAULT()->draw_buf_handlers.buf_free_cb = lv_free_core;
204 LV_GLOBAL_DEFAULT()->image_cache_draw_buf_handlers.buf_malloc_cb = draw_buf_alloc_cb;
205 LV_GLOBAL_DEFAULT()->image_cache_draw_buf_handlers.buf_free_cb = lv_free_core;
206 LV_GLOBAL_DEFAULT()->font_draw_buf_handlers.buf_malloc_cb = draw_buf_alloc_cb;
207 LV_GLOBAL_DEFAULT()->font_draw_buf_handlers.buf_free_cb = lv_free_core;
208 lv_tick_set_cb([] { return millis(); });
209 lv_update_event = static_cast<lv_event_code_t>(lv_event_register_id());
210}
211
212void LvglComponent::add_event_cb(lv_obj_t *obj, event_callback_t callback, lv_event_code_t event, void *user_data) {
213 lv_obj_add_event_cb(obj, callback, event, user_data);
214}
215
216void LvglComponent::add_event_cb(lv_obj_t *obj, event_callback_t callback, lv_event_code_t event1,
217 lv_event_code_t event2, void *user_data) {
218 add_event_cb(obj, callback, event1, user_data);
219 add_event_cb(obj, callback, event2, user_data);
220}
221
222void LvglComponent::add_event_cb(lv_obj_t *obj, event_callback_t callback, lv_event_code_t event1,
223 lv_event_code_t event2, lv_event_code_t event3, void *user_data) {
224 add_event_cb(obj, callback, event1, user_data);
225 add_event_cb(obj, callback, event2, user_data);
226 add_event_cb(obj, callback, event3, user_data);
227}
228
229void LvglComponent::lv_obj_set_custom_flag(lv_obj_t *obj, lv_obj_flag_t flag, bool value) {
230 // LVGL 9.6 keeps these flags in spec_attr->user_flags, in the same bits as its deprecated lv_obj_add_flag()
231 uint8_t mask;
232 switch (flag) {
233 case LV_OBJ_FLAG_LAYOUT_2:
234 mask = 1 << 1;
235 break;
236 case LV_OBJ_FLAG_WIDGET_1:
237 mask = 1 << 2;
238 break;
239 case LV_OBJ_FLAG_WIDGET_2:
240 mask = 1 << 3;
241 break;
242 default:
243 return;
244 }
245 if (value) {
246 lv_obj_spec_attr_t *spec_attr = lv_obj_allocate_spec_attr(obj);
247 if (spec_attr == nullptr)
248 return;
249 spec_attr->user_flags |= mask;
250 } else if (obj->spec_attr != nullptr) {
251 obj->spec_attr->user_flags &= ~mask;
252 }
253 if (flag == LV_OBJ_FLAG_LAYOUT_2) {
254 lv_obj_mark_layout_as_dirty(lv_obj_get_parent(obj));
255 lv_obj_mark_layout_as_dirty(obj);
256 }
257}
258
260 this->pages_.push_back(page);
261 page->set_parent(this);
262 lv_display_set_default(this->disp_);
263 page->setup(this->pages_.size() - 1);
264}
265
266void LvglComponent::show_page(size_t index, lv_screen_load_anim_t anim, uint32_t time) {
267 if (index >= this->pages_.size())
268 return;
269 this->current_page_ = index;
270 if (anim == LV_SCREEN_LOAD_ANIM_NONE) {
271 lv_screen_load(this->pages_[this->current_page_]->obj);
272 } else {
273 lv_screen_load_anim(this->pages_[this->current_page_]->obj, anim, time, 0, false);
274 }
275}
276
277void LvglComponent::show_next_page(lv_screen_load_anim_t anim, uint32_t time) {
278 if (this->pages_.empty() || (this->current_page_ == this->pages_.size() - 1 && !this->page_wrap_))
279 return;
280 size_t start = this->current_page_;
281 do {
282 this->current_page_ = (this->current_page_ + 1) % this->pages_.size();
283 if (this->current_page_ == start)
284 return; // all pages have skip=true (guaranteed not to happen by YAML validation)
285 } while (this->pages_[this->current_page_]->skip); // skip empty pages()
286 this->show_page(this->current_page_, anim, time);
287}
288
289void LvglComponent::show_prev_page(lv_screen_load_anim_t anim, uint32_t time) {
290 if (this->pages_.empty() || (this->current_page_ == 0 && !this->page_wrap_))
291 return;
292 size_t start = this->current_page_;
293 do {
294 this->current_page_ = (this->current_page_ + this->pages_.size() - 1) % this->pages_.size();
295 if (this->current_page_ == start)
296 return; // all pages have skip=true (guaranteed not to happen by YAML validation)
297 } while (this->pages_[this->current_page_]->skip); // skip empty pages()
298 this->show_page(this->current_page_, anim, time);
299}
300
301size_t LvglComponent::get_current_page() const { return this->current_page_; }
302bool LvPageType::is_showing() const { return this->parent_->get_current_page() == this->index; }
303
304#ifdef USE_ESP32_VARIANT_ESP32P4
305bool LvglComponent::ppa_rotate_(const lv_color_data *src, lv_color_data *dst, uint16_t width, uint16_t height,
306 uint32_t height_rounded) {
307 ppa_srm_rotation_angle_t angle;
308 uint16_t out_w, out_h;
309
310 // Map ESPHome clockwise display rotation to PPA counter-clockwise angles
311 switch (this->rotation_) {
313 angle = PPA_SRM_ROTATION_ANGLE_270; // 270° CCW = 90° CW
314 out_w = height_rounded;
315 out_h = width;
316 break;
318 angle = PPA_SRM_ROTATION_ANGLE_180;
319 out_w = width;
320 out_h = height;
321 break;
323 angle = PPA_SRM_ROTATION_ANGLE_90; // 90° CCW = 270° CW
324 out_w = height_rounded;
325 out_h = width;
326 break;
327 default:
328 return false; // No rotation needed
329 }
330
331 // Align buffer size to cache line (LV_DRAW_BUF_ALIGN) as required by PPA DMA
332 // the underlying buffer will be large enough as the size is also padded when allocating.
333 size_t out_buf_size = out_w * out_h * sizeof(lv_color_data);
334 out_buf_size = LV_ROUND_UP(out_buf_size, LV_DRAW_BUF_ALIGN);
335
336 ppa_srm_oper_config_t srm_config{};
337 srm_config.in.buffer = src;
338 srm_config.in.pic_w = width;
339 srm_config.in.pic_h = height;
340 srm_config.in.block_w = width;
341 srm_config.in.block_h = height;
342#if LV_COLOR_DEPTH == 16
343 srm_config.in.srm_cm = PPA_SRM_COLOR_MODE_RGB565;
344#elif LV_COLOR_DEPTH == 32
345 srm_config.in.srm_cm = PPA_SRM_COLOR_MODE_ARGB8888;
346#endif
347 srm_config.out.buffer = dst;
348 srm_config.out.buffer_size = out_buf_size;
349 srm_config.out.pic_w = out_w;
350 srm_config.out.pic_h = out_h;
351#if LV_COLOR_DEPTH == 16
352 srm_config.out.srm_cm = PPA_SRM_COLOR_MODE_RGB565;
353#elif LV_COLOR_DEPTH == 32
354 srm_config.out.srm_cm = PPA_SRM_COLOR_MODE_ARGB8888;
355#endif
356 srm_config.rotation_angle = angle;
357 srm_config.scale_x = 1.0f;
358 srm_config.scale_y = 1.0f;
359 srm_config.mode = PPA_TRANS_MODE_BLOCKING;
360
361 esp_err_t ret = ppa_do_scale_rotate_mirror(this->ppa_client_, &srm_config);
362 if (ret != ESP_OK) {
363 ESP_LOGW(TAG, "PPA rotation failed: %s", esp_err_to_name(ret));
364 ESP_LOGW(TAG, "PPA SRM: in=%ux%u src=%p, out=%ux%u dst=%p size=%zu, angle=%d", width, height, src, out_w, out_h,
365 dst, out_buf_size, (int) angle);
366 return false;
367 }
368 return true;
369}
370#endif // USE_ESP32_VARIANT_ESP32P4
371
372void LvglComponent::draw_buffer_(const lv_area_t *area, lv_color_data *ptr) {
373 auto width = lv_area_get_width(area);
374 auto height = lv_area_get_height(area);
375 auto height_rounded = (height + this->draw_rounding - 1) / this->draw_rounding * this->draw_rounding;
376 auto x1 = area->x1;
377 auto y1 = area->y1;
378 if (this->rotation_type_ == ROTATION_SOFTWARE) {
379 lv_color_data *dst = reinterpret_cast<lv_color_data *>(this->rotate_buf_);
380#ifdef USE_ESP32_VARIANT_ESP32P4
381 bool ppa_done = this->ppa_client_ != nullptr && this->ppa_rotate_(ptr, dst, width, height, height_rounded);
382 if (!ppa_done)
383#endif
384 {
385 switch (this->rotation_) {
387 for (lv_coord_t x = height; x-- != 0;) {
388 for (lv_coord_t y = 0; y != width; y++) {
389 dst[y * height_rounded + x] = *ptr++;
390 }
391 }
392 break;
393
395 for (lv_coord_t y = height; y-- != 0;) {
396 for (lv_coord_t x = width; x-- != 0;) {
397 dst[y * width + x] = *ptr++;
398 }
399 }
400 break;
401
403 for (lv_coord_t x = 0; x != height; x++) {
404 for (lv_coord_t y = width; y-- != 0;) {
405 dst[y * height_rounded + x] = *ptr++;
406 }
407 }
408 break;
409
410 default:
411 dst = ptr;
412 break;
413 }
414 }
415 // Coordinate adjustments apply regardless of PPA or SW rotation
416 switch (this->rotation_) {
418 y1 = x1;
419 x1 = this->width_ - area->y1 - height;
420 height = width;
421 width = height_rounded;
422 break;
423
425 x1 = this->width_ - x1 - width;
426 y1 = this->height_ - y1 - height;
427 break;
428
430 x1 = y1;
431 y1 = this->height_ - area->x1 - width;
432 height = width;
433 width = height_rounded;
434 break;
435
436 default:
437 break;
438 }
439 ptr = dst;
440 }
441 for (auto *display : this->displays_) {
442 display->draw_pixels_at(x1, y1, width, height, (const uint8_t *) ptr, LV_BITNESS, this->big_endian_);
443 }
444}
445
446void LvglComponent::flush_cb_(lv_display_t *disp_drv, const lv_area_t *area, uint8_t *color_p) {
447 // no guard here for display busy, since LVGL will not call flush_cb until the refresh timer fires,
448 // and while the display is busy this is reset to 5 minutes. If that expires and the display is still
449 // busy there are bigger problems.
450 if (!this->paused_) {
451 auto now = millis();
452 this->draw_buffer_(area, reinterpret_cast<lv_color_data *>(color_p));
453 ESP_LOGV(TAG, "flush_cb, area=%d/%d, %d/%d took %dms", (int) area->x1, (int) area->y1,
454 (int) lv_area_get_width(area), (int) lv_area_get_height(area), (int) (millis() - now));
455 }
456 lv_display_flush_ready(disp_drv);
457}
458
460 parent->add_on_idle_callback([this](uint32_t idle_time) {
461 if (!this->is_idle_ && idle_time > this->timeout_.value()) {
462 this->is_idle_ = true;
463 this->trigger();
464 } else if (this->is_idle_ && idle_time < this->timeout_.value()) {
465 this->is_idle_ = false;
466 }
467 });
468}
469
470#ifdef USE_LVGL_TOUCHSCREEN
471LVTouchListener::LVTouchListener(uint16_t long_press_time, uint16_t long_press_repeat_time, LvglComponent *parent) {
472 this->set_parent(parent);
473 this->drv_ = lv_indev_create();
474 lv_indev_set_type(this->drv_, LV_INDEV_TYPE_POINTER);
475 lv_indev_set_disp(this->drv_, parent->get_disp());
476 lv_indev_set_long_press_time(this->drv_, long_press_time);
477 lv_indev_set_long_press_repeat_time(this->drv_, long_press_repeat_time);
478 lv_indev_set_user_data(this->drv_, this);
479 lv_indev_set_read_cb(this->drv_, [](lv_indev_t *d, lv_indev_data_t *data) {
480 auto *l = static_cast<LVTouchListener *>(lv_indev_get_user_data(d));
481 if (l->touch_pressed_) {
482 data->point.x = l->touch_point_.x;
483 data->point.y = l->touch_point_.y;
484 l->parent_->rotate_coordinates(data->point.x, data->point.y);
485 data->state = LV_INDEV_STATE_PRESSED;
486 } else {
487 data->state = LV_INDEV_STATE_RELEASED;
488 }
489 });
490}
491
493 this->touch_pressed_ = !this->parent_->is_paused() && !tpoints.empty();
494 if (this->touch_pressed_)
495 this->touch_point_ = tpoints[0];
496}
497#endif // USE_LVGL_TOUCHSCREEN
498
499#ifdef USE_LVGL_METER
500
501int16_t lv_get_needle_angle_for_value(lv_obj_t *obj, int32_t value) {
502 auto *scale = lv_obj_get_parent(obj);
503 auto min_value = lv_scale_get_range_min_value(scale);
504 auto max_value = lv_scale_get_range_max_value(scale);
505 value = clamp(value, min_value, max_value);
506 return ((value - min_value) * lv_scale_get_angle_range(scale) / (max_value - min_value) +
507 lv_scale_get_rotation((scale))) %
508 360;
509}
510
511void IndicatorLine::set_obj(lv_obj_t *lv_obj) {
512 LvCompound::set_obj(lv_obj);
513 lv_line_set_points(lv_obj, this->points_, 2);
514 lv_obj_add_event_cb(
515 lv_obj_get_parent(obj),
516 [](lv_event_t *e) {
517 auto *indicator = static_cast<IndicatorLine *>(lv_event_get_user_data(e));
518 indicator->update_length_();
519 ESP_LOGV(TAG, "Updated length, value = %d", indicator->angle_);
520 },
521 LV_EVENT_SIZE_CHANGED, this);
522}
523
525 auto angle = lv_get_needle_angle_for_value(this->obj, value);
526 if (angle != this->angle_) {
527 this->angle_ = angle;
528 this->update_length_();
529 }
530}
531
532void IndicatorLine::update_length_() {
533 auto cx = lv_obj_get_width(lv_obj_get_parent(this->obj)) / 2;
534 auto cy = lv_obj_get_height(lv_obj_get_parent(this->obj)) / 2;
535 auto radius = clamp_at_most(cx, cy);
536 auto length = lv_obj_get_style_length(this->obj, LV_PART_MAIN);
537 auto radial_offset = lv_obj_get_style_radial_offset(this->obj, LV_PART_MAIN);
538 if (LV_COORD_IS_PCT(radial_offset)) {
539 radial_offset = radius * LV_COORD_GET_PCT(radial_offset) / 100;
540 }
541 if (LV_COORD_IS_PCT(length)) {
542 length = radius * LV_COORD_GET_PCT(length) / 100;
543 } else if (length < 0) {
544 length += radius;
545 }
546 auto x = lv_trigo_cos(this->angle_) / 32768.0f;
547 auto y = lv_trigo_sin(this->angle_) / 32768.0f;
548 // radius here also represents the offset of the scale center from top left
549 this->points_[0].x = radius + radial_offset * x;
550 this->points_[0].y = radius + radial_offset * y;
551 this->points_[1].x = radius + x * (radial_offset + length);
552 this->points_[1].y = radius + y * (radial_offset + length);
553 lv_obj_refresh_self_size(this->obj);
554 lv_obj_invalidate(this->obj);
555}
556#endif
557
558#ifdef USE_LVGL_TABLE
560 uint32_t row;
561 uint32_t column;
562 lv_table_get_selected_cell(obj, &row, &column);
563 return row;
564}
565
567 uint32_t row;
568 uint32_t column;
569 lv_table_get_selected_cell(obj, &row, &column);
570 return column;
571}
572
573void LvTableType::set_obj(lv_obj_t *lv_obj) {
574 LvCompound::set_obj(lv_obj);
575 lv_obj_add_event_cb(
576 lv_obj,
577 [](lv_event_t *e) {
578 auto *table = static_cast<LvTableType *>(lv_event_get_user_data(e));
579 table->update_column_widths_();
580 },
581 LV_EVENT_SIZE_CHANGED, this);
582}
583
585 for (auto &i : this->column_pct_) {
586 if (i.col == col) {
587 i.pct = pct;
588 this->update_column_widths_();
589 return;
590 }
591 }
592 this->column_pct_.push_back({col, pct});
593 this->update_column_widths_();
594}
595
597 auto content_width = lv_obj_get_content_width(this->obj);
598 for (const auto &col : this->column_pct_) {
599 lv_table_set_column_width(this->obj, col.col, content_width * col.pct / 100);
600 }
601}
602#endif // USE_LVGL_TABLE
603
604#ifdef USE_LVGL_KEY_LISTENER
605LVEncoderListener::LVEncoderListener(lv_indev_type_t type, uint16_t long_press_time, uint16_t long_press_repeat_time) {
606 this->drv_ = lv_indev_create();
607 lv_indev_set_type(this->drv_, type);
608 lv_indev_set_long_press_time(this->drv_, long_press_time);
609 lv_indev_set_long_press_repeat_time(this->drv_, long_press_repeat_time);
610 lv_indev_set_user_data(this->drv_, this);
611 lv_indev_set_read_cb(this->drv_, [](lv_indev_t *d, lv_indev_data_t *data) {
612 auto *l = static_cast<LVEncoderListener *>(lv_indev_get_user_data(d));
613 data->state = l->pressed_ ? LV_INDEV_STATE_PRESSED : LV_INDEV_STATE_RELEASED;
614 data->key = l->key_;
615 data->enc_diff = (int16_t) (l->count_ - l->last_count_);
616 l->last_count_ = l->count_;
617 data->continue_reading = false;
618 });
619}
620#endif // USE_LVGL_KEY_LISTENER
621
622#if defined(USE_LVGL_DROPDOWN) || defined(LV_USE_ROLLER)
624 auto selected = this->get_selected_index();
625 if (selected >= this->options_.size())
626 return "";
627 return this->options_[selected];
628}
629
630static std::string join_string(const FixedVector<const char *> &options) {
631 return std::accumulate(
632 options.begin(), options.end(), std::string(),
633 [](const std::string &a, const char *b) -> std::string { return a + (!a.empty() ? "\n" : "") + b; });
634}
635
636void LvSelectable::set_selected_text(const std::string &text, lv_anim_enable_t anim) {
637 auto *index = std::find(this->options_.begin(), this->options_.end(), text);
638 if (index != this->options_.end()) {
639 this->set_selected_index(index - this->options_.begin(), anim);
640 lv_obj_send_event(this->obj, lv_update_event, nullptr);
641 }
642}
643
645 auto index = this->get_selected_index();
646 if (index >= options.size())
647 index = options.size() - 1;
648 this->options_ = std::move(options);
649 this->set_option_string(join_string(this->options_).c_str());
650 lv_obj_send_event(this->obj, LV_EVENT_REFRESH, nullptr);
651 this->set_selected_index(index, LV_ANIM_OFF);
652}
653#endif // USE_LVGL_DROPDOWN || LV_USE_ROLLER
654
655#ifdef USE_LVGL_BUTTONMATRIX
656void LvButtonMatrixType::set_obj(lv_obj_t *lv_obj) {
657 LvCompound::set_obj(lv_obj);
658 lv_obj_add_event_cb(
659 lv_obj,
660 [](lv_event_t *event) {
661 auto *self = static_cast<LvButtonMatrixType *>(lv_event_get_user_data(event));
662 if (self->key_callback_.size() == 0)
663 return;
664 auto key_idx = lv_buttonmatrix_get_selected_button(self->obj);
665 if (key_idx == LV_BUTTONMATRIX_BUTTON_NONE)
666 return;
667 if (self->key_map_.contains(key_idx)) {
668 self->send_key_(self->key_map_[key_idx]);
669 return;
670 }
671 const auto *str = lv_buttonmatrix_get_button_text(self->obj, key_idx);
672 auto len = strlen(str);
673 while (len--)
674 self->send_key_(*str++);
675 },
676 LV_EVENT_PRESSED, this);
677}
678#endif // USE_LVGL_BUTTONMATRIX
679
680#ifdef USE_LVGL_KEYBOARD
681static const char *const KB_SPECIAL_KEYS[] = {
682 "abc",
683 "ABC",
684 "1#",
685 // maybe add other special keys here
686};
687
688void LvKeyboardType::set_obj(lv_obj_t *lv_obj) {
689 LvCompound::set_obj(lv_obj);
690 lv_obj_add_event_cb(
691 lv_obj,
692 [](lv_event_t *event) {
693 auto *self = static_cast<LvKeyboardType *>(lv_event_get_user_data(event));
694 if (self->key_callback_.size() == 0)
695 return;
696
697 auto key_idx = lv_buttonmatrix_get_selected_button(self->obj);
698 if (key_idx == LV_BUTTONMATRIX_BUTTON_NONE)
699 return;
700 const char *txt = lv_buttonmatrix_get_button_text(self->obj, key_idx);
701 if (txt == nullptr)
702 return;
703 for (const auto *kb_special_key : KB_SPECIAL_KEYS) {
704 if (strcmp(txt, kb_special_key) == 0)
705 return;
706 }
707 while (*txt != 0)
708 self->send_key_(*txt++);
709 },
710 LV_EVENT_PRESSED, this);
711}
712#endif // USE_LVGL_KEYBOARD
713
715 if (this->draw_end_callback_ != nullptr)
717 // Only reachable once the display is idle again: while busy, the display's refr_timer_ is
718 // paused (see loop()), so LVGL never renders/flushes and this event never fires.
719 if (this->update_when_display_idle_) {
720 for (auto *disp : this->displays_)
721 disp->update();
722 }
723}
724
726 if (!this->update_when_display_idle_)
727 return false;
728 for (auto *disp : this->displays_) {
729 if (!disp->is_idle())
730 return true;
731 }
732 return false;
733}
734
736 int iterations = 6 - lv_display_get_inactive_time(this->disp_) / 60000;
737 if (iterations <= 0)
738 iterations = 1;
739 int16_t width = lv_display_get_horizontal_resolution(this->disp_);
740 int16_t height = lv_display_get_vertical_resolution(this->disp_);
741 while (iterations-- != 0) {
742 int32_t col = random_uint32() % width;
743 col = col / this->draw_rounding * this->draw_rounding;
744 int32_t row = random_uint32() % height;
745 row = row / this->draw_rounding * this->draw_rounding;
746 // size will be between 8 and 32, and a multiple of draw_rounding
747 int32_t size = (random_uint32() % 25 + 8) / this->draw_rounding * this->draw_rounding;
748 lv_area_t area{.x1 = col, .y1 = row, .x2 = col + size - 1, .y2 = row + size - 1};
749 // clip to display bounds just in case
750 if (area.x2 >= width)
751 area.x2 = width - 1;
752 if (area.y2 >= height)
753 area.y2 = height - 1;
754
755 // line_len can't exceed 1024, and minimum buffer size is 2048, so this won't overflow the buffer
756 size_t line_len = lv_area_get_width(&area) * lv_area_get_height(&area) / 2;
757 for (size_t i = 0; i != line_len; i++) {
758 reinterpret_cast<uint32_t *>(this->draw_buf_)[i] = random_uint32();
759 }
760 this->draw_buffer_(&area, reinterpret_cast<lv_color_data *>(this->draw_buf_));
761 }
762}
763
785LvglComponent::LvglComponent(std::vector<display::Display *> displays, float buffer_frac, bool full_refresh,
786 int draw_rounding, bool resume_on_input, bool update_when_display_idle,
787 RotationType rotation_type)
788 : draw_rounding(draw_rounding),
789 displays_(std::move(displays)),
790 buffer_frac_(buffer_frac),
791 full_refresh_(full_refresh),
792 resume_on_input_(resume_on_input),
793 update_when_display_idle_(update_when_display_idle),
794 rotation_type_(rotation_type) {
795 this->disp_ = lv_display_create(240, 240);
796}
797
799 int32_t width = this->width_;
800 int32_t height = this->height_;
803 std::swap(width, height);
804 }
805 ESP_LOGD(TAG, "Setting resolution to %u x %u (rotation %d)", (unsigned) width, (unsigned) height,
806 (int) this->rotation_);
808 for (auto *display : this->displays_)
809 display->set_rotation(this->rotation_);
810 }
811 lv_display_set_resolution(this->disp_, width, height);
812}
813
815 // A square display is treated as landscape.
817 if (orientation == this->orientation_)
818 return;
821 if (trigger != nullptr)
822 trigger->trigger();
823}
824
826 auto *display = this->displays_[0];
827 auto rounding = this->draw_rounding;
828 this->width_ = display->get_native_width();
829 this->height_ = display->get_native_height();
830 // cater for displays with dimensions that don't divide by the required rounding
831 auto width = (this->width_ + rounding - 1) / rounding * rounding;
832 auto height = (this->height_ + rounding - 1) / rounding * rounding;
833 auto frac = this->buffer_frac_;
834 if (frac == 0)
835 frac = 1;
836 auto buf_bytes = clamp_at_least(width * height / frac * LV_COLOR_DEPTH / 8, MIN_BUFFER_SIZE);
837 void *buffer = nullptr;
838 // for small buffers, try to allocate in internal memory first to improve performance
839 if (this->buffer_frac_ >= MIN_BUFFER_FRAC / 2)
840 buffer = lv_alloc_draw_buf(buf_bytes, true); // NOLINT
841 if (buffer == nullptr)
842 buffer = lv_alloc_draw_buf(buf_bytes, false); // NOLINT
843 // if specific buffer size not set and can't get 100%, try for a smaller one
844 if (buffer == nullptr && this->buffer_frac_ == 0) {
845 frac = MIN_BUFFER_FRAC;
846 buf_bytes /= MIN_BUFFER_FRAC;
847 buffer = lv_alloc_draw_buf(buf_bytes, false); // NOLINT
848 }
849 this->buffer_frac_ = frac;
850 if (buffer == nullptr) {
851 this->status_set_error(LOG_STR("Memory allocation failure"));
852 this->mark_failed();
853 return;
854 }
855 this->draw_buf_ = static_cast<uint8_t *>(buffer);
856 this->set_resolution_();
857 lv_display_set_color_format(this->disp_, LV_COLOR_FORMAT_DEFAULT);
858 lv_display_set_flush_cb(this->disp_, static_flush_cb);
859 lv_display_set_user_data(this->disp_, this);
860 lv_display_add_event_cb(this->disp_, rounder_cb, LV_EVENT_INVALIDATE_AREA, this);
861 lv_display_set_buffers(this->disp_, this->draw_buf_, nullptr, buf_bytes,
862 this->full_refresh_ ? LV_DISPLAY_RENDER_MODE_FULL : LV_DISPLAY_RENDER_MODE_PARTIAL);
863 if (this->rotation_type_ == ROTATION_SOFTWARE) {
864 this->rotate_buf_ = static_cast<lv_color_t *>(lv_alloc_draw_buf(buf_bytes, false)); // NOLINT
865 if (this->rotate_buf_ == nullptr) {
866 this->status_set_error(LOG_STR("Memory allocation failure"));
867 this->mark_failed();
868 return;
869 }
870#ifdef USE_ESP32_VARIANT_ESP32P4
871 ppa_client_config_t ppa_config{};
872 ppa_config.oper_type = PPA_OPERATION_SRM;
873 ppa_config.max_pending_trans_num = 1;
874 if (ppa_register_client(&ppa_config, &this->ppa_client_) != ESP_OK) {
875 ESP_LOGW(TAG, "PPA client registration failed, using software rotation");
876 this->ppa_client_ = nullptr;
877 }
878#endif
879 }
880 if (this->draw_start_callback_ != nullptr) {
881 lv_display_add_event_cb(this->disp_, render_start_cb, LV_EVENT_RENDER_START, this);
882 }
883 if (this->draw_end_callback_ != nullptr || this->update_when_display_idle_) {
884 lv_display_add_event_cb(this->disp_, render_end_cb, LV_EVENT_REFR_READY, this);
885 }
886 this->refr_timer_ = lv_display_get_refr_timer(this->disp_);
887 lv_timer_set_period(this->refr_timer_, this->refr_timer_period_);
888#if LV_USE_LOG
889 lv_log_register_print_cb([](lv_log_level_t level, const char *buf) {
890 auto *next = strchr(buf, ')');
891 if (next != nullptr)
892 buf = next + 1;
893 while (isspace(*buf))
894 buf++;
895 if (level >= sizeof(LOG_LEVEL_MAP) / sizeof(LOG_LEVEL_MAP[0]))
896 level = sizeof(LOG_LEVEL_MAP) / sizeof(LOG_LEVEL_MAP[0]) - 1;
897 esp_log_printf_(LOG_LEVEL_MAP[level], TAG, 0, "%.*s", (int) strlen(buf) - 1, buf);
898 });
899#endif
900 this->show_page(0, LV_SCREEN_LOAD_ANIM_NONE, 0);
901 lv_display_trigger_activity(this->disp_);
902 this->update_orientation_();
903}
904
906 // update indicators
907 if (this->is_paused()) {
908 return;
909 }
910 this->idle_callbacks_.call(lv_display_get_inactive_time(this->disp_));
911}
912
914 if (this->paused_) {
915 if (this->show_snow_)
916 this->write_random_();
917 return;
918 }
919 // Pause/resume the display's own refresh timer to track its busy state. While paused, LVGL
920 // still keeps track of invalidated areas but won't render or flush them, so nothing needs to
921 // be discarded or replayed: once resumed, the accumulated areas are simply drawn as normal.
922 // Input events and other timers keep being processed below regardless of this state.
923 if (this->update_when_display_idle_) {
924 bool busy = this->displays_busy_();
925 if (busy && !this->refr_timer_paused_) {
926 this->refr_timer_paused_ = true;
927 // calling lv_timer_pause() here would be ineffective; LVGL pauses and resumes the timer based on its own internal
928 // state, which is not aware of the display's busy state. Instead, we extend the timer period to avoid it firing
929 // while the display is busy.
930 lv_timer_set_period(this->refr_timer_, 5 * 60 * 1000);
931 } else if (!busy && this->refr_timer_paused_) {
932 this->refr_timer_paused_ = false;
933 lv_timer_set_period(this->refr_timer_, this->refr_timer_period_);
934 // Don't wait for the timer's next natural period: refresh right away now that the
935 // display is idle again.
936 lv_timer_ready(this->refr_timer_);
937 }
938 }
939 lv_timer_handler();
940}
941
942#ifdef USE_LVGL_ANIMIMG
943void lv_animimg_stop(lv_obj_t *obj) {
944 int32_t duration = lv_animimg_get_duration(obj);
945 lv_animimg_set_duration(obj, 0);
946 lv_animimg_start(obj);
947 lv_animimg_set_duration(obj, duration);
948}
949#endif
950void LvglComponent::static_flush_cb(lv_display_t *disp_drv, const lv_area_t *area, uint8_t *color_p) {
951 reinterpret_cast<LvglComponent *>(lv_display_get_user_data(disp_drv))->flush_cb_(disp_drv, area, color_p);
952}
953
954#ifdef USE_LVGL_SCALE
962void lv_scale_draw_event_cb(lv_event_t *e, int16_t range_start, int16_t range_end, lv_color_t color_start,
963 lv_color_t color_end, int width, bool local) {
964 auto *scale = static_cast<lv_obj_t *>(lv_event_get_target(e));
965 lv_draw_task_t *task = lv_event_get_draw_task(e);
966
967 if (lv_draw_task_get_type(task) == LV_DRAW_TASK_TYPE_LINE) {
968 auto *line_dsc = static_cast<lv_draw_line_dsc_t *>(lv_draw_task_get_draw_dsc(task));
969 int tick = line_dsc->base.id2;
970 if (tick >= range_start && tick <= range_end) {
971 int ratio;
972 if (local) {
973 int range = range_end - range_start;
974 tick -= range_start;
975 ratio = range == 0 ? 0 : (tick * 255) / range;
976 } else {
977 // total tick count is guaranteed to be at least 2.
978 ratio = (line_dsc->base.id1 * 255) / (lv_scale_get_total_tick_count(scale) - 1);
979 }
980 line_dsc->color = lv_color_mix(color_end, color_start, ratio);
981 line_dsc->width += width;
982 }
983 }
984}
985#endif // USE_LVGL_SCALE
986
987#ifdef USE_LVGL_GRADIENT
995lv_color_t lv_grad_calculate_color(const lv_grad_dsc_t *dsc, int32_t pos) {
996 if (dsc->stops_count == 0)
997 return lv_color_black();
998 if (dsc->stops_count == 1 || pos <= dsc->stops[0].frac)
999 return dsc->stops[0].color;
1000 if (pos >= dsc->stops[dsc->stops_count - 1].frac)
1001 return dsc->stops[dsc->stops_count - 1].color;
1002 int i = 1;
1003 while (i < dsc->stops_count && dsc->stops[i].frac < pos)
1004 i++;
1005 auto *stop1 = &dsc->stops[i - 1];
1006 auto *stop2 = &dsc->stops[i];
1007 int32_t range = stop2->frac - stop1->frac;
1008 int32_t offset = pos - stop1->frac;
1009 return lv_color_mix(stop2->color, stop1->color, range == 0 ? 0 : (offset * 255) / range);
1010}
1011#endif // USE_LVGL_GRADIENT
1012
1013#ifdef USE_LVGL_MENU
1014bool lv_menu_back_button_is_root_click(lv_obj_t *menu, lv_event_t *e) {
1015 return lv_event_get_code(e) == LV_EVENT_CLICKED &&
1016 lv_menu_back_button_is_root(menu, static_cast<lv_obj_t *>(lv_event_get_target(e)));
1017}
1018#endif // USE_LVGL_MENU
1019
1021 auto *indev = lv_indev_get_act();
1022 if (indev == nullptr) {
1023 return {INT32_MAX, INT32_MAX};
1024 }
1025 lv_point_t point;
1026 lv_indev_get_point(indev, &point);
1027 lv_area_t coords;
1028 lv_obj_get_coords(obj, &coords);
1029 point.x -= coords.x1;
1030 point.y -= coords.y1;
1031 return point;
1032}
1033
1034static void lv_container_constructor(const lv_obj_class_t *class_p, lv_obj_t *obj) {
1035 LV_TRACE_OBJ_CREATE("begin");
1036 lv_obj_set_clickable(obj, false);
1037 LV_UNUSED(class_p);
1038}
1039
1040// Container class. Name is based on LVGL naming convention but upper case to keep ESPHome clang-tidy happy
1041const lv_obj_class_t LV_CONTAINER_CLASS = {
1042 .base_class = &lv_obj_class,
1043 .constructor_cb = lv_container_constructor,
1044 .name = "lv_container",
1045};
1046
1047lv_obj_t *lv_container_create(lv_obj_t *parent) {
1048 lv_obj_t *obj = lv_obj_class_create_obj(&LV_CONTAINER_CLASS, parent);
1049 lv_obj_class_init_obj(obj);
1050 return obj;
1051}
1052
1053#ifdef USE_LVGL_LIST
1054int lv_list_get_row_index(lv_obj_t *list, lv_obj_t *child) {
1055 for (lv_obj_t *obj = child; obj != nullptr; obj = lv_obj_get_parent(obj)) {
1056 if (lv_obj_get_parent(obj) == list)
1057 return lv_obj_get_index(obj);
1058 }
1059 ESP_LOGW(TAG, "lvgl.list: entry is not inside the list it was added to");
1060 return -1;
1061}
1062
1063lv_obj_t *lv_list_get_row_for_remove(lv_obj_t *list, int index) {
1064 lv_obj_t *child = index < 0 ? nullptr : lv_obj_get_child(list, index);
1065 if (child == nullptr) {
1066 ESP_LOGW(TAG, "lvgl.list.remove: index %d is out of range, ignoring", index);
1067 }
1068 return child;
1069}
1070#endif // USE_LVGL_LIST
1071} // namespace esphome::lvgl
1072
1073lv_result_t lv_mem_test_core() { return LV_RESULT_OK; }
1074
1076
1078
1079#ifndef USE_ESP32
1080void *lv_malloc_core(size_t size) {
1081 auto *ptr = malloc(size); // NOLINT
1082 if (ptr == nullptr) {
1083 ESP_LOGE(esphome::lvgl::TAG, "Failed to allocate %zu bytes", size);
1084 }
1085 return ptr;
1086}
1087void lv_free_core(void *ptr) { return free(ptr); } // NOLINT
1088void *lv_realloc_core(void *ptr, size_t size) { return realloc(ptr, size); } // NOLINT
1089
1090void lv_mem_monitor_core(lv_mem_monitor_t *mon_p) { memset(mon_p, 0, sizeof(lv_mem_monitor_t)); }
1091static void *lv_alloc_draw_buf(size_t size, bool internal) {
1092 return malloc(size); // NOLINT
1093}
1094
1095#else
1096static unsigned cap_bits = MALLOC_CAP_SPIRAM | MALLOC_CAP_8BIT; // NOLINT
1097
1098static void *lv_alloc_draw_buf(size_t size, bool internal) {
1099 void *buffer;
1100 size = LV_ROUND_UP(size, LV_DRAW_BUF_ALIGN);
1101 buffer = heap_caps_aligned_alloc(LV_DRAW_BUF_ALIGN, size, internal ? MALLOC_CAP_8BIT : cap_bits); // NOLINT
1102 if (buffer == nullptr) {
1103 ESP_LOGW(esphome::lvgl::TAG, "Failed to allocate %zu bytes for %sdraw buffer", size, internal ? "internal " : "");
1104 }
1105 return buffer;
1106}
1107
1108void lv_mem_monitor_core(lv_mem_monitor_t *mon_p) {
1109 multi_heap_info_t heap_info;
1110 heap_caps_get_info(&heap_info, cap_bits);
1111 mon_p->total_size = heap_info.total_allocated_bytes + heap_info.total_free_bytes;
1112 mon_p->free_size = heap_info.total_free_bytes;
1113 mon_p->max_used = heap_info.total_allocated_bytes;
1114 mon_p->free_biggest_size = heap_info.largest_free_block;
1115 mon_p->used_cnt = heap_info.allocated_blocks;
1116 mon_p->free_cnt = heap_info.free_blocks;
1117 mon_p->used_pct = heap_info.allocated_blocks * 100 / (heap_info.allocated_blocks + heap_info.free_blocks);
1118 mon_p->frag_pct = 0;
1119}
1120
1121void *lv_malloc_core(size_t size) {
1122 void *ptr;
1123 ptr = heap_caps_malloc(size, cap_bits);
1124 if (ptr == nullptr) {
1125 cap_bits = MALLOC_CAP_8BIT;
1126 ptr = heap_caps_malloc(size, cap_bits);
1127 }
1128 if (ptr == nullptr) {
1129 ESP_LOGE(esphome::lvgl::TAG, "Failed to allocate %zu bytes", size);
1130 return nullptr;
1131 }
1132 ESP_LOGV(esphome::lvgl::TAG, "allocate %zu - > %p", size, ptr);
1133 return ptr;
1134}
1135
1136void lv_free_core(void *ptr) {
1137 ESP_LOGV(esphome::lvgl::TAG, "free %p", ptr);
1138 if (ptr == nullptr)
1139 return;
1140 heap_caps_free(ptr);
1141}
1142
1143void *lv_realloc_core(void *ptr, size_t size) {
1144 ESP_LOGV(esphome::lvgl::TAG, "realloc %p: %zu", ptr, size);
1145 return heap_caps_realloc(ptr, size, cap_bits);
1146}
1147#endif
uint8_t l
Definition bl0906.h:0
void mark_failed()
Mark this component as failed.
bool is_ready() const
Fixed-capacity vector - sized once through init() or try_init(); push_back never reallocates This avo...
Definition helpers.h:624
size_t size() const
Definition helpers.h:800
void set_parent(T *parent)
Set the parent of this object.
Definition helpers.h:2081
Function-pointer-only templatable storage (4 bytes on 32-bit).
Definition automation.h:20
T value(X... x) const
Definition automation.h:68
void trigger(const Ts &...x) ESPHOME_ALWAYS_INLINE
Inform the parent automation that the event has triggered.
Definition automation.h:556
TemplatableFn< uint32_t > timeout_
IdleTrigger(LvglComponent *parent, TemplatableFn< uint32_t > timeout)
void set_obj(lv_obj_t *lv_obj) override
LVEncoderListener(lv_indev_type_t type, uint16_t long_press_time, uint16_t long_press_repeat_time)
LVTouchListener(uint16_t long_press_time, uint16_t long_press_repeat_time, LvglComponent *parent)
touchscreen::TouchPoint touch_point_
void update(const touchscreen::TouchPoints_t &tpoints) override
void set_obj(lv_obj_t *lv_obj) override
virtual void set_obj(lv_obj_t *lv_obj)
void set_obj(lv_obj_t *lv_obj) override
void setup(size_t index)
void set_selected_text(const std::string &text, lv_anim_enable_t anim)
FixedVector< const char * > options_
virtual void set_selected_index(size_t index, lv_anim_enable_t anim)=0
virtual size_t get_selected_index()=0
virtual void set_option_string(const char *options)=0
void set_options(FixedVector< const char * > options)
void set_obj(lv_obj_t *lv_obj) override
void add_column_width_pct(uint32_t col, uint8_t pct)
FixedVector< ColumnPct > column_pct_
Component for rendering LVGL.
void set_paused(bool paused, bool show_snow)
display::DisplayRotation rotation_
void rotate_coordinates(int32_t &x, int32_t &y) const
std::vector< LvPageType * > pages_
void set_rotation(display::DisplayRotation rotation)
bool ppa_rotate_(const lv_color_data *src, lv_color_data *dst, uint16_t width, uint16_t height, uint32_t height_rounded)
CallbackManager< void(uint32_t)> idle_callbacks_
void show_next_page(lv_screen_load_anim_t anim, uint32_t time)
std::vector< display::Display * > displays_
static void esphome_lvgl_init()
Initialize the LVGL library and register custom events.
void show_prev_page(lv_screen_load_anim_t anim, uint32_t time)
ppa_client_handle_t ppa_client_
LvglComponent(std::vector< display::Display * > displays, float buffer_frac, bool full_refresh, int draw_rounding, bool resume_on_input, bool update_when_display_idle, RotationType rotation_type)
static void render_start_cb(lv_event_t *event)
static lv_point_t get_touch_relative_to_obj(lv_obj_t *obj)
void add_on_idle_callback(F &&callback)
void add_page(LvPageType *page)
static void static_flush_cb(lv_display_t *disp_drv, const lv_area_t *area, uint8_t *color_p)
static void render_end_cb(lv_event_t *event)
static void lv_obj_set_custom_flag(lv_obj_t *obj, lv_obj_flag_t flag, bool value)
void draw_buffer_(const lv_area_t *area, lv_color_data *ptr)
void show_page(size_t index, lv_screen_load_anim_t anim, uint32_t time)
static void add_event_cb(lv_obj_t *obj, event_callback_t callback, lv_event_code_t event, void *user_data=nullptr)
void flush_cb_(lv_display_t *disp_drv, const lv_area_t *area, uint8_t *color_p)
uint8_t options
uint8_t type
NameCopy * next
uint32_t buffer
Definition gif.cpp:50
int ret
void lv_mem_deinit()
void lv_mem_init()
void lv_free_core(void *ptr)
void * lv_malloc_core(size_t size)
void lv_mem_monitor_core(lv_mem_monitor_t *mon_p)
void * lv_realloc_core(void *ptr, size_t size)
lv_result_t lv_mem_test_core()
Range range
Definition msa3xx.h:0
uint8_t duration
Definition msa3xx.h:0
@ DISPLAY_ROTATION_270_DEGREES
Definition display.h:139
@ DISPLAY_ROTATION_180_DEGREES
Definition display.h:138
@ DISPLAY_ROTATION_90_DEGREES
Definition display.h:137
static void int int int int const uint8_t * ptr
Definition display.cpp:63
bool lv_menu_back_button_is_root_click(lv_obj_t *menu, lv_event_t *e)
lv_color_t lv_grad_calculate_color(const lv_grad_dsc_t *dsc, int32_t pos)
void lv_animimg_stop(lv_obj_t *obj)
uint16_t lv_color_data
int lv_list_get_row_index(lv_obj_t *list, lv_obj_t *child)
void(lv_event_t *) event_callback_t
const lv_obj_class_t LV_CONTAINER_CLASS
uint32_t lv_table_get_selected_row(lv_obj_t *obj)
lv_event_code_t lv_update_event
int16_t lv_get_needle_angle_for_value(lv_obj_t *obj, int32_t value)
lv_obj_t * lv_container_create(lv_obj_t *parent)
lv_obj_t * lv_list_get_row_for_remove(lv_obj_t *list, int index)
std::string lv_event_code_name_for(lv_event_t *event)
ESPHOME_LOG_TAG(TAG, "lvgl")
void lv_scale_draw_event_cb(lv_event_t *e, int16_t range_start, int16_t range_end, lv_color_t color_start, lv_color_t color_end, int width, bool local)
Function to apply colors to ticks based on position.
uint32_t lv_table_get_selected_column(lv_obj_t *obj)
std::vector< TouchPoint > TouchPoints_t
Definition touchscreen.h:29
T clamp_at_most(T value, U max)
Definition helpers.h:2478
void HOT esp_log_printf_(int level, const char *tag, int line, const char *format,...)
Definition log.cpp:21
const void size_t len
Definition hal.h:68
uint16_t size
Definition helpers.cpp:34
T clamp_at_least(T value, U min)
Definition helpers.h:2473
uint32_t random_uint32()
Return a random 32-bit unsigned integer.
Definition helpers.cpp:12
size_t size_t pos
Definition helpers.h:1255
const void * src
Definition hal.h:68
uint32_t IRAM_ATTR HOT millis()
Definition hal.cpp:25
STL namespace.
static void uint32_t
uint16_t length
Definition tt21100.cpp:0
uint16_t x
Definition tt21100.cpp:5
uint16_t y
Definition tt21100.cpp:6
uint8_t orientation
Definition tt21100.cpp:9