<commit_before>// Copyright (c) 2007, 2008 libmv authors.
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to
// deal in the Software without restriction, including without limitation the
// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
// sell copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
// IN THE SOFTWARE.

#if _WIN32
#define NOMINMAX // Eigen needs this
#endif //_WIN32

#include <QtGui>
#include <QtOpenGL>
#include <cmath>

#include "ui/tvr/match_viewer.h"

using libmv::Matches;
using libmv::PointFeature;

static bool ImageContains(OnScreenImage &im, GLTexture &tex, float x, float y) {
  return im.posx < x && x < im.posx + tex.width
      && im.posy < y && y < im.posy + tex.height;
}

MatchViewer::MatchViewer(QGLWidget *share, GLTexture *textures, QWidget *parent)
: QGLWidget(0, share), document_(NULL), textures_(textures) {
  tx = 0;
  ty = 0;
  zoom = 1;
  InitTextures();

  connect(parent, SIGNAL(GLUpdateNeeded()), this, SLOT(GLUpdate()));
  connect(parent, SIGNAL(TextureChanged()), this, SLOT(TextureChange()));

  setWindowTitle("2D View");
}

MatchViewer::~MatchViewer() {
  makeCurrent();
}

void MatchViewer::SetDocument(TvrDocument *doc) {
  document_ = doc;
}

QSize MatchViewer::minimumSizeHint() const {
  return QSize(50, 50);
}

QSize MatchViewer::sizeHint() const {
  return QSize(800, 400);
}

void MatchViewer::SetTransformation(float tx_, float ty_, float zoom_) {
  tx = tx_;
  ty = ty_;
  zoom = zoom_;
  updateGL();
}

void MatchViewer::PlaneFromScreen(float xw, float yw, float *xi, float *yi) {
  *xi = zoom * xw + tx;
  *yi = zoom * yw + ty;
}

void MatchViewer::ScreenFromPlane(float xi, float yi, float *xw, float *yw) {
  *xw = (xi - tx) / zoom;
  *yw = (yi - ty) / zoom;
}

void MatchViewer::initializeGL() {
  glClearColor(0, 0, 0, 1);
  glShadeModel(GL_FLAT);
}

// Sets up GL_PROJECTION so that we can start drawing in plane coordinates.
void MatchViewer::SetUpGlCamera() {
  glMatrixMode(GL_PROJECTION);
  glLoadIdentity();
  float x0, y0, x1, y1;
  PlaneFromScreen(0, 0, &x0, &y0);
  PlaneFromScreen(width(), height(), &x1, &y1);
  glOrtho(x0, x1, y1, y0, -1, 1);
  glMatrixMode(GL_MODELVIEW);
  glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
  glLoadIdentity();
}

void MatchViewer::DrawImage(int i) {
  assert(document_);
  assert(!document_->images[i].isNull());

  if (textures_[i].textureID) {
    OnScreenImage &si = screen_images_[i];

    glMatrixMode(GL_MODELVIEW);
    glPushMatrix();
    glTranslatef(si.posx, si.posy, 0);

    glEnable(GL_TEXTURE_2D);
    glBindTexture(GL_TEXTURE_2D, textures_[i].textureID);
    glBegin(GL_QUADS);
    glTexCoord2d(0, 0); glVertex2d(0, 0);
    glTexCoord2d(1, 0); glVertex2d(textures_[i].width, 0);
    glTexCoord2d(1, 1); glVertex2d(textures_[i].width, textures_[i].height);
    glTexCoord2d(0, 1); glVertex2d(0, textures_[i].height);
    glEnd();
    glDisable(GL_TEXTURE_2D);

    DrawFeatures(i);

    glPopMatrix();
  }
}

void MatchViewer::DrawFeatures(int image_index) {
  //libmv::vector<libmv::SurfFeature> &features =
  //    document_->feature_sets[image_index].features;

  for (Matches::Points r =
      document_->matches.InImage<PointFeature>(image_index); r; ++r) {
    glPushMatrix();
    glTranslatef(r.feature()->x(), r.feature()->y(), 0);
    // Convert from gaussian scale to pixel scale (see surf.h).
    float scale = r.feature()->scale * 9.0 / 1.2 / 2.0;
    glScalef(scale, scale, scale);
    // TODO(pau) when surf orientation will be detected, ensure that this is
    //           turning in the right sense and the right units (deg vs rad).
    //glRotatef(features[i].orientation, 0, 0, 1);
    glBegin(GL_LINES);
    glVertex2f(-1, -1); glVertex2f( 1, -1); // Square Box.
    glVertex2f( 1, -1); glVertex2f( 1,  1);
    glVertex2f( 1,  1); glVertex2f(-1,  1);
    glVertex2f(-1,  1); glVertex2f(-1, -1);
    glVertex2f(0,  0); glVertex2f(0, -1);   // Vertical line to see orientation.
    glEnd();
    glPopMatrix();
  }
}

void MatchViewer::DrawCandidateMatches() {
  float xoff[2] = { screen_images_[0].posx, screen_images_[1].posx};
  float yoff[2] = { screen_images_[0].posy, screen_images_[1].posy};

  glBegin(GL_LINES);  // TODO(keir): Note that this will break with > 2 images.
  for (Matches::Points r =
      document_->matches.AllReversed<PointFeature>(); r; ++r) {
    glVertex2f(xoff[r.image()] +  r.feature()->x(),
               yoff[r.image()] +  r.feature()->y());
  }
  glEnd();
}

void MatchViewer::paintGL() {
  if(TexturesInited()) {
    SetUpGlCamera();
    for (int i = 0; i < 2; ++i) {
      DrawImage(i);
    }
    DrawCandidateMatches();
  }
}

bool MatchViewer::TexturesInited() {
  return textures_[0].textureID && textures_[1].textureID
    && glIsTexture(textures_[0].textureID) && glIsTexture(textures_[1].textureID);
}

void MatchViewer::InitTextures() {
  int i;
  for (i=0; i<2; ++i) {
    InitTexture(i);
  }
}

void MatchViewer::InitTexture(int index) {
  OnScreenImage &oi = screen_images_[index];

  if (index == 0) {
    oi.posx = 0;
    oi.posy = 0;
  } else {
    OnScreenImage &prev = screen_images_[index - 1];
    oi.posx = prev.posx + textures_[index].width + 10;
    oi.posy = prev.posy;
  }
}

void MatchViewer::resizeGL(int width, int height) {
  glViewport(0, 0, width, height);
}

int MatchViewer::ImageUnderPointer(QMouseEvent *event) {
  float x, y;
  PlaneFromScreen(event->x(), event->y(), &x, &y);

  for (int i = 0; i < 2; ++i) {
    if (ImageContains(screen_images_[i], textures_[i], x, y)) {
      return i;
    }
  }
  return -1;
}

void MatchViewer::mousePressEvent(QMouseEvent *event) {
  lastPos = event->pos();

  // Set mouse drag behavior.
  mouse_drag_behavior_ = NONE;
  if (event->modifiers() & Qt::ShiftModifier) {
    int i = ImageUnderPointer(event);
    if (i >= 0) {
      dragging_image_ = i;
      mouse_drag_behavior_ = MOVE_IMAGE;
    }
  } else {
    mouse_drag_behavior_ = MOVE_VIEW;
  }
}

void MatchViewer::mouseMoveEvent(QMouseEvent *event) {
  float x0, y0, x1, y1;
  PlaneFromScreen(lastPos.x(), lastPos.y(), &x0, &y0);
  PlaneFromScreen(event->x(), event->y(), &x1, &y1);

  if (event->buttons() & Qt::LeftButton) {
    if (mouse_drag_behavior_ == MOVE_IMAGE) {
      // Move the first image under the pointer.
      screen_images_[dragging_image_].posx += x1 - x0;
      screen_images_[dragging_image_].posy += y1 - y0;
      updateGL();
    } else if (mouse_drag_behavior_ == MOVE_VIEW) {
      // Move view.
      SetTransformation(tx + x0 - x1, ty + y0 - y1, zoom);
    }
  }
  lastPos = event->pos();
}

void MatchViewer::wheelEvent(QWheelEvent *event) {
  // Zoom so that the image point under the cursor does not move.
  const float newzoom = zoom * pow(1.001, event->delta());
  const float newtx = (zoom - newzoom) * event->x() + tx;
  const float newty = (zoom - newzoom) * event->y() + ty;

  SetTransformation(newtx, newty, newzoom);
}
<commit_msg>- Bug fix. Fix images display if second image have a width inferior to the first. (They were displayed overlapped).<commit_after>// Copyright (c) 2007, 2008 libmv authors.
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to
// deal in the Software without restriction, including without limitation the
// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
// sell copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
// IN THE SOFTWARE.

#if _WIN32
#define NOMINMAX // Eigen needs this
#endif //_WIN32

#include <QtGui>
#include <QtOpenGL>
#include <cmath>

#include "ui/tvr/match_viewer.h"

using libmv::Matches;
using libmv::PointFeature;

static bool ImageContains(OnScreenImage &im, GLTexture &tex, float x, float y) {
  return im.posx < x && x < im.posx + tex.width
      && im.posy < y && y < im.posy + tex.height;
}

MatchViewer::MatchViewer(QGLWidget *share, GLTexture *textures, QWidget *parent)
: QGLWidget(0, share), document_(NULL), textures_(textures) {
  tx = 0;
  ty = 0;
  zoom = 1;
  InitTextures();

  connect(parent, SIGNAL(GLUpdateNeeded()), this, SLOT(GLUpdate()));
  connect(parent, SIGNAL(TextureChanged()), this, SLOT(TextureChange()));

  setWindowTitle("2D View");
}

MatchViewer::~MatchViewer() {
  makeCurrent();
}

void MatchViewer::SetDocument(TvrDocument *doc) {
  document_ = doc;
}

QSize MatchViewer::minimumSizeHint() const {
  return QSize(50, 50);
}

QSize MatchViewer::sizeHint() const {
  return QSize(800, 400);
}

void MatchViewer::SetTransformation(float tx_, float ty_, float zoom_) {
  tx = tx_;
  ty = ty_;
  zoom = zoom_;
  updateGL();
}

void MatchViewer::PlaneFromScreen(float xw, float yw, float *xi, float *yi) {
  *xi = zoom * xw + tx;
  *yi = zoom * yw + ty;
}

void MatchViewer::ScreenFromPlane(float xi, float yi, float *xw, float *yw) {
  *xw = (xi - tx) / zoom;
  *yw = (yi - ty) / zoom;
}

void MatchViewer::initializeGL() {
  glClearColor(0, 0, 0, 1);
  glShadeModel(GL_FLAT);
}

