Skip to content

Commit e220938

Browse files
authored
feat(display): Update to support multiple displays (#391)
* feat(display): Update to support multiple displays * Update `espp::Display` to store the `flush` and `rotate` functions and to pass `this` as the context for the lvgl `display` and `event` callback This allows any arbitrary function (including bound class member functions) to be passed for the flush callback and rotate callbacks, instead of requiring that they match the signatures and linkage of the lvgl lower-layer functions. This will enable a follow-up PR which allows the display drivers to no longer be static classes, further enabling multiple displays to be used in a single project. Build and run `esp-box/example`, `t-dongle-s3/example` to ensure the existing code still works. * update naming / docs * update to assert if flush is nullptr and warn if rotation is nullptr
1 parent 8013682 commit e220938

File tree

1 file changed

+74
-11
lines changed

1 file changed

+74
-11
lines changed

components/display/include/display.hpp

+74-11
Original file line numberDiff line numberDiff line change
@@ -44,13 +44,18 @@ template <typename Pixel> class Display : public BaseComponent {
4444
/**
4545
* @brief Callback for lvgl to flush segments of pixel data from the pixel
4646
* buffers to the display.
47+
* @param disp The display to flush data to.
48+
* @param area The area of the display to flush data to.
49+
* @param color_map The color data to flush to the display.
4750
*/
48-
using flush_fn = lv_display_flush_cb_t;
51+
typedef std::function<void(lv_display_t *disp, const lv_area_t *area, uint8_t *color_map)>
52+
flush_fn;
4953

5054
/**
5155
* @brief Callback for lvgl event handler to reconfigure the display hardware.
56+
* @param rotation The new rotation setting for the display.
5257
*/
53-
typedef void (*rotation_fn)(const DisplayRotation &rotation);
58+
typedef std::function<void(const DisplayRotation &rotation)> rotation_fn;
5459

5560
/**
5661
* @brief Callback for setting the display brightness.
@@ -325,20 +330,64 @@ template <typename Pixel> class Display : public BaseComponent {
325330
*/
326331
size_t vram_size_bytes() const { return display_buffer_px_size_ * sizeof(Pixel); }
327332

333+
/**
334+
* @brief Set the rotation of the display.
335+
* @note This function is called by the event handler when the display
336+
* resolution changes, so that the display hardware can be reconfigured
337+
* to match the new software rotation setting.
338+
* @param rotation The new rotation setting for the display.
339+
*/
340+
void set_rotation(DisplayRotation rotation) {
341+
if (rotation_callback_ != nullptr) {
342+
rotation_callback_(rotation);
343+
}
344+
}
345+
346+
/**
347+
* @brief Flush the data to the display.
348+
* @warning This function is called by the LVGL flush callback, so it is
349+
* recommended to not call this function directly.
350+
* @param disp The display to flush data to.
351+
* @param area The area of the display to flush data to.
352+
* @param color_map The color data to flush to the display.
353+
*/
354+
void flush(lv_display_t *disp, const lv_area_t *area, uint8_t *color_map) {
355+
if (flush_callback_ != nullptr) {
356+
flush_callback_(disp, area, color_map);
357+
}
358+
}
359+
328360
protected:
329361
/**
330362
* @brief LVGL event handler.
363+
* @param event The event to handle.
331364
*/
332365
static void event_cb(lv_event_t *event) {
333366
if (lv_event_get_code(event) == LV_EVENT_RESOLUTION_CHANGED) {
334-
auto rotation = lv_display_get_rotation(lv_display_get_default());
335-
auto rotation_callback = reinterpret_cast<rotation_fn>(lv_event_get_user_data(event));
336-
if (rotation_callback != nullptr) {
337-
rotation_callback(static_cast<DisplayRotation>(rotation));
367+
auto rotation =
368+
static_cast<DisplayRotation>(lv_display_get_rotation(lv_display_get_default()));
369+
auto display = static_cast<Display *>(lv_display_get_user_data(lv_disp_get_default()));
370+
if (display != nullptr) {
371+
display->set_rotation(rotation);
338372
}
339373
}
340374
};
341375

376+
/**
377+
* @brief Callback for the LVGL flush function to call when it needs to flush
378+
* data to the display.
379+
* @param disp The display to flush data to.
380+
* @param area The area of the display to flush data to.
381+
* @param color_map The color data to flush to the display.
382+
*/
383+
static void flush_cb(lv_display_t *disp, const lv_area_t *area, uint8_t *color_map) {
384+
// use the display to call the registered flush callback appropriately
385+
auto display = static_cast<Display *>(lv_display_get_user_data(disp));
386+
if (display != nullptr) {
387+
display->flush(disp, area, color_map);
388+
}
389+
}
390+
342391
/**
343392
* @brief Initialize the lvgl subsystem, display buffer configuration, and
344393
* display driver. Start the task to run the high-priority lvgl
@@ -350,19 +399,31 @@ template <typename Pixel> class Display : public BaseComponent {
350399
*/
351400
void init_gfx(const flush_fn flush_callback, const rotation_fn rotation_callback,
352401
DisplayRotation rotation, const Task::BaseConfig &task_config) {
402+
// save the callbacks
403+
if (flush_callback == nullptr) {
404+
logger_.error("No flush callback provided, cannot initialize display!");
405+
assert(false); // cannot continue without a flush callback
406+
}
407+
flush_callback_ = flush_callback;
408+
if (rotation_callback == nullptr) {
409+
logger_.warn("No rotation callback provided, resolution changed event will not automatically "
410+
"update the display hardware rotation.");
411+
}
412+
rotation_callback_ = rotation_callback;
413+
353414
lv_init();
354415

355416
display_ = lv_display_create(width_, height_);
417+
// store a pointer to this object in the display user data, so that we can
418+
// access it in the flush callback
419+
lv_display_set_user_data(display_, this);
356420

357421
// Configure the lvgl display buffer with our pixel buffers
358422
lv_display_set_buffers(display_, vram_0_, vram_1_, vram_size_bytes(),
359423
LV_DISPLAY_RENDER_MODE_PARTIAL);
360-
lv_display_set_flush_cb(display_, flush_callback);
424+
lv_display_set_flush_cb(display_, Display::flush_cb);
361425

362-
if (rotation_callback != nullptr) {
363-
lv_display_add_event_cb(display_, event_cb, LV_EVENT_RESOLUTION_CHANGED,
364-
reinterpret_cast<void *>(rotation_callback));
365-
}
426+
lv_display_add_event_cb(display_, event_cb, LV_EVENT_RESOLUTION_CHANGED, this);
366427

367428
lv_display_set_rotation(display_, static_cast<lv_display_rotation_t>(rotation));
368429

@@ -449,6 +510,8 @@ template <typename Pixel> class Display : public BaseComponent {
449510
std::unique_ptr<Task> task_;
450511
size_t width_;
451512
size_t height_;
513+
flush_fn flush_callback_{nullptr};
514+
rotation_fn rotation_callback_{nullptr};
452515
size_t display_buffer_px_size_;
453516
Pixel *vram_0_{nullptr};
454517
Pixel *vram_1_{nullptr};

0 commit comments

Comments
 (0)