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