/* * 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 #include #include #include #include #include #include #include #include #include #include #include #include #include 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 > HandlerObserverMap; typedef map > 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(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( 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& handlers = fHandlerMap[what]; vector::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& messengers = fMessengerMap[what]; vector::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 &handlers = fHandlerMap[what]; vector::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 &messengers = fMessengerMap[what]; vector::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 &handlers = fHandlerMap[what]; vector::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 &messengers = fMessengerMap[what]; vector::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(_filter); return false; }