diff --git a/docs/others/index.md b/docs/others/index.md index 78ce404774..a33305eacb 100644 --- a/docs/others/index.md +++ b/docs/others/index.md @@ -14,5 +14,6 @@ monkey gridnav fragment + msg ``` diff --git a/docs/others/msg.md b/docs/others/msg.md new file mode 100644 index 0000000000..1aa0e130e4 --- /dev/null +++ b/docs/others/msg.md @@ -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 + +``` diff --git a/examples/others/msg/index.rst b/examples/others/msg/index.rst new file mode 100644 index 0000000000..93d04dc52d --- /dev/null +++ b/examples/others/msg/index.rst @@ -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 + + diff --git a/examples/others/msg/lv_example_msg.h b/examples/others/msg/lv_example_msg.h new file mode 100644 index 0000000000..e7accc8df6 --- /dev/null +++ b/examples/others/msg/lv_example_msg.h @@ -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*/ diff --git a/examples/others/msg/lv_example_msg_1.c b/examples/others/msg/lv_example_msg_1.c new file mode 100644 index 0000000000..e345ab770a --- /dev/null +++ b/examples/others/msg/lv_example_msg_1.c @@ -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 diff --git a/examples/others/msg/lv_example_msg_2.c b/examples/others/msg/lv_example_msg_2.c new file mode 100644 index 0000000000..c829b607b8 --- /dev/null +++ b/examples/others/msg/lv_example_msg_2.c @@ -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 diff --git a/examples/others/msg/lv_example_msg_3.c b/examples/others/msg/lv_example_msg_3.c new file mode 100644 index 0000000000..266daf39bf --- /dev/null +++ b/examples/others/msg/lv_example_msg_3.c @@ -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 diff --git a/lv_conf_template.h b/lv_conf_template.h index 2a1401ee18..9fb9181f4b 100644 --- a/lv_conf_template.h +++ b/lv_conf_template.h @@ -663,9 +663,12 @@ /*1: Enable lv_obj fragment*/ #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 +/*1: Enable a published subscriber based messaging system */ +#define LV_USE_MSG 0 + /*================== * EXAMPLES *==================*/ diff --git a/src/extra/lv_extra.c b/src/extra/lv_extra.c index d789e86b06..0b5000240e 100644 --- a/src/extra/lv_extra.c +++ b/src/extra/lv_extra.c @@ -42,6 +42,10 @@ void lv_extra_init(void) lv_grid_init(); #endif +#if LV_USE_MSG + lv_msg_init(); +#endif + #if LV_USE_FS_FATFS != '\0' lv_fs_fatfs_init(); #endif diff --git a/src/extra/others/lv_others.h b/src/extra/others/lv_others.h index 0223c96c90..19afdde3de 100644 --- a/src/extra/others/lv_others.h +++ b/src/extra/others/lv_others.h @@ -18,6 +18,7 @@ extern "C" { #include "gridnav/lv_gridnav.h" #include "fragment/lv_fragment.h" #include "imgfont/lv_imgfont.h" +#include "msg/lv_msg.h" /********************* * DEFINES diff --git a/src/extra/others/msg/lv_msg.c b/src/extra/others/msg/lv_msg.c new file mode 100644 index 0000000000..10a1d50830 --- /dev/null +++ b/src/extra/others/msg/lv_msg.c @@ -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*/ diff --git a/src/extra/others/msg/lv_msg.h b/src/extra/others/msg/lv_msg.h new file mode 100644 index 0000000000..11a55b5a70 --- /dev/null +++ b/src/extra/others/msg/lv_msg.h @@ -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*/ diff --git a/src/lv_conf_internal.h b/src/lv_conf_internal.h index 26e51e7140..ddad1db4e8 100644 --- a/src/lv_conf_internal.h +++ b/src/lv_conf_internal.h @@ -2167,7 +2167,7 @@ #endif #endif -/*draw img in label or span obj */ +/*1: Support using images as font in label or span widgets */ #ifndef LV_USE_IMGFONT #ifdef CONFIG_LV_USE_IMGFONT #define LV_USE_IMGFONT CONFIG_LV_USE_IMGFONT @@ -2176,6 +2176,15 @@ #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 *==================*/