Flush_cb is called many times when just click button once


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

PC simulator (Mac)

What LVGL version are you using?


What do you want to achieve?

I am trying lv_ex_get_started_1 in lv_exmaples. I wonder why my flush callback (my_disp_flush_ex) is called many times (about 10 or more) when I just click the button once.
In my understanding, the flush_cb will be called when there is an update, and lvgl finishes drawing and then send back the data to me. Am I wrong?
Please give me some advice.
Many thanks for your help.

What have you tried so far?

This is the result after click the button once.

lvgl init ok!
create tick thread ok!
create lvtask thread ok!
hal init ok!
display and input device set ok!
x1:[0], y1:[0], x2:[799], y2:[799]
state= 1, x=90, y=44
touchpad_get_xy called
x1:[0], y1:[0], x2:[799], y2:[799]
state= 0, x=90, y=44
x1:[0], y1:[0], x2:[799], y2:[799]
x1:[0], y1:[0], x2:[799], y2:[799]
x1:[0], y1:[0], x2:[799], y2:[799]
x1:[0], y1:[0], x2:[799], y2:[799]
x1:[0], y1:[0], x2:[799], y2:[799]
x1:[0], y1:[0], x2:[799], y2:[799]
x1:[0], y1:[0], x2:[799], y2:[799]
x1:[0], y1:[0], x2:[799], y2:[799]
x1:[0], y1:[0], x2:[799], y2:[799]
x1:[0], y1:[0], x2:[799], y2:[799]

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:

#include <stdio.h>
#include <SDL2/SDL.h>
#include "lvgl/lvgl.h"
#include "lv_examples/lv_examples.h"
SDL_Window *gWin = NULL;
SDL_Renderer *gRenderer = NULL;
SDL_Texture *gTexture = NULL;
static uint32_t tft_buf[LV_HOR_RES_MAX * LV_VER_RES_MAX]; //(800 * 800)
typedef struct InputSDL {
    bool pressed;
    lv_coord_t x;
    lv_coord_t y;
} InputSDL_t;
InputSDL_t gInput = {false, 0, 0};
static void hal_init(void);
static int tick_thread(void *data);
static int lvtask_thread(void *data);
void my_disp_flush_ex(lv_disp_drv_t * disp, const lv_area_t * area, lv_color_t * color_p);
void my_set_rect(int32_t x1, int32_t y1, int32_t x2, int32_t y2, lv_color_t *color_p);
bool my_touchpad_read(lv_indev_drv_t * indev_driver, lv_indev_data_t * data);
bool my_touchpad_is_pressed(void);
void touchpad_get_xy(lv_coord_t *x, lv_coord_t *y);
int main(int argc, char* argv[]){
    //1,Call lv_init().
    printf("lvgl init ok!\n");
    //2,Initialize your drivers.
    printf("hal init ok!\n");
    //3,Register the display and input devices drivers in LittlevGL.
    /*Static or global buffer(s). The second buffer is optional*/
    static lv_disp_buf_t disp_buf;
    static lv_color_t buf_1[LV_HOR_RES_MAX * LV_VER_RES_MAX];
    static lv_color_t buf_2[LV_HOR_RES_MAX * LV_VER_RES_MAX];                   /*Declare a buffer for 100 lines*/
    lv_disp_buf_init(&disp_buf, buf_1, buf_2, LV_HOR_RES_MAX * LV_VER_RES_MAX);    /*Initialize the display buffer*/
    lv_disp_drv_t disp_drv;               /*Descriptor of a display driver*/
    lv_disp_drv_init(&disp_drv);          /*Basic initialization*/
    disp_drv.flush_cb = my_disp_flush_ex;    /*Set your driver function*/
    disp_drv.buffer = &disp_buf;          /*Assign the buffer to the display*/
    lv_disp_drv_register(&disp_drv);      /*Finally register the driver*/
    lv_indev_drv_t indev_drv;
    lv_indev_drv_init(&indev_drv);             /*Descriptor of a input device driver*/
    indev_drv.type = LV_INDEV_TYPE_POINTER;    /*Touch pad is a pointer-like device*/
    indev_drv.read_cb = my_touchpad_read;      /*Set your driver function*/
    lv_indev_drv_register(&indev_drv);         /*Finally register the driver*/
    printf("display and input device set ok!\n");
    bool quit = false;
    SDL_Event e;
        while(SDL_PollEvent(&e) != 0){
                case SDL_QUIT:
                    quit = true;
                case SDL_MOUSEBUTTONDOWN:
                    gInput.pressed = true;
                    gInput.x = e.motion.x;
                    gInput.y = e.motion.y;
                    printf("state= %d, x=%d, y=%d\n", e.button.state, e.motion.x, e.motion.y);
                case SDL_MOUSEBUTTONUP:
                    gInput.pressed = false;
                    printf("state= %d, x=%d, y=%d\n", e.button.state, e.motion.x, e.motion.y);
                case SDL_MOUSEMOTION:
                    gInput.x = e.motion.x;
                    gInput.y = e.motion.y;
                    //printf("mouse motion, x=%d, y=%d\n", e.motion.x, e.motion.y);
    printf("program quit!\n");
    return 0;
void hal_init(void){
    if(SDL_Init(SDL_INIT_VIDEO) != 0){
        printf("SDL init failed: %s\n", SDL_GetError());
        if(SDL_CreateWindowAndRenderer(LV_HOR_RES_MAX, LV_VER_RES_MAX, SDL_WINDOW_OPENGL, &gWin, &gRenderer) != 0){
            printf("SDL create window and renderer failed: %s\n", SDL_GetError());
            SDL_SetWindowTitle(gWin, "LittlevGL PC Simulator based on SDL2");
                printf("create texture failed: %s\n", SDL_GetError());
                SDL_SetTextureBlendMode(gTexture, SDL_BLENDMODE_BLEND);
                SDL_SetRenderDrawColor(gRenderer, 0xff, 0xff, 0xff, 0xff);
                SDL_RenderCopy(gRenderer, gTexture, NULL, NULL);
    SDL_Thread *tickThread = SDL_CreateThread(tick_thread, "tick", NULL);
    if(tickThread) printf("create tick thread ok!\n");
    else printf("create tick thread failed: %s\n", SDL_GetError());
    SDL_Thread *lvtaskThread = SDL_CreateThread(lvtask_thread, "lvtask", NULL);
    if(lvtaskThread) printf("create lvtask thread ok!\n");
    else printf("create lvtask thread failed: %s\n", SDL_GetError());
static int tick_thread(void *data){
    while(1) {
        SDL_Delay(1);   /*Sleep for 1 millisecond*/
    return 0;
static int lvtask_thread(void *data){
    return 0;


void my_disp_flush_ex(struct _disp_drv_t * disp, const lv_area_t * area, lv_color_t * color_p){
    int32_t x1 = area->x1;
    int32_t x2 = area->x2;
    int32_t y1 = area->y1;
    int32_t y2 = area->y2;
    printf("x1:[%u], y1:[%u], x2:[%u], y2:[%u]\n",x1,y1,x2,y2);

    if(x1 < 0 || x2 < 0 || y1 < 0 || y2 < 0 ||
       x1 > LV_HOR_RES_MAX - 1 || x2 > LV_HOR_RES_MAX - 1 ||
       y1 > LV_VER_RES_MAX - 1 || y2 > LV_VER_RES_MAX - 1){
    uint32_t w = x2 - x1 + 1;
    for(int32_t y = y1; y <= y2; y++) {
        memcpy(&tft_buf[y * LV_HOR_RES_MAX + x1], color_p, w * sizeof(lv_color_t));
        color_p += w;
    SDL_UpdateTexture(gTexture, NULL, tft_buf, LV_HOR_RES_MAX * sizeof(uint32_t));
    SDL_RenderCopy(gRenderer, gTexture, NULL, NULL);
    lv_disp_flush_ready(disp);         //Indicate you are ready with the flushing

bool my_touchpad_read(lv_indev_drv_t * indev_driver, lv_indev_data_t * data){
    data->state = my_touchpad_is_pressed() ? LV_INDEV_STATE_PR : LV_INDEV_STATE_REL;
    if(data->state == LV_INDEV_STATE_PR) touchpad_get_xy(&data->point.x, &data->point.y);
    return false; /*Return `false` because we are not buffering and no more data to read*/
bool my_touchpad_is_pressed(void){
    return gInput.pressed;
void touchpad_get_xy(lv_coord_t *x, lv_coord_t *y){
    printf("touchpad_get_xy called\n");
    *x = gInput.x;
    *y = gInput.y;

Screenshot and/or video

If possible, add screenshots and/or videos about the current state.

Hi @ShouCheng,

I expect the mouse cursor is moving when you click the button, so that will also be redrawn, possibly a few times.

Kind Regards,


If you are using the material theme (lv_comf.h) then it set a transition when buttons state changes (it animates background color). During this the button is redrawn several times.