<commit_before>/*
 * Copyright 2015 WebAssembly Community Group participants
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

#include <chrono>
#include <sstream>

#include <passes/passes.h>
#include <pass.h>
#include <wasm-validator.h>

namespace wasm {

// PassRegistry

PassRegistry::PassRegistry() {
  registerPasses();
}

static PassRegistry singleton;

PassRegistry* PassRegistry::get() {
  return &singleton;
}

void PassRegistry::registerPass(const char* name, const char *description, Creator create) {
  assert(passInfos.find(name) == passInfos.end());
  passInfos[name] = PassInfo(description, create);
}

Pass* PassRegistry::createPass(std::string name) {
  if (passInfos.find(name) == passInfos.end()) return nullptr;
  auto ret = passInfos[name].create();
  ret->name = name;
  return ret;
}

std::vector<std::string> PassRegistry::getRegisteredNames() {
  std::vector<std::string> ret;
  for (auto pair : passInfos) {
    ret.push_back(pair.first);
  }
  return ret;
}

std::string PassRegistry::getPassDescription(std::string name) {
  assert(passInfos.find(name) != passInfos.end());
  return passInfos[name].description;
}

// PassRunner

void PassRegistry::registerPasses() {
  registerPass("coalesce-locals", "reduce # of locals by coalescing", createCoalesceLocalsPass);
  registerPass("coalesce-locals-learning", "reduce # of locals by coalescing and learning", createCoalesceLocalsWithLearningPass);
  registerPass("dce", "removes unreachable code", createDeadCodeEliminationPass);
  registerPass("duplicate-function-elimination", "removes duplicate functions", createDuplicateFunctionEliminationPass);
  registerPass("extract-function", "leaves just one function (useful for debugging)", createExtractFunctionPass);
  registerPass("legalize-js-interface", "legalizes i64 types on the import/export boundary", createLegalizeJSInterfacePass);
  registerPass("merge-blocks", "merges blocks to their parents", createMergeBlocksPass);
  registerPass("metrics", "reports metrics", createMetricsPass);
  registerPass("nm", "name list", createNameListPass);
  registerPass("name-manager", "utility pass to manage names in modules", createNameManagerPass);
  registerPass("optimize-instructions", "optimizes instruction combinations", createOptimizeInstructionsPass);
  registerPass("post-emscripten", "miscellaneous optimizations for Emscripten-generated code", createPostEmscriptenPass);
  registerPass("print", "print in s-expression format", createPrinterPass);
  registerPass("print-minified", "print in minified s-expression format", createMinifiedPrinterPass);
  registerPass("print-full", "print in full s-expression format", createFullPrinterPass);
  registerPass("relooper-jump-threading", "thread relooper jumps (fastcomp output only)", createRelooperJumpThreadingPass);
  registerPass("remove-imports", "removes imports and replaces them with nops", createRemoveImportsPass);
  registerPass("remove-memory", "removes memory segments", createRemoveMemoryPass);
  registerPass("remove-unused-brs", "removes breaks from locations that are not needed", createRemoveUnusedBrsPass);
  registerPass("remove-unused-functions", "removes unused functions", createRemoveUnusedFunctionsPass);
  registerPass("remove-unused-names", "removes names from locations that are never branched to", createRemoveUnusedNamesPass);
  registerPass("reorder-functions", "sorts functions by access frequency", createReorderFunctionsPass);
  registerPass("reorder-locals", "sorts locals by access frequency", createReorderLocalsPass);
  registerPass("simplify-locals", "miscellaneous locals-related optimizations", createSimplifyLocalsPass);
  registerPass("vacuum", "removes obviously unneeded code", createVacuumPass);
  registerPass("precompute", "computes compile-time evaluatable expressions", createPrecomputePass);
//  registerPass("lower-i64", "lowers i64 into pairs of i32s", createLowerInt64Pass);
}

void PassRunner::addDefaultOptimizationPasses() {
  add("duplicate-function-elimination");
  add("dce");
  add("remove-unused-brs");
  add("remove-unused-names");
  add("optimize-instructions");
  add("precompute");
  add("simplify-locals");
  add("vacuum"); // previous pass creates garbage
  add("remove-unused-brs"); // simplify-locals opens opportunities for phi optimizations
  add("coalesce-locals");
  add("vacuum"); // previous pass creates garbage
  add("reorder-locals");
  add("merge-blocks");
  add("optimize-instructions");
  add("precompute");
  add("vacuum"); // should not be needed, last few passes do not create garbage, but just to be safe
  add("duplicate-function-elimination"); // optimizations show more functions as duplicate
}

void PassRunner::addDefaultFunctionOptimizationPasses() {
  add("dce");
  add("remove-unused-brs");
  add("remove-unused-names");
  add("optimize-instructions");
  add("precompute");
  add("simplify-locals");
  add("vacuum"); // previous pass creates garbage
  add("remove-unused-brs"); // simplify-locals opens opportunities for phi optimizations
  add("coalesce-locals");
  add("vacuum"); // previous pass creates garbage
  add("reorder-locals");
  add("merge-blocks");
  add("optimize-instructions");
  add("precompute");
  add("vacuum"); // should not be needed, last few passes do not create garbage, but just to be safe
}

void PassRunner::addDefaultGlobalOptimizationPasses() {
  add("duplicate-function-elimination");
}

void PassRunner::run() {
  if (debug) {
    // for debug logging purposes, run each pass in full before running the other
    auto totalTime = std::chrono::duration<double>(0);
    size_t padding = 0;
    std::cerr << "[PassRunner] running passes..." << std::endl;
    for (auto pass : passes) {
      padding = std::max(padding, pass->name.size());
    }
    for (auto* pass : passes) {
      // ignoring the time, save a printout of the module before, in case this pass breaks it, so we can print the before and after
      std::stringstream moduleBefore;
      WasmPrinter::printModule(wasm, moduleBefore);
      // prepare to run
      std::chrono::high_resolution_clock::time_point before;
      std::cerr << "[PassRunner]   running pass: " << pass->name << "... ";
      for (size_t i = 0; i < padding - pass->name.size(); i++) {
        std::cerr << ' ';
      }
      before = std::chrono::high_resolution_clock::now();
      if (pass->isFunctionParallel()) {
        // function-parallel passes should get a new instance per function
        for (auto& func : wasm->functions) {
          runPassOnFunction(pass, func.get());
        }
      } else {
        pass->run(this, wasm);
      }
      auto after = std::chrono::high_resolution_clock::now();
      std::chrono::duration<double> diff = after - before;
      std::cerr << diff.count() << " seconds." << std::endl;
      totalTime += diff;
      // validate, ignoring the time
      std::cerr << "[PassRunner]   (validating)\n";
      if (!WasmValidator().validate(*wasm, false, validateGlobally)) {
        std::cerr << "Last pass (" << pass->name << ") broke validation. Here is the module before: \n" << moduleBefore.str() << "\n";
        abort();
      }
    }
    std::cerr << "[PassRunner] passes took " << totalTime.count() << " seconds." << std::endl;
    // validate
    std::cerr << "[PassRunner] (final validation)\n";
    if (!WasmValidator().validate(*wasm, false, validateGlobally)) {
      std::cerr << "final module does not validate\n";
      abort();
    }
  } else {
    // non-debug normal mode, run them in an optimal manner - for locality it is better
    // to run as many passes as possible on a single function before moving to the next
    std::vector<Pass*> stack;
    auto flush = [&]() {
      if (stack.size() > 0) {
        // run the stack of passes on all the functions, in parallel
        size_t num = ThreadPool::get()->size();
        std::vector<std::function<ThreadWorkState ()>> doWorkers;
        std::atomic<size_t> nextFunction;
        nextFunction.store(0);
        size_t numFunctions = wasm->functions.size();
        for (size_t i = 0; i < num; i++) {
          doWorkers.push_back([&]() {
            auto index = nextFunction.fetch_add(1);
            // get the next task, if there is one
            if (index >= numFunctions) {
              return ThreadWorkState::Finished; // nothing left
            }
            Function* func = this->wasm->functions[index].get();
            // do the current task: run all passes on this function
            for (auto* pass : stack) {
              runPassOnFunction(pass, func);
            }
            if (index + 1 == numFunctions) {
              return ThreadWorkState::Finished; // we did the last one
            }
            return ThreadWorkState::More;
          });
        }
        ThreadPool::get()->work(doWorkers);
      }
      stack.clear();
    };
    for (auto* pass : passes) {
      if (pass->isFunctionParallel()) {
        stack.push_back(pass);
      } else {
        flush();
        pass->run(this, wasm);
      }
    }
    flush();
  }
}

void PassRunner::runFunction(Function* func) {
  if (debug) {
    std::cerr << "[PassRunner] running passes on function " << func->name << std::endl;
  }
  for (auto* pass : passes) {
    runPassOnFunction(pass, func);
  }
}

PassRunner::~PassRunner() {
  for (auto pass : passes) {
    delete pass;
  }
}

void PassRunner::doAdd(Pass* pass) {
  passes.push_back(pass);
  pass->prepareToRun(this, wasm);
}

void PassRunner::runPassOnFunction(Pass* pass, Function* func) {
#if 0
  if (debug) {
    std::cerr << "[PassRunner]   runPass " << pass->name << " OnFunction " << func->name << "\n";
  }
#endif
  // function-parallel passes get a new instance per function
  if (pass->isFunctionParallel()) {
    auto instance = std::unique_ptr<Pass>(pass->create());
    instance->runFunction(this, wasm, func);
  } else {
    pass->runFunction(this, wasm, func);
  }
}

} // namespace wasm
<commit_msg>put heavy pass debugging operations behind BINARYEN_PASS_DEBUG (#755)<commit_after>/*
 * Copyright 2015 WebAssembly Community Group participants
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

#include <chrono>
#include <sstream>

#include <passes/passes.h>
#include <pass.h>
#include <wasm-validator.h>

namespace wasm {

// PassRegistry

PassRegistry::PassRegistry() {
  registerPasses();
}

static PassRegistry singleton;

PassRegistry* PassRegistry::get() {
  return &singleton;
}

void PassRegistry::registerPass(const char* name, const char *description, Creator create) {
  assert(passInfos.find(name) == passInfos.end());
  passInfos[name] = PassInfo(description, create);
}

Pass* PassRegistry::createPass(std::string name) {
  if (passInfos.find(name) == passInfos.end()) return nullptr;
  auto ret = passInfos[name].create();
  ret->name = name;
  return ret;
}

std::vector<std::string> PassRegistry::getRegisteredNames() {
  std::vector<std::string> ret;
  for (auto pair : passInfos) {
    ret.push_back(pair.first);
  }
  return ret;
}

std::string PassRegistry::getPassDescription(std::string name) {
  assert(passInfos.find(name) != passInfos.end());
  return passInfos[name].description;
}

// PassRunner

void PassRegistry::registerPasses() {
  registerPass("coalesce-locals", "reduce # of locals by coalescing", createCoalesceLocalsPass);
  registerPass("coalesce-locals-learning", "reduce # of locals by coalescing and learning", createCoalesceLocalsWithLearningPass);
  registerPass("dce", "removes unreachable code", createDeadCodeEliminationPass);
  registerPass("duplicate-function-elimination", "removes duplicate functions", createDuplicateFunctionEliminationPass);
  registerPass("extract-function", "leaves just one function (useful for debugging)", createExtractFunctionPass);
  registerPass("legalize-js-interface", "legalizes i64 types on the import/export boundary", createLegalizeJSInterfacePass);
  registerPass("merge-blocks", "merges blocks to their parents", createMergeBlocksPass);
  registerPass("metrics", "reports metrics", createMetricsPass);
  registerPass("nm", "name list", createNameListPass);
  registerPass("name-manager", "utility pass to manage names in modules", createNameManagerPass);
  registerPass("optimize-instructions", "optimizes instruction combinations", createOptimizeInstructionsPass);
  registerPass("post-emscripten", "miscellaneous optimizations for Emscripten-generated code", createPostEmscriptenPass);
  registerPass("print", "print in s-expression format", createPrinterPass);
  registerPass("print-minified", "print in minified s-expression format", createMinifiedPrinterPass);
  registerPass("print-full", "print in full s-expression format", createFullPrinterPass);
  registerPass("relooper-jump-threading", "thread relooper jumps (fastcomp output only)", createRelooperJumpThreadingPass);
  registerPass("remove-imports", "removes imports and replaces them with nops", createRemoveImportsPass);
  registerPass("remove-memory", "removes memory segments", createRemoveMemoryPass);
  registerPass("remove-unused-brs", "removes breaks from locations that are not needed", createRemoveUnusedBrsPass);
  registerPass("remove-unused-functions", "removes unused functions", createRemoveUnusedFunctionsPass);
  registerPass("remove-unused-names", "removes names from locations that are never branched to", createRemoveUnusedNamesPass);
  registerPass("reorder-functions", "sorts functions by access frequency", createReorderFunctionsPass);
  registerPass("reorder-locals", "sorts locals by access frequency", createReorderLocalsPass);
  registerPass("simplify-locals", "miscellaneous locals-related optimizations", createSimplifyLocalsPass);
  registerPass("vacuum", "removes obviously unneeded code", createVacuumPass);
  registerPass("precompute", "computes compile-time evaluatable expressions", createPrecomputePass);
//  registerPass("lower-i64", "lowers i64 into pairs of i32s", createLowerInt64Pass);
}

void PassRunner::addDefaultOptimizationPasses() {
  add("duplicate-function-elimination");
  add("dce");
  add("remove-unused-brs");
  add("remove-unused-names");
  add("optimize-instructions");
  add("precompute");
  add("simplify-locals");
  add("vacuum"); // previous pass creates garbage
  add("remove-unused-brs"); // simplify-locals opens opportunities for phi optimizations
  add("coalesce-locals");
  add("vacuum"); // previous pass creates garbage
  add("reorder-locals");
  add("merge-blocks");
  add("optimize-instructions");
  add("precompute");
  add("vacuum"); // should not be needed, last few passes do not create garbage, but just to be safe
  add("duplicate-function-elimination"); // optimizations show more functions as duplicate
}

void PassRunner::addDefaultFunctionOptimizationPasses() {
  add("dce");
  add("remove-unused-brs");
  add("remove-unused-names");
  add("optimize-instructions");
  add("precompute");
  add("simplify-locals");
  add("vacuum"); // previous pass creates garbage
  add("remove-unused-brs"); // simplify-locals opens opportunities for phi optimizations
  add("coalesce-locals");
  add("vacuum"); // previous pass creates garbage
  add("reorder-locals");
  add("merge-blocks");
  add("optimize-instructions");
  add("precompute");
  add("vacuum"); // should not be needed, last few passes do not create garbage, but just to be safe
}

void PassRunner::addDefaultGlobalOptimizationPasses() {
  add("duplicate-function-elimination");
}

void PassRunner::run() {
  if (debug) {
    // for debug logging purposes, run each pass in full before running the other
    auto totalTime = std::chrono::duration<double>(0);
    size_t padding = 0;
    std::cerr << "[PassRunner] running passes..." << std::endl;
    for (auto pass : passes) {
      padding = std::max(padding, pass->name.size());
    }
    bool passDebug = getenv("BINARYEN_PASS_DEBUG") && getenv("BINARYEN_PASS_DEBUG")[0] != '0';
    for (auto* pass : passes) {
      // ignoring the time, save a printout of the module before, in case this pass breaks it, so we can print the before and after
      std::stringstream moduleBefore;
      if (passDebug) {
        WasmPrinter::printModule(wasm, moduleBefore);
      }
      // prepare to run
      std::chrono::high_resolution_clock::time_point before;
      std::cerr << "[PassRunner]   running pass: " << pass->name << "... ";
      for (size_t i = 0; i < padding - pass->name.size(); i++) {
        std::cerr << ' ';
      }
      before = std::chrono::high_resolution_clock::now();
      if (pass->isFunctionParallel()) {
        // function-parallel passes should get a new instance per function
        for (auto& func : wasm->functions) {
          runPassOnFunction(pass, func.get());
        }
      } else {
        pass->run(this, wasm);
      }
      auto after = std::chrono::high_resolution_clock::now();
      std::chrono::duration<double> diff = after - before;
      std::cerr << diff.count() << " seconds." << std::endl;
      totalTime += diff;
      // validate, ignoring the time
      std::cerr << "[PassRunner]   (validating)\n";
      if (!WasmValidator().validate(*wasm, false, validateGlobally)) {
        if (passDebug) {
          std::cerr << "Last pass (" << pass->name << ") broke validation. Here is the module before: \n" << moduleBefore.str() << "\n";
        } else {
          std::cerr << "Last pass (" << pass->name << ") broke validation. Run with BINARYEN_PASS_DEBUG=1 in the env to see the earlier state\n";
        }
        abort();
      }
    }
    std::cerr << "[PassRunner] passes took " << totalTime.count() << " seconds." << std::endl;
    // validate
    std::cerr << "[PassRunner] (final validation)\n";
    if (!WasmValidator().validate(*wasm, false, validateGlobally)) {
      std::cerr << "final module does not validate\n";
      abort();
    }
  } else {
    // non-debug normal mode, run them in an optimal manner - for locality it is better
    // to run as many passes as possible on a single function before moving to the next
    std::vector<Pass*> stack;
    auto flush = [&]() {
      if (stack.size() > 0) {
        // run the stack of passes on all the functions, in parallel
        size_t num = ThreadPool::get()->size();
        std::vector<std::function<ThreadWorkState ()>> doWorkers;
        std::atomic<size_t> nextFunction;
        nextFunction.store(0);
        size_t numFunctions = wasm->functions.size();
        for (size_t i = 0; i < num; i++) {
          doWorkers.push_back([&]() {
            auto index = nextFunction.fetch_add(1);
            // get the next task, if there is one
            if (index >= numFunctions) {
              return ThreadWorkState::Finished; // nothing left
            }
            Function* func = this->wasm->functions[index].get();
            // do the current task: run all passes on this function
            for (auto* pass : stack) {
              runPassOnFunction(pass, func);
            }
            if (index + 1 == numFunctions) {
              return ThreadWorkState::Finished; // we did the last one
            }
            return ThreadWorkState::More;
          });
        }
        ThreadPool::get()->work(doWorkers);
      }
      stack.clear();
    };
    for (auto* pass : passes) {
      if (pass->isFunctionParallel()) {
        stack.push_back(pass);
      } else {
        flush();
        pass->run(this, wasm);
      }
    }
    flush();
  }
}

void PassRunner::runFunction(Function* func) {
  if (debug) {
    std::cerr << "[PassRunner] running passes on function " << func->name << std::endl;
  }
  for (auto* pass : passes) {
    runPassOnFunction(pass, func);
  }
}

PassRunner::~PassRunner() {
  for (auto pass : passes) {
    delete pass;
  }
}

void PassRunner::doAdd(Pass* pass) {
  passes.push_back(pass);
  pass->prepareToRun(this, wasm);
}

void PassRunner::runPassOnFunction(Pass* pass, Function* func) {
#if 0
  if (debug) {
    std::cerr << "[PassRunner]   runPass " << pass->name << " OnFunction " << func->name << "\n";
  }
#endif
  // function-parallel passes get a new instance per function
  if (pass->isFunctionParallel()) {
    auto instance = std::unique_ptr<Pass>(pass->create());
    instance->runFunction(this, wasm, func);
  } else {
    pass->runFunction(this, wasm, func);
  }
}

} // namespace wasm
<|endoftext|>


<commit_before>//
// Copyright (c) 2014 CNRS
// Authors: Florent Lamiraux
//
// This file is part of hpp-core
// hpp-core 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
// 3 of the License, or (at your option) any later version.
//
// hpp-core 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 Lesser Public License for more details.  You should have
// received a copy of the GNU Lesser General Public License along with
// hpp-core  If not, see
// <http://www.gnu.org/licenses/>.

#include <boost/tuple/tuple.hpp>

#include <hpp/core/path-planner.hh>
#include <hpp/core/nearest-neighbor.hh>
#include <hpp/util/debug.hh>

#include <hpp/core/roadmap.hh>
#include <hpp/core/problem.hh>
#include <hpp/core/problem-target.hh>
#include <hpp/core/node.hh>
#include <hpp/core/edge.hh>
#include <hpp/core/path.hh>
#include <hpp/core/path-validation.hh>
#include <hpp/core/path-projector.hh>
#include <hpp/core/steering-method.hh>
#include "astar.hh"
#include <hpp/util/timer.hh>

namespace hpp {
  namespace core {

    PathPlanner::PathPlanner (const Problem& problem) :
      problem_ (problem), roadmap_ (Roadmap::create (problem.distance (),
						     problem.robot())),
      interrupt_ (false),
      maxIterations_ (std::numeric_limits <unsigned long int>::infinity ()),
      timeOut_ (std::numeric_limits <double>::infinity ())
    {
    }

    PathPlanner::PathPlanner (const Problem& problem,
			      const RoadmapPtr_t& roadmap) :
      problem_ (problem), roadmap_ (roadmap),
      interrupt_ (false),
      maxIterations_ (std::numeric_limits <unsigned long int>::infinity ()),
      timeOut_ (std::numeric_limits <double>::infinity ())
    {
    }

    void PathPlanner::init (const PathPlannerWkPtr_t& weak)
    {
      weakPtr_ = weak;
    }

    const RoadmapPtr_t& PathPlanner::roadmap () const
    {
      return roadmap_;
    }

    const Problem& PathPlanner::problem () const
    {
      return problem_;
    }

    void PathPlanner::startSolve ()
    {
      problem_.checkProblem ();
      // Tag init and goal configurations in the roadmap
      roadmap()->resetGoalNodes ();
      roadmap()->initNode (problem_.initConfig ());
      const Configurations_t goals (problem_.goalConfigs ());
      for (Configurations_t::const_iterator itGoal = goals.begin ();
          itGoal != goals.end (); ++itGoal) {
        roadmap()->addGoalNode (*itGoal);
      }

      problem_.target()->check(roadmap());
    }

    PathVectorPtr_t PathPlanner::solve ()
    {
      interrupt_ = false;
      bool solved = false;
      unsigned long int nIter (0);
      boost::posix_time::ptime timeStart(boost::posix_time::microsec_clock::universal_time());
      startSolve ();
      tryConnectInitAndGoals ();
      solved = problem_.target()->reached (roadmap());
      if (solved ) {
	hppDout (info, "tryConnectInitAndGoals succeeded");
      }
      if (interrupt_) throw std::runtime_error ("Interruption");
      while (!solved) {
	std::ostringstream oss;
    if (maxIterations_ !=  std::numeric_limits <unsigned long int>::infinity () && nIter >= maxIterations_) {
	  oss << "Maximal number of iterations reached: " << maxIterations_;
	  throw std::runtime_error (oss.str ().c_str ());
    }
    if(((boost::posix_time::microsec_clock::universal_time() - timeStart).total_milliseconds()) > timeOut_*1000.){
      oss << "time out reached : " << timeOut_<<" s";
      throw std::runtime_error (oss.str ().c_str ());
    }
    hppStartBenchmark(ONE_STEP);
	oneStep ();
    hppStopBenchmark(ONE_STEP);
    hppDisplayBenchmark(ONE_STEP);

	++nIter;
        solved = problem_.target()->reached (roadmap());
	if (interrupt_) throw std::runtime_error ("Interruption");
      }
      PathVectorPtr_t planned =  computePath ();
      return finishSolve (planned);
    }

    void PathPlanner::interrupt ()
    {
      interrupt_ = true;
    }

    void PathPlanner::maxIterations (const unsigned long int& n)
    {
      maxIterations_ = n;
    }

    void PathPlanner::timeOut (const double& time)
    {
      timeOut_ = time;
    }

    PathVectorPtr_t PathPlanner::computePath () const
    {
      return problem_.target()->computePath(roadmap());
    }

    PathVectorPtr_t PathPlanner::finishSolve (const PathVectorPtr_t& path)
    {
      return path;
    }

    void PathPlanner::tryDirectPath ()
    {
      // call steering method here to build a direct conexion
      const SteeringMethodPtr_t& sm (problem ().steeringMethod ());
      PathValidationPtr_t pathValidation (problem ().pathValidation ());
      PathProjectorPtr_t pathProjector (problem ().pathProjector ());
      PathPtr_t validPath, projPath, path;
      NodePtr_t initNode = roadmap ()->initNode();
      for (NodeVector_t::const_iterator itn = roadmap ()->goalNodes ().begin();
	   itn != roadmap ()->goalNodes ().end (); ++itn) {
	ConfigurationPtr_t q1 ((initNode)->configuration ());
	ConfigurationPtr_t q2 ((*itn)->configuration ());
	path = (*sm) (*q1, *q2);
        if (!path) continue;
        if (pathProjector) {
          if (!pathProjector->apply (path, projPath)) continue;
        } else {
          projPath = path;
        }
        if (projPath) {
	  PathValidationReportPtr_t report;
          bool pathValid = pathValidation->validate (projPath, false, validPath,
						     report);
          if (pathValid && validPath->length() > 0) {
            roadmap ()->addEdge (initNode, *itn, projPath);
            roadmap ()->addEdge (*itn, initNode, projPath->reverse());
          }
        }
      }
    }
    void PathPlanner::tryConnectInitAndGoals ()
    {
      // call steering method here to build a direct conexion
      const SteeringMethodPtr_t& sm (problem ().steeringMethod ());
      PathValidationPtr_t pathValidation (problem ().pathValidation ());
      PathProjectorPtr_t pathProjector (problem ().pathProjector ());
      PathPtr_t validPath, projPath, path;
      NodePtr_t initNode = roadmap ()->initNode();
      NearestNeighborPtr_t nn (roadmap ()->nearestNeighbor ());
      // Register edges to add to roadmap and add them after iterating
      // among the connected components.
      typedef boost::tuple <NodePtr_t, NodePtr_t, PathPtr_t> FutureEdge_t;
      typedef std::vector <FutureEdge_t> FutureEdges_t;
      FutureEdges_t futureEdges;
      ConnectedComponentPtr_t initCC (initNode->connectedComponent ());
      for (ConnectedComponents_t::iterator itCC
             (roadmap ()->connectedComponents ().begin ());
           itCC != roadmap ()->connectedComponents ().end (); ++itCC) {
        if (*itCC != initCC) {
          value_type d;
          NodePtr_t near (nn->search (*initNode->configuration (),
                                      *itCC, d, true));
          assert (near);
          ConfigurationPtr_t q1 (initNode->configuration ());
          ConfigurationPtr_t q2 (near->configuration ());
          path = (*sm) (*q1, *q2);
          if (!path) continue;
          if (pathProjector) {
            if (!pathProjector->apply (path, projPath)) continue;
          } else {
            projPath = path;
          }
          if (projPath) {
            PathValidationReportPtr_t report;
            bool pathValid = pathValidation->validate (projPath, false,
                                                       validPath, report);
            if (pathValid && validPath->length() > 0) {
              futureEdges.push_back (FutureEdge_t (initNode, near, projPath));
            }
          }
        }
      }
      for (NodeVector_t::const_iterator itn = roadmap ()->goalNodes ().begin();
	   itn != roadmap ()->goalNodes ().end (); ++itn) {
        ConnectedComponentPtr_t goalCC ((*itn)->connectedComponent ());
        for (ConnectedComponents_t::iterator itCC
               (roadmap ()->connectedComponents ().begin ());
             itCC != roadmap ()->connectedComponents ().end (); ++itCC) {
          if (*itCC != goalCC) {
            value_type d;
            NodePtr_t near (nn->search (*(*itn)->configuration (), *itCC, d,
                                        false));
            assert (near);
            ConfigurationPtr_t q1 (near->configuration ());
            ConfigurationPtr_t q2 ((*itn)->configuration ());
            path = (*sm) (*q1, *q2);
            if (!path) continue;
            if (pathProjector) {
              if (!pathProjector->apply (path, projPath)) continue;
            } else {
              projPath = path;
            }
            if (projPath) {
              PathValidationReportPtr_t report;
              bool pathValid = pathValidation->validate (projPath, false,
                                                         validPath, report);
              if (pathValid && validPath->length() > 0) {
                futureEdges.push_back (FutureEdge_t (near, (*itn), projPath));
              }
            }
          }
        }
      }
      // Add edges
      for (FutureEdges_t::const_iterator it (futureEdges.begin ());
           it != futureEdges.end (); ++it) {
        roadmap ()->addEdge (it->get <0> (), it->get <1> (), it->get <2> ());
      }
    }

  } //   namespace core
} // namespace hpp
<commit_msg>Fix compilation warning.<commit_after>//
// Copyright (c) 2014 CNRS
// Authors: Florent Lamiraux
//
// This file is part of hpp-core
// hpp-core 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
// 3 of the License, or (at your option) any later version.
//
// hpp-core 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 Lesser Public License for more details.  You should have
// received a copy of the GNU Lesser General Public License along with
// hpp-core  If not, see
// <http://www.gnu.org/licenses/>.

#include <boost/tuple/tuple.hpp>

#include <hpp/core/path-planner.hh>
#include <hpp/core/nearest-neighbor.hh>
#include <hpp/util/debug.hh>

#include <hpp/core/roadmap.hh>
#include <hpp/core/problem.hh>
#include <hpp/core/problem-target.hh>
#include <hpp/core/node.hh>
#include <hpp/core/edge.hh>
#include <hpp/core/path.hh>
#include <hpp/core/path-validation.hh>
#include <hpp/core/path-projector.hh>
#include <hpp/core/steering-method.hh>
#include "astar.hh"
#include <hpp/util/timer.hh>

namespace hpp {
  namespace core {

    PathPlanner::PathPlanner (const Problem& problem) :
      problem_ (problem), roadmap_ (Roadmap::create (problem.distance (),
						     problem.robot())),
      interrupt_ (false),
      maxIterations_ (std::numeric_limits <unsigned long int>::infinity ()),
      timeOut_ (std::numeric_limits <double>::infinity ())
    {
    }

    PathPlanner::PathPlanner (const Problem& problem,
			      const RoadmapPtr_t& roadmap) :
      problem_ (problem), roadmap_ (roadmap),
      interrupt_ (false),
      maxIterations_ (std::numeric_limits <unsigned long int>::infinity ()),
      timeOut_ (std::numeric_limits <double>::infinity ())
    {
    }

    void PathPlanner::init (const PathPlannerWkPtr_t& weak)
    {
      weakPtr_ = weak;
    }

    const RoadmapPtr_t& PathPlanner::roadmap () const
    {
      return roadmap_;
    }

    const Problem& PathPlanner::problem () const
    {
      return problem_;
    }

    void PathPlanner::startSolve ()
    {
      problem_.checkProblem ();
      // Tag init and goal configurations in the roadmap
      roadmap()->resetGoalNodes ();
      roadmap()->initNode (problem_.initConfig ());
      const Configurations_t goals (problem_.goalConfigs ());
      for (Configurations_t::const_iterator itGoal = goals.begin ();
          itGoal != goals.end (); ++itGoal) {
        roadmap()->addGoalNode (*itGoal);
      }

      problem_.target()->check(roadmap());
    }

    PathVectorPtr_t PathPlanner::solve ()
    {
      namespace bpt = boost::posix_time;

      interrupt_ = false;
      bool solved = false;
      unsigned long int nIter (0);
      bpt::ptime timeStart(bpt::microsec_clock::universal_time());
      startSolve ();
      tryConnectInitAndGoals ();
      solved = problem_.target()->reached (roadmap());
      if (solved ) {
	hppDout (info, "tryConnectInitAndGoals succeeded");
      }
      if (interrupt_) throw std::runtime_error ("Interruption");
      while (!solved) {
	std::ostringstream oss;
    if (maxIterations_ !=  std::numeric_limits <unsigned long int>::infinity () && nIter >= maxIterations_) {
	  oss << "Maximal number of iterations reached: " << maxIterations_;
	  throw std::runtime_error (oss.str ().c_str ());
    }
      bpt::ptime timeStop(bpt::microsec_clock::universal_time());
      if(static_cast<value_type>((timeStop - timeStart).total_milliseconds())
          > timeOut_*1000){
      oss << "time out reached : " << timeOut_<<" s";
      throw std::runtime_error (oss.str ().c_str ());
    }
    hppStartBenchmark(ONE_STEP);
	oneStep ();
    hppStopBenchmark(ONE_STEP);
    hppDisplayBenchmark(ONE_STEP);

	++nIter;
        solved = problem_.target()->reached (roadmap());
	if (interrupt_) throw std::runtime_error ("Interruption");
      }
      PathVectorPtr_t planned =  computePath ();
      return finishSolve (planned);
    }

    void PathPlanner::interrupt ()
    {
      interrupt_ = true;
    }

    void PathPlanner::maxIterations (const unsigned long int& n)
    {
      maxIterations_ = n;
    }

    void PathPlanner::timeOut (const double& time)
    {
      timeOut_ = time;
    }

    PathVectorPtr_t PathPlanner::computePath () const
    {
      return problem_.target()->computePath(roadmap());
    }

    PathVectorPtr_t PathPlanner::finishSolve (const PathVectorPtr_t& path)
    {
      return path;
    }

    void PathPlanner::tryDirectPath ()
    {
      // call steering method here to build a direct conexion
      const SteeringMethodPtr_t& sm (problem ().steeringMethod ());
      PathValidationPtr_t pathValidation (problem ().pathValidation ());
      PathProjectorPtr_t pathProjector (problem ().pathProjector ());
      PathPtr_t validPath, projPath, path;
      NodePtr_t initNode = roadmap ()->initNode();
      for (NodeVector_t::const_iterator itn = roadmap ()->goalNodes ().begin();
	   itn != roadmap ()->goalNodes ().end (); ++itn) {
	ConfigurationPtr_t q1 ((initNode)->configuration ());
	ConfigurationPtr_t q2 ((*itn)->configuration ());
	path = (*sm) (*q1, *q2);
        if (!path) continue;
        if (pathProjector) {
          if (!pathProjector->apply (path, projPath)) continue;
        } else {
          projPath = path;
        }
        if (projPath) {
	  PathValidationReportPtr_t report;
          bool pathValid = pathValidation->validate (projPath, false, validPath,
						     report);
          if (pathValid && validPath->length() > 0) {
            roadmap ()->addEdge (initNode, *itn, projPath);
            roadmap ()->addEdge (*itn, initNode, projPath->reverse());
          }
        }
      }
    }
    void PathPlanner::tryConnectInitAndGoals ()
    {
      // call steering method here to build a direct conexion
      const SteeringMethodPtr_t& sm (problem ().steeringMethod ());
      PathValidationPtr_t pathValidation (problem ().pathValidation ());
      PathProjectorPtr_t pathProjector (problem ().pathProjector ());
      PathPtr_t validPath, projPath, path;
      NodePtr_t initNode = roadmap ()->initNode();
      NearestNeighborPtr_t nn (roadmap ()->nearestNeighbor ());
      // Register edges to add to roadmap and add them after iterating
      // among the connected components.
      typedef boost::tuple <NodePtr_t, NodePtr_t, PathPtr_t> FutureEdge_t;
      typedef std::vector <FutureEdge_t> FutureEdges_t;
      FutureEdges_t futureEdges;
      ConnectedComponentPtr_t initCC (initNode->connectedComponent ());
      for (ConnectedComponents_t::iterator itCC
             (roadmap ()->connectedComponents ().begin ());
           itCC != roadmap ()->connectedComponents ().end (); ++itCC) {
        if (*itCC != initCC) {
          value_type d;
          NodePtr_t near (nn->search (*initNode->configuration (),
                                      *itCC, d, true));
          assert (near);
          ConfigurationPtr_t q1 (initNode->configuration ());
          ConfigurationPtr_t q2 (near->configuration ());
          path = (*sm) (*q1, *q2);
          if (!path) continue;
          if (pathProjector) {
            if (!pathProjector->apply (path, projPath)) continue;
          } else {
            projPath = path;
          }
          if (projPath) {
            PathValidationReportPtr_t report;
            bool pathValid = pathValidation->validate (projPath, false,
                                                       validPath, report);
            if (pathValid && validPath->length() > 0) {
              futureEdges.push_back (FutureEdge_t (initNode, near, projPath));
            }
          }
        }
      }
      for (NodeVector_t::const_iterator itn = roadmap ()->goalNodes ().begin();
	   itn != roadmap ()->goalNodes ().end (); ++itn) {
        ConnectedComponentPtr_t goalCC ((*itn)->connectedComponent ());
        for (ConnectedComponents_t::iterator itCC
               (roadmap ()->connectedComponents ().begin ());
             itCC != roadmap ()->connectedComponents ().end (); ++itCC) {
          if (*itCC != goalCC) {
            value_type d;
            NodePtr_t near (nn->search (*(*itn)->configuration (), *itCC, d,
                                        false));
            assert (near);
            ConfigurationPtr_t q1 (near->configuration ());
            ConfigurationPtr_t q2 ((*itn)->configuration ());
            path = (*sm) (*q1, *q2);
            if (!path) continue;
            if (pathProjector) {
              if (!pathProjector->apply (path, projPath)) continue;
            } else {
              projPath = path;
            }
            if (projPath) {
              PathValidationReportPtr_t report;
              bool pathValid = pathValidation->validate (projPath, false,
                                                         validPath, report);
              if (pathValid && validPath->length() > 0) {
                futureEdges.push_back (FutureEdge_t (near, (*itn), projPath));
              }
            }
          }
        }
      }
      // Add edges
      for (FutureEdges_t::const_iterator it (futureEdges.begin ());
           it != futureEdges.end (); ++it) {
        roadmap ()->addEdge (it->get <0> (), it->get <1> (), it->get <2> ());
      }
    }

  } //   namespace core
} // namespace hpp
<|endoftext|>


<commit_before>#include "queueloader.h"

#include <cstdlib>
#include <cstring>
#include <fstream>
#include <libgen.h>
#include <unistd.h>

#include "config.h"
#include "configcontainer.h"
#include "logger.h"
#include "stflpp.h"
#include "utils.h"

using namespace newsboat;

namespace podboat {

queueloader::queueloader(const std::string& file, pb_controller* c)
	: queuefile(file)
	, ctrl(c)
{
}

void queueloader::reload(std::vector<download>& downloads, bool remove_unplayed)
{
	std::vector<download> dltemp;
	std::fstream f;

	for (auto& dl : downloads) {
		if (dl.status() == dlstatus::DOWNLOADING) { // we are not
							    // allowed to reload
							    // if a download is
							    // in progress!
			LOG(level::INFO,
				"queueloader::reload: aborting reload due to "
				"dlstatus::DOWNLOADING status");
			return;
		}
		switch (dl.status()) {
		case dlstatus::QUEUED:
		case dlstatus::CANCELLED:
		case dlstatus::FAILED:
		case dlstatus::ALREADY_DOWNLOADED:
		case dlstatus::READY:
			LOG(level::DEBUG,
				"queueloader::reload: storing %s to new vector",
				dl.url());
			dltemp.push_back(dl);
			break;
		case dlstatus::PLAYED:
		case dlstatus::FINISHED:
			if (!remove_unplayed) {
				LOG(level::DEBUG,
					"queueloader::reload: storing %s to "
					"new "
					"vector",
					dl.url());
				dltemp.push_back(dl);
			}
			break;
		default:
			break;
		}
	}

	f.open(queuefile.c_str(), std::fstream::in);
	if (f.is_open()) {
		std::string line;
		do {
			std::getline(f, line);
			if (!f.eof() && line.length() > 0) {
				LOG(level::DEBUG,
					"queueloader::reload: loaded `%s' from "
					"queue file",
					line);
				std::vector<std::string> fields =
					utils::tokenize_quoted(line);
				bool url_found = false;

				for (auto& dl : dltemp) {
					if (fields[0] == dl.url()) {
						LOG(level::INFO,
							"queueloader::reload: "
							"found `%s' in old "
							"vector",
							fields[0]);
						url_found = true;
						break;
					}
				}

				for (auto& dl : downloads) {
					if (fields[0] == dl.url()) {
						LOG(level::INFO,
							"queueloader::reload: "
							"found `%s' in new "
							"vector",
							line);
						url_found = true;
						break;
					}
				}

				if (!url_found) {
					LOG(level::INFO,
						"queueloader::reload: found "
						"`%s' "
						"nowhere -> storing to new "
						"vector",
						line);
					download d(ctrl);
					std::string fn;
					if (fields.size() == 1)
						fn = get_filename(fields[0]);
					else
						fn = fields[1];
					d.set_filename(fn);
					if (access(fn.c_str(), F_OK) == 0) {
						LOG(level::INFO,
							"queueloader::reload: "
							"found `%s' on file "
							"system "
							"-> mark as already "
							"downloaded",
							fn);
						if (fields.size() >= 3) {
							if (fields[2] ==
								"downloaded")
								d.set_status(
									dlstatus::
										READY);
							if (fields[2] ==
								"played")
								d.set_status(
									dlstatus::
										PLAYED);
						} else
							d.set_status(dlstatus::
									ALREADY_DOWNLOADED); // TODO: scrap dlstatus::ALREADY_DOWNLOADED state
					} else if (
						access((fn +
							       configcontainer::
								       PARTIAL_FILE_SUFFIX)
								.c_str(),
							F_OK) == 0) {
						LOG(level::INFO,
							"queueloader::reload: "
							"found `%s' on file "
							"system "
							"-> mark as partially "
							"downloaded",
							fn +
								configcontainer::
									PARTIAL_FILE_SUFFIX);
						d.set_status(dlstatus::
								ALREADY_DOWNLOADED);
					}

					d.set_url(fields[0]);
					dltemp.push_back(d);
				}
			}
		} while (!f.eof());
		f.close();
	}

	f.open(queuefile.c_str(), std::fstream::out);
	if (f.is_open()) {
		for (auto& dl : dltemp) {
			f << dl.url() << " " << stfl::quote(dl.filename());
			if (dl.status() == dlstatus::READY)
				f << " downloaded";
			if (dl.status() == dlstatus::PLAYED)
				f << " played";
			f << std::endl;
		}
		f.close();
	}

	downloads = dltemp;
}

std::string queueloader::get_filename(const std::string& str)
{
	std::string fn = ctrl->get_dlpath();

	if (fn[fn.length() - 1] != NEWSBEUTER_PATH_SEP[0])
		fn.append(NEWSBEUTER_PATH_SEP);
	char buf[1024];
	snprintf(buf, sizeof(buf), "%s", str.c_str());
	char* base = basename(buf);
	if (!base || strlen(base) == 0) {
		char lbuf[128];
		time_t t = time(nullptr);
		strftime(lbuf,
			sizeof(lbuf),
			"%Y-%b-%d-%H%M%S.unknown",
			localtime(&t));
		fn.append(lbuf);
	} else {
		fn.append(utils::replace_all(base, "'", "%27"));
	}
	return fn;
}

} // namespace podboat
<commit_msg>Constify more for-range loops<commit_after>#include "queueloader.h"

#include <cstdlib>
#include <cstring>
#include <fstream>
#include <libgen.h>
#include <unistd.h>

#include "config.h"
#include "configcontainer.h"
#include "logger.h"
#include "stflpp.h"
#include "utils.h"

using namespace newsboat;

namespace podboat {

queueloader::queueloader(const std::string& file, pb_controller* c)
	: queuefile(file)
	, ctrl(c)
{
}

void queueloader::reload(std::vector<download>& downloads, bool remove_unplayed)
{
	std::vector<download> dltemp;
	std::fstream f;

	for (const auto& dl : downloads) {
		if (dl.status() == dlstatus::DOWNLOADING) { // we are not
							    // allowed to reload
							    // if a download is
							    // in progress!
			LOG(level::INFO,
				"queueloader::reload: aborting reload due to "
				"dlstatus::DOWNLOADING status");
			return;
		}
		switch (dl.status()) {
		case dlstatus::QUEUED:
		case dlstatus::CANCELLED:
		case dlstatus::FAILED:
		case dlstatus::ALREADY_DOWNLOADED:
		case dlstatus::READY:
			LOG(level::DEBUG,
				"queueloader::reload: storing %s to new vector",
				dl.url());
			dltemp.push_back(dl);
			break;
		case dlstatus::PLAYED:
		case dlstatus::FINISHED:
			if (!remove_unplayed) {
				LOG(level::DEBUG,
					"queueloader::reload: storing %s to "
					"new "
					"vector",
					dl.url());
				dltemp.push_back(dl);
			}
			break;
		default:
			break;
		}
	}

	f.open(queuefile.c_str(), std::fstream::in);
	if (f.is_open()) {
		std::string line;
		do {
			std::getline(f, line);
			if (!f.eof() && line.length() > 0) {
				LOG(level::DEBUG,
					"queueloader::reload: loaded `%s' from "
					"queue file",
					line);
				std::vector<std::string> fields =
					utils::tokenize_quoted(line);
				bool url_found = false;

				for (const auto& dl : dltemp) {
					if (fields[0] == dl.url()) {
						LOG(level::INFO,
							"queueloader::reload: "
							"found `%s' in old "
							"vector",
							fields[0]);
						url_found = true;
						break;
					}
				}

				for (const auto& dl : downloads) {
					if (fields[0] == dl.url()) {
						LOG(level::INFO,
							"queueloader::reload: "
							"found `%s' in new "
							"vector",
							line);
						url_found = true;
						break;
					}
				}

				if (!url_found) {
					LOG(level::INFO,
						"queueloader::reload: found "
						"`%s' "
						"nowhere -> storing to new "
						"vector",
						line);
					download d(ctrl);
					std::string fn;
					if (fields.size() == 1)
						fn = get_filename(fields[0]);
					else
						fn = fields[1];
					d.set_filename(fn);
					if (access(fn.c_str(), F_OK) == 0) {
						LOG(level::INFO,
							"queueloader::reload: "
							"found `%s' on file "
							"system "
							"-> mark as already "
							"downloaded",
							fn);
						if (fields.size() >= 3) {
							if (fields[2] ==
								"downloaded")
								d.set_status(
									dlstatus::
										READY);
							if (fields[2] ==
								"played")
								d.set_status(
									dlstatus::
										PLAYED);
						} else
							d.set_status(dlstatus::
									ALREADY_DOWNLOADED); // TODO: scrap dlstatus::ALREADY_DOWNLOADED state
					} else if (
						access((fn +
							       configcontainer::
								       PARTIAL_FILE_SUFFIX)
								.c_str(),
							F_OK) == 0) {
						LOG(level::INFO,
							"queueloader::reload: "
							"found `%s' on file "
							"system "
							"-> mark as partially "
							"downloaded",
							fn +
								configcontainer::
									PARTIAL_FILE_SUFFIX);
						d.set_status(dlstatus::
								ALREADY_DOWNLOADED);
					}

					d.set_url(fields[0]);
					dltemp.push_back(d);
				}
			}
		} while (!f.eof());
		f.close();
	}

	f.open(queuefile.c_str(), std::fstream::out);
	if (f.is_open()) {
		for (const auto& dl : dltemp) {
			f << dl.url() << " " << stfl::quote(dl.filename());
			if (dl.status() == dlstatus::READY)
				f << " downloaded";
			if (dl.status() == dlstatus::PLAYED)
				f << " played";
			f << std::endl;
		}
		f.close();
	}

	downloads = dltemp;
}

std::string queueloader::get_filename(const std::string& str)
{
	std::string fn = ctrl->get_dlpath();

	if (fn[fn.length() - 1] != NEWSBEUTER_PATH_SEP[0])
		fn.append(NEWSBEUTER_PATH_SEP);
	char buf[1024];
	snprintf(buf, sizeof(buf), "%s", str.c_str());
	char* base = basename(buf);
	if (!base || strlen(base) == 0) {
		char lbuf[128];
		time_t t = time(nullptr);
		strftime(lbuf,
			sizeof(lbuf),
			"%Y-%b-%d-%H%M%S.unknown",
			localtime(&t));
		fn.append(lbuf);
	} else {
		fn.append(utils::replace_all(base, "'", "%27"));
	}
	return fn;
}

} // namespace podboat
<|endoftext|>


<commit_before>#include "queueloader.h"

#include <cstdlib>
#include <cstring>
#include <fstream>
#include <libgen.h>
#include <unistd.h>

#include "config.h"
#include "configcontainer.h"
#include "logger.h"
#include "stflpp.h"
#include "utils.h"

using namespace newsboat;

namespace podboat {

queueloader::queueloader(const std::string& file, pb_controller* c)
	: queuefile(file)
	, ctrl(c)
{
}

void queueloader::reload(std::vector<download>& downloads, bool remove_unplayed)
{
	std::vector<download> dltemp;
	std::fstream f;

	for (const auto& dl : downloads) {
		if (dl.status() == dlstatus::DOWNLOADING) { // we are not
							    // allowed to reload
							    // if a download is
							    // in progress!
			LOG(level::INFO,
				"queueloader::reload: aborting reload due to "
				"dlstatus::DOWNLOADING status");
			return;
		}
		switch (dl.status()) {
		case dlstatus::QUEUED:
		case dlstatus::CANCELLED:
		case dlstatus::FAILED:
		case dlstatus::ALREADY_DOWNLOADED:
		case dlstatus::READY:
			LOG(level::DEBUG,
				"queueloader::reload: storing %s to new vector",
				dl.url());
			dltemp.push_back(dl);
			break;
		case dlstatus::PLAYED:
		case dlstatus::FINISHED:
			if (!remove_unplayed) {
				LOG(level::DEBUG,
					"queueloader::reload: storing %s to "
					"new "
					"vector",
					dl.url());
				dltemp.push_back(dl);
			}
			break;
		default:
			break;
		}
	}

	f.open(queuefile.c_str(), std::fstream::in);
	if (f.is_open()) {
		std::string line;
		do {
			std::getline(f, line);
			if (!f.eof() && line.length() > 0) {
				LOG(level::DEBUG,
					"queueloader::reload: loaded `%s' from "
					"queue file",
					line);
				std::vector<std::string> fields =
					utils::tokenize_quoted(line);
				bool url_found = false;

				for (const auto& dl : dltemp) {
					if (fields[0] == dl.url()) {
						LOG(level::INFO,
							"queueloader::reload: "
							"found `%s' in old "
							"vector",
							fields[0]);
						url_found = true;
						break;
					}
				}

				for (const auto& dl : downloads) {
					if (fields[0] == dl.url()) {
						LOG(level::INFO,
							"queueloader::reload: "
							"found `%s' in new "
							"vector",
							line);
						url_found = true;
						break;
					}
				}

				if (!url_found) {
					LOG(level::INFO,
						"queueloader::reload: found "
						"`%s' "
						"nowhere -> storing to new "
						"vector",
						line);
					download d(ctrl);
					std::string fn;
					if (fields.size() == 1)
						fn = get_filename(fields[0]);
					else
						fn = fields[1];
					d.set_filename(fn);
					if (access(fn.c_str(), F_OK) == 0) {
						LOG(level::INFO,
							"queueloader::reload: "
							"found `%s' on file "
							"system "
							"-> mark as already "
							"downloaded",
							fn);
						if (fields.size() >= 3) {
							if (fields[2] ==
								"downloaded")
								d.set_status(
									dlstatus::
										READY);
							if (fields[2] ==
								"played")
								d.set_status(
									dlstatus::
										PLAYED);
						} else
							d.set_status(dlstatus::
									ALREADY_DOWNLOADED); // TODO: scrap dlstatus::ALREADY_DOWNLOADED state
					} else if (
						access((fn +
							       configcontainer::
								       PARTIAL_FILE_SUFFIX)
								.c_str(),
							F_OK) == 0) {
						LOG(level::INFO,
							"queueloader::reload: "
							"found `%s' on file "
							"system "
							"-> mark as partially "
							"downloaded",
							fn +
								configcontainer::
									PARTIAL_FILE_SUFFIX);
						d.set_status(dlstatus::
								ALREADY_DOWNLOADED);
					}

					d.set_url(fields[0]);
					dltemp.push_back(d);
				}
			}
		} while (!f.eof());
		f.close();
	}

	f.open(queuefile.c_str(), std::fstream::out);
	if (f.is_open()) {
		for (const auto& dl : dltemp) {
			f << dl.url() << " " << stfl::quote(dl.filename());
			if (dl.status() == dlstatus::READY)
				f << " downloaded";
			if (dl.status() == dlstatus::PLAYED)
				f << " played";
			f << std::endl;
		}
		f.close();
	}

	downloads = dltemp;
}

std::string queueloader::get_filename(const std::string& str)
{
	std::string fn = ctrl->get_dlpath();

	if (fn[fn.length() - 1] != NEWSBEUTER_PATH_SEP[0])
		fn.append(NEWSBEUTER_PATH_SEP);
	char buf[1024];
	snprintf(buf, sizeof(buf), "%s", str.c_str());
	char* base = basename(buf);
	if (!base || strlen(base) == 0) {
		char lbuf[128];
		time_t t = time(nullptr);
		strftime(lbuf,
			sizeof(lbuf),
			"%Y-%b-%d-%H%M%S.unknown",
			localtime(&t));
		fn.append(lbuf);
	} else {
		fn.append(utils::replace_all(base, "'", "%27"));
	}
	return fn;
}

} // namespace podboat
<commit_msg>Fix podboat segfault when parsing comment in queue<commit_after>#include "queueloader.h"

#include <cstdlib>
#include <cstring>
#include <fstream>
#include <iostream>
#include <libgen.h>
#include <unistd.h>

#include "config.h"
#include "configcontainer.h"
#include "logger.h"
#include "stflpp.h"
#include "strprintf.h"
#include "utils.h"

using namespace newsboat;

namespace podboat {

queueloader::queueloader(const std::string& file, pb_controller* c)
	: queuefile(file)
	, ctrl(c)
{
}

void queueloader::reload(std::vector<download>& downloads, bool remove_unplayed)
{
	std::vector<download> dltemp;
	std::fstream f;

	for (const auto& dl : downloads) {
		if (dl.status() == dlstatus::DOWNLOADING) { // we are not
							    // allowed to reload
							    // if a download is
							    // in progress!
			LOG(level::INFO,
				"queueloader::reload: aborting reload due to "
				"dlstatus::DOWNLOADING status");
			return;
		}
		switch (dl.status()) {
		case dlstatus::QUEUED:
		case dlstatus::CANCELLED:
		case dlstatus::FAILED:
		case dlstatus::ALREADY_DOWNLOADED:
		case dlstatus::READY:
			LOG(level::DEBUG,
				"queueloader::reload: storing %s to new vector",
				dl.url());
			dltemp.push_back(dl);
			break;
		case dlstatus::PLAYED:
		case dlstatus::FINISHED:
			if (!remove_unplayed) {
				LOG(level::DEBUG,
					"queueloader::reload: storing %s to "
					"new "
					"vector",
					dl.url());
				dltemp.push_back(dl);
			}
			break;
		default:
			break;
		}
	}

	f.open(queuefile.c_str(), std::fstream::in);
	bool comments_ignored = false;
	if (f.is_open()) {
		std::string line;
		do {
			std::getline(f, line);
			if (!f.eof() && line.length() > 0) {
				LOG(level::DEBUG,
					"queueloader::reload: loaded `%s' from "
					"queue file",
					line);
				std::vector<std::string> fields =
					utils::tokenize_quoted(line);
				bool url_found = false;

				if (fields.empty()) {
					if (!comments_ignored) {
						std::cout << StrPrintf::fmt(
							     _("WARNING: Comment found "
							       "in %s. The queue file is regenerated "
							       "when podboat exits and comments will "
							       "be deleted. Press enter to continue or "
							       "Ctrl+C to abort"),
							     queuefile)
							  << std::endl;
						std::cin.ignore();
						comments_ignored = true;
					}
					continue;
				}

				for (const auto& dl : dltemp) {
					if (fields[0] == dl.url()) {
						LOG(level::INFO,
							"queueloader::reload: "
							"found `%s' in old "
							"vector",
							fields[0]);
						url_found = true;
						break;
					}
				}

				for (const auto& dl : downloads) {
					if (fields[0] == dl.url()) {
						LOG(level::INFO,
							"queueloader::reload: "
							"found `%s' in new "
							"vector",
							line);
						url_found = true;
						break;
					}
				}

				if (!url_found) {
					LOG(level::INFO,
						"queueloader::reload: found "
						"`%s' "
						"nowhere -> storing to new "
						"vector",
						line);
					download d(ctrl);
					std::string fn;
					if (fields.size() == 1)
						fn = get_filename(fields[0]);
					else
						fn = fields[1];
					d.set_filename(fn);
					if (access(fn.c_str(), F_OK) == 0) {
						LOG(level::INFO,
							"queueloader::reload: "
							"found `%s' on file "
							"system "
							"-> mark as already "
							"downloaded",
							fn);
						if (fields.size() >= 3) {
							if (fields[2] ==
								"downloaded")
								d.set_status(
									dlstatus::
										READY);
							if (fields[2] ==
								"played")
								d.set_status(
									dlstatus::
										PLAYED);
						} else
							d.set_status(dlstatus::
									ALREADY_DOWNLOADED); // TODO: scrap dlstatus::ALREADY_DOWNLOADED state
					} else if (
						access((fn +
							       configcontainer::
								       PARTIAL_FILE_SUFFIX)
								.c_str(),
							F_OK) == 0) {
						LOG(level::INFO,
							"queueloader::reload: "
							"found `%s' on file "
							"system "
							"-> mark as partially "
							"downloaded",
							fn +
								configcontainer::
									PARTIAL_FILE_SUFFIX);
						d.set_status(dlstatus::
								ALREADY_DOWNLOADED);
					}

					d.set_url(fields[0]);
					dltemp.push_back(d);
				}
			}
		} while (!f.eof());
		f.close();
	}

	f.open(queuefile.c_str(), std::fstream::out);
	if (f.is_open()) {
		for (const auto& dl : dltemp) {
			f << dl.url() << " " << stfl::quote(dl.filename());
			if (dl.status() == dlstatus::READY)
				f << " downloaded";
			if (dl.status() == dlstatus::PLAYED)
				f << " played";
			f << std::endl;
		}
		f.close();
	}

	downloads = dltemp;
}

std::string queueloader::get_filename(const std::string& str)
{
	std::string fn = ctrl->get_dlpath();

	if (fn[fn.length() - 1] != NEWSBEUTER_PATH_SEP[0])
		fn.append(NEWSBEUTER_PATH_SEP);
	char buf[1024];
	snprintf(buf, sizeof(buf), "%s", str.c_str());
	char* base = basename(buf);
	if (!base || strlen(base) == 0) {
		char lbuf[128];
		time_t t = time(nullptr);
		strftime(lbuf,
			sizeof(lbuf),
			"%Y-%b-%d-%H%M%S.unknown",
			localtime(&t));
		fn.append(lbuf);
	} else {
		fn.append(utils::replace_all(base, "'", "%27"));
	}
	return fn;
}

} // namespace podboat
<|endoftext|>


<commit_before>/* This is free and unencumbered software released into the public domain. */

