1 /**
2  * @file lv_disp.c
3  *
4  */
5 
6 /*********************
7  *      INCLUDES
8  *********************/
9 #include "lv_disp.h"
10 #include "../misc/lv_math.h"
11 #include "../core/lv_refr.h"
12 
13 /*********************
14  *      DEFINES
15  *********************/
16 
17 /**********************
18  *      TYPEDEFS
19  **********************/
20 
21 /**********************
22  *  STATIC PROTOTYPES
23  **********************/
24 static void scr_load_internal(lv_obj_t * scr);
25 static void scr_load_anim_start(lv_anim_t * a);
26 static void opa_scale_anim(void * obj, int32_t v);
27 static void set_x_anim(void * obj, int32_t v);
28 static void set_y_anim(void * obj, int32_t v);
29 static void scr_anim_ready(lv_anim_t * a);
30 static bool is_out_anim(lv_scr_load_anim_t a);
31 
32 /**********************
33  *  STATIC VARIABLES
34  **********************/
35 
36 /**********************
37  *      MACROS
38  **********************/
39 
40 /**********************
41  *   GLOBAL FUNCTIONS
42  **********************/
43 
44 /**
45  * Return with a pointer to the active screen
46  * @param disp pointer to display which active screen should be get. (NULL to use the default
47  * screen)
48  * @return pointer to the active screen object (loaded by 'lv_scr_load()')
49  */
lv_disp_get_scr_act(lv_disp_t * disp)50 lv_obj_t * lv_disp_get_scr_act(lv_disp_t * disp)
51 {
52     if(!disp) disp = lv_disp_get_default();
53     if(!disp) {
54         LV_LOG_WARN("no display registered to get its active screen");
55         return NULL;
56     }
57 
58     return disp->act_scr;
59 }
60 
61 /**
62  * Return with a pointer to the previous screen. Only used during screen transitions.
63  * @param disp pointer to display which previous screen should be get. (NULL to use the default
64  * screen)
65  * @return pointer to the previous screen object or NULL if not used now
66  */
lv_disp_get_scr_prev(lv_disp_t * disp)67 lv_obj_t * lv_disp_get_scr_prev(lv_disp_t * disp)
68 {
69     if(!disp) disp = lv_disp_get_default();
70     if(!disp) {
71         LV_LOG_WARN("no display registered to get its previous screen");
72         return NULL;
73     }
74 
75     return disp->prev_scr;
76 }
77 
78 /**
79  * Make a screen active
80  * @param scr pointer to a screen
81  */
lv_disp_load_scr(lv_obj_t * scr)82 void lv_disp_load_scr(lv_obj_t * scr)
83 {
84     lv_scr_load_anim(scr, LV_SCR_LOAD_ANIM_NONE, 0, 0, false);
85 }
86 
87 /**
88  * Return with the top layer. (Same on every screen and it is above the normal screen layer)
89  * @param disp pointer to display which top layer should be get. (NULL to use the default screen)
90  * @return pointer to the top layer object (transparent screen sized lv_obj)
91  */
lv_disp_get_layer_top(lv_disp_t * disp)92 lv_obj_t * lv_disp_get_layer_top(lv_disp_t * disp)
93 {
94     if(!disp) disp = lv_disp_get_default();
95     if(!disp) {
96         LV_LOG_WARN("lv_layer_top: no display registered to get its top layer");
97         return NULL;
98     }
99 
100     return disp->top_layer;
101 }
102 
103 /**
104  * Return with the sys. layer. (Same on every screen and it is above the normal screen and the top
105  * layer)
106  * @param disp pointer to display which sys. layer should be retrieved. (NULL to use the default screen)
107  * @return pointer to the sys layer object (transparent screen sized lv_obj)
108  */
lv_disp_get_layer_sys(lv_disp_t * disp)109 lv_obj_t * lv_disp_get_layer_sys(lv_disp_t * disp)
110 {
111     if(!disp) disp = lv_disp_get_default();
112     if(!disp) {
113         LV_LOG_WARN("lv_layer_sys: no display registered to get its sys. layer");
114         return NULL;
115     }
116 
117     return disp->sys_layer;
118 }
119 
120 /**
121  * Set the theme of a display
122  * @param disp pointer to a display
123  */
lv_disp_set_theme(lv_disp_t * disp,lv_theme_t * th)124 void lv_disp_set_theme(lv_disp_t * disp, lv_theme_t * th)
125 {
126     if(!disp) disp = lv_disp_get_default();
127     if(!disp) {
128         LV_LOG_WARN("no display registered");
129         return;
130     }
131 
132     disp->theme = th;
133 
134     if(disp->screen_cnt == 3 &&
135        lv_obj_get_child_cnt(disp->screens[0]) == 0 &&
136        lv_obj_get_child_cnt(disp->screens[1]) == 0 &&
137        lv_obj_get_child_cnt(disp->screens[2]) == 0) {
138         lv_theme_apply(disp->screens[0]);
139     }
140 }
141 /**
142  * Get the theme of a display
143  * @param disp pointer to a display
144  * @return the display's theme (can be NULL)
145  */
lv_disp_get_theme(lv_disp_t * disp)146 lv_theme_t * lv_disp_get_theme(lv_disp_t * disp)
147 {
148     if(disp == NULL) disp = lv_disp_get_default();
149     return disp->theme;
150 }
151 
152 /**
153  * Set the background color of a display
154  * @param disp pointer to a display
155  * @param color color of the background
156  */
lv_disp_set_bg_color(lv_disp_t * disp,lv_color_t color)157 void lv_disp_set_bg_color(lv_disp_t * disp, lv_color_t color)
158 {
159     if(!disp) disp = lv_disp_get_default();
160     if(!disp) {
161         LV_LOG_WARN("no display registered");
162         return;
163     }
164 
165     disp->bg_color = color;
166 
167     lv_area_t a;
168     lv_area_set(&a, 0, 0, lv_disp_get_hor_res(disp) - 1, lv_disp_get_ver_res(disp) - 1);
169     _lv_inv_area(disp, &a);
170 
171 }
172 
173 /**
174  * Set the background image of a display
175  * @param disp pointer to a display
176  * @param img_src path to file or pointer to an `lv_img_dsc_t` variable
177  */
lv_disp_set_bg_image(lv_disp_t * disp,const void * img_src)178 void lv_disp_set_bg_image(lv_disp_t * disp, const void  * img_src)
179 {
180     if(!disp) disp = lv_disp_get_default();
181     if(!disp) {
182         LV_LOG_WARN("no display registered");
183         return;
184     }
185 
186     disp->bg_img = img_src;
187 
188     lv_area_t a;
189     lv_area_set(&a, 0, 0, lv_disp_get_hor_res(disp) - 1, lv_disp_get_ver_res(disp) - 1);
190     _lv_inv_area(disp, &a);
191 }
192 
193 /**
194  * Set opacity of the background
195  * @param disp pointer to a display
196  * @param opa opacity (0..255)
197  */
lv_disp_set_bg_opa(lv_disp_t * disp,lv_opa_t opa)198 void lv_disp_set_bg_opa(lv_disp_t * disp, lv_opa_t opa)
199 {
200     if(!disp) disp = lv_disp_get_default();
201     if(!disp) {
202         LV_LOG_WARN("no display registered");
203         return;
204     }
205 
206     disp->bg_opa = opa;
207 
208     lv_area_t a;
209     lv_area_set(&a, 0, 0, lv_disp_get_hor_res(disp) - 1, lv_disp_get_ver_res(disp) - 1);
210     _lv_inv_area(disp, &a);
211 }
212 
213 /**
214  * Switch screen with animation
215  * @param scr pointer to the new screen to load
216  * @param anim_type type of the animation from `lv_scr_load_anim_t`, e.g. `LV_SCR_LOAD_ANIM_MOVE_LEFT`
217  * @param time time of the animation
218  * @param delay delay before the transition
219  * @param auto_del true: automatically delete the old screen
220  */
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)221 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)
222 {
223 
224     lv_disp_t * d = lv_obj_get_disp(new_scr);
225     lv_obj_t * act_scr = lv_scr_act();
226 
227     if(act_scr == new_scr || d->scr_to_load == new_scr) {
228         return;
229     }
230 
231     /*If an other screen load animation is in progress
232      *make target screen loaded immediately. */
233     if(d->scr_to_load) {
234         lv_anim_del(d->scr_to_load, NULL);
235         lv_obj_set_pos(d->scr_to_load, 0, 0);
236         lv_obj_remove_local_style_prop(d->scr_to_load, LV_STYLE_OPA, 0);
237 
238         if(d->del_prev) {
239             lv_obj_del(act_scr);
240         }
241         act_scr = d->scr_to_load;
242 
243         scr_load_internal(d->scr_to_load);
244     }
245 
246     d->scr_to_load = new_scr;
247 
248     if(d->prev_scr && d->del_prev) {
249         lv_obj_del(d->prev_scr);
250         d->prev_scr = NULL;
251     }
252 
253     d->draw_prev_over_act = is_out_anim(anim_type);
254     d->del_prev = auto_del;
255 
256     /*Be sure there is no other animation on the screens*/
257     lv_anim_del(new_scr, NULL);
258     lv_anim_del(lv_scr_act(), NULL);
259 
260     /*Be sure both screens are in a normal position*/
261     lv_obj_set_pos(new_scr, 0, 0);
262     lv_obj_set_pos(lv_scr_act(), 0, 0);
263     lv_obj_remove_local_style_prop(new_scr, LV_STYLE_OPA, 0);
264     lv_obj_remove_local_style_prop(lv_scr_act(), LV_STYLE_OPA, 0);
265 
266     /*Shortcut for immediate load*/
267     if(time == 0 && delay == 0) {
268         scr_load_internal(new_scr);
269         if(auto_del) lv_obj_del(act_scr);
270         return;
271     }
272 
273     lv_anim_t a_new;
274     lv_anim_init(&a_new);
275     lv_anim_set_var(&a_new, new_scr);
276     lv_anim_set_start_cb(&a_new, scr_load_anim_start);
277     lv_anim_set_ready_cb(&a_new, scr_anim_ready);
278     lv_anim_set_time(&a_new, time);
279     lv_anim_set_delay(&a_new, delay);
280 
281     lv_anim_t a_old;
282     lv_anim_init(&a_old);
283     lv_anim_set_var(&a_old, d->act_scr);
284     lv_anim_set_time(&a_old, time);
285     lv_anim_set_delay(&a_old, delay);
286 
287     switch(anim_type) {
288         case LV_SCR_LOAD_ANIM_NONE:
289             /*Create a dummy animation to apply the delay*/
290             lv_anim_set_exec_cb(&a_new, set_x_anim);
291             lv_anim_set_values(&a_new, 0, 0);
292             break;
293         case LV_SCR_LOAD_ANIM_OVER_LEFT:
294             lv_anim_set_exec_cb(&a_new, set_x_anim);
295             lv_anim_set_values(&a_new, lv_disp_get_hor_res(d), 0);
296             break;
297         case LV_SCR_LOAD_ANIM_OVER_RIGHT:
298             lv_anim_set_exec_cb(&a_new, set_x_anim);
299             lv_anim_set_values(&a_new, -lv_disp_get_hor_res(d), 0);
300             break;
301         case LV_SCR_LOAD_ANIM_OVER_TOP:
302             lv_anim_set_exec_cb(&a_new, set_y_anim);
303             lv_anim_set_values(&a_new, lv_disp_get_ver_res(d), 0);
304             break;
305         case LV_SCR_LOAD_ANIM_OVER_BOTTOM:
306             lv_anim_set_exec_cb(&a_new, set_y_anim);
307             lv_anim_set_values(&a_new, -lv_disp_get_ver_res(d), 0);
308             break;
309         case LV_SCR_LOAD_ANIM_MOVE_LEFT:
310             lv_anim_set_exec_cb(&a_new, set_x_anim);
311             lv_anim_set_values(&a_new, lv_disp_get_hor_res(d), 0);
312 
313             lv_anim_set_exec_cb(&a_old, set_x_anim);
314             lv_anim_set_values(&a_old, 0, -lv_disp_get_hor_res(d));
315             break;
316         case LV_SCR_LOAD_ANIM_MOVE_RIGHT:
317             lv_anim_set_exec_cb(&a_new, set_x_anim);
318             lv_anim_set_values(&a_new, -lv_disp_get_hor_res(d), 0);
319 
320             lv_anim_set_exec_cb(&a_old, set_x_anim);
321             lv_anim_set_values(&a_old, 0, lv_disp_get_hor_res(d));
322             break;
323         case LV_SCR_LOAD_ANIM_MOVE_TOP:
324             lv_anim_set_exec_cb(&a_new, set_y_anim);
325             lv_anim_set_values(&a_new, lv_disp_get_ver_res(d), 0);
326 
327             lv_anim_set_exec_cb(&a_old, set_y_anim);
328             lv_anim_set_values(&a_old, 0, -lv_disp_get_ver_res(d));
329             break;
330         case LV_SCR_LOAD_ANIM_MOVE_BOTTOM:
331             lv_anim_set_exec_cb(&a_new, set_y_anim);
332             lv_anim_set_values(&a_new, -lv_disp_get_ver_res(d), 0);
333 
334             lv_anim_set_exec_cb(&a_old, set_y_anim);
335             lv_anim_set_values(&a_old, 0, lv_disp_get_ver_res(d));
336             break;
337         case LV_SCR_LOAD_ANIM_FADE_IN:
338             lv_anim_set_exec_cb(&a_new, opa_scale_anim);
339             lv_anim_set_values(&a_new, LV_OPA_TRANSP, LV_OPA_COVER);
340             break;
341         case LV_SCR_LOAD_ANIM_FADE_OUT:
342             lv_anim_set_exec_cb(&a_old, opa_scale_anim);
343             lv_anim_set_values(&a_old, LV_OPA_COVER, LV_OPA_TRANSP);
344             break;
345         case LV_SCR_LOAD_ANIM_OUT_LEFT:
346             lv_anim_set_exec_cb(&a_old, set_x_anim);
347             lv_anim_set_values(&a_old, 0, -lv_disp_get_hor_res(d));
348             break;
349         case LV_SCR_LOAD_ANIM_OUT_RIGHT:
350             lv_anim_set_exec_cb(&a_old, set_x_anim);
351             lv_anim_set_values(&a_old, 0, lv_disp_get_hor_res(d));
352             break;
353         case LV_SCR_LOAD_ANIM_OUT_TOP:
354             lv_anim_set_exec_cb(&a_old, set_y_anim);
355             lv_anim_set_values(&a_old, 0, -lv_disp_get_ver_res(d));
356             break;
357         case LV_SCR_LOAD_ANIM_OUT_BOTTOM:
358             lv_anim_set_exec_cb(&a_old, set_y_anim);
359             lv_anim_set_values(&a_old, 0, lv_disp_get_ver_res(d));
360             break;
361     }
362 
363     lv_event_send(act_scr, LV_EVENT_SCREEN_UNLOAD_START, NULL);
364 
365     lv_anim_start(&a_new);
366     lv_anim_start(&a_old);
367 }
368 
369 /**
370  * Get elapsed time since last user activity on a display (e.g. click)
371  * @param disp pointer to a display (NULL to get the overall smallest inactivity)
372  * @return elapsed ticks (milliseconds) since the last activity
373  */
lv_disp_get_inactive_time(const lv_disp_t * disp)374 uint32_t lv_disp_get_inactive_time(const lv_disp_t * disp)
375 {
376     if(disp) return lv_tick_elaps(disp->last_activity_time);
377 
378     lv_disp_t * d;
379     uint32_t t = UINT32_MAX;
380     d          = lv_disp_get_next(NULL);
381     while(d) {
382         uint32_t elaps = lv_tick_elaps(d->last_activity_time);
383         t = LV_MIN(t, elaps);
384         d = lv_disp_get_next(d);
385     }
386 
387     return t;
388 }
389 
390 /**
391  * Manually trigger an activity on a display
392  * @param disp pointer to a display (NULL to use the default display)
393  */
lv_disp_trig_activity(lv_disp_t * disp)394 void lv_disp_trig_activity(lv_disp_t * disp)
395 {
396     if(!disp) disp = lv_disp_get_default();
397     if(!disp) {
398         LV_LOG_WARN("lv_disp_trig_activity: no display registered");
399         return;
400     }
401 
402     disp->last_activity_time = lv_tick_get();
403 }
404 
405 /**
406  * Clean any CPU cache that is related to the display.
407  * @param disp pointer to a display (NULL to use the default display)
408  */
lv_disp_clean_dcache(lv_disp_t * disp)409 void lv_disp_clean_dcache(lv_disp_t * disp)
410 {
411     if(!disp) disp = lv_disp_get_default();
412     if(!disp) {
413         LV_LOG_WARN("lv_disp_clean_dcache: no display registered");
414         return;
415     }
416 
417     if(disp->driver->clean_dcache_cb)
418         disp->driver->clean_dcache_cb(disp->driver);
419 }
420 
421 /**
422  * Temporarily enable and disable the invalidation of the display.
423  * @param disp pointer to a display (NULL to use the default display)
424  * @param en true: enable invalidation; false: invalidation
425  */
lv_disp_enable_invalidation(lv_disp_t * disp,bool en)426 void lv_disp_enable_invalidation(lv_disp_t * disp, bool en)
427 {
428     if(!disp) disp = lv_disp_get_default();
429     if(!disp) {
430         LV_LOG_WARN("no display registered");
431         return;
432     }
433 
434     disp->inv_en_cnt += en ? 1 : -1;
435 }
436 
437 /**
438  * Get display invalidation is enabled.
439  * @param disp pointer to a display (NULL to use the default display)
440  * @return return true if invalidation is enabled
441  */
lv_disp_is_invalidation_enabled(lv_disp_t * disp)442 bool lv_disp_is_invalidation_enabled(lv_disp_t * disp)
443 {
444     if(!disp) disp = lv_disp_get_default();
445     if(!disp) {
446         LV_LOG_WARN("no display registered");
447         return false;
448     }
449 
450     return (disp->inv_en_cnt > 0);
451 }
452 
453 /**
454  * Get a pointer to the screen refresher timer to
455  * modify its parameters with `lv_timer_...` functions.
456  * @param disp pointer to a display
457  * @return pointer to the display refresher timer. (NULL on error)
458  */
_lv_disp_get_refr_timer(lv_disp_t * disp)459 lv_timer_t * _lv_disp_get_refr_timer(lv_disp_t * disp)
460 {
461     if(!disp) disp = lv_disp_get_default();
462     if(!disp) {
463         LV_LOG_WARN("lv_disp_get_refr_timer: no display registered");
464         return NULL;
465     }
466 
467     return disp->refr_timer;
468 }
469 
470 /**********************
471  *   STATIC FUNCTIONS
472  **********************/
473 
scr_load_internal(lv_obj_t * scr)474 static void scr_load_internal(lv_obj_t * scr)
475 {
476     lv_disp_t * d = lv_obj_get_disp(scr);
477     if(!d) return;  /*Shouldn't happen, just to be sure*/
478 
479     lv_obj_t * old_scr = d->act_scr;
480 
481     if(d->act_scr) lv_event_send(old_scr, LV_EVENT_SCREEN_UNLOAD_START, NULL);
482     if(d->act_scr) lv_event_send(scr, LV_EVENT_SCREEN_LOAD_START, NULL);
483 
484     d->act_scr = scr;
485     d->scr_to_load = NULL;
486 
487     if(d->act_scr) lv_event_send(scr, LV_EVENT_SCREEN_LOADED, NULL);
488     if(d->act_scr) lv_event_send(old_scr, LV_EVENT_SCREEN_UNLOADED, NULL);
489 
490     lv_obj_invalidate(scr);
491 }
492 
scr_load_anim_start(lv_anim_t * a)493 static void scr_load_anim_start(lv_anim_t * a)
494 {
495     lv_disp_t * d = lv_obj_get_disp(a->var);
496 
497     d->prev_scr = lv_scr_act();
498     d->act_scr = a->var;
499 
500     lv_event_send(d->act_scr, LV_EVENT_SCREEN_LOAD_START, NULL);
501 }
502 
opa_scale_anim(void * obj,int32_t v)503 static void opa_scale_anim(void * obj, int32_t v)
504 {
505     lv_obj_set_style_opa(obj, v, 0);
506 }
507 
set_x_anim(void * obj,int32_t v)508 static void set_x_anim(void * obj, int32_t v)
509 {
510     lv_obj_set_x(obj, v);
511 }
512 
set_y_anim(void * obj,int32_t v)513 static void set_y_anim(void * obj, int32_t v)
514 {
515     lv_obj_set_y(obj, v);
516 }
517 
scr_anim_ready(lv_anim_t * a)518 static void scr_anim_ready(lv_anim_t * a)
519 {
520     lv_disp_t * d = lv_obj_get_disp(a->var);
521 
522     lv_event_send(d->act_scr, LV_EVENT_SCREEN_LOADED, NULL);
523     lv_event_send(d->prev_scr, LV_EVENT_SCREEN_UNLOADED, NULL);
524 
525     if(d->prev_scr && d->del_prev) lv_obj_del(d->prev_scr);
526     d->prev_scr = NULL;
527     d->draw_prev_over_act = false;
528     d->scr_to_load = NULL;
529     lv_obj_remove_local_style_prop(a->var, LV_STYLE_OPA, 0);
530     lv_obj_invalidate(d->act_scr);
531 }
532 
is_out_anim(lv_scr_load_anim_t anim_type)533 static bool is_out_anim(lv_scr_load_anim_t anim_type)
534 {
535     return anim_type == LV_SCR_LOAD_ANIM_FADE_OUT  ||
536            anim_type == LV_SCR_LOAD_ANIM_OUT_LEFT  ||
537            anim_type == LV_SCR_LOAD_ANIM_OUT_RIGHT ||
538            anim_type == LV_SCR_LOAD_ANIM_OUT_TOP   ||
539            anim_type == LV_SCR_LOAD_ANIM_OUT_BOTTOM;
540 }
541