<commit_before>/***************************************************************************
 *   Copyright (C) 2008 by Jason Ansel                                     *
 *   jansel@csail.mit.edu                                                  *
 *                                                                         *
 *   This program is free software; you can redistribute it and/or modify  *
 *   it under the terms of the GNU General Public License as published by  *
 *   the Free Software Foundation; either version 3 of the License, or     *
 *   (at your option) any later version.                                   *
 *                                                                         *
 *   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 General Public License     *
 *   along with this program; if not, write to the                         *
 *   Free Software Foundation, Inc.,                                       *
 *   59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.             *
 ***************************************************************************/
#include "staticscheduler.h"
#include "transform.h"
#include "jasm.h"
#include "codegenerator.h"

namespace { //file local
void _remapSet(petabricks::ScheduleNodeSet& set, const petabricks::ScheduleNodeRemapping& map){
  using namespace petabricks;
  for(ScheduleNodeRemapping::const_iterator i=map.begin(); i!=map.end(); ++i){
    ScheduleNodeSet::iterator rslt = set.find(i->first);
    if(rslt!=set.end()){
      set.erase(rslt);
      set.insert(i->second);
    }
  }
}
}

petabricks::ScheduleNode::ScheduleNode()
  : _isInput(false)
  , _isLast(false)
{
  static jalib::AtomicT i=0;
  _id=jalib::atomicIncrementReturn(&i);
}

petabricks::StaticScheduler::StaticScheduler(const ChoiceGridMap& cg){
  for(ChoiceGridMap::const_iterator m=cg.begin(); m!=cg.end(); ++m){
    ScheduleNodeList& regions = _matrixToNodes[m->first];
    for(ChoiceGridIndex::const_iterator i=m->second.begin(); i!=m->second.end(); ++i){
      SimpleRegionPtr tmp = new SimpleRegion(i->first);
      regions.push_back(new UnischeduledNode(m->first, tmp, i->second));
      _allNodes.push_back(regions.back());
    }
  }
}

petabricks::ScheduleNodeSet petabricks::StaticScheduler::lookupNode(const MatrixDefPtr& matrix, const SimpleRegionPtr& region){
  ScheduleNodeSet rv;
  ScheduleNodeList& regions = _matrixToNodes[matrix];
  if(matrix->numDimensions()==0){
    JASSERT(regions.size()==1);
    rv.insert(regions.begin()->asPtr());
  }else{
    for(ScheduleNodeList::iterator i=regions.begin(); i!=regions.end(); ++i){
      if(region->toString() == (*i)->region()->toString()){
        rv.insert(i->asPtr());
        break; //optimization
      }
      if((*i)->region()->hasIntersect(region)){
        rv.insert(i->asPtr());
      }
    }
  }
  JASSERT(rv.size()>0)(matrix)(region).Text("failed to find rule for region");
  return rv;
}

void petabricks::StaticScheduler::generateSchedule(){
  #ifdef DEBUG
//   writeGraphAsPDF("schedule_initial.pdf");
  #endif

  computeIndirectDependencies();

  mergeCoscheduledNodes();

  #ifdef DEBUG
  writeGraphAsPDF("schedule.pdf");
  #endif

  for( std::set<ScheduleNode*>::const_iterator i=_goals.begin()
      ; i!=_goals.end()
      ; ++i )
  {
    depthFirstSchedule(*i);
  }
}

void petabricks::StaticScheduler::computeIndirectDependencies(){
  // this algorithm can be optimized, but since graphs are small it doesn't matter
  for(int c=1; c>0;){ //keep interating until no changes have been made
    c=0;
    for(ScheduleNodeList::iterator i=_allNodes.begin(); i!=_allNodes.end(); ++i)
      c+=(*i)->updateIndirectDepends();
  }
}

void petabricks::StaticScheduler::mergeCoscheduledNodes(){
  ScheduleNodeSet done;
  ScheduleNodeRemapping mapping;
  ScheduleNodeList tmp = _allNodes;
  for(ScheduleNodeList::iterator i=tmp.begin(); i!=tmp.end(); ++i){
    if(done.find(i->asPtr()) == done.end()){
      ScheduleNodeSet set=(*i)->getStronglyConnectedComponent();
      done.insert(set.begin(),set.end());
      if(set.size()>1){
        JTRACE("coscheduling nodes")((*i)->nodename())(set.size());
        ScheduleNode* coscheduled = new CoscheduledNode(set);
        _allNodes.push_back(coscheduled);
        for(ScheduleNodeSet::iterator e=set.begin();e!=set.end(); ++e)
          mapping[*e] = coscheduled;
      }
    }
  }
  applyRemapping(mapping);
}

void petabricks::StaticScheduler::applyRemapping(const ScheduleNodeRemapping& m){
  for(ScheduleNodeList::iterator i=_allNodes.begin(); i!=_allNodes.end(); ++i){
      (*i)->applyRemapping(m);
  }
  _remapSet(_goals, m);
  _remapSet(_generated, m);
  _remapSet(_pending, m);
  ScheduleNodeList tmp;
  _allNodes.swap(tmp);
  _allNodes.reserve(tmp.size());
  for(ScheduleNodeList::iterator i=tmp.begin(); i!=tmp.end(); ++i){
    if(m.find(i->asPtr())==m.end())
      _allNodes.push_back(*i);
    else
      _remappedNodes.push_back(*i);
  }
}

void petabricks::StaticScheduler::depthFirstSchedule(ScheduleNode* n){
  if(_generated.find(n)!=_generated.end())
    return;

  JASSERT(_pending.find(n)==_pending.end()).Text("dependency cycle");
  _pending.insert(n);

  for( ScheduleDependencies::const_iterator i=n->directDepends().begin()
      ; i!=n->directDepends().end()
      ; ++i)
  {
    if(i->first != n)
      depthFirstSchedule(i->first);
  }
//   JTRACE("scheduling")(n->matrix());
  _schedule.push_back(n);
  _generated.insert(n);
}

void petabricks::StaticScheduler::generateCodeDynamic(Transform& trans, CodeGenerator& o){
  JASSERT(_schedule.size()>0);
  for(ScheduleNodeList::iterator i=_schedule.begin(); i!=_schedule.end(); ++i){
    if(i!=_schedule.begin()) o.continuationPoint();
    (*i)->generateCodeSimple(trans, o, false);
  }
  o.write("DynamicTaskPtr  _fini = new NullDynamicTask();");
  for(ScheduleNodeSet::iterator i=_goals.begin(); i!=_goals.end(); ++i)
    o.write("_fini->dependsOn(" + (*i)->nodename() + ".completionTask());");
  o.withEachMember("SpatialTaskList", ".clear()");
  o.withEachMember("DynamicTaskPtr", "=0");
  o.write("return _fini;");
}
void petabricks::StaticScheduler::generateCodeStatic(Transform& trans, CodeGenerator& o){
  JASSERT(_schedule.size()>0);
  for(ScheduleNodeList::iterator i=_schedule.begin(); i!=_schedule.end(); ++i){
    (*i)->generateCodeSimple(trans, o, true);
  }
}

void petabricks::UnischeduledNode::generateCodeSimple(Transform& trans, CodeGenerator& o, bool isStatic){
  RuleChoicePtr rule = trans.learner().makeRuleChoice(_choices->rules(), _matrix, _region);
  if(!isStatic){
    o.addMember("SpatialTaskList", nodename(), "");
    rule->generateCodeSimple(false, nodename(), trans, *this, _region, o);
  }else{
    rule->generateCodeSimple(true, "", trans, *this, _region, o);
  }
}

void petabricks::ScheduleNode::printDepsAndEnqueue(CodeGenerator& o, Transform& trans,  const RulePtr& rule, bool useDirections){
//bool printedBeforeDep = false;

  ScheduleDependencies::const_iterator sd = _indirectDepends.find(this);
  if(sd==_indirectDepends.end() && rule->isSingleElement()){
    trans.markSplitSizeUse(o);
    o.write(nodename()+".spatialSplit("SPLIT_CHUNK_SIZE");");
  }else{
    //TODO split selfdep tasks too
  }

  for(ScheduleDependencies::const_iterator i=_directDepends.begin();  i!=_directDepends.end(); ++i){
    if(i->first!=this){
      if(i->first->isInput()){
//      if(!printedBeforeDep){
//       printedBeforeDep=true;
//       if(useDirections)
//         o.write(nodename()+".dependsOn(_before);");
//       else
//         o.write(nodename()+"->dependsOn(_before);");
//      }
      }else{
        if(useDirections){
          o.write("{");
          o.incIndent();
          o.write("DependencyDirection::DirectionT _dir[] = {"+i->second.direction.toCodeStr()+"};");
          o.write(nodename()+".dependsOn<"+jalib::XToString(i->second.direction.size())+">"
                            "("+i->first->nodename()+", _dir);");
          o.decIndent();
          o.write("}");
        }else{
          o.write(nodename()+"->dependsOn("+i->first->nodename()+");");
        }
      }
    }
  }
  if(!_isLast)
    o.write(nodename()+"->enqueue();");
}

void petabricks::UnischeduledNode::generateCodeForSlice(Transform& trans, CodeGenerator& o, int d, const FormulaPtr& pos, bool isStatic){
  RuleChoicePtr rule = trans.learner().makeRuleChoice(_choices->rules(), _matrix, _region);

  CoordinateFormula min = _region->minCoord();
  CoordinateFormula max = _region->maxCoord();

  min[d] = pos;
  max[d] = pos->plusOne();

  SimpleRegionPtr t = new SimpleRegion(min,max);

  rule->generateCodeSimple(isStatic, "", trans, *this, t, o);
  //TODO deps for slice
}

void petabricks::StaticScheduler::writeGraphAsPDF(const char* filename) const{
  std::string schedulerGraph = toString();
  FILE* fd = popen(("dot -Grankdir=LR -Tpdf -o "+std::string(filename)).c_str(), "w");
  fwrite(schedulerGraph.c_str(),1,schedulerGraph.length(),fd);
  pclose(fd);
}

int petabricks::ScheduleNode::updateIndirectDepends(){
  int c = 0;
  if(_indirectDepends.empty()){  // seed first iteration
    _indirectDepends = _directDepends;
    c+=_indirectDepends.size();
  }
  ScheduleDependencies tmp = _indirectDepends;
  for(ScheduleDependencies::iterator i=tmp.begin(); i!=tmp.end(); ++i){
    const ScheduleDependencies& remote = i->first->indirectDepends();
    for( ScheduleDependencies::const_iterator dep=remote.begin(); dep!=remote.end(); ++dep)
    { //for each dependency
      if(_indirectDepends[dep->first].merge(dep->second))
        ++c;
    }
  }
  return c;
}

petabricks::ScheduleNodeSet petabricks::ScheduleNode::getStronglyConnectedComponent(){
  /// compute strongly connected component
  ScheduleNodeSet s;
  s.insert(this);
  if(_indirectDepends.find(this)==_indirectDepends.end())
    return s;

  for(ScheduleDependencies::iterator i=_indirectDepends.begin(); i!=_indirectDepends.end(); ++i){
    if(i->first->indirectDepends().find(this)!=i->first->indirectDepends().end()) //if in a cycle with this
      s.insert(i->first);
  }
  return s;
}

petabricks::CoscheduledNode::CoscheduledNode(const ScheduleNodeSet& set)
  : _originalNodes(set)
{
  for(ScheduleNodeSet::const_iterator i=set.begin(); i!=set.end(); ++i){
    _directDepends.merge((*i)->directDepends());
    _indirectDepends.merge((*i)->indirectDepends());
  }
}

