feat(msg): add publisher-subscriber messaging

This commit is contained in:
Gabor Kiss-Vamosi
2022-04-06 21:36:15 +02:00
parent 5977eeff3c
commit 79a29d749d
13 changed files with 853 additions and 2 deletions

View File

@@ -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

View File

@@ -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

View 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*/

View 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*/

View File

@@ -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
*==================*/