#ifndef DISPLAY_H #define DISPLAY_H #include #include #include "time_keeper.h" #include "panel.h" #define LVGL_TICK_PERIOD_MS 5 #define LVGL_TASK_STACK_SIZE (4 * 1024) #define LVGL_TASK_PRIORITY 2 class Display { public: // // CONSTRUCTORS Display(const Display &) = delete; Display(Display &) = delete; Display &operator=(Display &) = delete; /** * Construct a new Display using an IPanelDevice. * * @param device An object that implements the IPanelDevice interface. */ explicit Display(IPanelDevice &device); ~Display() = default; // // GETTERS /** * Getter for accessing LVGL display handle. * * @sa ScopedLock for calling custom LVGL API's not implemented by Display. */ [[nodiscard]] inline const lv_display_t *get() const { return lv_display_; } /** * Getter for accessing LVGL display handle. * * @sa ScopedLock for calling custom LVGL API's not implemented by Display. */ [[nodiscard]] inline lv_display_t *get() { return lv_display_; } /// Dereference operator for accessing LVGL display handle. [[nodiscard]] inline const lv_display_t *operator*() const { return get(); } /// Dereference operator for accessing LVGL display handle. [[nodiscard]] inline lv_display_t *operator*() { return get(); } // // LVGL OPERATIONS /** * Create a LVGL label with some given text on the current display. * The name of the object can be reused to change text on this label later. * * @param text Text to write to the display. * @param name Name for the LVGL label object associated with this text. * @param long_mode LVGL long mode for text wider than the current display. * @param align LVGL alignment to use for placing the label on the display. */ void set_text(const char *text, const char *name, lv_label_long_mode_t long_mode = LV_LABEL_LONG_SCROLL_CIRCULAR, lv_align_t align = LV_ALIGN_TOP_MID); // // TYPE DEFINITIONS /** * Obtains LVGL API mutex lock for the duration of local scope. * * LVGL library is not thread-safe, this example calls LVGL APIs from tasks. */ struct ScopedLock { explicit ScopedLock() { _lock_acquire(&lv_lock_); } ~ScopedLock() { _lock_release(&lv_lock_); } /// Mutex used to protect LVGL API calls. static _lock_t lv_lock_; }; // // PUBLIC STATIC MEMBERS /// Public static TimeKeeper for managing ESP timers across all displays. static TimeKeeper timers_; private: /// Registers LVGL draw buffers for this display. void register_draw_buffer(); /// Registers LVGL ticker timer callback for rendering this display. static void register_lvgl_tick_timer(); static bool lvgl_flush_ready_cb(esp_lcd_panel_io_handle_t panel, esp_lcd_panel_io_event_data_t *data, void *user_ctx); static void lvgl_flush_cb(lv_display_t *display, const lv_area_t *area, uint8_t *px_map); static void lvgl_increase_tick_cb(void *arg); [[noreturn]] static void lvgl_port_task(void *arg); // // PRIVATE MEMBERS /// Panel associated with this Display. Panel panel_; /// LVGL display handle. lv_display_t *lv_display_; /// LVGL draw buffer associated with this Display's lv_display_t. void *lv_buf_; /** * LVGL object handles stored in the LVGL screen associated with this Display. * * @sa Display::set_text * @sa lv_display_get_screen_active */ std::unordered_map lv_objects_; }; #endif // DISPLAY_H