void petabricks::CoscheduledNode::generateCodeSimple(Transform& trans, CodeGenerator& o, bool isStatic){
  const DependencyInformation& selfDep = _indirectDepends[this];

  if(selfDep.direction.isNone()){
    if(!isStatic) o.addMember("SpatialTaskList", nodename(), "");
    o.comment("Dual outputs compacted "+nodename());
    std::string region;
    ScheduleNode* first = NULL;
    //test matching region extents in d
    for(ScheduleNodeSet::const_iterator i=_originalNodes.begin(); i!=_originalNodes.end(); ++i){
      if(first==NULL){
        region=(*i)->region()->toString();
        first=*i;
      }
      else if(first->region()->dimensions()<(*i)->region()->dimensions())
        first=*i;
      JWARNING(region==(*i)->region()->toString())(region)((*i)->region()->toString())
        .Text("to(...) regions of differing size not yet supported");
    }
    RuleChoicePtr rule = trans.learner().makeRuleChoice(first->choices()->rules(), first->matrix(), first->region());
    rule->generateCodeSimple(isStatic, nodename(), trans, *this, first->region(), o);
  }else{
    if(!isStatic) o.addMember("DynamicTaskPtr", nodename(),"");
    std::vector<std::string> args;
    args.push_back("const jalib::JRef<"+trans.instClassName()+"> transform");
    TaskCodeGenerator* task;
    if(!isStatic) task=&o.createTask("coscheduled_"+nodename(), args, "DynamicTask", "_task");
    CodeGenerator& ot = isStatic ? o : *task;
    std::string varname="coscheduled_"+nodename();
    if(!isStatic){
      task->beginRunFunc();
      for(FreeVars::const_iterator i=trans.constants().begin()
         ;i!=trans.constants().end()
         ;++i)
      {
        ot.define(*i, "(transform->"+*i+")");
      }
    }

    for(size_t d=selfDep.direction.size()-1; d>=0; --d){
      bool passed=true;
      FormulaPtr begin,end;

      //test matching region extents in d
      for(ScheduleNodeSet::const_iterator i=_originalNodes.begin(); i!=_originalNodes.end(); ++i){
        if(!begin) begin = (*i)->region()->minCoord()[d];
        if(!end)   end   = (*i)->region()->maxCoord()[d];
        if(   begin->toString() != (*i)->region()->minCoord()[d]->toString()
          ||  end->toString()   != (*i)->region()->maxCoord()[d]->toString())
        {
          JTRACE("Can't coschedule due to mismatched extents")(d);
          passed=false;
          break;
        }
      }
      if(!passed) continue;

      //test direction
      if((selfDep.direction[d]& ~DependencyDirection::D_LE) == 0){
        JTRACE("Coscheduling forward")(d)(*this);
        ot.beginFor(varname, begin, end, FormulaInteger::one());
      }else if((selfDep.direction[d]& ~DependencyDirection::D_GE) == 0){
        JTRACE("Coscheduling backward")(d)(*this);
        ot.beginReverseFor(varname, begin, end, FormulaInteger::one());
      }else{
        JTRACE("Can't coschedule due to mismatched direction")(d);
        passed=false;
        continue;
      }

      for(ScheduleNodeSet::iterator i=_originalNodes.begin(); i!=_originalNodes.end(); ++i){
        (*i)->generateCodeForSlice(trans, ot, d, new FormulaVariable(varname), isStatic);
      }
      ot.endFor();
      if(!isStatic){
        ot.write("return NULL;");
        ot.undefineAll();
        ot.endFunc();
        std::vector<std::string> args(1, "this");
        o.setcall(nodename(),"new "+varname+"_task", args);
        printDepsAndEnqueue(o, trans, NULL, false);
      }
      return;
    }
    JASSERT(false)(*this)(selfDep.direction).Text("Unresolved dependency cycle");
  }
}
<commit_msg>- fixed input_count bug for coscheduled tasks (rootsolve bug)<commit_after>/***************************************************************************
 *   Copyright (C) 2008 by Jason Ansel                                     *
 *   jansel@csail.mit.edu                                                  *
 *                                                                         *
 *   This program is free software; you can redistribute it and/or modify  *
 *   it under the terms of the GNU General Public License as published by  *
 *   the Free Software Foundation; either version 3 of the License, or     *
 *   (at your option) any later version.                                   *
 *                                                                         *
 *   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 General Public License     *
 *   along with this program; if not, write to the                         *
 *   Free Software Foundation, Inc.,                                       *
 *   59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.             *
 ***************************************************************************/
#include "staticscheduler.h"
#include "transform.h"
#include "jasm.h"
#include "codegenerator.h"

namespace { //file local
void _remapSet(petabricks::ScheduleNodeSet& set, const petabricks::ScheduleNodeRemapping& map){
  using namespace petabricks;
  for(ScheduleNodeRemapping::const_iterator i=map.begin(); i!=map.end(); ++i){
    ScheduleNodeSet::iterator rslt = set.find(i->first);
    if(rslt!=set.end()){
      set.erase(rslt);
      set.insert(i->second);
    }
  }
}
}

petabricks::ScheduleNode::ScheduleNode()
  : _isInput(false)
  , _isLast(false)
{
  static jalib::AtomicT i=0;
  _id=jalib::atomicIncrementReturn(&i);
}

petabricks::StaticScheduler::StaticScheduler(const ChoiceGridMap& cg){
  for(ChoiceGridMap::const_iterator m=cg.begin(); m!=cg.end(); ++m){
    ScheduleNodeList& regions = _matrixToNodes[m->first];
    for(ChoiceGridIndex::const_iterator i=m->second.begin(); i!=m->second.end(); ++i){
      SimpleRegionPtr tmp = new SimpleRegion(i->first);
      regions.push_back(new UnischeduledNode(m->first, tmp, i->second));
      _allNodes.push_back(regions.back());
    }
  }
}

petabricks::ScheduleNodeSet petabricks::StaticScheduler::lookupNode(const MatrixDefPtr& matrix, const SimpleRegionPtr& region){
  ScheduleNodeSet rv;
  ScheduleNodeList& regions = _matrixToNodes[matrix];
  if(matrix->numDimensions()==0){
    JASSERT(regions.size()==1);
    rv.insert(regions.begin()->asPtr());
  }else{
    for(ScheduleNodeList::iterator i=regions.begin(); i!=regions.end(); ++i){
      if(region->toString() == (*i)->region()->toString()){
        rv.insert(i->asPtr());
        break; //optimization
      }
      if((*i)->region()->hasIntersect(region)){
        rv.insert(i->asPtr());
      }
    }
  }
  JASSERT(rv.size()>0)(matrix)(region).Text("failed to find rule for region");
  return rv;
}

void petabricks::StaticScheduler::generateSchedule(){
  #ifdef DEBUG
//   writeGraphAsPDF("schedule_initial.pdf");
  #endif

  computeIndirectDependencies();

  mergeCoscheduledNodes();

  #ifdef DEBUG
  writeGraphAsPDF("schedule.pdf");
  #endif

  for( std::set<ScheduleNode*>::const_iterator i=_goals.begin()
      ; i!=_goals.end()
      ; ++i )
  {
    depthFirstSchedule(*i);
  }
}

void petabricks::StaticScheduler::computeIndirectDependencies(){
  // this algorithm can be optimized, but since graphs are small it doesn't matter
  for(int c=1; c>0;){ //keep interating until no changes have been made
    c=0;
    for(ScheduleNodeList::iterator i=_allNodes.begin(); i!=_allNodes.end(); ++i)
      c+=(*i)->updateIndirectDepends();
  }
}

void petabricks::StaticScheduler::mergeCoscheduledNodes(){
  ScheduleNodeSet done;
  ScheduleNodeRemapping mapping;
  ScheduleNodeList tmp = _allNodes;
  for(ScheduleNodeList::iterator i=tmp.begin(); i!=tmp.end(); ++i){
    if(done.find(i->asPtr()) == done.end()){
      ScheduleNodeSet set=(*i)->getStronglyConnectedComponent();
      done.insert(set.begin(),set.end());
      if(set.size()>1){
        JTRACE("coscheduling nodes")((*i)->nodename())(set.size());
        ScheduleNode* coscheduled = new CoscheduledNode(set);
        _allNodes.push_back(coscheduled);
        for(ScheduleNodeSet::iterator e=set.begin();e!=set.end(); ++e)
          mapping[*e] = coscheduled;
      }
    }
  }
  applyRemapping(mapping);
}

void petabricks::StaticScheduler::applyRemapping(const ScheduleNodeRemapping& m){
  for(ScheduleNodeList::iterator i=_allNodes.begin(); i!=_allNodes.end(); ++i){
      (*i)->applyRemapping(m);
  }
  _remapSet(_goals, m);
  _remapSet(_generated, m);
  _remapSet(_pending, m);
  ScheduleNodeList tmp;
  _allNodes.swap(tmp);
  _allNodes.reserve(tmp.size());
  for(ScheduleNodeList::iterator i=tmp.begin(); i!=tmp.end(); ++i){
    if(m.find(i->asPtr())==m.end())
      _allNodes.push_back(*i);
    else
      _remappedNodes.push_back(*i);
  }
}

void petabricks::StaticScheduler::depthFirstSchedule(ScheduleNode* n){
  if(_generated.find(n)!=_generated.end())
    return;

  JASSERT(_pending.find(n)==_pending.end()).Text("dependency cycle");
  _pending.insert(n);

  for( ScheduleDependencies::const_iterator i=n->directDepends().begin()
      ; i!=n->directDepends().end()
      ; ++i)
  {
    if(i->first != n)
      depthFirstSchedule(i->first);
  }
//   JTRACE("scheduling")(n->matrix());
  _schedule.push_back(n);
  _generated.insert(n);
}

void petabricks::StaticScheduler::generateCodeDynamic(Transform& trans, CodeGenerator& o){
  JASSERT(_schedule.size()>0);
  for(ScheduleNodeList::iterator i=_schedule.begin(); i!=_schedule.end(); ++i){
    if(i!=_schedule.begin()) o.continuationPoint();
    (*i)->generateCodeSimple(trans, o, false);
  }
  o.write("DynamicTaskPtr  _fini = new NullDynamicTask();");
  for(ScheduleNodeSet::iterator i=_goals.begin(); i!=_goals.end(); ++i)
    o.write("_fini->dependsOn(" + (*i)->nodename() + ".completionTask());");
  o.withEachMember("SpatialTaskList", ".clear()");
  o.withEachMember("DynamicTaskPtr", "=0");
  o.write("return _fini;");
}
void petabricks::StaticScheduler::generateCodeStatic(Transform& trans, CodeGenerator& o){
  JASSERT(_schedule.size()>0);
  for(ScheduleNodeList::iterator i=_schedule.begin(); i!=_schedule.end(); ++i){
    (*i)->generateCodeSimple(trans, o, true);
  }
}

void petabricks::UnischeduledNode::generateCodeSimple(Transform& trans, CodeGenerator& o, bool isStatic){
  RuleChoicePtr rule = trans.learner().makeRuleChoice(_choices->rules(), _matrix, _region);
  if(!isStatic){
    o.addMember("SpatialTaskList", nodename(), "");
    rule->generateCodeSimple(false, nodename(), trans, *this, _region, o);
  }else{
    rule->generateCodeSimple(true, "", trans, *this, _region, o);
  }
}

void petabricks::ScheduleNode::printDepsAndEnqueue(CodeGenerator& o, Transform& trans,  const RulePtr& rule, bool useDirections){
//bool printedBeforeDep = false;

  ScheduleDependencies::const_iterator sd = _indirectDepends.find(this);
  if(sd==_indirectDepends.end() && rule->isSingleElement()){
    trans.markSplitSizeUse(o);
    o.write(nodename()+".spatialSplit("SPLIT_CHUNK_SIZE");");
  }else{
    //TODO split selfdep tasks too
  }

  for(ScheduleDependencies::const_iterator i=_directDepends.begin();  i!=_directDepends.end(); ++i){
    if(i->first!=this){
      if(i->first->isInput()){
//      if(!printedBeforeDep){
//       printedBeforeDep=true;
//       if(useDirections)
//         o.write(nodename()+".dependsOn(_before);");
//       else
//         o.write(nodename()+"->dependsOn(_before);");
//      }
      }else{
        if(useDirections){
          o.write("{");
          o.incIndent();
          o.write("DependencyDirection::DirectionT _dir[] = {"+i->second.direction.toCodeStr()+"};");
          o.write(nodename()+".dependsOn<"+jalib::XToString(i->second.direction.size())+">"
                            "("+i->first->nodename()+", _dir);");
          o.decIndent();
          o.write("}");
        }else{
          o.write(nodename()+"->dependsOn("+i->first->nodename()+");");
        }
      }
    }
  }
  if(!_isLast)
    o.write(nodename()+"->enqueue();");
}

void petabricks::UnischeduledNode::generateCodeForSlice(Transform& trans, CodeGenerator& o, int d, const FormulaPtr& pos, bool isStatic){
  RuleChoicePtr rule = trans.learner().makeRuleChoice(_choices->rules(), _matrix, _region);

  CoordinateFormula min = _region->minCoord();
  CoordinateFormula max = _region->maxCoord();

  min[d] = pos;
  max[d] = pos->plusOne();

  SimpleRegionPtr t = new SimpleRegion(min,max);

  rule->generateCodeSimple(isStatic, "", trans, *this, t, o);
  //TODO deps for slice
}