// Sets up GL_PROJECTION so that we can start drawing in plane coordinates.
void MatchViewer::SetUpGlCamera() {
  glMatrixMode(GL_PROJECTION);
  glLoadIdentity();
  float x0, y0, x1, y1;
  PlaneFromScreen(0, 0, &x0, &y0);
  PlaneFromScreen(width(), height(), &x1, &y1);
  glOrtho(x0, x1, y1, y0, -1, 1);
  glMatrixMode(GL_MODELVIEW);
  glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
  glLoadIdentity();
}

void MatchViewer::DrawImage(int i) {
  assert(document_);
  assert(!document_->images[i].isNull());

  if (textures_[i].textureID) {
    OnScreenImage &si = screen_images_[i];

    glMatrixMode(GL_MODELVIEW);
    glPushMatrix();
    glTranslatef(si.posx, si.posy, 0);

    glEnable(GL_TEXTURE_2D);
    glBindTexture(GL_TEXTURE_2D, textures_[i].textureID);
    glBegin(GL_QUADS);
    glTexCoord2d(0, 0); glVertex2d(0, 0);
    glTexCoord2d(1, 0); glVertex2d(textures_[i].width, 0);
    glTexCoord2d(1, 1); glVertex2d(textures_[i].width, textures_[i].height);
    glTexCoord2d(0, 1); glVertex2d(0, textures_[i].height);
    glEnd();
    glDisable(GL_TEXTURE_2D);

    DrawFeatures(i);

    glPopMatrix();
  }
}

void MatchViewer::DrawFeatures(int image_index) {
  //libmv::vector<libmv::SurfFeature> &features =
  //    document_->feature_sets[image_index].features;

  for (Matches::Points r =
      document_->matches.InImage<PointFeature>(image_index); r; ++r) {
    glPushMatrix();
    glTranslatef(r.feature()->x(), r.feature()->y(), 0);
    // Convert from gaussian scale to pixel scale (see surf.h).
    float scale = r.feature()->scale * 9.0 / 1.2 / 2.0;
    glScalef(scale, scale, scale);
    // TODO(pau) when surf orientation will be detected, ensure that this is
    //           turning in the right sense and the right units (deg vs rad).
    //glRotatef(features[i].orientation, 0, 0, 1);
    glBegin(GL_LINES);
    glVertex2f(-1, -1); glVertex2f( 1, -1); // Square Box.
    glVertex2f( 1, -1); glVertex2f( 1,  1);
    glVertex2f( 1,  1); glVertex2f(-1,  1);
    glVertex2f(-1,  1); glVertex2f(-1, -1);
    glVertex2f(0,  0); glVertex2f(0, -1);   // Vertical line to see orientation.
    glEnd();
    glPopMatrix();
  }
}

void MatchViewer::DrawCandidateMatches() {
  float xoff[2] = { screen_images_[0].posx, screen_images_[1].posx};
  float yoff[2] = { screen_images_[0].posy, screen_images_[1].posy};

  glBegin(GL_LINES);  // TODO(keir): Note that this will break with > 2 images.
  for (Matches::Points r =
      document_->matches.AllReversed<PointFeature>(); r; ++r) {
    glVertex2f(xoff[r.image()] +  r.feature()->x(),
               yoff[r.image()] +  r.feature()->y());
  }
  glEnd();
}

void MatchViewer::paintGL() {
  if(TexturesInited()) {
    SetUpGlCamera();
    for (int i = 0; i < 2; ++i) {
      DrawImage(i);
    }
    DrawCandidateMatches();
  }
}

bool MatchViewer::TexturesInited() {
  return textures_[0].textureID && textures_[1].textureID
    && glIsTexture(textures_[0].textureID) && glIsTexture(textures_[1].textureID);
}

void MatchViewer::InitTextures() {
  int i;
  for (i=0; i<2; ++i) {
    InitTexture(i);
  }
}

void MatchViewer::InitTexture(int index) {
  OnScreenImage &oi = screen_images_[index];

  if (index == 0) {
    oi.posx = 0;
    oi.posy = 0;
  } else {
    OnScreenImage &prev = screen_images_[index - 1];
    oi.posx = prev.posx + textures_[index-1].width + 10;
    oi.posy = prev.posy;
  }
}

void MatchViewer::resizeGL(int width, int height) {
  glViewport(0, 0, width, height);
}

int MatchViewer::ImageUnderPointer(QMouseEvent *event) {
  float x, y;
  PlaneFromScreen(event->x(), event->y(), &x, &y);

  for (int i = 0; i < 2; ++i) {
    if (ImageContains(screen_images_[i], textures_[i], x, y)) {
      return i;
    }
  }
  return -1;
}

void MatchViewer::mousePressEvent(QMouseEvent *event) {
  lastPos = event->pos();

  // Set mouse drag behavior.
  mouse_drag_behavior_ = NONE;
  if (event->modifiers() & Qt::ShiftModifier) {
    int i = ImageUnderPointer(event);
    if (i >= 0) {
      dragging_image_ = i;
      mouse_drag_behavior_ = MOVE_IMAGE;
    }
  } else {
    mouse_drag_behavior_ = MOVE_VIEW;
  }
}

void MatchViewer::mouseMoveEvent(QMouseEvent *event) {
  float x0, y0, x1, y1;
  PlaneFromScreen(lastPos.x(), lastPos.y(), &x0, &y0);
  PlaneFromScreen(event->x(), event->y(), &x1, &y1);

  if (event->buttons() & Qt::LeftButton) {
    if (mouse_drag_behavior_ == MOVE_IMAGE) {
      // Move the first image under the pointer.
      screen_images_[dragging_image_].posx += x1 - x0;
      screen_images_[dragging_image_].posy += y1 - y0;
      updateGL();
    } else if (mouse_drag_behavior_ == MOVE_VIEW) {
      // Move view.
      SetTransformation(tx + x0 - x1, ty + y0 - y1, zoom);
    }
  }
  lastPos = event->pos();
}

void MatchViewer::wheelEvent(QWheelEvent *event) {
  // Zoom so that the image point under the cursor does not move.
  const float newzoom = zoom * pow(1.001, event->delta());
  const float newtx = (zoom - newzoom) * event->x() + tx;
  const float newty = (zoom - newzoom) * event->y() + ty;

  SetTransformation(newtx, newty, newzoom);
}
<|endoftext|>


<commit_before>/*
**  Copyright (C) 2012 Aldebaran Robotics
**  See COPYING for the license
*/
#include <map>
#include <qi/atomic.hpp>

#include <boost/thread/recursive_mutex.hpp>
#include <boost/make_shared.hpp>

#include <qitype/signal.hpp>
#include <qitype/genericvalue.hpp>
#include <qitype/genericobject.hpp>

#include "object_p.hpp"
#include "signal_p.hpp"

qiLogCategory("qitype.signal");

namespace qi {

  SignalSubscriber::SignalSubscriber(qi::ObjectPtr target, unsigned int method)
  : weakLock(0), threadingModel(MetaCallType_Direct),  target(new qi::ObjectWeakPtr(target)), method(method), enabled(true)
  { // The slot has its own threading model: be synchronous
  }

  SignalSubscriber::SignalSubscriber(GenericFunction func, MetaCallType model, detail::WeakLock* lock)
     : handler(func), weakLock(lock), threadingModel(model), target(0), method(0), enabled(true)
   {
   }

  SignalSubscriber::~SignalSubscriber()
  {
    delete target;
    delete weakLock;
  }

  SignalSubscriber::SignalSubscriber(const SignalSubscriber& b)
  : weakLock(0), target(0)
  {
    *this = b;
  }

  void SignalSubscriber::operator=(const SignalSubscriber& b)
  {
    source = b.source;
    linkId = b.linkId;
    handler = b.handler;
    weakLock = b.weakLock?b.weakLock->clone():0;
    threadingModel = b.threadingModel;
    target = b.target?new ObjectWeakPtr(*b.target):0;
    method = b.method;
    enabled = b.enabled;
  }

  static qi::Atomic<int> linkUid = 1;

  void SignalBase::setCallType(MetaCallType callType)
  {
    if (!_p)
    {
      _p = boost::make_shared<SignalBasePrivate>();
    }
    _p->defaultCallType = callType;
  }

  void SignalBase::operator()(
      qi::AutoGenericValuePtr p1,
      qi::AutoGenericValuePtr p2,
      qi::AutoGenericValuePtr p3,
      qi::AutoGenericValuePtr p4,
      qi::AutoGenericValuePtr p5,
      qi::AutoGenericValuePtr p6,
      qi::AutoGenericValuePtr p7,
      qi::AutoGenericValuePtr p8)
  {
    qi::AutoGenericValuePtr* vals[8]= {&p1, &p2, &p3, &p4, &p5, &p6, &p7, &p8};
    std::vector<qi::GenericValuePtr> params;
    for (unsigned i = 0; i < 8; ++i)
      if (vals[i]->value)
        params.push_back(*vals[i]);
    // Signature construction
    std::string signature = "(";
    for (unsigned i=0; i< params.size(); ++i)
      signature += params[i].signature();
    signature += ")";
    if (signature != _p->signature)
    {
      qiLogError() << "Dropping emit: signature mismatch: " << signature <<" " << _p->signature;
      return;
    }
    trigger(params, _p->defaultCallType);
  }

  void SignalBase::trigger(const GenericFunctionParameters& params, MetaCallType callType)
  {
    MetaCallType mct = callType;

    if (!_p)
      return;

    if (mct == qi::MetaCallType_Auto)
      mct = _p->defaultCallType;
    SignalSubscriberMap copy;
    {
      boost::recursive_mutex::scoped_lock sl(_p->mutex);
      copy = _p->subscriberMap;
    }

    SignalSubscriberMap::iterator i;
    for (i = copy.begin(); i != copy.end(); ++i)
    {
      qiLogDebug() << (void*)this << " Invoking signal subscriber";
      SignalSubscriberPtr s = i->second; // hold s alive
      s->call(params, mct);
    }
    qiLogDebug() << (void*)this << " done invoking signal subscribers";
  }

  class FunctorCall
  {
  public:
    FunctorCall(GenericFunctionParameters* params, SignalSubscriberPtr* sub)
    : params(params)
    , sub(sub)
    {
    }

    FunctorCall(const FunctorCall& b)
    {
      *this = b;
    }

    void operator=(const FunctorCall& b)
    {
      params = b.params;
      sub = b.sub;
    }

    void operator() ()
    {
      try
      {
        {
          SignalSubscriberPtr s;
          boost::mutex::scoped_lock sl((*sub)->mutex);
          // verify-enabled-then-register-active op must be locked
          if (!(*sub)->enabled)
          {
            s = *sub; // delay destruction until after we leave the scoped_lock
            delete sub;
            params->destroy();
            delete params;
            return;
          }
          (*sub)->addActive(false);
        } // end mutex-protected scope
        (*sub)->handler(*params);
      }
      catch(const std::exception& e)
      {
        qiLogVerbose() << "Exception caught from signal subscriber: " << e.what();
      }
      (*sub)->removeActive(true);
      params->destroy();
      delete params;
      if ((*sub)->weakLock)
        (*sub)->weakLock->unlock();
      delete sub;
    }

