Transition to hekate bdk layout

This commit is contained in:
shchmue 2020-06-26 14:17:06 -06:00
parent 4425e81085
commit 4ffd4ce7f0
317 changed files with 60891 additions and 1003 deletions

View file

@ -0,0 +1,12 @@
CSRCS += lv_group.c
CSRCS += lv_indev.c
CSRCS += lv_obj.c
CSRCS += lv_refr.c
CSRCS += lv_style.c
CSRCS += lv_vdb.c
CSRCS += lv_lang.c
DEPPATH += --dep-path $(LVGL_DIR)/lvgl/lv_core
VPATH += :$(LVGL_DIR)/lvgl/lv_core
CFLAGS += "-I$(LVGL_DIR)/lvgl/lv_core"

View file

@ -0,0 +1,554 @@
/**
* @file lv_group.c
*
*/
/*********************
* INCLUDES
*********************/
#include "lv_group.h"
#if USE_LV_GROUP != 0
#include <stddef.h>
/*********************
* DEFINES
*********************/
/**********************
* TYPEDEFS
**********************/
/**********************
* STATIC PROTOTYPES
**********************/
static void style_mod_def(lv_style_t * style);
static void style_mod_edit_def(lv_style_t * style);
static void lv_group_refocus(lv_group_t *g);
static void obj_to_foreground(lv_obj_t * obj);
/**********************
* STATIC VARIABLES
**********************/
/**********************
* MACROS
**********************/
/**********************
* GLOBAL FUNCTIONS
**********************/
/**
* Create a new object group
* @return pointer to the new object group
*/
lv_group_t * lv_group_create(void)
{
lv_group_t * group = lv_mem_alloc(sizeof(lv_group_t));
lv_mem_assert(group);
if(group == NULL) return NULL;
lv_ll_init(&group->obj_ll, sizeof(lv_obj_t *));
group->style_mod = style_mod_def;
group->style_mod_edit = style_mod_edit_def;
group->obj_focus = NULL;
group->frozen = 0;
group->focus_cb = NULL;
group->click_focus = 1;
group->editing = 0;
return group;
}
/**
* Delete a group object
* @param group pointer to a group
*/
void lv_group_del(lv_group_t * group)
{
/*Defocus the the currently focused object*/
if(group->obj_focus != NULL) {
(*group->obj_focus)->signal_func(*group->obj_focus, LV_SIGNAL_DEFOCUS, NULL);
lv_obj_invalidate(*group->obj_focus);
}
/*Remove the objects from the group*/
lv_obj_t ** obj;
LL_READ(group->obj_ll, obj) {
(*obj)->group_p = NULL;
}
lv_ll_clear(&(group->obj_ll));
lv_mem_free(group);
}
/**
* Add an object to a group
* @param group pointer to a group
* @param obj pointer to an object to add
*/
void lv_group_add_obj(lv_group_t * group, lv_obj_t * obj)
{
if(group == NULL) return;
/*If the object is already in a group and focused then defocus it*/
if(obj->group_p) {
if(lv_obj_is_focused(obj)) {
lv_group_refocus(obj->group_p);
LV_LOG_INFO("group: assign object to an other group");
}
}
obj->group_p = group;
lv_obj_t ** next = lv_ll_ins_tail(&group->obj_ll);
lv_mem_assert(next);
if(next == NULL) return;
*next = obj;
/* If the head and the tail is equal then there is only one object in the linked list.
* In this case automatically activate it*/
if(lv_ll_get_head(&group->obj_ll) == next) {
lv_group_refocus(group);
}
}
/**
* Remove an object from its group
* @param obj pointer to an object to remove
*/
void lv_group_remove_obj(lv_obj_t * obj)
{
lv_group_t * g = obj->group_p;
if(g == NULL) return;
if(g->obj_focus == NULL) return; /*Just to be sure (Not possible if there is at least one object in the group)*/
/*Focus on the next object*/
if(*g->obj_focus == obj) {
/*If this is the only object in the group then focus to nothing.*/
if(lv_ll_get_head(&g->obj_ll) == g->obj_focus && lv_ll_get_tail(&g->obj_ll) == g->obj_focus) {
(*g->obj_focus)->signal_func(*g->obj_focus, LV_SIGNAL_DEFOCUS, NULL);
}
/*If there more objects in the group then focus to the next/prev object*/
else {
lv_group_refocus(g);
}
}
/* If the focuses object is still the same then it was the only object in the group but it will be deleted.
* Set the `obj_focus` to NULL to get back to the initial state of the group with zero objects*/
if(*g->obj_focus == obj) {
g->obj_focus = NULL;
}
/*Search the object and remove it from its group */
lv_obj_t ** i;
LL_READ(g->obj_ll, i) {
if(*i == obj) {
lv_ll_rem(&g->obj_ll, i);
lv_mem_free(i);
obj->group_p = NULL;
break;
}
}
}
/**
* Focus on an object (defocus the current)
* @param obj pointer to an object to focus on
*/
void lv_group_focus_obj(lv_obj_t * obj)
{
lv_group_t * g = obj->group_p;
if(g == NULL) return;
if(g->frozen != 0) return;
/*On defocus edit mode must be leaved*/
lv_group_set_editing(g, false);
lv_obj_t ** i;
LL_READ(g->obj_ll, i) {
if(*i == obj) {
if(g->obj_focus == i) return; /*Don't focus the already focused object again*/
if(g->obj_focus != NULL) {
(*g->obj_focus)->signal_func(*g->obj_focus, LV_SIGNAL_DEFOCUS, NULL);
lv_obj_invalidate(*g->obj_focus);
}
g->obj_focus = i;
if(g->obj_focus != NULL) {
(*g->obj_focus)->signal_func(*g->obj_focus, LV_SIGNAL_FOCUS, NULL);
if(g->focus_cb) g->focus_cb(g);
lv_obj_invalidate(*g->obj_focus);
/*If the object or its parent has `top == true` bring it to the foregorund*/
obj_to_foreground(*g->obj_focus);
}
break;
}
}
}
/**
* Focus the next object in a group (defocus the current)
* @param group pointer to a group
*/
void lv_group_focus_next(lv_group_t * group)
{
if(group->frozen) return;
if(group->obj_focus) {
(*group->obj_focus)->signal_func(*group->obj_focus, LV_SIGNAL_DEFOCUS, NULL);
lv_obj_invalidate(*group->obj_focus);
}
lv_obj_t ** obj_next;
if(group->obj_focus == NULL) obj_next = lv_ll_get_head(&group->obj_ll);
else obj_next = lv_ll_get_next(&group->obj_ll, group->obj_focus);
if(obj_next == NULL) {
if(group->wrap) obj_next = lv_ll_get_head(&group->obj_ll);
else obj_next = lv_ll_get_tail(&group->obj_ll);
}
group->obj_focus = obj_next;
if(group->obj_focus) {
(*group->obj_focus)->signal_func(*group->obj_focus, LV_SIGNAL_FOCUS, NULL);
lv_obj_invalidate(*group->obj_focus);
if(group->focus_cb) group->focus_cb(group);
/*If the object or its parent has `top == true` bring it to the foregorund*/
obj_to_foreground(*group->obj_focus);
}
}
/**
* Focus the previous object in a group (defocus the current)
* @param group pointer to a group
*/
void lv_group_focus_prev(lv_group_t * group)
{
if(group->frozen) return;
if(group->obj_focus) {
(*group->obj_focus)->signal_func(*group->obj_focus, LV_SIGNAL_DEFOCUS, NULL);
lv_obj_invalidate(*group->obj_focus);
}
lv_obj_t ** obj_next;
if(group->obj_focus == NULL) obj_next = lv_ll_get_tail(&group->obj_ll);
else obj_next = lv_ll_get_prev(&group->obj_ll, group->obj_focus);
if(obj_next == NULL) {
if(group->wrap) obj_next = lv_ll_get_tail(&group->obj_ll);
else obj_next = lv_ll_get_head(&group->obj_ll);
}
group->obj_focus = obj_next;
if(group->obj_focus != NULL) {
(*group->obj_focus)->signal_func(*group->obj_focus, LV_SIGNAL_FOCUS, NULL);
lv_obj_invalidate(*group->obj_focus);
if(group->focus_cb) group->focus_cb(group);
/*If the object or its parent has `top == true` bring it to the foregorund*/
obj_to_foreground(*group->obj_focus);
}
}
/**
* Do not let to change the focus from the current object
* @param group pointer to a group
* @param en true: freeze, false: release freezing (normal mode)
*/
void lv_group_focus_freeze(lv_group_t * group, bool en)
{
if(en == false) group->frozen = 0;
else group->frozen = 1;
}
/**
* Send a control character to the focuses object of a group
* @param group pointer to a group
* @param c a character (use LV_GROUP_KEY_.. to navigate)
* @return result of focused object in group.
*/
lv_res_t lv_group_send_data(lv_group_t * group, uint32_t c)
{
lv_obj_t * act = lv_group_get_focused(group);
if(act == NULL) return LV_RES_OK;
return act->signal_func(act, LV_SIGNAL_CONTROLL, &c);
}
/**
* Set a function for a group which will modify the object's style if it is in focus
* @param group pointer to a group
* @param style_mod_func the style modifier function pointer
*/
void lv_group_set_style_mod_cb(lv_group_t * group, lv_group_style_mod_func_t style_mod_func)
{
group->style_mod = style_mod_func;
if(group->obj_focus != NULL) lv_obj_invalidate(*group->obj_focus);
}
/**
* Set a function for a group which will modify the object's style if it is in focus in edit mode
* @param group pointer to a group
* @param style_mod_func the style modifier function pointer
*/
void lv_group_set_style_mod_edit_cb(lv_group_t * group, lv_group_style_mod_func_t style_mod_func)
{
group->style_mod_edit = style_mod_func;
if(group->obj_focus != NULL) lv_obj_invalidate(*group->obj_focus);
}
/**
* Set a function for a group which will be called when a new object is focused
* @param group pointer to a group
* @param focus_cb the call back function or NULL if unused
*/
void lv_group_set_focus_cb(lv_group_t * group, lv_group_focus_cb_t focus_cb)
{
group->focus_cb = focus_cb;
}
/**
* Manually set the current mode (edit or navigate).
* @param group pointer to group
* @param edit: true: edit mode; false: navigate mode
*/
void lv_group_set_editing(lv_group_t * group, bool edit)
{
uint8_t en_val = edit ? 1 : 0;
if(en_val == group->editing) return; /*Do not set the same mode again*/
group->editing = en_val;
lv_obj_t * focused = lv_group_get_focused(group);
if(focused) focused->signal_func(focused, LV_SIGNAL_FOCUS, NULL); /*Focus again to properly leave edit mode*/
lv_obj_invalidate(focused);
}
/**
* Set the `click_focus` attribute. If enabled then the object will be focused then it is clicked.
* @param group pointer to group
* @param en: true: enable `click_focus`
*/
void lv_group_set_click_focus(lv_group_t * group, bool en)
{
group->click_focus = en ? 1 : 0;
}
void lv_group_set_refocus_policy(lv_group_t * group, lv_group_refocus_policy_t policy) {
group->refocus_policy = policy & 0x01;
}
static void lv_group_refocus(lv_group_t *g) {
/*Refocus must temporarily allow wrapping to work correctly*/
uint8_t temp_wrap = g->wrap;
g->wrap = 1;
if(g->refocus_policy == LV_GROUP_REFOCUS_POLICY_NEXT)
lv_group_focus_next(g);
else if(g->refocus_policy == LV_GROUP_REFOCUS_POLICY_PREV)
lv_group_focus_prev(g);
/*Restore wrap property*/
g->wrap = temp_wrap;
}
/**
* Set whether focus next/prev will allow wrapping from first->last or last->first.
* @param group pointer to group
* @param en: true: enable `click_focus`
*/
void lv_group_set_wrap(lv_group_t * group, bool en)
{
group->wrap = en ? 1 : 0;
}
/**
* Modify a style with the set 'style_mod' function. The input style remains unchanged.
* @param group pointer to group
* @param style pointer to a style to modify
* @return a copy of the input style but modified with the 'style_mod' function
*/
lv_style_t * lv_group_mod_style(lv_group_t * group, const lv_style_t * style)
{
lv_style_copy(&group->style_tmp, style);
if(group->editing) {
if(group->style_mod_edit) group->style_mod_edit(&group->style_tmp);
} else {
if(group->style_mod) group->style_mod(&group->style_tmp);
}
return &group->style_tmp;
}
/**
* Get the focused object or NULL if there isn't one
* @param group pointer to a group
* @return pointer to the focused object
*/
lv_obj_t * lv_group_get_focused(const lv_group_t * group)
{
if(!group) return NULL;
if(group->obj_focus == NULL) return NULL;
return *group->obj_focus;
}
/**
* Get a the style modifier function of a group
* @param group pointer to a group
* @return pointer to the style modifier function
*/
lv_group_style_mod_func_t lv_group_get_style_mod_cb(const lv_group_t * group)
{
if(!group) return false;
return group->style_mod ;
}
/**
* Get a the style modifier function of a group in edit mode
* @param group pointer to a group
* @return pointer to the style modifier function
*/
lv_group_style_mod_func_t lv_group_get_style_mod_edit_cb(const lv_group_t * group)
{
if(!group) return false;
return group->style_mod_edit;
}
/**
* Get the focus callback function of a group
* @param group pointer to a group
* @return the call back function or NULL if not set
*/
lv_group_focus_cb_t lv_group_get_focus_cb(const lv_group_t * group)
{
if(!group) return false;
return group->focus_cb;
}
/**
* Get the current mode (edit or navigate).
* @param group pointer to group
* @return true: edit mode; false: navigate mode
*/
bool lv_group_get_editing(const lv_group_t * group)
{
if(!group) return false;
return group->editing ? true : false;
}
/**
* Get the `click_focus` attribute.
* @param group pointer to group
* @return true: `click_focus` is enabled; false: disabled
*/
bool lv_group_get_click_focus(const lv_group_t * group)
{
if(!group) return false;
return group->click_focus ? true : false;
}
/**
* Get whether focus next/prev will allow wrapping from first->last or last->first object.
* @param group pointer to group
* @param en: true: wrapping enabled; false: wrapping disabled
*/
bool lv_group_get_wrap(lv_group_t * group)
{
if(!group) return false;
return group->wrap ? true : false;
}
/**********************
* STATIC FUNCTIONS
**********************/
/**
* Default style modifier function
* @param style pointer to a style to modify. (Typically group.style_tmp) It will be OVERWRITTEN.
*/
static void style_mod_def(lv_style_t * style)
{
#if LV_COLOR_DEPTH != 1
/*Make the style to be a little bit orange*/
style->body.border.opa = LV_OPA_COVER;
style->body.border.color = LV_COLOR_ORANGE;
/*If not empty or has border then emphasis the border*/
if(style->body.empty == 0 || style->body.border.width != 0) style->body.border.width = LV_DPI / 20;
style->body.main_color = lv_color_mix(style->body.main_color, LV_COLOR_ORANGE, LV_OPA_70);
style->body.grad_color = lv_color_mix(style->body.grad_color, LV_COLOR_ORANGE, LV_OPA_70);
style->body.shadow.color = lv_color_mix(style->body.shadow.color, LV_COLOR_ORANGE, LV_OPA_60);
style->text.color = lv_color_mix(style->text.color, LV_COLOR_ORANGE, LV_OPA_70);
#else
style->body.border.opa = LV_OPA_COVER;
style->body.border.color = LV_COLOR_BLACK;
style->body.border.width = 2;
#endif
}
/**
* Default style modifier function
* @param style pointer to a style to modify. (Typically group.style_tmp) It will be OVERWRITTEN.
*/
static void style_mod_edit_def(lv_style_t * style)
{
#if LV_COLOR_DEPTH != 1
/*Make the style to be a little bit orange*/
style->body.border.opa = LV_OPA_COVER;
style->body.border.color = LV_COLOR_GREEN;
/*If not empty or has border then emphasis the border*/
if(style->body.empty == 0 || style->body.border.width != 0) style->body.border.width = LV_DPI / 20;
style->body.main_color = lv_color_mix(style->body.main_color, LV_COLOR_GREEN, LV_OPA_70);
style->body.grad_color = lv_color_mix(style->body.grad_color, LV_COLOR_GREEN, LV_OPA_70);
style->body.shadow.color = lv_color_mix(style->body.shadow.color, LV_COLOR_GREEN, LV_OPA_60);
style->text.color = lv_color_mix(style->text.color, LV_COLOR_GREEN, LV_OPA_70);
#else
style->body.border.opa = LV_OPA_COVER;
style->body.border.color = LV_COLOR_BLACK;
style->body.border.width = 3;
#endif
}
static void obj_to_foreground(lv_obj_t * obj)
{
/*Search for 'top' attribute*/
lv_obj_t * i = obj;
lv_obj_t * last_top = NULL;
while(i != NULL) {
if(i->top != 0) last_top = i;
i = lv_obj_get_parent(i);
}
if(last_top != NULL) {
/*Move the last_top object to the foreground*/
lv_obj_t * par = lv_obj_get_parent(last_top);
/*After list change it will be the new head*/
lv_ll_chg_list(&par->child_ll, &par->child_ll, last_top);
lv_obj_invalidate(last_top);
}
}
#endif /*USE_LV_GROUP != 0*/

View file