void petabricks::StaticScheduler::writeGraphAsPDF(const char* filename) const{
  std::string schedulerGraph = toString();
  FILE* fd = popen(("dot -Grankdir=LR -Tpdf -o "+std::string(filename)).c_str(), "w");
  fwrite(schedulerGraph.c_str(),1,schedulerGraph.length(),fd);
  pclose(fd);
}

int petabricks::ScheduleNode::updateIndirectDepends(){
  int c = 0;
  if(_indirectDepends.empty()){  // seed first iteration
    _indirectDepends = _directDepends;
    c+=_indirectDepends.size();
  }
  ScheduleDependencies tmp = _indirectDepends;
  for(ScheduleDependencies::iterator i=tmp.begin(); i!=tmp.end(); ++i){
    const ScheduleDependencies& remote = i->first->indirectDepends();
    for( ScheduleDependencies::const_iterator dep=remote.begin(); dep!=remote.end(); ++dep)
    { //for each dependency
      if(_indirectDepends[dep->first].merge(dep->second))
        ++c;
    }
  }
  return c;
}

petabricks::ScheduleNodeSet petabricks::ScheduleNode::getStronglyConnectedComponent(){
  /// compute strongly connected component
  ScheduleNodeSet s;
  s.insert(this);
  if(_indirectDepends.find(this)==_indirectDepends.end())
    return s;

  for(ScheduleDependencies::iterator i=_indirectDepends.begin(); i!=_indirectDepends.end(); ++i){
    if(i->first->indirectDepends().find(this)!=i->first->indirectDepends().end()) //if in a cycle with this
      s.insert(i->first);
  }
  return s;
}

petabricks::CoscheduledNode::CoscheduledNode(const ScheduleNodeSet& set)
  : _originalNodes(set)
{
  for(ScheduleNodeSet::const_iterator i=set.begin(); i!=set.end(); ++i){
    _directDepends.merge((*i)->directDepends());
    _indirectDepends.merge((*i)->indirectDepends());
  }
}

void petabricks::CoscheduledNode::generateCodeSimple(Transform& trans, CodeGenerator& o, bool isStatic){
  const DependencyInformation& selfDep = _indirectDepends[this];

  if(selfDep.direction.isNone()){
    if(!isStatic) o.addMember("SpatialTaskList", nodename(), "");
    o.comment("Dual outputs compacted "+nodename());
    std::string region;
    ScheduleNode* first = NULL;
    //test matching region extents in d
    for(ScheduleNodeSet::const_iterator i=_originalNodes.begin(); i!=_originalNodes.end(); ++i){
      if(first==NULL){
        region=(*i)->region()->toString();
        first=*i;
      }
      else if(first->region()->dimensions()<(*i)->region()->dimensions())
        first=*i;
      JWARNING(region==(*i)->region()->toString())(region)((*i)->region()->toString())
        .Text("to(...) regions of differing size not yet supported");
    }
    RuleChoicePtr rule = trans.learner().makeRuleChoice(first->choices()->rules(), first->matrix(), first->region());
    rule->generateCodeSimple(isStatic, nodename(), trans, *this, first->region(), o);
  }else{
    if(!isStatic) o.addMember("DynamicTaskPtr", nodename(),"");
    std::vector<std::string> args;
    args.push_back("const jalib::JRef<"+trans.instClassName()+"> transform");
    TaskCodeGenerator* task;
    if(!isStatic) task=&o.createTask("coscheduled_"+nodename(), args, "DynamicTask", "_task");
    CodeGenerator& ot = isStatic ? o : *task;
    std::string varname="coscheduled_"+nodename();
    if(!isStatic){
      task->beginRunFunc();
      for(FreeVars::const_iterator i=trans.constants().begin()
         ;i!=trans.constants().end()
         ;++i)
      {
        ot.define(*i, "(transform->"+*i+")");
      }
      ot.define("input_count", "(transform->input_count)");
    }

    for(size_t d=selfDep.direction.size()-1; d>=0; --d){
      bool passed=true;
      FormulaPtr begin,end;

      //test matching region extents in d
      for(ScheduleNodeSet::const_iterator i=_originalNodes.begin(); i!=_originalNodes.end(); ++i){
        if(!begin) begin = (*i)->region()->minCoord()[d];
        if(!end)   end   = (*i)->region()->maxCoord()[d];
        if(   begin->toString() != (*i)->region()->minCoord()[d]->toString()
          ||  end->toString()   != (*i)->region()->maxCoord()[d]->toString())
        {
          JTRACE("Can't coschedule due to mismatched extents")(d);
          passed=false;
          break;
        }
      }
      if(!passed) continue;

      //test direction
      if((selfDep.direction[d]& ~DependencyDirection::D_LE) == 0){
        JTRACE("Coscheduling forward")(d)(*this);
        ot.beginFor(varname, begin, end, FormulaInteger::one());
      }else if((selfDep.direction[d]& ~DependencyDirection::D_GE) == 0){
        JTRACE("Coscheduling backward")(d)(*this);
        ot.beginReverseFor(varname, begin, end, FormulaInteger::one());
      }else{
        JTRACE("Can't coschedule due to mismatched direction")(d);
        passed=false;
        continue;
      }

      for(ScheduleNodeSet::iterator i=_originalNodes.begin(); i!=_originalNodes.end(); ++i){
        (*i)->generateCodeForSlice(trans, ot, d, new FormulaVariable(varname), isStatic);
      }
      ot.endFor();
      if(!isStatic){
        ot.write("return NULL;");
        ot.undefineAll();
        ot.endFunc();
        std::vector<std::string> args(1, "this");
        o.setcall(nodename(),"new "+varname+"_task", args);
        printDepsAndEnqueue(o, trans, NULL, false);
      }
      return;
    }
    JASSERT(false)(*this)(selfDep.direction).Text("Unresolved dependency cycle");
  }
}
<|endoftext|>


<commit_before>/*
    Copyright (C) 2011  Lasath Fernando <kde@lasath.org>

    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 of the License, or (at your option) any later version.

    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, write to the Free Software
    Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
*/

#include "conversations-model.h"
#include "conversation.h"
#include "messages-model.h"

#include "debug.h"

#include <TelepathyQt/ChannelClassSpec>
#include <TelepathyQt/TextChannel>
#include <TelepathyQt/ClientRegistrar>

static inline Tp::ChannelClassSpecList channelClassList()
{
    return Tp::ChannelClassSpecList() << Tp::ChannelClassSpec::textChat();
}

class ConversationsModel::ConversationsModelPrivate
{
  public:
    QList<Conversation*> conversations;
    int activeChatIndex;
};

ConversationsModel::ConversationsModel(QObject *parent) :
        QAbstractListModel(parent),
        Tp::AbstractClientHandler(channelClassList()),
        d(new ConversationsModelPrivate)
{
    d->activeChatIndex = -1;
    connect(this, SIGNAL(rowsInserted(QModelIndex,int,int)), SIGNAL(totalUnreadCountChanged()));
    connect(this, SIGNAL(rowsRemoved(QModelIndex,int,int)), SIGNAL(totalUnreadCountChanged()));
}

ConversationsModel::~ConversationsModel()
{
    qDeleteAll(d->conversations);
    delete d;
}

QHash<int, QByteArray> ConversationsModel::roleNames() const
{
    QHash<int, QByteArray> roles = QAbstractListModel::roleNames();
    roles[ConversationRole] = "conversation";
    return roles;
}

QVariant ConversationsModel::data(const QModelIndex &index, int role) const
{
    QVariant result;
    if (index.isValid()) {
        if (role == ConversationRole) {
            result = QVariant::fromValue<Conversation*>(d->conversations[index.row()]);
        }
    }
    return result;
}

int ConversationsModel::rowCount(const QModelIndex &parent) const
{
    Q_UNUSED(parent);
    return d->conversations.count();
}

void ConversationsModel::handleChannels(const Tp::MethodInvocationContextPtr<> &context,
                                        const Tp::AccountPtr &account,
                                        const Tp::ConnectionPtr &connection,
                                        const QList<Tp::ChannelPtr> &channels,
                                        const QList<Tp::ChannelRequestPtr> &channelRequests,
                                        const QDateTime &userActionTime,
                                        const HandlerInfo &handlerInfo)
{
    Q_UNUSED(connection);
    Q_UNUSED(handlerInfo);
    Q_UNUSED(userActionTime);

    bool handled = false;
    bool shouldDelegate = false;

    //check that the channel is of type text
    Tp::TextChannelPtr textChannel;
    Q_FOREACH(const Tp::ChannelPtr &channel, channels) {
        textChannel = Tp::TextChannelPtr::dynamicCast(channel);
        if (textChannel) {
            break;
        }
    }

    Q_ASSERT(textChannel);

    //find the relevant channelRequest
    Q_FOREACH(const Tp::ChannelRequestPtr channelRequest, channelRequests) {
        qCDebug(KTP_DECLARATIVE) << channelRequest->hints().allHints();
        shouldDelegate = channelRequest->hints().hint(QLatin1String("org.freedesktop.Telepathy.ChannelRequest"), QLatin1String("DelegateToPreferredHandler")).toBool();
    }

    //loop through all conversations checking for matches

    //if we are handling and we're not told to delegate it, update the text channel
    //if we are handling but should delegate, call delegate channel
    int i = 0;
    Q_FOREACH(Conversation *convo, d->conversations) {
        if (convo->textChannel()->targetId() == textChannel->targetId() &&
                convo->textChannel()->targetHandleType() == textChannel->targetHandleType())
        {
            if (!shouldDelegate) {
                convo->setTextChannel(textChannel);
                //Update the active chat index to this channel
                d->activeChatIndex = i;
                Q_EMIT activeChatIndexChanged();
            } else {
                if (convo->textChannel() == textChannel) {
                    convo->delegateToProperClient();
                }
            }
            handled = true;
            break;
        }
        i++;
    }

    //if we are not handling channel already and should not delegate, add the conversation
    //if we not handling the channel but should delegate it, do nothing.
    if (!handled && !shouldDelegate) {
        beginInsertRows(QModelIndex(), rowCount(), rowCount());
        Conversation *newConvo = new Conversation(textChannel, account, this);
        connect(newConvo, SIGNAL(conversationCloseRequested()), SLOT(onConversationCloseRequested()));
        connect(newConvo->messages(), SIGNAL(unreadCountChanged(int)), SIGNAL(totalUnreadCountChanged()));
        d->conversations.append(newConvo);
        endInsertRows();

        //If this is a locally generated request or there is no active chat, the index of the newly inserted conversation is saved as the active chat
        //The model is reset to load the newly created chat channel
        if(textChannel->isRequested() || d->activeChatIndex == -1) {
            d->activeChatIndex = rowCount() - 1;
            Q_EMIT activeChatIndexChanged();
        }
        context->setFinished();
    }
}

bool ConversationsModel::bypassApproval() const
{
    return true;
}

void ConversationsModel::onConversationCloseRequested()
{
    removeConversation(qobject_cast<Conversation*>(QObject::sender()));
}

void ConversationsModel::removeConversation(Conversation* conv)
{
    int index = d->conversations.indexOf(conv);
    if (index != -1) {
        beginRemoveRows(QModelIndex(), index, index);
        d->conversations.removeAt(index);
        conv->deleteLater();
        endRemoveRows();
    } else {
        qWarning() << "attempting to delete non-existent conversation";
    }
}

int ConversationsModel::nextActiveConversation(int fromRow)
{
    if(d->conversations.isEmpty()) {
        return -1;
    }
    Q_ASSERT(qBound(0, fromRow, d->conversations.count()-1) == fromRow);

    bool first = true; //let first be checked on the first loop
    for(int i = fromRow; i != fromRow || first; i = (i + 1) % d->conversations.count()) {
        if(d->conversations[i]->messages()->unreadCount() > 0) {
            return i;
        }
        first = false;
    }
    return -1;
}

int ConversationsModel::totalUnreadCount() const
{
    int ret = 0;
    Q_FOREACH(Conversation *c, d->conversations) {
        ret += c->messages()->unreadCount();
    }
    return ret;
}

int ConversationsModel::activeChatIndex() const
{
    return d->activeChatIndex;
}

void ConversationsModel::closeAllConversations()
{
    Q_FOREACH(Conversation *c, d->conversations) {
        c->requestClose();
    }
}
<commit_msg>Improve how we close all conversations at once<commit_after>/*
    Copyright (C) 2011  Lasath Fernando <kde@lasath.org>

    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 of the License, or (at your option) any later version.

    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, write to the Free Software
    Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
*/

