mirror of
				https://github.com/esphome/esphome.git
				synced 2025-10-30 22:53:59 +00:00 
			
		
		
		
	[lvgl] Implement better software rotation (#7595)
This commit is contained in:
		| @@ -48,6 +48,7 @@ from .types import ( | ||||
|     FontEngine, | ||||
|     IdleTrigger, | ||||
|     ObjUpdateAction, | ||||
|     PauseTrigger, | ||||
|     lv_font_t, | ||||
|     lv_group_t, | ||||
|     lv_style_t, | ||||
| @@ -233,6 +234,8 @@ async def to_code(config): | ||||
|         frac = 8 | ||||
|     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_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: | ||||
|         await cg.get_variable(font) | ||||
| @@ -272,11 +275,19 @@ async def to_code(config): | ||||
|     async with LvContext(lv_component): | ||||
|         await generate_triggers(lv_component) | ||||
|         await generate_page_triggers(lv_component, config) | ||||
|         await initial_focus_to_code(config) | ||||
|         for conf in config.get(CONF_ON_IDLE, ()): | ||||
|             templ = await cg.templatable(conf[CONF_TIMEOUT], [], cg.uint32) | ||||
|             idle_trigger = cg.new_Pvariable(conf[CONF_TRIGGER_ID], lv_component, templ) | ||||
|             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: | ||||
|         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_DEFAULT_FONT, default="montserrat_14"): lvalid.lv_font, | ||||
|             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(df.CONF_LOG_LEVEL, default="WARN"): cv.one_of( | ||||
|                 *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(CONF_PAGES, CONF_PAGES): cv.ensure_list( | ||||
|                 container_schema(page_spec) | ||||
| @@ -356,6 +378,7 @@ CONFIG_SCHEMA = ( | ||||
|             cv.Optional(df.CONF_TOUCHSCREENS, default=None): touchscreen_schema, | ||||
|             cv.Optional(df.CONF_ENCODERS, default=None): ENCODERS_CONFIG, | ||||
|             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) | ||||
|   | ||||
| @@ -408,6 +408,7 @@ CONF_DEFAULT_FONT = "default_font" | ||||
| CONF_DEFAULT_GROUP = "default_group" | ||||
| CONF_DIR = "dir" | ||||
| CONF_DISPLAYS = "displays" | ||||
| CONF_DRAW_ROUNDING = "draw_rounding" | ||||
| CONF_EDITING = "editing" | ||||
| CONF_ENCODERS = "encoders" | ||||
| CONF_END_ANGLE = "end_angle" | ||||
| @@ -451,6 +452,8 @@ CONF_OFFSET_X = "offset_x" | ||||
| CONF_OFFSET_Y = "offset_y" | ||||
| CONF_ONE_CHECKED = "one_checked" | ||||
| CONF_ONE_LINE = "one_line" | ||||
| CONF_ON_PAUSE = "on_pause" | ||||
| CONF_ON_RESUME = "on_resume" | ||||
| CONF_ON_SELECT = "on_select" | ||||
| CONF_OPA = "opa" | ||||
| CONF_NEXT = "next" | ||||
| @@ -466,6 +469,7 @@ CONF_POINTS = "points" | ||||
| CONF_PREVIOUS = "previous" | ||||
| CONF_REPEAT_COUNT = "repeat_count" | ||||
| CONF_RECOLOR = "recolor" | ||||
| CONF_RESUME_ON_INPUT = "resume_on_input" | ||||
| CONF_RIGHT_BUTTON = "right_button" | ||||
| CONF_ROLLOVER = "rollover" | ||||
| 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); | ||||
| } | ||||
|  | ||||
| static void rounder_cb(lv_disp_drv_t *disp_drv, lv_area_t *area) { | ||||
|   // make sure all coordinates are even | ||||
|   if (area->x1 & 1) | ||||
|     area->x1--; | ||||
|   if (!(area->x2 & 1)) | ||||
|     area->x2++; | ||||
|   if (area->y1 & 1) | ||||
|     area->y1--; | ||||
|   if (!(area->y2 & 1)) | ||||
|     area->y2++; | ||||
|   // cater for display driver chips with special requirements for bounds of partial | ||||
|   // draw areas. Extend the draw area to satisfy: | ||||
|   // * Coordinates must be a multiple of draw_rounding | ||||
|   auto *comp = static_cast<LvglComponent *>(disp_drv->user_data); | ||||
|   auto draw_rounding = comp->draw_rounding; | ||||
|   // round down the start coordinates | ||||
|   area->x1 = area->x1 / draw_rounding * draw_rounding; | ||||
|   area->y1 = area->y1 / draw_rounding * draw_rounding; | ||||
|   // 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_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) { | ||||
|   this->paused_ = paused; | ||||
|   this->show_snow_ = show_snow; | ||||
|   this->snow_line_ = 0; | ||||
|   if (!paused && lv_scr_act() != nullptr) { | ||||
|     lv_disp_trig_activity(this->disp_);  // resets the inactivity time | ||||
|     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) { | ||||
|   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() | ||||
|   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_) { | ||||
|     display->draw_pixels_at(area->x1, area->y1, lv_area_get_width(area), lv_area_get_height(area), ptr, | ||||
|                             display::COLOR_ORDER_RGB, LV_BITNESS, LV_COLOR_16_SWAP); | ||||
|     ESP_LOGV(TAG, "draw buffer x1=%d, y1=%d, width=%d, height=%d", x1, y1, width, height); | ||||
|     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) { | ||||
|   if (!this->paused_) { | ||||
|     auto now = millis(); | ||||
|     this->draw_buffer_(area, (const uint8_t *) color_p); | ||||
|     ESP_LOGV(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)); | ||||
|     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), | ||||
|               lv_area_get_height(area), (int) (millis() - now)); | ||||
|   } | ||||
|   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 | ||||
| LVTouchListener::LVTouchListener(uint16_t long_press_time, uint16_t long_press_repeat_time) { | ||||
|   lv_indev_drv_init(&this->drv_); | ||||
| @@ -261,23 +321,31 @@ void LvKeyboardType::set_obj(lv_obj_t *lv_obj) { | ||||
| #endif  // USE_LVGL_KEYBOARD | ||||
|  | ||||
| void LvglComponent::write_random_() { | ||||
|   // length of 2 lines in 32 bit units | ||||
|   // we write 2 lines for the benefit of displays that won't write one line at a time. | ||||
|   size_t line_len = this->disp_drv_.hor_res * LV_COLOR_DEPTH / 8 / 4 * 2; | ||||
|   for (size_t i = 0; i != line_len; i++) { | ||||
|     ((uint32_t *) (this->draw_buf_.buf1))[i] = random_uint32(); | ||||
|   int iterations = 6 - lv_disp_get_inactive_time(this->disp_) / 60000; | ||||
|   if (iterations <= 0) | ||||
|     iterations = 1; | ||||
|   while (iterations-- != 0) { | ||||
|     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() { | ||||
| @@ -291,7 +359,7 @@ void LvglComponent::setup() { | ||||
|   auto *display = this->displays_[0]; | ||||
|   size_t buffer_pixels = display->get_width() * display->get_height() / this->buffer_frac_; | ||||
|   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 ESPHOME_LOG_LEVEL >= ESPHOME_LOG_LEVEL_ERROR | ||||
|     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_.flush_cb = static_flush_cb; | ||||
|   this->disp_drv_.rounder_cb = rounder_cb; | ||||
|   switch (display->get_rotation()) { | ||||
|     case display::DISPLAY_ROTATION_0_DEGREES: | ||||
|       break; | ||||
|     case display::DISPLAY_ROTATION_90_DEGREES: | ||||
|       this->disp_drv_.sw_rotate = true; | ||||
|       this->disp_drv_.rotated = LV_DISP_ROT_90; | ||||
|       break; | ||||
|     case display::DISPLAY_ROTATION_180_DEGREES: | ||||
|       this->disp_drv_.sw_rotate = true; | ||||
|       this->disp_drv_.rotated = LV_DISP_ROT_180; | ||||
|       break; | ||||
|     case display::DISPLAY_ROTATION_270_DEGREES: | ||||
|       this->disp_drv_.sw_rotate = true; | ||||
|       this->disp_drv_.rotated = LV_DISP_ROT_270; | ||||
|       break; | ||||
|   this->rotation = display->get_rotation(); | ||||
|   if (this->rotation != display::DISPLAY_ROTATION_0_DEGREES) { | ||||
|     this->rotate_buf_ = static_cast<lv_color_t *>(lv_custom_mem_alloc(buf_bytes));  // NOLINT | ||||
|     if (this->rotate_buf_ == nullptr) { | ||||
| #if ESPHOME_LOG_LEVEL >= ESPHOME_LOG_LEVEL_ERROR | ||||
|       ESP_LOGE(TAG, "Malloc failed to allocate %zu bytes", buf_bytes); | ||||
| #endif | ||||
|       this->mark_failed(); | ||||
|       this->status_set_error("Memory allocation failure"); | ||||
|       return; | ||||
|     } | ||||
|   } | ||||
|   display->set_rotation(display::DISPLAY_ROTATION_0_DEGREES); | ||||
|   this->disp_drv_.hor_res = (lv_coord_t) display->get_width(); | ||||
|   this->disp_drv_.ver_res = (lv_coord_t) display->get_height(); | ||||
|   ESP_LOGV(TAG, "sw_rotate = %d, rotated=%d", this->disp_drv_.sw_rotate, this->disp_drv_.rotated); | ||||
|   switch (this->rotation) { | ||||
|     default: | ||||
|       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_); | ||||
|   for (const auto &v : this->init_lambdas_) | ||||
|     v(this); | ||||
|   | ||||
| @@ -119,6 +119,7 @@ class LvglComponent : public PollingComponent { | ||||
|   void add_on_idle_callback(std::function<void(uint32_t)> &&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_init_lambda(const std::function<void(LvglComponent *)> &lamb) { this->init_lambdas_.push_back(lamb); } | ||||
|   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; } | ||||
|   void set_buffer_frac(size_t frac) { this->buffer_frac_ = frac; } | ||||
|   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); | ||||
|   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 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); | ||||
|   bool is_paused() const { return this->paused_; } | ||||
|   void add_page(LvPageType *page); | ||||
|   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); | ||||
| @@ -144,10 +155,17 @@ class LvglComponent : public PollingComponent { | ||||
|       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: | ||||
|   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); | ||||
|   std::vector<display::Display *> displays_{}; | ||||
|   lv_disp_draw_buf_t draw_buf_{}; | ||||
| @@ -157,14 +175,16 @@ class LvglComponent : public PollingComponent { | ||||
|   std::vector<LvPageType *> pages_{}; | ||||
|   size_t current_page_{0}; | ||||
|   bool show_snow_{}; | ||||
|   lv_coord_t snow_line_{}; | ||||
|   bool page_wrap_{true}; | ||||
|   bool resume_on_input_{}; | ||||
|   std::map<lv_group_t *, lv_obj_t *> focus_marks_{}; | ||||
|  | ||||
|   std::vector<std::function<void(LvglComponent *lv_component)>> init_lambdas_; | ||||
|   CallbackManager<void(uint32_t)> idle_callbacks_{}; | ||||
|   CallbackManager<void(bool)> pause_callbacks_{}; | ||||
|   size_t buffer_frac_{1}; | ||||
|   bool full_refresh_{}; | ||||
|   lv_color_t *rotate_buf_{}; | ||||
| }; | ||||
|  | ||||
| class IdleTrigger : public Trigger<> { | ||||
| @@ -176,6 +196,14 @@ class IdleTrigger : public Trigger<> { | ||||
|   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> { | ||||
|  public: | ||||
|   explicit LvglAction(std::function<void(LvglComponent *)> &&lamb) : action_(std::move(lamb)) {} | ||||
| @@ -200,7 +228,10 @@ class LVTouchListener : public touchscreen::TouchListener, public Parented<LvglC | ||||
|  public: | ||||
|   LVTouchListener(uint16_t long_press_time, uint16_t long_press_repeat_time); | ||||
|   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_; } | ||||
|  | ||||
|  protected: | ||||
| @@ -236,12 +267,18 @@ class LVEncoderListener : public Parented<LvglComponent> { | ||||
|     if (!this->parent_->is_paused()) { | ||||
|       this->pressed_ = pressed; | ||||
|       this->key_ = key; | ||||
|     } else if (!pressed) { | ||||
|       // maybe wakeup on release if paused | ||||
|       this->parent_->maybe_wakeup(); | ||||
|     } | ||||
|   } | ||||
|  | ||||
|   void set_count(int32_t count) { | ||||
|     if (!this->parent_->is_paused()) | ||||
|     if (!this->parent_->is_paused()) { | ||||
|       this->count_ = count; | ||||
|     } else { | ||||
|       this->parent_->maybe_wakeup(); | ||||
|     } | ||||
|   } | ||||
|  | ||||
|   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") | ||||
| FontEngine = lvgl_ns.class_("FontEngine") | ||||
| IdleTrigger = lvgl_ns.class_("IdleTrigger", automation.Trigger.template()) | ||||
| PauseTrigger = lvgl_ns.class_("PauseTrigger", automation.Trigger.template()) | ||||
| ObjUpdateAction = lvgl_ns.class_("ObjUpdateAction", automation.Action) | ||||
| LvglCondition = lvgl_ns.class_("LvglCondition", automation.Condition) | ||||
| LvglAction = lvgl_ns.class_("LvglAction", automation.Action) | ||||
|   | ||||
		Reference in New Issue
	
	Block a user