  public:
    GenericFunctionParameters* params;
    SignalSubscriberPtr*         sub;
  };

  void SignalSubscriber::call(const GenericFunctionParameters& args, MetaCallType callType)
  {
    // this is held alive by caller
    if (handler)
    {
      // Try to acquire weakLock, for both the sync and async cases
      if (weakLock)
      {
        bool locked = weakLock->tryLock();
        if (!locked)
        {
          source->disconnect(linkId);
          return;
        }
      }
      bool async = true;
      if (threadingModel != MetaCallType_Auto)
        async = (threadingModel == MetaCallType_Queued);
      else if (callType != MetaCallType_Auto)
        async = (callType == MetaCallType_Queued);

      qiLogDebug() << "subscriber call async=" << async <<" ct " << callType <<" tm " << threadingModel;
      if (async)
      {
        GenericFunctionParameters* copy = new GenericFunctionParameters(args.copy());
        // We will check enabled when we will be scheduled in the target
        // thread, and we hold this SignalSubscriber alive, so no need to
        // explicitly track the asynccall
        getDefaultThreadPoolEventLoop()->post(FunctorCall(copy, new SignalSubscriberPtr(shared_from_this())));
      }
      else
      {
        // verify-enabled-then-register-active op must be locked
        {
          boost::mutex::scoped_lock sl(mutex);
          if (!enabled)
            return;
          addActive(false);
        }
        handler(args);
        if (weakLock)
          weakLock->unlock();
        removeActive(true);
      }
    }
    else if (target)
    {
      ObjectPtr lockedTarget = target->lock();
      if (!lockedTarget)
      {
        source->disconnect(linkId);
      }
      else // no need to keep anything locked, whatever happens this is not used
        lockedTarget->metaPost(method, args);
    }
  }

  //check if we are called from the same thread that triggered us.
  //in that case, do not wait.
  void SignalSubscriber::waitForInactive()
  {
    boost::thread::id tid = boost::this_thread::get_id();
    while (true)
    {
      {
        boost::mutex::scoped_lock sl(mutex);
        if (activeThreads.empty())
          return;
        // There cannot be two activeThreads entry for the same tid
        // because activeThreads is not set at the post() stage
        if (activeThreads.size() == 1
          && *activeThreads.begin() == tid)
        { // One active callback in this thread, means above us in call stack
          // So we cannot wait for it
          return;
        }
      }
      os::msleep(1); // FIXME too long use a condition
    }
  }

  void SignalSubscriber::addActive(bool acquireLock, boost::thread::id id)
  {
    if (acquireLock)
    {
      boost::mutex::scoped_lock sl(mutex);
      activeThreads.push_back(id);
    }
    else
      activeThreads.push_back(id);
  }

  void SignalSubscriber::removeActive(bool acquireLock, boost::thread::id id)
  {

    boost::mutex::scoped_lock sl(mutex, boost::defer_lock_t());
    if (acquireLock)
      sl.lock();

    for (unsigned i=0; i<activeThreads.size(); ++i)
    {
      if (activeThreads[i] == id)
      { // fast remove by swapping with last and then pop_back
        activeThreads[i] = activeThreads[activeThreads.size() - 1];
        activeThreads.pop_back();
      }
    }
  }

  SignalSubscriber& SignalBase::connect(GenericFunction callback, MetaCallType model)
  {
    return connect(SignalSubscriber(callback, model));
  }

  SignalSubscriber& SignalBase::connect(qi::ObjectPtr o, unsigned int slot)
  {
    return connect(SignalSubscriber(o, slot));
  }

  SignalSubscriber& SignalBase::connect(const SignalSubscriber& src)
  {
    qiLogDebug() << (void*)this << " connecting new subscriber";
    static SignalSubscriber invalid;
    if (!_p)
    {
      _p = boost::make_shared<SignalBasePrivate>();
    }
    // Check arity. Does not require to acquire weakLock.
    int sigArity = Signature(signature()).begin().children().size();
    int subArity = -1;
    if (src.handler)
    {
      if (src.handler.functionType() == dynamicFunctionType())
        goto proceed; // no arity checking is possible
      subArity = src.handler.argumentsType().size();
    }
    else if (src.target)
    {
      ObjectPtr locked = src.target->lock();
      if (!locked)
      {
        qiLogVerbose() << "connecting a dead slot (weak ptr out)";
        return invalid;
      }
      const MetaMethod* ms = locked->metaObject().method(src.method);
      if (!ms)
      {
        qiLogWarning() << "Method " << src.method <<" not found, proceeding anyway";
        goto proceed;
      }
      else
        subArity = Signature(qi::signatureSplit(ms->signature())[2]).size();
    }
    if (sigArity != subArity)
    {
      qiLogWarning() << "Subscriber has incorrect arity (expected "
        << sigArity  << " , got " << subArity <<")";
      return invalid;
    }
  proceed:
    boost::recursive_mutex::scoped_lock sl(_p->mutex);
    Link res = ++linkUid;
    SignalSubscriberPtr s = boost::make_shared<SignalSubscriber>(src);
    s->linkId = res;
    s->source = this;
    bool first = _p->subscriberMap.empty();
    _p->subscriberMap[res] = s;
    if (first && _p->onSubscribers)
      _p->onSubscribers(true);
    return *s.get();
  }

  bool SignalBase::disconnectAll() {
    if (_p)
    {
      bool hasSubs = !_p->subscriberMap.empty();
      return _p->reset();
      if (hasSubs && _p->onSubscribers)
        _p->onSubscribers(false);
    }
    return false;
  }

  SignalBase::SignalBase(const std::string& sig, OnSubscribers onSubscribers)
    : _p(new SignalBasePrivate)
  {
    _p->onSubscribers = onSubscribers;
    _p->signature = sig;
  }

  SignalBase::SignalBase(OnSubscribers onSubscribers)
  : _p(new SignalBasePrivate)
  {
    _p->onSubscribers = onSubscribers;
  }

  SignalBase::SignalBase(const SignalBase& b)
  {
    (*this) = b;
  }

  SignalBase& SignalBase::operator=(const SignalBase& b)
  {
    if (!b._p)
    {
      const_cast<SignalBase&>(b)._p = boost::make_shared<SignalBasePrivate>();
    }
    _p = b._p;
    return *this;
  }

  std::string SignalBase::signature() const
  {
    return _p ? _p->signature : "";
  }

  bool SignalBasePrivate::disconnect(const SignalBase::Link& l)
  {

    SignalSubscriberPtr s;
    // Acquire signal mutex
    boost::recursive_mutex::scoped_lock sigLock(mutex);
    SignalSubscriberMap::iterator it = subscriberMap.find(l);
    if (it == subscriberMap.end())
      return false;
    s = it->second;
    // Remove from map (but SignalSubscriber object still good)
    subscriberMap.erase(it);
    // Acquire subscriber mutex before releasing mutex
    boost::mutex::scoped_lock subLock(s->mutex);
    // Release signal mutex
    sigLock.release()->unlock();
    // Ensure no call on subscriber occurrs once this function returns
    s->enabled = false;
    if (subscriberMap.empty() && onSubscribers)
      onSubscribers(false);
    if ( s->activeThreads.empty()
         || (s->activeThreads.size() == 1
             && *s->activeThreads.begin() == boost::this_thread::get_id()))
    { // One active callback in this thread, means above us in call stack
      // So we cannot trash s right now
      return true;
    }
    // More than one active callback, or one in a state that prevent us
    // from knowing in which thread it will run
    subLock.release()->unlock();
    s->waitForInactive();
    return true;
  }

  bool SignalBase::disconnect(const Link &link) {
    if (!_p)
      return false;
    else
      return _p->disconnect(link);
  }

  SignalBase::~SignalBase()
  {
    if (!_p)
      return;
    _p->onSubscribers = OnSubscribers();
    boost::shared_ptr<SignalBasePrivate> p(_p);
    _p.reset();
    SignalSubscriberMap::iterator i;
    std::vector<Link> links;
    for (i = p->subscriberMap.begin(); i!= p->subscriberMap.end(); ++i)
    {
      links.push_back(i->first);
    }
    for (unsigned i=0; i<links.size(); ++i)
      p->disconnect(links[i]);
  }

  std::vector<SignalSubscriber> SignalBase::subscribers()
  {
    std::vector<SignalSubscriber> res;
    if (!_p)
      return res;
    boost::recursive_mutex::scoped_lock sl(_p->mutex);
    SignalSubscriberMap::iterator i;
    for (i = _p->subscriberMap.begin(); i!= _p->subscriberMap.end(); ++i)
      res.push_back(*i->second);
    return res;
  }

  bool SignalBasePrivate::reset() {
    bool ret = true;
    boost::recursive_mutex::scoped_lock sl(mutex);
    SignalSubscriberMap::iterator it = subscriberMap.begin();
    while (it != subscriberMap.end()) {
      bool b = disconnect(it->first);
      if (!b)
        ret = false;
      it = subscriberMap.begin();
    }
    return ret;
  }

  QITYPE_API const SignalBase::Link SignalBase::invalidLink = ((unsigned int)-1);

}
<commit_msg>Signal: remove dead code.<commit_after>/*
**  Copyright (C) 2012 Aldebaran Robotics
**  See COPYING for the license
*/
#include <map>
#include <qi/atomic.hpp>

#include <boost/thread/recursive_mutex.hpp>
#include <boost/make_shared.hpp>

#include <qitype/signal.hpp>
#include <qitype/genericvalue.hpp>
#include <qitype/genericobject.hpp>

#include "object_p.hpp"
#include "signal_p.hpp"

qiLogCategory("qitype.signal");

namespace qi {

  SignalSubscriber::SignalSubscriber(qi::ObjectPtr target, unsigned int method)
  : weakLock(0), threadingModel(MetaCallType_Direct),  target(new qi::ObjectWeakPtr(target)), method(method), enabled(true)
  { // The slot has its own threading model: be synchronous
  }

  SignalSubscriber::SignalSubscriber(GenericFunction func, MetaCallType model, detail::WeakLock* lock)
     : handler(func), weakLock(lock), threadingModel(model), target(0), method(0), enabled(true)
   {
   }

  SignalSubscriber::~SignalSubscriber()
  {
    delete target;
    delete weakLock;
  }

  SignalSubscriber::SignalSubscriber(const SignalSubscriber& b)
  : weakLock(0), target(0)
  {
    *this = b;
  }