#include "conversations-model.h"
#include "conversation.h"
#include "messages-model.h"

#include "debug.h"

#include <TelepathyQt/ChannelClassSpec>
#include <TelepathyQt/TextChannel>
#include <TelepathyQt/ClientRegistrar>

static inline Tp::ChannelClassSpecList channelClassList()
{
    return Tp::ChannelClassSpecList() << Tp::ChannelClassSpec::textChat();
}

class ConversationsModel::ConversationsModelPrivate
{
  public:
    QList<Conversation*> conversations;
    int activeChatIndex;
};

ConversationsModel::ConversationsModel(QObject *parent) :
        QAbstractListModel(parent),
        Tp::AbstractClientHandler(channelClassList()),
        d(new ConversationsModelPrivate)
{
    d->activeChatIndex = -1;
    connect(this, SIGNAL(rowsInserted(QModelIndex,int,int)), SIGNAL(totalUnreadCountChanged()));
    connect(this, SIGNAL(rowsRemoved(QModelIndex,int,int)), SIGNAL(totalUnreadCountChanged()));
}

ConversationsModel::~ConversationsModel()
{
    qDeleteAll(d->conversations);
    delete d;
}

QHash<int, QByteArray> ConversationsModel::roleNames() const
{
    QHash<int, QByteArray> roles = QAbstractListModel::roleNames();
    roles[ConversationRole] = "conversation";
    return roles;
}

QVariant ConversationsModel::data(const QModelIndex &index, int role) const
{
    QVariant result;
    if (index.isValid()) {
        if (role == ConversationRole) {
            result = QVariant::fromValue<Conversation*>(d->conversations[index.row()]);
        }
    }
    return result;
}

int ConversationsModel::rowCount(const QModelIndex &parent) const
{
    Q_UNUSED(parent);
    return d->conversations.count();
}

void ConversationsModel::handleChannels(const Tp::MethodInvocationContextPtr<> &context,
                                        const Tp::AccountPtr &account,
                                        const Tp::ConnectionPtr &connection,
                                        const QList<Tp::ChannelPtr> &channels,
                                        const QList<Tp::ChannelRequestPtr> &channelRequests,
                                        const QDateTime &userActionTime,
                                        const HandlerInfo &handlerInfo)
{
    Q_UNUSED(connection);
    Q_UNUSED(handlerInfo);
    Q_UNUSED(userActionTime);

    bool handled = false;
    bool shouldDelegate = false;

    //check that the channel is of type text
    Tp::TextChannelPtr textChannel;
    Q_FOREACH(const Tp::ChannelPtr &channel, channels) {
        textChannel = Tp::TextChannelPtr::dynamicCast(channel);
        if (textChannel) {
            break;
        }
    }

    Q_ASSERT(textChannel);

    //find the relevant channelRequest
    Q_FOREACH(const Tp::ChannelRequestPtr channelRequest, channelRequests) {
        qCDebug(KTP_DECLARATIVE) << channelRequest->hints().allHints();
        shouldDelegate = channelRequest->hints().hint(QLatin1String("org.freedesktop.Telepathy.ChannelRequest"), QLatin1String("DelegateToPreferredHandler")).toBool();
    }

    //loop through all conversations checking for matches

    //if we are handling and we're not told to delegate it, update the text channel
    //if we are handling but should delegate, call delegate channel
    int i = 0;
    Q_FOREACH(Conversation *convo, d->conversations) {
        if (convo->textChannel()->targetId() == textChannel->targetId() &&
                convo->textChannel()->targetHandleType() == textChannel->targetHandleType())
        {
            if (!shouldDelegate) {
                convo->setTextChannel(textChannel);
                //Update the active chat index to this channel
                d->activeChatIndex = i;
                Q_EMIT activeChatIndexChanged();
            } else {
                if (convo->textChannel() == textChannel) {
                    convo->delegateToProperClient();
                }
            }
            handled = true;
            break;
        }
        i++;
    }

    //if we are not handling channel already and should not delegate, add the conversation
    //if we not handling the channel but should delegate it, do nothing.
    if (!handled && !shouldDelegate) {
        beginInsertRows(QModelIndex(), rowCount(), rowCount());
        Conversation *newConvo = new Conversation(textChannel, account, this);
        connect(newConvo, SIGNAL(conversationCloseRequested()), SLOT(onConversationCloseRequested()));
        connect(newConvo->messages(), SIGNAL(unreadCountChanged(int)), SIGNAL(totalUnreadCountChanged()));
        d->conversations.append(newConvo);
        endInsertRows();

        //If this is a locally generated request or there is no active chat, the index of the newly inserted conversation is saved as the active chat
        //The model is reset to load the newly created chat channel
        if(textChannel->isRequested() || d->activeChatIndex == -1) {
            d->activeChatIndex = rowCount() - 1;
            Q_EMIT activeChatIndexChanged();
        }
        context->setFinished();
    }
}

bool ConversationsModel::bypassApproval() const
{
    return true;
}

void ConversationsModel::onConversationCloseRequested()
{
    removeConversation(qobject_cast<Conversation*>(QObject::sender()));
}

void ConversationsModel::removeConversation(Conversation* conv)
{
    int index = d->conversations.indexOf(conv);
    if (index != -1) {
        beginRemoveRows(QModelIndex(), index, index);
        d->conversations.removeAt(index);
        conv->deleteLater();
        endRemoveRows();
    } else {
        qWarning() << "attempting to delete non-existent conversation";
    }
}

int ConversationsModel::nextActiveConversation(int fromRow)
{
    if(d->conversations.isEmpty()) {
        return -1;
    }
    Q_ASSERT(qBound(0, fromRow, d->conversations.count()-1) == fromRow);

    bool first = true; //let first be checked on the first loop
    for(int i = fromRow; i != fromRow || first; i = (i + 1) % d->conversations.count()) {
        if(d->conversations[i]->messages()->unreadCount() > 0) {
            return i;
        }
        first = false;
    }
    return -1;
}

int ConversationsModel::totalUnreadCount() const
{
    int ret = 0;
    Q_FOREACH(Conversation *c, d->conversations) {
        ret += c->messages()->unreadCount();
    }
    return ret;
}

int ConversationsModel::activeChatIndex() const
{
    return d->activeChatIndex;
}

void ConversationsModel::closeAllConversations()
{
    if (!d->conversations.isEmpty()) {
        beginRemoveRows(QModelIndex(), 0, rowCount() - 1);
        d->conversations.clear();
        endRemoveRows();
        qDeleteAll(d->conversations);
    }
}
<|endoftext|>


<commit_before>#include <algorithm>
#include <cassert>
#include <cmath>
#include <complex>
#include <cstring>
#include <float.h>
#include <numeric>
#include <pitch_detection.h>
#include <pitch_detection_priv.h>
#include <vector>
#include <ffts/ffts.h>

static std::vector<double>
acorr_r(const std::vector<double> &signal)
{
	int N = signal.size();
	int N2 = 2 * N - 1;

	/* https://stackoverflow.com/questions/466204/rounding-up-to-next-power-of-2
	 */
	if (N2 & (N2 - 1)) {
		N2--;
		N2 |= N2 >> 1;
		N2 |= N2 >> 2;
		N2 |= N2 >> 4;
		N2 |= N2 >> 8;
		N2 |= N2 >> 16;
		N2++;
	}

	assert(!(N2 & (N2 - 1)));

	auto fft_forward = ffts_init_1d(N2, false);
	auto fft_backward = ffts_init_1d(N2, false);

	std::vector<std::complex<double>> signala_ext(N2);
	std::vector<std::complex<double>> signalb_ext(N2);

	for (int i = 0; i < N; i++) {
		signala_ext[(N2 - N) + i] = {signal[i], 0.0};
		signalb_ext[i] = {signal[i], 0.0};
	}

	std::vector<std::complex<double>> outa(N2);
	std::vector<std::complex<double>> outb(N2);
	std::vector<std::complex<double>> out(N2);
	std::vector<std::complex<double>> result(N2);

	ffts_execute(fft_forward, signala_ext.data(), outa.data());
	ffts_execute(fft_forward, signalb_ext.data(), outb.data());

	std::complex<double> scale = {1.0 / (double)N2, 0.0};
	for (int i = 0; i < N2; ++i)
		out[i] = outa[i] * std::conj(outb[i]) * scale;

	ffts_execute(fft_backward, out.data(), result.data());

	ffts_free(fft_forward);
	ffts_free(fft_backward);

	std::vector<double> normalized_result(N, 0.0);
	for (int i = 0; i < N; ++i)
		normalized_result[i] = std::real(result[i + (N2 - N)]) / std::real(result[N2 - N]);
	return normalized_result;
}

static std::vector<int>
peak_picking(const std::vector<double> &nsdf)
{
	std::vector<int> max_positions{};
	int pos = 0;
	int cur_max_pos = 0;
	ssize_t size = nsdf.size();

	while (pos < (size - 1) / 3 && nsdf[pos] > 0)
		pos++;
	while (pos < size - 1 && nsdf[pos] <= 0.0)
		pos++;

	if (pos == 0)
		pos = 1;

	while (pos < size - 1) {
		if (nsdf[pos] > nsdf[pos - 1] && nsdf[pos] >= nsdf[pos + 1]) {
			if (cur_max_pos == 0) {
				cur_max_pos = pos;
			} else if (nsdf[pos] > nsdf[cur_max_pos]) {
				cur_max_pos = pos;
			}
		}
		pos++;
		if (pos < size - 1 && nsdf[pos] <= 0) {
			if (cur_max_pos > 0) {
				max_positions.push_back(cur_max_pos);
				cur_max_pos = 0;
			}
			while (pos < size - 1 && nsdf[pos] <= 0.0) {
				pos++;
			}
		}
	}
	if (cur_max_pos > 0) {
		max_positions.push_back(cur_max_pos);
	}
	return max_positions;
}

double
get_pitch_mpm(const std::vector<double> &data, int sample_rate)
{
	std::vector<double> nsdf = acorr_r(data);
	std::vector<int> max_positions = peak_picking(nsdf);
	std::vector<std::pair<double, double>> estimates;

	double highest_amplitude = -DBL_MAX;

	for (int i : max_positions) {
		highest_amplitude = std::max(highest_amplitude, nsdf[i]);
		if (nsdf[i] > MPM_SMALL_CUTOFF) {
			auto x = parabolic_interpolation(nsdf, i);
			estimates.push_back(x);
			highest_amplitude = std::max(highest_amplitude, std::get<1>(x));
		}
	}

	if (estimates.empty())
		return -1;

	double actual_cutoff = MPM_CUTOFF * highest_amplitude;
	double period = 0;

	for (auto i : estimates) {
		if (std::get<1>(i) >= actual_cutoff) {
			period = std::get<0>(i);
			break;
		}
	}

	double pitch_estimate = (sample_rate / period);
	return (pitch_estimate > MPM_LOWER_PITCH_CUTOFF) ? pitch_estimate : -1;
}
<commit_msg>Fix use of FFTS<commit_after>#include <algorithm>
#include <cassert>
#include <cmath>
#include <complex>
#include <cstring>
#include <ffts/ffts.h>
#include <float.h>
#include <numeric>
#include <pitch_detection.h>
#include <pitch_detection_priv.h>
#include <vector>

static std::vector<double>
acorr_r(const std::vector<double> &signal)
{
	int N = signal.size();
	int N2 = 2 * N;// - 1;

	auto fft_forward = ffts_init_1d(N2, false);
	auto fft_backward = ffts_init_1d(N2, false);

	std::vector<std::complex<float>> signala_ext(N2);
	std::vector<std::complex<float>> signalb_ext(N2);

	for (int i = 0; i < N; i++) {
		signala_ext[(N2 - N) + i] = {float(signal[i]), 0.0};
		signalb_ext[i] = {float(signal[i]), 0.0};
	}

	std::vector<std::complex<float>> outa(N2);
	std::vector<std::complex<float>> outb(N2);
	std::vector<std::complex<float>> out(N2);
	std::vector<std::complex<float>> result(N2);

	ffts_execute(fft_forward, signala_ext.data(), outa.data());
	ffts_execute(fft_forward, signalb_ext.data(), outb.data());

	std::complex<float> scale = {1.0f / (float)N2, 0.0};
	for (int i = 0; i < N2; ++i)
		out[i] = outa[i] * std::conj(outb[i]) * scale;

	ffts_execute(fft_backward, out.data(), result.data());

	ffts_free(fft_forward);
	ffts_free(fft_backward);

	std::vector<double> normalized_result(N, 0.0);
	for (int i = 0; i < N; ++i)
		normalized_result[i] =
		    std::real(result[i + (N2 - N)]) / std::real(result[N2 - N]);
	return normalized_result;
}

