2015-08-12 10:53:41 +00:00
|
|
|
/**
|
|
|
|
* Class definition for a MicroBitMessageBus.
|
|
|
|
*
|
|
|
|
* The MicroBitMessageBus handles all messages passed between components.
|
|
|
|
*/
|
|
|
|
|
|
|
|
#include "MicroBit.h"
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Constructor.
|
|
|
|
* Create a new Message Bus.
|
|
|
|
*/
|
|
|
|
MicroBitMessageBus::MicroBitMessageBus()
|
|
|
|
{
|
|
|
|
this->listeners = NULL;
|
2015-08-19 22:35:45 +00:00
|
|
|
this->evt_queue_head = NULL;
|
|
|
|
this->evt_queue_tail = NULL;
|
2015-09-11 15:39:38 +00:00
|
|
|
this->nonce_val = 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Returns a 'nonce' for use with the NONCE_ID channel of the message bus.
|
|
|
|
*/
|
|
|
|
uint16_t MicroBitMessageBus::nonce()
|
|
|
|
{
|
|
|
|
// In the global scheme of things, a terrible nonce generator.
|
|
|
|
// However, for our purposes, this is simple and adequate for local use.
|
|
|
|
// This would be a bad idea if our events were networked though - can you think why?
|
|
|
|
return nonce_val++;
|
2015-08-12 10:53:41 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Invokes a callback on a given MicroBitListener
|
|
|
|
*
|
|
|
|
* Internal wrapper function, used to enable
|
2015-08-31 22:25:10 +00:00
|
|
|
* parameterised callbacks through the fiber scheduler.
|
2015-08-12 10:53:41 +00:00
|
|
|
*/
|
|
|
|
void async_callback(void *param)
|
|
|
|
{
|
|
|
|
MicroBitListener *listener = (MicroBitListener *)param;
|
2015-09-09 23:04:27 +00:00
|
|
|
|
2015-09-11 15:39:38 +00:00
|
|
|
// OK, now we need to decide how to behave depending on our configuration.
|
|
|
|
// If this a fiber f already active within this listener then check our
|
|
|
|
// configuration to determine the correct course of action.
|
|
|
|
//
|
|
|
|
|
|
|
|
if (listener->flags & MESSAGE_BUS_LISTENER_BUSY)
|
|
|
|
{
|
|
|
|
// Drop this event, if that's how we've been configured.
|
|
|
|
if (listener->flags & MESSAGE_BUS_LISTENER_DROP_IF_BUSY)
|
|
|
|
return;
|
|
|
|
|
|
|
|
// Queue this event up for later, if that's how we've been configured.
|
|
|
|
if (listener->flags & MESSAGE_BUS_LISTENER_QUEUE_IF_BUSY)
|
|
|
|
{
|
|
|
|
listener->queue(listener->evt);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2015-09-09 23:04:27 +00:00
|
|
|
// Determine the calling convention for the callback, and invoke...
|
|
|
|
// C++ is really bad at this! Especially as the ARM compiler is yet to support C++ 11 :-/
|
|
|
|
|
2015-09-11 15:39:38 +00:00
|
|
|
// Record that we have a fiber going into this listener...
|
|
|
|
listener->flags |= MESSAGE_BUS_LISTENER_BUSY;
|
2015-09-09 23:04:27 +00:00
|
|
|
|
2015-09-11 15:39:38 +00:00
|
|
|
while (1)
|
|
|
|
{
|
|
|
|
// Firstly, check for a method callback into an object.
|
|
|
|
if (listener->flags & MESSAGE_BUS_LISTENER_METHOD)
|
|
|
|
listener->cb_method->fire(listener->evt);
|
|
|
|
|
|
|
|
// Now a parameterised C function
|
|
|
|
else if (listener->flags & MESSAGE_BUS_LISTENER_PARAMETERISED)
|
|
|
|
listener->cb_param(listener->evt, listener->cb_arg);
|
|
|
|
|
|
|
|
// We must have a plain C function
|
|
|
|
else
|
|
|
|
listener->cb(listener->evt);
|
|
|
|
|
|
|
|
// If there are more events to process, dequeue te next one and process it.
|
|
|
|
if ((listener->flags & MESSAGE_BUS_LISTENER_QUEUE_IF_BUSY) && listener->evt_queue)
|
|
|
|
{
|
|
|
|
MicroBitEventQueueItem *item = listener->evt_queue;
|
|
|
|
|
|
|
|
listener->evt = item->evt;
|
|
|
|
listener->evt_queue = listener->evt_queue->next;
|
|
|
|
delete item;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
// The fiber of exiting... clear our state.
|
|
|
|
listener->flags &= ~MESSAGE_BUS_LISTENER_BUSY;
|
2015-08-12 10:53:41 +00:00
|
|
|
}
|
|
|
|
|
2015-08-19 22:35:45 +00:00
|
|
|
|
|
|
|
/**
|
|
|
|
* Queue the given event for processing at a later time.
|
|
|
|
* Add the given event at the tail of our queue.
|
|
|
|
*
|
|
|
|
* @param The event to queue.
|
|
|
|
*/
|
|
|
|
void MicroBitMessageBus::queueEvent(MicroBitEvent &evt)
|
|
|
|
{
|
|
|
|
MicroBitEventQueueItem *item = new MicroBitEventQueueItem(evt);
|
|
|
|
|
|
|
|
__disable_irq();
|
|
|
|
|
|
|
|
if (evt_queue_tail == NULL)
|
|
|
|
evt_queue_head = evt_queue_tail = item;
|
|
|
|
else
|
|
|
|
evt_queue_tail->next = item;
|
|
|
|
|
|
|
|
__enable_irq();
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Extract the next event from the front of the event queue (if present).
|
|
|
|
* @return
|
|
|
|
*
|
|
|
|
* @param The event to queue.
|
|
|
|
*/
|
|
|
|
MicroBitEventQueueItem* MicroBitMessageBus::dequeueEvent()
|
|
|
|
{
|
|
|
|
MicroBitEventQueueItem *item = NULL;
|
|
|
|
|
|
|
|
__disable_irq();
|
|
|
|
|
|
|
|
if (evt_queue_head != NULL)
|
|
|
|
{
|
|
|
|
item = evt_queue_head;
|
|
|
|
evt_queue_head = item->next;
|
|
|
|
|
|
|
|
if (evt_queue_head == NULL)
|
|
|
|
evt_queue_tail = NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
__enable_irq();
|
|
|
|
|
|
|
|
return item;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Periodic callback from MicroBit.
|
|
|
|
* Process at least one event from the event queue, if it is not empty.
|
2015-08-31 22:25:10 +00:00
|
|
|
* We then continue processing events until something appears on the runqueue.
|
2015-08-19 22:35:45 +00:00
|
|
|
*/
|
|
|
|
void MicroBitMessageBus::idleTick()
|
|
|
|
{
|
|
|
|
MicroBitEventQueueItem *item = this->dequeueEvent();
|
|
|
|
|
2015-08-31 22:25:10 +00:00
|
|
|
// Whilst there are events to process and we have no useful other work to do, pull them off the queue and process them.
|
|
|
|
while (item)
|
2015-08-19 22:35:45 +00:00
|
|
|
{
|
|
|
|
// send the event.
|
2015-08-31 22:25:10 +00:00
|
|
|
this->process(item->evt);
|
2015-08-19 22:35:45 +00:00
|
|
|
|
|
|
|
// Free the queue item.
|
|
|
|
delete item;
|
|
|
|
|
2015-08-31 22:25:10 +00:00
|
|
|
// If we have created some useful work to do, we stop processing.
|
|
|
|
// This helps to minimise the number of blocked fibers we create at any point in time, therefore
|
|
|
|
// also reducing the RAM footprint.
|
|
|
|
if(!scheduler_runqueue_empty())
|
|
|
|
break;
|
|
|
|
|
2015-08-19 22:35:45 +00:00
|
|
|
// Pull the next event to process, if there is one.
|
|
|
|
item = this->dequeueEvent();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Indicates whether or not we have any background work to do.
|
|
|
|
* @ return 1 if there are any events waitingto be processed, 0 otherwise.
|
|
|
|
*/
|
|
|
|
int MicroBitMessageBus::isIdleCallbackNeeded()
|
|
|
|
{
|
|
|
|
return !(evt_queue_head == NULL);
|
|
|
|
}
|
|
|
|
|
2015-08-12 10:53:41 +00:00
|
|
|
/**
|
2015-08-31 22:25:10 +00:00
|
|
|
* Queues the given event to be sent to all registered recipients.
|
2015-08-12 10:53:41 +00:00
|
|
|
*
|
2015-08-31 22:25:10 +00:00
|
|
|
* @param The event to send.
|
2015-08-12 10:53:41 +00:00
|
|
|
*
|
2015-08-31 22:25:10 +00:00
|
|
|
* n.b. THIS IS NOW WRAPPED BY THE MicroBitEvent CLASS FOR CONVENIENCE...
|
2015-08-12 10:53:41 +00:00
|
|
|
*
|
|
|
|
* Example:
|
|
|
|
* @code
|
2015-09-12 10:34:16 +00:00
|
|
|
* MicroBitEvent evt(id,MICROBIT_BUTTON_EVT_DOWN,ticks,CREATE_ONLY);
|
2015-08-12 10:53:41 +00:00
|
|
|
* evt.fire();
|
2015-09-12 10:34:16 +00:00
|
|
|
*
|
2015-08-12 10:53:41 +00:00
|
|
|
* //OR YOU CAN DO THIS...
|
2015-08-31 22:25:10 +00:00
|
|
|
* MicroBitEvent evt(id,MICROBIT_BUTTON_EVT_DOWN);
|
2015-08-12 10:53:41 +00:00
|
|
|
* @endcode
|
|
|
|
*/
|
2015-08-31 22:25:10 +00:00
|
|
|
void MicroBitMessageBus::send(MicroBitEvent evt)
|
2015-08-12 10:53:41 +00:00
|
|
|
{
|
2015-08-31 22:25:10 +00:00
|
|
|
// We simply queue processing of the event until we're scheduled in normal thread context.
|
|
|
|
// We do this to avoid the possibility of executing event handler code in IRQ context, which may bring
|
|
|
|
// hidden race conditions to kids code. Queuing all events ensures causal ordering (total ordering in fact).
|
2015-08-12 10:53:41 +00:00
|
|
|
|
2015-08-31 22:25:10 +00:00
|
|
|
this->queueEvent(evt);
|
|
|
|
return;
|
|
|
|
}
|
2015-08-19 22:35:45 +00:00
|
|
|
|
2015-08-31 22:25:10 +00:00
|
|
|
/*
|
|
|
|
* Deliver the given event to all registered event handlers.
|
|
|
|
* Event handlers are called using the invoke() mechanism provided by the fier scheduler
|
|
|
|
* This will attempt to call the event handler directly, but spawn a fiber should that
|
|
|
|
* event handler attempt a blocking operation.
|
|
|
|
* @param evt The event to be delivered.
|
|
|
|
*/
|
|
|
|
void MicroBitMessageBus::process(MicroBitEvent evt)
|
|
|
|
{
|
|
|
|
MicroBitListener *l;
|
2015-08-19 22:35:45 +00:00
|
|
|
|
2015-08-31 22:25:10 +00:00
|
|
|
l = listeners;
|
2015-09-11 15:39:38 +00:00
|
|
|
while (l != NULL)
|
|
|
|
{
|
|
|
|
if((l->id == evt.source || l->id == MICROBIT_ID_ANY) && (l->value == evt.value || l->value == MICROBIT_EVT_ANY))
|
|
|
|
{
|
2015-08-12 10:53:41 +00:00
|
|
|
l->evt = evt;
|
2015-09-11 15:39:38 +00:00
|
|
|
|
|
|
|
// OK, if this handler has regisitered itself as non-blocking, we just execute it directly...
|
|
|
|
// This is normally only done for trusted system components.
|
|
|
|
// Otherwise, we invoke it in a 'fork on block' context, that will automatically create a fiber
|
|
|
|
// should the event handler attempt a blocking operation, but doesn't have the overhead
|
|
|
|
// of creating a fiber needlessly. (cool huh?)
|
2015-09-19 20:44:58 +00:00
|
|
|
if (l->flags & MESSAGE_BUS_LISTENER_NONBLOCKING || currentFiber->flags & MICROBIT_FIBER_FLAG_DO_NOT_PAGE)
|
2015-09-11 15:39:38 +00:00
|
|
|
async_callback(l);
|
|
|
|
else
|
|
|
|
invoke(async_callback, l);
|
2015-08-12 10:53:41 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
l = l->next;
|
|
|
|
}
|
|
|
|
|
2015-08-31 22:25:10 +00:00
|
|
|
// Finally, forward the event to any other internal subsystems that may be interested.
|
|
|
|
// We *could* do this through the message bus of course, but this saves additional RAM,
|
|
|
|
// and procssor time (as we know these are non-blocking calls).
|
|
|
|
|
2015-08-12 10:53:41 +00:00
|
|
|
// Wake up any fibers that are blocked on this event
|
|
|
|
if (uBit.flags & MICROBIT_FLAG_SCHEDULER_RUNNING)
|
|
|
|
scheduler_event(evt);
|
|
|
|
|
2015-08-31 22:25:10 +00:00
|
|
|
// See if this event needs to be propogated through our BLE interface
|
2015-08-12 10:53:41 +00:00
|
|
|
if (uBit.ble_event_service)
|
|
|
|
uBit.ble_event_service->onMicroBitEvent(evt);
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Register a listener function.
|
|
|
|
*
|
|
|
|
* @param id The source of messages to listen for. Events sent from any other IDs will be filtered.
|
|
|
|
* Use MICROBIT_ID_ANY to receive events from all components.
|
|
|
|
*
|
|
|
|
* @param value The value of messages to listen for. Events with any other values will be filtered.
|
|
|
|
* Use MICROBIT_VALUE_ANY to receive events of any value.
|
|
|
|
*
|
|
|
|
* @param hander The function to call when an event is received.
|
|
|
|
*
|
|
|
|
* Example:
|
|
|
|
* @code
|
|
|
|
* void onButtonBClick(MicroBitEvent evt)
|
|
|
|
* {
|
|
|
|
* //do something
|
|
|
|
* }
|
|
|
|
* uBit.MessageBus.listen(MICROBIT_ID_BUTTON_B, MICROBIT_BUTTON_EVT_CLICK, onButtonBClick); // call function when ever a click event is detected.
|
|
|
|
* @endcode
|
|
|
|
*/
|
|
|
|
|
2015-09-11 15:39:38 +00:00
|
|
|
void MicroBitMessageBus::listen(int id, int value, void (*handler)(MicroBitEvent), uint16_t flags)
|
2015-09-09 23:04:27 +00:00
|
|
|
{
|
|
|
|
if (handler == NULL)
|
|
|
|
return;
|
|
|
|
|
2015-09-11 15:39:38 +00:00
|
|
|
MicroBitListener *newListener = new MicroBitListener(id, value, handler, flags);
|
2015-08-12 10:53:41 +00:00
|
|
|
|
2015-09-09 23:04:27 +00:00
|
|
|
if(!add(newListener))
|
|
|
|
delete newListener;
|
2015-08-12 10:53:41 +00:00
|
|
|
}
|
|
|
|
|
2015-09-11 15:39:38 +00:00
|
|
|
void MicroBitMessageBus::listen(int id, int value, void (*handler)(MicroBitEvent, void*), void* arg, uint16_t flags)
|
2015-08-12 10:53:41 +00:00
|
|
|
{
|
|
|
|
if (handler == NULL)
|
|
|
|
return;
|
|
|
|
|
2015-09-11 15:39:38 +00:00
|
|
|
MicroBitListener *newListener = new MicroBitListener(id, value, handler, arg, flags);
|
2015-09-09 23:04:27 +00:00
|
|
|
|
|
|
|
if(!add(newListener))
|
|
|
|
delete newListener;
|
2015-09-10 11:53:39 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Unregister a listener function.
|
|
|
|
* Listners are identified by the Event ID, Event VALUE and handler registered using listen().
|
|
|
|
*
|
|
|
|
* @param id The Event ID used to register the listener.
|
|
|
|
* @param value The Event VALUE used to register the listener.
|
|
|
|
* @param handler The function used to register the listener.
|
|
|
|
*
|
|
|
|
*
|
|
|
|
* Example:
|
|
|
|
* @code
|
|
|
|
* void onButtonBClick()
|
|
|
|
* {
|
|
|
|
* //do something
|
|
|
|
* }
|
|
|
|
*
|
|
|
|
* uBit.MessageBus.ignore(MICROBIT_ID_BUTTON_B, MICROBIT_BUTTON_EVT_CLICK, onButtonBClick);
|
|
|
|
* @endcode
|
|
|
|
*/
|
|
|
|
void MicroBitMessageBus::ignore(int id, int value, void (*handler)(MicroBitEvent))
|
|
|
|
{
|
|
|
|
if (handler == NULL)
|
|
|
|
return;
|
|
|
|
|
|
|
|
MicroBitListener listener(id, value, handler);
|
|
|
|
remove(&listener);
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Unregister a listener function.
|
|
|
|
* Listners are identified by the Event ID, Event VALUE and handler registered using listen().
|
|
|
|
*
|
|
|
|
* @param id The Event ID used to register the listener.
|
|
|
|
* @param value The Event VALUE used to register the listener.
|
|
|
|
* @param handler The function used to register the listener.
|
|
|
|
*
|
|
|
|
*
|
|
|
|
* Example:
|
|
|
|
* @code
|
|
|
|
* void onButtonBClick(void *arg)
|
|
|
|
* {
|
|
|
|
* //do something
|
|
|
|
* }
|
|
|
|
*
|
|
|
|
* uBit.MessageBus.ignore(MICROBIT_ID_BUTTON_B, MICROBIT_BUTTON_EVT_CLICK, onButtonBClick);
|
|
|
|
* @endcode
|
|
|
|
*/
|
2015-09-16 19:08:09 +00:00
|
|
|
void MicroBitMessageBus::ignore(int id, int value, void (*handler)(MicroBitEvent, void*))
|
2015-09-10 11:53:39 +00:00
|
|
|
{
|
|
|
|
if (handler == NULL)
|
|
|
|
return;
|
|
|
|
|
2015-09-16 19:08:09 +00:00
|
|
|
// The remove function is not comparing the [arg] anyhow.
|
|
|
|
MicroBitListener listener(id, value, handler, NULL);
|
2015-09-10 11:53:39 +00:00
|
|
|
remove(&listener);
|
2015-09-09 23:04:27 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Add the given MicroBitListener to the list of event handlers, unconditionally.
|
|
|
|
* @param listener The MicroBitListener to validate.
|
|
|
|
* @return 1 if the listener is valid, 0 otherwise.
|
|
|
|
*/
|
|
|
|
int MicroBitMessageBus::add(MicroBitListener *newListener)
|
|
|
|
{
|
2015-08-12 10:53:41 +00:00
|
|
|
MicroBitListener *l, *p;
|
2015-09-09 23:04:27 +00:00
|
|
|
int methodCallback;
|
|
|
|
|
|
|
|
//handler can't be NULL!
|
|
|
|
if (newListener == NULL)
|
|
|
|
return 0;
|
|
|
|
|
2015-08-12 10:53:41 +00:00
|
|
|
|
|
|
|
l = listeners;
|
|
|
|
|
|
|
|
// Firstly, we treat a listener as an idempotent operation. Ensure we don't already have this handler
|
|
|
|
// registered in a that will already capture these events. If we do, silently ignore.
|
|
|
|
|
2015-09-09 23:04:27 +00:00
|
|
|
// We always check the ID, VALUE and CB_METHOD fields.
|
|
|
|
// If we have a callback to a method, check the cb_method class. Otherwise, the cb function point is sufficient.
|
|
|
|
while (l != NULL)
|
|
|
|
{
|
2015-09-10 11:53:39 +00:00
|
|
|
methodCallback = (newListener->flags & MESSAGE_BUS_LISTENER_METHOD) && (l->flags & MESSAGE_BUS_LISTENER_METHOD);
|
|
|
|
|
2015-09-09 23:04:27 +00:00
|
|
|
if (l->id == newListener->id && l->value == newListener->value && (methodCallback ? *l->cb_method == *newListener->cb_method : l->cb == newListener->cb))
|
|
|
|
return 0;
|
2015-08-12 10:53:41 +00:00
|
|
|
|
2015-09-09 23:04:27 +00:00
|
|
|
l = l->next;
|
|
|
|
}
|
2015-08-12 10:53:41 +00:00
|
|
|
|
2015-09-09 23:04:27 +00:00
|
|
|
// We have a valid, new event handler. Add it to the list.
|
|
|
|
// if listeners is null - we can automatically add this listener to the list at the beginning...
|
2015-08-12 10:53:41 +00:00
|
|
|
if (listeners == NULL)
|
|
|
|
{
|
|
|
|
listeners = newListener;
|
2015-09-09 23:04:27 +00:00
|
|
|
return 1;
|
2015-08-12 10:53:41 +00:00
|
|
|
}
|
|
|
|
|
2015-09-09 23:04:27 +00:00
|
|
|
// We maintain an ordered list of listeners.
|
|
|
|
// The chain is held stictly in increasing order of ID (first level), then value code (second level).
|
2015-08-12 10:53:41 +00:00
|
|
|
// Find the correct point in the chain for this event.
|
2015-09-09 23:04:27 +00:00
|
|
|
// Adding a listener is a rare occurance, so we just walk the list...
|
|
|
|
|
2015-08-12 10:53:41 +00:00
|
|
|
p = listeners;
|
|
|
|
l = listeners;
|
|
|
|
|
2015-09-09 23:04:27 +00:00
|
|
|
while (l != NULL && l->id < newListener->id)
|
2015-08-12 10:53:41 +00:00
|
|
|
{
|
|
|
|
p = l;
|
|
|
|
l = l->next;
|
|
|
|
}
|
|
|
|
|
2015-09-09 23:04:27 +00:00
|
|
|
while (l != NULL && l->id == newListener->id && l->value < newListener->value)
|
2015-08-12 10:53:41 +00:00
|
|
|
{
|
|
|
|
p = l;
|
|
|
|
l = l->next;
|
|
|
|
}
|
|
|
|
|
|
|
|
//add at front of list
|
2015-09-09 23:04:27 +00:00
|
|
|
if (p == listeners && (newListener->id < p->id || (p->id == newListener->id && p->value > newListener->value)))
|
2015-08-12 10:53:41 +00:00
|
|
|
{
|
|
|
|
newListener->next = p;
|
|
|
|
|
|
|
|
//this new listener is now the front!
|
|
|
|
listeners = newListener;
|
|
|
|
}
|
2015-09-09 23:04:27 +00:00
|
|
|
|
2015-08-12 10:53:41 +00:00
|
|
|
//add after p
|
|
|
|
else
|
|
|
|
{
|
|
|
|
newListener->next = p->next;
|
|
|
|
p->next = newListener;
|
|
|
|
}
|
2015-09-09 23:04:27 +00:00
|
|
|
|
|
|
|
return 1;
|
2015-08-12 10:53:41 +00:00
|
|
|
}
|
|
|
|
|
2015-09-10 11:53:39 +00:00
|
|
|
/**
|
|
|
|
* Remove a MicroBitListener from the list that matches the given listener.
|
|
|
|
* @param listener The MicroBitListener to validate.
|
|
|
|
* @return The number of listeners removed from the list.
|
|
|
|
*/
|
|
|
|
int MicroBitMessageBus::remove(MicroBitListener *listener)
|
|
|
|
{
|
|
|
|
MicroBitListener *l, *p;
|
|
|
|
int removed = 0;
|
|
|
|
|
|
|
|
//handler can't be NULL!
|
|
|
|
if (listener == NULL)
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
l = listeners;
|
|
|
|
p = NULL;
|
|
|
|
|
|
|
|
// Walk this list of event handlers. Delete any that match the given listener.
|
|
|
|
while (l != NULL)
|
|
|
|
{
|
|
|
|
if (l->id == listener->id && l->value == listener->value && ((listener->flags & MESSAGE_BUS_LISTENER_METHOD) == (l->flags & MESSAGE_BUS_LISTENER_METHOD)))
|
|
|
|
{
|
|
|
|
if(((listener->flags & MESSAGE_BUS_LISTENER_METHOD) && (*l->cb_method == *listener->cb_method)) ||
|
|
|
|
((!(listener->flags & MESSAGE_BUS_LISTENER_METHOD) && l->cb == listener->cb)))
|
|
|
|
{
|
|
|
|
// Found a match. Remove from the list.
|
|
|
|
if (p == NULL)
|
|
|
|
listeners = l->next;
|
|
|
|
else
|
|
|
|
p->next = l->next;
|
|
|
|
|
|
|
|
// delete the listener.
|
|
|
|
MicroBitListener *t = l;
|
|
|
|
l = l->next;
|
|
|
|
|
|
|
|
delete t;
|
|
|
|
removed++;
|
|
|
|
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
p = l;
|
|
|
|
l = l->next;
|
|
|
|
}
|
|
|
|
|
|
|
|
return removed;
|
|
|
|
}
|
|
|
|
|