How to get the total area to be updated inside custom flush callback function

Description

What MCU/Processor/Board and compiler are you using?

ESP32-S3 + E-PAPER SCREEN

What LVGL version are you using?

v8.4.0

What do you want to achieve?

I am developing an app with lvgl + GxEPD2 to drive epaper screen, due to the characteristic of epaper displays, we need to perform a full refresh after every 5 partitial refresh, but I got some questions when writing the custom flush callback function.

I have defined a display buffer with the 1/4 size of the screen, so everytime the flush callback is called, it just got a partitial area even I need to refresh the whole screen. So my question is, how to check this callback is called during the full refresh process or during the partitial refresh process, so I can start a draw indicating the GxEPD2 to perform full refresh?

What have you tried so far?

Code to reproduce

Add a code snippet which can run in the simulator. It should contain only the relevant code that compiles without errors when separated from your main code base.

The code block(s) should be formatted like:

#define BUFFER_SIZE (SCREEN_WIDTH * SCREEN_HEIGHT * sizeof(lv_color_t) / 4)
static lv_color_t buf[BUFFER_SIZE];
static lv_disp_draw_buf_t draw_buf;
static uint8_t epaper_partial_counter = 0;
#define EPAPER_PARTIAL_CNT 5

void init() {
  lv_init();
  lv_disp_draw_buf_init(&draw_buf, buf, NULL, SCREEN_WIDTH * SCREEN_HEIGHT);

  static lv_disp_drv_t disp_drv;
  lv_disp_drv_init(&disp_drv);
  disp_drv.hor_res = SCREEN_WIDTH;
  disp_drv.ver_res = SCREEN_HEIGHT;
  disp_drv.flush_cb = lvgl_disp_flush;
  disp_drv.draw_buf = &draw_buf;
  lv_disp_drv_register(&disp_drv);
}

void lvgl_disp_flush(lv_disp_drv_t *disp, const lv_area_t *area, lv_color_t *color_p) {
  // this function will be call everytime a refresh is needed
  // so how do I known this call is during a full refresh process or during a partitial refresh process?
    if (full_refresh) // HOW DO I KNOW THIS IS A FULL REFRESH?
    {
      display.setFullWindow();
    }
    else
    {
      display.setPartialWindow(area->x1, area->y1, w, h);
    }
}

very strange configuration.

your buf variable is lv_color_t type, and if you want to configure buf size as 1/4 of screen size BUFFER_SIZE should be (SCREEN_WIDTH * SCREEN_HEIGHT) / 4 and

lv_disp_draw_buf_init(&draw_buf, buf, NULL, BUFFER_SIZE );

in general LVGL redraw only invalidated objects on the screen, but you can enable full refresh.
but if you configure draw buffer size as a part of screen, LVGL can redraw segment that fit into the draw buffer and even entire screen will be rendered in parts.

if you change only 1 object on the screen LVGL will try to redraw only 1 object on the screen. If you need to redraw whole screen even if single object changed use lv_obj_invalidate(lv_scr_act()).

You’re absolutely right! The BUFFER_SIZE definition should be (SCREEN_WIDTH * SCREEN_HEIGHT) / 4 if the intention is to allocate a quarter of the screen size in terms of pixels. Using sizeof(lv_color_t) in the calculation would result in an incorrect buffer size, as lv_color_t already represents a single color unit.

Regarding LVGL’s rendering, it’s designed to optimize redrawing by refreshing only invalidated areas. Configuring a smaller draw buffer means LVGL will render the screen in segments, which is useful for memory-constrained environments. If full-screen updates are required regardless of what changes, lv_obj_invalidate(lv_scr_act()) ensures the entire screen is marked for redrawing.

So, the correct approach would be:

c

CopyEdit

#define BUFFER_SIZE (SCREEN_WIDTH * SCREEN_HEIGHT) / 4

And ensure lv_disp_draw_buf_init() is used correctly with this value. Thanks for pointing it out!
thai tea

Thanks for your replies first.

I know I need to narrow down the refresh area for each update, but due to the characteristic of the EPD screen, I need to do full refresh after 5 partitial refresh. If not setting the BUFFER_SIZE to the whole screen, how to do I know when should I perform the full refresh in the flush callback?

I am finding a unified approach to handle EPD special characteristic (full refresh and paritial refresh) in the flush callback (You can think that as “Object orentied pattern”), and keep the widget generic for other screen type (SPI TFT, RGB Screen, etc). If I mix the lv_obj_invalidate in the widgets, then the widgets are not good for migrations for different screen type. That’s what I concern about.

So I just need to know is there any way I can find out the total area pending to update inside flush callback.

you can find total area pending to update using area arg of lvgl_disp_flush. it have start and end coord of refreshed area

    lv_coord_t x1;
    lv_coord_t y1;
    lv_coord_t x2;
    lv_coord_t y2;

but if you will use draw buffer size as a part of full screen you will not know is current refresh cycle for full screen or not.
it can be assumed that there will be a full screen refresh if lvgl_disp_flush() receive area with x1 = 0, y1 = 0, x2 = width-1, y2 = height-1
how does your lvgl_disp_flush() use an color_p argument that points to an array of data to draw on the screen?

I am currently setting the buffer size as the screen size, and set full_refresh = true, so for each update (even a small area changed), lvgl_disp_flush will get the whole area from lv_area_t, and use a counter to record how many partitial updates are done.

As you said, If I set the buffer size to 1/4 of the screen size, I can’t tell which cycle I am in inside the lvgl_disp_flush function. I think this is the best solution I ever found.