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