  void SignalSubscriber::operator=(const SignalSubscriber& b)
  {
    source = b.source;
    linkId = b.linkId;
    handler = b.handler;
    weakLock = b.weakLock?b.weakLock->clone():0;
    threadingModel = b.threadingModel;
    target = b.target?new ObjectWeakPtr(*b.target):0;
    method = b.method;
    enabled = b.enabled;
  }

  static qi::Atomic<int> linkUid = 1;

  void SignalBase::setCallType(MetaCallType callType)
  {
    if (!_p)
    {
      _p = boost::make_shared<SignalBasePrivate>();
    }
    _p->defaultCallType = callType;
  }

  void SignalBase::operator()(
      qi::AutoGenericValuePtr p1,
      qi::AutoGenericValuePtr p2,
      qi::AutoGenericValuePtr p3,
      qi::AutoGenericValuePtr p4,
      qi::AutoGenericValuePtr p5,
      qi::AutoGenericValuePtr p6,
      qi::AutoGenericValuePtr p7,
      qi::AutoGenericValuePtr p8)
  {
    qi::AutoGenericValuePtr* vals[8]= {&p1, &p2, &p3, &p4, &p5, &p6, &p7, &p8};
    std::vector<qi::GenericValuePtr> params;
    for (unsigned i = 0; i < 8; ++i)
      if (vals[i]->value)
        params.push_back(*vals[i]);
    // Signature construction
    std::string signature = "(";
    for (unsigned i=0; i< params.size(); ++i)
      signature += params[i].signature();
    signature += ")";
    if (signature != _p->signature)
    {
      qiLogError() << "Dropping emit: signature mismatch: " << signature <<" " << _p->signature;
      return;
    }
    trigger(params, _p->defaultCallType);
  }

  void SignalBase::trigger(const GenericFunctionParameters& params, MetaCallType callType)
  {
    MetaCallType mct = callType;

    if (!_p)
      return;

    if (mct == qi::MetaCallType_Auto)
      mct = _p->defaultCallType;
    SignalSubscriberMap copy;
    {
      boost::recursive_mutex::scoped_lock sl(_p->mutex);
      copy = _p->subscriberMap;
    }

    SignalSubscriberMap::iterator i;
    for (i = copy.begin(); i != copy.end(); ++i)
    {
      qiLogDebug() << (void*)this << " Invoking signal subscriber";
      SignalSubscriberPtr s = i->second; // hold s alive
      s->call(params, mct);
    }
    qiLogDebug() << (void*)this << " done invoking signal subscribers";
  }

  class FunctorCall
  {
  public:
    FunctorCall(GenericFunctionParameters* params, SignalSubscriberPtr* sub)
    : params(params)
    , sub(sub)
    {
    }

    FunctorCall(const FunctorCall& b)
    {
      *this = b;
    }

    void operator=(const FunctorCall& b)
    {
      params = b.params;
      sub = b.sub;
    }

    void operator() ()
    {
      try
      {
        {
          SignalSubscriberPtr s;
          boost::mutex::scoped_lock sl((*sub)->mutex);
          // verify-enabled-then-register-active op must be locked
          if (!(*sub)->enabled)
          {
            s = *sub; // delay destruction until after we leave the scoped_lock
            delete sub;
            params->destroy();
            delete params;
            return;
          }
          (*sub)->addActive(false);
        } // end mutex-protected scope
        (*sub)->handler(*params);
      }
      catch(const std::exception& e)
      {
        qiLogVerbose() << "Exception caught from signal subscriber: " << e.what();
      }
      (*sub)->removeActive(true);
      params->destroy();
      delete params;
      if ((*sub)->weakLock)
        (*sub)->weakLock->unlock();
      delete sub;
    }

  public:
    GenericFunctionParameters* params;
    SignalSubscriberPtr*         sub;
  };

  void SignalSubscriber::call(const GenericFunctionParameters& args, MetaCallType callType)
  {
    // this is held alive by caller
    if (handler)
    {
      // Try to acquire weakLock, for both the sync and async cases
      if (weakLock)
      {
        bool locked = weakLock->tryLock();
        if (!locked)
        {
          source->disconnect(linkId);
          return;
        }
      }
      bool async = true;
      if (threadingModel != MetaCallType_Auto)
        async = (threadingModel == MetaCallType_Queued);
      else if (callType != MetaCallType_Auto)
        async = (callType == MetaCallType_Queued);

      qiLogDebug() << "subscriber call async=" << async <<" ct " << callType <<" tm " << threadingModel;
      if (async)
      {
        GenericFunctionParameters* copy = new GenericFunctionParameters(args.copy());
        // We will check enabled when we will be scheduled in the target
        // thread, and we hold this SignalSubscriber alive, so no need to
        // explicitly track the asynccall
        getDefaultThreadPoolEventLoop()->post(FunctorCall(copy, new SignalSubscriberPtr(shared_from_this())));
      }
      else
      {
        // verify-enabled-then-register-active op must be locked
        {
          boost::mutex::scoped_lock sl(mutex);
          if (!enabled)
            return;
          addActive(false);
        }
        handler(args);
        if (weakLock)
          weakLock->unlock();
        removeActive(true);
      }
    }
    else if (target)
    {
      ObjectPtr lockedTarget = target->lock();
      if (!lockedTarget)
      {
        source->disconnect(linkId);
      }
      else // no need to keep anything locked, whatever happens this is not used
        lockedTarget->metaPost(method, args);
    }
  }

  //check if we are called from the same thread that triggered us.
  //in that case, do not wait.
  void SignalSubscriber::waitForInactive()
  {
    boost::thread::id tid = boost::this_thread::get_id();
    while (true)
    {
      {
        boost::mutex::scoped_lock sl(mutex);
        if (activeThreads.empty())
          return;
        // There cannot be two activeThreads entry for the same tid
        // because activeThreads is not set at the post() stage
        if (activeThreads.size() == 1
          && *activeThreads.begin() == tid)
        { // One active callback in this thread, means above us in call stack
          // So we cannot wait for it
          return;
        }
      }
      os::msleep(1); // FIXME too long use a condition
    }
  }

  void SignalSubscriber::addActive(bool acquireLock, boost::thread::id id)
  {
    if (acquireLock)
    {
      boost::mutex::scoped_lock sl(mutex);
      activeThreads.push_back(id);
    }
    else
      activeThreads.push_back(id);
  }

  void SignalSubscriber::removeActive(bool acquireLock, boost::thread::id id)
  {

    boost::mutex::scoped_lock sl(mutex, boost::defer_lock_t());
    if (acquireLock)
      sl.lock();

    for (unsigned i=0; i<activeThreads.size(); ++i)
    {
      if (activeThreads[i] == id)
      { // fast remove by swapping with last and then pop_back
        activeThreads[i] = activeThreads[activeThreads.size() - 1];
        activeThreads.pop_back();
      }
    }
  }

  SignalSubscriber& SignalBase::connect(GenericFunction callback, MetaCallType model)
  {
    return connect(SignalSubscriber(callback, model));
  }

  SignalSubscriber& SignalBase::connect(qi::ObjectPtr o, unsigned int slot)
  {
    return connect(SignalSubscriber(o, slot));
  }

  SignalSubscriber& SignalBase::connect(const SignalSubscriber& src)
  {
    qiLogDebug() << (void*)this << " connecting new subscriber";
    static SignalSubscriber invalid;
    if (!_p)
    {
      _p = boost::make_shared<SignalBasePrivate>();
    }
    // Check arity. Does not require to acquire weakLock.
    int sigArity = Signature(signature()).begin().children().size();
    int subArity = -1;
    if (src.handler)
    {
      if (src.handler.functionType() == dynamicFunctionType())
        goto proceed; // no arity checking is possible
      subArity = src.handler.argumentsType().size();
    }
    else if (src.target)
    {
      ObjectPtr locked = src.target->lock();
      if (!locked)
      {
        qiLogVerbose() << "connecting a dead slot (weak ptr out)";
        return invalid;
      }
      const MetaMethod* ms = locked->metaObject().method(src.method);
      if (!ms)
      {
        qiLogWarning() << "Method " << src.method <<" not found, proceeding anyway";
        goto proceed;
      }
      else
        subArity = Signature(qi::signatureSplit(ms->signature())[2]).size();
    }
    if (sigArity != subArity)
    {
      qiLogWarning() << "Subscriber has incorrect arity (expected "
        << sigArity  << " , got " << subArity <<")";
      return invalid;
    }
  proceed:
    boost::recursive_mutex::scoped_lock sl(_p->mutex);
    Link res = ++linkUid;
    SignalSubscriberPtr s = boost::make_shared<SignalSubscriber>(src);
    s->linkId = res;
    s->source = this;
    bool first = _p->subscriberMap.empty();
    _p->subscriberMap[res] = s;
    if (first && _p->onSubscribers)
      _p->onSubscribers(true);
    return *s.get();
  }

  bool SignalBase::disconnectAll() {
    if (_p)
      return _p->reset();
    return false;
  }

  SignalBase::SignalBase(const std::string& sig, OnSubscribers onSubscribers)
    : _p(new SignalBasePrivate)
  {
    _p->onSubscribers = onSubscribers;
    _p->signature = sig;
  }

  SignalBase::SignalBase(OnSubscribers onSubscribers)
  : _p(new SignalBasePrivate)
  {
    _p->onSubscribers = onSubscribers;
  }

  SignalBase::SignalBase(const SignalBase& b)
  {
    (*this) = b;
  }

  SignalBase& SignalBase::operator=(const SignalBase& b)
  {
    if (!b._p)
    {
      const_cast<SignalBase&>(b)._p = boost::make_shared<SignalBasePrivate>();
    }
    _p = b._p;
    return *this;
  }

  std::string SignalBase::signature() const
  {
    return _p ? _p->signature : "";
  }

  bool SignalBasePrivate::disconnect(const SignalBase::Link& l)
  {

    SignalSubscriberPtr s;
    // Acquire signal mutex
    boost::recursive_mutex::scoped_lock sigLock(mutex);
    SignalSubscriberMap::iterator it = subscriberMap.find(l);
    if (it == subscriberMap.end())
      return false;
    s = it->second;
    // Remove from map (but SignalSubscriber object still good)
    subscriberMap.erase(it);
    // Acquire subscriber mutex before releasing mutex
    boost::mutex::scoped_lock subLock(s->mutex);
    // Release signal mutex
    sigLock.release()->unlock();
    // Ensure no call on subscriber occurrs once this function returns
    s->enabled = false;
    if (subscriberMap.empty() && onSubscribers)
      onSubscribers(false);
    if ( s->activeThreads.empty()
         || (s->activeThreads.size() == 1
             && *s->activeThreads.begin() == boost::this_thread::get_id()))
    { // One active callback in this thread, means above us in call stack
      // So we cannot trash s right now
      return true;
    }
    // More than one active callback, or one in a state that prevent us
    // from knowing in which thread it will run
    subLock.release()->unlock();
    s->waitForInactive();
    return true;
  }

