<commit_before>#include "caffe2/utils/threadpool/ThreadPool.h"
#include "caffe2/core/logging.h"

CAFFE2_DEFINE_bool(caffe2_threadpool_force_inline, false,
                   "Force to always run jobs on the calling thread");

#if CAFFE2_THREADPOOL_MOBILE

namespace caffe2 {

// Default smallest amount of work that will be partitioned between
// multiple threads; the runtime value is configurable
constexpr size_t kDefaultMinWorkSize = 8;

#ifdef CAFFE2_THREADPOOL_MAIN_IMBALANCE
constexpr float kDefaultImbalanceRatio = 1.0f;
#endif

ThreadPool::ThreadPool(int numThreads)
    : fn_(nullptr),
      workItemsPending_(0),
      currentWorkId_(0),
      threadsReady_(0),
      minWorkSize_(kDefaultMinWorkSize)
#ifdef CAFFE2_THREADPOOL_MAIN_IMBALANCE
    , imbalanceRatio_(kDefaultImbalanceRatio)
#endif
{
  std::lock_guard<std::mutex> guard(mutex_);

  // All worker threads (and the main thread) have a ThreadInfo
  for (auto i = 0; i < numThreads; ++i) {
    threadInfo_.emplace_back(
      MakeAligned<ThreadInfo>::make(kCacheLineSize, i, numThreads));
  }

  // The first ThreadInfo is for the main thread
  for (auto i = 1; i < numThreads; ++i) {
    auto pInfo = &(threadInfo_[i]);
    auto fn = [pInfo, this, i]() {
      (*pInfo)->threadMain(i, this);
    };

    threads_.emplace_back(std::thread(std::move(fn)));
  }
}

ThreadPool::~ThreadPool() {
  {
    std::lock_guard<std::mutex> guard(mutex_);
    for (auto& info : threadInfo_) {
      info->wantExit_ = true;
    }
  }

  threadStartMonitor_.notify_all();

  // Wait on all threads to exit
  for (auto& thread : threads_) {
    thread.join();
  }
}

int
ThreadPool::getNumThreads() const {
  std::lock_guard<std::mutex> guard(executionMutex_);

  return threadInfo_.size();
}

  // Sets the minimum work size (range) for which to invoke the
  // threadpool; work sizes smaller than this will just be run on the
  // main (calling) thread
void
ThreadPool::setMinWorkSize(size_t size) {
  std::lock_guard<std::mutex> guard(executionMutex_);

  minWorkSize_ = size;
}

#ifdef CAFFE2_THREADPOOL_MAIN_IMBALANCE
void
ThreadPool::setImbalanceRatio(float ratio) {
  std::lock_guard<std::mutex> guard(executionMutex_);

  imbalanceRatio_ = ratio;
}
#endif

#ifdef CAFFE2_THREADPOOL_STATS
std::vector<ThreadStats>
ThreadPool::getStats(bool reset) {
  std::lock_guard<std::mutex> guard(executionMutex_);

  // Set up thread state
  {
    std::unique_lock<std::mutex> lock(mutex_);

    // We've guaranteed that all threads have finished work for the
    // previous round, but we don't want threads to read new work
    // information out of order. Wait for all of the old threads to
    // check in first
    while (threadsReady_ < threads_.size()) {
      threadReadyMonitor_.wait(lock);
    }

    // The above serves as a barrier to ensure the stats are complete

    std::vector<ThreadStats> stats;
    for (auto& t : threadInfo_) {
      stats.push_back(t->stats_);
      if (reset) {
        t->stats_.reset();
      }
    }

    return stats;
  }
}
#endif

void
ThreadPool::run(const std::function<void(int, size_t)>& fn, size_t range) {
  std::lock_guard<std::mutex> guard(executionMutex_);

  // If there are no worker threads, or if the range is too small (too
  // little work), just run locally
  bool runLocally = threads_.empty() || range < minWorkSize_ ||
                    FLAGS_caffe2_threadpool_force_inline;

  auto numThreads = threadInfo_.size();
  size_t workUnitsPerThread = 0;
  size_t firstThreadWork = 0;
  size_t otherThreadWork = 0;

  if (!runLocally) {
    size_t workUnitsPerThread = (numThreads + range - 1) / numThreads;

    // On mobile devices (especially big.LITTLE cores), there is
    // significant lag in getting other threads to participate versus
    // the current thread, which is likely already running on a big
    // core.
    // Based on tests, the main thread will execute (through its own
    // work and stealing others) about 25% more work than other
    // threads.
    // To reduce the work stealing overhead, give the main thread 25%
    // more work to start with.
#ifdef CAFFE2_THREADPOOL_MAIN_IMBALANCE
    firstThreadWork = (size_t) (imbalanceRatio_ * workUnitsPerThread);
    if (firstThreadWork >= range) {
      // give all to first thread
      runLocally = true;
    }

    size_t remainderWork = range - firstThreadWork;
    otherThreadWork =
      ((numThreads - 1) + remainderWork - 1) / (numThreads - 1);
#else
    firstThreadWork = workUnitsPerThread;
    otherThreadWork = workUnitsPerThread;
#endif
  }

  if (runLocally) {
    // Work is small enough to just run locally; multithread overhead
    // is too high
    for (size_t i = 0; i < range; ++i) {
      fn(0, i);
    }

#ifdef CAFFE2_THREADPOOL_STATS
    // The main thread worked on this directly
    auto& stats = threadInfo_[0]->stats_;
    stats.numWorkedOn += range;
    stats.numAssigned += range;
#endif

    return;
  }

  // Otherwise, all worker threads participate
  // Set up thread state
  {
    std::unique_lock<std::mutex> lock(mutex_);

    // We've guaranteed that all threads have finished work for the
    // previous round, but we don't want threads to read new work
    // information out of order. Wait for all of the old threads to
    // check in first
    while (threadsReady_ < threads_.size()) {
      threadReadyMonitor_.wait(lock);
    }

    // Our threads are ready, and are waiting for us to start them.
    threadsReady_ = 0;

    fn_ = &fn;

    // Work given to main thread
    {
      auto& info = threadInfo_[0];
      info->rangeStart_ = 0;
      // already guaranteed to be within bounds
      info->rangeEnd_ = firstThreadWork;
      info->rangeLength_ = firstThreadWork;
#ifdef CAFFE2_THREADPOOL_STATS
      info->stats_.numAssigned += firstThreadWork;
#endif
    }

    // Work given to other threads
    size_t workStart = firstThreadWork;
    for (size_t i = 1; i < numThreads; ++i) {
      auto& info = threadInfo_[i];

      auto start = std::min(workStart, range);
      auto end = std::min(workStart + otherThreadWork, range);
      auto numAssigned = end - start;
      info->rangeStart_ = start;
      info->rangeEnd_ = end;
      info->rangeLength_ = numAssigned;
#ifdef CAFFE2_THREADPOOL_STATS
      info->stats_.numAssigned += numAssigned;
#endif
      workStart += otherThreadWork;
    }

    workItemsPending_ = range;
    ++currentWorkId_;
  }

  // Wake all worker threads
  threadStartMonitor_.notify_all();

  // We participate as well
  bool done = threadInfo_[0]->runAndSteal(0, this);

  // This thread may have been the one to finish all the work
  if (!done) {
    // Wait until we get signalled back
    {
      std::unique_lock<std::mutex> lock(mutex_);
      while (workItemsPending_.load() > 0) {
        threadDoneMonitor_.wait(lock);
      }
    }
  }
}

void
ThreadInfo::threadMain(int threadId, ThreadPool* pool) {
  long lastProcessedWorkId = 0;

  while (true) {
    {
      // Kick off
      std::unique_lock<std::mutex> lock(pool->mutex_);
      int numAtBarrier = ++(pool->threadsReady_);
      // numThreads includes main thread, we only care about the # of
      // worker threads here
      if (numAtBarrier == (numThreads_ - 1)) {
        pool->threadReadyMonitor_.notify_one();
      }

      // Wait on main to give us new work
      while (!wantExit_ && pool->currentWorkId_ <= lastProcessedWorkId) {
        pool->threadStartMonitor_.wait(lock);
      }

      // Whether or not we actually do some work, this is the new work
      // item we're handling
      lastProcessedWorkId = pool->currentWorkId_;
    }

    if (wantExit_) {
      return;
    }

    bool shouldSignal = runAndSteal(threadId, pool);

    if (shouldSignal) {
      std::lock_guard<std::mutex> guard(pool->mutex_);
      pool->threadDoneMonitor_.notify_one();
    }
  }
}

bool
ThreadInfo::runAndSteal(int threadId, ThreadPool* pool) {
  auto lambdaFunctionToRun = pool->fn_;
  int localItemsCompleted = 0;
  int localItemsStolen = 0;

  /* Process thread's own range of items */
  auto curItem = rangeStart_;
  while (true) {
    auto curRangeLength = --rangeLength_; // atomic

    if (curRangeLength < 0) {
      // someone stole all of our work
      break;
    }

    (*lambdaFunctionToRun)(threadId, curItem);

    ++curItem;
    ++localItemsCompleted;
  }

  // Done, now look for other threads' items to steal
  for (auto i = (threadId_ + 1) % numThreads_;
       i != threadId_;
       i = (i + 1) % numThreads_) {
    auto& otherThread = pool->threadInfo_[i];

    while (true) {
      auto curRangeLength = --(otherThread->rangeLength_); // atomic

      if (curRangeLength < 0) {
        break;
      }

      // We're successfully stealing a work item from the other thread
      auto itemId = --(otherThread->rangeEnd_); // atomic

      (*lambdaFunctionToRun)(threadId, itemId);
      ++localItemsCompleted;
#ifdef CAFFE2_THREADPOOL_STATS
      ++localItemsStolen;
#endif
    }
  }

  bool lastThread = false;

  if (localItemsCompleted > 0) {
    auto numRemaining =
      (pool->workItemsPending_ -= localItemsCompleted); // atomic
    DCHECK_GE(numRemaining, 0);

    if (numRemaining == 0) {
      // We were the last thread to finish all work
      lastThread = true;
    }
  }

#ifdef CAFFE2_THREADPOOL_STATS
  stats_.numWorkedOn += localItemsCompleted;
  stats_.numStolen += localItemsStolen;
#endif

  return lastThread;
}

} // namespace caffe2

#endif // CAFFE2_THREADPOOL_MOBILE
<commit_msg>Revert threadpool minWorkSize change on iOS<commit_after>#include "caffe2/utils/threadpool/ThreadPool.h"
#include "caffe2/core/logging.h"

CAFFE2_DEFINE_bool(caffe2_threadpool_force_inline, false,
                   "Force to always run jobs on the calling thread");

#if CAFFE2_THREADPOOL_MOBILE

namespace caffe2 {

// Default smallest amount of work that will be partitioned between
// multiple threads; the runtime value is configurable
#if CAFFE2_ANDROID
constexpr size_t kDefaultMinWorkSize = 8;
#else
constexpr size_t kDefaultMinWorkSize = 80;
#endif

#ifdef CAFFE2_THREADPOOL_MAIN_IMBALANCE
constexpr float kDefaultImbalanceRatio = 1.0f;
#endif

ThreadPool::ThreadPool(int numThreads)
    : fn_(nullptr),
      workItemsPending_(0),
      currentWorkId_(0),
      threadsReady_(0),
      minWorkSize_(kDefaultMinWorkSize)
#ifdef CAFFE2_THREADPOOL_MAIN_IMBALANCE
    , imbalanceRatio_(kDefaultImbalanceRatio)
#endif
{
  std::lock_guard<std::mutex> guard(mutex_);

  // All worker threads (and the main thread) have a ThreadInfo
  for (auto i = 0; i < numThreads; ++i) {
    threadInfo_.emplace_back(
      MakeAligned<ThreadInfo>::make(kCacheLineSize, i, numThreads));
  }

  // The first ThreadInfo is for the main thread
  for (auto i = 1; i < numThreads; ++i) {
    auto pInfo = &(threadInfo_[i]);
    auto fn = [pInfo, this, i]() {
      (*pInfo)->threadMain(i, this);
    };

    threads_.emplace_back(std::thread(std::move(fn)));
  }
}

ThreadPool::~ThreadPool() {
  {
    std::lock_guard<std::mutex> guard(mutex_);
    for (auto& info : threadInfo_) {
      info->wantExit_ = true;
    }
  }

  threadStartMonitor_.notify_all();

  // Wait on all threads to exit
  for (auto& thread : threads_) {
    thread.join();
  }
}

int
ThreadPool::getNumThreads() const {
  std::lock_guard<std::mutex> guard(executionMutex_);

  return threadInfo_.size();
}

