feat(msg): allow using variable address as msg_id
This commit is contained in:
@@ -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
|
||||
|
||||
@@ -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,11 +89,9 @@ 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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void slider_event_cb(lv_event_t * e)
|
||||
@@ -136,16 +99,14 @@ 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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
**********************/
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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
|
||||
|
||||
55
tests/src/test_cases/test_msg.c
Normal file
55
tests/src/test_cases/test_msg.c
Normal 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
|
||||
Reference in New Issue
Block a user