  bool SignalBase::disconnect(const Link &link) {
    if (!_p)
      return false;
    else
      return _p->disconnect(link);
  }

  SignalBase::~SignalBase()
  {
    if (!_p)
      return;
    _p->onSubscribers = OnSubscribers();
    boost::shared_ptr<SignalBasePrivate> p(_p);
    _p.reset();
    SignalSubscriberMap::iterator i;
    std::vector<Link> links;
    for (i = p->subscriberMap.begin(); i!= p->subscriberMap.end(); ++i)
    {
      links.push_back(i->first);
    }
    for (unsigned i=0; i<links.size(); ++i)
      p->disconnect(links[i]);
  }

  std::vector<SignalSubscriber> SignalBase::subscribers()
  {
    std::vector<SignalSubscriber> res;
    if (!_p)
      return res;
    boost::recursive_mutex::scoped_lock sl(_p->mutex);
    SignalSubscriberMap::iterator i;
    for (i = _p->subscriberMap.begin(); i!= _p->subscriberMap.end(); ++i)
      res.push_back(*i->second);
    return res;
  }

  bool SignalBasePrivate::reset() {
    bool ret = true;
    boost::recursive_mutex::scoped_lock sl(mutex);
    SignalSubscriberMap::iterator it = subscriberMap.begin();
    while (it != subscriberMap.end()) {
      bool b = disconnect(it->first);
      if (!b)
        ret = false;
      it = subscriberMap.begin();
    }
    return ret;
  }

  QITYPE_API const SignalBase::Link SignalBase::invalidLink = ((unsigned int)-1);

}
<|endoftext|>


<commit_before>//
// LinkedView3D.cpp
//
// Copyright (C) 2013 by University of Stuttgart (VISUS).
// All rights reserved.
//

#include <stdafx.h>
#include "LinkedView3D.h"
#include "vislib/mathfunctions.h"
#include "CallCamParams.h"
#include "vislib/CameraParamsStore.h"

using namespace megamol;
using namespace megamol::core;

/*
 * view::LinkedView3D::LinkedView3D
 */
view::LinkedView3D::LinkedView3D(void) : core::view::View3D(),
        sharedCamParamsSlot("shareCamParams", "Obtain read and write access to shared camera parameters") {

    using namespace vislib::graphics;

    // Data caller slot to share camera parameters
    this->sharedCamParamsSlot.SetCompatibleCall<CallCamParamsDescription>();
    this->MakeSlotAvailable(&this->sharedCamParamsSlot);

    this->observableCamParams = new ObservableCameraParams();
    this->observableCamParams->CopyFrom(this->camParams);
    this->observableCamParams->AddCameraParameterObserver(&this->observer);
}

/*
 * view::LinkedView3D::~LinkedView3D
 */
view::LinkedView3D::~LinkedView3D(void) {
    this->Release();
}

/*
 * view::LinkedView3D::Render
 */
void view::LinkedView3D::Render(const mmcRenderViewContext& context) {

    this->observableCamParams->CopyChangedParamsFrom(this->camParams);

    // Get camera parameters call
    CallCamParams *cp = this->sharedCamParamsSlot.CallAs<CallCamParams>();
    if (cp == NULL) {
        return;
    }

    cp->SetCameraParameters(this->camParams); // Set pointer

    // Update shared camera parameters if necessary
    if (this->observer.HasCamChanged()) {
        // Obtain current shared camera parameters from cam params call
        if (!(*cp)(CallCamParams::CallForSetCamParams)) {
            return;
        }
    }

    // Obtain current shared camera parameters from cam params call
    if (!(*cp)(CallCamParams::CallForGetCamParams)) {
        return;
    }

    // Update observable parameters and reset flag
    this->observableCamParams->CopyFrom(this->camParams);
    this->observer.ResetCamChanged();

    core::view::View3D::Render(context); // Call parent
}

<commit_msg>LinkedView3D now uses non-release vislib method only if possible<commit_after>//
// LinkedView3D.cpp
//
// Copyright (C) 2013 by University of Stuttgart (VISUS).
// All rights reserved.
//

#include <stdafx.h>
#include "LinkedView3D.h"
#include "vislib/mathfunctions.h"
#include "CallCamParams.h"
#include "vislib/CameraParamsStore.h"
#include "vislib/vislibversion.h"

using namespace megamol;
using namespace megamol::core;

/*
 * view::LinkedView3D::LinkedView3D
 */
view::LinkedView3D::LinkedView3D(void) : core::view::View3D(),
        sharedCamParamsSlot("shareCamParams", "Obtain read and write access to shared camera parameters") {

    using namespace vislib::graphics;

    // Data caller slot to share camera parameters
    this->sharedCamParamsSlot.SetCompatibleCall<CallCamParamsDescription>();
    this->MakeSlotAvailable(&this->sharedCamParamsSlot);

    this->observableCamParams = new ObservableCameraParams();
    this->observableCamParams->CopyFrom(this->camParams);
    this->observableCamParams->AddCameraParameterObserver(&this->observer);
}

/*
 * view::LinkedView3D::~LinkedView3D
 */
view::LinkedView3D::~LinkedView3D(void) {
    this->Release();
}

/*
 * view::LinkedView3D::Render
 */
void view::LinkedView3D::Render(const mmcRenderViewContext& context) {

#if (VISLIB_VERSION_MAJOR > 1) || (VISLIB_VERSION_MINOR > 0)
    this->observableCamParams->CopyChangedParamsFrom(this->camParams);
#else
    this->observableCamParams->CopyFrom(this->camParams);
#endif

    // Get camera parameters call
    CallCamParams *cp = this->sharedCamParamsSlot.CallAs<CallCamParams>();
    if (cp == NULL) {
        return;
    }

    cp->SetCameraParameters(this->camParams); // Set pointer

    // Update shared camera parameters if necessary
    if (this->observer.HasCamChanged()) {
        // Obtain current shared camera parameters from cam params call
        if (!(*cp)(CallCamParams::CallForSetCamParams)) {
            return;
        }
    }

    // Obtain current shared camera parameters from cam params call
    if (!(*cp)(CallCamParams::CallForGetCamParams)) {
        return;
    }

    // Update observable parameters and reset flag
    this->observableCamParams->CopyFrom(this->camParams);
    this->observer.ResetCamChanged();

    core::view::View3D::Render(context); // Call parent
}

<|endoftext|>


<commit_before>#include "switch.h"
#include <functional>
using namespace std::placeholders;

Switch::Switch(const char* name, bool isSmart, int outputPin, int inputPin)
: _name(name),
  _inputPin(inputPin),
  _outputPin(outputPin),
  _isSmart(isSmart)
{
  Pressing i = Pressing(inputPin, HIGH, 400);
  _input = &i;
  auto didPressButton = std::bind(&Switch::didToggleViaHW, this);
  auto didToggleManualOverride = std::bind(&Switch::didToggleManualOverride, this);
  // auto handleClickAndHold = std::bind(&Switch::handleClickAndHold, this);
  _input->onClick(1, didPressButton);
  _input->onClick(5, didToggleManualOverride);
  // _input->onHold(1, handleClickAndHold);
  _input->setToggleMode(true);
  _input->onToggle(didPressButton);

  pinMode(outputPin, OUTPUT);

  HomieNode _node(name, "switch");
  auto didToggleViaMQTT = std::bind(&Switch::didToggleViaMQTT, this, _1, _2);
  _node.advertise("on").settable(didToggleViaMQTT);

  auto onHomieEvent = std::bind(&Switch::onHomieEvent, this, _1);
  Homie.onEvent(onHomieEvent);
}

void Switch::loop() {
  _input->loop();
}

// Handle any connect or disconnect events and set the current status.
void Switch::onHomieEvent(HomieEvent event) {
  switch(event.type) {
    case HomieEventType::MQTT_CONNECTED:
      Serial.println("Received connected event");
      this->didComeOnline();
      _isOnline = true;
      break;
    case HomieEventType::WIFI_DISCONNECTED:
    case HomieEventType::MQTT_DISCONNECTED:
      Serial.println("Received disconnected event");
      this->didGoOffline();
      _isOnline = false;
      break;
  }
}

// offlineMode returns true if we aren't online, or if we're in manual override.
bool Switch::isOfflineMode() {
  return !_isOnline || _isManualOverride;
}

void Switch::emitState() {
  Homie.setNodeProperty(*_node, "one").send(_currentState ? "true" : "false");
}

void Switch::setOutputToState(bool state) {
  digitalWrite(_outputPin, state ? HIGH : LOW);
}

void Switch::didComeOnline() {
  if (!_isSmart) {
    this->emitState();
    return;
  }

  if (!_hasChangedSinceOffline) {
    return;
  }

  if (_currentState == false) {
    this->setOutputToState(true);
    // Delay 1 second
    delayMicroseconds(1000000);
  }

  this->emitState();
}

void Switch::didGoOffline() {
  if (this->isOfflineMode()) {
    return;
  }
  _hasChangedSinceOffline = false;
}

bool Switch::didToggleViaMQTT(HomieRange range, String value) {
  bool newState = value == "true";
  if (_currentState == newState) {
    return true;
  }

  _currentState = !_currentState;
  if (!_isSmart) {
    this->setOutputToState(_currentState);
  }
  return true;
}

void Switch::didToggleViaHW() {
  Serial.println("Button state change initiated");
  _currentState = !_currentState;
  if (!this->isOfflineMode()) {
    this->emitState();

    if (!_isSmart) {
      this->setOutputToState(_currentState);
    }
    return;
  }

  if (!_isSmart) {
    this->setOutputToState(_currentState);
    return;
  }

  if (_currentState == false) {
    this->setOutputToState(false);
    return;
  }

  if (_relayState == true) {
    this->setOutputToState(false);
    // Delay 1 second
    delayMicroseconds(1000000);
  }

  this->setOutputToState(true);
}

// TODO: Add this to flow chart
void Switch::didToggleManualOverride() {
  Serial.println("Manual override toggled");
  _isManualOverride = !_isManualOverride;
  // Are we going offline?
  if (_isManualOverride && _isOnline) {
    this->didGoOffline();
    // Are we coming online?
  } else if (!_isManualOverride && _isOnline) {
    this->didComeOnline();
  }
}

void Switch::handleClickAndHold(int duration) {
  Serial.printf("Click and Hold %d\n", duration);
}
<commit_msg>s/one/on<commit_after>#include "switch.h"
#include <functional>
using namespace std::placeholders;