  // Sets the minimum work size (range) for which to invoke the
  // threadpool; work sizes smaller than this will just be run on the
  // main (calling) thread
void
ThreadPool::setMinWorkSize(size_t size) {
  std::lock_guard<std::mutex> guard(executionMutex_);

  minWorkSize_ = size;
}

#ifdef CAFFE2_THREADPOOL_MAIN_IMBALANCE
void
ThreadPool::setImbalanceRatio(float ratio) {
  std::lock_guard<std::mutex> guard(executionMutex_);

  imbalanceRatio_ = ratio;
}
#endif

#ifdef CAFFE2_THREADPOOL_STATS
std::vector<ThreadStats>
ThreadPool::getStats(bool reset) {
  std::lock_guard<std::mutex> guard(executionMutex_);

  // Set up thread state
  {
    std::unique_lock<std::mutex> lock(mutex_);

    // We've guaranteed that all threads have finished work for the
    // previous round, but we don't want threads to read new work
    // information out of order. Wait for all of the old threads to
    // check in first
    while (threadsReady_ < threads_.size()) {
      threadReadyMonitor_.wait(lock);
    }

    // The above serves as a barrier to ensure the stats are complete

    std::vector<ThreadStats> stats;
    for (auto& t : threadInfo_) {
      stats.push_back(t->stats_);
      if (reset) {
        t->stats_.reset();
      }
    }

    return stats;
  }
}
#endif

void
ThreadPool::run(const std::function<void(int, size_t)>& fn, size_t range) {
  std::lock_guard<std::mutex> guard(executionMutex_);

  // If there are no worker threads, or if the range is too small (too
  // little work), just run locally
  bool runLocally = threads_.empty() || range < minWorkSize_ ||
                    FLAGS_caffe2_threadpool_force_inline;

  auto numThreads = threadInfo_.size();
  size_t workUnitsPerThread = 0;
  size_t firstThreadWork = 0;
  size_t otherThreadWork = 0;

  if (!runLocally) {
    size_t workUnitsPerThread = (numThreads + range - 1) / numThreads;

    // On mobile devices (especially big.LITTLE cores), there is
    // significant lag in getting other threads to participate versus
    // the current thread, which is likely already running on a big
    // core.
    // Based on tests, the main thread will execute (through its own
    // work and stealing others) about 25% more work than other
    // threads.
    // To reduce the work stealing overhead, give the main thread 25%
    // more work to start with.
#ifdef CAFFE2_THREADPOOL_MAIN_IMBALANCE
    firstThreadWork = (size_t) (imbalanceRatio_ * workUnitsPerThread);
    if (firstThreadWork >= range) {
      // give all to first thread
      runLocally = true;
    }

    size_t remainderWork = range - firstThreadWork;
    otherThreadWork =
      ((numThreads - 1) + remainderWork - 1) / (numThreads - 1);
#else
    firstThreadWork = workUnitsPerThread;
    otherThreadWork = workUnitsPerThread;
#endif
  }

  if (runLocally) {
    // Work is small enough to just run locally; multithread overhead
    // is too high
    for (size_t i = 0; i < range; ++i) {
      fn(0, i);
    }

#ifdef CAFFE2_THREADPOOL_STATS
    // The main thread worked on this directly
    auto& stats = threadInfo_[0]->stats_;
    stats.numWorkedOn += range;
    stats.numAssigned += range;
#endif

    return;
  }

  // Otherwise, all worker threads participate
  // Set up thread state
  {
    std::unique_lock<std::mutex> lock(mutex_);

    // We've guaranteed that all threads have finished work for the
    // previous round, but we don't want threads to read new work
    // information out of order. Wait for all of the old threads to
    // check in first
    while (threadsReady_ < threads_.size()) {
      threadReadyMonitor_.wait(lock);
    }

    // Our threads are ready, and are waiting for us to start them.
    threadsReady_ = 0;

    fn_ = &fn;

    // Work given to main thread
    {
      auto& info = threadInfo_[0];
      info->rangeStart_ = 0;
      // already guaranteed to be within bounds
      info->rangeEnd_ = firstThreadWork;
      info->rangeLength_ = firstThreadWork;
#ifdef CAFFE2_THREADPOOL_STATS
      info->stats_.numAssigned += firstThreadWork;
#endif
    }

    // Work given to other threads
    size_t workStart = firstThreadWork;
    for (size_t i = 1; i < numThreads; ++i) {
      auto& info = threadInfo_[i];

      auto start = std::min(workStart, range);
      auto end = std::min(workStart + otherThreadWork, range);
      auto numAssigned = end - start;
      info->rangeStart_ = start;
      info->rangeEnd_ = end;
      info->rangeLength_ = numAssigned;
#ifdef CAFFE2_THREADPOOL_STATS
      info->stats_.numAssigned += numAssigned;
#endif
      workStart += otherThreadWork;
    }

    workItemsPending_ = range;
    ++currentWorkId_;
  }

  // Wake all worker threads
  threadStartMonitor_.notify_all();

  // We participate as well
  bool done = threadInfo_[0]->runAndSteal(0, this);

  // This thread may have been the one to finish all the work
  if (!done) {
    // Wait until we get signalled back
    {
      std::unique_lock<std::mutex> lock(mutex_);
      while (workItemsPending_.load() > 0) {
        threadDoneMonitor_.wait(lock);
      }
    }
  }
}

void
ThreadInfo::threadMain(int threadId, ThreadPool* pool) {
  long lastProcessedWorkId = 0;

  while (true) {
    {
      // Kick off
      std::unique_lock<std::mutex> lock(pool->mutex_);
      int numAtBarrier = ++(pool->threadsReady_);
      // numThreads includes main thread, we only care about the # of
      // worker threads here
      if (numAtBarrier == (numThreads_ - 1)) {
        pool->threadReadyMonitor_.notify_one();
      }

      // Wait on main to give us new work
      while (!wantExit_ && pool->currentWorkId_ <= lastProcessedWorkId) {
        pool->threadStartMonitor_.wait(lock);
      }

      // Whether or not we actually do some work, this is the new work
      // item we're handling
      lastProcessedWorkId = pool->currentWorkId_;
    }

    if (wantExit_) {
      return;
    }

    bool shouldSignal = runAndSteal(threadId, pool);

    if (shouldSignal) {
      std::lock_guard<std::mutex> guard(pool->mutex_);
      pool->threadDoneMonitor_.notify_one();
    }
  }
}

bool
ThreadInfo::runAndSteal(int threadId, ThreadPool* pool) {
  auto lambdaFunctionToRun = pool->fn_;
  int localItemsCompleted = 0;
  int localItemsStolen = 0;

  /* Process thread's own range of items */
  auto curItem = rangeStart_;
  while (true) {
    auto curRangeLength = --rangeLength_; // atomic

    if (curRangeLength < 0) {
      // someone stole all of our work
      break;
    }

    (*lambdaFunctionToRun)(threadId, curItem);

    ++curItem;
    ++localItemsCompleted;
  }

  // Done, now look for other threads' items to steal
  for (auto i = (threadId_ + 1) % numThreads_;
       i != threadId_;
       i = (i + 1) % numThreads_) {
    auto& otherThread = pool->threadInfo_[i];

    while (true) {
      auto curRangeLength = --(otherThread->rangeLength_); // atomic

      if (curRangeLength < 0) {
        break;
      }

      // We're successfully stealing a work item from the other thread
      auto itemId = --(otherThread->rangeEnd_); // atomic

      (*lambdaFunctionToRun)(threadId, itemId);
      ++localItemsCompleted;
#ifdef CAFFE2_THREADPOOL_STATS
      ++localItemsStolen;
#endif
    }
  }

  bool lastThread = false;

  if (localItemsCompleted > 0) {
    auto numRemaining =
      (pool->workItemsPending_ -= localItemsCompleted); // atomic
    DCHECK_GE(numRemaining, 0);

    if (numRemaining == 0) {
      // We were the last thread to finish all work
      lastThread = true;
    }
  }

#ifdef CAFFE2_THREADPOOL_STATS
  stats_.numWorkedOn += localItemsCompleted;
  stats_.numStolen += localItemsStolen;
#endif

  return lastThread;
}

} // namespace caffe2

#endif // CAFFE2_THREADPOOL_MOBILE
<|endoftext|>


<commit_before>#include <gtest/gtest.h>

#include "../request.hh"

namespace mimosa
{
  namespace http
  {
    namespace
    {
#define METHOD_TEST(Meth)                               \
      TEST(RequestParser, Meth##Simple)                 \
      {                                                 \
        char str[] = #Meth " / HTTP/1.0\r\n\r\n";       \
        Request rq;                                     \
        EXPECT_EQ(1, rq.parse(str, sizeof (str)));      \
      }

      METHOD_TEST(GET)
      METHOD_TEST(HEAD)
      METHOD_TEST(PUT)
      METHOD_TEST(DELETE)
      METHOD_TEST(TRACE)
      METHOD_TEST(OPTIONS)
      METHOD_TEST(CONNECT)
      METHOD_TEST(PATCH)

      TEST(RequestParser, CheckFields)
      {
        // ASSERT_NE(std::hash<std::string>(std::string("attr0")),
        //           std::hash<std::string>(std::string("attr1")));

        char str[] = "GET / HTTP/1.0\r\n"
          "Host: www.toto.com\r\n"
          "Connection: keep-alive\r\n"
          "Content-Length: 854\r\n"
          "Content-Type: text/*\r\n"
          "Referer: http://tutu.com/hoho%34/?tutu=tata;#anchor\r\n"
          "User-Agent: TomBoy (esapce compatible)\r\n"
          "SomeKey:  SomeValue\r\n"
          "Cookie: attr0; attr01; attr02; attr1=value1; attr2=value2;"
          "        attr3; attr4=value4; attr5=\"xvalue\\o\\\"\"\r\n"
          "Cookie: attr6 = value6 ; attr7 ; attr8 = \" value 8 \" \r\n"
          "\r\n";
        Request rq;
        EXPECT_EQ(true, rq.parse(str, sizeof (str)));
        EXPECT_EQ(rq.host_, "www.toto.com");
        EXPECT_EQ(rq.keep_alive_, true);
        EXPECT_EQ(rq.content_length_, 854);
        EXPECT_EQ(rq.content_type_, "text/*");
        EXPECT_EQ(rq.referrer_, "http://tutu.com/hoho%34/?tutu=tata;#anchor");
        EXPECT_EQ(rq.user_agent_, "TomBoy (esapce compatible)");

        auto it = rq.cookies_.find("attr1");
        EXPECT_NE(it, rq.cookies_.end());
        if (it != rq.cookies_.end())
          EXPECT_EQ(it->second, "value1");
        it = rq.cookies_.find("attr5");
        EXPECT_NE(it, rq.cookies_.end());
        if (it != rq.cookies_.end())
          EXPECT_EQ(it->second, std::string("xvalueo\""));

        it = rq.unparsed_headers_.find("SomeKey");
        EXPECT_NE(it, rq.unparsed_headers_.end());
        if (it != rq.unparsed_headers_.end())
          EXPECT_EQ(it->second, "SomeValue");
      }
    }
  }
}

int main(int argc, char **argv)
{
  ::testing::InitGoogleTest(&argc, argv);
  return RUN_ALL_TESTS();
}
<commit_msg>Updated request parser's unit tests<commit_after>#include <gtest/gtest.h>

#include "../request.hh"

namespace mimosa
{
  namespace http
  {
    namespace
    {
#define METHOD_TEST(Meth)                               \
      TEST(RequestParser, Meth##Simple)                 \
      {                                                 \
        char str[] = #Meth " / HTTP/1.0\r\n\r\n";       \
        Request rq;                                     \
        EXPECT_EQ(1, rq.parse(str, sizeof (str)));      \
      }

      METHOD_TEST(GET)
      METHOD_TEST(HEAD)
      METHOD_TEST(PUT)
      METHOD_TEST(DELETE)
      METHOD_TEST(TRACE)
      METHOD_TEST(OPTIONS)
      METHOD_TEST(CONNECT)
      METHOD_TEST(PATCH)

      TEST(RequestParser, CheckFields)
      {
        // ASSERT_NE(std::hash<std::string>(std::string("attr0")),
        //           std::hash<std::string>(std::string("attr1")));

        char str[] = "GET / HTTP/1.0\r\n"
          "Host: www.toto.com\r\n"
          "Connection: keep-alive\r\n"
          "Content-Length: 854\r\n"
          "Content-Type: text/*\r\n"
          "Referer: http://tutu.com/hoho%34/?tutu=tata;#anchor\r\n"
          "User-Agent: TomBoy (esapce compatible)\r\n"
          "SomeKey:  SomeValue\r\n"
          "Cookie: attr0; attr01; attr02; attr1=value1; attr2=value2;"
          "        attr3; attr4=value4; attr5=\"xvalue\\o\\\"\"\r\n"
          "Cookie: attr6 = value6 ; attr7 ; attr8 = \" value 8 \" \r\n"
          "\r\n";
        Request rq;
        EXPECT_EQ(true, rq.parse(str, sizeof (str)));
        EXPECT_EQ(rq.host_, "www.toto.com");
        EXPECT_EQ(rq.keep_alive_, true);
        EXPECT_EQ(rq.content_length_, 854);
        EXPECT_EQ(rq.content_type_, "text/*");
        EXPECT_EQ(rq.referrer_, "http://tutu.com/hoho%34/?tutu=tata;#anchor");
        EXPECT_EQ(rq.user_agent_, "TomBoy (esapce compatible)");

        auto it = rq.cookies_.find("attr1");
        EXPECT_NE(it, rq.cookies_.end());
        if (it != rq.cookies_.end())
          EXPECT_EQ(it->second, "value1");
        it = rq.cookies_.find("attr5");
        EXPECT_NE(it, rq.cookies_.end());
        if (it != rq.cookies_.end())
          EXPECT_EQ(it->second, std::string("xvalueo\""));

        it = rq.unparsed_headers_.find("SomeKey");
        EXPECT_NE(it, rq.unparsed_headers_.end());
        if (it != rq.unparsed_headers_.end())
          EXPECT_EQ(it->second, "SomeValue");
      }
    }
  }
}
<|endoftext|>


<commit_before>#include "ofApp.h"
#include "ofxBinaryMesh.h"
#include <Alembic/AbcGeom/All.h>
#include <Alembic/AbcCoreHDF5/All.h>
#include "ofxAlembic.h"

using namespace Alembic::AbcGeom;

static string dataPath = "../../../sharedData/";

//void listDirs();

#ifndef NO_ALEMBIC
#include "ofxAlembic.h"
ofxAlembic::Reader abc;
#endif

void ofApp::setup() {

    // THIS IS SUPER WEIRD FOR ME -- adjustments gets NANs on OSX, and weirdness.

    // adjustments.set(0,0,0);
    // adjustGui = new ofxUISuperCanvas("ADJUST", 0,0, 300,500);
    // adjustGui->addMinimalSlider("ADJUST X", -50, 50, &adjustments->x);
    // adjustGui->addMinimalSlider("ADJUST Y", -50, 50, &adjustments->y);
    // adjustGui->addMinimalSlider("ADJUST Z", -50, 50, &adjustments->z);
    // scaleFac = 1.0;
    // adjustGui->addMinimalSlider("SCALE", 0.7, 1.3, &scaleFac);
    // adjustGui->loadSettings("adjustments->xml");

    gui.setup("panel"); // most of the time you don't need a name but don't forget to call setup
    gui.add(adjustments.set("adjustments", ofPoint(0, 0, 0), -ofPoint(200,200,200), ofPoint(200,200,200)));
    gui.add(showWireframe.set("showWireframe", false));
    gui.add(showFilled.set("showFilled", false));
    gui.add(scaleFac.set("scaleFac", 1, 0.1, 2.0));
    gui.loadFromFile("adjustments.xml");

    bSaving = false;

#ifdef TARGET_WIN32
    FDM.setup("../../../sharedData/SH002_Craig_test/");
#else
    FDM.setup("/Users/zachlieberman/Dropbox/+PopTech_Toyota_Footage/SH002_Craig_test/");
#endif

    FDM.loadFrame(0, frame);            // load frame 0
    FDM.loadFrame(0, firstFrame);

	ofSetVerticalSync(true);

	useSideCamera = false;
	currentCamera = &easyCam;

	light.enable();
	light.setPosition(+500, +500, +500);

	string testSequenceFolder = dataPath + "aCam/";

	CCM.loadCalibration(
                        testSequenceFolder + "matrices/rgbCalib.yml",
                        testSequenceFolder + "matrices/depthCalib.yml",
                        testSequenceFolder + "matrices/rotationDepthToRGB.yml",
                        testSequenceFolder + "matrices/translationDepthToRGB.yml");

    int ppWidth = ofNextPow2(CCM.rgbCalibration.getDistortedIntrinsics().getImageSize().width);
    int ppHeight = ofNextPow2(CCM.rgbCalibration.getDistortedIntrinsics().getImageSize().height);

    targetFbo.allocate(CCM.rgbCalibration.getDistortedIntrinsics().getImageSize().width,
                       CCM.rgbCalibration.getDistortedIntrinsics().getImageSize().height,GL_RGBA32F);

    //
    //
    //

//    std::string name = "someOpsXform.abc";
//    OArchive archive( Alembic::AbcCoreHDF5::WriteArchive(), name );
//    TimeSampling Ts(1.0 / 24.0, 0);
//    Alembic::Util::uint32_t tsidx = archive.addTimeSampling(Ts);
//    OXform a( OObject( archive, kTop ), "a");
//    a.getSchema().setTimeSampling(tsidx);
//
//
//    for (int j = 0; j < FDM.numFrames; j++){
//
//        FDM.loadFrame(j, frame);            // load frame 0
//
//        vector<ofVec3f> from;
//        vector<ofVec3f> to;
//        for (int i = 0; i < frame.head.getIndices().size(); i+= 10){
//            to.push_back(frame.head.getVertices()[frame.head.getIndices()[i]]);
//            from.push_back(firstFrame.head.getVertices()[firstFrame.head.getIndices()[i]]);
//        }
//
//        ofMatrix4x4 rigidEstimate = ofxCv::estimateAffine3D(from, to);
//
//        XformSample samp;
//        XformOp matop( kMatrixOperation, kMatrixHint );
//
//
//        ofPoint midPt (0,0,0);
//        ofMesh m = frame.head;
//        for (int i = 0; i < m.getNumIndices(); i++){
//            midPt += m.getVertices()[m.getIndices()[i]];
//        }
//        midPt /= (int)m.getNumIndices();
//        midPt = midPt;
//
//
//
//        rigidEstimate.decompose(decompTranslation, decompRotation, decompScale, decompSo);
//
//        float angle;
//        float x, y,z;
//        decompRotation.getRotate(angle, x, y, z);
//
//        XformOp transop( kTranslateOperation, kTranslateHint );
//        XformOp rotatop( kRotateOperation, kRotateHint );
//        XformOp scaleop( kScaleOperation, kScaleHint );
//        const float *p = rigidEstimate.getPtr();
//        Imath::M44d md = Imath::M44d(p[0], p[1], p[2], p[3],
//                                     p[4], p[5], p[6], p[7],
//                                     p[8], p[9], p[10], p[11],
//                                     p[12], p[13], p[14], p[15]);
//
//        //samp.addOp( XformOp( kMatrixOperation, kMatrixHint ), md );
//
////        mm.glTranslate(ofVec3f(-adjustments->x,adjustments->y,adjustments->z));
////        mm.glTranslate( ofPoint(-midPt.x, midPt.y, midPt.z) + ofPoint(100,100,-100));
////        mm.glScale(-scaleFac,scaleFac,scaleFac);
//        // mm.glScale(1,1,1);
//        //
//        //mm.glRotate(mouseY, 1,0,0);
//        //
//
//        ofMatrix4x4 rot;
//        rot.glRotate(angle, x,y,z);
//
//
//        samp.addOp( transop, V3d(-adjustments->x,adjustments->y,adjustments->z));
//        samp.addOp( transop, V3d(-midPt.x, midPt.y, midPt.z) );
//        //samp.addOp( scaleop, V3d(1.0, 1.0, 1.0) );
//        samp.addOp( rotatop, V3d(x,y,z), angle);
//
//
//        a.getSchema().set(samp);
//    }

    ofxAlembic::Writer writer;

    string path = ofGetTimestampString() + ".abc";
    writer.open(path, 24);

    for (int j = 0; j < FDM.numFrames; j++){

        FDM.loadFrame(j, frame);            // load frame 0
        ofMatrix4x4 mm;
        mm.glScale(-scaleFac,scaleFac,scaleFac);
        mm.glTranslate(ofVec3f(-adjustments->x,adjustments->y,adjustments->z));

        ofMesh temp = frame.head;
        cout << temp.getNumVertices() << endl;
        ofxAlembic::transform(temp, mm);
        writer.addPolyMesh("/head", temp);

        ofNode n;
        FDM.getOrientation(frame, n);

        ofMatrix4x4 mat = n.getGlobalTransformMatrix();
        ofMatrix4x4 newMat =  mat * mm;
        newMat.glScale(-1, 1,1);
        writer.addXform("/box", newMat);
        if (j == 0){
            ofBoxPrimitive box;
            box.set(40);
            writer.addPolyMesh("/box/boxShape", box.getMesh());
        }

        // draw the box of orientation using new alexmbic style

    }

    writer.close();
//

}

void ofApp::update() {

    currentFrame = mouseX;
    if (lastFrame != currentFrame){
        FDM.loadFrame(currentFrame, frame);

        // do the transform estimation:
        vector<ofVec3f> from;
        vector<ofVec3f> to;
        for (int i = 0; i < frame.head.getIndices().size(); i+= 1){
            to.push_back(frame.head.getVertices()[frame.head.getIndices()[i]]);
            from.push_back(firstFrame.head.getVertices()[firstFrame.head.getIndices()[i]]);
        }
        ofMatrix4x4 rigidEstimate = ofxCv::estimateAffine3D(from, to);
        XformSample samp;
        XformOp matop( kMatrixOperation, kMatrixHint );
        rigidEstimate.decompose(decompTranslation, decompRotation, decompScale, decompSo);
        //cout << " ? ? " << decompScale << endl;
    }
    lastFrame = currentFrame;

    if(!useSideCamera){
		currentCamera = &baseCamera;
	}
	else{
		//update camera positions;
		ofVec3f center = baseCamera.screenToWorld( ofPoint(targetFbo.getWidth()/2,targetFbo.getHeight()) / 2 );
		ofVec3f camP = baseCamera.getPosition();

		center = camP + (center - camP).normalize() * 351*2;

		sideCam.setPosition(center + ofVec3f(targetFbo.getWidth(),0,0));
		sideCam.lookAt(center,ofVec3f(0,1,0));

		topCamera.setPosition(center + ofVec3f(0,targetFbo.getHeight(), 0));
		topCamera.lookAt(center,ofVec3f(1,0,0));

	}

}

void ofApp::draw(){

    //void setupScreenPerspective(float width = -1, float height = -1, float fov = 60, float nearDist = 0, float farDist = 0);
	//ofSetupScreen();
#ifndef NO_ALEMBIC
    float t = currentFrame / 24.0;
    if (t > abc.getMaxTime()){
        t = abc.getMaxTime();
    }

	// update alemblic reader with time in sec
	abc.setTime(t);
#endif

	targetFbo.begin();
    ofViewport(ofRectangle(0,0,1920, 1080));

	ofClear(0,0,0,0);
    glClear(GL_DEPTH);

	float videoScale = targetFbo.getWidth() / frame.img.getWidth();

	if(useSideCamera){
		currentCamera->begin();
		ofPushStyle();
		ofPushMatrix();
		ofNoFill();
		ofColor(255,0,0);
        //		ofDrawSphere(depthToRGBTranslation, 10);
		ofNode n;
		n.setPosition(CCM.depthToRGBTranslation);
		n.draw();
		ofColor(0,250,0);
		ofSphere(0,0,0,10);
		ofFill();
		ofSetColor(255,0,0);
		if(ofGetKeyPressed('m')){
			ofMultMatrix(CCM.extrinsics);
		}
		ofSetLineWidth(5);
		ofLine(ofVec3f(0,0,0), ofVec3f(0,0,-100));
		ofPopMatrix();
		ofPopStyle();
        ofEnableDepthTest();
	}
	else{
		ofVec3f camPos(0,0,0);
		camPos = CCM.extrinsics * camPos;
		if(ofGetKeyPressed('m')){
			baseCamera.setPosition( ofVec3f(0,0,0) );
			baseCamera.lookAt(ofVec3f(0,0,-1));
		}
		else{
			baseCamera.setTransformMatrix(CCM.extrinsics);
		}
		baseCamera.setFov( CCM.rgbCalibration.getDistortedIntrinsics().getFov().y );
		baseCamera.begin(ofRectangle(0,0,1920, 1080));
        ofEnableDepthTest();
	}

    ofPoint a,b,c,d, e;

    a = baseCamera.screenToWorld( ofPoint(0,targetFbo.getHeight()));
    b = baseCamera.screenToWorld( ofPoint(targetFbo.getWidth(),targetFbo.getHeight()));
    c = baseCamera.screenToWorld( ofPoint(0,0));
    d = baseCamera.screenToWorld( ofPoint(targetFbo.getWidth(),0));
    e = baseCamera.screenToWorld( ofPoint(targetFbo.getWidth(),targetFbo.getHeight()) / 2.0  + ofPoint(50,-50));

    ofPoint camP = baseCamera.getPosition();

    a = camP + (a - camP).normalize() * 351*5;
    b = camP + (b - camP).normalize() * 351*5;
    c = camP + (c - camP).normalize() * 351*5;
    d = camP + (d - camP).normalize() * 351*5;
    e = camP + (e - camP).normalize() * 351*4;

    // figure out a Z distance.
    frame.img.bind();
    ofMesh mesh;
    mesh.setMode(OF_PRIMITIVE_TRIANGLE_STRIP);
    mesh.addVertex( a) ;
    mesh.addTexCoord(   ofPoint(0,frame.img.getHeight()));
    mesh.addVertex(  b) ;
    mesh.addTexCoord(   ofPoint(frame.img.getWidth(),frame.img.getHeight()));
    mesh.addVertex(  c) ;
    mesh.addTexCoord(   ofPoint(0,0));
    mesh.addVertex(  d ) ;
    mesh.addTexCoord(   ofPoint(frame.img.getWidth(), 0));
    mesh.draw();
    frame.img.unbind();

    ofLine( baseCamera.getPosition(), a);
    ofLine( baseCamera.getPosition(), b);
    ofLine( baseCamera.getPosition(), c);
    ofLine( baseCamera.getPosition(), d);

    ofPushMatrix();
    ofScale(-scaleFac,scaleFac,scaleFac);
	ofTranslate(ofVec3f(-adjustments->x,adjustments->y,adjustments->z));

	drawMesh(frame.head, ofColor::darkGoldenRod);
	drawMesh(frame.rightEye, ofColor::red);
	drawMesh(frame.leftEye, ofColor::blue);

	ofPushStyle();

	//ofScale(10,10,10);
	ofNoFill();
	ofNode n;
	FDM.getOrientation(frame, n);
	n.draw();
	ofSetColor(255);

    ofPushMatrix();

    ofMatrix4x4 mat = n.getGlobalTransformMatrix();
    ofMultMatrix(mat);
    ofBoxPrimitive(100, 100, 100).draw();
    ofPopMatrix();

	ofPopStyle();
	ofPopMatrix();

#ifndef NO_ALEMBIC
    vector<ofPolyline> curvesMe;
    abc.get("SplineSpline", curvesMe);
#endif

    ofSetColor(ofColor::white);

	////////////////

	ofDisableDepthTest();

	if(useSideCamera){
		currentCamera->end();
	}
	else{
		baseCamera.end();
	}

    ofEnableAlphaBlending();

	targetFbo.end();

    targetFbo.getTextureReference().drawSubsection(0, 0, 1920/2, 1080/2, 0, targetFbo.getHeight() - 1080, 1920, 1080);

    gui.draw();

}

void ofApp::drawMesh(ofMesh& m, ofFloatColor color){
	if(showFilled){
		ofSetColor(color);
		ofEnableDepthTest();
		ofEnableLighting();
		m.draw();
	}
	if(showWireframe){
		ofDisableLighting();
		ofSetColor(0);
		glDepthFunc(GL_LEQUAL);
		m.drawWireframe();
	}
	glDepthFunc(GL_LESS);
    ofDisableLighting();
}

void ofApp::exit(){
	//adjustGui->saveSettings("adjustments->xml");
}

void ofApp::keyPressed(ofKeyEventArgs& args){
	if(args.key == ' '){
		useSideCamera = !useSideCamera;
        if (useSideCamera){
            currentCamera == &easyCam;
        }
	}

	if(useSideCamera){
		if(args.key == OF_KEY_LEFT){
			if(currentCamera == &easyCam){
				currentCamera = &sideCam;
			}
			else if(currentCamera == &sideCam){
				currentCamera = &topCamera;
			}
			else {
				currentCamera = &easyCam;
			}
		}
		else if(args.key == OF_KEY_RIGHT){
			if(currentCamera == &easyCam){
				currentCamera = &topCamera;
			}
			else if(currentCamera == &topCamera){
				currentCamera = &sideCam;
			}
			else {
				currentCamera = &easyCam;
			}
		}
	}

    if (args.key == 's'){
        gui.saveToFile("adjustments.xml");
    }

}
<commit_msg>comment out big save at start<commit_after>#include "ofApp.h"
#include "ofxBinaryMesh.h"
#include <Alembic/AbcGeom/All.h>
#include <Alembic/AbcCoreHDF5/All.h>
#include "ofxAlembic.h"

using namespace Alembic::AbcGeom;

static string dataPath = "../../../sharedData/";

//void listDirs();

#ifndef NO_ALEMBIC
#include "ofxAlembic.h"
ofxAlembic::Reader abc;
#endif

void ofApp::setup() {

    // THIS IS SUPER WEIRD FOR ME -- adjustments gets NANs on OSX, and weirdness.

    // adjustments.set(0,0,0);
    // adjustGui = new ofxUISuperCanvas("ADJUST", 0,0, 300,500);
    // adjustGui->addMinimalSlider("ADJUST X", -50, 50, &adjustments->x);
    // adjustGui->addMinimalSlider("ADJUST Y", -50, 50, &adjustments->y);
    // adjustGui->addMinimalSlider("ADJUST Z", -50, 50, &adjustments->z);
    // scaleFac = 1.0;
    // adjustGui->addMinimalSlider("SCALE", 0.7, 1.3, &scaleFac);
    // adjustGui->loadSettings("adjustments->xml");

    gui.setup("panel"); // most of the time you don't need a name but don't forget to call setup
    gui.add(adjustments.set("adjustments", ofPoint(0, 0, 0), -ofPoint(200,200,200), ofPoint(200,200,200)));
    gui.add(showWireframe.set("showWireframe", false));
    gui.add(showFilled.set("showFilled", false));
    gui.add(scaleFac.set("scaleFac", 1, 0.1, 2.0));
    gui.loadFromFile("adjustments.xml");

    bSaving = false;

#ifdef TARGET_WIN32
    FDM.setup("../../../sharedData/SH002_Craig_test/");
#else
    FDM.setup("/Users/zachlieberman/Dropbox/+PopTech_Toyota_Footage/SH002_Craig_test/");
#endif

    FDM.loadFrame(0, frame);            // load frame 0
    FDM.loadFrame(0, firstFrame);

	ofSetVerticalSync(true);

	useSideCamera = false;
	currentCamera = &easyCam;

	light.enable();
	light.setPosition(+500, +500, +500);

	string testSequenceFolder = dataPath + "aCam/";

	CCM.loadCalibration(
                        testSequenceFolder + "matrices/rgbCalib.yml",
                        testSequenceFolder + "matrices/depthCalib.yml",
                        testSequenceFolder + "matrices/rotationDepthToRGB.yml",
                        testSequenceFolder + "matrices/translationDepthToRGB.yml");

    int ppWidth = ofNextPow2(CCM.rgbCalibration.getDistortedIntrinsics().getImageSize().width);
    int ppHeight = ofNextPow2(CCM.rgbCalibration.getDistortedIntrinsics().getImageSize().height);

    targetFbo.allocate(CCM.rgbCalibration.getDistortedIntrinsics().getImageSize().width,
                       CCM.rgbCalibration.getDistortedIntrinsics().getImageSize().height,GL_RGBA32F);

    // save head and tranform

//    ofxAlembic::Writer writer;
//
//
//    string path = ofGetTimestampString() + ".abc";
//    writer.open(path, 24);
//
//    for (int j = 0; j < FDM.numFrames; j++){
//
//        FDM.loadFrame(j, frame);            // load frame 0
//        ofMatrix4x4 mm;
//        mm.glScale(-scaleFac,scaleFac,scaleFac);
//        mm.glTranslate(ofVec3f(-adjustments->x,adjustments->y,adjustments->z));
//
//        ofMesh temp = frame.head;
//        cout << temp.getNumVertices() << endl;
//        ofxAlembic::transform(temp, mm);
//        writer.addPolyMesh("/head", temp);
//
//        ofNode n;
//        FDM.getOrientation(frame, n);
//
//        ofMatrix4x4 mat = n.getGlobalTransformMatrix();
//        ofMatrix4x4 newMat =  mat * mm;
//        newMat.glScale(-1, 1,1);
//        writer.addXform("/box", newMat);
//        if (j == 0){
//            ofBoxPrimitive box;
//            box.set(40);
//            writer.addPolyMesh("/box/boxShape", box.getMesh());
//        }
//
//        // draw the box of orientation using new alexmbic style
//
//
//    }
//
//    writer.close();
//

}

void ofApp::update() {

    currentFrame = mouseX;
    if (lastFrame != currentFrame){
        FDM.loadFrame(currentFrame, frame);

        // do the transform estimation:
        vector<ofVec3f> from;
        vector<ofVec3f> to;
        for (int i = 0; i < frame.head.getIndices().size(); i+= 1){
            to.push_back(frame.head.getVertices()[frame.head.getIndices()[i]]);
            from.push_back(firstFrame.head.getVertices()[firstFrame.head.getIndices()[i]]);
        }
        ofMatrix4x4 rigidEstimate = ofxCv::estimateAffine3D(from, to);
        XformSample samp;
        XformOp matop( kMatrixOperation, kMatrixHint );
        rigidEstimate.decompose(decompTranslation, decompRotation, decompScale, decompSo);
        //cout << " ? ? " << decompScale << endl;
    }
    lastFrame = currentFrame;

    if(!useSideCamera){
		currentCamera = &baseCamera;
	}
	else{
		//update camera positions;
		ofVec3f center = baseCamera.screenToWorld( ofPoint(targetFbo.getWidth()/2,targetFbo.getHeight()) / 2 );
		ofVec3f camP = baseCamera.getPosition();

		center = camP + (center - camP).normalize() * 351*2;

		sideCam.setPosition(center + ofVec3f(targetFbo.getWidth(),0,0));
		sideCam.lookAt(center,ofVec3f(0,1,0));

		topCamera.setPosition(center + ofVec3f(0,targetFbo.getHeight(), 0));
		topCamera.lookAt(center,ofVec3f(1,0,0));

	}

}

void ofApp::draw(){

    //void setupScreenPerspective(float width = -1, float height = -1, float fov = 60, float nearDist = 0, float farDist = 0);
	//ofSetupScreen();
#ifndef NO_ALEMBIC
    float t = currentFrame / 24.0;
    if (t > abc.getMaxTime()){
        t = abc.getMaxTime();
    }

	// update alemblic reader with time in sec
	abc.setTime(t);
#endif

	targetFbo.begin();
    ofViewport(ofRectangle(0,0,1920, 1080));

	ofClear(0,0,0,0);
    glClear(GL_DEPTH);

	float videoScale = targetFbo.getWidth() / frame.img.getWidth();

	if(useSideCamera){
		currentCamera->begin();
		ofPushStyle();
		ofPushMatrix();
		ofNoFill();
		ofColor(255,0,0);
        //		ofDrawSphere(depthToRGBTranslation, 10);
		ofNode n;
		n.setPosition(CCM.depthToRGBTranslation);
		n.draw();
		ofColor(0,250,0);
		ofSphere(0,0,0,10);
		ofFill();
		ofSetColor(255,0,0);
		if(ofGetKeyPressed('m')){
			ofMultMatrix(CCM.extrinsics);
		}
		ofSetLineWidth(5);
		ofLine(ofVec3f(0,0,0), ofVec3f(0,0,-100));
		ofPopMatrix();
		ofPopStyle();
        ofEnableDepthTest();
	}
	else{
		ofVec3f camPos(0,0,0);
		camPos = CCM.extrinsics * camPos;
		if(ofGetKeyPressed('m')){
			baseCamera.setPosition( ofVec3f(0,0,0) );
			baseCamera.lookAt(ofVec3f(0,0,-1));
		}
		else{
			baseCamera.setTransformMatrix(CCM.extrinsics);
		}
		baseCamera.setFov( CCM.rgbCalibration.getDistortedIntrinsics().getFov().y );
		baseCamera.begin(ofRectangle(0,0,1920, 1080));
        ofEnableDepthTest();
	}

    ofPoint a,b,c,d, e;

    a = baseCamera.screenToWorld( ofPoint(0,targetFbo.getHeight()));
    b = baseCamera.screenToWorld( ofPoint(targetFbo.getWidth(),targetFbo.getHeight()));
    c = baseCamera.screenToWorld( ofPoint(0,0));
    d = baseCamera.screenToWorld( ofPoint(targetFbo.getWidth(),0));
    e = baseCamera.screenToWorld( ofPoint(targetFbo.getWidth(),targetFbo.getHeight()) / 2.0  + ofPoint(50,-50));

    ofPoint camP = baseCamera.getPosition();

    a = camP + (a - camP).normalize() * 351*5;
    b = camP + (b - camP).normalize() * 351*5;
    c = camP + (c - camP).normalize() * 351*5;
    d = camP + (d - camP).normalize() * 351*5;
    e = camP + (e - camP).normalize() * 351*4;

    // figure out a Z distance.
    frame.img.bind();
    ofMesh mesh;
    mesh.setMode(OF_PRIMITIVE_TRIANGLE_STRIP);
    mesh.addVertex( a) ;
    mesh.addTexCoord(   ofPoint(0,frame.img.getHeight()));
    mesh.addVertex(  b) ;
    mesh.addTexCoord(   ofPoint(frame.img.getWidth(),frame.img.getHeight()));
    mesh.addVertex(  c) ;
    mesh.addTexCoord(   ofPoint(0,0));
    mesh.addVertex(  d ) ;
    mesh.addTexCoord(   ofPoint(frame.img.getWidth(), 0));
    mesh.draw();
    frame.img.unbind();

    ofLine( baseCamera.getPosition(), a);
    ofLine( baseCamera.getPosition(), b);
    ofLine( baseCamera.getPosition(), c);
    ofLine( baseCamera.getPosition(), d);

    ofPushMatrix();
    ofScale(-scaleFac,scaleFac,scaleFac);
	ofTranslate(ofVec3f(-adjustments->x,adjustments->y,adjustments->z));

	drawMesh(frame.head, ofColor::darkGoldenRod);
	drawMesh(frame.rightEye, ofColor::red);
	drawMesh(frame.leftEye, ofColor::blue);

	ofPushStyle();

	//ofScale(10,10,10);
	ofNoFill();
	ofNode n;
	FDM.getOrientation(frame, n);
	n.draw();
	ofSetColor(255);

    ofPushMatrix();

    ofMatrix4x4 mat = n.getGlobalTransformMatrix();
    ofMultMatrix(mat);
    ofBoxPrimitive(100, 100, 100).draw();
    ofPopMatrix();

	ofPopStyle();
	ofPopMatrix();

#ifndef NO_ALEMBIC
    vector<ofPolyline> curvesMe;
    abc.get("SplineSpline", curvesMe);
#endif

    ofSetColor(ofColor::white);

	////////////////

	ofDisableDepthTest();

	if(useSideCamera){
		currentCamera->end();
	}
	else{
		baseCamera.end();
	}

    ofEnableAlphaBlending();

	targetFbo.end();

    targetFbo.getTextureReference().drawSubsection(0, 0, 1920/2, 1080/2, 0, targetFbo.getHeight() - 1080, 1920, 1080);

    gui.draw();

}

void ofApp::drawMesh(ofMesh& m, ofFloatColor color){
	if(showFilled){
		ofSetColor(color);
		ofEnableDepthTest();
		ofEnableLighting();
		m.draw();
	}
	if(showWireframe){
		ofDisableLighting();
		ofSetColor(0);
		glDepthFunc(GL_LEQUAL);
		m.drawWireframe();
	}
	glDepthFunc(GL_LESS);
    ofDisableLighting();
}

void ofApp::exit(){
	//adjustGui->saveSettings("adjustments->xml");
}

void ofApp::keyPressed(ofKeyEventArgs& args){
	if(args.key == ' '){
		useSideCamera = !useSideCamera;
        if (useSideCamera){
            currentCamera == &easyCam;
        }
	}

	if(useSideCamera){
		if(args.key == OF_KEY_LEFT){
			if(currentCamera == &easyCam){
				currentCamera = &sideCam;
			}
			else if(currentCamera == &sideCam){
				currentCamera = &topCamera;
			}
			else {
				currentCamera = &easyCam;
			}
		}
		else if(args.key == OF_KEY_RIGHT){
			if(currentCamera == &easyCam){
				currentCamera = &topCamera;
			}
			else if(currentCamera == &topCamera){
				currentCamera = &sideCam;
			}
			else {
				currentCamera = &easyCam;
			}
		}
	}

    if (args.key == 's'){
        gui.saveToFile("adjustments.xml");
    }

}
<|endoftext|>


<commit_before>// Copyright (c) 2012 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include "ui/views/controls/webview/webview.h"

#include "content/public/browser/browser_context.h"
#include "content/public/browser/notification_details.h"
#include "content/public/browser/notification_registrar.h"
#include "content/public/browser/notification_source.h"
#include "content/public/browser/notification_types.h"
#include "content/public/browser/render_view_host.h"
#include "content/public/browser/render_widget_host_view.h"
#include "ipc/ipc_message.h"
#include "ui/base/accessibility/accessible_view_state.h"
#include "ui/base/accessibility/accessibility_types.h"
#include "ui/views/controls/native/native_view_host.h"
#include "ui/views/focus/focus_manager.h"

namespace views {

////////////////////////////////////////////////////////////////////////////////
// WebView, public:

WebView::WebView(content::BrowserContext* browser_context)
    : wcv_holder_(new NativeViewHost),
      web_contents_(NULL),
      browser_context_(browser_context) {
  AddChildView(wcv_holder_);
}

WebView::~WebView() {
}

content::WebContents* WebView::GetWebContents() {
  if (!web_contents_) {
    wc_owner_.reset(content::WebContents::Create(browser_context_,
                                                 NULL,
                                                 MSG_ROUTING_NONE,
                                                 NULL,
                                                 NULL));
    web_contents_ = wc_owner_.get();
    web_contents_->SetDelegate(this);
    AttachWebContents();
  }
  return web_contents_;
}

void WebView::SetWebContents(content::WebContents* web_contents) {
  if (web_contents == web_contents_)
    return;
  DetachWebContents();
  wc_owner_.reset();
  web_contents_ = web_contents;
  AttachWebContents();
}

void WebView::SetFastResize(bool fast_resize) {
  wcv_holder_->set_fast_resize(fast_resize);
}

void WebView::OnWebContentsFocused(content::WebContents* web_contents) {
  DCHECK(web_contents == web_contents_);
  FocusManager* focus_manager = GetFocusManager();
  if (focus_manager)
    focus_manager->SetFocusedView(this);
}

////////////////////////////////////////////////////////////////////////////////
// WebView, View overrides:

void WebView::OnBoundsChanged(const gfx::Rect& previous_bounds) {
  wcv_holder_->SetSize(bounds().size());
}

void WebView::ViewHierarchyChanged(bool is_add, View* parent, View* child) {
  if (is_add)
    AttachWebContents();
}

bool WebView::SkipDefaultKeyEventProcessing(const views::KeyEvent& event) {
  // Don't look-up accelerators or tab-traversal if we are showing a non-crashed
  // TabContents.
  // We'll first give the page a chance to process the key events.  If it does
  // not process them, they'll be returned to us and we'll treat them as
  // accelerators then.
  return web_contents_ && !web_contents_->IsCrashed();
}

bool WebView::IsFocusable() const {
  // We need to be focusable when our contents is not a view hierarchy, as
  // clicking on the contents needs to focus us.
  return !!web_contents_;
}

void WebView::OnFocus() {
  if (web_contents_)
    web_contents_->Focus();
}

void WebView::AboutToRequestFocusFromTabTraversal(bool reverse) {
  if (web_contents_)
    web_contents_->FocusThroughTabTraversal(reverse);
}

void WebView::GetAccessibleState(ui::AccessibleViewState* state) {
  state->role = ui::AccessibilityTypes::ROLE_GROUPING;
}

gfx::NativeViewAccessible WebView::GetNativeViewAccessible() {
  if (web_contents_) {
    content::RenderWidgetHostView* host_view =
        web_contents_->GetRenderWidgetHostView();
    if (host_view)
      return host_view->GetNativeViewAccessible();
  }
  return View::GetNativeViewAccessible();
}

////////////////////////////////////////////////////////////////////////////////
// WebView, content::NotificationObserver implementation:

void WebView::Observe(int type,
                      const content::NotificationSource& source,
                      const content::NotificationDetails& details) {
  if (type == content::NOTIFICATION_RENDER_VIEW_HOST_CHANGED) {
    std::pair<content::RenderViewHost*, content::RenderViewHost*>*
        switched_details =
            content::Details<std::pair<content::RenderViewHost*,
                                       content::RenderViewHost*> >(
                details).ptr();
    RenderViewHostChanged(switched_details->first,
                          switched_details->second);
  } else if (type == content::NOTIFICATION_WEB_CONTENTS_DESTROYED) {
    WebContentsDestroyed(content::Source<content::WebContents>(source).ptr());
  } else {
    NOTREACHED();
  }
}

////////////////////////////////////////////////////////////////////////////////
// WebView, content::WebContentsDelegate implementation:

void WebView::WebContentsFocused(content::WebContents* web_contents) {
  DCHECK(wc_owner_.get());
  // The WebView is only the delegate of WebContentses it creates itself.
  WebContentsFocused(web_contents_);
}

////////////////////////////////////////////////////////////////////////////////
// WebView, private:

void WebView::AttachWebContents() {
  // Prevents attachment if the WebView isn't already in a Widget, or it's
  // already attached.
  if (!GetWidget() || !web_contents_ ||
      wcv_holder_->native_view() == web_contents_->GetNativeView()) {
    return;
  }

  if (web_contents_) {
    wcv_holder_->Attach(web_contents_->GetNativeView());

    registrar_.Add(
        this,
        content::NOTIFICATION_RENDER_VIEW_HOST_CHANGED,
        content::Source<content::NavigationController>(
            &web_contents_->GetController()));
    registrar_.Add(
        this,
        content::NOTIFICATION_WEB_CONTENTS_DESTROYED,
        content::Source<content::WebContents>(web_contents_));
  }
}

void WebView::DetachWebContents() {
  if (web_contents_) {
    wcv_holder_->Detach();
#if defined(OS_WIN) && !defined(USE_AURA)
    // TODO(beng): This should either not be necessary, or be done implicitly by
    //             NativeViewHostWin on Detach(). As it stands, this is needed
    //             so that the view of the detached contents knows to tell the
    //             renderer its been hidden.
    ShowWindow(web_contents_->GetNativeView(), SW_HIDE);
#endif
  }
  registrar_.RemoveAll();
}

void WebView::RenderViewHostChanged(content::RenderViewHost* old_host,
                                    content::RenderViewHost* new_host) {
  if (GetFocusManager()->GetFocusedView() == this)
    web_contents_->Focus();
}

void WebView::WebContentsDestroyed(content::WebContents* web_contents) {
  DCHECK(web_contents == web_contents_);
  SetWebContents(NULL);
}

}  // namespace views
<commit_msg>Remove a dcheck I just added since I apparently don't understand the various circumstances that result in this function being called.<commit_after>// Copyright (c) 2012 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include "ui/views/controls/webview/webview.h"

#include "content/public/browser/browser_context.h"
#include "content/public/browser/notification_details.h"
#include "content/public/browser/notification_registrar.h"
#include "content/public/browser/notification_source.h"
#include "content/public/browser/notification_types.h"
#include "content/public/browser/render_view_host.h"
#include "content/public/browser/render_widget_host_view.h"
#include "ipc/ipc_message.h"
#include "ui/base/accessibility/accessible_view_state.h"
#include "ui/base/accessibility/accessibility_types.h"
#include "ui/views/controls/native/native_view_host.h"
#include "ui/views/focus/focus_manager.h"

namespace views {

////////////////////////////////////////////////////////////////////////////////
// WebView, public:

WebView::WebView(content::BrowserContext* browser_context)
    : wcv_holder_(new NativeViewHost),
      web_contents_(NULL),
      browser_context_(browser_context) {
  AddChildView(wcv_holder_);
}

WebView::~WebView() {
}

content::WebContents* WebView::GetWebContents() {
  if (!web_contents_) {
    wc_owner_.reset(content::WebContents::Create(browser_context_,
                                                 NULL,
                                                 MSG_ROUTING_NONE,
                                                 NULL,
                                                 NULL));
    web_contents_ = wc_owner_.get();
    web_contents_->SetDelegate(this);
    AttachWebContents();
  }
  return web_contents_;
}

void WebView::SetWebContents(content::WebContents* web_contents) {
  if (web_contents == web_contents_)
    return;
  DetachWebContents();
  wc_owner_.reset();
  web_contents_ = web_contents;
  AttachWebContents();
}

void WebView::SetFastResize(bool fast_resize) {
  wcv_holder_->set_fast_resize(fast_resize);
}

void WebView::OnWebContentsFocused(content::WebContents* web_contents) {
  FocusManager* focus_manager = GetFocusManager();
  if (focus_manager)
    focus_manager->SetFocusedView(this);
}

////////////////////////////////////////////////////////////////////////////////
// WebView, View overrides:

void WebView::OnBoundsChanged(const gfx::Rect& previous_bounds) {
  wcv_holder_->SetSize(bounds().size());
}

void WebView::ViewHierarchyChanged(bool is_add, View* parent, View* child) {
  if (is_add)
    AttachWebContents();
}

bool WebView::SkipDefaultKeyEventProcessing(const views::KeyEvent& event) {
  // Don't look-up accelerators or tab-traversal if we are showing a non-crashed
  // TabContents.
  // We'll first give the page a chance to process the key events.  If it does
  // not process them, they'll be returned to us and we'll treat them as
  // accelerators then.
  return web_contents_ && !web_contents_->IsCrashed();
}

bool WebView::IsFocusable() const {
  // We need to be focusable when our contents is not a view hierarchy, as
  // clicking on the contents needs to focus us.
  return !!web_contents_;
}

void WebView::OnFocus() {
  if (web_contents_)
    web_contents_->Focus();
}

void WebView::AboutToRequestFocusFromTabTraversal(bool reverse) {
  if (web_contents_)
    web_contents_->FocusThroughTabTraversal(reverse);
}

void WebView::GetAccessibleState(ui::AccessibleViewState* state) {
  state->role = ui::AccessibilityTypes::ROLE_GROUPING;
}

gfx::NativeViewAccessible WebView::GetNativeViewAccessible() {
  if (web_contents_) {
    content::RenderWidgetHostView* host_view =
        web_contents_->GetRenderWidgetHostView();
    if (host_view)
      return host_view->GetNativeViewAccessible();
  }
  return View::GetNativeViewAccessible();
}

////////////////////////////////////////////////////////////////////////////////
// WebView, content::NotificationObserver implementation:

void WebView::Observe(int type,
                      const content::NotificationSource& source,
                      const content::NotificationDetails& details) {
  if (type == content::NOTIFICATION_RENDER_VIEW_HOST_CHANGED) {
    std::pair<content::RenderViewHost*, content::RenderViewHost*>*
        switched_details =
            content::Details<std::pair<content::RenderViewHost*,
                                       content::RenderViewHost*> >(
                details).ptr();
    RenderViewHostChanged(switched_details->first,
                          switched_details->second);
  } else if (type == content::NOTIFICATION_WEB_CONTENTS_DESTROYED) {
    WebContentsDestroyed(content::Source<content::WebContents>(source).ptr());
  } else {
    NOTREACHED();
  }
}

////////////////////////////////////////////////////////////////////////////////
// WebView, content::WebContentsDelegate implementation:

void WebView::WebContentsFocused(content::WebContents* web_contents) {
  DCHECK(wc_owner_.get());
  // The WebView is only the delegate of WebContentses it creates itself.
  WebContentsFocused(web_contents_);
}

////////////////////////////////////////////////////////////////////////////////
// WebView, private:

void WebView::AttachWebContents() {
  // Prevents attachment if the WebView isn't already in a Widget, or it's
  // already attached.
  if (!GetWidget() || !web_contents_ ||
      wcv_holder_->native_view() == web_contents_->GetNativeView()) {
    return;
  }

  if (web_contents_) {
    wcv_holder_->Attach(web_contents_->GetNativeView());

    registrar_.Add(
        this,
        content::NOTIFICATION_RENDER_VIEW_HOST_CHANGED,
        content::Source<content::NavigationController>(
            &web_contents_->GetController()));
    registrar_.Add(
        this,
        content::NOTIFICATION_WEB_CONTENTS_DESTROYED,
        content::Source<content::WebContents>(web_contents_));
  }
}

void WebView::DetachWebContents() {
  if (web_contents_) {
    wcv_holder_->Detach();
#if defined(OS_WIN) && !defined(USE_AURA)
    // TODO(beng): This should either not be necessary, or be done implicitly by
    //             NativeViewHostWin on Detach(). As it stands, this is needed
    //             so that the view of the detached contents knows to tell the
    //             renderer its been hidden.
    ShowWindow(web_contents_->GetNativeView(), SW_HIDE);
#endif
  }
  registrar_.RemoveAll();
}

void WebView::RenderViewHostChanged(content::RenderViewHost* old_host,
                                    content::RenderViewHost* new_host) {
  if (GetFocusManager()->GetFocusedView() == this)
    web_contents_->Focus();
}

void WebView::WebContentsDestroyed(content::WebContents* web_contents) {
  DCHECK(web_contents == web_contents_);
  SetWebContents(NULL);
}

}  // namespace views
<|endoftext|>


<commit_before>/*  This file is part of the KDE project.

    Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).

    This library is free software: you can redistribute it and/or modify
    it under the terms of the GNU Lesser General Public License as published by
    the Free Software Foundation, either version 2.1 or 3 of the License.

    This library is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU Lesser General Public License for more details.

    You should have received a copy of the GNU Lesser General Public License
    along with this library.  If not, see <http://www.gnu.org/licenses/>.
*/

#include "common.h"
#include "backend.h"
#include "audiooutput.h"
#include "audiodataoutput.h"
#include "audioeffect.h"
#include "mediaobject.h"
#include "videowidget.h"
#include "devicemanager.h"
#include "effectmanager.h"
#include "message.h"
#include "volumefadereffect.h"
#include <gst/interfaces/propertyprobe.h>

#include <QtCore/QSet>
#include <QtCore/QVariant>
#include <QtCore/QtPlugin>

QT_BEGIN_NAMESPACE

Q_EXPORT_PLUGIN2(phonon_gstreamer, Phonon::Gstreamer::Backend)

namespace Phonon
{
namespace Gstreamer
{

class MediaNode;

Backend::Backend(QObject *parent, const QVariantList &)
        : QObject(parent)
        , m_deviceManager(0)
        , m_effectManager(0)
        , m_debugLevel(Warning)
        , m_isValid(false)
{
    GError *err = 0;
    bool wasInit = gst_init_check(0, 0, &err);  //init gstreamer: must be called before any gst-related functions
    if (err)
        g_error_free(err);

    qRegisterMetaType<Message>("Message");

    setProperty("identifier",     QLatin1String("phonon_gstreamer"));
    setProperty("backendName",    QLatin1String("Gstreamer"));
    setProperty("backendComment", QLatin1String("Gstreamer plugin for Phonon"));
    setProperty("backendVersion", QLatin1String("0.2"));
    setProperty("backendWebsite", QLatin1String("http://qtsoftware.com/"));

    //check if we should enable debug output
    QString debugLevelString = qgetenv("PHONON_GST_DEBUG");
    int debugLevel = debugLevelString.toInt();
    if (debugLevel > 3) //3 is maximum
        debugLevel = 3;
    m_debugLevel = (DebugLevel)debugLevel;

    if (wasInit) {
        m_isValid = checkDependencies();
        gchar *versionString = gst_version_string();
        logMessage(QString("Using %0").arg(versionString));
        g_free(versionString);
    }
    if (!m_isValid)
        qWarning("Phonon::GStreamer::Backend: Failed to initialize GStreamer");

    m_deviceManager = new DeviceManager(this);
    m_effectManager = new EffectManager(this);
}

Backend::~Backend()
{
    gst_deinit();
}

gboolean Backend::busCall(GstBus *bus, GstMessage *msg, gpointer data)
{
    Q_UNUSED(bus);
    Q_ASSERT(msg);

    MediaObject *mediaObject = static_cast<MediaObject*>(data);
    Q_ASSERT(mediaObject);

    Message message(msg, mediaObject);
    QMetaObject::invokeMethod(mediaObject->backend(), "handleBusMessage", Qt::QueuedConnection, Q_ARG(Message, message));

    return true;
}

/***
 * !reimp
 */
QObject *Backend::createObject(BackendInterface::Class c, QObject *parent, const QList<QVariant> &args)
{
    // Return nothing if dependencies are not met

    switch (c) {
    case MediaObjectClass:
        return new MediaObject(this, parent);

    case AudioOutputClass:
        return new AudioOutput(this, parent);

    case EffectClass:
        return new AudioEffect(this, args[0].toInt(), parent);

    case AudioDataOutputClass:
        return new AudioDataOutput(this, parent);

    case VideoDataOutputClass:
        logMessage("createObject() : VideoDataOutput not implemented");
        break;

    case VideoWidgetClass: {
            QWidget *widget =  qobject_cast<QWidget*>(parent);
            return new VideoWidget(this, widget);
        }
    case VolumeFaderEffectClass:
        return new VolumeFaderEffect(this, parent);

    case VisualizationClass:  //Fall through
    default:
        logMessage("createObject() : Backend object not available");
    }
    return 0;
}

// Returns true if all dependencies are met
// and gstreamer is usable, otherwise false
bool Backend::isValid() const
{
    return m_isValid;
}

bool Backend::supportsVideo() const
{
    return isValid();
}

bool Backend::checkDependencies() const
{
    bool success = false;
    // Verify that gst-plugins-base is installed
    GstElementFactory *acFactory = gst_element_factory_find ("audioconvert");
    if (acFactory) {
        gst_object_unref(acFactory);
        success = true;
        // Check if gst-plugins-good is installed
        GstElementFactory *csFactory = gst_element_factory_find ("videobalance");
        if (csFactory) {
            gst_object_unref(csFactory);
        } else {
            QString message = tr("Warning: You do not seem to have the package gstreamer0.10-plugins-good installed.\n"
                                 "          Some video features have been disabled.");
            qDebug() << message;
        }
    } else {
        qWarning() << tr("Warning: You do not seem to have the base GStreamer plugins installed.\n"
                         "          All audio and video support has been disabled");
    }
    return success;
}

/***
 * !reimp
 */
QStringList Backend::availableMimeTypes() const
{
    QStringList availableMimeTypes;

    if (!isValid())
        return availableMimeTypes;

    GstElementFactory *mpegFactory;
    // Add mp3 as a separate mime type as people are likely to look for it.
    if ((mpegFactory = gst_element_factory_find ("ffmpeg")) ||
        (mpegFactory = gst_element_factory_find ("mad"))) {
        availableMimeTypes << QLatin1String("audio/x-mp3");
        gst_object_unref(GST_OBJECT(mpegFactory));
    }

    // Iterate over all audio and video decoders and extract mime types from sink caps
    GList* factoryList = gst_registry_get_feature_list(gst_registry_get_default (), GST_TYPE_ELEMENT_FACTORY);
    for (GList* iter = g_list_first(factoryList) ; iter != NULL ; iter = g_list_next(iter)) {
        GstPluginFeature *feature = GST_PLUGIN_FEATURE(iter->data);
        QString klass = gst_element_factory_get_klass(GST_ELEMENT_FACTORY(feature));

        if (klass == QLatin1String("Codec/Decoder/Audio") ||
            klass == QLatin1String("Codec/Decoder/Video")) {

            const GList *static_templates;
            GstElementFactory *factory = GST_ELEMENT_FACTORY(feature);
            static_templates = gst_element_factory_get_static_pad_templates(factory);

            for (; static_templates != NULL ; static_templates = static_templates->next) {
                GstStaticPadTemplate *padTemplate = (GstStaticPadTemplate *) static_templates->data;
                if (padTemplate && padTemplate->direction == GST_PAD_SINK) {
                    GstCaps *caps = gst_static_pad_template_get_caps (padTemplate);

                    if (caps) {
                        const GstStructure* capsStruct = gst_caps_get_structure (caps, 0);
                        QString mime = QString::fromUtf8(gst_structure_get_name (capsStruct));
                        if (!availableMimeTypes.contains(mime))
                              availableMimeTypes.append(mime);
                    }
                }
            }
        }
    }
    g_list_free(factoryList);
    availableMimeTypes.sort();
    return availableMimeTypes;
}

/***
 * !reimp
 */
QList<int> Backend::objectDescriptionIndexes(ObjectDescriptionType type) const
{
    QList<int> list;

    if (!isValid())
        return list;

    switch (type) {
    case Phonon::AudioOutputDeviceType: {
            QList<AudioDevice> deviceList = deviceManager()->audioOutputDevices();
            for (int dev = 0 ; dev < deviceList.size() ; ++dev)
                list.append(deviceList[dev].id);
            break;
        }
        break;

    case Phonon::EffectType: {
            QList<EffectInfo*> effectList = effectManager()->audioEffects();
            for (int eff = 0 ; eff < effectList.size() ; ++eff)
                list.append(eff);
            break;
        }
        break;
    default:
        break;
    }
    return list;
}

/***
 * !reimp
 */
QHash<QByteArray, QVariant> Backend::objectDescriptionProperties(ObjectDescriptionType type, int index) const
{

    QHash<QByteArray, QVariant> ret;

    if (!isValid())
        return ret;

    switch (type) {
    case Phonon::AudioOutputDeviceType: {
            QList<AudioDevice> audioDevices = deviceManager()->audioOutputDevices();
            if (index >= 0 && index < audioDevices.size()) {
                ret.insert("name", audioDevices[index].gstId);
                ret.insert("description", audioDevices[index].description);
                ret.insert("icon", QLatin1String("audio-card"));
            }
        }
        break;

    case Phonon::EffectType: {
            QList<EffectInfo*> effectList = effectManager()->audioEffects();
            if (index >= 0 && index <= effectList.size()) {
                const EffectInfo *effect = effectList[index];
                ret.insert("name", effect->name());
                ret.insert("description", effect->description());
                ret.insert("author", effect->author());
            } else
                Q_ASSERT(1); // Since we use list position as ID, this should not happen
        }
    default:
        break;
    }
    return ret;
}

/***
 * !reimp
 */
bool Backend::startConnectionChange(QSet<QObject *> objects)
{
    foreach (QObject *object, objects) {
        MediaNode *sourceNode = qobject_cast<MediaNode *>(object);
        MediaObject *media = sourceNode->root();
        if (media) {
            media->saveState();
            return true;
        }
    }
    return true;
}

/***
 * !reimp
 */
bool Backend::connectNodes(QObject *source, QObject *sink)
{
    if (isValid()) {
        MediaNode *sourceNode = qobject_cast<MediaNode *>(source);
        MediaNode *sinkNode = qobject_cast<MediaNode *>(sink);
        if (sourceNode && sinkNode) {
            if (sourceNode->connectNode(sink)) {
                sourceNode->root()->invalidateGraph();
                logMessage(QString("Backend connected %0 to %1").arg(source->metaObject()->className()).arg(sink->metaObject()->className()));
                return true;
            }
        }
    }
    logMessage(QString("Linking %0 to %1 failed").arg(source->metaObject()->className()).arg(sink->metaObject()->className()), Warning);
    return false;
}

/***
 * !reimp
 */
bool Backend::disconnectNodes(QObject *source, QObject *sink)
{
    MediaNode *sourceNode = qobject_cast<MediaNode *>(source);
    MediaNode *sinkNode = qobject_cast<MediaNode *>(sink);

    if (sourceNode && sinkNode)
        return sourceNode->disconnectNode(sink);
    else
        return false;
}

/***
 * !reimp
 */
bool Backend::endConnectionChange(QSet<QObject *> objects)
{
    foreach (QObject *object, objects) {
        MediaNode *sourceNode = qobject_cast<MediaNode *>(object);
        MediaObject *media = sourceNode->root();
        if (media) {
            media->resumeState();
            return true;
        }
    }
    return true;
}

/***
 * Request bus messages for this mediaobject
 */
void Backend::addBusWatcher(MediaObject* node)
{
    Q_ASSERT(node);
    GstBus *bus = gst_pipeline_get_bus (GST_PIPELINE(node->pipeline()));
    gst_bus_add_watch (bus, busCall, node);
    gst_object_unref(bus);
}

/***
 * Ignore bus messages for this mediaobject
 */
void Backend::removeBusWatcher(MediaObject* node)
{
    Q_ASSERT(node);
    g_source_remove_by_user_data(node);
}

/***
 * Polls each mediaobject's pipeline and delivers
 * pending any pending messages
 */
void Backend::handleBusMessage(Message message)
{
    MediaObject *mediaObject = message.source();
    mediaObject->handleBusMessage(message);
}

DeviceManager* Backend::deviceManager() const
{
    return m_deviceManager;
}

EffectManager* Backend::effectManager() const
{
    return m_effectManager;
}

/**
 * Returns a debuglevel that is determined by the
 * PHONON_GSTREAMER_DEBUG environment variable.
 *
 *  Warning - important warnings
 *  Info    - general info
 *  Debug   - gives extra info
 */
Backend::DebugLevel Backend::debugLevel() const
{
    return m_debugLevel;
}

/***
 * Prints a conditional debug message based on the current debug level
 * If obj is provided, classname and objectname will be printed as well
 *
 * see debugLevel()
 */
void Backend::logMessage(const QString &message, int priority, QObject *obj) const
{
    if (debugLevel() > 0) {
        QString output;
        if (obj) {
            // Strip away namespace from className
            QString className(obj->metaObject()->className());
            int nameLength = className.length() - className.lastIndexOf(':') - 1;
            className = className.right(nameLength);
            output.sprintf("%s %s (%s %p)", message.toLatin1().constData(),
                                          obj->objectName().toLatin1().constData(),
                                          className.toLatin1().constData(), obj);
        }
        else {
            output = message;
        }
        if (priority <= (int)debugLevel()) {
            qDebug() << QString("PGST(%1): %2").arg(priority).arg(output);
        }
    }
}

}
}

QT_END_NAMESPACE

#include "moc_backend.cpp"
<commit_msg>show clearly what fails<commit_after>/*  This file is part of the KDE project.

    Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).

    This library is free software: you can redistribute it and/or modify
    it under the terms of the GNU Lesser General Public License as published by
    the Free Software Foundation, either version 2.1 or 3 of the License.

    This library is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU Lesser General Public License for more details.

    You should have received a copy of the GNU Lesser General Public License
    along with this library.  If not, see <http://www.gnu.org/licenses/>.
*/

#include "common.h"
#include "backend.h"
#include "audiooutput.h"
#include "audiodataoutput.h"
#include "audioeffect.h"
#include "mediaobject.h"
#include "videowidget.h"
#include "devicemanager.h"
#include "effectmanager.h"
#include "message.h"
#include "volumefadereffect.h"
#include <gst/interfaces/propertyprobe.h>

#include <QtCore/QSet>
#include <QtCore/QVariant>
#include <QtCore/QtPlugin>

QT_BEGIN_NAMESPACE

Q_EXPORT_PLUGIN2(phonon_gstreamer, Phonon::Gstreamer::Backend)

namespace Phonon
{
namespace Gstreamer
{

class MediaNode;

Backend::Backend(QObject *parent, const QVariantList &)
        : QObject(parent)
        , m_deviceManager(0)
        , m_effectManager(0)
        , m_debugLevel(Warning)
        , m_isValid(false)
{
    GError *err = 0;
    bool wasInit = gst_init_check(0, 0, &err);  //init gstreamer: must be called before any gst-related functions
    if (err)
        g_error_free(err);

    qRegisterMetaType<Message>("Message");

    setProperty("identifier",     QLatin1String("phonon_gstreamer"));
    setProperty("backendName",    QLatin1String("Gstreamer"));
    setProperty("backendComment", QLatin1String("Gstreamer plugin for Phonon"));
    setProperty("backendVersion", QLatin1String("0.2"));
    setProperty("backendWebsite", QLatin1String("http://qtsoftware.com/"));

    //check if we should enable debug output
    QString debugLevelString = qgetenv("PHONON_GST_DEBUG");
    int debugLevel = debugLevelString.toInt();
    if (debugLevel > 3) //3 is maximum
        debugLevel = 3;
    m_debugLevel = (DebugLevel)debugLevel;

    if (wasInit) {
        m_isValid = checkDependencies();
        gchar *versionString = gst_version_string();
        logMessage(QString("Using %0").arg(versionString));
        g_free(versionString);
    }
    if (!m_isValid)
        qWarning("Phonon::GStreamer::Backend: Failed to initialize GStreamer");

    m_deviceManager = new DeviceManager(this);
    m_effectManager = new EffectManager(this);
}

Backend::~Backend()
{
    gst_deinit();
}

gboolean Backend::busCall(GstBus *bus, GstMessage *msg, gpointer data)
{
    Q_UNUSED(bus);
    Q_ASSERT(msg);

    MediaObject *mediaObject = static_cast<MediaObject*>(data);
    Q_ASSERT(mediaObject);

    Message message(msg, mediaObject);
    QMetaObject::invokeMethod(mediaObject->backend(), "handleBusMessage", Qt::QueuedConnection, Q_ARG(Message, message));

    return true;
}

/***
 * !reimp
 */
QObject *Backend::createObject(BackendInterface::Class c, QObject *parent, const QList<QVariant> &args)
{
    // Return nothing if dependencies are not met

    switch (c) {
    case MediaObjectClass:
        return new MediaObject(this, parent);

    case AudioOutputClass:
        return new AudioOutput(this, parent);

    case EffectClass:
        return new AudioEffect(this, args[0].toInt(), parent);

    case AudioDataOutputClass:
        return new AudioDataOutput(this, parent);

    case VideoDataOutputClass:
        logMessage("createObject() : VideoDataOutput not implemented");
        break;

    case VideoWidgetClass: {
            QWidget *widget =  qobject_cast<QWidget*>(parent);
            return new VideoWidget(this, widget);
        }
    case VolumeFaderEffectClass:
        return new VolumeFaderEffect(this, parent);

    case VisualizationClass:  //Fall through
    default:
        logMessage("createObject() : Backend object not available");
    }
    return 0;
}

// Returns true if all dependencies are met
// and gstreamer is usable, otherwise false
bool Backend::isValid() const
{
    return m_isValid;
}

bool Backend::supportsVideo() const
{
    return isValid();
}

bool Backend::checkDependencies() const
{
    bool success = false;
    // Verify that gst-plugins-base is installed
    GstElementFactory *acFactory = gst_element_factory_find ("audioconvert");
    if (acFactory) {
        gst_object_unref(acFactory);
        success = true;
        // Check if gst-plugins-good is installed
        GstElementFactory *csFactory = gst_element_factory_find ("videobalance");
        if (csFactory) {
            gst_object_unref(csFactory);
        } else {
            QString message = tr("Warning: You do not seem to have the package gstreamer0.10-plugins-good installed.\n"
                                 "          Some video features have been disabled.");
            qDebug() << message;
        }
    } else {
        qWarning() << tr("Warning: You do not seem to have the base GStreamer plugins installed.\n"
                         "          All audio and video support has been disabled");
    }
    return success;
}

/***
 * !reimp
 */
QStringList Backend::availableMimeTypes() const
{
    QStringList availableMimeTypes;

    if (!isValid())
        return availableMimeTypes;

    GstElementFactory *mpegFactory;
    // Add mp3 as a separate mime type as people are likely to look for it.
    if ((mpegFactory = gst_element_factory_find ("ffmpeg")) ||
        (mpegFactory = gst_element_factory_find ("mad"))) {
        availableMimeTypes << QLatin1String("audio/x-mp3");
        gst_object_unref(GST_OBJECT(mpegFactory));
    }

    // Iterate over all audio and video decoders and extract mime types from sink caps
    GList* factoryList = gst_registry_get_feature_list(gst_registry_get_default (), GST_TYPE_ELEMENT_FACTORY);
    for (GList* iter = g_list_first(factoryList) ; iter != NULL ; iter = g_list_next(iter)) {
        GstPluginFeature *feature = GST_PLUGIN_FEATURE(iter->data);
        QString klass = gst_element_factory_get_klass(GST_ELEMENT_FACTORY(feature));

        if (klass == QLatin1String("Codec/Decoder/Audio") ||
            klass == QLatin1String("Codec/Decoder/Video")) {

            const GList *static_templates;
            GstElementFactory *factory = GST_ELEMENT_FACTORY(feature);
            static_templates = gst_element_factory_get_static_pad_templates(factory);

            for (; static_templates != NULL ; static_templates = static_templates->next) {
                GstStaticPadTemplate *padTemplate = (GstStaticPadTemplate *) static_templates->data;
                if (padTemplate && padTemplate->direction == GST_PAD_SINK) {
                    GstCaps *caps = gst_static_pad_template_get_caps (padTemplate);

                    if (caps) {
                        const GstStructure* capsStruct = gst_caps_get_structure (caps, 0);
                        QString mime = QString::fromUtf8(gst_structure_get_name (capsStruct));
                        if (!availableMimeTypes.contains(mime))
                              availableMimeTypes.append(mime);
                    }
                }
            }
        }
    }
    g_list_free(factoryList);
    availableMimeTypes.sort();
    return availableMimeTypes;
}

/***
 * !reimp
 */
QList<int> Backend::objectDescriptionIndexes(ObjectDescriptionType type) const
{
    QList<int> list;

    if (!isValid())
        return list;

    switch (type) {
    case Phonon::AudioOutputDeviceType: {
            QList<AudioDevice> deviceList = deviceManager()->audioOutputDevices();
            for (int dev = 0 ; dev < deviceList.size() ; ++dev)
                list.append(deviceList[dev].id);
            break;
        }
        break;

    case Phonon::EffectType: {
            QList<EffectInfo*> effectList = effectManager()->audioEffects();
            for (int eff = 0 ; eff < effectList.size() ; ++eff)
                list.append(eff);
            break;
        }
        break;
    default:
        break;
    }
    return list;
}

/***
 * !reimp
 */
QHash<QByteArray, QVariant> Backend::objectDescriptionProperties(ObjectDescriptionType type, int index) const
{

    QHash<QByteArray, QVariant> ret;

    if (!isValid())
        return ret;

    switch (type) {
    case Phonon::AudioOutputDeviceType: {
            QList<AudioDevice> audioDevices = deviceManager()->audioOutputDevices();
            if (index >= 0 && index < audioDevices.size()) {
                ret.insert("name", audioDevices[index].gstId);
                ret.insert("description", audioDevices[index].description);
                ret.insert("icon", QLatin1String("audio-card"));
            }
        }
        break;

    case Phonon::EffectType: {
            QList<EffectInfo*> effectList = effectManager()->audioEffects();
            if (index >= 0 && index <= effectList.size()) {
                const EffectInfo *effect = effectList[index];
                ret.insert("name", effect->name());
                ret.insert("description", effect->description());
                ret.insert("author", effect->author());
            } else
                Q_ASSERT(1); // Since we use list position as ID, this should not happen
        }
    default:
        break;
    }
    return ret;
}

/***
 * !reimp
 */
bool Backend::startConnectionChange(QSet<QObject *> objects)
{
    foreach (QObject *object, objects) {
        qWarning() << object->metaObject()->className();
        MediaNode *sourceNode = qobject_cast<MediaNode *>(object);
        Q_ASSERT(sourceNode);
        MediaObject *media = sourceNode->root();
        if (media) {
            media->saveState();
            return true;
        }
    }
    return true;
}

/***
 * !reimp
 */
bool Backend::connectNodes(QObject *source, QObject *sink)
{
    if (isValid()) {
        MediaNode *sourceNode = qobject_cast<MediaNode *>(source);
        MediaNode *sinkNode = qobject_cast<MediaNode *>(sink);
        qWarning() << sink->metaObject()->className();
        Q_ASSERT(sinkNode);
        if (sourceNode && sinkNode) {
            if (sourceNode->connectNode(sink)) {
                sourceNode->root()->invalidateGraph();
                logMessage(QString("Backend connected %0 to %1").arg(source->metaObject()->className()).arg(sink->metaObject()->className()));
                return true;
            }
        }
    }
    logMessage(QString("Linking %0 to %1 failed").arg(source->metaObject()->className()).arg(sink->metaObject()->className()), Warning);
    return false;
}

/***
 * !reimp
 */
bool Backend::disconnectNodes(QObject *source, QObject *sink)
{
    MediaNode *sourceNode = qobject_cast<MediaNode *>(source);
    MediaNode *sinkNode = qobject_cast<MediaNode *>(sink);

    if (sourceNode && sinkNode)
        return sourceNode->disconnectNode(sink);
    else
        return false;
}

/***
 * !reimp
 */
bool Backend::endConnectionChange(QSet<QObject *> objects)
{
    foreach (QObject *object, objects) {
        MediaNode *sourceNode = qobject_cast<MediaNode *>(object);
        MediaObject *media = sourceNode->root();
        if (media) {
            media->resumeState();
            return true;
        }
    }
    return true;
}

/***
 * Request bus messages for this mediaobject
 */
void Backend::addBusWatcher(MediaObject* node)
{
    Q_ASSERT(node);
    GstBus *bus = gst_pipeline_get_bus (GST_PIPELINE(node->pipeline()));
    gst_bus_add_watch (bus, busCall, node);
    gst_object_unref(bus);
}

/***
 * Ignore bus messages for this mediaobject
 */
void Backend::removeBusWatcher(MediaObject* node)
{
    Q_ASSERT(node);
    g_source_remove_by_user_data(node);
}

/***
 * Polls each mediaobject's pipeline and delivers
 * pending any pending messages
 */
void Backend::handleBusMessage(Message message)
{
    MediaObject *mediaObject = message.source();
    mediaObject->handleBusMessage(message);
}

DeviceManager* Backend::deviceManager() const
{
    return m_deviceManager;
}

EffectManager* Backend::effectManager() const
{
    return m_effectManager;
}

/**
 * Returns a debuglevel that is determined by the
 * PHONON_GSTREAMER_DEBUG environment variable.
 *
 *  Warning - important warnings
 *  Info    - general info
 *  Debug   - gives extra info
 */
Backend::DebugLevel Backend::debugLevel() const
{
    return m_debugLevel;
}

/***
 * Prints a conditional debug message based on the current debug level
 * If obj is provided, classname and objectname will be printed as well
 *
 * see debugLevel()
 */
void Backend::logMessage(const QString &message, int priority, QObject *obj) const
{
    if (debugLevel() > 0) {
        QString output;
        if (obj) {
            // Strip away namespace from className
            QString className(obj->metaObject()->className());
            int nameLength = className.length() - className.lastIndexOf(':') - 1;
            className = className.right(nameLength);
            output.sprintf("%s %s (%s %p)", message.toLatin1().constData(),
                                          obj->objectName().toLatin1().constData(),
                                          className.toLatin1().constData(), obj);
        }
        else {
            output = message;
        }
        if (priority <= (int)debugLevel()) {
            qDebug() << QString("PGST(%1): %2").arg(priority).arg(output);
        }
    }
}

}
}

QT_END_NAMESPACE

#include "moc_backend.cpp"
<|endoftext|>


<commit_before>/*
 * opencog/atomspace/TypeIndex.cc
 *
 * Copyright (C) 2008 Linas Vepstas <linasvepstas@gmail.com>
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU Affero General Public License v3 as
 * published by the Free Software Foundation and including the exceptions
 * at http://opencog.org/wiki/Licenses
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU Affero General Public License
 * along with this program; if not, write to:
 * Free Software Foundation, Inc.,
 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
 */

#include <opencog/atomspace/TypeIndex.h>
#include <opencog/atomspace/ClassServer.h>
#include <opencog/atomspace/type_codes.h>
#include <opencog/atomspace/HandleEntry.h>

using namespace opencog;

TypeIndex::TypeIndex(void)
{
	resize(ClassServer::getNumberOfClasses() + 2);
}

HandleEntry * TypeIndex::getHandleSet(Type type, bool subclass) const
{
	iterator it = begin(type, subclass);
	iterator itend = end();

	HandleEntry *he = NULL;
	while (it != itend)
	{
		HandleEntry *nhe = new HandleEntry(*it);
		nhe->next = he;
		he = nhe;
		it++;
	}
	return he;
}

// ================================================================

TypeIndex::iterator TypeIndex::begin(Type t, bool sub) const
{
	iterator it(t, sub);
	it.send = idx.end();

	it.s = idx.begin();
	it.currtype = 0;
	while (it.s != it.send)
	{
		// Find the first type which is a subtype, and start iteration there.
		if ((it.type == it.currtype) ||
		    (sub && (ClassServer::isAssignableFrom(it.type, it.currtype))))
		{
			it.se = it.s->begin();
			if (it.se != it.s->end()) return it;
		}
		it.currtype++;
		it.s++;
	}

	return it;
}

TypeIndex::iterator TypeIndex::end(void) const
{
	iterator it(NOTYPE, false);
	it.se = idx.at(NOTYPE).end();
	it.s = idx.end();
	it.send = idx.end();
	it.currtype = NOTYPE;
	return it;
}

TypeIndex::iterator::iterator(Type t, bool sub)
{
	type = t;
	subclass = sub;
}

TypeIndex::iterator& TypeIndex::iterator::operator=(iterator v)
{
	s = v.s;
	send = v.send;
	se = v.se;
	currtype = v.currtype;
	type = v.type;
	subclass = v.subclass;
	return *this;
}

Handle TypeIndex::iterator::operator*(void)
{
	if (s == send) return Handle::UNDEFINED;
	return *se;
}

bool TypeIndex::iterator::operator==(iterator v)
{
	if ((v.s == v.send) && (s == send)) return true;
	return v.se == se;
}

bool TypeIndex::iterator::operator!=(iterator v)
{
	if ((v.s == v.send) && (s != send)) return v.se != se;
	if ((v.s != v.send) && (s == send)) return v.se != se;
	return false;
}

TypeIndex::iterator& TypeIndex::iterator::operator++(int i)
{
	if (s == send) return *this;

	se++;
	if (se == s->end())
	{
		do
		{
			s++;
			currtype++;

			// Find the first type which is a subtype, and start iteration there.
			if ((type == currtype) ||
			    (subclass && (ClassServer::isAssignableFrom(type, currtype))))
			{
				se = s->begin();
				if (se != s->end()) return *this;
			}
		} while (s != send);
	}

	return *this;
}

// ================================================================
<commit_msg>add note copied from AtomTable.<commit_after>/*
 * opencog/atomspace/TypeIndex.cc
 *
 * Copyright (C) 2008 Linas Vepstas <linasvepstas@gmail.com>
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU Affero General Public License v3 as
 * published by the Free Software Foundation and including the exceptions
 * at http://opencog.org/wiki/Licenses
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU Affero General Public License
 * along with this program; if not, write to:
 * Free Software Foundation, Inc.,
 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
 */

#include <opencog/atomspace/TypeIndex.h>
#include <opencog/atomspace/ClassServer.h>
#include <opencog/atomspace/type_codes.h>
#include <opencog/atomspace/HandleEntry.h>

using namespace opencog;

TypeIndex::TypeIndex(void)
{
	// The typeIndex is NUMBER_OF_CLASSES+2 because NOTYPE is
	// NUMBER_OF_CLASSES+1 and typeIndex[NOTYPE] is asked for if a
	// typename is misspelled, because ClassServer::getType()
	// returns NOTYPE in this case).
	resize(ClassServer::getNumberOfClasses() + 2);
}

HandleEntry * TypeIndex::getHandleSet(Type type, bool subclass) const
{
	iterator it = begin(type, subclass);
	iterator itend = end();

	HandleEntry *he = NULL;
	while (it != itend)
	{
		HandleEntry *nhe = new HandleEntry(*it);
		nhe->next = he;
		he = nhe;
		it++;
	}
	return he;
}

// ================================================================

TypeIndex::iterator TypeIndex::begin(Type t, bool sub) const
{
	iterator it(t, sub);
	it.send = idx.end();

	it.s = idx.begin();
	it.currtype = 0;
	while (it.s != it.send)
	{
		// Find the first type which is a subtype, and start iteration there.
		if ((it.type == it.currtype) ||
		    (sub && (ClassServer::isAssignableFrom(it.type, it.currtype))))
		{
			it.se = it.s->begin();
			if (it.se != it.s->end()) return it;
		}
		it.currtype++;
		it.s++;
	}

	return it;
}

TypeIndex::iterator TypeIndex::end(void) const
{
	iterator it(NOTYPE, false);
	it.se = idx.at(NOTYPE).end();
	it.s = idx.end();
	it.send = idx.end();
	it.currtype = NOTYPE;
	return it;
}

TypeIndex::iterator::iterator(Type t, bool sub)
{
	type = t;
	subclass = sub;
}

TypeIndex::iterator& TypeIndex::iterator::operator=(iterator v)
{
	s = v.s;
	send = v.send;
	se = v.se;
	currtype = v.currtype;
	type = v.type;
	subclass = v.subclass;
	return *this;
}

Handle TypeIndex::iterator::operator*(void)
{
	if (s == send) return Handle::UNDEFINED;
	return *se;
}

bool TypeIndex::iterator::operator==(iterator v)
{
	if ((v.s == v.send) && (s == send)) return true;
	return v.se == se;
}

bool TypeIndex::iterator::operator!=(iterator v)
{
	if ((v.s == v.send) && (s != send)) return v.se != se;
	if ((v.s != v.send) && (s == send)) return v.se != se;
	return false;
}

TypeIndex::iterator& TypeIndex::iterator::operator++(int i)
{
	if (s == send) return *this;

	se++;
	if (se == s->end())
	{
		do
		{
			s++;
			currtype++;

			// Find the first type which is a subtype, and start iteration there.
			if ((type == currtype) ||
			    (subclass && (ClassServer::isAssignableFrom(type, currtype))))
			{
				se = s->begin();
				if (se != s->end()) return *this;
			}
		} while (s != send);
	}

	return *this;
}

// ================================================================
<|endoftext|>