text: 
  /*
   * Copyright 2006 The Android Open Source Project
   *
   * Use of this source code is governed by a BSD-style license that can be
   * found in the LICENSE file.
   */
  
  
  #include "SkEvent.h"
  
  void SkEvent::initialize(const char* type, size_t typeLen,
                           SkEventSinkID targetID) {
      fType = NULL;
      setType(type, typeLen);
      f32 = 0;
      fTargetID = targetID;
      fTargetProc = NULL;
  #ifdef SK_DEBUG
      fTime = 0;
      fNextEvent = NULL;
  #endif
  }
  
  SkEvent::SkEvent()
  {
      initialize("", 0, 0);
  }
  
  SkEvent::SkEvent(const SkEvent& src)
  {
      *this = src;
      if (((size_t) fType & 1) == 0)
          setType(src.fType);
  }
  
  SkEvent::SkEvent(const SkString& type, SkEventSinkID targetID)
  {
      initialize(type.c_str(), type.size(), targetID);
  }
  
  SkEvent::SkEvent(const char type[], SkEventSinkID targetID)
  {
      SkASSERT(type);
      initialize(type, strlen(type), targetID);
  }
  
  SkEvent::~SkEvent()
  {
      if (((size_t) fType & 1) == 0)
          sk_free((void*) fType);
  }
  
  static size_t makeCharArray(char* buffer, size_t compact)
  {
      size_t bits = (size_t) compact >> 1;
      memcpy(buffer, &bits, sizeof(compact));
      buffer[sizeof(compact)] = 0;
      return strlen(buffer);
  }
  
  void SkEvent::getType(SkString* str) const 
  { 
      if (str) 
      {
          if ((size_t) fType & 1) // not a pointer
          {
              char chars[sizeof(size_t) + 1];
              size_t len = makeCharArray(chars, (size_t) fType);
              str->set(chars, len);
          }
          else
              str->set(fType);
      }
  }
  
  bool SkEvent::isType(const SkString& str) const 
  {
      return this->isType(str.c_str(), str.size()); 
  }
  
  bool SkEvent::isType(const char type[], size_t typeLen) const 
  { 
      if (typeLen == 0)
          typeLen = strlen(type);
      if ((size_t) fType & 1) {   // not a pointer
          char chars[sizeof(size_t) + 1];
          size_t len = makeCharArray(chars, (size_t) fType);
          return len == typeLen && strncmp(chars, type, typeLen) == 0;
      }
      return strncmp(fType, type, typeLen) == 0 && fType[typeLen] == 0; 
  }
  
  void SkEvent::setType(const char type[], size_t typeLen)
  {
      if (typeLen == 0)
          typeLen = strlen(type);
      if (typeLen <= sizeof(fType)) {
          size_t slot = 0;
          memcpy(&slot, type, typeLen);
          if (slot << 1 >> 1 != slot)
              goto useCharStar;
          slot <<= 1;
          slot |= 1;
          fType = (char*) slot;
      } else {
  useCharStar:
          fType = (char*) sk_malloc_throw(typeLen + 1);
          SkASSERT(((size_t) fType & 1) == 0);
          memcpy(fType, type, typeLen);
          fType[typeLen] = 0;
      }
  }
  
  void SkEvent::setType(const SkString& type)
  {
      setType(type.c_str());
  }
  
  ////////////////////////////////////////////////////////////////////////////
  
  #include "SkParse.h"
  
  void SkEvent::inflate(const SkDOM& dom, const SkDOM::Node* node)
  {
      const char* name = dom.findAttr(node, "type");
      if (name)
          this->setType(name);
  
      const char* value;
      if ((value = dom.findAttr(node, "fast32")) != NULL)
      {
          int32_t n;
          if (SkParse::FindS32(value, &n))
              this->setFast32(n);
      }
  
      for (node = dom.getFirstChild(node); node; node = dom.getNextSibling(node))
      {
          if (strcmp(dom.getName(node), "data"))
          {
              SkDEBUGCODE(SkDebugf("SkEvent::inflate unrecognized subelement <%s>\n", dom.getName(node));)
              continue;
          }
  
          name = dom.findAttr(node, "name");
          if (name == NULL)
          {
              SkDEBUGCODE(SkDebugf("SkEvent::inflate missing required \"name\" attribute in <data> subelement\n");)
              continue;
          }
  
          if ((value = dom.findAttr(node, "s32")) != NULL)
          {
              int32_t n;
              if (SkParse::FindS32(value, &n))
                  this->setS32(name, n);
          }
          else if ((value = dom.findAttr(node, "scalar")) != NULL)
          {
              SkScalar x;
              if (SkParse::FindScalar(value, &x))
                  this->setScalar(name, x);
          }
          else if ((value = dom.findAttr(node, "string")) != NULL)
              this->setString(name, value);
  #ifdef SK_DEBUG
          else
          {
              SkDebugf("SkEvent::inflate <data name=\"%s\"> subelement missing required type attribute [S32 | scalar | string]\n", name);
          }
  #endif
      }
  }
  
  #ifdef SK_DEBUG
  
      #ifndef SkScalarToFloat
          #define SkScalarToFloat(x)  ((x) / 65536.f)
      #endif
  
      void SkEvent::dump(const char title[])
      {
          if (title)
              SkDebugf("%s ", title);
              
          SkString    etype;
          this->getType(&etype);
          SkDebugf("event<%s> fast32=%d", etype.c_str(), this->getFast32());
  
          const SkMetaData&   md = this->getMetaData();
          SkMetaData::Iter    iter(md);
          SkMetaData::Type    mtype;
          int                 count;
          const char*         name;
          
          while ((name = iter.next(&mtype, &count)) != NULL)
          {
              SkASSERT(count > 0);
  
              SkDebugf(" <%s>=", name);
              switch (mtype) {
              case SkMetaData::kS32_Type:     // vector version???
                  {
                      int32_t value;
                      md.findS32(name, &value);
                      SkDebugf("%d ", value);
                  }
                  break;
              case SkMetaData::kScalar_Type:
                  {
                      const SkScalar* values = md.findScalars(name, &count, NULL);
                      SkDebugf("%f", SkScalarToFloat(values[0]));
                      for (int i = 1; i < count; i++)
                          SkDebugf(", %f", SkScalarToFloat(values[i]));
                      SkDebugf(" ");
                  }
                  break;
              case SkMetaData::kString_Type:
                  {
                      const char* value = md.findString(name);
                      SkASSERT(value);
                      SkDebugf("<%s> ", value);
                  }
                  break;
              case SkMetaData::kPtr_Type:     // vector version???
                  {
                      void*   value;
                      md.findPtr(name, &value);
                      SkDebugf("%p ", value);
                  }
                  break;
              case SkMetaData::kBool_Type:    // vector version???
                  {
                      bool    value;
                      md.findBool(name, &value);
                      SkDebugf("%s ", value ? "true" : "false");
                  }
                  break;
              default:
                  SkASSERT(!"unknown metadata type returned from iterator");
                  break;
              }
          }
          SkDebugf("\n");
      }
  #endif
  
  ///////////////////////////////////////////////////////////////////////////////////////
  
  #ifdef SK_DEBUG
  // #define SK_TRACE_EVENTSx
  #endif
  
  #ifdef SK_TRACE_EVENTS
      static void event_log(const char s[])
      {
          SkDEBUGF(("%s\n", s));
      }
  
      #define EVENT_LOG(s)        event_log(s)
      #define EVENT_LOGN(s, n)    do { SkString str(s); str.append(" "); str.appendS32(n); event_log(str.c_str()); } while (0)
  #else
      #define EVENT_LOG(s)
      #define EVENT_LOGN(s, n)
  #endif
  
  #include "SkThread.h"
  #include "SkTime.h"
  
  class SkEvent_Globals {
  public:
      SkEvent_Globals() {
          fEventQHead = NULL;
          fEventQTail = NULL;
          fDelayQHead = NULL;
          SkDEBUGCODE(fEventCounter = 0;)
      }
  
      SkMutex     fEventMutex;
      SkEvent*    fEventQHead, *fEventQTail;
      SkEvent*    fDelayQHead;
      SkDEBUGCODE(int fEventCounter;)
  };
  
  static SkEvent_Globals& getGlobals() {
      // leak this, so we don't incure any shutdown perf hit
      static SkEvent_Globals* gGlobals = new SkEvent_Globals;
      return *gGlobals;
  }
  
  ///////////////////////////////////////////////////////////////////////////////
  
  void SkEvent::postDelay(SkMSec delay) {
      if (!fTargetID && !fTargetProc) {
          delete this;
          return;
      }
      
      if (delay) {
          this->postTime(SkTime::GetMSecs() + delay);
          return;
      }
  
      SkEvent_Globals& globals = getGlobals();
  
      globals.fEventMutex.acquire();
      bool wasEmpty = SkEvent::Enqueue(this);
      globals.fEventMutex.release();
      
      // call outside of us holding the mutex
      if (wasEmpty) {
          SkEvent::SignalNonEmptyQueue();
      }
  }
  
  void SkEvent::postTime(SkMSec time) {
      if (!fTargetID && !fTargetProc) {
          delete this;
          return;
      }
  
      SkEvent_Globals& globals = getGlobals();
      
      globals.fEventMutex.acquire();
      SkMSec queueDelay = SkEvent::EnqueueTime(this, time);
      globals.fEventMutex.release();
      
      // call outside of us holding the mutex
      if ((int32_t)queueDelay != ~0) {
          SkEvent::SignalQueueTimer(queueDelay);
      }
  }
  
  bool SkEvent::Enqueue(SkEvent* evt) {
      SkEvent_Globals& globals = getGlobals();
      //  gEventMutex acquired by caller
  
      SkASSERT(evt);
  
      bool wasEmpty = globals.fEventQHead == NULL;
  
      if (globals.fEventQTail)
          globals.fEventQTail->fNextEvent = evt;
      globals.fEventQTail = evt;
      if (globals.fEventQHead == NULL)
          globals.fEventQHead = evt;
      evt->fNextEvent = NULL;
  
      SkDEBUGCODE(++globals.fEventCounter);
  
      return wasEmpty;
  }
  
  SkEvent* SkEvent::Dequeue() {
      SkEvent_Globals& globals = getGlobals();
      globals.fEventMutex.acquire();
  
      SkEvent* evt = globals.fEventQHead;
      if (evt) {
          SkDEBUGCODE(--globals.fEventCounter);
  
          globals.fEventQHead = evt->fNextEvent;
          if (globals.fEventQHead == NULL) {
              globals.fEventQTail = NULL;
          }
      }
      globals.fEventMutex.release();
  
      return evt;
  }
  
  bool SkEvent::QHasEvents() {
      SkEvent_Globals& globals = getGlobals();
  
      // this is not thread accurate, need a semaphore for that
      return globals.fEventQHead != NULL;
  }
  
  #ifdef SK_TRACE_EVENTS
      static int gDelayDepth;
  #endif
  
  SkMSec SkEvent::EnqueueTime(SkEvent* evt, SkMSec time) {
      SkEvent_Globals& globals = getGlobals();
      //  gEventMutex acquired by caller
  
      SkEvent* curr = globals.fDelayQHead;
      SkEvent* prev = NULL;
  
      while (curr) {
          if (SkMSec_LT(time, curr->fTime)) {
              break;
          }
          prev = curr;
          curr = curr->fNextEvent;
      }
  
      evt->fTime = time;
      evt->fNextEvent = curr;
      if (prev == NULL) {
          globals.fDelayQHead = evt;
      } else {
          prev->fNextEvent = evt;
      }
  
      SkMSec delay = globals.fDelayQHead->fTime - SkTime::GetMSecs();
      if ((int32_t)delay <= 0) {
          delay = 1;
      }
      return delay;
  }
  
  ///////////////////////////////////////////////////////////////////////////////
  
  #include "SkEventSink.h"
  
  bool SkEvent::ProcessEvent() {
      SkEvent*                evt = SkEvent::Dequeue();
      SkAutoTDelete<SkEvent>  autoDelete(evt);
      bool                    again = false;
  
      EVENT_LOGN("ProcessEvent", (int32_t)evt);
  
      if (evt) {
          (void)SkEventSink::DoEvent(*evt);
          again = SkEvent::QHasEvents();
      }
      return again;
  }
  
  void SkEvent::ServiceQueueTimer()
  {
      SkEvent_Globals& globals = getGlobals();
  
      globals.fEventMutex.acquire();
  
      bool        wasEmpty = false;
      SkMSec      now = SkTime::GetMSecs();
      SkEvent*    evt = globals.fDelayQHead;
  
      while (evt)
      {
          if (SkMSec_LT(now, evt->fTime))
              break;
  
  #ifdef SK_TRACE_EVENTS
          --gDelayDepth;
          SkDebugf("dequeue-delay %s (%d)", evt->getType(), gDelayDepth);
          const char* idStr = evt->findString("id");
          if (idStr)
              SkDebugf(" (%s)", idStr);
          SkDebugf("\n");
  #endif
  
          SkEvent* next = evt->fNextEvent;
          if (SkEvent::Enqueue(evt))
              wasEmpty = true;
          evt = next;
      }
      globals.fDelayQHead = evt;
  
      SkMSec time = evt ? evt->fTime - now : 0;
  
      globals.fEventMutex.release();
  
      if (wasEmpty)
          SkEvent::SignalNonEmptyQueue();
  
      SkEvent::SignalQueueTimer(time);
  }
  
  int SkEvent::CountEventsOnQueue() {
      SkEvent_Globals& globals = getGlobals();
      globals.fEventMutex.acquire();
      
      int count = 0;
      const SkEvent* evt = globals.fEventQHead;
      while (evt) {
          count += 1;
          evt = evt->fNextEvent;
      }
      globals.fEventMutex.release();
  
      return count;
  }
  
  ///////////////////////////////////////////////////////////////////////////////
  
  void SkEvent::Init() {}
  
  void SkEvent::Term() {
      SkEvent_Globals& globals = getGlobals();
  
      SkEvent* evt = globals.fEventQHead;
      while (evt) {
          SkEvent* next = evt->fNextEvent;
          delete evt;
          evt = next;
      }
  
      evt = globals.fDelayQHead;
      while (evt) {
          SkEvent* next = evt->fNextEvent;
          delete evt;
          evt = next;
      }
  }
  
  