Switch::Switch(const char* name, bool isSmart, int outputPin, int inputPin)
: _name(name),
  _inputPin(inputPin),
  _outputPin(outputPin),
  _isSmart(isSmart)
{
  Pressing i = Pressing(inputPin, HIGH, 400);
  _input = &i;
  auto didPressButton = std::bind(&Switch::didToggleViaHW, this);
  auto didToggleManualOverride = std::bind(&Switch::didToggleManualOverride, this);
  // auto handleClickAndHold = std::bind(&Switch::handleClickAndHold, this);
  _input->onClick(1, didPressButton);
  _input->onClick(5, didToggleManualOverride);
  // _input->onHold(1, handleClickAndHold);
  _input->setToggleMode(true);
  _input->onToggle(didPressButton);

  pinMode(outputPin, OUTPUT);

  HomieNode _node(name, "switch");
  auto didToggleViaMQTT = std::bind(&Switch::didToggleViaMQTT, this, _1, _2);
  _node.advertise("on").settable(didToggleViaMQTT);

  auto onHomieEvent = std::bind(&Switch::onHomieEvent, this, _1);
  Homie.onEvent(onHomieEvent);
}

void Switch::loop() {
  _input->loop();
}

// Handle any connect or disconnect events and set the current status.
void Switch::onHomieEvent(HomieEvent event) {
  switch(event.type) {
    case HomieEventType::MQTT_CONNECTED:
      Serial.println("Received connected event");
      this->didComeOnline();
      _isOnline = true;
      break;
    case HomieEventType::WIFI_DISCONNECTED:
    case HomieEventType::MQTT_DISCONNECTED:
      Serial.println("Received disconnected event");
      this->didGoOffline();
      _isOnline = false;
      break;
  }
}

// offlineMode returns true if we aren't online, or if we're in manual override.
bool Switch::isOfflineMode() {
  return !_isOnline || _isManualOverride;
}

void Switch::emitState() {
  Homie.setNodeProperty(*_node, "on").send(_currentState ? "true" : "false");
}

void Switch::setOutputToState(bool state) {
  digitalWrite(_outputPin, state ? HIGH : LOW);
}

void Switch::didComeOnline() {
  if (!_isSmart) {
    this->emitState();
    return;
  }

  if (!_hasChangedSinceOffline) {
    return;
  }

  if (_currentState == false) {
    this->setOutputToState(true);
    // Delay 1 second
    delayMicroseconds(1000000);
  }

  this->emitState();
}

void Switch::didGoOffline() {
  if (this->isOfflineMode()) {
    return;
  }
  _hasChangedSinceOffline = false;
}

bool Switch::didToggleViaMQTT(HomieRange range, String value) {
  bool newState = value == "true";
  if (_currentState == newState) {
    return true;
  }

  _currentState = !_currentState;
  if (!_isSmart) {
    this->setOutputToState(_currentState);
  }
  return true;
}

void Switch::didToggleViaHW() {
  Serial.println("Button state change initiated");
  _currentState = !_currentState;
  if (!this->isOfflineMode()) {
    this->emitState();

    if (!_isSmart) {
      this->setOutputToState(_currentState);
    }
    return;
  }

  if (!_isSmart) {
    this->setOutputToState(_currentState);
    return;
  }

  if (_currentState == false) {
    this->setOutputToState(false);
    return;
  }

  if (_relayState == true) {
    this->setOutputToState(false);
    // Delay 1 second
    delayMicroseconds(1000000);
  }

  this->setOutputToState(true);
}

// TODO: Add this to flow chart
void Switch::didToggleManualOverride() {
  Serial.println("Manual override toggled");
  _isManualOverride = !_isManualOverride;
  // Are we going offline?
  if (_isManualOverride && _isOnline) {
    this->didGoOffline();
    // Are we coming online?
  } else if (!_isManualOverride && _isOnline) {
    this->didComeOnline();
  }
}

void Switch::handleClickAndHold(int duration) {
  Serial.printf("Click and Hold %d\n", duration);
}
<|endoftext|>


<commit_before>/**
 Implements of AST with LLVM
 @author : Dong-hee,Na
 @date   : 2014-10-12
**/
#include <llvm/Value.h>

class CBaseAST
{
      public:
        virtual ~CBaseAST(){}
        virtual llvm::Value* codeGen(CodeGenContext& context){}
};

class CExpression : public CBaseAST
{
};

class CStatement  : public CBaseAST
{
};

class CInteger    : public CBaseAST
{
      public:
        size_t value;
        CInteger(size_t val)
                 : value(val)
        {
        }
        virtual llvm::Value* codeGen(CodeGenContext& context);
};

class CDouble     : public CBaseAST
{
      public:
       double value;
       CDouble(double val)
              : value(val)
       {
       }
       virtual llvm::Value* codeGen(CodeGenContext& context);
};

class CFloat      : public CBaseAST
{
      public:
       float value;
       CFloat(val)
             :value(val)
       {
       }
       virtual llvm::Value* codeGen(CodeGenContext& context);
};
<commit_msg>last work 2014-10-12 5:39 PM<commit_after>/**
 Implements of AST with LLVM
 @author : Dong-hee,Na
 @date   : 2014-10-12
**/
#include <llvm/Value.h>
#include <stack>

class CBaseAST
{
      public:
        virtual ~CBaseAST(){}
        virtual llvm::Value* codeGen(CodeGenContext& context){}
};

class CExpression : public CBaseAST
{
};

class CStatement  : public CBaseAST
{
};

class CInteger    : public CBaseAST
{
      public:
        size_t value;
        CInteger(size_t val)
                 : value(val)
        {
        }
        virtual llvm::Value* codeGen(CodeGenContext& context);
};

class CDouble     : public CBaseAST
{
      public:
       double value;
       CDouble(double val)
              : value(val)
       {
       }
       virtual llvm::Value* codeGen(CodeGenContext& context);
};

class CFloat      : public CBaseAST
{
      public:
       float value;
       CFloat(val)
             :value(val)
       {
       }
       virtual llvm::Value* codeGen(CodeGenContext& context);
};

class CIdentifier : public CBaseAST
{
      public:
        std::string iden_name;
        CIdentifier(const std::string& name)
                   : iden_name(name)
        {
        }
        virtual llvm::Value* codeGen(CodeGenContext& context);
};
<|endoftext|>


<commit_before>/*
 * Copyright (C) 2011, Paul Tagliamonte <tag@pault.ag>
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to deal
 * in the Software without restriction, including without limitation the rights
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in
 * all copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
 * THE SOFTWARE.
 */

#include <ansiescape.hh>
#include <ansicsi.hh>
#include <iostream>

#include "ANSITerminal.hh"
#include "Terminal.hh"

ANSITerminal::ANSITerminal() {
	this->_init_ANSITerminal();
	this->_init_Terminal( 80, 25 );
}
ANSITerminal::ANSITerminal( int width, int height ) {
	this->_init_ANSITerminal();
	this->_init_Terminal( width, height );
}
ANSITerminal::~ANSITerminal() {}

void ANSITerminal::_init_ANSITerminal() {
	ansi_escape_parser_reset();
}

void ANSITerminal::_handle_private_escape( ansi_sequence * last ) {
	return;

	std::cerr << "Rcvd a private mode CSI: " << last->priv << std::endl;
	for ( unsigned int i = 0; i < last->values->size(); ++i )
		std::cerr << "  " << last->values->at(i) << std::endl;
	std::cerr << "  Mode: " << last->mode << std::endl;
}

void ANSITerminal::_handle_escape( ansi_sequence * last ) {
	char mode               = last->mode;
	std::vector<int> * seqs = last->values;

	if ( last->priv )
		std::cerr << last->priv << ", ";
	std::cerr << last->mode << ": ";
	for ( unsigned int i = 0; i < seqs->size(); ++i )
		std::cerr << seqs->at(i) << ", ";
	std::cerr << std::endl;

	int move_steps =  1;
	int nRow       = -1; /* Sorry about this hack, friend */
	int nCol       = -1;

	if ( last->priv ) {
		/* We have a private-mode ANSI CSI Sequence. */
		this->_handle_private_escape( last );
	}

	switch ( mode ) {
		case CSI_CUU:
		case CSI_CUD:
		case CSI_CUF:
		case CSI_CUB:
			/* Moves the cursor n (default 1) cells in the given direction. If
			 * the cursor is already at the edge of the screen, this has no
			 * effect. */
			switch ( seqs->at(0) ) {
				case -1:
					move_steps = 1;
				default:
					move_steps = ( seqs->at(0) > 0 ) ? seqs->at(0) : 1;
					for ( int i = 0; i < move_steps; ++i ) {
						switch ( mode ) {
							case CSI_CUU:
								this->cY--;
								break;
							case CSI_CUD:
								this->cY++;
								break;
							case CSI_CUF:
								this->cX++;
								break;
							case CSI_CUB:
								this->cX--;
								break;
						}
					}

					this->cX = ( this->cX < this->width  )
						? this->cX : this->width;
					this->cY = ( this->cY < this->height )
						? this->cY : this->height;

					break;
			}
			break;
		case CSI_CHA:
			/* Moves the cursor to column n.
			std::cerr << "Iput: " << seqs->at(0) << std::endl;
			std::cerr << this->cX << " < pre" << std::endl; */
			this->cX = ( seqs->at(0) < 1 ) ? 0 : seqs->at(0) - 1;
			// std::cerr << this->cX << " < post" << std::endl;
			break;
		case 'd':
			/* moves the cursor to row n. */
			this->cY = ( seqs->at(0) < 1 ) ? seqs->at(0) - 1 : 0;
			break;
		case CSI_CUP:
			/* Moves the cursor to row n, column m. The values are 1-based, and
			 * default to 1 (top left corner) if omitted. A sequence such as CSI
			 * ;5H is a synonym for CSI 1;5H as well as CSI 17;H is the same as
			 * CSI 17H and CSI 17;1H */
			nRow = seqs->at(0);

			if ( nRow < 0 )
				nRow = 1;

			if ( seqs->size() >= 2 )
				nCol = seqs->at(1);
			else
				nCol = 1;

			this->cX = (nCol - 1);
			this->cY = (nRow - 1);

			break;
		case CSI_EL:
			/* Erases part of the line. If n is zero (or missing), clear from
			 * cursor to the end of the line. If n is one, clear from cursor to
			 * beginning of the line. If n is two, clear entire line. Cursor
			 * position does not change. */
			switch ( seqs->at(0) ) {
				case -1:
				case 0:
					/*                           vvvv  XXX: Verify. */
					this->erase_to_from( this->cX + 1, this->cY,
						this->width - 1, this->cY );
					break;
				case 1:
					this->erase_to_from( 0, this->cY, this->cX, this->cY );
					break;
				case 2:
					this->erase_to_from( 0, this->cY,
						this->width - 1, this->cY );
					break;
			}
			break;
		case CSI_ED:
			/* Clears part of the screen. If n is zero (or missing),
			 * clear from cursor to end of screen. If n is one,
			 * clear from cursor to beginning of the screen. If n is two, clear
			 * entire screen (and moves cursor to upper left on MS-DOS
			 * ANSI.SYS).*/
			switch ( seqs->at(0) ) {
				case -1:
				case  0:
					this->erase_to_from( this->cX, this->cY,
						this->width - 1, this->height - 1 );
					break;
				case 1:
					this->erase_to_from( 0, 0, this->cX, this->cY );
					break;
				case 2:
					this->erase_to_from( 0, 0,
						this->width - 1, this->height - 1 );
					break;
			}
			break;
		default:
			break;
	}

}

