feat(msg): add publisher-subscriber messaging
This commit is contained in:
@@ -14,5 +14,6 @@
|
|||||||
monkey
|
monkey
|
||||||
gridnav
|
gridnav
|
||||||
fragment
|
fragment
|
||||||
|
msg
|
||||||
```
|
```
|
||||||
|
|
||||||
|
|||||||
101
docs/others/msg.md
Normal file
101
docs/others/msg.md
Normal file
@@ -0,0 +1,101 @@
|
|||||||
|
```eval_rst
|
||||||
|
.. include:: /header.rst
|
||||||
|
:github_url: |github_link_base|/others/msg.md
|
||||||
|
```
|
||||||
|
# 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:
|
||||||
|
```c
|
||||||
|
#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.
|
||||||
|
```c
|
||||||
|
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:
|
||||||
|
```c
|
||||||
|
|
||||||
|
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:
|
||||||
|
```c
|
||||||
|
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:
|
||||||
|
```c
|
||||||
|
void * s1;
|
||||||
|
s1 = lv_msg_subscribe(MSG_USER_DOOR_OPENED, some_callback, NULL);
|
||||||
|
|
||||||
|
...
|
||||||
|
|
||||||
|
lv_msg_unsubscribe(s1);
|
||||||
|
```
|
||||||
|
|
||||||
|
## Example
|
||||||
|
|
||||||
|
```eval_rst
|
||||||
|
|
||||||
|
.. include:: ../../examples/others/msg/index.rst
|
||||||
|
|
||||||
|
```
|
||||||
|
## API
|
||||||
|
|
||||||
|
|
||||||
|
```eval_rst
|
||||||
|
|
||||||
|
.. doxygenfile:: lv_msg.h
|
||||||
|
:project: lvgl
|
||||||
|
|
||||||
|
```
|
||||||
20
examples/others/msg/index.rst
Normal file
20
examples/others/msg/index.rst
Normal file
@@ -0,0 +1,20 @@
|
|||||||
|
|
||||||
|
Slider to label messaging
|
||||||
|
"""""""""""""""""""""""""
|
||||||
|
|
||||||
|
.. lv_example:: others/msg/lv_example_msg_1
|
||||||
|
:language: c
|
||||||
|
|
||||||
|
Handling login and its states
|
||||||
|
"""""""""""""""""""""""""""""
|
||||||
|
|
||||||
|
.. lv_example:: others/msg/lv_example_msg_2
|
||||||
|
:language: c
|
||||||
|
|
||||||
|
Setting the same value from many sources
|
||||||
|
""""""""""""""""""""""""""""""""""""""""
|
||||||
|
|
||||||
|
.. lv_example:: others/msg/lv_example_msg_3
|
||||||
|
:language: c
|
||||||
|
|
||||||
|
|
||||||
40
examples/others/msg/lv_example_msg.h
Normal file
40
examples/others/msg/lv_example_msg.h
Normal file
@@ -0,0 +1,40 @@
|
|||||||
|
/**
|
||||||
|
* @file lv_example_msg.h
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef LV_EXAMPLE_MSG_H
|
||||||
|
#define LV_EXAMPLE_MSG_H
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
extern "C" {
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/*********************
|
||||||
|
* INCLUDES
|
||||||
|
*********************/
|
||||||
|
|
||||||
|
/*********************
|
||||||
|
* DEFINES
|
||||||
|
*********************/
|
||||||
|
|
||||||
|
/**********************
|
||||||
|
* TYPEDEFS
|
||||||
|
**********************/
|
||||||
|
|
||||||
|
/**********************
|
||||||
|
* GLOBAL PROTOTYPES
|
||||||
|
**********************/
|
||||||
|
void lv_example_msg_1(void);
|
||||||
|
void lv_example_msg_2(void);
|
||||||
|
void lv_example_msg_3(void);
|
||||||
|
|
||||||
|
/**********************
|
||||||
|
* MACROS
|
||||||
|
**********************/
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
} /*extern "C"*/
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#endif /*LV_EXAMPLE_MSG_H*/
|
||||||
49
examples/others/msg/lv_example_msg_1.c
Normal file
49
examples/others/msg/lv_example_msg_1.c
Normal file
@@ -0,0 +1,49 @@
|
|||||||
|
#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
|
||||||
168
examples/others/msg/lv_example_msg_2.c
Normal file
168
examples/others/msg/lv_example_msg_2.c
Normal file
@@ -0,0 +1,168 @@
|
|||||||
|
#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
|
||||||
152
examples/others/msg/lv_example_msg_3.c
Normal file
152
examples/others/msg/lv_example_msg_3.c
Normal file
@@ -0,0 +1,152 @@
|
|||||||
|
#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
|
||||||
@@ -663,9 +663,12 @@
|
|||||||
/*1: Enable lv_obj fragment*/
|
/*1: Enable lv_obj fragment*/
|
||||||
#define LV_USE_FRAGMENT 0
|
#define LV_USE_FRAGMENT 0
|
||||||
|
|
||||||
/*draw img in label or span obj */
|
/*1: Support using images as font in label or span widgets */
|
||||||
#define LV_USE_IMGFONT 0
|
#define LV_USE_IMGFONT 0
|
||||||
|
|
||||||
|
/*1: Enable a published subscriber based messaging system */
|
||||||
|
#define LV_USE_MSG 0
|
||||||
|
|
||||||
/*==================
|
/*==================
|
||||||
* EXAMPLES
|
* EXAMPLES
|
||||||
*==================*/
|
*==================*/
|
||||||
|
|||||||
@@ -42,6 +42,10 @@ void lv_extra_init(void)
|
|||||||
lv_grid_init();
|
lv_grid_init();
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
#if LV_USE_MSG
|
||||||
|
lv_msg_init();
|
||||||
|
#endif
|
||||||
|
|
||||||
#if LV_USE_FS_FATFS != '\0'
|
#if LV_USE_FS_FATFS != '\0'
|
||||||
lv_fs_fatfs_init();
|
lv_fs_fatfs_init();
|
||||||
#endif
|
#endif
|
||||||
|
|||||||
@@ -18,6 +18,7 @@ extern "C" {
|
|||||||
#include "gridnav/lv_gridnav.h"
|
#include "gridnav/lv_gridnav.h"
|
||||||
#include "fragment/lv_fragment.h"
|
#include "fragment/lv_fragment.h"
|
||||||
#include "imgfont/lv_imgfont.h"
|
#include "imgfont/lv_imgfont.h"
|
||||||
|
#include "msg/lv_msg.h"
|
||||||
|
|
||||||
/*********************
|
/*********************
|
||||||
* DEFINES
|
* DEFINES
|
||||||
|
|||||||
179
src/extra/others/msg/lv_msg.c
Normal file
179
src/extra/others/msg/lv_msg.c
Normal file
@@ -0,0 +1,179 @@
|
|||||||
|
/**
|
||||||
|
* @file lv_msg.c
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
/*********************
|
||||||
|
* INCLUDES
|
||||||
|
*********************/
|
||||||
|
#include "lv_msg.h"
|
||||||
|
#if LV_USE_MSG
|
||||||
|
|
||||||
|
#include "../../../misc/lv_assert.h"
|
||||||
|
#include "../../../misc/lv_ll.h"
|
||||||
|
|
||||||
|
/*********************
|
||||||
|
* DEFINES
|
||||||
|
*********************/
|
||||||
|
|
||||||
|
/**********************
|
||||||
|
* TYPEDEFS
|
||||||
|
**********************/
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
uint32_t msg_id;
|
||||||
|
lv_msg_subscribe_cb_t callback;
|
||||||
|
void * user_data;
|
||||||
|
void * _priv_data; /*Internal: used only store 'obj' in lv_obj_subscribe*/
|
||||||
|
} sub_dsc_t;
|
||||||
|
|
||||||
|
/**********************
|
||||||
|
* STATIC PROTOTYPES
|
||||||
|
**********************/
|
||||||
|
|
||||||
|
static void notify(lv_msg_t * m);
|
||||||
|
static void obj_notify_cb(void * s, lv_msg_t * m);
|
||||||
|
static void obj_delete_event_cb(lv_event_t * e);
|
||||||
|
|
||||||
|
/**********************
|
||||||
|
* STATIC VARIABLES
|
||||||
|
**********************/
|
||||||
|
static lv_ll_t subs_ll;
|
||||||
|
static lv_ll_t reqs_ll;
|
||||||
|
|
||||||
|
/**********************
|
||||||
|
* GLOBAL VARIABLES
|
||||||
|
**********************/
|
||||||
|
lv_event_code_t LV_EVENT_MSG_RECEIVED;
|
||||||
|
|
||||||
|
/**********************
|
||||||
|
* MACROS
|
||||||
|
**********************/
|
||||||
|
|
||||||
|
/**********************
|
||||||
|
* GLOBAL FUNCTIONS
|
||||||
|
**********************/
|
||||||
|
|
||||||
|
void lv_msg_init(void)
|
||||||
|
{
|
||||||
|
LV_EVENT_MSG_RECEIVED = lv_event_register_id();
|
||||||
|
_lv_ll_init(&subs_ll, sizeof(sub_dsc_t));
|
||||||
|
}
|
||||||
|
|
||||||
|
void * lv_msg_subsribe(uint32_t msg_id, lv_msg_subscribe_cb_t cb, void * user_data)
|
||||||
|
{
|
||||||
|
sub_dsc_t * s = _lv_ll_ins_tail(&subs_ll);
|
||||||
|
LV_ASSERT_MALLOC(s);
|
||||||
|
if(s == NULL) return NULL;
|
||||||
|
|
||||||
|
lv_memset_00(s, sizeof(*s));
|
||||||
|
|
||||||
|
s->msg_id = msg_id;
|
||||||
|
s->callback = cb;
|
||||||
|
s->user_data = user_data;
|
||||||
|
return s;
|
||||||
|
}
|
||||||
|
|
||||||
|
void * lv_msg_subsribe_obj(uint32_t msg_id, lv_obj_t * obj, void * user_data)
|
||||||
|
{
|
||||||
|
sub_dsc_t * s = lv_msg_subsribe(msg_id, obj_notify_cb, user_data);
|
||||||
|
if(s == NULL) return NULL;
|
||||||
|
s->_priv_data = obj;
|
||||||
|
|
||||||
|
/*If not added yet, add a delete event cb which automatically unsubcribes the object*/
|
||||||
|
sub_dsc_t * s_first = lv_obj_get_event_user_data(obj, obj_delete_event_cb);
|
||||||
|
if(s_first == NULL) {
|
||||||
|
lv_obj_add_event_cb(obj, obj_delete_event_cb, LV_EVENT_DELETE, s);
|
||||||
|
}
|
||||||
|
return s;
|
||||||
|
}
|
||||||
|
|
||||||
|
void lv_msg_unsubscribe(void * s)
|
||||||
|
{
|
||||||
|
LV_ASSERT_NULL(s);
|
||||||
|
_lv_ll_remove(&subs_ll, s);
|
||||||
|
lv_mem_free(s);
|
||||||
|
}
|
||||||
|
|
||||||
|
uint32_t lv_msg_id_range(uint32_t start, uint32_t end)
|
||||||
|
{
|
||||||
|
if(start == end) return start;
|
||||||
|
else return start + (end << 16);
|
||||||
|
}
|
||||||
|
|
||||||
|
void lv_msg_send(uint32_t msg_id, const void * payload)
|
||||||
|
{
|
||||||
|
lv_msg_t m;
|
||||||
|
lv_memset_00(&m, sizeof(m));
|
||||||
|
m.id = msg_id;
|
||||||
|
m.payload = payload;
|
||||||
|
notify(&m);
|
||||||
|
}
|
||||||
|
|
||||||
|
uint32_t lv_msg_get_id(lv_msg_t * m)
|
||||||
|
{
|
||||||
|
return m->id;
|
||||||
|
}
|
||||||
|
|
||||||
|
const void * lv_msg_get_payload(lv_msg_t * m)
|
||||||
|
{
|
||||||
|
return m->payload;
|
||||||
|
}
|
||||||
|
|
||||||
|
void * lv_msg_get_user_data(lv_msg_t * m)
|
||||||
|
{
|
||||||
|
return m->user_data;
|
||||||
|
}
|
||||||
|
|
||||||
|
lv_msg_t * lv_event_get_msg(lv_event_t * e)
|
||||||
|
{
|
||||||
|
if(e->code == LV_EVENT_MSG_RECEIVED) {
|
||||||
|
return lv_event_get_param(e);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
LV_LOG_WARN("Not interpreted with this event code");
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/**********************
|
||||||
|
* STATIC FUNCTIONS
|
||||||
|
**********************/
|
||||||
|
|
||||||
|
static void notify(lv_msg_t * m)
|
||||||
|
{
|
||||||
|
sub_dsc_t * s;
|
||||||
|
_LV_LL_READ(&subs_ll, s) {
|
||||||
|
if(s->msg_id == m->id && s->callback) {
|
||||||
|
m->user_data = s->user_data;
|
||||||
|
m->_priv_data = s->_priv_data;
|
||||||
|
s->callback(s, m);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void obj_notify_cb(void * s, lv_msg_t * m)
|
||||||
|
{
|
||||||
|
LV_UNUSED(s);
|
||||||
|
lv_event_send(m->_priv_data, LV_EVENT_MSG_RECEIVED, m);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void obj_delete_event_cb(lv_event_t * e)
|
||||||
|
{
|
||||||
|
lv_obj_t * obj = lv_event_get_target(e);
|
||||||
|
|
||||||
|
sub_dsc_t * s = _lv_ll_get_head(&subs_ll);
|
||||||
|
sub_dsc_t * s_next;
|
||||||
|
while(s) {
|
||||||
|
/*On unsubscribe the list changes s becomes invalid so get next item while it's surely valid*/
|
||||||
|
s_next = _lv_ll_get_next(&subs_ll, s);
|
||||||
|
if(s->_priv_data == obj) {
|
||||||
|
lv_msg_unsubscribe(s);
|
||||||
|
}
|
||||||
|
s = s_next;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif /*LV_USE_MSG*/
|
||||||
124
src/extra/others/msg/lv_msg.h
Normal file
124
src/extra/others/msg/lv_msg.h
Normal file
@@ -0,0 +1,124 @@
|
|||||||
|
/**
|
||||||
|
* @file lv_msg.h
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef LV_MSG_H
|
||||||
|
#define LV_MSG_H
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
extern "C" {
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/*********************
|
||||||
|
* INCLUDES
|
||||||
|
*********************/
|
||||||
|
#include "../../../core/lv_obj.h"
|
||||||
|
#if LV_USE_MSG
|
||||||
|
|
||||||
|
/*********************
|
||||||
|
* DEFINES
|
||||||
|
*********************/
|
||||||
|
|
||||||
|
/**********************
|
||||||
|
* TYPEDEFS
|
||||||
|
**********************/
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
uint32_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*/
|
||||||
|
} lv_msg_t;
|
||||||
|
|
||||||
|
typedef void (*lv_msg_subscribe_cb_t)(void * s, lv_msg_t * msg);
|
||||||
|
|
||||||
|
typedef void (*lv_msg_request_cb_t)(void * r, uint32_t msg_id);
|
||||||
|
|
||||||
|
/**********************
|
||||||
|
* GLOBAL PROTOTYPES
|
||||||
|
**********************/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Called internally to initialize the message module
|
||||||
|
*/
|
||||||
|
void lv_msg_init(void);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Subscribe to an `msg_id`
|
||||||
|
* @param msg_id the message ID to listen to
|
||||||
|
* @param cb callback to call if a message with `msg_id` was sent
|
||||||
|
* @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_subsribe(uint32_t msg_id, lv_msg_subscribe_cb_t cb, 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
|
||||||
|
* @param msg_id the message ID to listen to
|
||||||
|
* @param obj pointer to an `lv_obj`
|
||||||
|
* @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_subsribe_obj(uint32_t msg_id, lv_obj_t * obj, void * user_data);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Cancel a previous subscription
|
||||||
|
* @param s pointer to a "subscibe object".
|
||||||
|
* Return value of `lv_msg_subsribe` or `lv_msg_subsribe_obj`
|
||||||
|
*/
|
||||||
|
void lv_msg_unsubscribe(void * s);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Send a message with a given ID and payload
|
||||||
|
* @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);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 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);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the payload of a message object. Typically used in the subscriber callback.
|
||||||
|
* @param m pointer to a message object
|
||||||
|
* @return the payload of the message
|
||||||
|
*/
|
||||||
|
const void * lv_msg_get_payload(lv_msg_t * m);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the user data of a message object. Typically used in the subscriber callback.
|
||||||
|
* @param m pointer to a message object
|
||||||
|
* @return the user data of the message
|
||||||
|
*/
|
||||||
|
void * lv_msg_get_user_data(lv_msg_t * m);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the message object from an event object. Can be used in `LV_EVENT_MSG_RECEIVED` events.
|
||||||
|
* @param e pointer to an event object
|
||||||
|
* @return the message object or NULL if called with unrelated event code.
|
||||||
|
*/
|
||||||
|
lv_msg_t * lv_event_get_msg(lv_event_t * e);
|
||||||
|
|
||||||
|
/**********************
|
||||||
|
* GLOBAL VARIABLES
|
||||||
|
**********************/
|
||||||
|
|
||||||
|
extern lv_event_code_t LV_EVENT_MSG_RECEIVED;
|
||||||
|
|
||||||
|
/**********************
|
||||||
|
* MACROS
|
||||||
|
**********************/
|
||||||
|
|
||||||
|
#endif /*LV_USE_MSG*/
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
} /*extern "C"*/
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#endif /*LV_MSG_H*/
|
||||||
@@ -2167,7 +2167,7 @@
|
|||||||
#endif
|
#endif
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
/*draw img in label or span obj */
|
/*1: Support using images as font in label or span widgets */
|
||||||
#ifndef LV_USE_IMGFONT
|
#ifndef LV_USE_IMGFONT
|
||||||
#ifdef CONFIG_LV_USE_IMGFONT
|
#ifdef CONFIG_LV_USE_IMGFONT
|
||||||
#define LV_USE_IMGFONT CONFIG_LV_USE_IMGFONT
|
#define LV_USE_IMGFONT CONFIG_LV_USE_IMGFONT
|
||||||
@@ -2176,6 +2176,15 @@
|
|||||||
#endif
|
#endif
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
/*1: Enable a published subscriber based messaging system */
|
||||||
|
#ifndef LV_USE_MSG
|
||||||
|
#ifdef CONFIG_LV_USE_MSG
|
||||||
|
#define LV_USE_MSG CONFIG_LV_USE_MSG
|
||||||
|
#else
|
||||||
|
#define LV_USE_MSG 0
|
||||||
|
#endif
|
||||||
|
#endif
|
||||||
|
|
||||||
/*==================
|
/*==================
|
||||||
* EXAMPLES
|
* EXAMPLES
|
||||||
*==================*/
|
*==================*/
|
||||||
|
|||||||
Reference in New Issue
Block a user