@ -0,0 +1,247 @@
/**
* @file lv_group.h
*
*/
#ifndef LV_GROUP_H
#define LV_GROUP_H
#ifdef __cplusplus
extern "C" {
#endif
/*********************
* INCLUDES
*********************/
#ifdef LV_CONF_INCLUDE_SIMPLE
#include "lv_conf.h"
#else
#include "../../lv_conf.h"
#endif
#include "lv_obj.h"
/*********************
* DEFINES
*********************/
/*Predefined keys to control the focused object via lv_group_send(group, c)*/
/*For compatibility in signal function define the keys regardless to LV_GROUP*/
#define LV_GROUP_KEY_UP 17 /*0x11*/
#define LV_GROUP_KEY_DOWN 18 /*0x12*/
#define LV_GROUP_KEY_RIGHT 19 /*0x13*/
#define LV_GROUP_KEY_LEFT 20 /*0x14*/
#define LV_GROUP_KEY_ESC 27 /*0x1B*/
#define LV_GROUP_KEY_DEL 127 /*0x7F*/
#define LV_GROUP_KEY_BACKSPACE 8 /*0x08*/
#define LV_GROUP_KEY_ENTER 10 /*0x0A, '\n'*/
#define LV_GROUP_KEY_NEXT 9 /*0x09, '\t'*/
#define LV_GROUP_KEY_PREV 11 /*0x0B, '*/
#if USE_LV_GROUP != 0
/**********************
* TYPEDEFS
**********************/
struct _lv_group_t;
typedef void (*lv_group_style_mod_func_t)(lv_style_t *);
typedef void (*lv_group_focus_cb_t)(struct _lv_group_t *);
typedef struct _lv_group_t
{
lv_ll_t obj_ll; /*Linked list to store the objects in the group */
lv_obj_t ** obj_focus; /*The object in focus*/
lv_group_style_mod_func_t style_mod; /*A function which modifies the style of the focused object*/
lv_group_style_mod_func_t style_mod_edit;/*A function which modifies the style of the focused object*/
lv_group_focus_cb_t focus_cb; /*A function to call when a new object is focused (optional)*/
lv_style_t style_tmp; /*Stores the modified style of the focused object */
uint8_t frozen :1; /*1: can't focus to new object*/
uint8_t editing :1; /*1: Edit mode, 0: Navigate mode*/
uint8_t click_focus :1; /*1: If an object in a group is clicked by an indev then it will be focused */
uint8_t refocus_policy :1; /*1: Focus prev if focused on deletion. 0: Focus prev if focused on deletion.*/
uint8_t wrap :1; /*1: Focus next/prev can wrap at end of list. 0: Focus next/prev stops at end of list.*/
} lv_group_t;
typedef enum _lv_group_refocus_policy_t {
LV_GROUP_REFOCUS_POLICY_NEXT = 0,
LV_GROUP_REFOCUS_POLICY_PREV = 1
} lv_group_refocus_policy_t;
/**********************
* GLOBAL PROTOTYPES
**********************/
/**
* Create a new object group
* @return pointer to the new object group
*/
lv_group_t * lv_group_create(void);
/**
* Delete a group object
* @param group pointer to a group
*/
void lv_group_del(lv_group_t * group);
/**
* Add an object to a group
* @param group pointer to a group
* @param obj pointer to an object to add
*/
void lv_group_add_obj(lv_group_t * group, lv_obj_t * obj);
/**
* Remove an object from its group
* @param obj pointer to an object to remove
*/
void lv_group_remove_obj(lv_obj_t * obj);
/**
* Focus on an object (defocus the current)
* @param obj pointer to an object to focus on
*/
void lv_group_focus_obj(lv_obj_t * obj);
/**
* Focus the next object in a group (defocus the current)
* @param group pointer to a group
*/
void lv_group_focus_next(lv_group_t * group);
/**
* Focus the previous object in a group (defocus the current)
* @param group pointer to a group
*/
void lv_group_focus_prev(lv_group_t * group);
/**
* Do not let to change the focus from the current object
* @param group pointer to a group
* @param en true: freeze, false: release freezing (normal mode)
*/
void lv_group_focus_freeze(lv_group_t * group, bool en);
/**
* Send a control character to the focuses object of a group
* @param group pointer to a group
* @param c a character (use LV_GROUP_KEY_.. to navigate)
* @return result of focused object in group.
*/
lv_res_t lv_group_send_data(lv_group_t * group, uint32_t c);
/**
* Set a function for a group which will modify the object's style if it is in focus
* @param group pointer to a group
* @param style_mod_func the style modifier function pointer
*/
void lv_group_set_style_mod_cb(lv_group_t * group, lv_group_style_mod_func_t style_mod_func);
/**
* Set a function for a group which will modify the object's style if it is in focus in edit mode
* @param group pointer to a group
* @param style_mod_func the style modifier function pointer
*/
void lv_group_set_style_mod_edit_cb(lv_group_t * group, lv_group_style_mod_func_t style_mod_func);
/**
* Set a function for a group which will be called when a new object is focused
* @param group pointer to a group
* @param focus_cb the call back function or NULL if unused
*/
void lv_group_set_focus_cb(lv_group_t * group, lv_group_focus_cb_t focus_cb);
/**
* Set whether the next or previous item in a group is focused if the currently focussed obj is deleted.
* @param group pointer to a group
* @param new refocus policy enum
*/
void lv_group_set_refocus_policy(lv_group_t * group, lv_group_refocus_policy_t policy);
/**
* Manually set the current mode (edit or navigate).
* @param group pointer to group
* @param edit: true: edit mode; false: navigate mode
*/
void lv_group_set_editing(lv_group_t * group, bool edit);
/**
* Set the `click_focus` attribute. If enabled then the object will be focused then it is clicked.
* @param group pointer to group
* @param en: true: enable `click_focus`
*/
void lv_group_set_click_focus(lv_group_t * group, bool en);
/**
* Set whether focus next/prev will allow wrapping from first->last or last->first object.
* @param group pointer to group
* @param en: true: wrapping enabled; false: wrapping disabled
*/
void lv_group_set_wrap(lv_group_t * group, bool en);
/**
* Modify a style with the set 'style_mod' function. The input style remains unchanged.
* @param group pointer to group
* @param style pointer to a style to modify
* @return a copy of the input style but modified with the 'style_mod' function
*/
lv_style_t * lv_group_mod_style(lv_group_t * group, const lv_style_t * style);
/**
* Get the focused object or NULL if there isn't one
* @param group pointer to a group
* @return pointer to the focused object
*/
lv_obj_t * lv_group_get_focused(const lv_group_t * group);
/**
* Get a the style modifier function of a group
* @param group pointer to a group
* @return pointer to the style modifier function
*/
lv_group_style_mod_func_t lv_group_get_style_mod_cb(const lv_group_t * group);
/**
* Get a the style modifier function of a group in edit mode
* @param group pointer to a group
* @return pointer to the style modifier function
*/
lv_group_style_mod_func_t lv_group_get_style_mod_edit_cb(const lv_group_t * group);
/**
* Get the focus callback function of a group
* @param group pointer to a group
* @return the call back function or NULL if not set
*/
lv_group_focus_cb_t lv_group_get_focus_cb(const lv_group_t * group);
/**
* Get the current mode (edit or navigate).
* @param group pointer to group
* @return true: edit mode; false: navigate mode
*/
bool lv_group_get_editing(const lv_group_t * group);
/**
* Get the `click_focus` attribute.
* @param group pointer to group
* @return true: `click_focus` is enabled; false: disabled
*/
bool lv_group_get_click_focus(const lv_group_t * group);
/**
* Get whether focus next/prev will allow wrapping from first->last or last->first object.
* @param group pointer to group
* @param en: true: wrapping enabled; false: wrapping disabled
*/
bool lv_group_get_wrap(lv_group_t * group);
/**********************
* MACROS
**********************/
#endif /*USE_LV_GROUP != 0*/
#ifdef __cplusplus
} /* extern "C" */
#endif
#endif /*LV_GROUP_H*/

View file

