/* libs/graphics/views/SkEventSink.cpp ** ** Copyright 2006, The Android Open Source Project ** ** Licensed under the Apache License, Version 2.0 (the "License"); ** you may not use this file except in compliance with the License. ** You may obtain a copy of the License at ** ** http://www.apache.org/licenses/LICENSE-2.0 ** ** Unless required by applicable law or agreed to in writing, software ** distributed under the License is distributed on an "AS IS" BASIS, ** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. ** See the License for the specific language governing permissions and ** limitations under the License. */ #include "SkEventSink.h" #include "SkTagList.h" #include "SkThread.h" #include "SkGlobals.h" #include "SkThread.h" #include "SkTime.h" #define SK_EventSink_GlobalsTag SkSetFourByteTag('e', 'v', 's', 'k') class SkEventSink_Globals : public SkGlobals::Rec { public: SkMutex fSinkMutex; SkEventSinkID fNextSinkID; SkEventSink* fSinkHead; }; static SkGlobals::Rec* create_globals() { SkEventSink_Globals* rec = new SkEventSink_Globals; rec->fNextSinkID = 0; rec->fSinkHead = NULL; return rec; } SkEventSink::SkEventSink() : fTagHead(NULL) { SkEventSink_Globals& globals = *(SkEventSink_Globals*)SkGlobals::Find(SK_EventSink_GlobalsTag, create_globals); globals.fSinkMutex.acquire(); fID = ++globals.fNextSinkID; fNextSink = globals.fSinkHead; globals.fSinkHead = this; globals.fSinkMutex.release(); } SkEventSink::~SkEventSink() { SkEventSink_Globals& globals = *(SkEventSink_Globals*)SkGlobals::Find(SK_EventSink_GlobalsTag, create_globals); if (fTagHead) SkTagList::DeleteAll(fTagHead); globals.fSinkMutex.acquire(); SkEventSink* sink = globals.fSinkHead; SkEventSink* prev = NULL; for (;;) { SkEventSink* next = sink->fNextSink; if (sink == this) { if (prev) prev->fNextSink = next; else globals.fSinkHead = next; break; } prev = sink; sink = next; } globals.fSinkMutex.release(); } bool SkEventSink::doEvent(const SkEvent& evt) { return this->onEvent(evt); } bool SkEventSink::doQuery(SkEvent* evt) { SkASSERT(evt); return this->onQuery(evt); } bool SkEventSink::onEvent(const SkEvent&) { return false; } bool SkEventSink::onQuery(SkEvent*) { return false; } /////////////////////////////////////////////////////////////////////////////// SkTagList* SkEventSink::findTagList(U8CPU tag) const { return fTagHead ? SkTagList::Find(fTagHead, tag) : NULL; } void SkEventSink::addTagList(SkTagList* rec) { SkASSERT(rec); SkASSERT(fTagHead == NULL || SkTagList::Find(fTagHead, rec->fTag) == NULL); rec->fNext = fTagHead; fTagHead = rec; } void SkEventSink::removeTagList(U8CPU tag) { if (fTagHead) SkTagList::DeleteTag(&fTagHead, tag); } /////////////////////////////////////////////////////////////////////////////// struct SkListenersTagList : SkTagList { SkListenersTagList(U16CPU count) : SkTagList(kListeners_SkTagList) { fExtra16 = SkToU16(count); fIDs = (SkEventSinkID*)sk_malloc_throw(count * sizeof(SkEventSinkID)); } virtual ~SkListenersTagList() { sk_free(fIDs); } int countListners() const { return fExtra16; } int find(SkEventSinkID id) const { const SkEventSinkID* idptr = fIDs; for (int i = fExtra16 - 1; i >= 0; --i) if (idptr[i] == id) return i; return -1; } SkEventSinkID* fIDs; }; void SkEventSink::addListenerID(SkEventSinkID id) { if (id == 0) return; SkListenersTagList* prev = (SkListenersTagList*)this->findTagList(kListeners_SkTagList); int count = 0; if (prev) { if (prev->find(id) >= 0) return; count = prev->countListners(); } SkListenersTagList* next = SkNEW_ARGS(SkListenersTagList, (count + 1)); if (prev) { memcpy(next->fIDs, prev->fIDs, count * sizeof(SkEventSinkID)); this->removeTagList(kListeners_SkTagList); } next->fIDs[count] = id; this->addTagList(next); } void SkEventSink::copyListeners(const SkEventSink& sink) { SkListenersTagList* sinkList = (SkListenersTagList*)sink.findTagList(kListeners_SkTagList); if (sinkList == NULL) return; SkASSERT(sinkList->countListners() > 0); const SkEventSinkID* iter = sinkList->fIDs; const SkEventSinkID* stop = iter + sinkList->countListners(); while (iter < stop) addListenerID(*iter++); } void SkEventSink::removeListenerID(SkEventSinkID id) { if (id == 0) return; SkListenersTagList* list = (SkListenersTagList*)this->findTagList(kListeners_SkTagList); if (list == NULL) return; int index = list->find(id); if (index >= 0) { int count = list->countListners(); SkASSERT(count > 0); if (count == 1) this->removeTagList(kListeners_SkTagList); else { // overwrite without resize/reallocating our struct (for speed) list->fIDs[index] = list->fIDs[count - 1]; list->fExtra16 = SkToU16(count - 1); } } } bool SkEventSink::hasListeners() const { return this->findTagList(kListeners_SkTagList) != NULL; } void SkEventSink::postToListeners(const SkEvent& evt, SkMSec delay) { SkListenersTagList* list = (SkListenersTagList*)this->findTagList(kListeners_SkTagList); if (list) { SkASSERT(list->countListners() > 0); const SkEventSinkID* iter = list->fIDs; const SkEventSinkID* stop = iter + list->countListners(); while (iter < stop) (SkNEW_ARGS(SkEvent, (evt)))->post(*iter++, delay); } } /////////////////////////////////////////////////////////////////////////////// SkEventSink::EventResult SkEventSink::DoEvent(const SkEvent& evt, SkEventSinkID sinkID) { SkEventSink* sink = SkEventSink::FindSink(sinkID); if (sink) { #ifdef SK_DEBUG if (evt.isDebugTrace()) { SkString etype; evt.getType(&etype); SkDebugf("SkEventTrace: dispatching event <%s> to 0x%x", etype.c_str(), sinkID); const char* idStr = evt.findString("id"); if (idStr) SkDebugf(" (%s)", idStr); SkDebugf("\n"); } #endif return sink->doEvent(evt) ? kHandled_EventResult : kNotHandled_EventResult; } else { #ifdef SK_DEBUG if (sinkID) SkDebugf("DoEvent: Can't find sink for ID(%x)\n", sinkID); else SkDebugf("Event sent to 0 sinkID\n"); if (evt.isDebugTrace()) { SkString etype; evt.getType(&etype); SkDebugf("SkEventTrace: eventsink not found <%s> for 0x%x\n", etype.c_str(), sinkID); } #endif return kSinkNotFound_EventResult; } } SkEventSink* SkEventSink::FindSink(SkEventSinkID sinkID) { if (sinkID == 0) return 0; SkEventSink_Globals& globals = *(SkEventSink_Globals*)SkGlobals::Find(SK_EventSink_GlobalsTag, create_globals); SkAutoMutexAcquire ac(globals.fSinkMutex); SkEventSink* sink = globals.fSinkHead; while (sink) { if (sink->getSinkID() == sinkID) return sink; sink = sink->fNextSink; } return NULL; } //////////////////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////////////////// #if 0 // experimental, not tested #include "SkThread.h" #include "SkTDict.h" #define kMinStringBufferSize 128 static SkMutex gNamedSinkMutex; static SkTDict<SkEventSinkID> gNamedSinkIDs(kMinStringBufferSize); /** Register a name/id pair with the system. If the name already exists, replace its ID with the new id. This pair will persist until UnregisterNamedSink() is called. */ void SkEventSink::RegisterNamedSinkID(const char name[], SkEventSinkID id) { if (id && name && *name) { SkAutoMutexAcquire ac(gNamedSinkMutex); gNamedSinkIDs.set(name, id); } } /** Return the id that matches the specified name (from a previous call to RegisterNamedSinkID(). If no match is found, return 0 */ SkEventSinkID SkEventSink::FindNamedSinkID(const char name[]) { SkEventSinkID id = 0; if (name && *name) { SkAutoMutexAcquire ac(gNamedSinkMutex); (void)gNamedSinkIDs.find(name, &id); } return id; } /** Remove all name/id pairs from the system. This is call internally on shutdown, to ensure no memory leaks. It should not be called before shutdown. */ void SkEventSink::RemoveAllNamedSinkIDs() { SkAutoMutexAcquire ac(gNamedSinkMutex); (void)gNamedSinkIDs.reset(); } #endif