haiku/src/kits/app/Handler.cpp

872 lines
17 KiB
C++

/*
* Copyright 2001-2014 Haiku, Inc. All rights reserved.
* Distributed under the terms of the MIT License.
*
* Authors:
* Axel Dörfler, axeld@pinc-software.de
* Erik Jaesler, erik@cgsoftware.com
*/
#include <TokenSpace.h>
#include <AppDefs.h>
#include <Handler.h>
#include <Looper.h>
#include <Message.h>
#include <MessageFilter.h>
#include <Messenger.h>
#include <PropertyInfo.h>
#include <algorithm>
#include <new>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <vector>
using std::map;
using std::vector;
using BPrivate::gDefaultTokens;
static const char* kArchiveNameField = "_name";
static const uint32 kMsgStartObserving = '_OBS';
static const uint32 kMsgStopObserving = '_OBP';
static const char* kObserveTarget = "be:observe_target";
static property_info sHandlerPropInfo[] = {
{
"Suites", // name
{ B_GET_PROPERTY }, // commands
{ B_DIRECT_SPECIFIER }, // specifiers
NULL, // usage
0, // extra data
{ 0 }, // types
{ // ctypes (compound_type)
{ // ctypes[0]
{ // pairs[0]
{
"suites", // name
B_STRING_TYPE // type
}
}
},
{ // ctypes[1]
{ // pairs[0]
{
"messages",
B_PROPERTY_INFO_TYPE
}
}
}
},
{} // reserved
},
{
"Messenger",
{ B_GET_PROPERTY },
{ B_DIRECT_SPECIFIER },
NULL, 0,
{ B_MESSENGER_TYPE },
{},
{}
},
{
"InternalName",
{ B_GET_PROPERTY },
{ B_DIRECT_SPECIFIER },
NULL, 0,
{ B_STRING_TYPE },
{},
{}
},
{ 0 }
};
bool FilterDeleter(void* filter);
namespace BPrivate {
class ObserverList {
public:
ObserverList();
~ObserverList();
status_t SendNotices(uint32 what, const BMessage* notice);
status_t Add(const BHandler* handler, uint32 what);
status_t Add(const BMessenger& messenger, uint32 what);
status_t Remove(const BHandler* handler, uint32 what);
status_t Remove(const BMessenger& messenger, uint32 what);
bool IsEmpty();
private:
typedef map<uint32, vector<const BHandler*> > HandlerObserverMap;
typedef map<uint32, vector<BMessenger> > MessengerObserverMap;
void _ValidateHandlers(uint32 what);
void _SendNotices(uint32 what, BMessage* notice);
HandlerObserverMap fHandlerMap;
MessengerObserverMap fMessengerMap;
};
} // namespace BPrivate
using namespace BPrivate;
// #pragma mark -
BHandler::BHandler(const char* name)
: BArchivable(),
fName(NULL)
{
_InitData(name);
}
BHandler::~BHandler()
{
if (LockLooper()) {
BLooper* looper = Looper();
looper->RemoveHandler(this);
looper->Unlock();
}
// remove all filters
if (fFilters) {
int32 count = fFilters->CountItems();
for (int32 i = 0; i < count; i++)
delete (BMessageFilter*)fFilters->ItemAtFast(i);
delete fFilters;
}
// remove all observers (the observer list manages itself)
delete fObserverList;
// free rest
free(fName);
gDefaultTokens.RemoveToken(fToken);
}
BHandler::BHandler(BMessage* data)
: BArchivable(data),
fName(NULL)
{
const char* name = NULL;
if (data)
data->FindString(kArchiveNameField, &name);
_InitData(name);
}
BArchivable*
BHandler::Instantiate(BMessage* data)
{
if (!validate_instantiation(data, "BHandler"))
return NULL;
return new BHandler(data);
}
status_t
BHandler::Archive(BMessage* data, bool deep) const
{
status_t status = BArchivable::Archive(data, deep);
if (status < B_OK)
return status;
if (fName == NULL)
return B_OK;
return data->AddString(kArchiveNameField, fName);
}
void
BHandler::MessageReceived(BMessage* message)
{
BMessage reply(B_REPLY);
switch (message->what) {
case kMsgStartObserving:
case kMsgStopObserving:
{
BMessenger target;
uint32 what;
if (message->FindMessenger(kObserveTarget, &target) != B_OK
|| message->FindInt32(B_OBSERVE_WHAT_CHANGE, (int32*)&what)
!= B_OK) {
break;
}
ObserverList* list = _ObserverList();
if (list != NULL) {
if (message->what == kMsgStartObserving)
list->Add(target, what);
else
list->Remove(target, what);
}
break;
}
case B_GET_PROPERTY:
{
int32 cur;
BMessage specifier;
int32 form;
const char* prop;
status_t err = message->GetCurrentSpecifier(&cur, &specifier,
&form, &prop);
if (err != B_OK && err != B_BAD_SCRIPT_SYNTAX)
break;
bool known = false;
// B_BAD_SCRIPT_SYNTAX defaults to the Messenger property
if (err == B_BAD_SCRIPT_SYNTAX || cur < 0
|| (strcmp(prop, "Messenger") == 0)) {
err = reply.AddMessenger("result", this);
known = true;
} else if (strcmp(prop, "Suites") == 0) {
err = GetSupportedSuites(&reply);
known = true;
} else if (strcmp(prop, "InternalName") == 0) {
err = reply.AddString("result", Name());
known = true;
}
if (known) {
reply.AddInt32("error", B_OK);
message->SendReply(&reply);
return;
}
// let's try next handler
break;
}
case B_GET_SUPPORTED_SUITES:
{
reply.AddInt32("error", GetSupportedSuites(&reply));
message->SendReply(&reply);
return;
}
}
// ToDo: there is some more work needed here
// (someone in the know should fill in)!
if (fNextHandler) {
// we need to apply the next handler's filters here, too
BHandler* target = Looper()->_HandlerFilter(message, fNextHandler);
if (target != NULL && target != this) {
// TODO: we also need to make sure that "target" is not before
// us in the handler chain - at least in case it wasn't before
// the handler actually targeted with this message - this could
// get ugly, though.
target->MessageReceived(message);
}
} else if (message->what != B_MESSAGE_NOT_UNDERSTOOD
&& (message->WasDropped() || message->HasSpecifiers())) {
printf("BHandler %s: MessageReceived() couldn't understand the message:\n", Name());
message->PrintToStream();
message->SendReply(B_MESSAGE_NOT_UNDERSTOOD);
}
}
BLooper*
BHandler::Looper() const
{
return fLooper;
}
void
BHandler::SetName(const char* name)
{
if (fName != NULL) {
free(fName);
fName = NULL;
}
if (name != NULL)
fName = strdup(name);
}
const char*
BHandler::Name() const
{
return fName;
}
void
BHandler::SetNextHandler(BHandler* handler)
{
if (fLooper == NULL) {
debugger("handler must belong to looper before setting NextHandler");
return;
}
if (!fLooper->IsLocked()) {
debugger("The handler's looper must be locked before setting NextHandler");
return;
}
if (handler != NULL && fLooper != handler->Looper()) {
debugger("The handler and its NextHandler must have the same looper");
return;
}
fNextHandler = handler;
}
BHandler*
BHandler::NextHandler() const
{
return fNextHandler;
}
void
BHandler::AddFilter(BMessageFilter* filter)
{
BLooper* looper = fLooper;
if (looper != NULL && !looper->IsLocked()) {
debugger("Owning Looper must be locked before calling SetFilterList");
return;
}
if (looper != NULL)
filter->SetLooper(looper);
if (fFilters == NULL)
fFilters = new BList;
fFilters->AddItem(filter);
}
bool
BHandler::RemoveFilter(BMessageFilter* filter)
{
BLooper* looper = fLooper;
if (looper != NULL && !looper->IsLocked()) {
debugger("Owning Looper must be locked before calling SetFilterList");
return false;
}
if (fFilters != NULL && fFilters->RemoveItem((void*)filter)) {
filter->SetLooper(NULL);
return true;
}
return false;
}
void
BHandler::SetFilterList(BList* filters)
{
BLooper* looper = fLooper;
if (looper != NULL && !looper->IsLocked()) {
debugger("Owning Looper must be locked before calling SetFilterList");
return;
}
/**
@note I would like to use BObjectList internally, but this function is
spec'd such that fFilters would get deleted and then assigned
'filters', which would obviously mess this up. Wondering if
anyone ever assigns a list of filters and then checks against
FilterList() to see if they are the same.
*/
// TODO: Explore issues with using BObjectList
if (fFilters != NULL) {
fFilters->DoForEach(FilterDeleter);
delete fFilters;
}
fFilters = filters;
if (fFilters) {
for (int32 i = 0; i < fFilters->CountItems(); ++i) {
BMessageFilter* filter =
static_cast<BMessageFilter*>(fFilters->ItemAt(i));
if (filter != NULL)
filter->SetLooper(looper);
}
}
}
BList*
BHandler::FilterList()
{
return fFilters;
}
bool
BHandler::LockLooper()
{
BLooper* looper = fLooper;
// Locking the looper also makes sure that the looper is valid
if (looper != NULL && looper->Lock()) {
// Have we locked the right looper? That's as far as the
// "pseudo-atomic" operation mentioned in the BeBook.
if (fLooper == looper)
return true;
// we locked the wrong looper, bail out
looper->Unlock();
}
return false;
}
status_t
BHandler::LockLooperWithTimeout(bigtime_t timeout)
{
BLooper* looper = fLooper;
if (looper == NULL)
return B_BAD_VALUE;
status_t status = looper->LockWithTimeout(timeout);
if (status != B_OK)
return status;
if (fLooper != looper) {
// we locked the wrong looper, bail out
looper->Unlock();
return B_MISMATCHED_VALUES;
}
return B_OK;
}
void
BHandler::UnlockLooper()
{
fLooper->Unlock();
}
BHandler*
BHandler::ResolveSpecifier(BMessage* message, int32 index,
BMessage* specifier, int32 what, const char* property)
{
// Straight from the BeBook
BPropertyInfo propertyInfo(sHandlerPropInfo);
if (propertyInfo.FindMatch(message, index, specifier, what, property) >= 0)
return this;
BMessage reply(B_MESSAGE_NOT_UNDERSTOOD);
reply.AddInt32("error", B_BAD_SCRIPT_SYNTAX);
reply.AddString("message", "Didn't understand the specifier(s)");
message->SendReply(&reply);
return NULL;
}
status_t
BHandler::GetSupportedSuites(BMessage* data)
{
/**
@note This is the output from the original implementation (calling
PrintToStream() on both data and the contained BPropertyInfo):
BMessage: what = (0x0, or 0)
entry suites, type='CSTR', c=1, size=21, data[0]: "suite/vnd.Be-handler"
entry messages, type='SCTD', c=1, size= 0,
property commands types specifiers
--------------------------------------------------------------------------------
Suites PGET 1
(RTSC,suites)
(DTCS,messages)
Messenger PGET GNSM 1
InternalName PGET RTSC 1
With a good deal of trial and error, I determined that the
parenthetical clauses are entries in the 'ctypes' field of
property_info. 'ctypes' is an array of 'compound_type', which
contains an array of 'field_pair's. I haven't the foggiest what
either 'compound_type' or 'field_pair' is for, being as the
scripting docs are so bloody horrible. The corresponding
property_info array is declared in the globals section.
*/
if (data == NULL)
return B_BAD_VALUE;
status_t result = data->AddString("suites", "suite/vnd.Be-handler");
if (result == B_OK) {
BPropertyInfo propertyInfo(sHandlerPropInfo);
result = data->AddFlat("messages", &propertyInfo);
}
return result;
}
status_t
BHandler::StartWatching(BMessenger target, uint32 what)
{
BMessage message(kMsgStartObserving);
message.AddMessenger(kObserveTarget, this);
message.AddInt32(B_OBSERVE_WHAT_CHANGE, what);
return target.SendMessage(&message);
}
status_t
BHandler::StartWatchingAll(BMessenger target)
{
return StartWatching(target, B_OBSERVER_OBSERVE_ALL);
}
status_t
BHandler::StopWatching(BMessenger target, uint32 what)
{
BMessage message(kMsgStopObserving);
message.AddMessenger(kObserveTarget, this);
message.AddInt32(B_OBSERVE_WHAT_CHANGE, what);
return target.SendMessage(&message);
}
status_t
BHandler::StopWatchingAll(BMessenger target)
{
return StopWatching(target, B_OBSERVER_OBSERVE_ALL);
}
status_t
BHandler::StartWatching(BHandler* handler, uint32 what)
{
ObserverList* list = _ObserverList();
if (list == NULL)
return B_NO_MEMORY;
return list->Add(handler, what);
}
status_t
BHandler::StartWatchingAll(BHandler* handler)
{
return StartWatching(handler, B_OBSERVER_OBSERVE_ALL);
}
status_t
BHandler::StopWatching(BHandler* handler, uint32 what)
{
ObserverList* list = _ObserverList();
if (list == NULL)
return B_NO_MEMORY;
return list->Remove(handler, what);
}
status_t
BHandler::StopWatchingAll(BHandler* handler)
{
return StopWatching(handler, B_OBSERVER_OBSERVE_ALL);
}
status_t
BHandler::Perform(perform_code d, void* arg)
{
return BArchivable::Perform(d, arg);
}
void
BHandler::SendNotices(uint32 what, const BMessage* notice)
{
if (fObserverList != NULL)
fObserverList->SendNotices(what, notice);
}
bool
BHandler::IsWatched() const
{
return fObserverList && !fObserverList->IsEmpty();
}
void
BHandler::_InitData(const char* name)
{
SetName(name);
fLooper = NULL;
fNextHandler = NULL;
fFilters = NULL;
fObserverList = NULL;
fToken = gDefaultTokens.NewToken(B_HANDLER_TOKEN, this);
}
ObserverList*
BHandler::_ObserverList()
{
if (fObserverList == NULL)
fObserverList = new (std::nothrow) BPrivate::ObserverList();
return fObserverList;
}
void
BHandler::SetLooper(BLooper* looper)
{
fLooper = looper;
gDefaultTokens.SetHandlerTarget(fToken,
looper ? looper->fDirectTarget : NULL);
if (fFilters != NULL) {
for (int32 i = 0; i < fFilters->CountItems(); i++) {
static_cast<BMessageFilter*>(
fFilters->ItemAtFast(i))->SetLooper(looper);
}
}
}
#if __GNUC__ < 3
// binary compatibility with R4.5
extern "C" void
_ReservedHandler1__8BHandler(BHandler* handler, uint32 what,
const BMessage* notice)
{
handler->BHandler::SendNotices(what, notice);
}
BHandler::BHandler(const BHandler &)
{
// No copy construction allowed.
}
BHandler &
BHandler::operator=(const BHandler &)
{
// No assignments allowed.
return *this;
}
#endif
void BHandler::_ReservedHandler2() {}
void BHandler::_ReservedHandler3() {}
void BHandler::_ReservedHandler4() {}
// #pragma mark -
ObserverList::ObserverList()
{
}
ObserverList::~ObserverList()
{
}
void
ObserverList::_ValidateHandlers(uint32 what)
{
vector<const BHandler*>& handlers = fHandlerMap[what];
vector<const BHandler*>::iterator iterator = handlers.begin();
while (iterator != handlers.end()) {
BMessenger target(*iterator);
if (!target.IsValid()) {
iterator++;
continue;
}
Add(target, what);
iterator = handlers.erase(iterator);
}
}
void
ObserverList::_SendNotices(uint32 what, BMessage* notice)
{
// first iterate over the list of handlers and try to make valid
// messengers out of them
_ValidateHandlers(what);
// now send it to all messengers we know
vector<BMessenger>& messengers = fMessengerMap[what];
vector<BMessenger>::iterator iterator = messengers.begin();
while (iterator != messengers.end()) {
if (!(*iterator).IsValid()) {
iterator = messengers.erase(iterator);
continue;
}
(*iterator).SendMessage(notice);
iterator++;
}
}
status_t
ObserverList::SendNotices(uint32 what, const BMessage* notice)
{
BMessage* copy = NULL;
if (notice != NULL) {
copy = new BMessage(*notice);
copy->what = B_OBSERVER_NOTICE_CHANGE;
copy->AddInt32(B_OBSERVE_ORIGINAL_WHAT, notice->what);
} else
copy = new BMessage(B_OBSERVER_NOTICE_CHANGE);
copy->AddInt32(B_OBSERVE_WHAT_CHANGE, what);
_SendNotices(what, copy);
_SendNotices(B_OBSERVER_OBSERVE_ALL, copy);
delete copy;
return B_OK;
}
status_t
ObserverList::Add(const BHandler* handler, uint32 what)
{
if (handler == NULL)
return B_BAD_HANDLER;
// if this handler already represents a valid target, add its messenger
BMessenger target(handler);
if (target.IsValid())
return Add(target, what);
vector<const BHandler*> &handlers = fHandlerMap[what];
vector<const BHandler*>::iterator iter;
iter = find(handlers.begin(), handlers.end(), handler);
if (iter != handlers.end()) {
// TODO: do we want to have a reference count for this?
return B_OK;
}
handlers.push_back(handler);
return B_OK;
}
status_t
ObserverList::Add(const BMessenger &messenger, uint32 what)
{
vector<BMessenger> &messengers = fMessengerMap[what];
vector<BMessenger>::iterator iter;
iter = find(messengers.begin(), messengers.end(), messenger);
if (iter != messengers.end()) {
// TODO: do we want to have a reference count for this?
return B_OK;
}
messengers.push_back(messenger);
return B_OK;
}
status_t
ObserverList::Remove(const BHandler* handler, uint32 what)
{
if (handler == NULL)
return B_BAD_HANDLER;
// look into the list of messengers
BMessenger target(handler);
if (target.IsValid() && Remove(target, what) == B_OK)
return B_OK;
vector<const BHandler*> &handlers = fHandlerMap[what];
vector<const BHandler*>::iterator iterator = find(handlers.begin(),
handlers.end(), handler);
if (iterator != handlers.end()) {
handlers.erase(iterator);
if (handlers.empty())
fHandlerMap.erase(what);
return B_OK;
}
return B_BAD_HANDLER;
}
status_t
ObserverList::Remove(const BMessenger &messenger, uint32 what)
{
vector<BMessenger> &messengers = fMessengerMap[what];
vector<BMessenger>::iterator iterator = find(messengers.begin(),
messengers.end(), messenger);
if (iterator != messengers.end()) {
messengers.erase(iterator);
if (messengers.empty())
fMessengerMap.erase(what);
return B_OK;
}
return B_BAD_HANDLER;
}
bool
ObserverList::IsEmpty()
{
return fHandlerMap.empty() && fMessengerMap.empty();
}
// #pragma mark -
bool
FilterDeleter(void* _filter)
{
delete static_cast<BMessageFilter*>(_filter);
return false;
}