#ifdef HAVE_CONFIG_H
#include <config.h>
#endif

#include "writer.h"
#include "writer/impl.h"

#ifndef DISABLE_JSONLD
#include "writer/jsonld.h"
#endif

#ifndef DISABLE_NQUADS
#include "writer/nquads.h"
#endif

#ifndef DISABLE_PAJEK
#include "writer/pajek.h"
#endif

#ifndef DISABLE_RDFJSON
#include "writer/rdfjson.h"
#endif

#ifndef DISABLE_TRIX
#include "writer/trix.h"
#endif

#ifndef DISABLE_XSLT
#include "writer/xslt.h"
#endif

#ifdef HAVE_LIBRAPTOR2
#include "raptor.h"
#include "writer/raptor.h"
#endif

#include "format.h"
#include "quad.h"
#include "term.h"
#include "triple.h"

#include <cassert>   /* for assert() */
#include <cstdlib>   /* for std::abort() */
#include <cstring>   /* for std::strcmp() */
#include <stdexcept> /* for std::invalid_argument, std::runtime_error */

using namespace rdf;

static rdf::writer::implementation*
rdf_writer_for(FILE* const stream,
               const char* const content_type,
               const char* const charset,
               const char* const base_uri) {
  const format* const format = format::find_for_content_type(content_type);
  if (format == nullptr) {
    return nullptr; /* unknown content type */
  }
  assert(format->module_name != nullptr);

#ifndef DISABLE_JSONLD
  if (std::strcmp("jsonld", format->module_name) == 0) {
    return rdf_writer_for_jsonld(stream, content_type, charset, base_uri);
  }
#endif

#ifndef DISABLE_NQUADS
  if (std::strcmp("nquads", format->module_name) == 0) {
    return rdf_writer_for_nquads(stream, content_type, charset, base_uri);
  }
#endif

#ifdef HAVE_LIBRAPTOR2
  if (std::strcmp("raptor", format->module_name) == 0) {
    return rdf_writer_for_raptor(stream, content_type, charset, base_uri);
  }
#endif

#ifndef DISABLE_PAJEK
  if (std::strcmp("pajek", format->module_name) == 0) {
    return rdf_writer_for_pajek(stream, content_type, charset, base_uri);
  }
#endif

#ifndef DISABLE_RDFJSON
  if (std::strcmp("rdfjson", format->module_name) == 0) {
    return rdf_writer_for_rdfjson(stream, content_type, charset, base_uri);
  }
#endif

#ifndef DISABLE_TRIX
  if (std::strcmp("trix", format->module_name) == 0) {
    return rdf_writer_for_trix(stream, content_type, charset, base_uri);
  }
#endif

#ifndef DISABLE_XSLT
  if (std::strcmp("xslt", format->module_name) == 0) {
    return rdf_writer_for_xslt(stream, content_type, charset, base_uri);
  }
#endif

  (void)stream, (void)charset, (void)base_uri;
  return std::abort(), nullptr; /* never reached */
}

writer::writer(const std::string& file_path,
               const std::string& content_type,
               const std::string& charset,
               const std::string& base_uri)
  : _implementation(nullptr) {
  assert(false && "file paths not supported yet"); // TODO
  (void)file_path, (void)content_type, (void)charset, (void)base_uri; // TODO
}

writer::writer(std::ostream& stream,
               const std::string& content_type,
               const std::string& charset,
               const std::string& base_uri)
  : _implementation(nullptr) {
  assert(false && "std::ostream not supported yet"); // TODO
  (void)stream, (void)content_type, (void)charset, (void)base_uri; // TODO
}

writer::writer(FILE* const stream,
               const std::string& content_type,
               const std::string& charset,
               const std::string& base_uri)
  : _implementation(rdf_writer_for(stream, content_type.c_str(), charset.c_str(), base_uri.c_str())) {
  if (!_implementation) {
    throw std::invalid_argument("unknown content type: " + content_type);
  }
}

writer::~writer() noexcept = default;

writer::writer(writer&&) /*noexcept*/ = default;

writer&
writer::operator=(writer&&) /*noexcept*/ = default;

void
writer::configure(const char* const key,
                  const char* const value) {
  assert(_implementation);
  assert(key != nullptr); // FIXME
  _implementation->configure(key, value);
}

void
writer::define_prefix(const char* prefix,
                      const char* uri_string) {
  assert(_implementation);
  _implementation->define_prefix(prefix, uri_string);
}

void
writer::begin() {
  assert(_implementation);
  _implementation->begin();
}

void
writer::finish() {
  assert(_implementation);
  _implementation->finish();
}

void
writer::flush() {
  assert(_implementation);
  _implementation->flush();
}

void
writer::write_triple(const triple& triple) {
  assert(_implementation);
  _implementation->write_triple(triple);
}

void
writer::write_quad(const quad& quad) {
  assert(_implementation);
  _implementation->write_quad(quad);
}

void
writer::write_comment(const char* const comment) {
  assert(_implementation);
  _implementation->write_comment(comment);
}
<commit_msg>Forced the use of Raptor for Turtle serialization for now.<commit_after>/* This is free and unencumbered software released into the public domain. */

#ifdef HAVE_CONFIG_H
#include <config.h>
#endif

#include "writer.h"
#include "writer/impl.h"

#ifndef DISABLE_JSONLD
#include "writer/jsonld.h"
#endif

#ifndef DISABLE_NQUADS
#include "writer/nquads.h"
#endif

#ifndef DISABLE_PAJEK
#include "writer/pajek.h"
#endif

#ifndef DISABLE_RDFJSON
#include "writer/rdfjson.h"
#endif

#ifndef DISABLE_TRIX
#include "writer/trix.h"
#endif

#ifndef DISABLE_XSLT
#include "writer/xslt.h"
#endif

#ifdef HAVE_LIBRAPTOR2
#include "raptor.h"
#include "writer/raptor.h"
#endif

#include "format.h"
#include "quad.h"
#include "term.h"
#include "triple.h"

#include <cassert>   /* for assert() */
#include <cstdlib>   /* for std::abort() */
#include <cstring>   /* for std::strcmp() */
#include <stdexcept> /* for std::invalid_argument, std::runtime_error */

using namespace rdf;

static rdf::writer::implementation*
rdf_writer_for(FILE* const stream,
               const char* const content_type,
               const char* const charset,
               const char* const base_uri) {
  const format* const format = format::find_for_content_type(content_type);
  if (format == nullptr) {
    return nullptr; /* unknown content type */
  }
  assert(format->module_name != nullptr);

  if (std::strcmp("text/turtle", content_type) == 0) { // FIXME
    return rdf_writer_for_raptor(stream, content_type, charset, base_uri);
  }

#ifndef DISABLE_JSONLD
  if (std::strcmp("jsonld", format->module_name) == 0) {
    return rdf_writer_for_jsonld(stream, content_type, charset, base_uri);
  }
#endif

#ifndef DISABLE_NQUADS
  if (std::strcmp("nquads", format->module_name) == 0) {
    return rdf_writer_for_nquads(stream, content_type, charset, base_uri);
  }
#endif

#ifdef HAVE_LIBRAPTOR2
  if (std::strcmp("raptor", format->module_name) == 0) {
    return rdf_writer_for_raptor(stream, content_type, charset, base_uri);
  }
#endif

#ifndef DISABLE_PAJEK
  if (std::strcmp("pajek", format->module_name) == 0) {
    return rdf_writer_for_pajek(stream, content_type, charset, base_uri);
  }
#endif

#ifndef DISABLE_RDFJSON
  if (std::strcmp("rdfjson", format->module_name) == 0) {
    return rdf_writer_for_rdfjson(stream, content_type, charset, base_uri);
  }
#endif

#ifndef DISABLE_TRIX
  if (std::strcmp("trix", format->module_name) == 0) {
    return rdf_writer_for_trix(stream, content_type, charset, base_uri);
  }
#endif

#ifndef DISABLE_XSLT
  if (std::strcmp("xslt", format->module_name) == 0) {
    return rdf_writer_for_xslt(stream, content_type, charset, base_uri);
  }
#endif

  (void)stream, (void)charset, (void)base_uri;
  return std::abort(), nullptr; /* never reached */
}

writer::writer(const std::string& file_path,
               const std::string& content_type,
               const std::string& charset,
               const std::string& base_uri)
  : _implementation(nullptr) {
  assert(false && "file paths not supported yet"); // TODO
  (void)file_path, (void)content_type, (void)charset, (void)base_uri; // TODO
}

writer::writer(std::ostream& stream,
               const std::string& content_type,
               const std::string& charset,
               const std::string& base_uri)
  : _implementation(nullptr) {
  assert(false && "std::ostream not supported yet"); // TODO
  (void)stream, (void)content_type, (void)charset, (void)base_uri; // TODO
}

writer::writer(FILE* const stream,
               const std::string& content_type,
               const std::string& charset,
               const std::string& base_uri)
  : _implementation(rdf_writer_for(stream, content_type.c_str(), charset.c_str(), base_uri.c_str())) {
  if (!_implementation) {
    throw std::invalid_argument("unknown content type: " + content_type);
  }
}

writer::~writer() noexcept = default;

writer::writer(writer&&) /*noexcept*/ = default;

writer&
writer::operator=(writer&&) /*noexcept*/ = default;

void
writer::configure(const char* const key,
                  const char* const value) {
  assert(_implementation);
  assert(key != nullptr); // FIXME
  _implementation->configure(key, value);
}

void
writer::define_prefix(const char* prefix,
                      const char* uri_string) {
  assert(_implementation);
  _implementation->define_prefix(prefix, uri_string);
}

void
writer::begin() {
  assert(_implementation);
  _implementation->begin();
}

void
writer::finish() {
  assert(_implementation);
  _implementation->finish();
}

void
writer::flush() {
  assert(_implementation);
  _implementation->flush();
}

void
writer::write_triple(const triple& triple) {
  assert(_implementation);
  _implementation->write_triple(triple);
}

void
writer::write_quad(const quad& quad) {
  assert(_implementation);
  _implementation->write_quad(quad);
}

void
writer::write_comment(const char* const comment) {
  assert(_implementation);
  _implementation->write_comment(comment);
}
<|endoftext|>


<commit_before>#include "scene_view.h"
#include "core/crc32.h"
#include "core/input_system.h"
#include "core/path.h"
#include "core/profiler.h"
#include "core/resource_manager.h"
#include "core/string.h"
#include "editor/gizmo.h"
#include "editor/imgui/imgui.h"
#include "editor/log_ui.h"
#include "editor/platform_interface.h"
#include "editor/settings.h"
#include "engine/engine.h"
#include "engine/plugin_manager.h"
#include "renderer/frame_buffer.h"
#include "renderer/pipeline.h"
#include "renderer/render_scene.h"
#include "renderer/renderer.h"

SceneView::SceneView()
{
	m_pipeline = nullptr;
	m_editor = nullptr;
	m_camera_speed = 0.1f;
	m_is_mouse_captured = false;
	m_show_stats = false;
	m_log_ui = nullptr;
}

SceneView::~SceneView()
{
}

void SceneView::setWireframe(bool wireframe)
{
	m_pipeline->setWireframe(wireframe);
}

void SceneView::setScene(Lumix::RenderScene* scene)
{
	m_pipeline->setScene(scene);
}

void SceneView::shutdown()
{
	m_editor->universeCreated().unbind<SceneView, &SceneView::onUniverseCreated>(this);
	m_editor->universeDestroyed().unbind<SceneView, &SceneView::onUniverseDestroyed>(this);
	Lumix::Pipeline::destroy(m_pipeline);
	m_pipeline = nullptr;
}

void SceneView::onUniverseCreated()
{
	auto* scene = m_editor->getScene(Lumix::crc32("renderer"));
	m_pipeline->setScene(static_cast<Lumix::RenderScene*>(scene));
}

void SceneView::onUniverseDestroyed()
{
	m_pipeline->setScene(nullptr);
}

bool SceneView::init(LogUI& log_ui, Lumix::WorldEditor& editor, Lumix::Array<Action*>& actions)
{
	m_log_ui = &log_ui;
	m_editor = &editor;
	auto& engine = editor.getEngine();
	auto& allocator = engine.getAllocator();
	auto* renderer = static_cast<Lumix::Renderer*>(engine.getPluginManager().getPlugin("renderer"));

	Lumix::Path path("pipelines/main.lua");
	m_pipeline = Lumix::Pipeline::create(*renderer, path, engine.getAllocator());
	m_pipeline->load();
	m_pipeline->addCustomCommandHandler("renderGizmos")
		.callback.bind<SceneView, &SceneView::renderGizmos>(this);
	m_pipeline->addCustomCommandHandler("renderIcons")
		.callback.bind<SceneView, &SceneView::renderIcons>(this);

	editor.universeCreated().bind<SceneView, &SceneView::onUniverseCreated>(this);
	editor.universeDestroyed().bind<SceneView, &SceneView::onUniverseDestroyed>(this);
	onUniverseCreated();

	m_toggle_gizmo_step_action =
		LUMIX_NEW(editor.getAllocator(), Action)("Enable/disable gizmo step", "toggleGizmoStep");
	m_toggle_gizmo_step_action->is_global = false;

	actions.push(m_toggle_gizmo_step_action);

	return true;
}

void SceneView::update()
{
	PROFILE_FUNCTION();
	if (ImGui::IsAnyItemActive()) return;
	if (!m_is_opened) return;
	if (ImGui::GetIO().KeyCtrl) return;

	m_camera_speed =
		Lumix::Math::maximum(0.01f, m_camera_speed + ImGui::GetIO().MouseWheel / 20.0f);

	int screen_x = int(ImGui::GetIO().MousePos.x);
	int screen_y = int(ImGui::GetIO().MousePos.y);
	bool is_inside = screen_x >= m_screen_x && screen_y >= m_screen_y &&
					 screen_x <= m_screen_x + m_width && screen_y <= m_screen_y + m_height;
	if (!is_inside) return;

	float speed = m_camera_speed;
	if (ImGui::GetIO().KeyShift)
	{
		speed *= 10;
	}
	if (ImGui::GetIO().KeysDown['W'])
	{
		m_editor->navigate(1.0f, 0, speed);
	}
	if (ImGui::GetIO().KeysDown['S'])
	{
		m_editor->navigate(-1.0f, 0, speed);
	}
	if (ImGui::GetIO().KeysDown['A'])
	{
		m_editor->navigate(0.0f, -1.0f, speed);
	}
	if (ImGui::GetIO().KeysDown['D'])
	{
		m_editor->navigate(0.0f, 1.0f, speed);
	}
}

void SceneView::renderIcons()
{
	m_editor->renderIcons();
}

void SceneView::renderGizmos()
{
	m_editor->getGizmo().render();
}

void SceneView::captureMouse(bool capture)
{
	if(m_is_mouse_captured == capture) return;
	m_is_mouse_captured = capture;
	PlatformInterface::showCursor(!m_is_mouse_captured);
	if(!m_is_mouse_captured) PlatformInterface::unclipCursor();
}

void SceneView::onGUI()
{
	PROFILE_FUNCTION();
	m_is_opened = false;
	ImVec2 view_pos;
	const char* title = "Scene View###Scene View";
	if (m_log_ui && m_log_ui->getUnreadErrorCount() > 0)
	{
		title = "Scene View | errors in log###Scene View";
	}

	if (ImGui::BeginDock(title))
	{
		m_is_opened = true;
		auto size = ImGui::GetContentRegionAvail();
		size.y -= ImGui::GetTextLineHeightWithSpacing();
		auto* fb = m_pipeline->getFramebuffer("default");
		if (size.x > 0 && size.y > 0 && fb)
		{
			auto pos = ImGui::GetWindowPos();
			m_pipeline->setViewport(0, 0, int(size.x), int(size.y));
			m_texture_handle = fb->getRenderbufferHandle(0);
			auto cursor_pos = ImGui::GetCursorScreenPos();
			m_screen_x = int(cursor_pos.x);
			m_screen_y = int(cursor_pos.y);
			m_width = int(size.x);
			m_height = int(size.y);
			auto content_min = ImGui::GetCursorScreenPos();
			ImVec2 content_max(content_min.x + size.x, content_min.y + size.y);
			ImGui::Image(&m_texture_handle, size);
			view_pos = content_min;
			if (ImGui::IsItemHovered())
			{
				m_editor->getGizmo().enableStep(m_toggle_gizmo_step_action->isActive());
				auto rel_mp = ImGui::GetMousePos();
				rel_mp.x -= m_screen_x;
				rel_mp.y -= m_screen_y;
				for (int i = 0; i < 3; ++i)
				{
					if (ImGui::IsMouseClicked(i))
					{
						ImGui::ResetActiveID();
						captureMouse(true);
						m_editor->onMouseDown((int)rel_mp.x, (int)rel_mp.y, (Lumix::MouseButton::Value)i);
					}

					auto& input = m_editor->getEngine().getInputSystem();
					auto delta = Lumix::Vec2(input.getMouseXMove(), input.getMouseYMove());
					if(delta.x != 0 || delta.y != 0)
					{
						m_editor->onMouseMove((int)rel_mp.x, (int)rel_mp.y, (int)delta.x, (int)delta.y);
					}
				}
			}
			for (int i = 0; i < 3; ++i)
			{
				auto rel_mp = ImGui::GetMousePos();
				rel_mp.x -= m_screen_x;
				rel_mp.y -= m_screen_y;
				if (ImGui::IsMouseReleased(i))
				{
					captureMouse(false);
					m_editor->onMouseUp((int)rel_mp.x, (int)rel_mp.y, (Lumix::MouseButton::Value)i);
				}
			}
			if(m_is_mouse_captured)
			{
				PlatformInterface::clipCursor(
					content_min.x, content_min.y, content_max.x, content_max.y);
			}
			m_pipeline->render();
		}

		ImGui::PushItemWidth(60);
		ImGui::DragFloat("Camera speed", &m_camera_speed, 0.1f, 0.01f, 999.0f, "%.2f");
		ImGui::SameLine();
		if (m_editor->isMeasureToolActive())
		{
			ImGui::Text("| Measured distance: %f", m_editor->getMeasuredDistance());
		}

		ImGui::SameLine();
		int step = m_editor->getGizmo().getStep();
		if (ImGui::DragInt("Gizmo step", &step, 1.0f, 0, 200))
		{
			m_editor->getGizmo().setStep(step);
		}

		ImGui::SameLine();
		ImGui::Checkbox("Stats", &m_show_stats);

		ImGui::SameLine();
	}

	ImGui::EndDock();

	if(m_show_stats)
	{
		view_pos.x += ImGui::GetStyle().FramePadding.x;
		view_pos.y += ImGui::GetStyle().FramePadding.y;
		ImGui::SetNextWindowPos(view_pos);
		auto col = ImGui::GetStyle().Colors[ImGuiCol_WindowBg];
		col.w = 0.3f;
		ImGui::PushStyleColor(ImGuiCol_WindowBg, col);
		if (ImGui::Begin("###stats_overlay",
				nullptr,
				ImGuiWindowFlags_NoTitleBar | ImGuiWindowFlags_AlwaysAutoResize |
					ImGuiWindowFlags_NoMove | ImGuiWindowFlags_NoSavedSettings |
					ImGuiWindowFlags_ShowBorders))
		{
			const auto& stats = m_pipeline->getStats();
			ImGui::LabelText("Draw calls", "%d", stats.m_draw_call_count);
			ImGui::LabelText("Instances", "%d", stats.m_instance_count);
			char buf[30];
			Lumix::toCStringPretty(stats.m_triangle_count, buf, Lumix::lengthOf(buf));
			ImGui::LabelText("Triangles", buf);
		}
		ImGui::End();
		ImGui::PopStyleColor();
	}
}<commit_msg>improved mouse handling in the Scene View - closes #763<commit_after>#include "scene_view.h"
#include "core/crc32.h"
#include "core/input_system.h"
#include "core/path.h"
#include "core/profiler.h"
#include "core/resource_manager.h"
#include "core/string.h"
#include "editor/gizmo.h"
#include "editor/imgui/imgui.h"
#include "editor/log_ui.h"
#include "editor/platform_interface.h"
#include "editor/settings.h"
#include "engine/engine.h"
#include "engine/plugin_manager.h"
#include "renderer/frame_buffer.h"
#include "renderer/pipeline.h"
#include "renderer/render_scene.h"
#include "renderer/renderer.h"

SceneView::SceneView()
{
	m_pipeline = nullptr;
	m_editor = nullptr;
	m_camera_speed = 0.1f;
	m_is_mouse_captured = false;
	m_show_stats = false;
	m_log_ui = nullptr;
}

SceneView::~SceneView()
{
}

void SceneView::setWireframe(bool wireframe)
{
	m_pipeline->setWireframe(wireframe);
}

void SceneView::setScene(Lumix::RenderScene* scene)
{
	m_pipeline->setScene(scene);
}

void SceneView::shutdown()
{
	m_editor->universeCreated().unbind<SceneView, &SceneView::onUniverseCreated>(this);
	m_editor->universeDestroyed().unbind<SceneView, &SceneView::onUniverseDestroyed>(this);
	Lumix::Pipeline::destroy(m_pipeline);
	m_pipeline = nullptr;
}

void SceneView::onUniverseCreated()
{
	auto* scene = m_editor->getScene(Lumix::crc32("renderer"));
	m_pipeline->setScene(static_cast<Lumix::RenderScene*>(scene));
}

void SceneView::onUniverseDestroyed()
{
	m_pipeline->setScene(nullptr);
}

bool SceneView::init(LogUI& log_ui, Lumix::WorldEditor& editor, Lumix::Array<Action*>& actions)
{
	m_log_ui = &log_ui;
	m_editor = &editor;
	auto& engine = editor.getEngine();
	auto& allocator = engine.getAllocator();
	auto* renderer = static_cast<Lumix::Renderer*>(engine.getPluginManager().getPlugin("renderer"));

	Lumix::Path path("pipelines/main.lua");
	m_pipeline = Lumix::Pipeline::create(*renderer, path, engine.getAllocator());
	m_pipeline->load();
	m_pipeline->addCustomCommandHandler("renderGizmos")
		.callback.bind<SceneView, &SceneView::renderGizmos>(this);
	m_pipeline->addCustomCommandHandler("renderIcons")
		.callback.bind<SceneView, &SceneView::renderIcons>(this);

	editor.universeCreated().bind<SceneView, &SceneView::onUniverseCreated>(this);
	editor.universeDestroyed().bind<SceneView, &SceneView::onUniverseDestroyed>(this);
	onUniverseCreated();

	m_toggle_gizmo_step_action =
		LUMIX_NEW(editor.getAllocator(), Action)("Enable/disable gizmo step", "toggleGizmoStep");
	m_toggle_gizmo_step_action->is_global = false;

	actions.push(m_toggle_gizmo_step_action);

	return true;
}

void SceneView::update()
{
	PROFILE_FUNCTION();
	if (ImGui::IsAnyItemActive()) return;
	if (!m_is_opened) return;
	if (ImGui::GetIO().KeyCtrl) return;

	m_camera_speed =
		Lumix::Math::maximum(0.01f, m_camera_speed + ImGui::GetIO().MouseWheel / 20.0f);

	int screen_x = int(ImGui::GetIO().MousePos.x);
	int screen_y = int(ImGui::GetIO().MousePos.y);
	bool is_inside = screen_x >= m_screen_x && screen_y >= m_screen_y &&
					 screen_x <= m_screen_x + m_width && screen_y <= m_screen_y + m_height;
	if (!is_inside) return;

	float speed = m_camera_speed;
	if (ImGui::GetIO().KeyShift)
	{
		speed *= 10;
	}
	if (ImGui::GetIO().KeysDown['W'])
	{
		m_editor->navigate(1.0f, 0, speed);
	}
	if (ImGui::GetIO().KeysDown['S'])
	{
		m_editor->navigate(-1.0f, 0, speed);
	}
	if (ImGui::GetIO().KeysDown['A'])
	{
		m_editor->navigate(0.0f, -1.0f, speed);
	}
	if (ImGui::GetIO().KeysDown['D'])
	{
		m_editor->navigate(0.0f, 1.0f, speed);
	}
}

void SceneView::renderIcons()
{
	m_editor->renderIcons();
}

void SceneView::renderGizmos()
{
	m_editor->getGizmo().render();
}

void SceneView::captureMouse(bool capture)
{
	if(m_is_mouse_captured == capture) return;
	m_is_mouse_captured = capture;
	PlatformInterface::showCursor(!m_is_mouse_captured);
	if(!m_is_mouse_captured) PlatformInterface::unclipCursor();
}

void SceneView::onGUI()
{
	PROFILE_FUNCTION();
	m_is_opened = false;
	ImVec2 view_pos;
	const char* title = "Scene View###Scene View";
	if (m_log_ui && m_log_ui->getUnreadErrorCount() > 0)
	{
		title = "Scene View | errors in log###Scene View";
	}

	if (ImGui::BeginDock(title))
	{
		m_is_opened = true;
		auto size = ImGui::GetContentRegionAvail();
		size.y -= ImGui::GetTextLineHeightWithSpacing();
		auto* fb = m_pipeline->getFramebuffer("default");
		if (size.x > 0 && size.y > 0 && fb)
		{
			auto pos = ImGui::GetWindowPos();
			m_pipeline->setViewport(0, 0, int(size.x), int(size.y));
			m_texture_handle = fb->getRenderbufferHandle(0);
			auto cursor_pos = ImGui::GetCursorScreenPos();
			m_screen_x = int(cursor_pos.x);
			m_screen_y = int(cursor_pos.y);
			m_width = int(size.x);
			m_height = int(size.y);
			auto content_min = ImGui::GetCursorScreenPos();
			ImVec2 content_max(content_min.x + size.x, content_min.y + size.y);
			ImGui::Image(&m_texture_handle, size);
			view_pos = content_min;
			auto rel_mp = ImGui::GetMousePos();
			rel_mp.x -= m_screen_x;
			rel_mp.y -= m_screen_y;
			if (ImGui::IsItemHovered())
			{
				m_editor->getGizmo().enableStep(m_toggle_gizmo_step_action->isActive());
				for (int i = 0; i < 3; ++i)
				{
					if (ImGui::IsMouseClicked(i))
					{
						ImGui::ResetActiveID();
						captureMouse(true);
						m_editor->onMouseDown((int)rel_mp.x, (int)rel_mp.y, (Lumix::MouseButton::Value)i);
						break;
					}
				}
			}
			if (m_is_mouse_captured || ImGui::IsItemHovered())
			{
				auto& input = m_editor->getEngine().getInputSystem();
				auto delta = Lumix::Vec2(input.getMouseXMove(), input.getMouseYMove());
				if (delta.x != 0 || delta.y != 0)
				{
					m_editor->onMouseMove((int)rel_mp.x, (int)rel_mp.y, (int)delta.x, (int)delta.y);
				}
			}
			if(m_is_mouse_captured)
			{
				PlatformInterface::clipCursor(
					content_min.x, content_min.y, content_max.x, content_max.y);
				for (int i = 0; i < 3; ++i)
				{
					auto rel_mp = ImGui::GetMousePos();
					rel_mp.x -= m_screen_x;
					rel_mp.y -= m_screen_y;
					if (ImGui::IsMouseReleased(i))
					{
						captureMouse(false);
						m_editor->onMouseUp((int)rel_mp.x, (int)rel_mp.y, (Lumix::MouseButton::Value)i);
					}
				}
			}
			m_pipeline->render();
		}

		ImGui::PushItemWidth(60);
		ImGui::DragFloat("Camera speed", &m_camera_speed, 0.1f, 0.01f, 999.0f, "%.2f");
		ImGui::SameLine();
		if (m_editor->isMeasureToolActive())
		{
			ImGui::Text("| Measured distance: %f", m_editor->getMeasuredDistance());
		}

		ImGui::SameLine();
		int step = m_editor->getGizmo().getStep();
		if (ImGui::DragInt("Gizmo step", &step, 1.0f, 0, 200))
		{
			m_editor->getGizmo().setStep(step);
		}

		ImGui::SameLine();
		ImGui::Checkbox("Stats", &m_show_stats);

		ImGui::SameLine();
	}

	ImGui::EndDock();

	if(m_show_stats)
	{
		view_pos.x += ImGui::GetStyle().FramePadding.x;
		view_pos.y += ImGui::GetStyle().FramePadding.y;
		ImGui::SetNextWindowPos(view_pos);
		auto col = ImGui::GetStyle().Colors[ImGuiCol_WindowBg];
		col.w = 0.3f;
		ImGui::PushStyleColor(ImGuiCol_WindowBg, col);
		if (ImGui::Begin("###stats_overlay",
				nullptr,
				ImGuiWindowFlags_NoTitleBar | ImGuiWindowFlags_AlwaysAutoResize |
					ImGuiWindowFlags_NoMove | ImGuiWindowFlags_NoSavedSettings |
					ImGuiWindowFlags_ShowBorders))
		{
			const auto& stats = m_pipeline->getStats();
			ImGui::LabelText("Draw calls", "%d", stats.m_draw_call_count);
			ImGui::LabelText("Instances", "%d", stats.m_instance_count);
			char buf[30];
			Lumix::toCStringPretty(stats.m_triangle_count, buf, Lumix::lengthOf(buf));
			ImGui::LabelText("Triangles", buf);
		}
		ImGui::End();
		ImGui::PopStyleColor();
	}
}<|endoftext|>