Messaging¶
Messaging (lv_msg
) is a classic []publisher subscriber](https://en.wikipedia.org/wiki/Publish%E2%80%93subscribe_pattern) implementation for LVGL.
IDs¶
Both the publishers and the subscribers needs to know the message identifiers.
In lv_msg
these are simple uint32_t
integers. For example:
#define MSG_DOOR_OPENED 1
#define MSG_DOOR_CLOSED 2
#define MSG_USER_NAME_CHANGED 100
#define MSG_USER_AVATAR_CHANGED 101
You can orgnaize 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.
Send message¶
Messages can be sent with lv_msg_send(msg_id, payload)
. E.g.
lv_msg_send(MSG_USER_DOOR_OPENED, NULL);
lv_msg_send(MSG_USER_NAME_CHANGED, "John Smith");
Subscribe to a message¶
lv_msg_subscribe(msg_id, callback, user_data)
can be used to subscribe to message.
The callback should look like this:
static void user_name_subscriber_cb(void * s, lv_msg_t * m)
{
/*s: a subscriber obeject, can be used to unscubscribe*/
/*m: a message object with the msg_id, payload, and user_data (set durung subscription)*/
...do something...
}
From lv_msg_t
the followings can be used to get some data:
lv_msg_get_id(m)
lv_msg_get_payload(m)
lv_msg_get_user_data(m)
Subscribe with an lv_obj¶
It's quite typical that an LVGL widget is interested in some messages.
To make it simpler lv_msg_subsribe_obj(msg_id, obj, user_data)
can be used.
If a new message is published with msg_id
an LV_EVENT_MSG_RECEIVED
event will be sent to the object.
For example:
lv_obj_add_event_cb(user_name_label, user_name_label_event_cb, LV_EVENT_MSG_RECEIVED, NULL);
lv_msg_subsribe_obj(MSG_USER_NAME_CHANGED, user_name_label, NULL);
...
void user_name_label_event_cb(lv_event_t * e)
{
lv_obj_t * label = lv_event_get_target(e);
lv_msg_t * m = lv_event_get_msg(e);
lv_label_set_text(label, lv_msg_get_payload(m));
}
Unsubscribe¶
lv_msg_subscribe
returns a pointer which can be used to unsubscribe:
void * s1;
s1 = lv_msg_subscribe(MSG_USER_DOOR_OPENED, some_callback, NULL);
...
lv_msg_unsubscribe(s1);
Example¶
Slider to label messaging¶
C code
GitHub#include "../../lv_examples.h"
#if LV_USE_MSG && LV_USE_SLIDER && LV_USE_LABEL && LV_BUILD_EXAMPLES
/*Define a message ID*/
#define MSG_NEW_TEMPERATURE 1
static void slider_event_cb(lv_event_t * e);
static void label_event_cb(lv_event_t * e);
/**
* A slider sends a message on value change and a label display's that value
*/
void lv_example_msg_1(void)
{
/*Create a slider in the center of the display*/
lv_obj_t * slider = lv_slider_create(lv_scr_act());
lv_obj_center(slider);
lv_obj_add_event_cb(slider, slider_event_cb, LV_EVENT_VALUE_CHANGED, NULL);
/*Create a label below the slider*/
lv_obj_t * label = lv_label_create(lv_scr_act());
lv_obj_add_event_cb(label, label_event_cb, LV_EVENT_MSG_RECEIVED, NULL);
lv_label_set_text(label, "0%");
lv_obj_align(label, LV_ALIGN_CENTER, 0, 30);
/*Subscribe the label to a message. Also use the user_data to set a format string here.*/
lv_msg_subsribe_obj(MSG_NEW_TEMPERATURE, label, "%d °C");
}
static void slider_event_cb(lv_event_t * e)
{
/*Notify all subscribers (only the label now) that the slider value has been changed*/
lv_obj_t * slider = lv_event_get_target(e);
int32_t v = lv_slider_get_value(slider);
lv_msg_send(MSG_NEW_TEMPERATURE, &v);
}
static void label_event_cb(lv_event_t * e)
{
lv_obj_t * label = lv_event_get_target(e);
lv_msg_t * m = lv_event_get_msg(e);
const char * fmt = lv_msg_get_user_data(m);
const int32_t * v = lv_msg_get_payload(m);
lv_label_set_text_fmt(label, fmt, *v);
}
#endif
Error encountered while trying to open /home/runner/work/lvgl/lvgl/examples/others/msg/lv_example_msg_1.py
Handling login and its states¶
C code
GitHub#include "../../lv_examples.h"
#if LV_USE_MSG && LV_USE_SLIDER && LV_USE_LABEL && LV_BUILD_EXAMPLES
/*Define a message ID*/
#define MSG_LOGIN_ATTEMPT 1
#define MSG_LOG_OUT 2
#define MSG_LOGIN_ERROR 3
#define MSG_LOGIN_OK 4
static void auth_manager(void * s, lv_msg_t * m);
static void textarea_event_cb(lv_event_t * e);
static void log_out_event_cb(lv_event_t * e);
static void start_engine_msg_event_cb(lv_event_t * e);
static void info_label_msg_event_cb(lv_event_t * e);
/**
* Simple PIN login screen.
* No global variables are used, all state changes are communicated via messages.
*/
void lv_example_msg_2(void)
{
lv_msg_subsribe(MSG_LOGIN_ATTEMPT, auth_manager, "hello");
/*Create a slider in the center of the display*/
lv_obj_t * ta = lv_textarea_create(lv_scr_act());
lv_obj_set_pos(ta, 10, 10);
lv_obj_set_width(ta, 200);
lv_textarea_set_one_line(ta, true);
lv_textarea_set_password_mode(ta, true);
lv_textarea_set_placeholder_text(ta, "The password is: hello");
lv_obj_add_event_cb(ta, textarea_event_cb, LV_EVENT_ALL, NULL);
lv_msg_subsribe_obj(MSG_LOGIN_ERROR, ta, NULL);
lv_msg_subsribe_obj(MSG_LOGIN_OK, ta, NULL);
lv_msg_subsribe_obj(MSG_LOG_OUT, ta, NULL);
lv_obj_t * kb = lv_keyboard_create(lv_scr_act());
lv_keyboard_set_textarea(kb, ta);
lv_obj_t * btn;
lv_obj_t * label;
/*Create a log out button which will be active only when logged in*/
btn = lv_btn_create(lv_scr_act());
lv_obj_set_pos(btn, 240, 10);
lv_obj_add_event_cb(btn, log_out_event_cb, LV_EVENT_ALL, NULL);
lv_msg_subsribe_obj(MSG_LOGIN_OK, btn, NULL);
lv_msg_subsribe_obj(MSG_LOG_OUT, btn, NULL);
label = lv_label_create(btn);
lv_label_set_text(label, "LOG OUT");
/*Create a label to show info*/
label = lv_label_create(lv_scr_act());
lv_label_set_text(label, "");
lv_obj_add_event_cb(label, info_label_msg_event_cb, LV_EVENT_MSG_RECEIVED, NULL);
lv_obj_set_pos(label, 10, 60);
lv_msg_subsribe_obj(MSG_LOGIN_ERROR, label, NULL);
lv_msg_subsribe_obj(MSG_LOGIN_OK, label, NULL);
lv_msg_subsribe_obj(MSG_LOG_OUT, label, NULL);
/*Create button which will be active only when logged in*/
btn = lv_btn_create(lv_scr_act());
lv_obj_set_pos(btn, 10, 80);
lv_obj_add_event_cb(btn, start_engine_msg_event_cb, LV_EVENT_MSG_RECEIVED, NULL);
lv_obj_add_flag(btn, LV_OBJ_FLAG_CHECKABLE);
lv_msg_subsribe_obj(MSG_LOGIN_OK, btn, NULL);
lv_msg_subsribe_obj(MSG_LOG_OUT, btn, NULL);
label = lv_label_create(btn);
lv_label_set_text(label, "START ENGINE");
lv_msg_send(MSG_LOG_OUT, NULL);
}
static void auth_manager(void * s, lv_msg_t * m)
{
LV_UNUSED(s);
const char * pin_act = lv_msg_get_payload(m);
const char * pin_expexted = lv_msg_get_user_data(m);
if(strcmp(pin_act, pin_expexted) == 0) {
lv_msg_send(MSG_LOGIN_OK, NULL);
}
else {
lv_msg_send(MSG_LOGIN_ERROR, "Incorrect PIN");
}
}
static void textarea_event_cb(lv_event_t * e)
{
lv_obj_t * ta = lv_event_get_target(e);
lv_event_code_t code = lv_event_get_code(e);
if(code == LV_EVENT_READY) {
lv_msg_send(MSG_LOGIN_ATTEMPT, lv_textarea_get_text(ta));
}
else if(code == LV_EVENT_MSG_RECEIVED) {
lv_msg_t * m = lv_event_get_msg(e);
switch(lv_msg_get_id(m)) {
case MSG_LOGIN_ERROR:
/*If there was an error, clean the text area*/
if(strlen(lv_msg_get_payload(m))) lv_textarea_set_text(ta, "");
break;
case MSG_LOGIN_OK:
lv_obj_add_state(ta, LV_STATE_DISABLED);
lv_obj_clear_state(ta, LV_STATE_FOCUSED | LV_STATE_FOCUS_KEY);
break;
case MSG_LOG_OUT:
lv_textarea_set_text(ta, "");
lv_obj_clear_state(ta, LV_STATE_DISABLED);
break;
}
}
}
static void log_out_event_cb(lv_event_t * e)
{
lv_event_code_t code = lv_event_get_code(e);
if(code == LV_EVENT_CLICKED) {
lv_msg_send(MSG_LOG_OUT, NULL);
}
else if(code == LV_EVENT_MSG_RECEIVED) {
lv_msg_t * m = lv_event_get_msg(e);
lv_obj_t * btn = lv_event_get_target(e);
switch(lv_msg_get_id(m)) {
case MSG_LOGIN_OK:
lv_obj_clear_state(btn, LV_STATE_DISABLED);
break;
case MSG_LOG_OUT:
lv_obj_add_state(btn, LV_STATE_DISABLED);
break;
}
}
}
static void start_engine_msg_event_cb(lv_event_t * e)
{
lv_msg_t * m = lv_event_get_msg(e);
lv_obj_t * btn = lv_event_get_target(e);
switch(lv_msg_get_id(m)) {
case MSG_LOGIN_OK:
lv_obj_clear_state(btn, LV_STATE_DISABLED);
break;
case MSG_LOG_OUT:
lv_obj_add_state(btn, LV_STATE_DISABLED);
break;
}
}
static void info_label_msg_event_cb(lv_event_t * e)
{
lv_obj_t * label = lv_event_get_target(e);
lv_msg_t * m = lv_event_get_msg(e);
switch(lv_msg_get_id(m)) {
case MSG_LOGIN_ERROR:
lv_label_set_text(label, lv_msg_get_payload(m));
lv_obj_set_style_text_color(label, lv_palette_main(LV_PALETTE_RED), 0);
break;
case MSG_LOGIN_OK:
lv_label_set_text(label, "Login successful");
lv_obj_set_style_text_color(label, lv_palette_main(LV_PALETTE_GREEN), 0);
break;
case MSG_LOG_OUT:
lv_label_set_text(label, "Logged out");
lv_obj_set_style_text_color(label, lv_palette_main(LV_PALETTE_GREY), 0);
break;
default:
break;
}
}
#endif
Error encountered while trying to open /home/runner/work/lvgl/lvgl/examples/others/msg/lv_example_msg_2.py
Setting the same value from many sources¶
C code
GitHub#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(void * s, lv_msg_t * m);
static void value_handler(void * s, lv_msg_t * m);
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);
/**
* Show how an increment button, a decrement button, as slider can set a value
* and a label display it.
* The current value (i.e. the system's state) is stored only in one static variable in a function
* and no global variables are required.
*/
void lv_example_msg_3(void)
{
lv_msg_subsribe(MSG_INC, value_handler, NULL);
lv_msg_subsribe(MSG_DEC, value_handler, NULL);
lv_msg_subsribe(MSG_SET, value_handler, NULL);
lv_msg_subsribe(MSG_UPDATE, value_handler, NULL);
lv_msg_subsribe(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);
lv_obj_set_flex_flow(panel, LV_FLEX_FLOW_ROW);
lv_obj_set_flex_align(panel, LV_FLEX_ALIGN_SPACE_BETWEEN, LV_FLEX_ALIGN_CENTER, LV_FLEX_ALIGN_START);
lv_obj_t * btn;
lv_obj_t * label;
/*Up button*/
btn = lv_btn_create(panel);
lv_obj_set_flex_grow(btn, 1);
lv_obj_add_event_cb(btn, btn_event_cb, LV_EVENT_ALL, NULL);
label = lv_label_create(btn);
lv_label_set_text(label, LV_SYMBOL_LEFT);
lv_obj_center(label);
/*Current value*/
label = lv_label_create(panel);
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_subsribe_obj(MSG_UPDATE, label, NULL);
lv_obj_add_event_cb(label, label_event_cb, LV_EVENT_MSG_RECEIVED, NULL);
/*Down button*/
btn = lv_btn_create(panel);
lv_obj_set_flex_grow(btn, 1);
lv_obj_add_event_cb(btn, btn_event_cb, LV_EVENT_ALL, NULL);
label = lv_label_create(btn);
lv_label_set_text(label, LV_SYMBOL_RIGHT);
lv_obj_center(label);
/*Slider*/
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_obj_add_event_cb(slider, slider_event_cb, LV_EVENT_ALL, NULL);
lv_msg_subsribe_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);
}
static void value_handler(void * s, lv_msg_t * m)
{
LV_UNUSED(s);
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);
}
}
static void btn_event_cb(lv_event_t * e)
{
lv_obj_t * btn = lv_event_get_target(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);
}
else {
lv_msg_send(MSG_INC, NULL);
}
}
}
static void label_event_cb(lv_event_t * e)
{
lv_obj_t * label = lv_event_get_target(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)
{
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);
}
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
Error encountered while trying to open /home/runner/work/lvgl/lvgl/examples/others/msg/lv_example_msg_3.py
API¶
Typedefs
-
typedef void (*lv_msg_request_cb_t)(void *r, uint32_t msg_id)¶
Functions
-
LV_EXPORT_CONST_INT(LV_MSG_ID_ANY)¶
-
void lv_msg_init(void)¶
Called internally to initialize the message module
-
void *lv_msg_subsribe(uint32_t msg_id, lv_msg_subscribe_cb_t cb, void *user_data)¶
Subscribe to an
msg_id
- Parameters
msg_id -- the message ID to listen to
cb -- callback to call if a message with
msg_id
was sentuser_data -- arbitrary data which will be available in
cb
too
- Returns
pointer to a "subscribe object". It can be used the unsubscribe.
-
void *lv_msg_subsribe_obj(uint32_t msg_id, lv_obj_t *obj, void *user_data)¶
Subscribe an
lv_obj
to a message.LV_EVENT_MSG_RECEIVED
will be triggered if a message with matching ID was sent- Parameters
msg_id -- the message ID to listen to
obj -- pointer to an
lv_obj
user_data -- arbitrary data which will be available in
cb
too
- Returns
pointer to a "subscribe object". It can be used the unsubscribe.
-
void lv_msg_unsubscribe(void *s)¶
Cancel a previous subscription
- Parameters
s -- pointer to a "subscibe object". Return value of
lv_msg_subsribe
orlv_msg_subsribe_obj
-
uint32_t lv_msg_unsubscribe_obj(uint32_t msg_id, lv_obj_t *obj)¶
Unsubscribe an object from a message ID
- Parameters
msg_id -- the message ID to unsubcribe from or
LV_MSG_ID_ANY
for any message IDobj -- the object to unsubscribe or NULL for any object
- Returns
number of unsubscriptions
-
void lv_msg_send(uint32_t msg_id, const void *payload)¶
Send a message with a given ID and payload
- Parameters
msg_id -- ID of the message to send
data -- pointer to the data to send
-
uint32_t lv_msg_get_id(lv_msg_t *m)¶
Get the ID of a message object. Typically used in the subscriber callback.
- Parameters
m -- pointer to a message object
- Returns
the ID of the message
-
const void *lv_msg_get_payload(lv_msg_t *m)¶
Get the payload of a message object. Typically used in the subscriber callback.
- Parameters
m -- pointer to a message object
- Returns
the payload of the message
-
void *lv_msg_get_user_data(lv_msg_t *m)¶
Get the user data of a message object. Typically used in the subscriber callback.
- Parameters
m -- pointer to a message object
- Returns
the user data of the message
-
lv_msg_t *lv_event_get_msg(lv_event_t *e)¶
Get the message object from an event object. Can be used in
LV_EVENT_MSG_RECEIVED
events.- Parameters
e -- pointer to an event object
- Returns
the message object or NULL if called with unrelated event code.
-
static inline void *lv_msg_subscribe(uint32_t msg_id, lv_msg_subscribe_cb_t cb, void *user_data)¶
Variables
-
lv_event_code_t LV_EVENT_MSG_RECEIVED¶
-
struct lv_msg_t¶