static std::vector<int>
peak_picking(const std::vector<double> &nsdf)
{
	std::vector<int> max_positions{};
	int pos = 0;
	int cur_max_pos = 0;
	ssize_t size = nsdf.size();

	while (pos < (size - 1) / 3 && nsdf[pos] > 0)
		pos++;
	while (pos < size - 1 && nsdf[pos] <= 0.0)
		pos++;

	if (pos == 0)
		pos = 1;

	while (pos < size - 1) {
		if (nsdf[pos] > nsdf[pos - 1] && nsdf[pos] >= nsdf[pos + 1]) {
			if (cur_max_pos == 0) {
				cur_max_pos = pos;
			} else if (nsdf[pos] > nsdf[cur_max_pos]) {
				cur_max_pos = pos;
			}
		}
		pos++;
		if (pos < size - 1 && nsdf[pos] <= 0) {
			if (cur_max_pos > 0) {
				max_positions.push_back(cur_max_pos);
				cur_max_pos = 0;
			}
			while (pos < size - 1 && nsdf[pos] <= 0.0) {
				pos++;
			}
		}
	}
	if (cur_max_pos > 0) {
		max_positions.push_back(cur_max_pos);
	}
	return max_positions;
}

double
get_pitch_mpm(const std::vector<double> &data, int sample_rate)
{
	std::vector<double> nsdf = acorr_r(data);
	std::vector<int> max_positions = peak_picking(nsdf);
	std::vector<std::pair<double, double>> estimates;

	double highest_amplitude = -DBL_MAX;

	for (int i : max_positions) {
		highest_amplitude = std::max(highest_amplitude, nsdf[i]);
		if (nsdf[i] > MPM_SMALL_CUTOFF) {
			auto x = parabolic_interpolation(nsdf, i);
			estimates.push_back(x);
			highest_amplitude = std::max(highest_amplitude, std::get<1>(x));
		}
	}

	if (estimates.empty())
		return -1;

	double actual_cutoff = MPM_CUTOFF * highest_amplitude;
	double period = 0;

	for (auto i : estimates) {
		if (std::get<1>(i) >= actual_cutoff) {
			period = std::get<0>(i);
			break;
		}
	}

	double pitch_estimate = (sample_rate / period);
	return (pitch_estimate > MPM_LOWER_PITCH_CUTOFF) ? pitch_estimate : -1;
}
<|endoftext|>


<commit_before>#include <SDL2/SDL.h>
#include <SDL2/SDL_opengles2.h>

#include <vector>
#include <string>
#include <cstdint>
#include <map>
#include <iostream>

#include "plasmacore.h"
#include "plasmacore_message.h"
#include "plasmacore_view.h"
#include "rogue_interface.h"

#ifdef __EMSCRIPTEN__
#include <emscripten.h>
#else
#include <libgen.h>
#include <unistd.h>
#endif

#define LOCAL_FS 1
//#define WINDOW_BASED 1

// The "code" value for SDL_USEREVENT for our async call mechanism
#define ASYNC_CALL_EVENT 1

static int gargc;
static char ** gargv;

static int iterations;

#ifdef __EMSCRIPTEN__
static void do_async_call ( void (*cb)(void *), int millis )
{
  emscripten_async_call(cb, 0, millis);
}

#else

static Uint32 sdl_async_cb_poster (Uint32 interval, void * arg)
{
  SDL_Event event;
  event.user.type = SDL_USEREVENT;
  event.user.code = ASYNC_CALL_EVENT;
  event.user.data1 = arg; // The callback
  //userevent.data2 = ...
  SDL_PushEvent(&event);
  return 0;
}

static void do_async_call ( void (*cb)(void *), int millis )
{
  SDL_AddTimer(millis, sdl_async_cb_poster, (void *)cb);
}

#endif

HID Plasmacore::addMessageHandler( std::string & type, HandlerCallback handler )
{
  auto info = PlasmacoreMessageHandler( nextHandlerID, type, handler );
  nextHandlerID += 1;

  handlers_by_id[ info.handlerID ] = info;
  handlers[ type ].push_back( info );
  return info.handlerID;
}

HID Plasmacore::addMessageHandler( const char * type, HandlerCallback handler )
{
  auto info = PlasmacoreMessageHandler( nextHandlerID, type, handler );
  nextHandlerID += 1;

  handlers_by_id[ info.handlerID ] = info;
  handlers[ type ].push_back( info );
  return info.handlerID;
}

Plasmacore & Plasmacore::configure()
{
  if (is_configured) return *this;
  is_configured = true;

  addMessageHandler( "<reply>", [] (PlasmacoreMessage m)
    {
        auto iter = singleton.reply_handlers.find( m.message_id );
        if (iter != singleton.reply_handlers.end())
        {
          auto info = iter->second;
          singleton.reply_handlers.erase(iter);
          info.callback( m );
        }
    }
  );

  #ifdef WINDOW_BASED
  addMessageHandler( "Window.create", [] (PlasmacoreMessage m)
    {
      auto name = m.getString( "name" );

      auto view = plasmacore_new_view(name);
      if (!view) throw "No view created!";

      Plasmacore::singleton.resources[ m.getInt32("id") ] = view;
      std::cerr << "Controller window: " << view << std::endl;
    }
  );

  addMessageHandler( "Window.show", [] (PlasmacoreMessage m)
    {
      auto window_id = m.getInt32( "id" );
      auto view  = (PlasmacoreView*)Plasmacore::singleton.resources[ window_id ];
      if (view) view->show();
    }
  );
  #else
  auto view = plasmacore_new_view("Main");
  if (!view) throw "No view created!";
  std::cerr << "Controller window: " << view << std::endl;
  #endif

  RogueInterface_set_arg_count( gargc );
  for (int i = 0; i < gargc; ++i)
  {
    RogueInterface_set_arg_value( i, gargv[i] );
  }

  RogueInterface_configure();
  return *this;
}

RID Plasmacore::getResourceID( void * resource)
{
  if (! resource) return 0;

  for (auto const & it : resources)
  {
    if (it.second == resource) { return it.first; }
  }
  return 0;
}

Plasmacore & Plasmacore::launch()
{
  if (is_launched) { return *this; }
  is_launched = true;

  RogueInterface_launch();
  auto m = PlasmacoreMessage( "Application.on_launch" );
#ifdef WINDOW_BASED
  m.set( "is_window_based", true );
#endif
  m.send();
  return *this;
}

Plasmacore & Plasmacore::relaunch()
{
  //XXX: Always relaunch window based?
  PlasmacoreMessage( "Application.on_launch" ).set( "is_window_based", true ).send();
  return *this;
}

void Plasmacore::removeMessageHandler( HID handlerID )
{
  auto iter = handlers_by_id.find(handlerID);
  if (iter != handlers_by_id.end())
  {
    auto handler = iter->second;
    handlers_by_id.erase( handlerID );
    auto & handler_list = handlers[ handler.type ]; // This should always work, right?
    {
      for (int i = 0; i < handler_list.size(); ++i)
      {
        if (handler_list[i].handlerID == handlerID)
        {
          handler_list.erase(handler_list.begin() + i);
          return;
        }
      }
    }
  }
}

void Plasmacore::send( PlasmacoreMessage & m )
{
  auto size = m.data.size();
  pending_message_data.push_back( uint8_t((size>>24)&255) );
  pending_message_data.push_back( uint8_t((size>>16)&255) );
  pending_message_data.push_back( uint8_t((size>>8)&255) );
  pending_message_data.push_back( uint8_t(size&255) );
  for (int i = 0; i < m.data.size(); ++i)
  {
    pending_message_data.push_back( m.data[i] );
  }
  real_update(false);
}

void Plasmacore::send_rsvp( PlasmacoreMessage & m, HandlerCallback callback )
{
  reply_handlers[ m.message_id ] = PlasmacoreMessageHandler( nextHandlerID, "<reply>", callback );
  nextHandlerID += 1;
  send( m );
}

Plasmacore & Plasmacore::setIdleUpdateFrequency( double f )
{
  idleUpdateFrequency = f;
  if (update_timer)
  {
    stop();
    start();
  }
  return *this;
}

void Plasmacore::start()
{
  if ( !is_launched ) { configure().launch(); }
  printf("LOG: start()\n");

  update_timer = true;
  real_update(true);
}

void Plasmacore::stop()
{
  // Note that we don't actually stop any timer from running.  The major
  // outcome here is that you may get an extra update if you stop, restart,
  // or change the update frequency.
  // We *could* probably make this work "right" by using a cancellable SDL
  // timer, but I don't think it really matters, and I'm not sure that such
  // timers are available in emscripten, so we'd have to do this for emscripten
  // mode anyway.
  update_timer = false;
}

void Plasmacore::update(void * dummy)
{
  //printf("LOG: update(); iterations=%i\n", iterations);
  Plasmacore::singleton.real_update(true);
}

void Plasmacore::fast_update(void * dummy)
{
  //printf("LOG: update(); iterations=%i\n", iterations);
  Plasmacore::singleton.real_update(false);
}

void Plasmacore::real_update (bool reschedule)
{
  if (!Plasmacore::singleton.update_timer) return; // Ignore, since timer shouldn't be running.

  // Schedule the next idle update.
  if (reschedule) do_async_call(update, int(1000*idleUpdateFrequency));

  if (is_sending)
  {
    update_requested = true;
    return;
  }

  is_sending = true;

  // Execute a small burst of message dispatching and receiving.  Stop after
  // 10 iterations or when there are no new messages.  Global state updates
  // are frequency capped to 1/60 second intervals and draws are synced to
  // the display refresh so this isn't triggering large amounts of extra work.
  for (int _ = 0; _ < 10; ++_)
  {
    update_requested = false;

    // Swap pending data with io_buffer data
    io_buffer.swap(pending_message_data);

    RogueInterface_send_messages( &io_buffer[0], io_buffer.size(), pending_message_data );
    auto count = pending_message_data.size();
    //if (count || io_buffer.size())
    //  printf("TX:%-8lu  RX:%-8lu  @iter:%i\n", io_buffer.size(), count, iterations);
    uint8_t * bytes = &pending_message_data[0];

    int read_pos = 0;
    while (read_pos+4 <= count)
    {
      auto size = int( bytes[read_pos] ) << 24;
      size |= int( bytes[read_pos+1] ) << 16;
      size |= int( bytes[read_pos+2] ) << 8;
      size |= int( bytes[read_pos+3] );
      read_pos += 4;

      if (read_pos + size <= count)
      {
        std::vector<uint8_t> message_data;
        message_data.reserve( size );
        for (int i = 0; i < size; ++i)
        {
          message_data.push_back( bytes[read_pos+i] );
        }

        auto m = PlasmacoreMessage( message_data );
        printf( "Received message type %s\n", m.type.c_str() );
        auto iter = handlers.find(m.type);
        if (iter != handlers.end())
        {
          for (auto const & handler : iter->second)
          {
            handler.callback( m );
          }
        }
      }
      else
      {
        std::cerr << "*** Skipping message due to invalid size.\n";
      }
      read_pos += size;
    }

    io_buffer.clear();

    if ( !update_requested )
    {
      break;
    }
  }

  is_sending = false;

  if (update_requested)
  {
    // There are still some pending messages after 10 iterations.  Schedule another round
    // in 1/60 second instead of the usual 1.0 seconds.
    // We'll now have another (slow) call to update, but it'll probably return
    do_async_call(fast_update, 16);
  }
}

static bool should_quit = false;