void ANSITerminal::insert( unsigned char c ) {
	ANSI_ESCAPE_PARSE_T res = ansi_escape_parser_feed( c );
	ansi_sequence * last = NULL;
	switch ( res ) {
		case ANSI_ESCAPE_PARSE_OK:
			last = ansi_escape_get_last_sequence();
			this->_handle_escape( last );
			break;
		case ANSI_ESCAPE_PARSE_BAD:
			ansi_escape_parser_reset();
			Terminal::insert(c);
			break;
		case ANSI_ESCAPE_PARSE_INCOMPLETE:
			break;
	}
}
<commit_msg>removing crap<commit_after>/*
 * Copyright (C) 2011, Paul Tagliamonte <tag@pault.ag>
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to deal
 * in the Software without restriction, including without limitation the rights
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in
 * all copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
 * THE SOFTWARE.
 */

#include <ansiescape.hh>
#include <ansicsi.hh>
#include <iostream>

#include "ANSITerminal.hh"
#include "Terminal.hh"

ANSITerminal::ANSITerminal() {
	this->_init_ANSITerminal();
	this->_init_Terminal( 80, 25 );
}
ANSITerminal::ANSITerminal( int width, int height ) {
	this->_init_ANSITerminal();
	this->_init_Terminal( width, height );
}
ANSITerminal::~ANSITerminal() {}

void ANSITerminal::_init_ANSITerminal() {
	ansi_escape_parser_reset();
}

void ANSITerminal::_handle_private_escape( ansi_sequence * last ) {
	return;

	std::cerr << "Rcvd a private mode CSI: " << last->priv << std::endl;
	for ( unsigned int i = 0; i < last->values->size(); ++i )
		std::cerr << "  " << last->values->at(i) << std::endl;
	std::cerr << "  Mode: " << last->mode << std::endl;
}

void ANSITerminal::_handle_escape( ansi_sequence * last ) {
	char mode               = last->mode;
	std::vector<int> * seqs = last->values;

	if ( last->priv )
		std::cerr << last->priv << ", ";
	std::cerr << last->mode << ": ";
	for ( unsigned int i = 0; i < seqs->size(); ++i )
		std::cerr << seqs->at(i) << ", ";
	std::cerr << std::endl;

	int move_steps =  1;
	int nRow       = -1; /* Sorry about this hack, friend */
	int nCol       = -1;

	if ( last->priv ) {
		/* We have a private-mode ANSI CSI Sequence. */
		this->_handle_private_escape( last );
	}

	switch ( mode ) {
		case CSI_CUU:
		case CSI_CUD:
		case CSI_CUF:
		case CSI_CUB:
			/* Moves the cursor n (default 1) cells in the given direction. If
			 * the cursor is already at the edge of the screen, this has no
			 * effect. */
			move_steps = ( seqs->at(0) > 0 ) ? seqs->at(0) : 1;
			for ( int i = 0; i < move_steps; ++i ) {
				switch ( mode ) {
					case CSI_CUU:
						this->cY--;
						break;
					case CSI_CUD:
						this->cY++;
						break;
					case CSI_CUF:
						this->cX++;
						break;
					case CSI_CUB:
						this->cX--;
						break;
				}
			}
			this->cX = ( this->cX < this->width  )
				? this->cX : this->width;
			this->cY = ( this->cY < this->height )
				? this->cY : this->height;
			break;
		case CSI_CHA:
			/* Moves the cursor to column n.
			std::cerr << "Iput: " << seqs->at(0) << std::endl;
			std::cerr << this->cX << " < pre" << std::endl; */
			this->cX = ( seqs->at(0) < 1 ) ? 0 : seqs->at(0) - 1;
			// std::cerr << this->cX << " < post" << std::endl;
			break;
		case 'd':
			/* moves the cursor to row n. */
			this->cY = ( seqs->at(0) < 1 ) ? seqs->at(0) - 1 : 0;
			break;
		case CSI_CUP:
			/* Moves the cursor to row n, column m. The values are 1-based, and
			 * default to 1 (top left corner) if omitted. A sequence such as CSI
			 * ;5H is a synonym for CSI 1;5H as well as CSI 17;H is the same as
			 * CSI 17H and CSI 17;1H */
			nRow = seqs->at(0);

			if ( nRow < 0 )
				nRow = 1;

			if ( seqs->size() >= 2 )
				nCol = seqs->at(1);
			else
				nCol = 1;

			this->cX = (nCol - 1);
			this->cY = (nRow - 1);

			break;
		case CSI_EL:
			/* Erases part of the line. If n is zero (or missing), clear from
			 * cursor to the end of the line. If n is one, clear from cursor to
			 * beginning of the line. If n is two, clear entire line. Cursor
			 * position does not change. */
			switch ( seqs->at(0) ) {
				case -1:
				case 0:
					/*                           vvvv  XXX: Verify. */
					this->erase_to_from( this->cX + 1, this->cY,
						this->width - 1, this->cY );
					break;
				case 1:
					this->erase_to_from( 0, this->cY, this->cX, this->cY );
					break;
				case 2:
					this->erase_to_from( 0, this->cY,
						this->width - 1, this->cY );
					break;
			}
			break;
		case CSI_ED:
			/* Clears part of the screen. If n is zero (or missing),
			 * clear from cursor to end of screen. If n is one,
			 * clear from cursor to beginning of the screen. If n is two, clear
			 * entire screen (and moves cursor to upper left on MS-DOS
			 * ANSI.SYS).*/
			switch ( seqs->at(0) ) {
				case -1:
				case  0:
					this->erase_to_from( this->cX, this->cY,
						this->width - 1, this->height - 1 );
					break;
				case 1:
					this->erase_to_from( 0, 0, this->cX, this->cY );
					break;
				case 2:
					this->erase_to_from( 0, 0,
						this->width - 1, this->height - 1 );
					break;
			}
			break;
		default:
			break;
	}

}

void ANSITerminal::insert( unsigned char c ) {
	ANSI_ESCAPE_PARSE_T res = ansi_escape_parser_feed( c );
	ansi_sequence * last = NULL;
	switch ( res ) {
		case ANSI_ESCAPE_PARSE_OK:
			last = ansi_escape_get_last_sequence();
			this->_handle_escape( last );
			break;
		case ANSI_ESCAPE_PARSE_BAD:
			ansi_escape_parser_reset();
			Terminal::insert(c);
			break;
		case ANSI_ESCAPE_PARSE_INCOMPLETE:
			break;
	}
}
<|endoftext|>


<commit_before>/*
 * Copyright (C) 2011, Paul Tagliamonte <tag@pault.ag>
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to deal
 * in the Software without restriction, including without limitation the rights
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in
 * all copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
 * THE SOFTWARE.
 */

#include <ansiescape.hh>
#include <ansicsi.hh>
#include <iostream>

#include "ANSITerminal.hh"
#include "Shibuya.hh"
#include "Terminal.hh"

ANSITerminal::ANSITerminal() {
	this->_init_ANSITerminal();
	this->_init_Terminal( 80, 25 );
}
ANSITerminal::ANSITerminal( int width, int height ) {
	this->_init_ANSITerminal();
	this->_init_Terminal( width, height );
}
ANSITerminal::~ANSITerminal() {}

void ANSITerminal::_init_ANSITerminal() {
	ansi_escape_parser_reset();
}

void ANSITerminal::_handle_private_escape( ansi_sequence * last ) {
	return;

	// std::cerr << "Rcvd a private mode CSI: " << last->priv << std::endl;
	for ( unsigned int i = 0; i < last->values->size(); ++i )
		std::cerr << "  " << last->values->at(i) << std::endl;
	// std::cerr << "  Mode: " << last->mode << std::endl;
}

