1
2/*
3 * Copyright 2006 The Android Open Source Project
4 *
5 * Use of this source code is governed by a BSD-style license that can be
6 * found in the LICENSE file.
7 */
8
9
10#include "SkDisplayPost.h"
11#include "SkAnimateMaker.h"
12#include "SkAnimator.h"
13#include "SkDisplayMovie.h"
14#include "SkPostParts.h"
15#include "SkScript.h"
16#ifdef SK_DEBUG
17#include "SkDump.h"
18#include "SkTime.h"
19#endif
20
21enum SkPost_Properties {
22    SK_PROPERTY(target),
23    SK_PROPERTY(type)
24};
25
26#if SK_USE_CONDENSED_INFO == 0
27
28const SkMemberInfo SkPost::fInfo[] = {
29    SK_MEMBER(delay, MSec),
30//  SK_MEMBER(initialized, Boolean),
31    SK_MEMBER(mode, EventMode),
32    SK_MEMBER(sink, String),
33    SK_MEMBER_PROPERTY(target, String),
34    SK_MEMBER_PROPERTY(type, String)
35};
36
37#endif
38
39DEFINE_GET_MEMBER(SkPost);
40
41SkPost::SkPost() : delay(0), /*initialized(SkBool(-1)), */ mode(kImmediate), fMaker(NULL),
42    fSinkID(0), fTargetMaker(NULL), fChildHasID(false), fDirty(false) {
43}
44
45SkPost::~SkPost() {
46    for (SkDataInput** part = fParts.begin(); part < fParts.end();  part++)
47        delete *part;
48}
49
50bool SkPost::add(SkAnimateMaker& , SkDisplayable* child) {
51    SkASSERT(child && child->isDataInput());
52    SkDataInput* part = (SkDataInput*) child;
53    *fParts.append() = part;
54    return true;
55}
56
57bool SkPost::childrenNeedDisposing() const {
58    return false;
59}
60
61void SkPost::dirty() {
62    fDirty = true;
63}
64
65#ifdef SK_DUMP_ENABLED
66void SkPost::dump(SkAnimateMaker* maker) {
67    dumpBase(maker);
68    SkString* eventType = new SkString();
69    fEvent.getType(eventType);
70    if (eventType->equals("user")) {
71        const char* target = fEvent.findString("id");
72        SkDebugf("target=\"%s\" ", target);
73    }
74    else
75        SkDebugf("type=\"%s\" ", eventType->c_str());
76    delete eventType;
77
78    if (delay > 0) {
79#ifdef SK_CAN_USE_FLOAT
80        SkDebugf("delay=\"%g\" ", SkScalarToFloat(SkScalarDiv(delay, 1000)));
81#else
82        SkDebugf("delay=\"%x\" ", SkScalarDiv(delay, 1000));
83#endif
84    }
85//  if (initialized == false)
86//      SkDebugf("(uninitialized) ");
87    SkString string;
88    SkDump::GetEnumString(SkType_EventMode, mode, &string);
89    if (!string.equals("immediate"))
90        SkDebugf("mode=\"%s\" ", string.c_str());
91    // !!! could enhance this to search through make hierarchy to show name of sink
92    if (sink.size() > 0) {
93        SkDebugf("sink=\"%s\" sinkID=\"%d\" ", sink.c_str(), fSinkID);
94    } else if (fSinkID != maker->getAnimator()->getSinkID() && fSinkID != 0) {
95        SkDebugf("sinkID=\"%d\" ", fSinkID);
96    }
97    const SkMetaData& meta = fEvent.getMetaData();
98    SkMetaData::Iter iter(meta);
99    SkMetaData::Type    type;
100    int number;
101    const char* name;
102    bool closedYet = false;
103    SkDisplayList::fIndent += 4;
104    //this seems to work, but kinda hacky
105    //for some reason the last part is id, which i don't want
106    //and the parts seem to be in the reverse order from the one in which we find the
107    //data itself
108    //SkDataInput** ptr = fParts.end();
109    //SkDataInput* data;
110    //const char* ID;
111    while ((name = iter.next(&type, &number)) != NULL) {
112        //ptr--;
113        if (strcmp(name, "id") == 0)
114            continue;
115        if (closedYet == false) {
116            SkDebugf(">\n");
117            closedYet = true;
118        }
119        //data = *ptr;
120        //if (data->id)
121        //    ID = data->id;
122        //else
123        //    ID = "";
124        SkDebugf("%*s<data name=\"%s\" ", SkDisplayList::fIndent, "", name);
125        switch (type) {
126            case SkMetaData::kS32_Type: {
127                int32_t s32;
128                meta.findS32(name, &s32);
129                SkDebugf("int=\"%d\" ", s32);
130                } break;
131            case SkMetaData::kScalar_Type: {
132                SkScalar scalar;
133                meta.findScalar(name, &scalar);
134#ifdef SK_CAN_USE_FLOAT
135                SkDebugf("float=\"%g\" ", SkScalarToFloat(scalar));
136#else
137                SkDebugf("float=\"%x\" ", scalar);
138#endif
139                } break;
140            case SkMetaData::kString_Type:
141                SkDebugf("string=\"%s\" ", meta.findString(name));
142                break;
143            case SkMetaData::kPtr_Type: {//when do we have a pointer
144                    void* ptr;
145                    meta.findPtr(name, &ptr);
146                    SkDebugf("0x%08x ", ptr);
147                } break;
148            case SkMetaData::kBool_Type: {
149                bool boolean;
150                meta.findBool(name, &boolean);
151                SkDebugf("boolean=\"%s\" ", boolean ? "true " : "false ");
152                } break;
153            default:
154                break;
155        }
156        SkDebugf("/>\n");
157        //ptr++;
158/*      perhaps this should only be done in the case of a pointer?
159        SkDisplayable* displayable;
160        if (maker->find(name, &displayable))
161            displayable->dump(maker);
162        else
163            SkDebugf("\n");*/
164    }
165    SkDisplayList::fIndent -= 4;
166    if (closedYet)
167        dumpEnd(maker);
168    else
169        SkDebugf("/>\n");
170
171}
172#endif
173
174bool SkPost::enable(SkAnimateMaker& maker ) {
175    if (maker.hasError())
176        return true;
177    if (fDirty) {
178        if (sink.size() > 0)
179            findSinkID();
180        if (fChildHasID) {
181            SkString preserveID(fEvent.findString("id"));
182            fEvent.getMetaData().reset();
183            if (preserveID.size() > 0)
184                fEvent.setString("id", preserveID);
185            for (SkDataInput** part = fParts.begin(); part < fParts.end();  part++) {
186                if ((*part)->add())
187                    maker.setErrorCode(SkDisplayXMLParserError::kErrorAddingDataToPost);
188            }
189        }
190        fDirty = false;
191    }
192#ifdef SK_DUMP_ENABLED
193    if (maker.fDumpPosts) {
194        SkDebugf("post enable: ");
195        dump(&maker);
196    }
197#if defined SK_DEBUG_ANIMATION_TIMING
198    SkString debugOut;
199    SkMSec time = maker.getAppTime();
200    debugOut.appendS32(time - maker.fDebugTimeBase);
201    debugOut.append(" post id=");
202    debugOut.append(_id);
203    debugOut.append(" enable=");
204    debugOut.appendS32(maker.fEnableTime - maker.fDebugTimeBase);
205    debugOut.append(" delay=");
206    debugOut.appendS32(delay);
207#endif
208#endif
209//  SkMSec adjustedDelay = maker.adjustDelay(maker.fEnableTime, delay);
210    SkMSec futureTime = maker.fEnableTime + delay;
211    fEvent.setFast32(futureTime);
212#if defined SK_DEBUG && defined SK_DEBUG_ANIMATION_TIMING
213    debugOut.append(" future=");
214    debugOut.appendS32(futureTime - maker.fDebugTimeBase);
215    SkDebugf("%s\n", debugOut.c_str());
216#endif
217    SkEventSinkID targetID = fSinkID;
218    bool isAnimatorEvent = true;
219    SkAnimator* anim = maker.getAnimator();
220    if (targetID == 0) {
221        isAnimatorEvent = fEvent.findString("id") != NULL;
222        if (isAnimatorEvent)
223            targetID = anim->getSinkID();
224        else if (maker.fHostEventSinkID)
225            targetID = maker.fHostEventSinkID;
226        else
227            return true;
228    } else
229        anim = fTargetMaker->getAnimator();
230    if (delay == 0) {
231        if (isAnimatorEvent && mode == kImmediate)
232            fTargetMaker->doEvent(fEvent);
233        else
234            anim->onEventPost(new SkEvent(fEvent), targetID);
235    } else
236        anim->onEventPostTime(new SkEvent(fEvent), targetID, futureTime);
237    return true;
238}
239
240void SkPost::findSinkID() {
241    // get the next delimiter '.' if any
242    fTargetMaker = fMaker;
243    const char* ch = sink.c_str();
244    do {
245        const char* end = strchr(ch, '.');
246        size_t len = end ? end - ch : strlen(ch);
247        SkDisplayable* displayable = NULL;
248        if (SK_LITERAL_STR_EQUAL("parent", ch, len)) {
249            if (fTargetMaker->fParentMaker)
250                fTargetMaker = fTargetMaker->fParentMaker;
251            else {
252                fTargetMaker->setErrorCode(SkDisplayXMLParserError::kNoParentAvailable);
253                return;
254            }
255        } else {
256            fTargetMaker->find(ch, len, &displayable);
257            if (displayable == NULL || displayable->getType() != SkType_Movie) {
258                fTargetMaker->setErrorCode(SkDisplayXMLParserError::kExpectedMovie);
259                return;
260            }
261            SkDisplayMovie* movie = (SkDisplayMovie*) displayable;
262            fTargetMaker = movie->fMovie.fMaker;
263        }
264        if (end == NULL)
265            break;
266        ch = ++end;
267    } while (true);
268    SkAnimator* anim = fTargetMaker->getAnimator();
269    fSinkID = anim->getSinkID();
270}
271
272bool SkPost::hasEnable() const {
273    return true;
274}
275
276void SkPost::onEndElement(SkAnimateMaker& maker) {
277    fTargetMaker = fMaker = &maker;
278    if (fChildHasID == false) {
279        for (SkDataInput** part = fParts.begin(); part < fParts.end();  part++)
280            delete *part;
281        fParts.reset();
282    }
283}
284
285void SkPost::setChildHasID() {
286    fChildHasID = true;
287}
288
289bool SkPost::setProperty(int index, SkScriptValue& value) {
290    SkASSERT(value.fType == SkType_String);
291    SkString* string = value.fOperand.fString;
292    switch(index) {
293        case SK_PROPERTY(target): {
294            fEvent.setType("user");
295            fEvent.setString("id", *string);
296            mode = kImmediate;
297            } break;
298        case SK_PROPERTY(type):
299            fEvent.setType(*string);
300            break;
301        default:
302            SkASSERT(0);
303            return false;
304    }
305    return true;
306}
307
308