/* libs/graphics/animator/SkDisplayPost.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 "SkDisplayPost.h"
#include "SkAnimateMaker.h"
#include "SkAnimator.h"
#include "SkDisplayMovie.h"
#include "SkPostParts.h"
#include "SkScript.h"
#ifdef SK_DEBUG
#include "SkDump.h"
#include "SkTime.h"
#endif
enum SkPost_Properties {
SK_PROPERTY(target),
SK_PROPERTY(type)
};
#if SK_USE_CONDENSED_INFO == 0
const SkMemberInfo SkPost::fInfo[] = {
SK_MEMBER(delay, MSec),
// SK_MEMBER(initialized, Boolean),
SK_MEMBER(mode, EventMode),
SK_MEMBER(sink, String),
SK_MEMBER_PROPERTY(target, String),
SK_MEMBER_PROPERTY(type, String)
};
#endif
DEFINE_GET_MEMBER(SkPost);
SkPost::SkPost() : delay(0), /*initialized(SkBool(-1)), */ mode(kImmediate), fMaker(NULL),
fSinkID(0), fTargetMaker(NULL), fChildHasID(false), fDirty(false) {
}
SkPost::~SkPost() {
for (SkData** part = fParts.begin(); part < fParts.end(); part++)
delete *part;
}
bool SkPost::add(SkAnimateMaker& , SkDisplayable* child) {
SkASSERT(child && child->isData());
SkData* part = (SkData*) child;
*fParts.append() = part;
return true;
}
bool SkPost::childrenNeedDisposing() const {
return false;
}
void SkPost::dirty() {
fDirty = true;
}
#ifdef SK_DUMP_ENABLED
void SkPost::dump(SkAnimateMaker* maker) {
dumpBase(maker);
SkString* eventType = new SkString();
fEvent.getType(eventType);
if (eventType->equals("user")) {
const char* target = fEvent.findString("id");
SkDebugf("target=\"%s\" ", target);
}
else
SkDebugf("type=\"%s\" ", eventType->c_str());
delete eventType;
if (delay > 0) {
#ifdef SK_CAN_USE_FLOAT
SkDebugf("delay=\"%g\" ", SkScalarToFloat(SkScalarDiv(delay, 1000)));
#else
SkDebugf("delay=\"%x\" ", SkScalarDiv(delay, 1000));
#endif
}
// if (initialized == false)
// SkDebugf("(uninitialized) ");
SkString string;
SkDump::GetEnumString(SkType_EventMode, mode, &string);
if (!string.equals("immediate"))
SkDebugf("mode=\"%s\" ", string.c_str());
// !!! could enhance this to search through make hierarchy to show name of sink
if (sink.size() > 0) {
SkDebugf("sink=\"%s\" sinkID=\"%d\" ", sink.c_str(), fSinkID);
} else if (fSinkID != maker->getAnimator()->getSinkID() && fSinkID != 0) {
SkDebugf("sinkID=\"%d\" ", fSinkID);
}
const SkMetaData& meta = fEvent.getMetaData();
SkMetaData::Iter iter(meta);
SkMetaData::Type type;
int number;
const char* name;
bool closedYet = false;
SkDisplayList::fIndent += 4;
//this seems to work, but kinda hacky
//for some reason the last part is id, which i don't want
//and the parts seem to be in the reverse order from the one in which we find the
//data itself
//SkData** ptr = fParts.end();
//SkData* data;
//const char* ID;
while ((name = iter.next(&type, &number)) != NULL) {
//ptr--;
if (strcmp(name, "id") == 0)
continue;
if (closedYet == false) {
SkDebugf(">\n");
closedYet = true;
}
//data = *ptr;
//if (data->id)
// ID = data->id;
//else
// ID = "";
SkDebugf("%*s<data name=\"%s\" ", SkDisplayList::fIndent, "", name);
switch (type) {
case SkMetaData::kS32_Type: {
int32_t s32;
meta.findS32(name, &s32);
SkDebugf("int=\"%d\" ", s32);
} break;
case SkMetaData::kScalar_Type: {
SkScalar scalar;
meta.findScalar(name, &scalar);
#ifdef SK_CAN_USE_FLOAT
SkDebugf("float=\"%g\" ", SkScalarToFloat(scalar));
#else
SkDebugf("float=\"%x\" ", scalar);
#endif
} break;
case SkMetaData::kString_Type:
SkDebugf("string=\"%s\" ", meta.findString(name));
break;
case SkMetaData::kPtr_Type: {//when do we have a pointer
void* ptr;
meta.findPtr(name, &ptr);
SkDebugf("0x%08x ", ptr);
} break;
case SkMetaData::kBool_Type: {
bool boolean;
meta.findBool(name, &boolean);
SkDebugf("boolean=\"%s\" ", boolean ? "true " : "false ");
} break;
default:
break;
}
SkDebugf("/>\n");
//ptr++;
/* perhaps this should only be done in the case of a pointer?
SkDisplayable* displayable;
if (maker->find(name, &displayable))
displayable->dump(maker);
else
SkDebugf("\n");*/
}
SkDisplayList::fIndent -= 4;
if (closedYet)
dumpEnd(maker);
else
SkDebugf("/>\n");
}
#endif
bool SkPost::enable(SkAnimateMaker& maker ) {
if (maker.hasError())
return true;
if (fDirty) {
if (sink.size() > 0)
findSinkID();
if (fChildHasID) {
SkString preserveID(fEvent.findString("id"));
fEvent.getMetaData().reset();
if (preserveID.size() > 0)
fEvent.setString("id", preserveID);
for (SkData** part = fParts.begin(); part < fParts.end(); part++) {
if ((*part)->add())
maker.setErrorCode(SkDisplayXMLParserError::kErrorAddingDataToPost);
}
}
fDirty = false;
}
#ifdef SK_DUMP_ENABLED
if (maker.fDumpPosts) {
SkDebugf("post enable: ");
dump(&maker);
}
#if defined SK_DEBUG_ANIMATION_TIMING
SkString debugOut;
SkMSec time = maker.getAppTime();
debugOut.appendS32(time - maker.fDebugTimeBase);
debugOut.append(" post id=");
debugOut.append(_id);
debugOut.append(" enable=");
debugOut.appendS32(maker.fEnableTime - maker.fDebugTimeBase);
debugOut.append(" delay=");
debugOut.appendS32(delay);
#endif
#endif
// SkMSec adjustedDelay = maker.adjustDelay(maker.fEnableTime, delay);
SkMSec futureTime = maker.fEnableTime + delay;
fEvent.setFast32(futureTime);
#if defined SK_DEBUG && defined SK_DEBUG_ANIMATION_TIMING
debugOut.append(" future=");
debugOut.appendS32(futureTime - maker.fDebugTimeBase);
SkDebugf("%s\n", debugOut.c_str());
#endif
SkEventSinkID targetID = fSinkID;
bool isAnimatorEvent = true;
SkAnimator* anim = maker.getAnimator();
if (targetID == 0) {
isAnimatorEvent = fEvent.findString("id") != NULL;
if (isAnimatorEvent)
targetID = anim->getSinkID();
else if (maker.fHostEventSinkID)
targetID = maker.fHostEventSinkID;
else
return true;
} else
anim = fTargetMaker->getAnimator();
if (delay == 0) {
if (isAnimatorEvent && mode == kImmediate)
fTargetMaker->doEvent(fEvent);
else
anim->onEventPost(new SkEvent(fEvent), targetID);
} else
anim->onEventPostTime(new SkEvent(fEvent), targetID, futureTime);
return true;
}
void SkPost::findSinkID() {
// get the next delimiter '.' if any
fTargetMaker = fMaker;
const char* ch = sink.c_str();
do {
const char* end = strchr(ch, '.');
size_t len = end ? end - ch : strlen(ch);
SkDisplayable* displayable = NULL;
if (SK_LITERAL_STR_EQUAL("parent", ch, len)) {
if (fTargetMaker->fParentMaker)
fTargetMaker = fTargetMaker->fParentMaker;
else {
fTargetMaker->setErrorCode(SkDisplayXMLParserError::kNoParentAvailable);
return;
}
} else {
fTargetMaker->find(ch, len, &displayable);
if (displayable == NULL || displayable->getType() != SkType_Movie) {
fTargetMaker->setErrorCode(SkDisplayXMLParserError::kExpectedMovie);
return;
}
SkDisplayMovie* movie = (SkDisplayMovie*) displayable;
fTargetMaker = movie->fMovie.fMaker;
}
if (end == NULL)
break;
ch = ++end;
} while (true);
SkAnimator* anim = fTargetMaker->getAnimator();
fSinkID = anim->getSinkID();
}
bool SkPost::hasEnable() const {
return true;
}
void SkPost::onEndElement(SkAnimateMaker& maker) {
fTargetMaker = fMaker = &maker;
if (fChildHasID == false) {
for (SkData** part = fParts.begin(); part < fParts.end(); part++)
delete *part;
fParts.reset();
}
}
void SkPost::setChildHasID() {
fChildHasID = true;
}
bool SkPost::setProperty(int index, SkScriptValue& value) {
SkASSERT(value.fType == SkType_String);
SkString* string = value.fOperand.fString;
switch(index) {
case SK_PROPERTY(target): {
fEvent.setType("user");
fEvent.setString("id", *string);
mode = kImmediate;
} break;
case SK_PROPERTY(type):
fEvent.setType(*string);
break;
default:
SkASSERT(0);
return false;
}
return true;
}