void ANSITerminal::_handle_escape( ansi_sequence * last ) {
	char mode               = last->mode;
	std::vector<int> * seqs = last->values;

	if ( last->priv )
		SDEBUG << last->priv << ", ";
	SDEBUG << last->mode << ": ";
	for ( unsigned int i = 0; i < seqs->size(); ++i )
		SDEBUG << seqs->at(i) << ", ";
	SDEBUG << std::endl;

	int move_steps =  1;
	int nRow       = -1;
	int nCol       = -1; /* Sorry about this hack, friend */
	int nTop       = -1;
	int nBottom    = -1;

	if ( last->priv ) {
		/* We have a private-mode ANSI CSI Sequence. */
		this->_handle_private_escape( last );
	}

	switch ( mode ) {
		case CSI_CUU:
		case CSI_CUD:
		case CSI_CUF:
		case CSI_CUB:
			/* Moves the cursor n (default 1) cells in the given direction. If
			 * the cursor is already at the edge of the screen, this has no
			 * effect. */
			move_steps = ( seqs->at(0) > 0 ) ? seqs->at(0) : 1;
			for ( int i = 0; i < move_steps; ++i ) {
				switch ( mode ) {
					case CSI_CUU:
						this->cY--;
						break;
					case CSI_CUD:
						this->cY++;
						break;
					case CSI_CUF:
						this->cX++;
						break;
					case CSI_CUB:
						this->cX--;
						break;
				}
			}
			this->cX = ( this->cX < this->width  )
				? this->cX : this->width;
			this->cY = ( this->cY < this->height )
				? this->cY : this->height;
			break;
		case CSI_CHA:
			/* Moves the cursor to column n.
			std::cerr << "Iput: " << seqs->at(0) << std::endl;
			std::cerr << this->cX << " < pre" << std::endl; */
			this->cX = ( seqs->at(0) < 1 ) ? 0 : seqs->at(0) - 1;
			// std::cerr << this->cX << " < post" << std::endl;
			break;
		case 'd': // XXX: Fixme
			/* moves the cursor to row n. */
			this->cY = ( seqs->at(0) < 1 ) ? 0 : seqs->at(0) - 1;
			break;
		case 'M': // XXX: Fixme (CSI_DL)
			/* DL | Delete the indicated # of lines. */
			move_steps = ( seqs->at(0) > 0 ) ? seqs->at(0) : 0;
			for ( int i = 0; i < move_steps; ++i )
				this->delete_line( this->cY );
			break;
		case 'L':
			move_steps = ( seqs->at(0) > 0 ) ? seqs->at(0) : 0;
			for ( int i = 0; i < move_steps; ++i )
				this->insert_line( this->cY );
			break;
		case 'r':
			nTop = seqs->at(0);

			if ( nTop < 0 )
				nTop = 1;

			if ( seqs->size() >= 2 )
				nBottom = ( seqs->at(1) > 0 ) ? seqs->at(1) : this->height;
			else
				nBottom = this->height;

			nBottom = ( nBottom < 1 ) ? 0 : nBottom - 1;
			nTop    = ( nTop > this->height ) ? this->height - 1 :
				nTop - 1;

			this->scroll_frame_bottom = nBottom;
			this->scroll_frame_top    = nTop;

			/* std::cerr << "XY: " << this->scroll_frame_top << ", " <<
				this->scroll_frame_bottom << std::endl; */

			this->cX = 0;
			this->cY = nTop;

			break;
		case CSI_CUP:
			/* Moves the cursor to row n, column m. The values are 1-based, and
			 * default to 1 (top left corner) if omitted. A sequence such as CSI
			 * ;5H is a synonym for CSI 1;5H as well as CSI 17;H is the same as
			 * CSI 17H and CSI 17;1H */
			nRow = seqs->at(0);

			if ( nRow < 0 )
				nRow = 1;

			if ( seqs->size() >= 2 )
				nCol = seqs->at(1);
			else
				nCol = 1;

			this->cX = (nCol - 1); /* XXX: This worries me... */
			this->cY = (nRow - 1);

			break;
		case CSI_EL:
			/* Erases part of the line. If n is zero (or missing), clear from
			 * cursor to the end of the line. If n is one, clear from cursor to
			 * beginning of the line. If n is two, clear entire line. Cursor
			 * position does not change. */
			switch ( seqs->at(0) ) {
				case -1:
				case 0:
					this->erase_to_from( this->cX, this->cY,
						this->width - 1, this->cY );
					break;
				case 1:
					this->erase_to_from( 0, this->cY, this->cX, this->cY );
					break;
				case 2:
					this->erase_to_from( 0, this->cY,
						this->width - 1, this->cY );
					break;
			}
			break;
		case CSI_ED:
			/* Clears part of the screen. If n is zero (or missing),
			 * clear from cursor to end of screen. If n is one,
			 * clear from cursor to beginning of the screen. If n is two, clear
			 * entire screen (and moves cursor to upper left on MS-DOS
			 * ANSI.SYS).*/
			switch ( seqs->at(0) ) {
				case -1:
				case  0:
					this->erase_to_from( this->cX, this->cY,
						this->width - 1, this->height - 1 );
					break;
				case 1:
					this->erase_to_from( 0, 0, this->cX, this->cY );
					break;
				case 2:
					this->erase_to_from( 0, 0,
						this->width - 1, this->height - 1 );
					break;
			}
			break;
		default:
			SDEBUG << "(Unhandled)" << std::endl;
			break;
	}

}

void ANSITerminal::insert( unsigned char c ) {
	ANSI_ESCAPE_PARSE_T res = ansi_escape_parser_feed( c );
	ansi_sequence * last = NULL;
	switch ( res ) {
		case ANSI_ESCAPE_PARSE_OK:
			last = ansi_escape_get_last_sequence();
			this->_handle_escape( last );
			break;
		case ANSI_ESCAPE_PARSE_BAD:
			ansi_escape_parser_reset();
			Terminal::insert(c);
			break;
		case ANSI_ESCAPE_PARSE_INCOMPLETE:
			break;
	}
}
<commit_msg>interesting little off-by-one.<commit_after>/*
 * Copyright (C) 2011, Paul Tagliamonte <tag@pault.ag>
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to deal
 * in the Software without restriction, including without limitation the rights
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in
 * all copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
 * THE SOFTWARE.
 */

#include <ansiescape.hh>
#include <ansicsi.hh>
#include <iostream>

#include "ANSITerminal.hh"
#include "Shibuya.hh"
#include "Terminal.hh"

ANSITerminal::ANSITerminal() {
	this->_init_ANSITerminal();
	this->_init_Terminal( 80, 25 );
}
ANSITerminal::ANSITerminal( int width, int height ) {
	this->_init_ANSITerminal();
	this->_init_Terminal( width, height );
}
ANSITerminal::~ANSITerminal() {}

void ANSITerminal::_init_ANSITerminal() {
	ansi_escape_parser_reset();
}

void ANSITerminal::_handle_private_escape( ansi_sequence * last ) {
	return;

	// std::cerr << "Rcvd a private mode CSI: " << last->priv << std::endl;
	for ( unsigned int i = 0; i < last->values->size(); ++i )
		std::cerr << "  " << last->values->at(i) << std::endl;
	// std::cerr << "  Mode: " << last->mode << std::endl;
}

void ANSITerminal::_handle_escape( ansi_sequence * last ) {
	char mode               = last->mode;
	std::vector<int> * seqs = last->values;

	if ( last->priv )
		SDEBUG << last->priv << ", ";
	SDEBUG << last->mode << ": ";
	for ( unsigned int i = 0; i < seqs->size(); ++i )
		SDEBUG << seqs->at(i) << ", ";
	SDEBUG << std::endl;

	int move_steps =  1;
	int nRow       = -1;
	int nCol       = -1; /* Sorry about this hack, friend */
	int nTop       = -1;
	int nBottom    = -1;

	if ( last->priv ) {
		/* We have a private-mode ANSI CSI Sequence. */
		this->_handle_private_escape( last );
	}

	switch ( mode ) {
		case CSI_CUU:
		case CSI_CUD:
		case CSI_CUF:
		case CSI_CUB:
			/* Moves the cursor n (default 1) cells in the given direction. If
			 * the cursor is already at the edge of the screen, this has no
			 * effect. */
			move_steps = ( seqs->at(0) > 0 ) ? seqs->at(0) : 1;
			for ( int i = 0; i < move_steps; ++i ) {
				switch ( mode ) {
					case CSI_CUU:
						this->cY--;
						break;
					case CSI_CUD:
						this->cY++;
						break;
					case CSI_CUF:
						this->cX++;
						break;
					case CSI_CUB:
						this->cX--;
						break;
				}
			}
			this->cX = ( this->cX < this->width  )
				? this->cX : this->width;
			this->cY = ( this->cY < this->height )
				? this->cY : this->height;
			break;
		case CSI_CHA:
			/* Moves the cursor to column n.
			std::cerr << "Iput: " << seqs->at(0) << std::endl;
			std::cerr << this->cX << " < pre" << std::endl; */
			this->cX = ( seqs->at(0) < 1 ) ? 0 : seqs->at(0) - 1;
			// std::cerr << this->cX << " < post" << std::endl;
			break;
		case 'd': // XXX: Fixme
			/* moves the cursor to row n. */
			this->cY = ( seqs->at(0) < 1 ) ? 0 : seqs->at(0) - 1;
			break;
		case 'M': // XXX: Fixme (CSI_DL)
			/* DL | Delete the indicated # of lines. */
			move_steps = ( seqs->at(0) > 0 ) ? seqs->at(0) : 0;
			for ( int i = 0; i < move_steps; ++i )
				this->delete_line( this->cY );
			break;
		case 'L':
			move_steps = ( seqs->at(0) > 0 ) ? seqs->at(0) : 0;
			for ( int i = 0; i < move_steps; ++i )
				this->insert_line( this->cY );
			break;
		case 'r':
			nTop = seqs->at(0);

			if ( nTop < 0 )
				nTop = 1;

			if ( seqs->size() >= 2 )
				nBottom = ( seqs->at(1) > 0 ) ? seqs->at(1) : this->height;
			else
				nBottom = this->height;

			nBottom = ( nBottom < 1 ) ? 0 : nBottom - 1;
			nTop    = ( nTop > this->height ) ? this->height - 1 :
				nTop - 1;

			this->scroll_frame_bottom = nBottom;
			this->scroll_frame_top    = nTop;

			/* std::cerr << "XY: " << this->scroll_frame_top << ", " <<
				this->scroll_frame_bottom << std::endl; */

			this->cX = 0;
			this->cY = nTop;

			break;
		case CSI_CUP:
			/* Moves the cursor to row n, column m. The values are 1-based, and
			 * default to 1 (top left corner) if omitted. A sequence such as CSI
			 * ;5H is a synonym for CSI 1;5H as well as CSI 17;H is the same as
			 * CSI 17H and CSI 17;1H */
			nRow = seqs->at(0);

			if ( nRow < 0 )
				nRow = 1;

			if ( seqs->size() >= 2 )
				nCol = seqs->at(1);
			else
				nCol = 1;

			this->cX = (nCol - 1); /* XXX: This worries me... */
			this->cY = (nRow - 1);

			break;
		case CSI_EL:
			/* Erases part of the line. If n is zero (or missing), clear from
			 * cursor to the end of the line. If n is one, clear from cursor to
			 * beginning of the line. If n is two, clear entire line. Cursor
			 * position does not change. */
			switch ( seqs->at(0) ) {
				case -1:
				case 0:
					this->erase_to_from( this->cX, this->cY,
						this->width - 1, this->cY );
					break;
				case 1:
					this->erase_to_from( 0, this->cY, this->cX, this->cY );
					break;
				case 2:
					this->erase_to_from( 0, this->cY,
						this->width - 1, this->cY );
					break;
			}
			break;
		case CSI_ED:
			/* Clears part of the screen. If n is zero (or missing),
			 * clear from cursor to end of screen. If n is one,
			 * clear from cursor to beginning of the screen. If n is two, clear
			 * entire screen (and moves cursor to upper left on MS-DOS
			 * ANSI.SYS).*/
			switch ( seqs->at(0) ) {
				case -1:
				case  0:
					this->erase_to_from( this->cX, this->cY,
						this->width - 1, this->height - 1 );
					break;
				case 1:
					this->erase_to_from( 0, 0, this->cX + 1, this->cY );
					break;
				case 2:
					this->erase_to_from( 0, 0,
						this->width - 1, this->height - 1 );
					break;
			}
			break;
		default:
			SDEBUG << "(Unhandled)" << std::endl;
			break;
	}

}

void ANSITerminal::insert( unsigned char c ) {
	ANSI_ESCAPE_PARSE_T res = ansi_escape_parser_feed( c );
	ansi_sequence * last = NULL;
	switch ( res ) {
		case ANSI_ESCAPE_PARSE_OK:
			last = ansi_escape_get_last_sequence();
			this->_handle_escape( last );
			break;
		case ANSI_ESCAPE_PARSE_BAD:
			ansi_escape_parser_reset();
			Terminal::insert(c);
			break;
		case ANSI_ESCAPE_PARSE_INCOMPLETE:
			break;
	}
}
<|endoftext|>