@ -0,0 +1,971 @@
/**
* @file lv_indev_proc.c
*
*/
/*********************
* INCLUDES
********************/
#include "lv_indev.h"
#include "../lv_hal/lv_hal_tick.h"
#include "../lv_core/lv_group.h"
#include "../lv_core/lv_refr.h"
#include "../lv_misc/lv_task.h"
#include "../lv_misc/lv_math.h"
#include "../lv_draw/lv_draw_rbasic.h"
#include "lv_obj.h"
/*********************
* DEFINES
*********************/
#if LV_INDEV_DRAG_THROW <= 0
#warning "LV_INDEV_DRAG_THROW must be greater than 0"
#endif
/**********************
* TYPEDEFS
**********************/
/**********************
* STATIC PROTOTYPES
**********************/
#if LV_INDEV_READ_PERIOD != 0
static void indev_proc_task(void * param);
static void indev_pointer_proc(lv_indev_t * i, lv_indev_data_t * data);
static void indev_keypad_proc(lv_indev_t * i, lv_indev_data_t * data);
static void indev_encoder_proc(lv_indev_t * i, lv_indev_data_t * data);
static void indev_button_proc(lv_indev_t * i, lv_indev_data_t * data);
static void indev_proc_press(lv_indev_proc_t * proc);
static void indev_proc_release(lv_indev_proc_t * proc);
static void indev_proc_reset_query_handler(lv_indev_t * indev);
static lv_obj_t * indev_search_obj(const lv_indev_proc_t * proc, lv_obj_t * obj);
static void indev_drag(lv_indev_proc_t * state);
static void indev_drag_throw(lv_indev_proc_t * state);
#endif
/**********************
* STATIC VARIABLES
**********************/
static lv_indev_t * indev_act;
/**********************
* MACROS
**********************/
/**********************
* GLOBAL FUNCTIONS
**********************/
/**
* Initialize the display input device subsystem
*/
void lv_indev_init(void)
{
#if LV_INDEV_READ_PERIOD != 0
lv_task_create(indev_proc_task, LV_INDEV_READ_PERIOD, LV_TASK_PRIO_MID, NULL);
#endif
lv_indev_reset(NULL); /*Reset all input devices*/
}
/**
* Get the currently processed input device. Can be used in action functions too.
* @return pointer to the currently processed input device or NULL if no input device processing right now
*/
lv_indev_t * lv_indev_get_act(void)
{
return indev_act;
}
/**
* Get the type of an input device
* @param indev pointer to an input device
* @return the type of the input device from `lv_hal_indev_type_t` (`LV_INDEV_TYPE_...`)
*/
lv_hal_indev_type_t lv_indev_get_type(const lv_indev_t * indev)
{
if(indev == NULL) return LV_INDEV_TYPE_NONE;
return indev->driver.type;
}
/**
* Reset one or all input devices
* @param indev pointer to an input device to reset or NULL to reset all of them
*/
void lv_indev_reset(lv_indev_t * indev)
{
if(indev) indev->proc.reset_query = 1;
else {
lv_indev_t * i = lv_indev_next(NULL);
while(i) {
i->proc.reset_query = 1;
i = lv_indev_next(i);
}
}
}
/**
* Reset the long press state of an input device
* @param indev pointer to an input device
*/
void lv_indev_reset_lpr(lv_indev_t * indev)
{
indev->proc.long_pr_sent = 0;
indev->proc.longpr_rep_timestamp = lv_tick_get();
indev->proc.pr_timestamp = lv_tick_get();
}
/**
* Enable input devices device by type
* @param type Input device type
* @param enable true: enable this type; false: disable this type
*/
void lv_indev_enable(lv_hal_indev_type_t type, bool enable)
{
lv_indev_t * i = lv_indev_next(NULL);
while(i) {
if(i->driver.type == type) i->proc.disabled = enable == false ? 1 : 0;
i = lv_indev_next(i);
}
}
/**
* Set a cursor for a pointer input device (for LV_INPUT_TYPE_POINTER and LV_INPUT_TYPE_BUTTON)
* @param indev pointer to an input device
* @param cur_obj pointer to an object to be used as cursor
*/
void lv_indev_set_cursor(lv_indev_t * indev, lv_obj_t * cur_obj)
{
if(indev->driver.type != LV_INDEV_TYPE_POINTER) return;
indev->cursor = cur_obj;
if (indev->cursor)
{
lv_obj_set_parent(indev->cursor, lv_layer_sys());
lv_obj_set_pos(indev->cursor, indev->proc.act_point.x, indev->proc.act_point.y);
}
}
#if USE_LV_GROUP
/**
* Set a destination group for a keypad input device (for LV_INDEV_TYPE_KEYPAD)
* @param indev pointer to an input device
* @param group point to a group
*/
void lv_indev_set_group(lv_indev_t * indev, lv_group_t * group)
{
if(indev->driver.type == LV_INDEV_TYPE_KEYPAD || indev->driver.type == LV_INDEV_TYPE_ENCODER) indev->group = group;
}
#endif
/**
* Set the an array of points for LV_INDEV_TYPE_BUTTON.
* These points will be assigned to the buttons to press a specific point on the screen
* @param indev pointer to an input device
* @param group point to a group
*/
void lv_indev_set_button_points(lv_indev_t * indev, const lv_point_t * points)
{
if(indev->driver.type == LV_INDEV_TYPE_BUTTON) indev->btn_points = points;
}
/**
* Set feedback callback for indev.
* @param indev pointer to an input device
* @param feedback feedback callback
*/
void lv_indev_set_feedback(lv_indev_t *indev, lv_indev_feedback_t feedback)
{
indev->feedback = feedback;
}
/**
* Get the last point of an input device (for LV_INDEV_TYPE_POINTER and LV_INDEV_TYPE_BUTTON)
* @param indev pointer to an input device
* @param point pointer to a point to store the result
*/
void lv_indev_get_point(const lv_indev_t * indev, lv_point_t * point)
{
if(indev->driver.type != LV_INDEV_TYPE_POINTER && indev->driver.type != LV_INDEV_TYPE_BUTTON) {
point->x = -1;
point->y = -1;
} else {
point->x = indev->proc.act_point.x;
point->y = indev->proc.act_point.y;
}
}
/**
* Get the last key of an input device (for LV_INDEV_TYPE_KEYPAD)
* @param indev pointer to an input device
* @return the last pressed key (0 on error)
*/
uint32_t lv_indev_get_key(const lv_indev_t * indev)
{
if(indev->driver.type != LV_INDEV_TYPE_KEYPAD) return 0;
else return indev->proc.last_key;
}
/**
* Check if there is dragging with an input device or not (for LV_INDEV_TYPE_POINTER and LV_INDEV_TYPE_BUTTON)
* @param indev pointer to an input device
* @return true: drag is in progress
*/
bool lv_indev_is_dragging(const lv_indev_t * indev)
{
if(indev == NULL) return false;
if(indev->driver.type != LV_INDEV_TYPE_POINTER && indev->driver.type != LV_INDEV_TYPE_BUTTON) return false;
return indev->proc.drag_in_prog == 0 ? false : true;
}
/**
* Get the vector of dragging of an input device (for LV_INDEV_TYPE_POINTER and LV_INDEV_TYPE_BUTTON)
* @param indev pointer to an input device
* @param point pointer to a point to store the vector
*/
void lv_indev_get_vect(const lv_indev_t * indev, lv_point_t * point)
{
if(indev == NULL) {
point->x = 0;
point->y = 0;
return;
}
if(indev->driver.type != LV_INDEV_TYPE_POINTER && indev->driver.type != LV_INDEV_TYPE_BUTTON) {
point->x = 0;
point->y = 0;
} else {
point->x = indev->proc.vect.x;
point->y = indev->proc.vect.y;
}
}
/**
* Get elapsed time since last press
* @param indev pointer to an input device (NULL to get the overall smallest inactivity)
* @return Elapsed ticks (milliseconds) since last press
*/
uint32_t lv_indev_get_inactive_time(const lv_indev_t * indev)
{
uint32_t t;
if(indev) return t = lv_tick_elaps(indev->last_activity_time);
lv_indev_t * i;
t = UINT16_MAX;
i = lv_indev_next(NULL);
while(i) {
t = LV_MATH_MIN(t, lv_tick_elaps(i->last_activity_time));
i = lv_indev_next(i);
}
return t;
}
/**
* Get feedback callback for indev.
* @param indev pointer to an input device
* @return feedback callback
*/
lv_indev_feedback_t lv_indev_get_feedback(const lv_indev_t *indev)
{
return indev->feedback;
}
/**
* Do nothing until the next release
* @param indev pointer to an input device
*/
void lv_indev_wait_release(lv_indev_t * indev)
{
indev->proc.wait_unil_release = 1;
}
/**********************
* STATIC FUNCTIONS
**********************/
#if LV_INDEV_READ_PERIOD != 0
/**
* Called periodically to handle the input devices
* @param param unused
*/
static void indev_proc_task(void * param)
{
(void)param;
LV_LOG_TRACE("indev task started");
lv_indev_data_t data;
lv_indev_t * i;
i = lv_indev_next(NULL);
/*Read and process all indevs*/
while(i) {
indev_act = i;
/*Handle reset query before processing the point*/
indev_proc_reset_query_handler(i);
if(i->proc.disabled == 0) {
bool more_to_read;
do {
/*Read the data*/
more_to_read = lv_indev_read(i, &data);
indev_proc_reset_query_handler(i); /*The active object might deleted even in the read function*/
i->proc.state = data.state;
if(i->proc.state == LV_INDEV_STATE_PR) {
i->last_activity_time = lv_tick_get();
}
if(i->driver.type == LV_INDEV_TYPE_POINTER) {
indev_pointer_proc(i, &data);
} else if(i->driver.type == LV_INDEV_TYPE_KEYPAD) {
indev_keypad_proc(i, &data);
} else if(i->driver.type == LV_INDEV_TYPE_ENCODER) {
indev_encoder_proc(i, &data);
} else if(i->driver.type == LV_INDEV_TYPE_BUTTON) {
indev_button_proc(i, &data);
}
/*Handle reset query if it happened in during processing*/
indev_proc_reset_query_handler(i);
} while(more_to_read);
}
i = lv_indev_next(i); /*Go to the next indev*/
}
indev_act = NULL; /*End of indev processing, so no act indev*/
LV_LOG_TRACE("indev task finished");
}
/**
* Process a new point from LV_INDEV_TYPE_POINTER input device
* @param i pointer to an input device
* @param data pointer to the data read from the input device
*/
static void indev_pointer_proc(lv_indev_t * i, lv_indev_data_t * data)
{
/*Move the cursor if set and moved*/
if(i->cursor != NULL &&
(i->proc.last_point.x != data->point.x ||
i->proc.last_point.y != data->point.y)) {
/*Use cursor's center as pointer*/
uint32_t off_x = lv_obj_get_width(i->cursor) >> 1;
uint32_t off_y = lv_obj_get_height(i->cursor) >> 1;
lv_obj_set_pos(i->cursor, data->point.x - off_x, data->point.y - off_y);
}
i->proc.act_point.x = data->point.x;
i->proc.act_point.y = data->point.y;
if(i->proc.state == LV_INDEV_STATE_PR) {
#if LV_INDEV_POINT_MARKER != 0
lv_area_t area;
area.x1 = i->proc.act_point.x - (LV_INDEV_POINT_MARKER >> 1);
area.y1 = i->proc.act_point.y - (LV_INDEV_POINT_MARKER >> 1);
area.x2 = i->proc.act_point.x + ((LV_INDEV_POINT_MARKER >> 1) | 0x1);
area.y2 = i->proc.act_point.y + ((LV_INDEV_POINT_MARKER >> 1) | 0x1);
lv_rfill(&area, NULL, LV_COLOR_MAKE(0xFF, 0, 0), LV_OPA_COVER);
#endif
indev_proc_press(&i->proc);
} else {
indev_proc_release(&i->proc);
}
i->proc.last_point.x = i->proc.act_point.x;
i->proc.last_point.y = i->proc.act_point.y;
}
/**
* Process a new point from LV_INDEV_TYPE_KEYPAD input device
* @param i pointer to an input device
* @param data pointer to the data read from the input device
*/
static void indev_keypad_proc(lv_indev_t * i, lv_indev_data_t * data)
{
#if USE_LV_GROUP
if(i->group == NULL) return;
/*Key press happened*/
if(data->state == LV_INDEV_STATE_PR &&
i->proc.last_state == LV_INDEV_STATE_REL) {
i->proc.pr_timestamp = lv_tick_get();
lv_obj_t * focused = lv_group_get_focused(i->group);
if(focused && data->key == LV_GROUP_KEY_ENTER) {
focused->signal_func(focused, LV_SIGNAL_PRESSED, indev_act);
}
}
/*Pressing*/
else if(data->state == LV_INDEV_STATE_PR && i->proc.last_state == LV_INDEV_STATE_PR) {
if(data->key == LV_GROUP_KEY_ENTER &&
i->proc.long_pr_sent == 0 &&
lv_tick_elaps(i->proc.pr_timestamp) > LV_INDEV_LONG_PRESS_TIME) {
/*On enter long press leave edit mode.*/
lv_obj_t * focused = lv_group_get_focused(i->group);
if(focused) {
focused->signal_func(focused, LV_SIGNAL_LONG_PRESS, indev_act);
i->proc.long_pr_sent = 1;
}
}
}
/*Release happened*/
else if(data->state == LV_INDEV_STATE_REL && i->proc.last_state == LV_INDEV_STATE_PR) {
/*The user might clear the key when it was released. Always release the pressed key*/
data->key = i->proc.last_key;
/* Edit mode is not used by KEYPAD devices.
* So leave edit mode if we are in it before focusing on the next/prev object*/
if(data->key == LV_GROUP_KEY_NEXT || data->key == LV_GROUP_KEY_PREV) {
lv_group_set_editing(i->group, false);
}
if(data->key == LV_GROUP_KEY_NEXT) {
lv_group_focus_next(i->group);
} else if(data->key == LV_GROUP_KEY_PREV) {
lv_group_focus_prev(i->group);
} else if(data->key == LV_GROUP_KEY_ENTER) {
if(!i->proc.long_pr_sent) {
lv_group_send_data(i->group, data->key);
}
} else {
lv_group_send_data(i->group, data->key);
}
if(i->proc.reset_query) return; /*The object might be deleted in `focus_cb` or due to any other user event*/
i->proc.pr_timestamp = 0;
i->proc.long_pr_sent = 0;
}
i->proc.last_state = data->state;
i->proc.last_key = data->key;
#else
(void)data; /*Unused*/
(void)i; /*Unused*/
#endif
}
/**
* Process a new point from LV_INDEV_TYPE_ENCODER input device
* @param i pointer to an input device
* @param data pointer to the data read from the input device
*/
static void indev_encoder_proc(lv_indev_t * i, lv_indev_data_t * data)
{
#if USE_LV_GROUP
if(i->group == NULL) return;
/*Process the steps first. They are valid only with released button*/
if(data->state == LV_INDEV_STATE_REL) {
/*In edit mode send LEFT/RIGHT keys*/
if(lv_group_get_editing(i->group)) {
int32_t s;
if(data->enc_diff < 0) {
for(s = 0; s < -data->enc_diff; s++) lv_group_send_data(i->group, LV_GROUP_KEY_LEFT);
} else if(data->enc_diff > 0) {
for(s = 0; s < data->enc_diff; s++) lv_group_send_data(i->group, LV_GROUP_KEY_RIGHT);
}
}
/*In navigate mode focus on the next/prev objects*/
else {
int32_t s;
if(data->enc_diff < 0) {
for(s = 0; s < -data->enc_diff; s++) lv_group_focus_prev(i->group);
} else if(data->enc_diff > 0) {
for(s = 0; s < data->enc_diff; s++) lv_group_focus_next(i->group);
}
}
}
/*Key press happened*/
if(data->state == LV_INDEV_STATE_PR &&
i->proc.last_state == LV_INDEV_STATE_REL) {
i->proc.pr_timestamp = lv_tick_get();
}
/*Pressing*/
else if(data->state == LV_INDEV_STATE_PR && i->proc.last_state == LV_INDEV_STATE_PR) {
if(i->proc.long_pr_sent == 0 &&
lv_tick_elaps(i->proc.pr_timestamp) > LV_INDEV_LONG_PRESS_TIME) {
/*On enter long press leave edit mode.*/
lv_obj_t * focused = lv_group_get_focused(i->group);
bool editable = false;
if(focused) focused->signal_func(focused, LV_SIGNAL_GET_EDITABLE, &editable);
if(editable) {
if(i->group->obj_ll.head != i->group->obj_ll.tail)
lv_group_set_editing(i->group, lv_group_get_editing(i->group) ? false : true); /*Toggle edit mode on long press*/
else if(focused)
focused->signal_func(focused, LV_SIGNAL_LONG_PRESS, indev_act);
}
/*If not editable then just send a long press signal*/
else {
if(focused)
focused->signal_func(focused, LV_SIGNAL_LONG_PRESS, indev_act);
}
i->proc.long_pr_sent = 1;
}
}
/*Release happened*/
else if(data->state == LV_INDEV_STATE_REL && i->proc.last_state == LV_INDEV_STATE_PR) {
lv_obj_t * focused = lv_group_get_focused(i->group);
bool editable = false;
if(focused) focused->signal_func(focused, LV_SIGNAL_GET_EDITABLE, &editable);
/*The button was released on a non-editable object. Just send enter*/
if(!editable) {
lv_group_send_data(i->group, LV_GROUP_KEY_ENTER);
}
/*An object is being edited and the button is releases. Just send enter */
else if(i->group->editing) {
if(!i->proc.long_pr_sent || i->group->obj_ll.head == i->group->obj_ll.tail)
lv_group_send_data(i->group, LV_GROUP_KEY_ENTER); /*Ignore long pressed enter release because it comes from mode switch*/
}
/*If the focused object is editable and now in navigate mode then enter edit mode*/
else if(editable && !i->group->editing && !i->proc.long_pr_sent) {
lv_group_set_editing(i->group, lv_group_get_editing(i->group) ? false : true); /*Toggle edit mode on long press*/
}
if(i->proc.reset_query) return; /*The object might be deleted in `focus_cb` or due to any other user event*/
i->proc.pr_timestamp = 0;
i->proc.long_pr_sent = 0;
}
i->proc.last_state = data->state;
i->proc.last_key = data->key;
#else
(void)data; /*Unused*/
(void)i; /*Unused*/
#endif
}
/**
* Process new points from a input device. indev->state.pressed has to be set
* @param indev pointer to an input device state
* @param x x coordinate of the next point
* @param y y coordinate of the next point
*/
static void indev_button_proc(lv_indev_t * i, lv_indev_data_t * data)
{
i->proc.act_point.x = i->btn_points[data->btn].x;
i->proc.act_point.y = i->btn_points[data->btn].y;
/*Still the same point is pressed*/
if(i->proc.last_point.x == i->proc.act_point.x &&
i->proc.last_point.y == i->proc.act_point.y &&
data->state == LV_INDEV_STATE_PR) {
#if LV_INDEV_POINT_MARKER != 0
lv_area_t area;
area.x1 = i->proc.act_point.x - (LV_INDEV_POINT_MARKER >> 1);
area.y1 = i->proc.act_point.y - (LV_INDEV_POINT_MARKER >> 1);
area.x2 = i->proc.act_point.x + ((LV_INDEV_POINT_MARKER >> 1) | 0x1);
area.y2 = i->proc.act_point.y + ((LV_INDEV_POINT_MARKER >> 1) | 0x1);
lv_rfill(&area, NULL, LV_COLOR_MAKE(0xFF, 0, 0), LV_OPA_COVER);
#endif
indev_proc_press(&i->proc);
} else {
/*If a new point comes always make a release*/
indev_proc_release(&i->proc);
}
i->proc.last_point.x = i->proc.act_point.x;
i->proc.last_point.y = i->proc.act_point.y;
}
/**
* Process the pressed state of LV_INDEV_TYPE_POINER input devices
* @param indev pointer to an input device 'proc'
*/
static void indev_proc_press(lv_indev_proc_t * proc)
{
lv_obj_t * pr_obj = proc->act_obj;
if(proc->wait_unil_release != 0) return;
/*If there is no last object then search*/
if(proc->act_obj == NULL) {
pr_obj = indev_search_obj(proc, lv_layer_top());
if(pr_obj == NULL) pr_obj = indev_search_obj(proc, lv_scr_act());
}
/*If there is last object but it is not dragged and not protected also search*/
else if(proc->drag_in_prog == 0 &&
lv_obj_is_protected(proc->act_obj, LV_PROTECT_PRESS_LOST) == false) {/*Now act_obj != NULL*/
pr_obj = indev_search_obj(proc, lv_layer_top());
if(pr_obj == NULL) pr_obj = indev_search_obj(proc, lv_scr_act());
}
/*If a dragable or a protected object was the last then keep it*/
else {
}
/*If a new object was found reset some variables and send a pressed signal*/
if(pr_obj != proc->act_obj) {
proc->last_point.x = proc->act_point.x;
proc->last_point.y = proc->act_point.y;
/*If a new object found the previous was lost, so send a signal*/
if(proc->act_obj != NULL) {
proc->act_obj->signal_func(proc->act_obj, LV_SIGNAL_PRESS_LOST, indev_act);
if(proc->reset_query != 0) return;
}
proc->act_obj = pr_obj; /*Save the pressed object*/
proc->last_obj = proc->act_obj; /*Refresh the last_obj*/
if(proc->act_obj != NULL) {
/* Save the time when the obj pressed.
* It is necessary to count the long press time.*/
proc->pr_timestamp = lv_tick_get();
proc->long_pr_sent = 0;
proc->drag_range_out = 0;
proc->drag_in_prog = 0;
proc->drag_sum.x = 0;
proc->drag_sum.y = 0;
proc->vect.x = 0;
proc->vect.y = 0;
/*Search for 'top' attribute*/
lv_obj_t * i = proc->act_obj;
lv_obj_t * last_top = NULL;
while(i != NULL) {
if(i->top != 0) last_top = i;
i = lv_obj_get_parent(i);
}
if(last_top != NULL) {
/*Move the last_top object to the foreground*/
lv_obj_t * par = lv_obj_get_parent(last_top);
/*After list change it will be the new head*/
lv_ll_chg_list(&par->child_ll, &par->child_ll, last_top);
lv_obj_invalidate(last_top);
}
/*Send a signal about the press*/
proc->act_obj->signal_func(proc->act_obj, LV_SIGNAL_PRESSED, indev_act);
if(proc->reset_query != 0) return;
}
}
/*Calculate the vector*/
proc->vect.x = proc->act_point.x - proc->last_point.x;
proc->vect.y = proc->act_point.y - proc->last_point.y;
/*If there is active object and it can be dragged run the drag*/
if(proc->act_obj != NULL) {
proc->act_obj->signal_func(proc->act_obj, LV_SIGNAL_PRESSING, indev_act);
if(proc->reset_query != 0) return;
indev_drag(proc);
if(proc->reset_query != 0) return;
/*If there is no drag then check for long press time*/
if(proc->drag_in_prog == 0 && proc->long_pr_sent == 0) {
/*Send a signal about the long press if enough time elapsed*/
if(lv_tick_elaps(proc->pr_timestamp) > LV_INDEV_LONG_PRESS_TIME) {
pr_obj->signal_func(pr_obj, LV_SIGNAL_LONG_PRESS, indev_act);
if(proc->reset_query != 0) return;
/*Mark the signal sending to do not send it again*/
proc->long_pr_sent = 1;
/*Save the long press time stamp for the long press repeat handler*/
proc->longpr_rep_timestamp = lv_tick_get();
}
}
/*Send long press repeated signal*/
if(proc->drag_in_prog == 0 && proc->long_pr_sent == 1) {
/*Send a signal about the long press repeate if enough time elapsed*/
if(lv_tick_elaps(proc->longpr_rep_timestamp) > LV_INDEV_LONG_PRESS_REP_TIME) {
pr_obj->signal_func(pr_obj, LV_SIGNAL_LONG_PRESS_REP, indev_act);
if(proc->reset_query != 0) return;
proc->longpr_rep_timestamp = lv_tick_get();
}
}
}
}
/**
* Process the released state of LV_INDEV_TYPE_POINER input devices
* @param proc pointer to an input device 'proc'
*/
static void indev_proc_release(lv_indev_proc_t * proc)
{
if(proc->wait_unil_release != 0) {
proc->act_obj = NULL;
proc->last_obj = NULL;
proc->pr_timestamp = 0;
proc->longpr_rep_timestamp = 0;
proc->wait_unil_release = 0;
}
/*Forgot the act obj and send a released signal */
if(proc->act_obj != NULL) {
/* If the object was protected against press lost then it possible that
* the object is already not pressed but still it is the `act_obj`.
* In this case send the `LV_SIGNAL_RELEASED` if the indev is ON the `act_obj` */
if(lv_obj_is_protected(proc->act_obj, LV_PROTECT_PRESS_LOST)) {
/* Search the object on the current current coordinates.
* The start object is the object itself. If not ON it the the result will be NULL*/
lv_obj_t * obj_on = indev_search_obj(proc, proc->act_obj);
if(obj_on == proc->act_obj) proc->act_obj->signal_func(proc->act_obj, LV_SIGNAL_RELEASED, indev_act);
else proc->act_obj->signal_func(proc->act_obj, LV_SIGNAL_PRESS_LOST, indev_act);
}
/* The simple case: `act_obj` was not protected against press lost.
* If it is already not pressed then was handled in `indev_proc_press`*/
else {
proc->act_obj->signal_func(proc->act_obj, LV_SIGNAL_RELEASED, indev_act);
}
if(proc->reset_query != 0) return;
/*Handle click focus*/
#if USE_LV_GROUP
/*Edit mode is not used by POINTER devices. So leave edit mode if we are in it*/
lv_group_t * act_g = lv_obj_get_group(proc->act_obj);
if(lv_group_get_editing(act_g)) {
lv_group_set_editing(act_g, false);
}
/*Check, if the parent is in a group focus on it.*/
if(lv_obj_is_protected(proc->act_obj, LV_PROTECT_CLICK_FOCUS) == false) { /*Respect the click protection*/
lv_group_t * g = lv_obj_get_group(proc->act_obj);
lv_obj_t * parent = proc->act_obj;
while(g == NULL) {
parent = lv_obj_get_parent(parent);
if(parent == NULL) break;
if(lv_obj_is_protected(parent, LV_PROTECT_CLICK_FOCUS)) { /*Ignore is the protected against click focus*/
parent = NULL;
break;
}
g = lv_obj_get_group(parent);
}
if(g != NULL && parent != NULL)
if(lv_group_get_click_focus(g)) {
lv_group_focus_obj(parent);
}
}
#endif
if(proc->reset_query != 0) return;
proc->act_obj = NULL;
proc->pr_timestamp = 0;
proc->longpr_rep_timestamp = 0;
}
/*The reset can be set in the signal function.
* In case of reset query ignore the remaining parts.*/
if(proc->last_obj != NULL && proc->reset_query == 0) {
indev_drag_throw(proc);
if(proc->reset_query != 0) return;
}
}
/**
* Process a new point from LV_INDEV_TYPE_BUTTON input device
* @param i pointer to an input device
* @param data pointer to the data read from the input device
* Reset input device if a reset query has been sent to it
* @param indev pointer to an input device
*/
static void indev_proc_reset_query_handler(lv_indev_t * indev)
{
if(indev->proc.reset_query) {
indev->proc.act_obj = NULL;
indev->proc.last_obj = NULL;
indev->proc.drag_range_out = 0;
indev->proc.drag_in_prog = 0;
indev->proc.long_pr_sent = 0;
indev->proc.pr_timestamp = 0;
indev->proc.longpr_rep_timestamp = 0;
indev->proc.drag_sum.x = 0;
indev->proc.drag_sum.y = 0;
indev->proc.reset_query = 0;
}
}
/**
* Search the most top, clickable object on the last point of an input device
* @param proc pointer to the `lv_indev_proc_t` part of the input device
* @param obj pointer to a start object, typically the screen
* @return pointer to the found object or NULL if there was no suitable object
*/
static lv_obj_t * indev_search_obj(const lv_indev_proc_t * proc, lv_obj_t * obj)
{
lv_obj_t * found_p = NULL;
/*If the point is on this object*/
/*Check its children too*/
if(lv_area_is_point_on(&obj->coords, &proc->act_point)) {
lv_obj_t * i;
LL_READ(obj->child_ll, i) {
found_p = indev_search_obj(proc, i);
/*If a child was found then break*/
if(found_p != NULL) {
break;
}
}
/*If then the children was not ok, and this obj is clickable
* and it or its parent is not hidden then save this object*/
if(found_p == NULL && lv_obj_get_click(obj) != false) {
lv_obj_t * hidden_i = obj;
while(hidden_i != NULL) {
if(lv_obj_get_hidden(hidden_i) == true) break;
hidden_i = lv_obj_get_parent(hidden_i);
}
/*No parent found with hidden == true*/
if(hidden_i == NULL) found_p = obj;
}
}
return found_p;
}
/**
* Handle the dragging of indev_proc_p->act_obj
* @param indev pointer to a input device state
*/
static void indev_drag(lv_indev_proc_t * state)
{
lv_obj_t * drag_obj = state->act_obj;
/*If drag parent is active check recursively the drag_parent attribute*/
while(lv_obj_get_drag_parent(drag_obj) != false &&
drag_obj != NULL) {
drag_obj = lv_obj_get_parent(drag_obj);
}
if(drag_obj == NULL) return;
if(lv_obj_get_drag(drag_obj) == false) return;
/*Count the movement by drag*/
state->drag_sum.x += state->vect.x;
state->drag_sum.y += state->vect.y;
/*Enough move?*/
if(state->drag_range_out == 0) {
/*If a move is greater then LV_DRAG_LIMIT then begin the drag*/
if(LV_MATH_ABS(state->drag_sum.x) >= LV_INDEV_DRAG_LIMIT ||
LV_MATH_ABS(state->drag_sum.y) >= LV_INDEV_DRAG_LIMIT) {
state->drag_range_out = 1;
}
}
/*If the drag limit is stepped over then handle the dragging*/
if(state->drag_range_out != 0) {
/*Set new position if the vector is not zero*/
if(state->vect.x != 0 ||
state->vect.y != 0) {
/*Get the coordinates of the object and modify them*/
lv_coord_t act_x = lv_obj_get_x(drag_obj);
lv_coord_t act_y = lv_obj_get_y(drag_obj);
uint16_t inv_buf_size = lv_refr_get_buf_size(); /*Get the number of currently invalidated areas*/
lv_coord_t prev_x = drag_obj->coords.x1;
lv_coord_t prev_y = drag_obj->coords.y1;
lv_coord_t prev_par_w = lv_obj_get_width(lv_obj_get_parent(drag_obj));
lv_coord_t prev_par_h = lv_obj_get_height(lv_obj_get_parent(drag_obj));
lv_obj_set_pos(drag_obj, act_x + state->vect.x, act_y + state->vect.y);
/*Set the drag in progress flag if the object is really moved*/
if(drag_obj->coords.x1 != prev_x || drag_obj->coords.y1 != prev_y) {
if(state->drag_range_out != 0) { /*Send the drag begin signal on first move*/
drag_obj->signal_func(drag_obj, LV_SIGNAL_DRAG_BEGIN, indev_act);
if(state->reset_query != 0) return;
}
state->drag_in_prog = 1;
}
/*If the object didn't moved then clear the invalidated areas*/
else {
/*In a special case if the object is moved on a page and
* the scrollable has fit == true and the object is dragged of the page then
* while its coordinate is not changing only the parent's size is reduced */
lv_coord_t act_par_w = lv_obj_get_width(lv_obj_get_parent(drag_obj));
lv_coord_t act_par_h = lv_obj_get_height(lv_obj_get_parent(drag_obj));
if(act_par_w == prev_par_w && act_par_h == prev_par_h) {
uint16_t new_inv_buf_size = lv_refr_get_buf_size();
lv_refr_pop_from_buf(new_inv_buf_size - inv_buf_size);
}
}
}
}
}
/**
* Handle throwing by drag if the drag is ended
* @param indev pointer to an input device state
*/
static void indev_drag_throw(lv_indev_proc_t * state)
{
if(state->drag_in_prog == 0) return;
/*Set new position if the vector is not zero*/
lv_obj_t * drag_obj = state->last_obj;
/*If drag parent is active check recursively the drag_parent attribute*/
while(lv_obj_get_drag_parent(drag_obj) != false &&
drag_obj != NULL) {
drag_obj = lv_obj_get_parent(drag_obj);
}
if(drag_obj == NULL) return;
/*Return if the drag throw is not enabled*/
if(lv_obj_get_drag_throw(drag_obj) == false) {
state->drag_in_prog = 0;
drag_obj->signal_func(drag_obj, LV_SIGNAL_DRAG_END, indev_act);
return;
}
/*Reduce the vectors*/
state->vect.x = state->vect.x * (100 - LV_INDEV_DRAG_THROW) / 100;
state->vect.y = state->vect.y * (100 - LV_INDEV_DRAG_THROW) / 100;
if(state->vect.x != 0 ||
state->vect.y != 0) {
/*Get the coordinates and modify them*/
lv_area_t coords_ori;
lv_obj_get_coords(drag_obj, &coords_ori);
lv_coord_t act_x = lv_obj_get_x(drag_obj) + state->vect.x;
lv_coord_t act_y = lv_obj_get_y(drag_obj) + state->vect.y;
lv_obj_set_pos(drag_obj, act_x, act_y);
lv_area_t coord_new;
lv_obj_get_coords(drag_obj, &coord_new);
/*If non of the coordinates are changed then do not continue throwing*/
if((coords_ori.x1 == coord_new.x1 || state->vect.x == 0) &&
(coords_ori.y1 == coord_new.y1 || state->vect.y == 0)) {
state->drag_in_prog = 0;
state->vect.x = 0;
state->vect.y = 0;
drag_obj->signal_func(drag_obj, LV_SIGNAL_DRAG_END, indev_act);
}
}
/*If the vectors become 0 -> drag_in_prog = 0 and send a drag end signal*/
else {
state->drag_in_prog = 0;
drag_obj->signal_func(drag_obj, LV_SIGNAL_DRAG_END, indev_act);
}
}
#endif

View file

@ -0,0 +1,157 @@
/**
* @file lv_indev_proc.h
*
*/
#ifndef LV_INDEV_H
#define LV_INDEV_H
#ifdef __cplusplus
extern "C" {
#endif
/*********************
* INCLUDES
*********************/
#include "lv_obj.h"
#include "../lv_hal/lv_hal_indev.h"
#include "../lv_core/lv_group.h"
/*********************
* DEFINES
*********************/
/**********************
* TYPEDEFS
**********************/
/**********************
* GLOBAL PROTOTYPES
**********************/
/**
* Initialize the display input device subsystem
*/
void lv_indev_init(void);
/**
* Get the currently processed input device. Can be used in action functions too.
* @return pointer to the currently processed input device or NULL if no input device processing right now
*/
lv_indev_t * lv_indev_get_act(void);
/**
* Get the type of an input device
* @param indev pointer to an input device
* @return the type of the input device from `lv_hal_indev_type_t` (`LV_INDEV_TYPE_...`)
*/
lv_hal_indev_type_t lv_indev_get_type(const lv_indev_t * indev);
/**
* Reset one or all input devices
* @param indev pointer to an input device to reset or NULL to reset all of them
*/
void lv_indev_reset(lv_indev_t * indev);
/**
* Reset the long press state of an input device
* @param indev_proc pointer to an input device
*/
void lv_indev_reset_lpr(lv_indev_t * indev);
/**
* Enable input devices device by type
* @param type Input device type
* @param enable true: enable this type; false: disable this type
*/
void lv_indev_enable(lv_hal_indev_type_t type, bool enable);
/**
* Set a cursor for a pointer input device (for LV_INPUT_TYPE_POINTER and LV_INPUT_TYPE_BUTTON)
* @param indev pointer to an input device
* @param cur_obj pointer to an object to be used as cursor
*/
void lv_indev_set_cursor(lv_indev_t *indev, lv_obj_t *cur_obj);
#if USE_LV_GROUP
/**
* Set a destination group for a keypad input device (for LV_INDEV_TYPE_KEYPAD)
* @param indev pointer to an input device
* @param group point to a group
*/
void lv_indev_set_group(lv_indev_t *indev, lv_group_t *group);
#endif
/**
* Set the an array of points for LV_INDEV_TYPE_BUTTON.
* These points will be assigned to the buttons to press a specific point on the screen
* @param indev pointer to an input device
* @param group point to a group
*/
void lv_indev_set_button_points(lv_indev_t *indev, const lv_point_t *points);
/**
* Set feedback callback for indev.
* @param indev pointer to an input device
* @param feedback feedback callback
*/
void lv_indev_set_feedback(lv_indev_t *indev, lv_indev_feedback_t feedback);
/**
* Get the last point of an input device (for LV_INDEV_TYPE_POINTER and LV_INDEV_TYPE_BUTTON)
* @param indev pointer to an input device
* @param point pointer to a point to store the result
*/
void lv_indev_get_point(const lv_indev_t * indev, lv_point_t * point);
/**
* Get the last key of an input device (for LV_INDEV_TYPE_KEYPAD)
* @param indev pointer to an input device
* @return the last pressed key (0 on error)
*/
uint32_t lv_indev_get_key(const lv_indev_t * indev);
/**
* Check if there is dragging with an input device or not (for LV_INDEV_TYPE_POINTER and LV_INDEV_TYPE_BUTTON)
* @param indev pointer to an input device
* @return true: drag is in progress
*/
bool lv_indev_is_dragging(const lv_indev_t * indev);
/**
* Get the vector of dragging of an input device (for LV_INDEV_TYPE_POINTER and LV_INDEV_TYPE_BUTTON)
* @param indev pointer to an input device
* @param point pointer to a point to store the vector
*/
void lv_indev_get_vect(const lv_indev_t * indev, lv_point_t * point);
/**
* Get elapsed time since last press
* @param indev pointer to an input device (NULL to get the overall smallest inactivity)
* @return Elapsed ticks (milliseconds) since last press
*/
uint32_t lv_indev_get_inactive_time(const lv_indev_t * indev);
/**
* Get feedback callback for indev.
* @param indev pointer to an input device
* @return feedback callback
*/
lv_indev_feedback_t lv_indev_get_feedback(const lv_indev_t *indev);
/**
* Do nothing until the next release
* @param indev pointer to an input device
*/
void lv_indev_wait_release(lv_indev_t * indev);
/**********************
* MACROS
**********************/
#ifdef __cplusplus
} /* extern "C" */
#endif
#endif /*LV_INDEV_H*/

View file

@ -0,0 +1,117 @@
/**
* @file lv_lang.c
*
*/
/*********************
* INCLUDES
*********************/
#include "lv_lang.h"
#if USE_LV_MULTI_LANG
#include "lv_obj.h"
#include "../lv_misc/lv_gc.h"
/*********************
* DEFINES
*********************/
/**********************
* TYPEDEFS
**********************/
/**********************
* STATIC PROTOTYPES
**********************/
static void lang_set_core(lv_obj_t * obj);
/**********************
* STATIC VARIABLES
**********************/
static uint8_t lang_act = 0;
static const void * (*get_txt)(uint16_t);
/**********************
* MACROS
**********************/
/**********************
* GLOBAL FUNCTIONS
**********************/
/**
* Change the language
* @param lang_id the id of the
*/
void lv_lang_set(uint8_t lang_id)
{
lang_act = lang_id;
lv_obj_t * i;
LL_READ(LV_GC_ROOT(_lv_scr_ll), i) {
i->signal_func(i, LV_SIGNAL_LANG_CHG, NULL);
lang_set_core(i);
}
lang_set_core(lv_scr_act());
}
/**
* Set a function to get the texts of the set languages from a `txt_id`
* @param fp a function pointer to get the texts
*/
void lv_lang_set_text_func(const void * (*fp)(uint16_t))
{
get_txt = fp;
}
/**
* Use the function set by `lv_lang_set_text_func` to get the `txt_id` text in the set language
* @param txt_id an ID of the text to get
* @return the `txt_id` txt on the set language
*/
const void * lv_lang_get_text(uint16_t txt_id)
{
if(get_txt == NULL) {
LV_LOG_WARN("lv_lang_get_text: text_func is not specified");
return NULL; /*No text_get function specified */
}
if(txt_id == LV_LANG_TXT_ID_NONE) {
LV_LOG_WARN("lv_lang_get_text: attempts to get invalid text ID");
return NULL; /*Invalid txt_id*/
}
return get_txt(txt_id);
}
/**
* Return with ID of the currently selected language
* @return pointer to the active screen object (loaded by 'lv_scr_load()')
*/
uint8_t lv_lang_act(void)
{
return lang_act;
}
/**********************
* STATIC FUNCTIONS
**********************/
/**
* Change the language of the children. (Called recursively)
* @param obj pointer to an object
*/
static void lang_set_core(lv_obj_t * obj)
{
lv_obj_t * i;
LL_READ(obj->child_ll, i) {
i->signal_func(i, LV_SIGNAL_LANG_CHG, NULL);
lang_set_core(i);
}
}
#endif /*USE_LV_MULTI_LANG*/

View file

@ -0,0 +1,74 @@
/**
* @file lv_lang.h
*
*/
#ifndef LV_LANG_H
#define LV_LANG_H
#ifdef __cplusplus
extern "C" {
#endif
/*********************
* INCLUDES
*********************/
#ifdef LV_CONF_INCLUDE_SIMPLE
#include "lv_conf.h"
#else
#include "../../lv_conf.h"
#endif
#if USE_LV_MULTI_LANG
#include <stdint.h>
/*********************
* DEFINES
*********************/
#define LV_LANG_TXT_ID_NONE 0xFFFF /*Used to not assign any text IDs for a multi-language object.*/
/**********************
* TYPEDEFS
**********************/
/**********************
* GLOBAL PROTOTYPES
**********************/
/**
* Change the language
* @param lang_id the id of the
*/
void lv_lang_set(uint8_t lang_id);
/**
* Set a function to get the texts of the set languages from a `txt_id`
* @param fp a function pointer to get the texts
*/
void lv_lang_set_text_func(const void * (*fp)(uint16_t));
/**
* Use the function set by `lv_lang_set_text_func` to get the `txt_id` text in the set language
* @param txt_id an ID of the text to get
* @return the `txt_id` txt on the set language
*/
const void * lv_lang_get_text(uint16_t txt_id);
/**
* Return with ID of the currently selected language
* @return pointer to the active screen object (loaded by 'lv_scr_load()')
*/
uint8_t lv_lang_act(void);
/**********************
* MACROS
**********************/
#endif /*USE_LV_MULTI_LANG*/
#ifdef __cplusplus
} /* extern "C" */
#endif
#endif /*LV_LANG_H*/

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,840 @@
/**
* @file lv_obj.h
*
*/
#ifndef LV_OBJ_H
#define LV_OBJ_H
#ifdef __cplusplus
extern "C" {
#endif
/*********************
* INCLUDES
*********************/
#ifdef LV_CONF_INCLUDE_SIMPLE
#include "lv_conf.h"
#else
#include "../../lv_conf.h"
#endif
#include <stddef.h>
#include "lv_style.h"
#include "../lv_misc/lv_area.h"
#include "../lv_misc/lv_mem.h"
#include "../lv_misc/lv_ll.h"
#include "../lv_misc/lv_color.h"
#include "../lv_misc/lv_log.h"
/*********************
* DEFINES
*********************/
/*Error check of lv_conf.h*/
#if LV_HOR_RES == 0 || LV_VER_RES == 0
#error "LittlevGL: LV_HOR_RES and LV_VER_RES must be greater than 0"
#endif
#if LV_ANTIALIAS > 1
#error "LittlevGL: LV_ANTIALIAS can be only 0 or 1"
#endif
#if LV_VDB_SIZE == 0 && LV_ANTIALIAS != 0
#error "LittlevGL: If LV_VDB_SIZE == 0 the anti-aliasing must be disabled"
#endif
#if LV_VDB_SIZE > 0 && LV_VDB_SIZE < LV_HOR_RES
#error "LittlevGL: Small Virtual Display Buffer (lv_conf.h: LV_VDB_SIZE >= LV_HOR_RES)"
#endif
#if LV_VDB_SIZE == 0 && USE_LV_REAL_DRAW == 0
#error "LittlevGL: If LV_VDB_SIZE = 0 Real drawing function are required (lv_conf.h: USE_LV_REAL_DRAW 1)"
#endif
#define LV_ANIM_IN 0x00 /*Animation to show an object. 'OR' it with lv_anim_builtin_t*/
#define LV_ANIM_OUT 0x80 /*Animation to hide an object. 'OR' it with lv_anim_builtin_t*/
#define LV_ANIM_DIR_MASK 0x80 /*ANIM_IN/ANIM_OUT mask*/
#define LV_MAX_ANCESTOR_NUM 8
/**********************
* TYPEDEFS
**********************/
struct _lv_obj_t;
enum
{
LV_DESIGN_DRAW_MAIN,
LV_DESIGN_DRAW_POST,
LV_DESIGN_COVER_CHK,
};
typedef uint8_t lv_design_mode_t;
typedef bool (* lv_design_func_t) (struct _lv_obj_t * obj, const lv_area_t * mask_p, lv_design_mode_t mode);
enum
{
LV_RES_INV = 0, /*Typically indicates that the object is deleted (become invalid) in the action function or an operation was failed*/
LV_RES_OK, /*The object is valid (no deleted) after the action*/
};
typedef uint8_t lv_res_t;
enum
{
/*General signals*/
LV_SIGNAL_CLEANUP,
LV_SIGNAL_CHILD_CHG,
LV_SIGNAL_CORD_CHG,
LV_SIGNAL_STYLE_CHG,
LV_SIGNAL_REFR_EXT_SIZE,
LV_SIGNAL_LANG_CHG,
LV_SIGNAL_GET_TYPE,
_LV_SIGNAL_FEEDBACK_SECTION_START,
/*Input device related*/
LV_SIGNAL_PRESSED,
LV_SIGNAL_PRESSING,
LV_SIGNAL_PRESS_LOST,
LV_SIGNAL_RELEASED,
LV_SIGNAL_LONG_PRESS,
LV_SIGNAL_LONG_PRESS_REP,
LV_SIGNAL_DRAG_BEGIN,
LV_SIGNAL_DRAG_END,
/*Group related*/
LV_SIGNAL_FOCUS,
LV_SIGNAL_DEFOCUS,
LV_SIGNAL_CONTROLL,
_LV_SIGNAL_FEEDBACK_SECTION_END,
LV_SIGNAL_GET_EDITABLE,
};
typedef uint8_t lv_signal_t;
typedef lv_res_t (* lv_signal_func_t) (struct _lv_obj_t * obj, lv_signal_t sign, void * param);
enum
{
LV_ALIGN_CENTER = 0,
LV_ALIGN_IN_TOP_LEFT,
LV_ALIGN_IN_TOP_MID,
LV_ALIGN_IN_TOP_RIGHT,
LV_ALIGN_IN_BOTTOM_LEFT,
LV_ALIGN_IN_BOTTOM_MID,
LV_ALIGN_IN_BOTTOM_RIGHT,
LV_ALIGN_IN_LEFT_MID,
LV_ALIGN_IN_RIGHT_MID,
LV_ALIGN_OUT_TOP_LEFT,
LV_ALIGN_OUT_TOP_MID,
LV_ALIGN_OUT_TOP_RIGHT,
LV_ALIGN_OUT_BOTTOM_LEFT,
LV_ALIGN_OUT_BOTTOM_MID,
LV_ALIGN_OUT_BOTTOM_RIGHT,
LV_ALIGN_OUT_LEFT_TOP,
LV_ALIGN_OUT_LEFT_MID,
LV_ALIGN_OUT_LEFT_BOTTOM,
LV_ALIGN_OUT_RIGHT_TOP,
LV_ALIGN_OUT_RIGHT_MID,
LV_ALIGN_OUT_RIGHT_BOTTOM,
};
typedef uint8_t lv_align_t;
#if LV_OBJ_REALIGN
typedef struct {
const struct _lv_obj_t * base;
lv_coord_t xofs;
lv_coord_t yofs;
lv_align_t align;
uint8_t auto_realign :1;
uint8_t origo_align :1; /*1: the oigo (center of the object) was aligned with `lv_obj_align_origo`*/
}lv_reailgn_t;
#endif
typedef struct _lv_obj_t
{
struct _lv_obj_t * par; /*Pointer to the parent object*/
lv_ll_t child_ll; /*Linked list to store the children objects*/
lv_area_t coords; /*Coordinates of the object (x1, y1, x2, y2)*/
lv_signal_func_t signal_func; /*Object type specific signal function*/
lv_design_func_t design_func; /*Object type specific design function*/
void * ext_attr; /*Object type specific extended data*/
lv_style_t * style_p; /*Pointer to the object's style*/
#if LV_OBJ_FREE_PTR != 0
void * free_ptr; /*Application specific pointer (set it freely)*/
#endif
#if USE_LV_GROUP != 0
void * group_p; /*Pointer to the group of the object*/
#endif
/*Attributes and states*/
uint8_t click :1; /*1: Can be pressed by an input device*/
uint8_t drag :1; /*1: Enable the dragging*/
uint8_t drag_throw :1; /*1: Enable throwing with drag*/
uint8_t drag_parent :1; /*1: Parent will be dragged instead*/
uint8_t hidden :1; /*1: Object is hidden*/
uint8_t top :1; /*1: If the object or its children is clicked it goes to the foreground*/
uint8_t opa_scale_en :1; /*1: opa_scale is set*/
uint8_t protect; /*Automatically happening actions can be prevented. 'OR'ed values from `lv_protect_t`*/
lv_opa_t opa_scale; /*Scale down the opacity by this factor. Effects all children as well*/
lv_coord_t ext_size; /*EXTtend the size of the object in every direction. E.g. for shadow drawing*/
#if LV_OBJ_REALIGN
lv_reailgn_t realign;
#endif
#ifdef LV_OBJ_FREE_NUM_TYPE
LV_OBJ_FREE_NUM_TYPE free_num; /*Application specific identifier (set it freely)*/
#endif
} lv_obj_t;
typedef lv_res_t (*lv_action_t) (struct _lv_obj_t * obj);
/*Protect some attributes (max. 8 bit)*/
enum
{
LV_PROTECT_NONE = 0x00,
LV_PROTECT_CHILD_CHG = 0x01, /*Disable the child change signal. Used by the library*/
LV_PROTECT_PARENT = 0x02, /*Prevent automatic parent change (e.g. in lv_page)*/
LV_PROTECT_POS = 0x04, /*Prevent automatic positioning (e.g. in lv_cont layout)*/
LV_PROTECT_FOLLOW = 0x08, /*Prevent the object be followed in automatic ordering (e.g. in lv_cont PRETTY layout)*/
LV_PROTECT_PRESS_LOST= 0x10, /*If the `indev` was pressing this object but swiped out while pressing do not search other object.*/
LV_PROTECT_CLICK_FOCUS= 0x20,/*Prevent focusing the object by clicking on it*/
};
typedef uint8_t lv_protect_t;
/*Used by `lv_obj_get_type()`. The object's and its ancestor types are stored here*/
typedef struct {
const char * type[LV_MAX_ANCESTOR_NUM]; /*[0]: the actual type, [1]: ancestor, [2] #1's ancestor ... [x]: "lv_obj" */
} lv_obj_type_t;
enum
{
LV_ANIM_NONE = 0,
LV_ANIM_FLOAT_TOP, /*Float from/to the top*/
LV_ANIM_FLOAT_LEFT, /*Float from/to the left*/
LV_ANIM_FLOAT_BOTTOM, /*Float from/to the bottom*/
LV_ANIM_FLOAT_RIGHT, /*Float from/to the right*/
LV_ANIM_GROW_H, /*Grow/shrink horizontally*/
LV_ANIM_GROW_V, /*Grow/shrink vertically*/
};
typedef uint8_t lv_anim_builtin_t;
/**********************
* GLOBAL PROTOTYPES
**********************/
/**
* Init. the 'lv' library.
*/
void lv_init(void);
/*--------------------
* Create and delete
*-------------------*/
/**
* Create a basic object
* @param parent pointer to a parent object.
* If NULL then a screen will be created
* @param copy pointer to a base object, if not NULL then the new object will be copied from it
* @return pointer to the new object
*/
lv_obj_t * lv_obj_create(lv_obj_t * parent,const lv_obj_t * copy);
/**
* Delete 'obj' and all of its children
* @param obj pointer to an object to delete
* @return LV_RES_INV because the object is deleted
*/
lv_res_t lv_obj_del(lv_obj_t * obj);
/**
* Delete all children of an object
* @param obj pointer to an object
*/
void lv_obj_clean(lv_obj_t *obj);
/**
* Mark the object as invalid therefore its current position will be redrawn by 'lv_refr_task'
* @param obj pointer to an object
*/
void lv_obj_invalidate(const lv_obj_t * obj);
/*=====================
* Setter functions
*====================*/
/*--------------
* Screen set
*--------------*/
/**
* Load a new screen
* @param scr pointer to a screen
*/
void lv_scr_load(lv_obj_t * scr);
/*--------------------
* Parent/children set
*--------------------*/
/**
* Set a new parent for an object. Its relative position will be the same.
* @param obj pointer to an object. Can't be a screen.
* @param parent pointer to the new parent object. (Can't be NULL)
*/
void lv_obj_set_parent(lv_obj_t * obj, lv_obj_t * parent);
/*--------------------
* Coordinate set
* ------------------*/
/**
* Set relative the position of an object (relative to the parent)
* @param obj pointer to an object
* @param x new distance from the left side of the parent
* @param y new distance from the top of the parent
*/
void lv_obj_set_pos(lv_obj_t * obj, lv_coord_t x, lv_coord_t y);
/**
* Set the x coordinate of a object
* @param obj pointer to an object
* @param x new distance from the left side from the parent
*/
void lv_obj_set_x(lv_obj_t * obj, lv_coord_t x);
/**
* Set the y coordinate of a object
* @param obj pointer to an object
* @param y new distance from the top of the parent
*/
void lv_obj_set_y(lv_obj_t * obj, lv_coord_t y);
/**
* Set the size of an object
* @param obj pointer to an object
* @param w new width
* @param h new height
*/
void lv_obj_set_size(lv_obj_t * obj, lv_coord_t w, lv_coord_t h);
/**
* Set the width of an object
* @param obj pointer to an object
* @param w new width
*/
void lv_obj_set_width(lv_obj_t * obj, lv_coord_t w);
/**
* Set the height of an object
* @param obj pointer to an object
* @param h new height
*/
void lv_obj_set_height(lv_obj_t * obj, lv_coord_t h);
/**
* Align an object to an other object.
* @param obj pointer to an object to align
* @param base pointer to an object (if NULL the parent is used). 'obj' will be aligned to it.
* @param align type of alignment (see 'lv_align_t' enum)
* @param x_mod x coordinate shift after alignment
* @param y_mod y coordinate shift after alignment
*/
void lv_obj_align(lv_obj_t * obj,const lv_obj_t * base, lv_align_t align, lv_coord_t x_mod, lv_coord_t y_mod);
/**
* Align an object to an other object.
* @param obj pointer to an object to align
* @param base pointer to an object (if NULL the parent is used). 'obj' will be aligned to it.
* @param align type of alignment (see 'lv_align_t' enum)
* @param x_mod x coordinate shift after alignment
* @param y_mod y coordinate shift after alignment
*/
void lv_obj_align_origo(lv_obj_t * obj, const lv_obj_t * base, lv_align_t align, lv_coord_t x_mod, lv_coord_t y_mod);
/**
* Realign the object based on the last `lv_obj_align` parameters.
* @param obj pointer to an object
*/
void lv_obj_realign(lv_obj_t * obj);
/**
* Enable the automatic realign of the object when its size has changed based on the last `lv_obj_align` parameters.
* @param obj pointer to an object
* @param en true: enable auto realign; false: disable auto realign
*/
void lv_obj_set_auto_realign(lv_obj_t * obj, bool en);
/*---------------------
* Appearance set
*--------------------*/
/**
* Set a new style for an object
* @param obj pointer to an object
* @param style_p pointer to the new style
*/
void lv_obj_set_style(lv_obj_t * obj, lv_style_t * style);
/**
* Notify an object about its style is modified
* @param obj pointer to an object
*/
void lv_obj_refresh_style(lv_obj_t * obj);
/**
* Notify all object if a style is modified
* @param style pointer to a style. Only the objects with this style will be notified
* (NULL to notify all objects)
*/
void lv_obj_report_style_mod(lv_style_t * style);
/*-----------------
* Attribute set
*----------------*/
/**
* Hide an object. It won't be visible and clickable.
* @param obj pointer to an object
* @param en true: hide the object
*/
void lv_obj_set_hidden(lv_obj_t * obj, bool en);
/**
* Enable or disable the clicking of an object
* @param obj pointer to an object
* @param en true: make the object clickable
*/
void lv_obj_set_click(lv_obj_t * obj, bool en);
/**
* Enable to bring this object to the foreground if it
* or any of its children is clicked
* @param obj pointer to an object
* @param en true: enable the auto top feature
*/
void lv_obj_set_top(lv_obj_t * obj, bool en);
/**
* Enable the dragging of an object
* @param obj pointer to an object
* @param en true: make the object dragable
*/
void lv_obj_set_drag(lv_obj_t * obj, bool en);
/**
* Enable the throwing of an object after is is dragged
* @param obj pointer to an object
* @param en true: enable the drag throw
*/
void lv_obj_set_drag_throw(lv_obj_t * obj, bool en);
/**
* Enable to use parent for drag related operations.
* If trying to drag the object the parent will be moved instead
* @param obj pointer to an object
* @param en true: enable the 'drag parent' for the object
*/
void lv_obj_set_drag_parent(lv_obj_t * obj, bool en);
/**
* Set editable parameter Used by groups and keyboard/encoder control.
* Editable object has something inside to choose (the elements of a list)
* @param obj pointer to an object
* @param en true: enable editing
*/
//void lv_obj_set_editable(lv_obj_t * obj, bool en);
/**
* Set the opa scale enable parameter (required to set opa_scale with `lv_obj_set_opa_scale()`)
* @param obj pointer to an object
* @param en true: opa scaling is enabled for this object and all children; false: no opa scaling
*/
void lv_obj_set_opa_scale_enable(lv_obj_t * obj, bool en);
/**
* Set the opa scale of an object
* @param obj pointer to an object
* @param opa_scale a factor to scale down opacity [0..255]
*/
void lv_obj_set_opa_scale(lv_obj_t * obj, lv_opa_t opa_scale);
/**
* Set a bit or bits in the protect filed
* @param obj pointer to an object
* @param prot 'OR'-ed values from `lv_protect_t`
*/
void lv_obj_set_protect(lv_obj_t * obj, uint8_t prot);
/**
* Clear a bit or bits in the protect filed
* @param obj pointer to an object
* @param prot 'OR'-ed values from `lv_protect_t`
*/
void lv_obj_clear_protect(lv_obj_t * obj, uint8_t prot);
/**
* Set the signal function of an object.
* Always call the previous signal function in the new.
* @param obj pointer to an object
* @param fp the new signal function
*/
void lv_obj_set_signal_func(lv_obj_t * obj, lv_signal_func_t fp);
/**
* Set a new design function for an object
* @param obj pointer to an object
* @param fp the new design function
*/
void lv_obj_set_design_func(lv_obj_t * obj, lv_design_func_t fp);
/*----------------
* Other set
*--------------*/
/**
* Allocate a new ext. data for an object
* @param obj pointer to an object
* @param ext_size the size of the new ext. data
* @return pointer to the allocated ext
*/
void * lv_obj_allocate_ext_attr(lv_obj_t * obj, uint16_t ext_size);
/**
* Send a 'LV_SIGNAL_REFR_EXT_SIZE' signal to the object
* @param obj pointer to an object
*/
void lv_obj_refresh_ext_size(lv_obj_t * obj);
#ifdef LV_OBJ_FREE_NUM_TYPE
/**
* Set an application specific number for an object.
* It can help to identify objects in the application.
* @param obj pointer to an object
* @param free_num the new free number
*/
void lv_obj_set_free_num(lv_obj_t * obj, LV_OBJ_FREE_NUM_TYPE free_num);
#endif
#if LV_OBJ_FREE_PTR != 0
/**
* Set an application specific pointer for an object.
* It can help to identify objects in the application.
* @param obj pointer to an object
* @param free_p the new free pinter
*/
void lv_obj_set_free_ptr(lv_obj_t * obj, void * free_p);
#endif
#if USE_LV_ANIMATION
/**
* Animate an object
* @param obj pointer to an object to animate
* @param type type of animation from 'lv_anim_builtin_t'. 'OR' it with ANIM_IN or ANIM_OUT
* @param time time of animation in milliseconds
* @param delay delay before the animation in milliseconds
* @param cb a function to call when the animation is ready
*/
void lv_obj_animate(lv_obj_t * obj, lv_anim_builtin_t type, uint16_t time, uint16_t delay, void (*cb) (lv_obj_t *));
#endif
/*=======================
* Getter functions
*======================*/
/*------------------
* Screen get
*-----------------*/
/**
* Return with a pointer to the active screen
* @return pointer to the active screen object (loaded by 'lv_scr_load()')
*/
lv_obj_t * lv_scr_act(void);
/**
* Return with the top layer. (Same on every screen and it is above the normal screen layer)
* @return pointer to the top layer object (transparent screen sized lv_obj)
*/
lv_obj_t * lv_layer_top(void);
/**
* Return with the system layer. (Same on every screen and it is above the all other layers)
* It is used for example by the cursor
* @return pointer to the system layer object (transparent screen sized lv_obj)
*/
lv_obj_t * lv_layer_sys(void);
/**
* Return with the screen of an object
* @param obj pointer to an object
* @return pointer to a screen
*/
lv_obj_t * lv_obj_get_screen(const lv_obj_t * obj);
/*---------------------
* Parent/children get
*--------------------*/
/**
* Returns with the parent of an object
* @param obj pointer to an object
* @return pointer to the parent of 'obj'
*/
lv_obj_t * lv_obj_get_parent(const lv_obj_t * obj);
/**
* Iterate through the children of an object (start from the "youngest, lastly created")
* @param obj pointer to an object
* @param child NULL at first call to get the next children
* and the previous return value later
* @return the child after 'act_child' or NULL if no more child
*/
lv_obj_t * lv_obj_get_child(const lv_obj_t * obj, const lv_obj_t * child);
/**
* Iterate through the children of an object (start from the "oldest", firstly created)
* @param obj pointer to an object
* @param child NULL at first call to get the next children
* and the previous return value later
* @return the child after 'act_child' or NULL if no more child
*/
lv_obj_t * lv_obj_get_child_back(const lv_obj_t * obj, const lv_obj_t * child);
/**
* Count the children of an object (only children directly on 'obj')
* @param obj pointer to an object
* @return children number of 'obj'
*/
uint16_t lv_obj_count_children(const lv_obj_t * obj);
/*---------------------
* Coordinate get
*--------------------*/
/**
* Copy the coordinates of an object to an area
* @param obj pointer to an object
* @param cords_p pointer to an area to store the coordinates
*/
void lv_obj_get_coords(const lv_obj_t * obj, lv_area_t * cords_p);
/**
* Get the x coordinate of object
* @param obj pointer to an object
* @return distance of 'obj' from the left side of its parent
*/
lv_coord_t lv_obj_get_x(const lv_obj_t * obj);
/**
* Get the y coordinate of object
* @param obj pointer to an object
* @return distance of 'obj' from the top of its parent
*/
lv_coord_t lv_obj_get_y(const lv_obj_t * obj);
/**
* Get the width of an object
* @param obj pointer to an object
* @return the width
*/
lv_coord_t lv_obj_get_width(const lv_obj_t * obj);
/**
* Get the height of an object
* @param obj pointer to an object
* @return the height
*/
lv_coord_t lv_obj_get_height(const lv_obj_t * obj);
/**
* Get the extended size attribute of an object
* @param obj pointer to an object
* @return the extended size attribute
*/
lv_coord_t lv_obj_get_ext_size(const lv_obj_t * obj);
/**
* Get the automatic realign property of the object.
* @param obj pointer to an object
* @return true: auto realign is enabled; false: auto realign is disabled
*/
bool lv_obj_get_auto_realign(lv_obj_t * obj);
/*-----------------
* Appearance get
*---------------*/
/**
* Get the style pointer of an object (if NULL get style of the parent)
* @param obj pointer to an object
* @return pointer to a style
*/
lv_style_t * lv_obj_get_style(const lv_obj_t * obj);
/*-----------------
* Attribute get
*----------------*/
/**
* Get the hidden attribute of an object
* @param obj pointer to an object
* @return true: the object is hidden
*/
bool lv_obj_get_hidden(const lv_obj_t * obj);
/**
* Get the click enable attribute of an object
* @param obj pointer to an object
* @return true: the object is clickable
*/
bool lv_obj_get_click(const lv_obj_t * obj);
/**
* Get the top enable attribute of an object
* @param obj pointer to an object
* @return true: the auto top feature is enabled
*/
bool lv_obj_get_top(const lv_obj_t * obj);
/**
* Get the drag enable attribute of an object
* @param obj pointer to an object
* @return true: the object is dragable
*/
bool lv_obj_get_drag(const lv_obj_t * obj);
/**
* Get the drag throw enable attribute of an object
* @param obj pointer to an object
* @return true: drag throw is enabled
*/
bool lv_obj_get_drag_throw(const lv_obj_t * obj);
/**
* Get the drag parent attribute of an object
* @param obj pointer to an object
* @return true: drag parent is enabled
*/
bool lv_obj_get_drag_parent(const lv_obj_t * obj);
/**
* Get the opa scale enable parameter
* @param obj pointer to an object
* @return true: opa scaling is enabled for this object and all children; false: no opa scaling
*/
lv_opa_t lv_obj_get_opa_scale_enable(const lv_obj_t * obj);
/**
* Get the opa scale parameter of an object
* @param obj pointer to an object
* @return opa scale [0..255]
*/
lv_opa_t lv_obj_get_opa_scale(const lv_obj_t * obj);
/**
* Get the protect field of an object
* @param obj pointer to an object
* @return protect field ('OR'ed values of `lv_protect_t`)
*/
uint8_t lv_obj_get_protect(const lv_obj_t * obj);
/**
* Check at least one bit of a given protect bitfield is set
* @param obj pointer to an object
* @param prot protect bits to test ('OR'ed values of `lv_protect_t`)
* @return false: none of the given bits are set, true: at least one bit is set
*/
bool lv_obj_is_protected(const lv_obj_t * obj, uint8_t prot);
/**
* Get the signal function of an object
* @param obj pointer to an object
* @return the signal function
*/
lv_signal_func_t lv_obj_get_signal_func(const lv_obj_t * obj);
/**
* Get the design function of an object
* @param obj pointer to an object
* @return the design function
*/
lv_design_func_t lv_obj_get_design_func(const lv_obj_t * obj);
/*------------------
* Other get
*-----------------*/
/**
* Get the ext pointer
* @param obj pointer to an object
* @return the ext pointer but not the dynamic version
* Use it as ext->data1, and NOT da(ext)->data1
*/
void * lv_obj_get_ext_attr(const lv_obj_t * obj);
/**
* Get object's and its ancestors type. Put their name in `type_buf` starting with the current type.
* E.g. buf.type[0]="lv_btn", buf.type[1]="lv_cont", buf.type[2]="lv_obj"
* @param obj pointer to an object which type should be get
* @param buf pointer to an `lv_obj_type_t` buffer to store the types
*/
void lv_obj_get_type(lv_obj_t * obj, lv_obj_type_t * buf);
#ifdef LV_OBJ_FREE_NUM_TYPE
/**
* Get the free number
* @param obj pointer to an object
* @return the free number
*/
LV_OBJ_FREE_NUM_TYPE lv_obj_get_free_num(const lv_obj_t * obj);
#endif
#if LV_OBJ_FREE_PTR != 0
/**
* Get the free pointer
* @param obj pointer to an object
* @return the free pointer
*/
void * lv_obj_get_free_ptr(const lv_obj_t * obj);
#endif
#if USE_LV_GROUP
/**
* Get the group of the object
* @param obj pointer to an object
* @return the pointer to group of the object
*/
void * lv_obj_get_group(const lv_obj_t * obj);
/**
* Tell whether the object is the focused object of a group or not.
* @param obj pointer to an object
* @return true: the object is focused, false: the object is not focused or not in a group
*/
bool lv_obj_is_focused(const lv_obj_t * obj);
#endif
/**********************
* MACROS
**********************/
#ifdef __cplusplus
} /* extern "C" */
#endif
#endif /*LV_OBJ_H*/

View file

@ -0,0 +1,614 @@
/**
* @file lv_refr.c
*
*/
/*********************
* INCLUDES
*********************/
#include <stddef.h>
#include "lv_refr.h"
#include "lv_vdb.h"
#include "../lv_hal/lv_hal_tick.h"
#include "../lv_hal/lv_hal_disp.h"
#include "../lv_misc/lv_task.h"
#include "../lv_misc/lv_mem.h"
/*********************
* DEFINES
*********************/
#ifndef LV_INV_FIFO_SIZE
#define LV_INV_FIFO_SIZE 32 /*The average count of objects on a screen */
#endif
/**********************
* TYPEDEFS
**********************/
typedef struct {
lv_area_t area;
uint8_t joined;
} lv_join_t;
/**********************
* STATIC PROTOTYPES
**********************/
static void lv_refr_task(void * param);
static void lv_refr_join_area(void);
static void lv_refr_areas(void);
#if LV_VDB_SIZE == 0
static void lv_refr_area_no_vdb(const lv_area_t * area_p);
#else
static void lv_refr_area_with_vdb(const lv_area_t * area_p);
static void lv_refr_area_part_vdb(const lv_area_t * area_p);
#endif
static lv_obj_t * lv_refr_get_top_obj(const lv_area_t * area_p, lv_obj_t * obj);
static void lv_refr_obj_and_children(lv_obj_t * top_p, const lv_area_t * mask_p);
static void lv_refr_obj(lv_obj_t * obj, const lv_area_t * mask_ori_p);
/**********************
* STATIC VARIABLES
**********************/
static lv_join_t inv_buf[LV_INV_FIFO_SIZE];
static uint16_t inv_buf_p;
static void (*monitor_cb)(uint32_t, uint32_t); /*Monitor the rendering time*/
static void (*round_cb)(lv_area_t *); /*If set then called to modify invalidated areas for special display controllers*/
static uint32_t px_num;
/**********************
* MACROS
**********************/
/**********************
* GLOBAL FUNCTIONS
**********************/
/**
* Initialize the screen refresh subsystem
*/
void lv_refr_init(void)
{
inv_buf_p = 0;
memset(inv_buf, 0, sizeof(inv_buf));
lv_task_t * task;
task = lv_task_create(lv_refr_task, LV_REFR_PERIOD, LV_TASK_PRIO_MID, NULL);
lv_task_ready(task); /*Be sure the screen will be refreshed immediately on start up*/
}
/**
* Redraw the invalidated areas now.
* Normally the redrawing is periodically executed in `lv_task_handler` but a long blocking process can
* prevent the call of `lv_task_handler`. In this case if the the GUI is updated in the process (e.g. progress bar)
* this function can be called when the screen should be updated.
*/
void lv_refr_now(void)
{
lv_refr_task(NULL);
}
/**
* Invalidate an area
* @param area_p pointer to area which should be invalidated
*/
void lv_inv_area(const lv_area_t * area_p)
{
/*Clear the invalidate buffer if the parameter is NULL*/
if(area_p == NULL) {
inv_buf_p = 0;
return;
}
lv_area_t scr_area;
scr_area.x1 = 0;
scr_area.y1 = 0;
scr_area.x2 = LV_HOR_RES - 1;
scr_area.y2 = LV_VER_RES - 1;
lv_area_t com_area;
bool suc;
suc = lv_area_intersect(&com_area, area_p, &scr_area);
/*The area is truncated to the screen*/
if(suc != false) {
if(round_cb) round_cb(&com_area);
/*Save only if this area is not in one of the saved areas*/
uint16_t i;
for(i = 0; i < inv_buf_p; i++) {
if(lv_area_is_in(&com_area, &inv_buf[i].area) != false) return;
}
/*Save the area*/
if(inv_buf_p < LV_INV_FIFO_SIZE) {
lv_area_copy(&inv_buf[inv_buf_p].area, &com_area);
} else {/*If no place for the area add the screen*/
inv_buf_p = 0;
lv_area_copy(&inv_buf[inv_buf_p].area, &scr_area);
}
inv_buf_p ++;
}
}
/**
* Set a function to call after every refresh to announce the refresh time and the number of refreshed pixels
* @param cb pointer to a callback function (void my_refr_cb(uint32_t time_ms, uint32_t px_num))
* time_ms: refresh time in [ms]
* px_num: not the drawn pixels but the number of affected pixels of the screen
* (more pixels are drawn because of overlapping objects)
*/
void lv_refr_set_monitor_cb(void (*cb)(uint32_t, uint32_t))
{
monitor_cb = cb;
}
/**
* Called when an area is invalidated to modify the coordinates of the area.
* Special display controllers may require special coordinate rounding
* @param cb pointer to the a function which will modify the area
*/
void lv_refr_set_round_cb(void(*cb)(lv_area_t *))
{
round_cb = cb;
}
/**
* Get the number of areas in the buffer
* @return number of invalid areas
*/
uint16_t lv_refr_get_buf_size(void)
{
return inv_buf_p;
}
/**
* Pop (delete) the last 'num' invalidated areas from the buffer
* @param num number of areas to delete
*/
void lv_refr_pop_from_buf(uint16_t num)
{
if(inv_buf_p < num) inv_buf_p = 0;
else inv_buf_p -= num;
}
/**********************
* STATIC FUNCTIONS
**********************/
/**
* Called periodically to handle the refreshing
* @param param unused
*/
static void lv_refr_task(void * param)
{
(void)param;
LV_LOG_TRACE("display refresh task started");
uint32_t start = lv_tick_get();
if(lv_disp_get_active() == NULL) {
LV_LOG_TRACE("No display is registered");
return;
}
lv_refr_join_area();
lv_refr_areas();
/*If refresh happened ...*/
if(inv_buf_p != 0) {
/*In true double buffered mode copy the refreshed areas to the new VDB to keep it up to date*/
#if LV_VDB_TRUE_DOUBLE_BUFFERED
lv_vdb_t * vdb_p = lv_vdb_get();
vdb_p->area.x1 = 0;
vdb_p->area.x2 = LV_HOR_RES-1;
vdb_p->area.y1 = 0;
vdb_p->area.y2 = LV_VER_RES - 1;
/*Flush the content of the VDB*/
lv_vdb_flush();
/* With true double buffering the flushing should be only the address change of the current frame buffer
* Wait until the address change is ready and copy the active content to the other frame buffer (new active VDB)
* The changes will be written to the new VDB.*/
lv_vdb_t * vdb_act = lv_vdb_get_active();
lv_vdb_t * vdb_ina = lv_vdb_get_inactive();
uint8_t * buf_act = (uint8_t *) vdb_act->buf;
uint8_t * buf_ina = (uint8_t *) vdb_ina->buf;
uint16_t a;
for(a = 0; a < inv_buf_p; a++) {
if(inv_buf[a].joined == 0) {
lv_coord_t y;
uint32_t start_offs = ((LV_HOR_RES * inv_buf[a].area.y1 + inv_buf[a].area.x1) * LV_VDB_PX_BPP) >> 3;
uint32_t line_length = (lv_area_get_width(&inv_buf[a].area) * LV_VDB_PX_BPP) >> 3;
for(y = inv_buf[a].area.y1; y <= inv_buf[a].area.y2; y++) {
memcpy(buf_act + start_offs, buf_ina + start_offs, line_length);
start_offs += (LV_HOR_RES * LV_VDB_PX_BPP) >> 3;
}
}
}
#endif
/*Clean up*/
memset(inv_buf, 0, sizeof(inv_buf));
inv_buf_p = 0;
/*Call monitor cb if present*/
if(monitor_cb != NULL) {
monitor_cb(lv_tick_elaps(start), px_num);
}
}
LV_LOG_TRACE("display refresh task finished");
}
/**
* Join the areas which has got common parts
*/
static void lv_refr_join_area(void)
{
uint32_t join_from;
uint32_t join_in;
lv_area_t joined_area;
for(join_in = 0; join_in < inv_buf_p; join_in++) {
if(inv_buf[join_in].joined != 0) continue;
/*Check all areas to join them in 'join_in'*/
for(join_from = 0; join_from < inv_buf_p; join_from++) {
/*Handle only unjoined areas and ignore itself*/
if(inv_buf[join_from].joined != 0 || join_in == join_from) {
continue;
}
/*Check if the areas are on each other*/
if(lv_area_is_on(&inv_buf[join_in].area,
&inv_buf[join_from].area) == false) {
continue;
}
lv_area_join(&joined_area, &inv_buf[join_in].area,
&inv_buf[join_from].area);
/*Join two area only if the joined area size is smaller*/
if(lv_area_get_size(&joined_area) <
(lv_area_get_size(&inv_buf[join_in].area) + lv_area_get_size(&inv_buf[join_from].area))) {
lv_area_copy(&inv_buf[join_in].area, &joined_area);
/*Mark 'join_form' is joined into 'join_in'*/
inv_buf[join_from].joined = 1;
}
}
}
}
/**
* Refresh the joined areas
*/
static void lv_refr_areas(void)
{
px_num = 0;
uint32_t i;
for(i = 0; i < inv_buf_p; i++) {
/*Refresh the unjoined areas*/
if(inv_buf[i].joined == 0) {
/*If there is no VDB do simple drawing*/
#if LV_VDB_SIZE == 0
lv_refr_area_no_vdb(&inv_buf[i].area);
#else
/*If VDB is used...*/
lv_refr_area_with_vdb(&inv_buf[i].area);
#endif
if(monitor_cb != NULL) px_num += lv_area_get_size(&inv_buf[i].area);
}
}
}
#if LV_VDB_SIZE == 0
/**
* Refresh an area if there is no Virtual Display Buffer
* @param area_p pointer to an area to refresh
*/
static void lv_refr_area_no_vdb(const lv_area_t * area_p)
{
lv_obj_t * top_p;
/*Get top object which is not covered by others*/
top_p = lv_refr_get_top_obj(area_p, lv_scr_act());
/*Do the refreshing*/
lv_refr_obj_and_children(top_p, area_p);
/*Also refresh top and sys layer unconditionally*/
lv_refr_obj_and_children(lv_layer_top(), area_p);
lv_refr_obj_and_children(lv_layer_sys(), area_p);
}
#else
/**
* Refresh an area if there is Virtual Display Buffer
* @param area_p pointer to an area to refresh
*/
static void lv_refr_area_with_vdb(const lv_area_t * area_p)
{
#if LV_VDB_TRUE_DOUBLE_BUFFERED == 0
/*Calculate the max row num*/
lv_coord_t w = lv_area_get_width(area_p);
lv_coord_t h = lv_area_get_height(area_p);
lv_coord_t y2 = area_p->y2 >= LV_VER_RES ? y2 = LV_VER_RES - 1 : area_p->y2;
int32_t max_row = (uint32_t) LV_VDB_SIZE / w;
if(max_row > h) max_row = h;
/*Round down the lines of VDB if rounding is added*/
if(round_cb) {
lv_area_t tmp;
tmp.x1 = 0;
tmp.x2 = 0;
tmp.y1 = 0;
lv_coord_t y_tmp = max_row - 1;
do {
tmp.y2 = y_tmp;
round_cb(&tmp);
/*If this height fits into `max_row` then fine*/
if(lv_area_get_height(&tmp) <= max_row) break;
/*Decrement the height of the area until it fits into `max_row` after rounding*/
y_tmp --;
} while(y_tmp != 0);
if(y_tmp == 0) {
LV_LOG_WARN("Can't set VDB height using the round function. (Wrong round_cb or to small VDB)");
return;
} else {
max_row = tmp.y2 + 1;
}
}
/*Always use the full row*/
lv_coord_t row;
lv_coord_t row_last = 0;
for(row = area_p->y1; row + max_row - 1 <= y2; row += max_row) {
lv_vdb_t * vdb_p = lv_vdb_get();
if(!vdb_p) {
LV_LOG_WARN("Invalid VDB pointer");
return;
}
/*Calc. the next y coordinates of VDB*/
vdb_p->area.x1 = area_p->x1;
vdb_p->area.x2 = area_p->x2;
vdb_p->area.y1 = row;
vdb_p->area.y2 = row + max_row - 1;
if(vdb_p->area.y2 > y2) vdb_p->area.y2 = y2;
row_last = vdb_p->area.y2;
lv_refr_area_part_vdb(area_p);
}
/*If the last y coordinates are not handled yet ...*/
if(y2 != row_last) {
lv_vdb_t * vdb_p = lv_vdb_get();
if(!vdb_p) {
LV_LOG_WARN("Invalid VDB pointer");
return;
}
/*Calc. the next y coordinates of VDB*/
vdb_p->area.x1 = area_p->x1;
vdb_p->area.x2 = area_p->x2;
vdb_p->area.y1 = row;
vdb_p->area.y2 = y2;
/*Refresh this part too*/
lv_refr_area_part_vdb(area_p);
}
#else
lv_vdb_t * vdb_p = lv_vdb_get();
vdb_p->area.x1 = 0;
vdb_p->area.x2 = LV_HOR_RES-1;
vdb_p->area.y1 = 0;
vdb_p->area.y2 = LV_VER_RES - 1;
lv_refr_area_part_vdb(area_p);
#endif
}
/**
* Refresh a part of an area which is on the actual Virtual Display Buffer
* @param area_p pointer to an area to refresh
*/
static void lv_refr_area_part_vdb(const lv_area_t * area_p)
{
lv_vdb_t * vdb_p = lv_vdb_get();
if(!vdb_p) {
LV_LOG_WARN("Invalid VDB pointer");
return;
}
lv_obj_t * top_p;
/*Get the new mask from the original area and the act. VDB
It will be a part of 'area_p'*/
lv_area_t start_mask;
lv_area_intersect(&start_mask, area_p, &vdb_p->area);
/*Get the most top object which is not covered by others*/
top_p = lv_refr_get_top_obj(&start_mask, lv_scr_act());
/*Do the refreshing from the top object*/
lv_refr_obj_and_children(top_p, &start_mask);
/*Also refresh top and sys layer unconditionally*/
lv_refr_obj_and_children(lv_layer_top(), &start_mask);
lv_refr_obj_and_children(lv_layer_sys(), &start_mask);
/* In true double buffered mode flush only once when all areas were rendered.
* In normal mode flush after every area */
#if LV_VDB_TRUE_DOUBLE_BUFFERED == 0
/*Flush the content of the VDB*/
lv_vdb_flush();
#endif
}
#endif /*LV_VDB_SIZE == 0*/
/**
* Search the most top object which fully covers an area
* @param area_p pointer to an area
* @param obj the first object to start the searching (typically a screen)
* @return
*/
static lv_obj_t * lv_refr_get_top_obj(const lv_area_t * area_p, lv_obj_t * obj)
{
lv_obj_t * i;
lv_obj_t * found_p = NULL;
/*If this object is fully cover the draw area check the children too */
if(lv_area_is_in(area_p, &obj->coords) && obj->hidden == 0) {
LL_READ(obj->child_ll, i) {
found_p = lv_refr_get_top_obj(area_p, i);
/*If a children is ok then break*/
if(found_p != NULL) {
break;
}
}
/*If no better children check this object*/
if(found_p == NULL) {
lv_style_t * style = lv_obj_get_style(obj);
if(style->body.opa == LV_OPA_COVER &&
obj->design_func(obj, area_p, LV_DESIGN_COVER_CHK) != false &&
lv_obj_get_opa_scale(obj) == LV_OPA_COVER) {
found_p = obj;
}
}
}
return found_p;
}
/**
* Make the refreshing from an object. Draw all its children and the youngers too.
* @param top_p pointer to an objects. Start the drawing from it.
* @param mask_p pointer to an area, the objects will be drawn only here
*/
static void lv_refr_obj_and_children(lv_obj_t * top_p, const lv_area_t * mask_p)
{
/* Normally always will be a top_obj (at least the screen)
* but in special cases (e.g. if the screen has alpha) it won't.
* In this case use the screen directly */
if(top_p == NULL) top_p = lv_scr_act();
/*Refresh the top object and its children*/
lv_refr_obj(top_p, mask_p);
/*Draw the 'younger' sibling objects because they can be on top_obj */
lv_obj_t * par;
lv_obj_t * i;
lv_obj_t * border_p = top_p;
par = lv_obj_get_parent(top_p);
/*Do until not reach the screen*/
while(par != NULL) {
/*object before border_p has to be redrawn*/
i = lv_ll_get_prev(&(par->child_ll), border_p);
while(i != NULL) {
/*Refresh the objects*/
lv_refr_obj(i, mask_p);
i = lv_ll_get_prev(&(par->child_ll), i);
}
/*The new border will be there last parents,
*so the 'younger' brothers of parent will be refreshed*/
border_p = par;
/*Go a level deeper*/
par = lv_obj_get_parent(par);
}
/*Call the post draw design function of the parents of the to object*/
par = lv_obj_get_parent(top_p);
while(par != NULL) {
par->design_func(par, mask_p, LV_DESIGN_DRAW_POST);
par = lv_obj_get_parent(par);
}
}
/**
* Refresh an object an all of its children. (Called recursively)
* @param obj pointer to an object to refresh
* @param mask_ori_p pointer to an area, the objects will be drawn only here
*/
static void lv_refr_obj(lv_obj_t * obj, const lv_area_t * mask_ori_p)
{
/*Do not refresh hidden objects*/
if(obj->hidden != 0) return;
bool union_ok; /* Store the return value of area_union */
/* Truncate the original mask to the coordinates of the parent
* because the parent and its children are visible only here */
lv_area_t obj_mask;
lv_area_t obj_ext_mask;
lv_area_t obj_area;
lv_coord_t ext_size = obj->ext_size;
lv_obj_get_coords(obj, &obj_area);
obj_area.x1 -= ext_size;
obj_area.y1 -= ext_size;
obj_area.x2 += ext_size;
obj_area.y2 += ext_size;
union_ok = lv_area_intersect(&obj_ext_mask, mask_ori_p, &obj_area);
/*Draw the parent and its children only if they ore on 'mask_parent'*/
if(union_ok != false) {
/* Redraw the object */
obj->design_func(obj, &obj_ext_mask, LV_DESIGN_DRAW_MAIN);
//usleep(5 * 1000); /*DEBUG: Wait after every object draw to see the order of drawing*/
/*Create a new 'obj_mask' without 'ext_size' because the children can't be visible there*/
lv_obj_get_coords(obj, &obj_area);
union_ok = lv_area_intersect(&obj_mask, mask_ori_p, &obj_area);
if(union_ok != false) {
lv_area_t mask_child; /*Mask from obj and its child*/
lv_obj_t * child_p;
lv_area_t child_area;
LL_READ_BACK(obj->child_ll, child_p) {
lv_obj_get_coords(child_p, &child_area);
ext_size = child_p->ext_size;
child_area.x1 -= ext_size;
child_area.y1 -= ext_size;
child_area.x2 += ext_size;
child_area.y2 += ext_size;
/* Get the union (common parts) of original mask (from obj)
* and its child */
union_ok = lv_area_intersect(&mask_child, &obj_mask, &child_area);
/*If the parent and the child has common area then refresh the child */
if(union_ok) {
/*Refresh the next children*/
lv_refr_obj(child_p, &mask_child);
}
}
}
/* If all the children are redrawn make 'post draw' design */
obj->design_func(obj, &obj_ext_mask, LV_DESIGN_DRAW_POST);
}
}

View file

@ -0,0 +1,94 @@
/**
* @file lv_refr.h
*
*/
#ifndef LV_REFR_H
#define LV_REFR_H
#ifdef __cplusplus
extern "C" {
#endif
/*********************
* INCLUDES
*********************/
#include "lv_obj.h"
/*********************
* DEFINES
*********************/
/**********************
* TYPEDEFS
**********************/
/**********************
* STATIC PROTOTYPES
**********************/
/**********************
* STATIC VARIABLES
**********************/
/**********************
* MACROS
**********************/
/**********************
* GLOBAL FUNCTIONS
**********************/
/**
* Initialize the screen refresh subsystem
*/
void lv_refr_init(void);
/**
* Redraw the invalidated areas now.
* Normally the redrawing is periodically executed in `lv_task_handler` but a long blocking process can
* prevent the call of `lv_task_handler`. In this case if the the GUI is updated in the process (e.g. progress bar)
* this function can be called when the screen should be updated.
*/
void lv_refr_now(void);
/**
* Invalidate an area
* @param area_p pointer to area which should be invalidated
*/
void lv_inv_area(const lv_area_t * area_p);
/**
* Set a function to call after every refresh to announce the refresh time and the number of refreshed pixels
* @param cb pointer to a callback function (void my_refr_cb(uint32_t time_ms, uint32_t px_num))
*/
void lv_refr_set_monitor_cb(void (*cb)(uint32_t, uint32_t));
/**
* Called when an area is invalidated to modify the coordinates of the area.
* Special display controllers may require special coordinate rounding
* @param cb pointer to the a function which will modify the area
*/
void lv_refr_set_round_cb(void(*cb)(lv_area_t*));
/**
* Get the number of areas in the buffer
* @return number of invalid areas
*/
uint16_t lv_refr_get_buf_size(void);
/**
* Pop (delete) the last 'num' invalidated areas from the buffer
* @param num number of areas to delete
*/
void lv_refr_pop_from_buf(uint16_t num);
/**********************
* STATIC FUNCTIONS
**********************/
#ifdef __cplusplus
} /* extern "C" */
#endif
#endif /*LV_REFR_H*/

View file

@ -0,0 +1,357 @@
/*
* Copyright (c) 2018 CTCaer
*
* This program is free software; you can redistribute it and/or modify it
* under the terms and conditions of the GNU General Public License,
* version 2, as published by the Free Software Foundation.
*
* This program is distributed in the hope it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
/*********************
* INCLUDES
*********************/
#include "lv_obj.h"
#include "../lv_misc/lv_mem.h"
/*********************
* DEFINES
*********************/
#define STYLE_MIX_MAX 256
#define STYLE_MIX_SHIFT 8 /*log2(STYLE_MIX_MAX)*/
#define VAL_PROP(v1, v2, r) v1 + (((v2-v1) * r) >> STYLE_MIX_SHIFT)
#define STYLE_ATTR_MIX(attr, r) if(start->attr != end->attr) {res->attr = VAL_PROP(start->attr, end->attr, r);} else {res->attr = start->attr;}
/**********************
* TYPEDEFS
**********************/
#if USE_LV_ANIMATION
typedef struct {
lv_style_t style_start; /*Save not only pointers because can be same as 'style_anim' then it will be modified too*/
lv_style_t style_end;
lv_style_t * style_anim;
void (*end_cb)(void *);
} lv_style_anim_dsc_t;
#endif
/**********************
* STATIC PROTOTYPES
**********************/
#if USE_LV_ANIMATION
static void style_animator(lv_style_anim_dsc_t * dsc, int32_t val);
static void style_animation_common_end_cb(void * ptr);
#endif
/**********************
* STATIC VARIABLES
**********************/
lv_style_t lv_style_scr;
lv_style_t lv_style_transp;
lv_style_t lv_style_transp_fit;
lv_style_t lv_style_transp_tight;
lv_style_t lv_style_plain;
lv_style_t lv_style_plain_color;
lv_style_t lv_style_pretty;
lv_style_t lv_style_pretty_color;
lv_style_t lv_style_btn_rel;
lv_style_t lv_style_btn_pr;
lv_style_t lv_style_btn_tgl_rel;
lv_style_t lv_style_btn_tgl_pr;
lv_style_t lv_style_btn_ina;
/**********************
* MACROS
**********************/
/**********************
* GLOBAL FUNCTIONS
**********************/
/**
* Init the basic styles
*/
void lv_style_init(void)
{
/* Not White/Black/Gray colors are created by HSV model with
* HUE = 210*/
/*Screen style*/
lv_style_scr.glass = 0;
lv_style_scr.body.opa = LV_OPA_COVER;
lv_style_scr.body.main_color = LV_COLOR_BLACK;
lv_style_scr.body.grad_color = LV_COLOR_BLACK;
lv_style_scr.body.radius = 0;
lv_style_scr.body.padding.ver = LV_DPI / 12;
lv_style_scr.body.padding.hor = LV_DPI / 12;
lv_style_scr.body.padding.inner = LV_DPI / 12;
lv_style_scr.body.border.color = LV_COLOR_WHITE;
lv_style_scr.body.border.opa = LV_OPA_COVER;
lv_style_scr.body.border.width = 0;
lv_style_scr.body.border.part = LV_BORDER_FULL;
lv_style_scr.body.shadow.color = LV_COLOR_GRAY;
lv_style_scr.body.shadow.type = LV_SHADOW_FULL;
lv_style_scr.body.shadow.width = 0;
lv_style_scr.text.opa = LV_OPA_COVER;
lv_style_scr.text.color = LV_COLOR_HEX(0xFBFBFB);
lv_style_scr.text.font = LV_FONT_DEFAULT;
lv_style_scr.text.letter_space = 3; // Important
lv_style_scr.text.line_space = 2;
lv_style_scr.image.opa = LV_OPA_COVER;
lv_style_scr.image.color = LV_COLOR_MAKE(0x20, 0x20, 0x20);
lv_style_scr.image.intense = LV_OPA_TRANSP;
lv_style_scr.line.opa = LV_OPA_COVER;
lv_style_scr.line.color = LV_COLOR_MAKE(0x20, 0x20, 0x20);
lv_style_scr.line.width = 2;
lv_style_scr.line.rounded = 0;
/*Plain style (by default near the same as the screen style)*/
memcpy(&lv_style_plain, &lv_style_scr, sizeof(lv_style_t));
/*Plain color style*/
memcpy(&lv_style_plain_color, &lv_style_plain, sizeof(lv_style_t));
lv_style_plain_color.text.color = LV_COLOR_MAKE(0xf0, 0xf0, 0xf0);
lv_style_plain_color.image.color = LV_COLOR_MAKE(0xf0, 0xf0, 0xf0);
lv_style_plain_color.line.color = LV_COLOR_MAKE(0xf0, 0xf0, 0xf0);
lv_style_plain_color.body.main_color = LV_COLOR_MAKE(0x55, 0x96, 0xd8);
lv_style_plain_color.body.grad_color = lv_style_plain_color.body.main_color;
/*Pretty style */
memcpy(&lv_style_pretty, &lv_style_plain, sizeof(lv_style_t));
lv_style_pretty.text.color = LV_COLOR_MAKE(0x20, 0x20, 0x20);
lv_style_pretty.image.color = LV_COLOR_MAKE(0x20, 0x20, 0x20);
lv_style_pretty.line.color = LV_COLOR_MAKE(0x20, 0x20, 0x20);
lv_style_pretty.body.main_color = LV_COLOR_WHITE;
lv_style_pretty.body.grad_color = LV_COLOR_SILVER;
lv_style_pretty.body.radius = LV_DPI / 15;
lv_style_pretty.body.border.color = LV_COLOR_MAKE(0x40, 0x40, 0x40);
lv_style_pretty.body.border.width = LV_DPI / 50 >= 1 ? LV_DPI / 50 : 1;
lv_style_pretty.body.border.opa = LV_OPA_30;
/*Pretty color style*/
memcpy(&lv_style_pretty_color, &lv_style_pretty, sizeof(lv_style_t));
lv_style_pretty_color.text.color = LV_COLOR_MAKE(0xe0, 0xe0, 0xe0);
lv_style_pretty_color.image.color = LV_COLOR_MAKE(0xe0, 0xe0, 0xe0);
lv_style_pretty_color.line.color = LV_COLOR_MAKE(0xc0, 0xc0, 0xc0);
lv_style_pretty_color.body.main_color = LV_COLOR_MAKE(0x6b, 0x9a, 0xc7);
lv_style_pretty_color.body.grad_color = LV_COLOR_MAKE(0x2b, 0x59, 0x8b);
lv_style_pretty_color.body.border.color = LV_COLOR_MAKE(0x15, 0x2c, 0x42);
/*Transparent style*/
memcpy(&lv_style_transp, &lv_style_plain, sizeof(lv_style_t));
lv_style_transp.body.empty = 1;
lv_style_transp.glass = 1;
lv_style_transp.body.border.width = 0;
/*Transparent fitting size*/
memcpy(&lv_style_transp_fit, &lv_style_transp, sizeof(lv_style_t));
lv_style_transp_fit.body.padding.hor = 0;
lv_style_transp_fit.body.padding.ver = 0;
/*Transparent tight style*/
memcpy(&lv_style_transp_tight, &lv_style_transp_fit, sizeof(lv_style_t));
lv_style_transp_tight.body.padding.inner = 0;
/*Button released style*/
memcpy(&lv_style_btn_rel, &lv_style_plain, sizeof(lv_style_t));
lv_style_btn_rel.body.main_color = LV_COLOR_MAKE(0x76, 0xa2, 0xd0);
lv_style_btn_rel.body.grad_color = LV_COLOR_MAKE(0x19, 0x3a, 0x5d);
lv_style_btn_rel.body.radius = LV_DPI / 15;
lv_style_btn_rel.body.padding.hor = LV_DPI / 4;
lv_style_btn_rel.body.padding.ver = LV_DPI / 6;
lv_style_btn_rel.body.padding.inner = LV_DPI / 10;
lv_style_btn_rel.body.border.color = LV_COLOR_MAKE(0x0b, 0x19, 0x28);
lv_style_btn_rel.body.border.width = LV_DPI / 50 >= 1 ? LV_DPI / 50 : 1;
lv_style_btn_rel.body.border.opa = LV_OPA_70;
lv_style_btn_rel.body.shadow.color = LV_COLOR_GRAY;
lv_style_btn_rel.body.shadow.width = 0;
lv_style_btn_rel.text.color = LV_COLOR_MAKE(0xff, 0xff, 0xff);
lv_style_btn_rel.image.color = LV_COLOR_MAKE(0xff, 0xff, 0xff);
/*Button pressed style*/
memcpy(&lv_style_btn_pr, &lv_style_btn_rel, sizeof(lv_style_t));
lv_style_btn_pr.body.main_color = LV_COLOR_MAKE(0x33, 0x62, 0x94);
lv_style_btn_pr.body.grad_color = LV_COLOR_MAKE(0x10, 0x26, 0x3c);
lv_style_btn_pr.text.color = LV_COLOR_MAKE(0xa4, 0xb5, 0xc6);
lv_style_btn_pr.image.color = LV_COLOR_MAKE(0xa4, 0xb5, 0xc6);
lv_style_btn_pr.line.color = LV_COLOR_MAKE(0xa4, 0xb5, 0xc6);
/*Button toggle released style*/
memcpy(&lv_style_btn_tgl_rel, &lv_style_btn_rel, sizeof(lv_style_t));
lv_style_btn_tgl_rel.body.main_color = LV_COLOR_MAKE(0x0a, 0x11, 0x22);
lv_style_btn_tgl_rel.body.grad_color = LV_COLOR_MAKE(0x37, 0x62, 0x90);
lv_style_btn_tgl_rel.body.border.color = LV_COLOR_MAKE(0x01, 0x07, 0x0d);
lv_style_btn_tgl_rel.text.color = LV_COLOR_MAKE(0xc8, 0xdd, 0xf4);
lv_style_btn_tgl_rel.image.color = LV_COLOR_MAKE(0xc8, 0xdd, 0xf4);
lv_style_btn_tgl_rel.line.color = LV_COLOR_MAKE(0xc8, 0xdd, 0xf4);
/*Button toggle pressed style*/
memcpy(&lv_style_btn_tgl_pr, &lv_style_btn_tgl_rel, sizeof(lv_style_t));
lv_style_btn_tgl_pr.body.main_color = LV_COLOR_MAKE(0x02, 0x14, 0x27);
lv_style_btn_tgl_pr.body.grad_color = LV_COLOR_MAKE(0x2b, 0x4c, 0x70);
lv_style_btn_tgl_pr.text.color = LV_COLOR_MAKE(0xa4, 0xb5, 0xc6);
lv_style_btn_tgl_pr.image.color = LV_COLOR_MAKE(0xa4, 0xb5, 0xc6);
lv_style_btn_tgl_pr.line.color = LV_COLOR_MAKE(0xa4, 0xb5, 0xc6);
/*Button inactive style*/
memcpy(&lv_style_btn_ina, &lv_style_btn_rel, sizeof(lv_style_t));
lv_style_btn_ina.body.main_color = LV_COLOR_MAKE(0xd8, 0xd8, 0xd8);
lv_style_btn_ina.body.grad_color = LV_COLOR_MAKE(0xd8, 0xd8, 0xd8);
lv_style_btn_ina.body.border.color = LV_COLOR_MAKE(0x90, 0x90, 0x90);
lv_style_btn_ina.text.color = LV_COLOR_MAKE(0x70, 0x70, 0x70);
lv_style_btn_ina.image.color = LV_COLOR_MAKE(0x70, 0x70, 0x70);
lv_style_btn_ina.line.color = LV_COLOR_MAKE(0x70, 0x70, 0x70);
}
/**
* Copy a style to an other
* @param dest pointer to the destination style
* @param src pointer to the source style
*/
void lv_style_copy(lv_style_t * dest, const lv_style_t * src)
{
memcpy(dest, src, sizeof(lv_style_t));
}
/**
* Mix two styles according to a given ratio
* @param start start style
* @param end end style
* @param res store the result style here
* @param ratio the ratio of mix [0..256]; 0: `start` style; 256: `end` style
*/
void lv_style_mix(const lv_style_t * start, const lv_style_t * end, lv_style_t * res, uint16_t ratio)
{
STYLE_ATTR_MIX(body.opa, ratio);
STYLE_ATTR_MIX(body.radius, ratio);
STYLE_ATTR_MIX(body.border.width, ratio);
STYLE_ATTR_MIX(body.border.opa, ratio);
STYLE_ATTR_MIX(body.shadow.width, ratio);
STYLE_ATTR_MIX(body.padding.hor, ratio);
STYLE_ATTR_MIX(body.padding.ver, ratio);
STYLE_ATTR_MIX(body.padding.inner, ratio);
STYLE_ATTR_MIX(text.line_space, ratio);
STYLE_ATTR_MIX(text.letter_space, ratio);
STYLE_ATTR_MIX(text.opa, ratio);
STYLE_ATTR_MIX(line.width, ratio);
STYLE_ATTR_MIX(line.opa, ratio);
STYLE_ATTR_MIX(image.intense, ratio);
STYLE_ATTR_MIX(image.opa, ratio);
lv_opa_t opa = ratio == STYLE_MIX_MAX ? LV_OPA_COVER : ratio;
res->body.main_color = lv_color_mix(end->body.main_color, start->body.main_color, opa);
res->body.grad_color = lv_color_mix(end->body.grad_color, start->body.grad_color, opa);
res->body.border.color = lv_color_mix(end->body.border.color, start->body.border.color, opa);
res->body.shadow.color = lv_color_mix(end->body.shadow.color, start->body.shadow.color, opa);
res->text.color = lv_color_mix(end->text.color, start->text.color, opa);
res->image.color = lv_color_mix(end->image.color, start->image.color, opa);
res->line.color = lv_color_mix(end->line.color, start->line.color, opa);
if(ratio < (STYLE_MIX_MAX >> 1)) {
res->body.empty = start->body.empty;
res->body.border.part = start->body.border.part;
res->glass = start->glass;
res->text.font = start->text.font;
res->body.shadow.type = start->body.shadow.type;
res->line.rounded = start->line.rounded;
} else {
res->body.empty = end->body.empty;
res->body.border.part = end->body.border.part;
res->glass = end->glass;
res->text.font = end->text.font;
res->body.shadow.type = end->body.shadow.type;
res->line.rounded = end->line.rounded;
}
}
#if USE_LV_ANIMATION
/**
* Create an animation from a pre-configured 'lv_style_anim_t' variable
* @param anim pointer to a pre-configured 'lv_style_anim_t' variable (will be copied)
* @return pointer to a descriptor. Really this variable will be animated. (Can be used in `lv_anim_del(dsc, NULL)`)
*/
void * lv_style_anim_create(lv_style_anim_t * anim)
{
lv_style_anim_dsc_t * dsc;
dsc = lv_mem_alloc(sizeof(lv_style_anim_dsc_t));
lv_mem_assert(dsc);
if(dsc == NULL) return NULL;
dsc->style_anim = anim->style_anim;
memcpy(&dsc->style_start, anim->style_start, sizeof(lv_style_t));
memcpy(&dsc->style_end, anim->style_end, sizeof(lv_style_t));
memcpy(dsc->style_anim, anim->style_start, sizeof(lv_style_t));
dsc->end_cb = anim->end_cb;
lv_anim_t a;
a.var = (void *)dsc;
a.start = 0;
a.end = STYLE_MIX_MAX;
a.fp = (lv_anim_fp_t)style_animator;
a.path = lv_anim_path_linear;
a.end_cb = style_animation_common_end_cb;
a.act_time = anim->act_time;
a.time = anim->time;
a.playback = anim->playback;
a.playback_pause = anim->playback_pause;
a.repeat = anim->repeat;
a.repeat_pause = anim->repeat_pause;
lv_anim_create(&a);
return dsc;
}
#endif
/**********************
* STATIC FUNCTIONS
**********************/
#if USE_LV_ANIMATION
/**
* Used by the style animations to set the values of a style according to start and end style.
* @param dsc the 'animated variable' set by lv_style_anim_create()
* @param val the current state of the animation between 0 and LV_STYLE_ANIM_RES
*/
static void style_animator(lv_style_anim_dsc_t * dsc, int32_t val)
{
const lv_style_t * start = &dsc->style_start;
const lv_style_t * end = &dsc->style_end;
lv_style_t * act = dsc->style_anim;
lv_style_mix(start, end, act, val);
lv_obj_report_style_mod(dsc->style_anim);
}
/**
* Called when a style animation is ready
* It called the user defined call back and free the allocated memories
* @param ptr the 'animated variable' set by lv_style_anim_create()
*/
static void style_animation_common_end_cb(void * ptr)
{
lv_style_anim_dsc_t * dsc = ptr; /*To avoid casting*/
if(dsc->end_cb) dsc->end_cb(dsc);
lv_mem_free(dsc);
}
#endif

View file

@ -0,0 +1,198 @@
/**
* @file lv_style.h
*
*/
#ifndef LV_STYLE_H
#define LV_STYLE_H
#ifdef __cplusplus
extern "C" {
#endif
/*********************
* INCLUDES
*********************/
#include "../lv_misc/lv_color.h"
#include "../lv_misc/lv_area.h"
#include "../lv_misc/lv_font.h"
#include "../lv_misc/lv_anim.h"
/*********************
* DEFINES
*********************/
#define LV_RADIUS_CIRCLE (LV_COORD_MAX) /*A very big radius to always draw as circle*/
/**********************
* TYPEDEFS
**********************/
/*Border types (Use 'OR'ed values)*/
enum
{
LV_BORDER_NONE = 0x00,
LV_BORDER_BOTTOM = 0x01,
LV_BORDER_TOP = 0x02,
LV_BORDER_LEFT = 0x04,
LV_BORDER_RIGHT = 0x08,
LV_BORDER_FULL = 0x0F,
LV_BORDER_INTERNAL = 0x10, /*FOR matrix-like objects (e.g. Button matrix)*/
};
typedef uint8_t lv_border_part_t;
/*Shadow types*/
enum
{
LV_SHADOW_BOTTOM = 0,
LV_SHADOW_FULL,
};
typedef uint8_t lv_shadow_type_t;
typedef struct
{
uint8_t glass :1; /*1: Do not inherit this style*/
struct {
lv_color_t main_color;
lv_color_t grad_color; /*`grad_color` will be removed in v6.0, use `aux_color` instead*/
lv_coord_t radius;
lv_opa_t opa;
struct {
lv_color_t color;
lv_coord_t width;
lv_border_part_t part;
lv_opa_t opa;
} border;
struct {
lv_color_t color;
lv_coord_t width;
lv_shadow_type_t type;
} shadow;
struct {
lv_coord_t ver;
lv_coord_t hor;
lv_coord_t inner;
} padding;
uint8_t empty :1; /*Transparent background (border still drawn)*/
} body;
struct {
lv_color_t color;
const lv_font_t * font;
lv_coord_t letter_space;
lv_coord_t line_space;
lv_opa_t opa;
} text;
struct {
lv_color_t color;
lv_opa_t intense;
lv_opa_t opa;
} image;
struct {
lv_color_t color;
lv_coord_t width;
lv_opa_t opa;
uint8_t rounded :1; /*1: rounded line endings*/
} line;
} lv_style_t;
#if USE_LV_ANIMATION
typedef struct {
const lv_style_t * style_start; /*Pointer to the starting style*/
const lv_style_t * style_end; /*Pointer to the destination style*/
lv_style_t * style_anim; /*Pointer to a style to animate*/
lv_anim_cb_t end_cb; /*Call it when the animation is ready (NULL if unused)*/
int16_t time; /*Animation time in ms*/
int16_t act_time; /*Current time in animation. Set to negative to make delay.*/
uint16_t playback_pause; /*Wait before play back*/
uint16_t repeat_pause; /*Wait before repeat*/
uint8_t playback :1; /*When the animation is ready play it back*/
uint8_t repeat :1; /*Repeat the animation infinitely*/
} lv_style_anim_t;
/* Example initialization
lv_style_anim_t a;
a.style_anim = &style_to_anim;
a.style_start = &style_1;
a.style_end = &style_2;
a.act_time = 0;
a.time = 1000;
a.playback = 0;
a.playback_pause = 0;
a.repeat = 0;
a.repeat_pause = 0;
a.end_cb = NULL;
lv_style_anim_create(&a);
*/
#endif
/**********************
* GLOBAL PROTOTYPES
**********************/
/**
* Init the basic styles
*/
void lv_style_init (void);
/**
* Copy a style to an other
* @param dest pointer to the destination style
* @param src pointer to the source style
*/
void lv_style_copy(lv_style_t * dest, const lv_style_t * src);
/**
* Mix two styles according to a given ratio
* @param start start style
* @param end end style
* @param res store the result style here
* @param ratio the ratio of mix [0..256]; 0: `start` style; 256: `end` style
*/
void lv_style_mix(const lv_style_t * start, const lv_style_t * end, lv_style_t * res, uint16_t ratio);
#if USE_LV_ANIMATION
/**
* Create an animation from a pre-configured 'lv_style_anim_t' variable
* @param anim pointer to a pre-configured 'lv_style_anim_t' variable (will be copied)
* @return pointer to a descriptor. Really this variable will be animated. (Can be used in `lv_anim_del(dsc, NULL)`)
*/
void * lv_style_anim_create(lv_style_anim_t * anim);
#endif
/*************************
* GLOBAL VARIABLES
*************************/
extern lv_style_t lv_style_scr;
extern lv_style_t lv_style_transp;
extern lv_style_t lv_style_transp_fit;
extern lv_style_t lv_style_transp_tight;
extern lv_style_t lv_style_plain;
extern lv_style_t lv_style_plain_color;
extern lv_style_t lv_style_pretty;
extern lv_style_t lv_style_pretty_color;
extern lv_style_t lv_style_btn_rel;
extern lv_style_t lv_style_btn_pr;
extern lv_style_t lv_style_btn_tgl_rel;
extern lv_style_t lv_style_btn_tgl_pr;
extern lv_style_t lv_style_btn_ina;
/**********************
* MACROS
**********************/
#ifdef __cplusplus
} /* extern "C" */
#endif
#endif /*LV_STYLE_H*/

View file

@ -0,0 +1,207 @@
/**
* @file lv_vdb.c
*
*/
/*********************
* INCLUDES
*********************/
#include "lv_vdb.h"
#if LV_VDB_SIZE != 0
#include "../lv_hal/lv_hal_disp.h"
#include "../lv_misc/lv_log.h"
#include <stddef.h>
/*********************
* DEFINES
*********************/
#ifndef LV_ATTRIBUTE_FLUSH_READY
#define LV_ATTRIBUTE_FLUSH_READY
#endif
#ifndef LV_ATTRIBUTE_MEM_ALIGN
#define LV_ATTRIBUTE_MEM_ALIGN
#endif
/**********************
* TYPEDEFS
**********************/
/**********************
* STATIC PROTOTYPES
**********************/
/**********************
* STATIC VARIABLES
**********************/
/*Simple VDB*/
#if LV_VDB_DOUBLE == 0
# if LV_VDB_ADR == 0
/*If the buffer address is not specified simply allocate it*/
static LV_ATTRIBUTE_MEM_ALIGN uint8_t vdb_buf[LV_VDB_SIZE_IN_BYTES];
static lv_vdb_t vdb = {.buf = (lv_color_t *)vdb_buf};
# else /*LV_VDB_ADR != 0*/
/*If the buffer address is specified use that address*/
static lv_vdb_t vdb = {.buf = (lv_color_t *)LV_VDB_ADR};
# endif
/*LV_VDB_DOUBLE != 0*/
#else
/*Double VDB*/
static uint8_t vdb_active = 0;
# if LV_VDB_ADR == 0
/*If the buffer address is not specified simply allocate it*/
static LV_ATTRIBUTE_MEM_ALIGN uint8_t vdb_buf1[LV_VDB_SIZE_IN_BYTES];
static LV_ATTRIBUTE_MEM_ALIGN uint8_t vdb_buf2[LV_VDB_SIZE_IN_BYTES];
static lv_vdb_t vdb[2] = {{.buf = (lv_color_t *) vdb_buf1}, {.buf = (lv_color_t *) vdb_buf2}};
# else /*LV_VDB_ADR != 0*/
/*If the buffer address is specified use that address*/
static lv_vdb_t vdb[2] = {{.buf = (lv_color_t *)LV_VDB_ADR}, {.buf = (lv_color_t *)LV_VDB2_ADR}};
# endif
#endif
static volatile bool vdb_flushing = false;
/**********************
* MACROS
**********************/
/**********************
* GLOBAL FUNCTIONS
**********************/
/**
* Get the 'vdb' variable or allocate one in LV_VDB_DOUBLE mode
* @return pointer to a 'vdb' variable
*/
lv_vdb_t * lv_vdb_get(void)
{
#if LV_VDB_DOUBLE == 0
/* Wait until VDB is flushing.
* (Until this user calls of 'lv_flush_ready()' in the display drivers's flush function*/
while(vdb_flushing);
return &vdb;
#else
/*If already there is an active do nothing*/
return &vdb[vdb_active];
#endif
}
/**
* Flush the content of the VDB
*/
void lv_vdb_flush(void)
{
lv_vdb_t * vdb_act = lv_vdb_get();
if(!vdb_act) {
LV_LOG_WARN("Invalid VDB pointer");
return;
}
/*Don't start a new flush while the previous is not finished*/
#if LV_VDB_DOUBLE
while(vdb_flushing);
#endif /*LV_VDB_DOUBLE*/
vdb_flushing = true;
/*Flush the rendered content to the display*/
lv_disp_flush(vdb_act->area.x1, vdb_act->area.y1, vdb_act->area.x2, vdb_act->area.y2, vdb_act->buf);
#if LV_VDB_DOUBLE
/*Make the other VDB active. The content of the current will be kept until the next flush*/
vdb_active++;
vdb_active &= 0x1;
/*If the screen is transparent initialize it when the new VDB is selected*/
# if LV_COLOR_SCREEN_TRANSP
memset(vdb[vdb_active].buf, 0x00, LV_VDB_SIZE_IN_BYTES);
# endif /*LV_COLOR_SCREEN_TRANSP*/
#endif /*#if LV_VDB_DOUBLE*/
}
/**
* Set the address of VDB buffer(s) manually. To use this set `LV_VDB_ADR` (and `LV_VDB2_ADR`) to `LV_VDB_ADR_INV` in `lv_conf.h`.
* It should be called before `lv_init()`. The size of the buffer should be: `LV_VDB_SIZE_IN_BYTES`
* @param buf1 address of the VDB.
* @param buf2 address of the second buffer. `NULL` if `LV_VDB_DOUBLE 0`
*/
void lv_vdb_set_adr(void * buf1, void * buf2)
{
#if LV_VDB_DOUBLE == 0
(void) buf2; /*unused*/
vdb.buf = buf1;
#else
vdb[0].buf = buf1;
vdb[1].buf = buf2;
#endif
}
/**
* Call in the display driver's 'disp_flush' function when the flushing is finished
*/
LV_ATTRIBUTE_FLUSH_READY void lv_flush_ready(void)
{
vdb_flushing = false;
/*If the screen is transparent initialize it when the flushing is ready*/
#if LV_VDB_DOUBLE == 0 && LV_COLOR_SCREEN_TRANSP
memset(vdb_buf, 0x00, LV_VDB_SIZE_IN_BYTES);
#endif
}
/**
* Get currently active VDB, where the drawing happens. Used with `LV_VDB_DOUBLE 1`
* @return pointer to the active VDB. If `LV_VDB_DOUBLE 0` give the single VDB
*/
lv_vdb_t * lv_vdb_get_active(void)
{
#if LV_VDB_DOUBLE == 0
return &vdb;
#else
return &vdb[vdb_active];
#endif
}
/**
* Get currently inactive VDB, which is being displayed or being flushed. Used with `LV_VDB_DOUBLE 1`
* @return pointer to the inactive VDB. If `LV_VDB_DOUBLE 0` give the single VDB
*/
lv_vdb_t * lv_vdb_get_inactive(void)
{
#if LV_VDB_DOUBLE == 0
return &vdb;
#else
return &vdb[(vdb_active + 1) & 0x1];
#endif
}
/**
* Whether the flushing is in progress or not
* @return true: flushing is in progress; false: flushing ready
*/
bool lv_vdb_is_flushing(void)
{
return vdb_flushing;
}
/**********************
* STATIC FUNCTIONS
**********************/
#else
/**
* Just for compatibility
*/
void lv_flush_ready(void)
{
/*Do nothing. It is used only for VDB*/
}
#endif

View file

@ -0,0 +1,119 @@
/**
* @file lv_vdb.h
*
*/
#ifndef LV_VDB_H
#define LV_VDB_H
#ifdef __cplusplus
extern "C" {
#endif
/*********************
* INCLUDES
*********************/
#ifdef LV_CONF_INCLUDE_SIMPLE
#include "lv_conf.h"
#else
#include "../../lv_conf.h"
#endif
#if LV_VDB_SIZE != 0
#include "../lv_misc/lv_color.h"
#include "../lv_misc/lv_area.h"
/*********************
* DEFINES
*********************/
/*Can be used in `lv_conf.h` the set an invalid address for the VDB. It should be replaced later by a valid address using `lv_vdb_set_adr()`*/
#define LV_VDB_ADR_INV 8 /*8 is still too small to be valid but it's aligned on 64 bit machines as well*/
#ifndef LV_VDB_PX_BPP
#define LV_VDB_PX_BPP LV_COLOR_SIZE /* Default is LV_COLOR_SIZE */
#endif
#if LV_VDB_TRUE_DOUBLE_BUFFERED && (LV_VDB_SIZE != LV_HOR_RES * LV_VER_RES || LV_VDB_DOUBLE == 0)
#error "With LV_VDB_TRUE_DOUBLE_BUFFERED: (LV_VDB_SIZE = LV_HOR_RES * LV_VER_RES and LV_VDB_DOUBLE = 1 is required"
#endif
/* The size of VDB in bytes.
* (LV_VDB_SIZE * LV_VDB_PX_BPP) >> 3): just divide by 8 to convert bits to bytes
* (((LV_VDB_SIZE * LV_VDB_PX_BPP) & 0x7) ? 1 : 0): add an extra byte to round up.
* E.g. if LV_VDB_SIZE = 10 and LV_VDB_PX_BPP = 1 -> 10 bits -> 2 bytes*/
#define LV_VDB_SIZE_IN_BYTES ((LV_VDB_SIZE * LV_VDB_PX_BPP) >> 3) + (((LV_VDB_SIZE * LV_VDB_PX_BPP) & 0x7) ? 1 : 0)
/**********************
* TYPEDEFS
**********************/
typedef struct
{
lv_area_t area;
lv_color_t *buf;
} lv_vdb_t;
/**********************
* GLOBAL PROTOTYPES
**********************/
/**
* Get the 'vdb' variable or allocate one in LV_VDB_DOUBLE mode
* @return pointer to a 'vdb' variable
*/
lv_vdb_t * lv_vdb_get(void);
/**
* Flush the content of the vdb
*/
void lv_vdb_flush(void);
/**
* Set the address of VDB buffer(s) manually. To use this set `LV_VDB_ADR` (and `LV_VDB2_ADR`) to `LV_VDB_ADR_INV` in `lv_conf.h`.
* It should be called before `lv_init()`. The size of the buffer should be: `LV_VDB_SIZE_IN_BYTES`
* @param buf1 address of the VDB.
* @param buf2 address of the second buffer. `NULL` if `LV_VDB_DOUBLE 0`
*/
void lv_vdb_set_adr(void * buf1, void * buf2);
/**
* Call in the display driver's 'disp_flush' function when the flushing is finished
*/
void lv_flush_ready(void);
/**
* Get currently active VDB, where the drawing happens. Used with `LV_VDB_DOUBLE 1`
* @return pointer to the active VDB. If `LV_VDB_DOUBLE 0` give the single VDB
*/
lv_vdb_t * lv_vdb_get_active(void);
/**
* Get currently inactive VDB, which is being displayed or being flushed. Used with `LV_VDB_DOUBLE 1`
* @return pointer to the inactive VDB. If `LV_VDB_DOUBLE 0` give the single VDB
*/
lv_vdb_t * lv_vdb_get_inactive(void);
/**
* Whether the flushing is in progress or not
* @return true: flushing is in progress; false: flushing ready
*/
bool lv_vdb_is_flushing(void);
/**********************
* MACROS
**********************/
#else /*LV_VDB_SIZE != 0*/
/*Just for compatibility*/
void lv_flush_ready(void);
#endif
#ifdef __cplusplus
} /* extern "C" */
#endif
#endif /*LV_VDB_H*/