static void do_iteration (void)
{
  ++iterations;
  plasmacore_redraw_all_windows();
  SDL_Event e;
  while (SDL_PollEvent(&e))
  {
    switch (e.type)
    {
      case SDL_QUIT:
        should_quit = true;
        return;
      case SDL_MOUSEBUTTONDOWN:
      case SDL_MOUSEBUTTONUP:
      {
        auto w = plasmacore_get_window(e.button.windowID);
        if (!w) break;
        int which;
        switch (e.button.button)
        {
          case SDL_BUTTON_LEFT:
            which = 0;
            break;
          case SDL_BUTTON_RIGHT:
            which = 1;
            break;
          default:
            return;
        }
        if (e.type == SDL_MOUSEBUTTONDOWN)
          w->on_mouse_down(e.button.x, e.button.y, which);
        else
          w->on_mouse_up(e.button.x, e.button.y, which);
        break;
      }
      case SDL_MOUSEMOTION:
      {
        auto w = plasmacore_get_window(e.motion.windowID);
        w->on_mouse_move(e.motion.x, e.motion.y);
        break;
      }
      case SDL_WINDOWEVENT:
      {
        auto w = plasmacore_get_window(e.button.windowID);
        if (!w) break;
        switch (e.window.event)
        {
          case SDL_WINDOWEVENT_FOCUS_GAINED:
            w->on_focus_gained();
            break;
        }
        break;
      }
      case SDL_USEREVENT:
      {
        if (e.user.code == ASYNC_CALL_EVENT)
        {
          // The following cast is theoretically not allowed.  Let's hope
          // it works anyway on platforms we care about.
          void (*f) (void*) = (void (*)(void*))e.user.data1;
          f(0);
          break;
        }
      }
    }
  }
}

extern "C" void start_main_loop (void)
{
  emscripten_set_main_loop(do_iteration, 0, 1);
}

Plasmacore Plasmacore::singleton;

int main (int argc, char * argv[])
{
  gargc = argc;
  gargv = argv;

  #ifdef __EMSCRIPTEN__
    auto flags = 0;
  #else
    auto flags = SDL_INIT_TIMER;

    // CD into what we think the executable's directory is.
    char * exe = strdup(argv[0]);
    char * dir = dirname(exe);
    chdir(dir);
    free(exe);
  #endif

  if (SDL_Init(SDL_INIT_VIDEO|SDL_INIT_AUDIO|flags) != 0) return 1;

  // Might want to call this here... but it might also be easier to just
  // call it from Rogue via "native".
  //SDL_ShowCursor(false);

  Plasmacore::singleton.configure().launch();
  PlasmacoreMessage( "Application.on_start" ).send();
  Plasmacore::singleton.start();

#ifdef __EMSCRIPTEN__
  #ifdef LOCAL_FS
    EM_ASM(
       FS.mkdir("/local_storage");
       FS.mount(IDBFS, {}, "/local_storage");
       FS.syncfs(true, function (err) {
         Module.print("Persistent storage ready.");
         Module["_start_main_loop"]();
       });
    );
  #else
    start_main_loop();
  #endif
#else
  SDL_GL_SetSwapInterval(1);
  while (!should_quit)
  {
    do_iteration();
    //SDL_Delay(20);
  }
#endif

  return 0;
}
<commit_msg>[Web] Attempt to sync FS on leaving page<commit_after>#include <SDL2/SDL.h>
#include <SDL2/SDL_opengles2.h>

#include <vector>
#include <string>
#include <cstdint>
#include <map>
#include <iostream>

#include "plasmacore.h"
#include "plasmacore_message.h"
#include "plasmacore_view.h"
#include "rogue_interface.h"

#ifdef __EMSCRIPTEN__
#include <emscripten.h>
#include <stdlib.h>
#else
#include <libgen.h>
#include <unistd.h>
#endif

#define LOCAL_FS 1
//#define WINDOW_BASED 1

// The "code" value for SDL_USEREVENT for our async call mechanism
#define ASYNC_CALL_EVENT 1

static int gargc;
static char ** gargv;

static int iterations;

#ifdef __EMSCRIPTEN__
static void do_async_call ( void (*cb)(void *), int millis )
{
  emscripten_async_call(cb, 0, millis);
}

#else

static Uint32 sdl_async_cb_poster (Uint32 interval, void * arg)
{
  SDL_Event event;
  event.user.type = SDL_USEREVENT;
  event.user.code = ASYNC_CALL_EVENT;
  event.user.data1 = arg; // The callback
  //userevent.data2 = ...
  SDL_PushEvent(&event);
  return 0;
}

static void do_async_call ( void (*cb)(void *), int millis )
{
  SDL_AddTimer(millis, sdl_async_cb_poster, (void *)cb);
}

#endif

HID Plasmacore::addMessageHandler( std::string & type, HandlerCallback handler )
{
  auto info = PlasmacoreMessageHandler( nextHandlerID, type, handler );
  nextHandlerID += 1;

  handlers_by_id[ info.handlerID ] = info;
  handlers[ type ].push_back( info );
  return info.handlerID;
}

HID Plasmacore::addMessageHandler( const char * type, HandlerCallback handler )
{
  auto info = PlasmacoreMessageHandler( nextHandlerID, type, handler );
  nextHandlerID += 1;

  handlers_by_id[ info.handlerID ] = info;
  handlers[ type ].push_back( info );
  return info.handlerID;
}

Plasmacore & Plasmacore::configure()
{
  if (is_configured) return *this;
  is_configured = true;

  addMessageHandler( "<reply>", [] (PlasmacoreMessage m)
    {
        auto iter = singleton.reply_handlers.find( m.message_id );
        if (iter != singleton.reply_handlers.end())
        {
          auto info = iter->second;
          singleton.reply_handlers.erase(iter);
          info.callback( m );
        }
    }
  );

  #ifdef WINDOW_BASED
  addMessageHandler( "Window.create", [] (PlasmacoreMessage m)
    {
      auto name = m.getString( "name" );

      auto view = plasmacore_new_view(name);
      if (!view) throw "No view created!";

      Plasmacore::singleton.resources[ m.getInt32("id") ] = view;
      std::cerr << "Controller window: " << view << std::endl;
    }
  );

  addMessageHandler( "Window.show", [] (PlasmacoreMessage m)
    {
      auto window_id = m.getInt32( "id" );
      auto view  = (PlasmacoreView*)Plasmacore::singleton.resources[ window_id ];
      if (view) view->show();
    }
  );
  #else
  auto view = plasmacore_new_view("Main");
  if (!view) throw "No view created!";
  std::cerr << "Controller window: " << view << std::endl;
  #endif

  RogueInterface_set_arg_count( gargc );
  for (int i = 0; i < gargc; ++i)
  {
    RogueInterface_set_arg_value( i, gargv[i] );
  }

  RogueInterface_configure();
  return *this;
}

RID Plasmacore::getResourceID( void * resource)
{
  if (! resource) return 0;

  for (auto const & it : resources)
  {
    if (it.second == resource) { return it.first; }
  }
  return 0;
}

Plasmacore & Plasmacore::launch()
{
  if (is_launched) { return *this; }
  is_launched = true;

  RogueInterface_launch();
  auto m = PlasmacoreMessage( "Application.on_launch" );
#ifdef WINDOW_BASED
  m.set( "is_window_based", true );
#endif
  m.send();
  return *this;
}

Plasmacore & Plasmacore::relaunch()
{
  //XXX: Always relaunch window based?
  PlasmacoreMessage( "Application.on_launch" ).set( "is_window_based", true ).send();
  return *this;
}

void Plasmacore::removeMessageHandler( HID handlerID )
{
  auto iter = handlers_by_id.find(handlerID);
  if (iter != handlers_by_id.end())
  {
    auto handler = iter->second;
    handlers_by_id.erase( handlerID );
    auto & handler_list = handlers[ handler.type ]; // This should always work, right?
    {
      for (int i = 0; i < handler_list.size(); ++i)
      {
        if (handler_list[i].handlerID == handlerID)
        {
          handler_list.erase(handler_list.begin() + i);
          return;
        }
      }
    }
  }
}

void Plasmacore::send( PlasmacoreMessage & m )
{
  auto size = m.data.size();
  pending_message_data.push_back( uint8_t((size>>24)&255) );
  pending_message_data.push_back( uint8_t((size>>16)&255) );
  pending_message_data.push_back( uint8_t((size>>8)&255) );
  pending_message_data.push_back( uint8_t(size&255) );
  for (int i = 0; i < m.data.size(); ++i)
  {
    pending_message_data.push_back( m.data[i] );
  }
  real_update(false);
}

void Plasmacore::send_rsvp( PlasmacoreMessage & m, HandlerCallback callback )
{
  reply_handlers[ m.message_id ] = PlasmacoreMessageHandler( nextHandlerID, "<reply>", callback );
  nextHandlerID += 1;
  send( m );
}

Plasmacore & Plasmacore::setIdleUpdateFrequency( double f )
{
  idleUpdateFrequency = f;
  if (update_timer)
  {
    stop();
    start();
  }
  return *this;
}

void Plasmacore::start()
{
  if ( !is_launched ) { configure().launch(); }
  printf("LOG: start()\n");

  update_timer = true;
  real_update(true);
}

void Plasmacore::stop()
{
  // Note that we don't actually stop any timer from running.  The major
  // outcome here is that you may get an extra update if you stop, restart,
  // or change the update frequency.
  // We *could* probably make this work "right" by using a cancellable SDL
  // timer, but I don't think it really matters, and I'm not sure that such
  // timers are available in emscripten, so we'd have to do this for emscripten
  // mode anyway.
  update_timer = false;
}

void Plasmacore::update(void * dummy)
{
  //printf("LOG: update(); iterations=%i\n", iterations);
  Plasmacore::singleton.real_update(true);
}

void Plasmacore::fast_update(void * dummy)
{
  //printf("LOG: update(); iterations=%i\n", iterations);
  Plasmacore::singleton.real_update(false);
}

void Plasmacore::real_update (bool reschedule)
{
  if (!Plasmacore::singleton.update_timer) return; // Ignore, since timer shouldn't be running.

  // Schedule the next idle update.
  if (reschedule) do_async_call(update, int(1000*idleUpdateFrequency));

  if (is_sending)
  {
    update_requested = true;
    return;
  }

  is_sending = true;

  // Execute a small burst of message dispatching and receiving.  Stop after
  // 10 iterations or when there are no new messages.  Global state updates
  // are frequency capped to 1/60 second intervals and draws are synced to
  // the display refresh so this isn't triggering large amounts of extra work.
  for (int _ = 0; _ < 10; ++_)
  {
    update_requested = false;

    // Swap pending data with io_buffer data
    io_buffer.swap(pending_message_data);

    RogueInterface_send_messages( &io_buffer[0], io_buffer.size(), pending_message_data );
    auto count = pending_message_data.size();
    //if (count || io_buffer.size())
    //  printf("TX:%-8lu  RX:%-8lu  @iter:%i\n", io_buffer.size(), count, iterations);
    uint8_t * bytes = &pending_message_data[0];

    int read_pos = 0;
    while (read_pos+4 <= count)
    {
      auto size = int( bytes[read_pos] ) << 24;
      size |= int( bytes[read_pos+1] ) << 16;
      size |= int( bytes[read_pos+2] ) << 8;
      size |= int( bytes[read_pos+3] );
      read_pos += 4;

      if (read_pos + size <= count)
      {
        std::vector<uint8_t> message_data;
        message_data.reserve( size );
        for (int i = 0; i < size; ++i)
        {
          message_data.push_back( bytes[read_pos+i] );
        }

        auto m = PlasmacoreMessage( message_data );
        printf( "Received message type %s\n", m.type.c_str() );
        auto iter = handlers.find(m.type);
        if (iter != handlers.end())
        {
          for (auto const & handler : iter->second)
          {
            handler.callback( m );
          }
        }
      }
      else
      {
        std::cerr << "*** Skipping message due to invalid size.\n";
      }
      read_pos += size;
    }

    io_buffer.clear();

    if ( !update_requested )
    {
      break;
    }
  }

  is_sending = false;

  if (update_requested)
  {
    // There are still some pending messages after 10 iterations.  Schedule another round
    // in 1/60 second instead of the usual 1.0 seconds.
    // We'll now have another (slow) call to update, but it'll probably return
    do_async_call(fast_update, 16);
  }
}

static bool should_quit = false;

static void do_iteration (void)
{
  ++iterations;
  plasmacore_redraw_all_windows();
  SDL_Event e;
  while (SDL_PollEvent(&e))
  {
    switch (e.type)
    {
      case SDL_QUIT:
        should_quit = true;
        return;
      case SDL_MOUSEBUTTONDOWN:
      case SDL_MOUSEBUTTONUP:
      {
        auto w = plasmacore_get_window(e.button.windowID);
        if (!w) break;
        int which;
        switch (e.button.button)
        {
          case SDL_BUTTON_LEFT:
            which = 0;
            break;
          case SDL_BUTTON_RIGHT:
            which = 1;
            break;
          default:
            return;
        }
        if (e.type == SDL_MOUSEBUTTONDOWN)
          w->on_mouse_down(e.button.x, e.button.y, which);
        else
          w->on_mouse_up(e.button.x, e.button.y, which);
        break;
      }
      case SDL_MOUSEMOTION:
      {
        auto w = plasmacore_get_window(e.motion.windowID);
        w->on_mouse_move(e.motion.x, e.motion.y);
        break;
      }
      case SDL_WINDOWEVENT:
      {
        auto w = plasmacore_get_window(e.button.windowID);
        if (!w) break;
        switch (e.window.event)
        {
          case SDL_WINDOWEVENT_FOCUS_GAINED:
            w->on_focus_gained();
            break;
        }
        break;
      }
      case SDL_USEREVENT:
      {
        if (e.user.code == ASYNC_CALL_EVENT)
        {
          // The following cast is theoretically not allowed.  Let's hope
          // it works anyway on platforms we care about.
          void (*f) (void*) = (void (*)(void*))e.user.data1;
          f(0);
          break;
        }
      }
    }
  }
}

