How to properly do Hook Drawing on moving arc indicator in Meter object when LV_EVENT_DRAW_PART_END happens


I’m putting together a test to show an lvgl behaviour/issue when you perform a hook drawing on an arc indicator in meter object when LV_EVENT_DRAW_PART_END event happens.

So, I created a simple meter with no style, needle or ticks but a moving green arc indicator with a fixed width that moves around the meter with an animation. So far so good.

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

STM32H645XI-Custom board

What LVGL version are you using?


What do you want to achieve?

Then I added an event to add a custom drawing on this arc by enabling LV_EVENT_DRAW_PART_END event. In the event callback, I drew another red arc with half-width the arc. (first half or second half or anywhere in between doesn’t matter). When I run the animation, it seems that not all essential drawing areas being refreshed (they are rendered I assumed)

What have you tried so far?

At this moment I tried two quick fixes to see what’s next:
1- Invalidate the whole meter area / object at the end of the callback
2- Invalidate dirty area at the end of animation callback
3- Use full refresh for the whole screen

Only solution 2 and 3 worked but I wanted to know, what’s the proper way to tell lvgl to update those dirty areas in the callback? Why invalidate functions doesn’t work in this scenario?

Code to reproduce

static lv_obj_t * meter;
static lv_meter_scale_t * scale;
static lv_meter_indicator_t * arc;

static void _arc_cb(lv_event_t * e)
    lv_event_code_t code = lv_event_get_code(e);
    lv_obj_draw_part_dsc_t * dsc = lv_event_get_draw_part_dsc(e);
    lv_meter_indicator_t * indic = (lv_meter_indicator_t *)dsc->sub_part_ptr;

    lv_draw_arc_dsc_t arc_dsc;
    arc_dsc.rounded = dsc->arc_dsc->rounded;			
    arc_dsc.start_angle = indic->scale->rotation;		
    arc_dsc.end_angle = indic->scale->rotation + 180;
    arc_dsc.color = lv_color_hex(0xFF0000);				
    arc_dsc.width = 200;
    uint16_t radius = dsc->radius;
    int32_t start = indic->start_value;
    int32_t end = indic->start_value + (indic->end_value - indic->start_value) / 2;
    lv_draw_arc(dsc->draw_ctx, &arc_dsc, dsc->p1, radius, start + indic->scale->rotation, 
                     end + indic->scale->rotation);

static void _anim_set_value(void * obj, int32_t v)
	const int32_t length = 40;
	int32_t start = v - length;
	int32_t end = start + length;
	if(v - length < 0)
		start = 0;
		end = v;

	lv_meter_set_indicator_start_value(meter, arc, start);
	lv_meter_set_indicator_end_value(meter, arc, end);
static void _animate(void)
  	lv_anim_t a;
	lv_anim_set_exec_cb(&a, _anim_set_value);					
	lv_anim_set_values(&a, 0, 180);								
	lv_anim_set_repeat_count(&a, LV_ANIM_REPEAT_INFINITE);		
	lv_anim_set_repeat_delay(&a, 100);							
	lv_anim_set_playback_delay(&a, 100);						
	lv_anim_set_time(&a, 60000);									
	lv_anim_set_playback_time(&a, 500);							
	lv_anim_set_var(&a, arc);			

void _arc_example(void)
   meter = lv_meter_create(lv_scr_act());
   lv_obj_remove_style(meter, NULL, LV_PART_INDICATOR);
   lv_obj_set_size(meter, 300, 300);
   scale = lv_meter_add_scale(meter);
   lv_meter_set_scale_ticks(meter, scale, 0, 0, 0, lv_color_black());
   arc = lv_meter_add_arc(meter, scale, 200, lv_color_hex(0x00FF00), 0);
   lv_meter_set_scale_range(meter, scale, 0, 180, 180, 180);
   lv_obj_add_event_cb(meter, _arc_cb, LV_EVENT_DRAW_PART_END, 0);

Screenshot and/or video

With full-refresh enabled: (disp_drv.full_refresh = 1;)

Without full-refresh: