feat(msg): allow using variable address as msg_id

This commit is contained in:
Gabor Kiss-Vamosi
2022-08-23 22:29:13 +02:00
parent bdd520e00c
commit 327dbb6031
8 changed files with 167 additions and 96 deletions

View File

@@ -4,7 +4,7 @@ Messaging (`lv_msg`) is a classic [publisher subscriber](https://en.wikipedia.or
## IDs
Both the publishers and the subscribers needs to know the message identifiers.
In `lv_msg` these are simple `uint32_t` integers. For example:
In `lv_msg` these are simple integers. For example:
```c
#define MSG_DOOR_OPENED 1
#define MSG_DOOR_CLOSED 2
@@ -12,30 +12,38 @@ In `lv_msg` these are simple `uint32_t` integers. For example:
#define MSG_USER_AVATAR_CHANGED 101
```
You can orgnaize the message IDs as you wish.
You can organize the message IDs as you wish.
Both parties also need to know about the format of teh payload. E.g. in the above example
`MSG_DOOR_OPENED` and `MSG_DOOR_CLOSED` has no payload but `MSG_USER_NAME_CHANGED` can have a `const char *` payload containing the user name, and `MSG_USER_AVATAR_CHANGED` a `const void *` image source with the new avatar image.
Both parties also need to know about the format of the payload. E.g. in the above example
`MSG_DOOR_OPENED` and `MSG_DOOR_CLOSED` might have no payload but `MSG_USER_NAME_CHANGED` can have a `const char *` payload containing the user name, and `MSG_USER_AVATAR_CHANGED` a `const void *` image source with the new avatar image.
## Send message
Messages can be sent with `lv_msg_send(msg_id, payload)`. E.g.
To be more precise the message ID`s type is declared like this:
```c
lv_msg_send(MSG_USER_DOOR_OPENED, NULL);
lv_msg_send(MSG_USER_NAME_CHANGED, "John Smith");
typedef lv_uintptr_t lv_msg_id_t;
```
This way, if you a value in stored in a global variable (e.g. the current temperature) then the address of that variable can be used as message ID too by simply casting it to `lv_msg_id_t`. It saves the creation of message IDs manually as the variable itself serves as message ID too.
## Subscribe to a message
`lv_msg_subscribe(msg_id, callback, user_data)` can be used to subscribe to message.
Don't forget that `msg_id` can be a constant or a variable address too:
```c
lv_msg_subscribe(45, my_callback_1, NULL);
int v;
lv_msg_subscribe((lv_msg_id_t)&v, my_callback_2, NULL);
```
The callback should look like this:
```c
static void user_name_subscriber_cb(lv_msg_t * m)
{
/*m: a message object with the msg_id, payload, and user_data (set durung subscription)*/
/*m: a message object with the msg_id, payload, and user_data (set during subscription)*/
...do something...
}
@@ -67,6 +75,12 @@ void user_name_label_event_cb(lv_event_t * e)
```
Here `msg_id` also can be a variable's address:
```c
char name[64];
lv_msg_subsribe_obj(name, user_name_label, NULL);
```
### Unsubscribe
`lv_msg_subscribe` returns a pointer which can be used to unsubscribe:
```c
@@ -78,6 +92,16 @@ s1 = lv_msg_subscribe(MSG_USER_DOOR_OPENED, some_callback, NULL);
lv_msg_unsubscribe(s1);
```
## Send message
Messages can be sent with `lv_msg_send(msg_id, payload)`. E.g.
```c
lv_msg_send(MSG_USER_DOOR_OPENED, NULL);
lv_msg_send(MSG_USER_NAME_CHANGED, "John Smith");
```
To update a variable
## Example
```eval_rst

View File

@@ -1,18 +1,13 @@
#include "../../lv_examples.h"
#if LV_USE_MSG && LV_USE_SLIDER && LV_USE_LABEL && LV_BUILD_EXAMPLES
/*Define a message ID*/
#define MSG_INC 1
#define MSG_DEC 2
#define MSG_SET 3
#define MSG_UPDATE 4
#define MSG_UPDATE_REQUEST 5
static void value_handler(lv_msg_t * m);
static int32_t limit_value(int32_t v);
static void btn_event_cb(lv_event_t * e);
static void label_event_cb(lv_event_t * e);
static void slider_event_cb(lv_event_t * e);
static int32_t power_value;
/**
* Show how an increment button, a decrement button, as slider can set a value
* and a label display it.
@@ -22,12 +17,6 @@ static void slider_event_cb(lv_event_t * e);
void lv_example_msg_3(void)
{
lv_msg_subscribe(MSG_INC, value_handler, NULL);
lv_msg_subscribe(MSG_DEC, value_handler, NULL);
lv_msg_subscribe(MSG_SET, value_handler, NULL);
lv_msg_subscribe(MSG_UPDATE, value_handler, NULL);
lv_msg_subscribe(MSG_UPDATE_REQUEST, value_handler, NULL);
lv_obj_t * panel = lv_obj_create(lv_scr_act());
lv_obj_set_size(panel, 250, LV_SIZE_CONTENT);
lv_obj_center(panel);
@@ -50,7 +39,7 @@ void lv_example_msg_3(void)
lv_obj_set_flex_grow(label, 2);
lv_obj_set_style_text_align(label, LV_TEXT_ALIGN_CENTER, 0);
lv_label_set_text(label, "?");
lv_msg_subscribe_obj(MSG_UPDATE, label, NULL);
lv_msg_subscribe_obj((lv_msg_id_t)&power_value, label, NULL);
lv_obj_add_event_cb(label, label_event_cb, LV_EVENT_MSG_RECEIVED, NULL);
/*Down button*/
@@ -65,42 +54,16 @@ void lv_example_msg_3(void)
lv_obj_t * slider = lv_slider_create(panel);
lv_obj_set_flex_grow(slider, 1);
lv_obj_add_flag(slider, LV_OBJ_FLAG_FLEX_IN_NEW_TRACK);
lv_msg_subscribe_obj((lv_msg_id_t)&power_value, slider, NULL);
lv_obj_add_event_cb(slider, slider_event_cb, LV_EVENT_ALL, NULL);
lv_msg_subscribe_obj(MSG_UPDATE, slider, NULL);
/* As there are new UI elements that don't know the system's state
* send an UPDATE REQUEST message which will trigger an UPDATE message with the current value*/
lv_msg_send(MSG_UPDATE_REQUEST, NULL);
power_value = 30;
lv_msg_update_value(&power_value);
}
static void value_handler(lv_msg_t * m)
static int32_t limit_value(int32_t v)
{
static int32_t value = 10;
int32_t old_value = value;
switch(lv_msg_get_id(m)) {
case MSG_INC:
if(value < 100) value++;
break;
case MSG_DEC:
if(value > 0) value--;
break;
case MSG_SET: {
const int32_t * new_value = lv_msg_get_payload(m);
value = *new_value;
}
break;
case MSG_UPDATE_REQUEST:
lv_msg_send(MSG_UPDATE, &value);
break;
default:
break;
}
if(value != old_value) {
lv_msg_send(MSG_UPDATE, &value);
}
return LV_CLAMP(30, v, 80);
}
@@ -110,10 +73,12 @@ static void btn_event_cb(lv_event_t * e)
lv_event_code_t code = lv_event_get_code(e);
if(code == LV_EVENT_CLICKED || code == LV_EVENT_LONG_PRESSED_REPEAT) {
if(lv_obj_get_index(btn) == 0) { /*First object is the dec. button*/
lv_msg_send(MSG_DEC, NULL);
power_value = limit_value(power_value - 1);
lv_msg_update_value(&power_value);
}
else {
lv_msg_send(MSG_INC, NULL);
power_value = limit_value(power_value + 1);
lv_msg_update_value(&power_value);
}
}
}
@@ -124,10 +89,8 @@ static void label_event_cb(lv_event_t * e)
lv_event_code_t code = lv_event_get_code(e);
if(code == LV_EVENT_MSG_RECEIVED) {
lv_msg_t * m = lv_event_get_msg(e);
if(lv_msg_get_id(m) == MSG_UPDATE) {
const int32_t * v = lv_msg_get_payload(m);
lv_label_set_text_fmt(label, "%d %%", *v);
}
const int32_t * v = lv_msg_get_payload(m);
lv_label_set_text_fmt(label, "%d %%", *v);
}
}
@@ -136,15 +99,13 @@ static void slider_event_cb(lv_event_t * e)
lv_obj_t * slider = lv_event_get_target(e);
lv_event_code_t code = lv_event_get_code(e);
if(code == LV_EVENT_VALUE_CHANGED) {
int32_t v = lv_slider_get_value(slider);
lv_msg_send(MSG_SET, &v);
power_value = limit_value(lv_slider_get_value(slider));
lv_msg_update_value(&power_value);
}
else if(code == LV_EVENT_MSG_RECEIVED) {
lv_msg_t * m = lv_event_get_msg(e);
if(lv_msg_get_id(m) == MSG_UPDATE) {
const int32_t * v = lv_msg_get_payload(m);
lv_slider_set_value(slider, *v, LV_ANIM_OFF);
}
const int32_t * v = lv_msg_get_payload(m);
lv_slider_set_value(slider, *v, LV_ANIM_OFF);
}
}

View File

@@ -232,7 +232,6 @@ lv_color_t lv_disp_get_chroma_key_color(lv_disp_t * disp)
*/
void lv_scr_load_anim(lv_obj_t * new_scr, lv_scr_load_anim_t anim_type, uint32_t time, uint32_t delay, bool auto_del)
{
lv_disp_t * d = lv_obj_get_disp(new_scr);
lv_obj_t * act_scr = lv_scr_act();
@@ -273,7 +272,9 @@ void lv_scr_load_anim(lv_obj_t * new_scr, lv_scr_load_anim_t anim_type, uint32_t
/*Shortcut for immediate load*/
if(time == 0 && delay == 0) {
scr_load_internal(new_scr);
if(auto_del) lv_obj_del(act_scr);
return;
}

View File

@@ -309,7 +309,7 @@
#if LV_USE_DRAW_SW
/*Enable complex draw engine.
*Required to draw shadow, gradient, rounded corners, circles, arc, skew lines, image transformations or any masks*/
*Required to draw shadow, gradient, rounded corners, circles, arc, skew lines, image transformations or any masks*/
#ifndef LV_DRAW_SW_COMPLEX
#ifdef _LV_KCONFIG_PRESENT
#ifdef CONFIG_LV_DRAW_SW_COMPLEX
@@ -323,9 +323,9 @@
#endif
/* If a widget has `style_opa < 255` (not `bg_opa`, `text_opa` etc) or not NORMAL blend mode
* it is buffered into a "simple" layer before rendering. The widget can be buffered in smaller chunks.
* "Transformed layers" (if `transform_angle/zoom` are set) use larger buffers
* and can't be drawn in chunks. */
* it is buffered into a "simple" layer before rendering. The widget can be buffered in smaller chunks.
* "Transformed layers" (if `transform_angle/zoom` are set) use larger buffers
* and can't be drawn in chunks. */
/*The target buffer size for simple layer chunks.*/
#ifndef LV_DRAW_SW_LAYER_SIMPLE_BUF_SIZE
@@ -369,10 +369,10 @@
#endif
/*Default gradient buffer size.
*When LVGL calculates the gradient "maps" it can save them into a cache to avoid calculating them again.
*LV_DRAW_SW_GRADIENT_CACHE_DEF_SIZE sets the size of this cache in bytes.
*If the cache is too small the map will be allocated only while it's required for the drawing.
*0 mean no caching.*/
*When LVGL calculates the gradient "maps" it can save them into a cache to avoid calculating them again.
*LV_DRAW_SW_GRADIENT_CACHE_DEF_SIZE sets the size of this cache in bytes.
*If the cache is too small the map will be allocated only while it's required for the drawing.
*0 mean no caching.*/
#ifndef LV_DRAW_SW_GRADIENT_CACHE_DEF_SIZE
#ifdef CONFIG_LV_DRAW_SW_GRADIENT_CACHE_DEF_SIZE
#define LV_DRAW_SW_GRADIENT_CACHE_DEF_SIZE CONFIG_LV_DRAW_SW_GRADIENT_CACHE_DEF_SIZE
@@ -382,8 +382,8 @@
#endif
/*Allow dithering the gradients (to achieve visual smooth color gradients on limited color depth display)
*LV_DRAW_SW_GRADIENT_DITHER implies allocating one or two more lines of the object's rendering surface
*The increase in memory consumption is (32 bits * object width) plus 24 bits * object width if using error diffusion */
*LV_DRAW_SW_GRADIENT_DITHER implies allocating one or two more lines of the object's rendering surface
*The increase in memory consumption is (32 bits * object width) plus 24 bits * object width if using error diffusion */
#ifndef LV_DRAW_SW_GRADIENT_DITHER
#ifdef CONFIG_LV_DRAW_SW_GRADIENT_DITHER
#define LV_DRAW_SW_GRADIENT_DITHER CONFIG_LV_DRAW_SW_GRADIENT_DITHER
@@ -393,8 +393,8 @@
#endif
#if LV_DRAW_SW_GRADIENT_DITHER
/*Add support for error diffusion dithering.
*Error diffusion dithering gets a much better visual result, but implies more CPU consumption and memory when drawing.
*The increase in memory consumption is (24 bits * object's width)*/
*Error diffusion dithering gets a much better visual result, but implies more CPU consumption and memory when drawing.
*The increase in memory consumption is (24 bits * object's width)*/
#ifndef LV_DRAW_SW_GRADIENT_DITHER_ERROR_DIFFUSION
#ifdef CONFIG_LV_DRAW_SW_GRADIENT_DITHER_ERROR_DIFFUSION
#define LV_DRAW_SW_GRADIENT_DITHER_ERROR_DIFFUSION CONFIG_LV_DRAW_SW_GRADIENT_DITHER_ERROR_DIFFUSION
@@ -585,7 +585,7 @@
#endif
/*1: Enable print timestamp;
*0: Disable print timestamp*/
*0: Disable print timestamp*/
#ifndef LV_LOG_USE_TIMESTAMP
#ifdef _LV_KCONFIG_PRESENT
#ifdef CONFIG_LV_LOG_USE_TIMESTAMP
@@ -2269,6 +2269,16 @@
#endif
#endif
/*1: Enable a database where values can be stored in a global place.
* Requires: lv_msg */
#ifndef LV_USE_DB
#ifdef CONFIG_LV_USE_DB
#define LV_USE_DB CONFIG_LV_USE_DB
#else
#define LV_USE_DB 0
#endif
#endif
/*1: Enable Pinyin input method*/
/*Requires: lv_keyboard*/
#ifndef LV_USE_IME_PINYIN

View File

@@ -22,7 +22,7 @@
**********************/
typedef struct {
uint32_t msg_id;
lv_msg_id_t msg_id;
lv_msg_subscribe_cb_t callback;
void * user_data;
void * _priv_data; /*Internal: used only store 'obj' in lv_obj_subscribe*/
@@ -57,7 +57,7 @@ void lv_msg_init(void)
_lv_ll_init(&LV_GC_ROOT(_subs_ll), sizeof(sub_dsc_t));
}
void * lv_msg_subscribe(uint32_t msg_id, lv_msg_subscribe_cb_t cb, void * user_data)
void * lv_msg_subscribe(lv_msg_id_t msg_id, lv_msg_subscribe_cb_t cb, void * user_data)
{
sub_dsc_t * s = _lv_ll_ins_tail(&LV_GC_ROOT(_subs_ll));
LV_ASSERT_MALLOC(s);
@@ -71,7 +71,7 @@ void * lv_msg_subscribe(uint32_t msg_id, lv_msg_subscribe_cb_t cb, void * user_d
return s;
}
void * lv_msg_subscribe_obj(uint32_t msg_id, lv_obj_t * obj, void * user_data)
void * lv_msg_subscribe_obj(lv_msg_id_t msg_id, lv_obj_t * obj, void * user_data)
{
sub_dsc_t * s = lv_msg_subscribe(msg_id, obj_notify_cb, user_data);
if(s == NULL) return NULL;
@@ -92,7 +92,7 @@ void lv_msg_unsubscribe(void * s)
lv_free(s);
}
void lv_msg_send(uint32_t msg_id, const void * payload)
void lv_msg_send(lv_msg_id_t msg_id, const void * payload)
{
lv_msg_t m;
lv_memzero(&m, sizeof(m));
@@ -101,7 +101,12 @@ void lv_msg_send(uint32_t msg_id, const void * payload)
notify(&m);
}
uint32_t lv_msg_get_id(lv_msg_t * m)
void lv_msg_update_value(void * v)
{
lv_msg_send((lv_msg_id_t)v, v);
}
lv_msg_id_t lv_msg_get_id(lv_msg_t * m)
{
return m->id;
}
@@ -127,8 +132,6 @@ lv_msg_t * lv_event_get_msg(lv_event_t * e)
}
}
/**********************
* STATIC FUNCTIONS
**********************/

View File

@@ -24,8 +24,10 @@ extern "C" {
* TYPEDEFS
**********************/
typedef lv_uintptr_t lv_msg_id_t;
typedef struct {
uint32_t id; /*Identifier of the message*/
lv_msg_id_t id; /*Identifier of the message*/
void * user_data; /*Set the the user_data set in `lv_msg_subscribe`*/
void * _priv_data; /*Used internally*/
const void * payload; /*Pointer to the data of the message*/
@@ -50,7 +52,7 @@ void lv_msg_init(void);
* @param user_data arbitrary data which will be available in `cb` too
* @return pointer to a "subscribe object". It can be used the unsubscribe.
*/
void * lv_msg_subscribe(uint32_t msg_id, lv_msg_subscribe_cb_t cb, void * user_data);
void * lv_msg_subscribe(lv_msg_id_t msg_id, lv_msg_subscribe_cb_t cb, void * user_data);
/**
* Subscribe an `lv_obj` to a message.
@@ -60,7 +62,7 @@ void * lv_msg_subscribe(uint32_t msg_id, lv_msg_subscribe_cb_t cb, void * user_d
* @param user_data arbitrary data which will be available in `cb` too
* @return pointer to a "subscribe object". It can be used the unsubscribe.
*/
void * lv_msg_subscribe_obj(uint32_t msg_id, lv_obj_t * obj, void * user_data);
void * lv_msg_subscribe_obj(lv_msg_id_t msg_id, lv_obj_t * obj, void * user_data);
/**
* Cancel a previous subscription
@@ -74,14 +76,24 @@ void lv_msg_unsubscribe(void * s);
* @param msg_id ID of the message to send
* @param data pointer to the data to send
*/
void lv_msg_send(uint32_t msg_id, const void * payload);
void lv_msg_send(lv_msg_id_t msg_id, const void * payload);
/**
* Send a message where the message ID is `v` (the value of the pointer)
* and the payload is `v`.
* It can be used to send unique messages when a variable changed.
* @param v pointer to a variable.
* @note to subscribe to a variable use `lv_msg_subscribe((lv_msg_id_t)v, msg_cb, user_data)`
* or `lv_msg_subscribe_obj((lv_msg_id_t)v, obj, user_data)`
*/
void lv_msg_update_value(void * v);
/**
* Get the ID of a message object. Typically used in the subscriber callback.
* @param m pointer to a message object
* @return the ID of the message
*/
uint32_t lv_msg_get_id(lv_msg_t * m);
lv_msg_id_t lv_msg_get_id(lv_msg_t * m);
/**
* Get the payload of a message object. Typically used in the subscriber callback.

View File

@@ -41,6 +41,7 @@ set(LVGL_TEST_OPTIONS_MINIMAL_MONOCHROME
-DLV_USE_BMP=1
-DLV_USE_GIF=1
-DLV_USE_QRCODE=1
-DLV_USE_MSG=1
)
set(LVGL_TEST_OPTIONS_NORMAL_8BIT
@@ -66,6 +67,7 @@ set(LVGL_TEST_OPTIONS_NORMAL_8BIT
-DLV_USE_SJPG=1
-DLV_USE_GIF=1
-DLV_USE_QRCODE=1
-DLV_USE_MSG=1
)
set(LVGL_TEST_OPTIONS_16BIT
@@ -92,6 +94,7 @@ set(LVGL_TEST_OPTIONS_16BIT
-DLV_USE_SJPG=1
-DLV_USE_GIF=1
-DLV_USE_QRCODE=1
-DLV_USE_MSG=1
)
set(LVGL_TEST_OPTIONS_16BIT_SWAP
@@ -120,6 +123,7 @@ set(LVGL_TEST_OPTIONS_16BIT_SWAP
-DLV_USE_SJPG=1
-DLV_USE_GIF=1
-DLV_USE_QRCODE=1
-DLV_USE_MSG=1
)
set(LVGL_TEST_OPTIONS_FULL_32BIT
@@ -189,8 +193,8 @@ set(LVGL_TEST_OPTIONS_FULL_32BIT
-DLV_USE_QRCODE=1
-DLV_USE_FRAGMENT=1
-DLV_USE_IMGFONT=1
-DLV_USE_MSG=1
-DLV_USE_IME_PINYIN=1
-DLV_USE_MSG=1
)
set(LVGL_TEST_OPTIONS_TEST_COMMON
@@ -235,6 +239,7 @@ set(LVGL_TEST_OPTIONS_TEST_COMMON
-DLV_USE_FS_POSIX=1
-DLV_FS_POSIX_LETTER='B'
-DLV_FS_POSIX_CACHE_SIZE=0
-DLV_USE_MSG=1
${LVGL_TEST_COMMON_EXAMPLE_OPTIONS}
-DLV_FONT_DEFAULT=&lv_font_montserrat_14
-Wno-unused-but-set-variable # unused variables are common in the dual-heap arrangement

View File

@@ -0,0 +1,55 @@
#if LV_BUILD_TEST
#include "../lvgl.h"
#include "unity/unity.h"
void setUp(void)
{
/* Function run before every test */
}
void tearDown(void)
{
/* Function run after every test */
}
static uint32_t value_received;
static void msg_cb(lv_msg_t * msg)
{
uint32_t * v = lv_msg_get_payload(msg);
value_received = *v;
}
static void event_cb(lv_event_t * e)
{
lv_msg_t * msg = lv_event_get_msg(e);
uint32_t * v = lv_msg_get_payload(msg);
lv_label_set_text_fmt(lv_event_get_target(e), "%d", *v);
}
void test_add_entry_and_send_msg(void)
{
static uint32_t value = 100;
lv_msg_subscribe((lv_msg_id_t)&value, msg_cb, NULL);
value_received = 0;
value = 100;
lv_msg_update_value(&value);
TEST_ASSERT_EQUAL_UINT32(100, value_received);
value = 200;
lv_msg_update_value(&value);
TEST_ASSERT_EQUAL_UINT32(200, value_received);
lv_obj_t * label = lv_label_create(lv_scr_act());
lv_msg_subscribe_obj((lv_msg_id_t)&value, label, NULL);
lv_obj_add_event_cb(label, event_cb, LV_EVENT_MSG_RECEIVED, NULL);
value = 300;
lv_msg_update_value(&value);
TEST_ASSERT_EQUAL_STRING("300", lv_label_get_text(label));
}
#endif