extern "C" void start_main_loop (void)
{
  emscripten_set_main_loop(do_iteration, 0, 1);
}

Plasmacore Plasmacore::singleton;

int main (int argc, char * argv[])
{
  gargc = argc;
  gargv = argv;

  #ifdef __EMSCRIPTEN__
    auto flags = 0;
  #else
    auto flags = SDL_INIT_TIMER;

    // CD into what we think the executable's directory is.
    char * exe = strdup(argv[0]);
    char * dir = dirname(exe);
    chdir(dir);
    free(exe);
  #endif

  if (SDL_Init(SDL_INIT_VIDEO|SDL_INIT_AUDIO|flags) != 0) return 1;

  // Might want to call this here... but it might also be easier to just
  // call it from Rogue via "native".
  //SDL_ShowCursor(false);

  Plasmacore::singleton.configure().launch();
  PlasmacoreMessage( "Application.on_start" ).send();
  Plasmacore::singleton.start();

#ifdef __EMSCRIPTEN__
  #ifdef LOCAL_FS
    EM_ASM(
       FS.mkdir("/local_storage");
       FS.mount(IDBFS, {}, "/local_storage");
       FS.syncfs(true, function (err) {
         Module.print("Persistent storage ready.");
         Module["_start_main_loop"]();
       });
       window.addEventListener("beforeunload", function (e) {
         // Sync all filesystems
         FS.syncfs(false, function(err) {});
         return null;
       });
    );
  #else
    start_main_loop();
  #endif
#else
  SDL_GL_SetSwapInterval(1);
  while (!should_quit)
  {
    do_iteration();
    //SDL_Delay(20);
  }
#endif

  return 0;
}
<|endoftext|>


<commit_before>#include <sys/time.h>
#include <sys/types.h>
#include <sys/sysctl.h>
#include <stdio.h>
#include <stdlib.h>

#include <iostream>
#include <fstream>
#include <map>

#include <CoreFoundation/CoreFoundation.h>

namespace {
  CFStringRef applicationID = CFSTR("org.pqrs.KeyRemap4MacBook");

  // ============================================================
  // SAVE & LOAD
  CFMutableDictionaryRef dict_sysctl = NULL;
  std::map<std::string, int> map_reset;

  void
  save(const char *name)
  {
    if (! dict_sysctl) return;

    char entry[512];
    snprintf(entry, sizeof(entry), "keyremap4macbook.%s", name);

    int value;
    size_t len = sizeof(value);
    if (sysctlbyname(entry, &value, &len, NULL, 0) == -1) return;

    CFStringRef key = CFStringCreateWithCString(NULL, name, kCFStringEncodingUTF8);
    CFNumberRef val = CFNumberCreate(NULL, kCFNumberIntType, &value);

    typeof(map_reset.end()) it = map_reset.find(name);
    if (it == map_reset.end()) return;
    CFNumberRef defaultval = CFNumberCreate(NULL, kCFNumberIntType, &(it->second));

    if (CFNumberCompare(val, defaultval, NULL) != 0) {
      CFDictionarySetValue(dict_sysctl, key, val);
    }
  }

  void
  load(const char *name)
  {
    if (! dict_sysctl) return;
    CFStringRef key = CFStringCreateWithCString(NULL, name, kCFStringEncodingUTF8);

    CFNumberRef val = reinterpret_cast<CFNumberRef>(CFDictionaryGetValue(dict_sysctl, key));
    if (! val) return;

    int value;
    if (! CFNumberGetValue(val, kCFNumberIntType, &value)) return;

    char cmd[512];
    snprintf(cmd, sizeof(cmd), "/Library/org.pqrs/KeyRemap4MacBook/bin/KeyRemap4MacBook_sysctl_set %s %d", name, value);
    system(cmd);
  }

  void
  scanLines(const char *filename, void (*func)(const char *))
  {
    std::ifstream ifs(filename);
    if (! ifs) return;

    while (! ifs.eof()) {
      char line[512];

      ifs.getline(line, sizeof(line));

      const char *sysctl_begin = "<sysctl>";
      const char *sysctl_end = "</sysctl>";

      char *begin = strstr(line, "<sysctl>");
      if (! begin) continue;
      char *end = strstr(line, sysctl_end);
      if (! end) continue;

      begin += strlen(sysctl_begin);
      *end = '\0';

      func(begin);
    }
  }

  bool
  makeMapReset(void)
  {
    std::ifstream ifs("/Library/org.pqrs/KeyRemap4MacBook/share/reset");
    if (! ifs) return false;

    while (! ifs.eof()) {
      char line[512];

      ifs.getline(line, sizeof(line));

      char *p = strchr(line, ' ');
      if (! p) continue;
      *p = '\0';

      int value = atoi(p + 1);

      map_reset[line] = value;
    }

    return true;
  }

  bool
  saveToFile(const char **targetFiles, CFStringRef identify)
  {
    if (! makeMapReset()) return false;

    dict_sysctl = CFDictionaryCreateMutable(NULL, 0, NULL, NULL);
    if (! dict_sysctl) return false;

    for (int i = 0; ; ++i) {
      const char *filename = targetFiles[i];
      if (! filename) break;
      scanLines(filename, save);
    }
    CFPreferencesSetAppValue(identify, dict_sysctl, applicationID);

    CFRelease(dict_sysctl); dict_sysctl = NULL;
    return true;
  }

  bool
  loadFromFile(const char **targetFiles, CFStringRef identify)
  {
    dict_sysctl = reinterpret_cast<CFMutableDictionaryRef>(const_cast<void *>(CFPreferencesCopyAppValue(identify, applicationID)));
    if (! dict_sysctl) return false;

    for (int i = 0; ; ++i) {
      const char *filename = targetFiles[i];
      if (! filename) break;
      scanLines(filename, load);
    }

    CFRelease(dict_sysctl); dict_sysctl = NULL;
    return true;
  }

  // ============================================================
  // ADD & DELETE & SELECT
  void
  setConfigList(CFArrayRef list)
  {
    CFPreferencesSetAppValue(CFSTR("configList"), list, applicationID);
  }

  CFArrayRef
  getConfigList(void)
  {
    CFArrayRef list = reinterpret_cast<CFArrayRef>(CFPreferencesCopyAppValue(CFSTR("configList"), applicationID));

    if (! list || CFArrayGetCount(list) == 0) {
      if (list) CFRelease(list);

      CFMutableDictionaryRef dict[1];
      dict[0] = CFDictionaryCreateMutable(NULL, 0, NULL, NULL);
      CFDictionarySetValue(dict[0], CFSTR("name"), CFSTR("Default"));
      CFDictionarySetValue(dict[0], CFSTR("identify"), CFSTR("config_default"));

      list = CFArrayCreate(NULL, const_cast<const void **>(reinterpret_cast<void **>(dict)), 1, NULL);
      setConfigList(list);
    }

    return list;
  }

  CFDictionaryRef
  getConfigDictionary(int index)
  {
    CFArrayRef list = getConfigList();
    if (! list) return NULL;

    if (index < 0) return NULL;
    if (index >= CFArrayGetCount(list)) return NULL;

    return reinterpret_cast<CFDictionaryRef>(CFArrayGetValueAtIndex(list, index));
  }

  CFStringRef
  getIdentify(int index)
  {
    CFDictionaryRef dict = getConfigDictionary(index);
    if (! dict) return NULL;

    return reinterpret_cast<CFStringRef>(CFDictionaryGetValue(dict, CFSTR("identify")));
  }

  bool
  appendConfig(void)
  {
    CFArrayRef list = getConfigList();
    if (! list) return false;

    struct timeval tm;
    gettimeofday(&tm, NULL);
    CFStringRef identify = CFStringCreateWithFormat(NULL, NULL, CFSTR("config_%d_%d"), tm.tv_sec, tm.tv_usec);

    CFMutableDictionaryRef dict;
    dict = CFDictionaryCreateMutable(NULL, 0, NULL, NULL);
    CFDictionarySetValue(dict, CFSTR("name"), CFSTR("NewItem"));
    CFDictionarySetValue(dict, CFSTR("identify"), identify);

    CFMutableArrayRef newlist = CFArrayCreateMutableCopy(NULL, 0, list);
    CFArrayAppendValue(newlist, dict);

    setConfigList(newlist);
    return true;
  }

  bool
  removeConfig(int index)
  {
    CFStringRef identify = getIdentify(index);
    if (! identify) return false;

    CFPreferencesSetAppValue(identify, NULL, applicationID);

    CFArrayRef list = getConfigList();
    if (! list) return false;

    CFMutableArrayRef newlist = CFArrayCreateMutableCopy(NULL, 0, list);
    CFArrayRemoveValueAtIndex(newlist, index);

    setConfigList(newlist);
    return true;
  }

  bool
  renameConfig(int index, const char *newname)
  {
    CFArrayRef list = getConfigList();
    if (! list) return false;

    CFDictionaryRef olddict = getConfigDictionary(index);
    if (! olddict) return false;

    CFMutableDictionaryRef newdict = CFDictionaryCreateMutableCopy(NULL, 0, olddict);
    CFDictionarySetValue(newdict, CFSTR("name"), CFStringCreateWithCString(NULL, newname, kCFStringEncodingUTF8));

    CFMutableArrayRef newlist = CFArrayCreateMutableCopy(NULL, 0, list);
    CFArraySetValueAtIndex(newlist, index, newdict);

    setConfigList(newlist);
    return true;
  }

  // ----------------------------------------
  bool
  setSelectedIndex(int index)
  {
    CFDictionaryRef dict = getConfigDictionary(index);
    if (! dict) return false;

    CFNumberRef val = CFNumberCreate(NULL, kCFNumberIntType, &index);
    CFPreferencesSetAppValue(CFSTR("selectedIndex"), val, applicationID);

    return true;
  }

  int
  getSelectedIndex(void)
  {
    Boolean isOK;
    CFIndex value = CFPreferencesGetAppIntegerValue(CFSTR("selectedIndex"), applicationID, &isOK);
    if (! isOK || value < 0) {
      value = 0;
      setSelectedIndex(value);
    }
    return value;
  }
}

int
main(int argc, char **argv)
{
  if (argc == 1) {
    fprintf(stderr, "Usage: %s (save|load|add|delete|name|select) [params]\n", argv[0]);
    return 1;
  }

  bool isSuccess = false;

  if (strcmp(argv[1], "select") == 0) {
    if (argc < 3) {
      fprintf(stderr, "Usage: %s select index\n", argv[0]);
      goto finish;
    }
    int index = atoi(argv[2]);
    isSuccess = setSelectedIndex(index);

  } else if (strcmp(argv[1], "add") == 0) {
    isSuccess = appendConfig();

  } else if (strcmp(argv[1], "delete") == 0) {
    if (argc < 3) {
      fprintf(stderr, "Usage: %s delete index\n", argv[0]);
      goto finish;
    }
    int index = atoi(argv[2]);
    isSuccess = removeConfig(index);

  } else if (strcmp(argv[1], "name") == 0) {
    // sysctl_ctl name "index" "newname"
    if (argc < 4) {
      fprintf(stderr, "Usage: %s name index newname\n", argv[0]);
      goto finish;
    }
    int index = atoi(argv[2]);
    isSuccess = renameConfig(index, argv[3]);

  } else if ((strcmp(argv[1], "save") == 0) || (strcmp(argv[1], "load") == 0)) {
    int value = getSelectedIndex();
    CFStringRef identify = getIdentify(value);

    const char *targetFiles[] = {
      "/Library/org.pqrs/KeyRemap4MacBook/prefpane/checkbox.xml",
      "/Library/org.pqrs/KeyRemap4MacBook/prefpane/number.xml",
      NULL,
    };
    if (strcmp(argv[1], "save") == 0) {
      isSuccess = saveToFile(targetFiles, identify);
    }
    if (strcmp(argv[1], "load") == 0) {
      system("/Library/org.pqrs/KeyRemap4MacBook/bin/KeyRemap4MacBook_sysctl_reset");
      system("/Library/org.pqrs/KeyRemap4MacBook/bin/KeyRemap4MacBook_sysctl_set initialized 1");
      isSuccess = loadFromFile(targetFiles, identify);
    }
  }

  CFPreferencesAppSynchronize(applicationID);

finish:
  if (isSuccess) {
    fprintf(stderr, "[DONE]\n");
  } else {
    fprintf(stderr, "[ERROR]\n");
  }

  return 0;
}
<commit_msg>support rename, count, getname @ sysctl_ctl<commit_after>#include <sys/time.h>
#include <sys/types.h>
#include <sys/sysctl.h>
#include <stdio.h>
#include <stdlib.h>

