/** * @file lv_disp.c * */ /********************* * INCLUDES *********************/ #include "lv_disp.h" #include "../misc/lv_math.h" #include "../core/lv_refr.h" /********************* * DEFINES *********************/ /********************** * TYPEDEFS **********************/ /********************** * STATIC PROTOTYPES **********************/ static void scr_load_anim_start(lv_anim_t * a); static void opa_scale_anim(void * obj, int32_t v); static void set_x_anim(void * obj, int32_t v); static void set_y_anim(void * obj, int32_t v); static void scr_anim_ready(lv_anim_t * a); static bool is_out_anim(lv_scr_load_anim_t a); /********************** * STATIC VARIABLES **********************/ /********************** * MACROS **********************/ /********************** * GLOBAL FUNCTIONS **********************/ /** * Return with a pointer to the active screen * @param disp pointer to display which active screen should be get. (NULL to use the default * screen) * @return pointer to the active screen object (loaded by 'lv_scr_load()') */ lv_obj_t * lv_disp_get_scr_act(lv_disp_t * disp) { if(!disp) disp = lv_disp_get_default(); if(!disp) { LV_LOG_WARN("no display registered to get its active screen"); return NULL; } return disp->act_scr; } /** * Return with a pointer to the previous screen. Only used during screen transitions. * @param disp pointer to display which previous screen should be get. (NULL to use the default * screen) * @return pointer to the previous screen object or NULL if not used now */ lv_obj_t * lv_disp_get_scr_prev(lv_disp_t * disp) { if(!disp) disp = lv_disp_get_default(); if(!disp) { LV_LOG_WARN("no display registered to get its previous screen"); return NULL; } return disp->prev_scr; } /** * Make a screen active * @param scr pointer to a screen */ void lv_disp_load_scr(lv_obj_t * scr) { lv_disp_t * d = lv_obj_get_disp(scr); if(!d) return; /*Shouldn't happen, just to be sure*/ lv_obj_t * old_scr = d->act_scr; if(d->act_scr) lv_event_send(old_scr, LV_EVENT_SCREEN_UNLOAD_START, NULL); if(d->act_scr) lv_event_send(scr, LV_EVENT_SCREEN_LOAD_START, NULL); d->act_scr = scr; if(d->act_scr) lv_event_send(scr, LV_EVENT_SCREEN_LOADED, NULL); if(d->act_scr) lv_event_send(old_scr, LV_EVENT_SCREEN_UNLOADED, NULL); lv_obj_invalidate(scr); } /** * Return with the top layer. (Same on every screen and it is above the normal screen layer) * @param disp pointer to display which top layer should be get. (NULL to use the default screen) * @return pointer to the top layer object (transparent screen sized lv_obj) */ lv_obj_t * lv_disp_get_layer_top(lv_disp_t * disp) { if(!disp) disp = lv_disp_get_default(); if(!disp) { LV_LOG_WARN("lv_layer_top: no display registered to get its top layer"); return NULL; } return disp->top_layer; } /** * Return with the sys. layer. (Same on every screen and it is above the normal screen and the top * layer) * @param disp pointer to display which sys. layer should be retrieved. (NULL to use the default screen) * @return pointer to the sys layer object (transparent screen sized lv_obj) */ lv_obj_t * lv_disp_get_layer_sys(lv_disp_t * disp) { if(!disp) disp = lv_disp_get_default(); if(!disp) { LV_LOG_WARN("lv_layer_sys: no display registered to get its sys. layer"); return NULL; } return disp->sys_layer; } /** * Set the theme of a display * @param disp pointer to a display */ void lv_disp_set_theme(lv_disp_t * disp, lv_theme_t * th) { if(disp == NULL) disp = lv_disp_get_default(); disp->theme = th; if(disp->screen_cnt == 3 && lv_obj_get_child_cnt(disp->screens[0]) == 0 && lv_obj_get_child_cnt(disp->screens[1]) == 0 && lv_obj_get_child_cnt(disp->screens[2]) == 0) { lv_theme_apply(disp->screens[0]); } } /** * Get the theme of a display * @param disp pointer to a display * @return the display's theme (can be NULL) */ lv_theme_t * lv_disp_get_theme(lv_disp_t * disp) { if(disp == NULL) disp = lv_disp_get_default(); return disp->theme; } /** * Set the background color of a display * @param disp pointer to a display * @param color color of the background */ void lv_disp_set_bg_color(lv_disp_t * disp, lv_color_t color) { if(!disp) disp = lv_disp_get_default(); if(!disp) { LV_LOG_WARN("no display registered"); return; } disp->bg_color = color; lv_area_t a; lv_area_set(&a, 0, 0, lv_disp_get_hor_res(disp) - 1, lv_disp_get_ver_res(disp) - 1); _lv_inv_area(disp, &a); } /** * Set the background image of a display * @param disp pointer to a display * @param img_src path to file or pointer to an `lv_img_dsc_t` variable */ void lv_disp_set_bg_image(lv_disp_t * disp, const void * img_src) { if(!disp) disp = lv_disp_get_default(); if(!disp) { LV_LOG_WARN("no display registered"); return; } disp->bg_img = img_src; lv_area_t a; lv_area_set(&a, 0, 0, lv_disp_get_hor_res(disp) - 1, lv_disp_get_ver_res(disp) - 1); _lv_inv_area(disp, &a); } /** * Set opacity of the background * @param disp pointer to a display * @param opa opacity (0..255) */ void lv_disp_set_bg_opa(lv_disp_t * disp, lv_opa_t opa) { if(!disp) disp = lv_disp_get_default(); if(!disp) { LV_LOG_WARN("no display registered"); return; } disp->bg_opa = opa; lv_area_t a; lv_area_set(&a, 0, 0, lv_disp_get_hor_res(disp) - 1, lv_disp_get_ver_res(disp) - 1); _lv_inv_area(disp, &a); } /** * Switch screen with animation * @param scr pointer to the new screen to load * @param anim_type type of the animation from `lv_scr_load_anim_t`. E.g. `LV_SCR_LOAD_ANIM_MOVE_LEFT` * @param time time of the animation * @param delay delay before the transition * @param auto_del true: automatically delete the old screen */ void lv_scr_load_anim(lv_obj_t * new_scr, lv_scr_load_anim_t anim_type, uint32_t time, uint32_t delay, bool auto_del) { lv_disp_t * d = lv_obj_get_disp(new_scr); lv_obj_t * act_scr = lv_scr_act(); /*If an other screen load animation is in progress *make target screen loaded immediately. */ if(d->scr_to_load && act_scr != d->scr_to_load) { lv_disp_load_scr(d->scr_to_load); lv_anim_del(d->scr_to_load, NULL); lv_obj_set_pos(d->scr_to_load, 0, 0); lv_obj_remove_local_style_prop(d->scr_to_load, LV_STYLE_OPA, 0); if(d->del_prev) { lv_obj_del(act_scr); } act_scr = d->scr_to_load; } d->scr_to_load = new_scr; if(d->prev_scr && d->del_prev) { lv_obj_del(d->prev_scr); d->prev_scr = NULL; } d->draw_prev_over_act = is_out_anim(anim_type); d->del_prev = auto_del; /*Be sure there is no other animation on the screens*/ lv_anim_del(new_scr, NULL); lv_anim_del(lv_scr_act(), NULL); /*Be sure both screens are in a normal position*/ lv_obj_set_pos(new_scr, 0, 0); lv_obj_set_pos(lv_scr_act(), 0, 0); lv_obj_remove_local_style_prop(new_scr, LV_STYLE_OPA, 0); lv_obj_remove_local_style_prop(lv_scr_act(), LV_STYLE_OPA, 0); lv_anim_t a_new; lv_anim_init(&a_new); lv_anim_set_var(&a_new, new_scr); lv_anim_set_start_cb(&a_new, scr_load_anim_start); lv_anim_set_ready_cb(&a_new, scr_anim_ready); lv_anim_set_time(&a_new, time); lv_anim_set_delay(&a_new, delay); lv_anim_t a_old; lv_anim_init(&a_old); lv_anim_set_var(&a_old, d->act_scr); lv_anim_set_time(&a_old, time); lv_anim_set_delay(&a_old, delay); switch(anim_type) { case LV_SCR_LOAD_ANIM_NONE: /*Create a dummy animation to apply the delay*/ lv_anim_set_exec_cb(&a_new, set_x_anim); lv_anim_set_values(&a_new, 0, 0); break; case LV_SCR_LOAD_ANIM_OVER_LEFT: lv_anim_set_exec_cb(&a_new, set_x_anim); lv_anim_set_values(&a_new, lv_disp_get_hor_res(d), 0); break; case LV_SCR_LOAD_ANIM_OVER_RIGHT: lv_anim_set_exec_cb(&a_new, set_x_anim); lv_anim_set_values(&a_new, -lv_disp_get_hor_res(d), 0); break; case LV_SCR_LOAD_ANIM_OVER_TOP: lv_anim_set_exec_cb(&a_new, set_y_anim); lv_anim_set_values(&a_new, lv_disp_get_ver_res(d), 0); break; case LV_SCR_LOAD_ANIM_OVER_BOTTOM: lv_anim_set_exec_cb(&a_new, set_y_anim); lv_anim_set_values(&a_new, -lv_disp_get_ver_res(d), 0); break; case LV_SCR_LOAD_ANIM_MOVE_LEFT: lv_anim_set_exec_cb(&a_new, set_x_anim); lv_anim_set_values(&a_new, lv_disp_get_hor_res(d), 0); lv_anim_set_exec_cb(&a_old, set_x_anim); lv_anim_set_values(&a_old, 0, -lv_disp_get_hor_res(d)); break; case LV_SCR_LOAD_ANIM_MOVE_RIGHT: lv_anim_set_exec_cb(&a_new, set_x_anim); lv_anim_set_values(&a_new, -lv_disp_get_hor_res(d), 0); lv_anim_set_exec_cb(&a_old, set_x_anim); lv_anim_set_values(&a_old, 0, lv_disp_get_hor_res(d)); break; case LV_SCR_LOAD_ANIM_MOVE_TOP: lv_anim_set_exec_cb(&a_new, set_y_anim); lv_anim_set_values(&a_new, lv_disp_get_ver_res(d), 0); lv_anim_set_exec_cb(&a_old, set_y_anim); lv_anim_set_values(&a_old, 0, -lv_disp_get_ver_res(d)); break; case LV_SCR_LOAD_ANIM_MOVE_BOTTOM: lv_anim_set_exec_cb(&a_new, set_y_anim); lv_anim_set_values(&a_new, -lv_disp_get_ver_res(d), 0); lv_anim_set_exec_cb(&a_old, set_y_anim); lv_anim_set_values(&a_old, 0, lv_disp_get_ver_res(d)); break; case LV_SCR_LOAD_ANIM_FADE_IN: lv_anim_set_exec_cb(&a_new, opa_scale_anim); lv_anim_set_values(&a_new, LV_OPA_TRANSP, LV_OPA_COVER); break; case LV_SCR_LOAD_ANIM_FADE_OUT: lv_anim_set_exec_cb(&a_old, opa_scale_anim); lv_anim_set_values(&a_old, LV_OPA_COVER, LV_OPA_TRANSP); break; case LV_SCR_LOAD_ANIM_OUT_LEFT: lv_anim_set_exec_cb(&a_old, set_x_anim); lv_anim_set_values(&a_old, 0, -lv_disp_get_hor_res(d)); break; case LV_SCR_LOAD_ANIM_OUT_RIGHT: lv_anim_set_exec_cb(&a_old, set_x_anim); lv_anim_set_values(&a_old, 0, lv_disp_get_hor_res(d)); break; case LV_SCR_LOAD_ANIM_OUT_TOP: lv_anim_set_exec_cb(&a_old, set_y_anim); lv_anim_set_values(&a_old, 0, -lv_disp_get_ver_res(d)); break; case LV_SCR_LOAD_ANIM_OUT_BOTTOM: lv_anim_set_exec_cb(&a_old, set_y_anim); lv_anim_set_values(&a_old, 0, lv_disp_get_ver_res(d)); break; } lv_event_send(act_scr, LV_EVENT_SCREEN_UNLOAD_START, NULL); lv_anim_start(&a_new); lv_anim_start(&a_old); } /** * Get elapsed time since last user activity on a display (e.g. click) * @param disp pointer to a display (NULL to get the overall smallest inactivity) * @return elapsed ticks (milliseconds) since the last activity */ uint32_t lv_disp_get_inactive_time(const lv_disp_t * disp) { if(disp) return lv_tick_elaps(disp->last_activity_time); lv_disp_t * d; uint32_t t = UINT32_MAX; d = lv_disp_get_next(NULL); while(d) { uint32_t elaps = lv_tick_elaps(d->last_activity_time); t = LV_MIN(t, elaps); d = lv_disp_get_next(d); } return t; } /** * Manually trigger an activity on a display * @param disp pointer to a display (NULL to use the default display) */ void lv_disp_trig_activity(lv_disp_t * disp) { if(!disp) disp = lv_disp_get_default(); if(!disp) { LV_LOG_WARN("lv_disp_trig_activity: no display registered"); return; } disp->last_activity_time = lv_tick_get(); } /** * Clean any CPU cache that is related to the display. * @param disp pointer to a display (NULL to use the default display) */ void lv_disp_clean_dcache(lv_disp_t * disp) { if(!disp) disp = lv_disp_get_default(); if(!disp) { LV_LOG_WARN("lv_disp_clean_dcache: no display registered"); return; } if(disp->driver->clean_dcache_cb) disp->driver->clean_dcache_cb(disp->driver); } /** * Get a pointer to the screen refresher timer to * modify its parameters with `lv_timer_...` functions. * @param disp pointer to a display * @return pointer to the display refresher timer. (NULL on error) */ lv_timer_t * _lv_disp_get_refr_timer(lv_disp_t * disp) { if(!disp) disp = lv_disp_get_default(); if(!disp) { LV_LOG_WARN("lv_disp_get_refr_timer: no display registered"); return NULL; } return disp->refr_timer; } /********************** * STATIC FUNCTIONS **********************/ static void scr_load_anim_start(lv_anim_t * a) { lv_disp_t * d = lv_obj_get_disp(a->var); d->prev_scr = lv_scr_act(); d->act_scr = a->var; lv_event_send(d->act_scr, LV_EVENT_SCREEN_LOAD_START, NULL); } static void opa_scale_anim(void * obj, int32_t v) { lv_obj_set_style_opa(obj, v, 0); } static void set_x_anim(void * obj, int32_t v) { lv_obj_set_x(obj, v); } static void set_y_anim(void * obj, int32_t v) { lv_obj_set_y(obj, v); } static void scr_anim_ready(lv_anim_t * a) { lv_disp_t * d = lv_obj_get_disp(a->var); lv_event_send(d->act_scr, LV_EVENT_SCREEN_LOADED, NULL); lv_event_send(d->prev_scr, LV_EVENT_SCREEN_UNLOADED, NULL); if(d->prev_scr && d->del_prev) lv_obj_del(d->prev_scr); d->prev_scr = NULL; d->draw_prev_over_act = false; d->scr_to_load = NULL; lv_obj_remove_local_style_prop(a->var, LV_STYLE_OPA, 0); } static bool is_out_anim(lv_scr_load_anim_t anim_type) { return anim_type == LV_SCR_LOAD_ANIM_FADE_OUT || anim_type == LV_SCR_LOAD_ANIM_OUT_LEFT || anim_type == LV_SCR_LOAD_ANIM_OUT_RIGHT || anim_type == LV_SCR_LOAD_ANIM_OUT_TOP || anim_type == LV_SCR_LOAD_ANIM_OUT_BOTTOM; }