#include <iostream>
#include <fstream>
#include <map>

#include <CoreFoundation/CoreFoundation.h>

namespace {
  CFStringRef applicationID = CFSTR("org.pqrs.KeyRemap4MacBook");

  // ============================================================
  // SAVE & LOAD
  CFMutableDictionaryRef dict_sysctl = NULL;
  std::map<std::string, int> map_reset;

  void
  save(const char *name)
  {
    if (! dict_sysctl) return;

    char entry[512];
    snprintf(entry, sizeof(entry), "keyremap4macbook.%s", name);

    int value;
    size_t len = sizeof(value);
    if (sysctlbyname(entry, &value, &len, NULL, 0) == -1) return;

    CFStringRef key = CFStringCreateWithCString(NULL, name, kCFStringEncodingUTF8);
    CFNumberRef val = CFNumberCreate(NULL, kCFNumberIntType, &value);

    typeof(map_reset.end()) it = map_reset.find(name);
    if (it == map_reset.end()) return;
    CFNumberRef defaultval = CFNumberCreate(NULL, kCFNumberIntType, &(it->second));

    if (CFNumberCompare(val, defaultval, NULL) != 0) {
      CFDictionarySetValue(dict_sysctl, key, val);
    }
  }

  void
  load(const char *name)
  {
    if (! dict_sysctl) return;
    CFStringRef key = CFStringCreateWithCString(NULL, name, kCFStringEncodingUTF8);

    CFNumberRef val = reinterpret_cast<CFNumberRef>(CFDictionaryGetValue(dict_sysctl, key));
    if (! val) return;

    int value;
    if (! CFNumberGetValue(val, kCFNumberIntType, &value)) return;

    char cmd[512];
    snprintf(cmd, sizeof(cmd), "/Library/org.pqrs/KeyRemap4MacBook/bin/KeyRemap4MacBook_sysctl_set %s %d", name, value);
    system(cmd);
  }

  void
  scanLines(const char *filename, void (*func)(const char *))
  {
    std::ifstream ifs(filename);
    if (! ifs) return;

    while (! ifs.eof()) {
      char line[512];

      ifs.getline(line, sizeof(line));

      const char *sysctl_begin = "<sysctl>";
      const char *sysctl_end = "</sysctl>";

      char *begin = strstr(line, "<sysctl>");
      if (! begin) continue;
      char *end = strstr(line, sysctl_end);
      if (! end) continue;

      begin += strlen(sysctl_begin);
      *end = '\0';

      func(begin);
    }
  }

  bool
  makeMapReset(void)
  {
    std::ifstream ifs("/Library/org.pqrs/KeyRemap4MacBook/share/reset");
    if (! ifs) return false;

    while (! ifs.eof()) {
      char line[512];

      ifs.getline(line, sizeof(line));

      char *p = strchr(line, ' ');
      if (! p) continue;
      *p = '\0';

      int value = atoi(p + 1);

      map_reset[line] = value;
    }

    return true;
  }

  bool
  saveToFile(const char **targetFiles, CFStringRef identify)
  {
    if (! makeMapReset()) return false;

    dict_sysctl = CFDictionaryCreateMutable(NULL, 0, NULL, NULL);
    if (! dict_sysctl) return false;

    for (int i = 0; ; ++i) {
      const char *filename = targetFiles[i];
      if (! filename) break;
      scanLines(filename, save);
    }
    CFPreferencesSetAppValue(identify, dict_sysctl, applicationID);

    CFRelease(dict_sysctl); dict_sysctl = NULL;
    return true;
  }

  bool
  loadFromFile(const char **targetFiles, CFStringRef identify)
  {
    dict_sysctl = reinterpret_cast<CFMutableDictionaryRef>(const_cast<void *>(CFPreferencesCopyAppValue(identify, applicationID)));
    if (! dict_sysctl) return false;

    for (int i = 0; ; ++i) {
      const char *filename = targetFiles[i];
      if (! filename) break;
      scanLines(filename, load);
    }

    CFRelease(dict_sysctl); dict_sysctl = NULL;
    return true;
  }

  // ============================================================
  // ADD & DELETE & SELECT
  void
  setConfigList(CFArrayRef list)
  {
    CFPreferencesSetAppValue(CFSTR("configList"), list, applicationID);
  }

  CFArrayRef
  getConfigList(void)
  {
    CFArrayRef list = reinterpret_cast<CFArrayRef>(CFPreferencesCopyAppValue(CFSTR("configList"), applicationID));

    if (! list || CFArrayGetCount(list) == 0) {
      if (list) CFRelease(list);

      CFMutableDictionaryRef dict[1];
      dict[0] = CFDictionaryCreateMutable(NULL, 0, NULL, NULL);
      CFDictionarySetValue(dict[0], CFSTR("name"), CFSTR("Default"));
      CFDictionarySetValue(dict[0], CFSTR("identify"), CFSTR("config_default"));

      list = CFArrayCreate(NULL, const_cast<const void **>(reinterpret_cast<void **>(dict)), 1, NULL);
      setConfigList(list);
    }

    return list;
  }

  CFDictionaryRef
  getConfigDictionary(int index)
  {
    CFArrayRef list = getConfigList();
    if (! list) return NULL;

    if (index < 0) return NULL;
    if (index >= CFArrayGetCount(list)) return NULL;

    return reinterpret_cast<CFDictionaryRef>(CFArrayGetValueAtIndex(list, index));
  }

  CFStringRef
  getIdentify(int index)
  {
    CFDictionaryRef dict = getConfigDictionary(index);
    if (! dict) return NULL;

    return reinterpret_cast<CFStringRef>(CFDictionaryGetValue(dict, CFSTR("identify")));
  }

  CFStringRef
  getConfigName(int index)
  {
    CFDictionaryRef dict = getConfigDictionary(index);
    if (! dict) return NULL;

    return reinterpret_cast<CFStringRef>(CFDictionaryGetValue(dict, CFSTR("name")));
  }

  bool
  appendConfig(void)
  {
    CFArrayRef list = getConfigList();
    if (! list) return false;

    struct timeval tm;
    gettimeofday(&tm, NULL);
    CFStringRef identify = CFStringCreateWithFormat(NULL, NULL, CFSTR("config_%d_%d"), tm.tv_sec, tm.tv_usec);

    CFMutableDictionaryRef dict;
    dict = CFDictionaryCreateMutable(NULL, 0, NULL, NULL);
    CFDictionarySetValue(dict, CFSTR("name"), CFSTR("NewItem"));
    CFDictionarySetValue(dict, CFSTR("identify"), identify);

    CFMutableArrayRef newlist = CFArrayCreateMutableCopy(NULL, 0, list);
    CFArrayAppendValue(newlist, dict);

    setConfigList(newlist);
    return true;
  }

  bool
  removeConfig(int index)
  {
    CFStringRef identify = getIdentify(index);
    if (! identify) return false;

    CFPreferencesSetAppValue(identify, NULL, applicationID);

    CFArrayRef list = getConfigList();
    if (! list) return false;

    CFMutableArrayRef newlist = CFArrayCreateMutableCopy(NULL, 0, list);
    CFArrayRemoveValueAtIndex(newlist, index);

    setConfigList(newlist);
    return true;
  }

  bool
  renameConfig(int index, const char *newname)
  {
    CFArrayRef list = getConfigList();
    if (! list) return false;

    CFDictionaryRef olddict = getConfigDictionary(index);
    if (! olddict) return false;

    CFMutableDictionaryRef newdict = CFDictionaryCreateMutableCopy(NULL, 0, olddict);
    CFDictionarySetValue(newdict, CFSTR("name"), CFStringCreateWithCString(NULL, newname, kCFStringEncodingUTF8));

    CFMutableArrayRef newlist = CFArrayCreateMutableCopy(NULL, 0, list);
    CFArraySetValueAtIndex(newlist, index, newdict);

    setConfigList(newlist);
    return true;
  }

  int
  getConfigCount(void)
  {
    CFArrayRef list = getConfigList();
    if (! list) return 0;

    return CFArrayGetCount(list);
  }

  // ----------------------------------------
  bool
  setSelectedIndex(int index)
  {
    CFDictionaryRef dict = getConfigDictionary(index);
    if (! dict) return false;

    CFNumberRef val = CFNumberCreate(NULL, kCFNumberIntType, &index);
    CFPreferencesSetAppValue(CFSTR("selectedIndex"), val, applicationID);

    return true;
  }

  int
  getSelectedIndex(void)
  {
    Boolean isOK;
    CFIndex value = CFPreferencesGetAppIntegerValue(CFSTR("selectedIndex"), applicationID, &isOK);
    if (! isOK || value < 0) {
      value = 0;
      setSelectedIndex(value);
    }
    return value;
  }
}

int
main(int argc, char **argv)
{
  if (argc == 1) {
    fprintf(stderr, "Usage: %s (save|load|add|delete|rename|getname|select|count) [params]\n", argv[0]);
    return 1;
  }

  bool isSuccess = false;

  if (strcmp(argv[1], "select") == 0) {
    if (argc < 3) {
      fprintf(stderr, "Usage: %s select index\n", argv[0]);
      goto finish;
    }
    int index = atoi(argv[2]);
    isSuccess = setSelectedIndex(index);

  } else if (strcmp(argv[1], "add") == 0) {
    isSuccess = appendConfig();

  } else if (strcmp(argv[1], "delete") == 0) {
    if (argc < 3) {
      fprintf(stderr, "Usage: %s delete index\n", argv[0]);
      goto finish;
    }
    int index = atoi(argv[2]);
    isSuccess = removeConfig(index);

  } else if (strcmp(argv[1], "rename") == 0) {
    // sysctl_ctl rename "index" "newname"
    if (argc < 4) {
      fprintf(stderr, "Usage: %s rename index newname\n", argv[0]);
      goto finish;
    }
    int index = atoi(argv[2]);
    isSuccess = renameConfig(index, argv[3]);

  } else if (strcmp(argv[1], "count") == 0) {
    printf("%d\n", getConfigCount());
    return 0;

  } else if (strcmp(argv[1], "getname") == 0) {
    if (argc < 3) {
      fprintf(stderr, "Usage: %s getname index\n", argv[0]);
      goto finish;
    }
    int index = atoi(argv[2]);
    CFStringRef name = getConfigName(index);
    if (name) {
      printf("%s\n", CFStringGetCStringPtr(name, kCFStringEncodingUTF8));
    }
    return 0;

  } else if ((strcmp(argv[1], "save") == 0) || (strcmp(argv[1], "load") == 0)) {
    int value = getSelectedIndex();
    CFStringRef identify = getIdentify(value);

    const char *targetFiles[] = {
      "/Library/org.pqrs/KeyRemap4MacBook/prefpane/checkbox.xml",
      "/Library/org.pqrs/KeyRemap4MacBook/prefpane/number.xml",
      NULL,
    };
    if (strcmp(argv[1], "save") == 0) {
      isSuccess = saveToFile(targetFiles, identify);
    }
    if (strcmp(argv[1], "load") == 0) {
      system("/Library/org.pqrs/KeyRemap4MacBook/bin/KeyRemap4MacBook_sysctl_reset");
      system("/Library/org.pqrs/KeyRemap4MacBook/bin/KeyRemap4MacBook_sysctl_set initialized 1");
      isSuccess = loadFromFile(targetFiles, identify);
    }
  }

  CFPreferencesAppSynchronize(applicationID);

finish:
  if (isSuccess) {
    fprintf(stderr, "[DONE]\n");
  } else {
    fprintf(stderr, "[ERROR]\n");
  }

  return 0;
}
<|endoftext|>