<commit_before>#pragma once
#include "stdafx.h"
#include "cJSON/cJSON.h"

CLANG_DIAGNOSTIC_PUSH;
CLANG_DIAGNOSTIC_IGNORE("-Wunused-macros");

// I dont think namespace matters for macros but whatever
namespace asdf
{
    #define CJSON_OBJ(node_name, obj_name) cJSON_GetObjectItem(node_name, #obj_name)
    #define CJSON_INT(node_name, int_name) CJSON_OBJ(node_name, int_name)->valueint
    #define CJSON_FLOAT(node_name, float_name) static_cast<float>(CJSON_OBJ(node_name, float_name)->valuedouble)
    #define CJSON_DOUBLE(node_name, double_name) CJSON_OBJ(node_name, double_name)->valuedouble
    #define CJSON_STR(node_name, str_name) CJSON_OBJ(node_name, str_name)->valuestring
    #define CJSON_ENUM(node_name, int_name, enum_type) static_cast<enum_type>(CJSON_INT(node_name, int_name))

    #define CJSON_VALUE_ARRAY(container_node, container, valuetype, resize_container)     \
        {                                                                     \
            size_t len = cJSON_GetArraySize(container_node);                  \
                                                                              \
            if(resize_container)                                              \
            {                                                                 \
                container.clear();                                            \
                container.resize(len);                                        \
            }                                                                 \
            else                                                              \
            {                                                                 \
                ASSERT(container.size() == len, "");                          \
            }                                                                 \
                                                                              \
            for(size_t i = 0; i < len; ++i)                                   \
            {                                                                 \
                cJSON* json = cJSON_GetArrayItem(container_node, i);          \
                container[i] = json->valuetype;                               \
            }                                                                 \
        }
    //---

    #define CJSON_GET_INT(int_name)    int_name = CJSON_INT(root, int_name)
    #define CJSON_GET_FLOAT(float_name) float_name = CJSON_FLOAT(root, float_name)
    #define CJSON_GET_DOUBLE(double_name) int_name = CJSON_DOUBLE(root, double_name)
    #define CJSON_GET_STR(str_name)    str_name = CJSON_STR(root, str_name)
    #define CJSON_GET_ITEM(obj_name) obj_name.from_JSON(cJSON_GetObjectItem(root, #obj_name));

    #define CJSON_GET_ENUM_INT(int_name, enum_type) int_name = CJSON_ENUM(int_name, enum_type);

    #define CJSON_GET_VALUE_ARRAY(container, valuetype, resize_container)     \
        {                                                                     \
            cJSON* container_json = cJSON_GetObjectItem(root, #container);  \
            CJSON_VALUE_ARRAY(container_json, container, valuetype, resize_container) \
        }
    //---

    #define CJSON_GET_INT_ARRAY(container)    CJSON_GET_VALUE_ARRAY(container, valueint, false);
    #define CJSON_GET_DOUBLE_ARRAY(container) CJSON_GET_VALUE_ARRAY(container, valuedouble, false);
    #define CJSON_GET_STR_ARRAY(container)    CJSON_GET_VALUE_ARRAY(container, valuestring, false);

    #define CJSON_GET_INT_VECTOR(container)    CJSON_GET_VALUE_ARRAY(container, valueint, true);
    #define CJSON_GET_DOUBLE_VECTOR(container) CJSON_GET_VALUE_ARRAY(container, valuedouble, true);
    #define CJSON_GET_STR_VECTOR(container)    CJSON_GET_VALUE_ARRAY(container, valuestring, true);

    #define CJSON_GET_ITEM_ARRAY(container) \
        {                                                                        \
            cJSON* container##_json = cJSON_GetObjectItem(root, #container);     \
            size_t len = cJSON_GetArraySize(container##_json);                   \
            for(size_t i = 0; i < len; ++i)                                      \
            {                                                                    \
                container[i].from_JSON(cJSON_GetArrayItem(container##_json, i)); \
            }                                                                    \
        }
    //---

    #define CJSON_GET_ITEM_VECTOR(container) \
        { \
            cJSON* container##_json = cJSON_GetObjectItem(root, #container);     \
            size_t len = cJSON_GetArraySize(container##_json);                   \
                                                                                \
            container.clear();                                                   \
            container.resize(len);                                               \
                                                                                \
            for(size_t i = 0; i < len; ++i)                                      \
            {                                                                    \
                container[i].from_JSON(cJSON_GetArrayItem(container##_json, i)); \
            }                                                                    \
        }
    //---

    /*
    */
    #define CJSON_CREATE_ROOT() cJSON* root = cJSON_CreateObject()
    #define CJSON_ADD_BLANK_ITEM(root, child) \
        cJSON* child##_json = cJSON_CreateObject(); \
        cJSON_AddItemToObject(root, #child, child##_json);
    //---
    #define CJSON_ADD_ITEM(obj)  cJSON_AddItemToObject(root, #obj, obj.to_JSON());

    #define CJSON_ADD_NUMBER(num_name)  cJSON_AddNumberToObject(root, #num_name, num_name);
    #define CJSON_ADD_INT(num_name)     CJSON_ADD_NUMBER(num_name)
    #define CJSON_ADD_STR(str_name)     cJSON_AddStringToObject(root, #str_name, (str_name##.c_str() == nullptr) ? "" : str_name##.c_str());
    #define CJSON_ADD_CSTR(str_name)    cJSON_AddStringToObject(root, #str_name, str_name);

    #define CJSON_ITEM_ARRAY(container_name)                              \
        cJSON* container_name##_json = cJSON_CreateArray();               \
        for(auto const& thing : container_name)                           \
        { cJSON_AddItemToArray(container_name##_json, thing.to_JSON()); }
    //---

    #define CJSON_ADD_INT_ARRAY(container)   cJSON_AddItemToObject(root, #container, cJSON_CreateIntArray(container.data(), container.size()));

    #define CJSON_ADD_ITEM_ARRAY(container_name)      \
        CJSON_ITEM_ARRAY(container_name);                       \
        cJSON_AddItemToObject(root, #container_name, container_name##_json);
    //---
}

CLANG_DIAGNOSTIC_POP<commit_msg>removed the asdf namespace from cjson_utils added a constexpr cstring array of json type strings for debugging and error messages<commit_after>#pragma once
#include "stdafx.h"
#include "cJSON/cJSON.h"

#include <array>

CLANG_DIAGNOSTIC_PUSH;
CLANG_DIAGNOSTIC_IGNORE("-Wunused-macros");

constexpr std::array<char const*, cJSON_Object+1> cJSON_type_strings =
{
    "cJSON_False"
  , "cJSON_True"
  , "cJSON_NULL"
  , "cJSON_Number"
  , "cJSON_String"
  , "cJSON_Array"
  , "cJSON_Object"
};

#define CJSON_OBJ(node_name, obj_name) cJSON_GetObjectItem(node_name, #obj_name)
#define CJSON_INT(node_name, int_name) CJSON_OBJ(node_name, int_name)->valueint
#define CJSON_FLOAT(node_name, float_name) static_cast<float>(CJSON_OBJ(node_name, float_name)->valuedouble)
#define CJSON_DOUBLE(node_name, double_name) CJSON_OBJ(node_name, double_name)->valuedouble
#define CJSON_STR(node_name, str_name) CJSON_OBJ(node_name, str_name)->valuestring
#define CJSON_ENUM(node_name, int_name, enum_type) static_cast<enum_type>(CJSON_INT(node_name, int_name))

#define CJSON_VALUE_ARRAY(container_node, container, valuetype, resize_container)     \
    {                                                                     \
        size_t len = cJSON_GetArraySize(container_node);                  \
                                                                          \
        if(resize_container)                                              \
        {                                                                 \
            container.clear();                                            \
            container.resize(len);                                        \
        }                                                                 \
        else                                                              \
        {                                                                 \
            ASSERT(container.size() == len, "");                          \
        }                                                                 \
                                                                          \
        for(size_t i = 0; i < len; ++i)                                   \
        {                                                                 \
            cJSON* json = cJSON_GetArrayItem(container_node, i);          \
            container[i] = json->valuetype;                               \
        }                                                                 \
    }
//---

#define CJSON_GET_INT(int_name)    int_name = CJSON_INT(root, int_name)
#define CJSON_GET_FLOAT(float_name) float_name = CJSON_FLOAT(root, float_name)
#define CJSON_GET_DOUBLE(double_name) int_name = CJSON_DOUBLE(root, double_name)
#define CJSON_GET_STR(str_name)    str_name = CJSON_STR(root, str_name)
#define CJSON_GET_ITEM(obj_name) obj_name.from_JSON(cJSON_GetObjectItem(root, #obj_name));

#define CJSON_GET_ENUM_INT(int_name, enum_type) int_name = CJSON_ENUM(int_name, enum_type);

#define CJSON_GET_VALUE_ARRAY(container, valuetype, resize_container)     \
    {                                                                     \
        cJSON* container_json = cJSON_GetObjectItem(root, #container);  \
        CJSON_VALUE_ARRAY(container_json, container, valuetype, resize_container) \
    }
//---

#define CJSON_GET_INT_ARRAY(container)    CJSON_GET_VALUE_ARRAY(container, valueint, false);
#define CJSON_GET_DOUBLE_ARRAY(container) CJSON_GET_VALUE_ARRAY(container, valuedouble, false);
#define CJSON_GET_STR_ARRAY(container)    CJSON_GET_VALUE_ARRAY(container, valuestring, false);

#define CJSON_GET_INT_VECTOR(container)    CJSON_GET_VALUE_ARRAY(container, valueint, true);
#define CJSON_GET_DOUBLE_VECTOR(container) CJSON_GET_VALUE_ARRAY(container, valuedouble, true);
#define CJSON_GET_STR_VECTOR(container)    CJSON_GET_VALUE_ARRAY(container, valuestring, true);

#define CJSON_GET_ITEM_ARRAY(container) \
    {                                                                        \
        cJSON* container##_json = cJSON_GetObjectItem(root, #container);     \
        size_t len = cJSON_GetArraySize(container##_json);                   \
        for(size_t i = 0; i < len; ++i)                                      \
        {                                                                    \
            container[i].from_JSON(cJSON_GetArrayItem(container##_json, i)); \
        }                                                                    \
    }
//---

#define CJSON_GET_ITEM_VECTOR(container) \
    { \
        cJSON* container##_json = cJSON_GetObjectItem(root, #container);     \
        size_t len = cJSON_GetArraySize(container##_json);                   \
                                                                             \
        container.clear();                                                   \
        container.resize(len);                                               \
                                                                             \
        for(size_t i = 0; i < len; ++i)                                      \
        {                                                                    \
            container[i].from_JSON(cJSON_GetArrayItem(container##_json, i)); \
        }                                                                    \
    }
//---

/*
*/
#define CJSON_CREATE_ROOT() cJSON* root = cJSON_CreateObject()
#define CJSON_ADD_BLANK_ITEM(root, child) \
    cJSON* child##_json = cJSON_CreateObject(); \
    cJSON_AddItemToObject(root, #child, child##_json);
//---
#define CJSON_ADD_ITEM(obj)  cJSON_AddItemToObject(root, #obj, obj.to_JSON());

#define CJSON_ADD_NUMBER(num_name)  cJSON_AddNumberToObject(root, #num_name, num_name);
#define CJSON_ADD_INT(num_name)     CJSON_ADD_NUMBER(num_name)
#define CJSON_ADD_STR(str_name)     cJSON_AddStringToObject(root, #str_name, (str_name##.c_str() == nullptr) ? "" : str_name##.c_str());
#define CJSON_ADD_CSTR(str_name)    cJSON_AddStringToObject(root, #str_name, str_name);

#define CJSON_ITEM_ARRAY(container_name)                              \
    cJSON* container_name##_json = cJSON_CreateArray();               \
    for(auto const& thing : container_name)                           \
    { cJSON_AddItemToArray(container_name##_json, thing.to_JSON()); }
//---

#define CJSON_ADD_INT_ARRAY(container)   cJSON_AddItemToObject(root, #container, cJSON_CreateIntArray(container.data(), container.size()));

#define CJSON_ADD_ITEM_ARRAY(container_name)      \
    CJSON_ITEM_ARRAY(container_name);                       \
    cJSON_AddItemToObject(root, #container_name, container_name##_json);
//---

CLANG_DIAGNOSTIC_POP<|endoftext|>


<commit_before>/*=========================================================================

  Library:   CTK

  Copyright (c) 2010  Kitware Inc.

  Licensed under the Apache License, Version 2.0 (the "License");
  you may not use this file except in compliance with the License.
  You may obtain a copy of the License at

      http://www.commontk.org/LICENSE

  Unless required by applicable law or agreed to in writing, software
  distributed under the License is distributed on an "AS IS" BASIS,
  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  See the License for the specific language governing permissions and
  limitations under the License.

=========================================================================*/

// QT includes
#include <QDebug>
#include <QHBoxLayout>

// CTK includes
#include "ctkDoubleSlider.h"

// STD includes
#include <limits>

//-----------------------------------------------------------------------------
class ctkDoubleSliderPrivate: public ctkPrivate<ctkDoubleSlider>
{
  public:
  ctkDoubleSliderPrivate();
  int toInt(double _value)const;
  double fromInt(int _value)const;
  void init();
  void updateOffset(double value);

  QSlider*    Slider;
  double      Minimum;
  double      Maximum;
  // we should have a Offset and SliderPositionOffset (and MinimumOffset?)
  double      Offset;
  double      SingleStep;
  double      Value;
};

// --------------------------------------------------------------------------
ctkDoubleSliderPrivate::ctkDoubleSliderPrivate()
{
  this->Slider = 0;
  this->Minimum = 0.;
  this->Maximum = 100.;
  this->Offset = 0.;
  this->SingleStep = 1.;
  this->Value = 0.;
}

// --------------------------------------------------------------------------
void ctkDoubleSliderPrivate::init()
{
  CTK_P(ctkDoubleSlider);
  this->Slider = new QSlider(p);
  QHBoxLayout* l = new QHBoxLayout(p);
  l->addWidget(this->Slider);
  l->setContentsMargins(0,0,0,0);

  this->Minimum = this->Slider->minimum();
  this->Maximum = this->Slider->maximum();
  this->SingleStep = this->Slider->singleStep();
  this->Value = this->Slider->value();

  p->connect(this->Slider, SIGNAL(valueChanged(int)), p, SLOT(onValueChanged(int)));
  p->connect(this->Slider, SIGNAL(sliderMoved(int)), p, SLOT(onSliderMoved(int)));
  p->connect(this->Slider, SIGNAL(sliderPressed()), p, SIGNAL(sliderPressed()));
  p->connect(this->Slider, SIGNAL(sliderReleased()), p, SIGNAL(sliderReleased()));
}

// --------------------------------------------------------------------------
int ctkDoubleSliderPrivate::toInt(double doubleValue)const
{
  double tmp = doubleValue / this->SingleStep;
  static const double minInt = std::numeric_limits<int>::min();
  static const double maxInt = std::numeric_limits<int>::max();
#ifndef QT_NO_DEBUG
  if (tmp < minInt || tmp > maxInt)
    {
    qWarning("ctkDoubleSliderPrivate::toInt value out of bounds !");
    }
#endif
  tmp = qBound(minInt, tmp, maxInt);
  int intValue = qRound(tmp);
  //qDebug() << __FUNCTION__ << doubleValue << tmp << intValue;
  return intValue;
}

// --------------------------------------------------------------------------
double ctkDoubleSliderPrivate::fromInt(int intValue)const
{
  double doubleValue = this->SingleStep * (this->Offset + intValue) ;
  //qDebug() << __FUNCTION__ << intValue << doubleValue;
  return doubleValue;
}

// --------------------------------------------------------------------------
void ctkDoubleSliderPrivate::updateOffset(double value)
{
  this->Offset = (value / this->SingleStep) - this->toInt(value);
}

// --------------------------------------------------------------------------
ctkDoubleSlider::ctkDoubleSlider(QWidget* _parent) : Superclass(_parent)
{
  CTK_INIT_PRIVATE(ctkDoubleSlider);
  ctk_d()->init();
}

// --------------------------------------------------------------------------
ctkDoubleSlider::ctkDoubleSlider(Qt::Orientation _orientation, QWidget* _parent)
  : Superclass(_parent)
{
  CTK_INIT_PRIVATE(ctkDoubleSlider);
  ctk_d()->init();
  this->setOrientation(_orientation);
}

// --------------------------------------------------------------------------
ctkDoubleSlider::~ctkDoubleSlider()
{
}

// --------------------------------------------------------------------------
void ctkDoubleSlider::setMinimum(double min)
{
  CTK_D(ctkDoubleSlider);
  d->Minimum = min;
  if (d->Minimum >= d->Value)
    {
    d->updateOffset(d->Minimum);
    }
  d->Slider->setMinimum(d->toInt(min));
}

// --------------------------------------------------------------------------
void ctkDoubleSlider::setMaximum(double max)
{
  CTK_D(ctkDoubleSlider);
  d->Maximum = max;
  if (d->Maximum <= d->Value)
    {
    d->updateOffset(d->Maximum);
    }
  d->Slider->setMaximum(d->toInt(max));
}

// --------------------------------------------------------------------------
void ctkDoubleSlider::setRange(double min, double max)
{
  CTK_D(ctkDoubleSlider);
  d->Minimum = min;
  d->Maximum = max;

  if (d->Minimum >= d->Value)
    {
    d->updateOffset(d->Minimum);
    }
  if (d->Maximum <= d->Value)
    {
    d->updateOffset(d->Maximum);
    }

  d->Slider->setRange(d->toInt(min), d->toInt(max));
}

// --------------------------------------------------------------------------
double ctkDoubleSlider::minimum()const
{
  CTK_D(const ctkDoubleSlider);
  return d->Minimum;
}

// --------------------------------------------------------------------------
double ctkDoubleSlider::maximum()const
{
  CTK_D(const ctkDoubleSlider);
  return d->Maximum;
}

// --------------------------------------------------------------------------
double ctkDoubleSlider::sliderPosition()const
{
  CTK_D(const ctkDoubleSlider);
  return d->fromInt(d->Slider->sliderPosition());
}

// --------------------------------------------------------------------------
void ctkDoubleSlider::setSliderPosition(double newSliderPosition)
{
  CTK_D(ctkDoubleSlider);
  d->Slider->setSliderPosition(d->toInt(newSliderPosition));
}

// --------------------------------------------------------------------------
double ctkDoubleSlider::value()const
{
  CTK_D(const ctkDoubleSlider);
  return d->Value;
}

// --------------------------------------------------------------------------
void ctkDoubleSlider::setValue(double newValue)
{
  CTK_D(ctkDoubleSlider);
  newValue = qBound(d->Minimum, newValue, d->Maximum);
  d->updateOffset(newValue);
  int newIntValue = d->toInt(newValue);
  if (newIntValue != d->Slider->value())
    {
    // d->Slider will emit a valueChanged signal that is connected to
    // ctkDoubleSlider::onValueChanged
    d->Slider->setValue(newIntValue);
    }
  else
    {
    double oldValue = d->Value;
    d->Value = newValue;
    // don't emit a valuechanged signal if the new value is quite
    // similar to the old value.
    if (qAbs(newValue - oldValue) > (d->SingleStep * 0.000000001))
      {
      emit this->valueChanged(newValue);
      }
    }
}

// --------------------------------------------------------------------------
double ctkDoubleSlider::singleStep()const
{
  CTK_D(const ctkDoubleSlider);
  return d->SingleStep;
}

// --------------------------------------------------------------------------
void ctkDoubleSlider::setSingleStep(double newStep)
{
  CTK_D(ctkDoubleSlider);
  d->SingleStep = newStep;
  // update the new values of the QSlider
  double _value = d->Value;
  d->updateOffset(_value);
  bool oldBlockSignals = this->blockSignals(true);
  this->setRange(d->Minimum, d->Maximum);
  d->Slider->setValue(d->toInt(_value));
  d->Value = _value;
  this->blockSignals(oldBlockSignals);
}

// --------------------------------------------------------------------------
double ctkDoubleSlider::tickInterval()const
{
  CTK_D(const ctkDoubleSlider);
  return d->fromInt(d->Slider->tickInterval());
}

// --------------------------------------------------------------------------
void ctkDoubleSlider::setTickInterval(double newTickInterval)
{
  CTK_D(ctkDoubleSlider);
  d->Slider->setTickInterval(d->toInt(newTickInterval));
}

// --------------------------------------------------------------------------
bool ctkDoubleSlider::hasTracking()const
{
  CTK_D(const ctkDoubleSlider);
  return d->Slider->hasTracking();
}

// --------------------------------------------------------------------------
void ctkDoubleSlider::setTracking(bool enable)
{
  CTK_D(ctkDoubleSlider);
  d->Slider->setTracking(enable);
}

// --------------------------------------------------------------------------
void ctkDoubleSlider::triggerAction( QAbstractSlider::SliderAction action)
{
  CTK_D(ctkDoubleSlider);
  d->Slider->triggerAction(action);
}

// --------------------------------------------------------------------------
Qt::Orientation ctkDoubleSlider::orientation()const
{
  CTK_D(const ctkDoubleSlider);
  return d->Slider->orientation();
}

// --------------------------------------------------------------------------
void ctkDoubleSlider::setOrientation(Qt::Orientation newOrientation)
{
  CTK_D(ctkDoubleSlider);
  d->Slider->setOrientation(newOrientation);
}

// --------------------------------------------------------------------------
void ctkDoubleSlider::onValueChanged(int newValue)
{
  CTK_D(ctkDoubleSlider);
  double doubleNewValue = d->fromInt(newValue);
/*
  qDebug() << "onValueChanged: " << newValue << "->"<< d->fromInt(newValue+d->Offset)
           << " old: " << d->Value << "->" << d->toInt(d->Value)
           << "offset:" << d->Offset << doubleNewValue;
*/
  if (d->Value == doubleNewValue)
    {
    return;
    }
  d->Value = doubleNewValue;
  emit this->valueChanged(d->Value);
}

// --------------------------------------------------------------------------
void ctkDoubleSlider::onSliderMoved(int newPosition)
{
  CTK_D(const ctkDoubleSlider);
  emit this->sliderMoved(d->fromInt(newPosition));
}

<commit_msg>ENH: ctkDoubleSlider now uses the same size policies than QSlider<commit_after>/*=========================================================================

  Library:   CTK

  Copyright (c) 2010  Kitware Inc.

  Licensed under the Apache License, Version 2.0 (the "License");
  you may not use this file except in compliance with the License.
  You may obtain a copy of the License at

      http://www.commontk.org/LICENSE

  Unless required by applicable law or agreed to in writing, software
  distributed under the License is distributed on an "AS IS" BASIS,
  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  See the License for the specific language governing permissions and
  limitations under the License.

=========================================================================*/

// QT includes
#include <QDebug>
#include <QHBoxLayout>

// CTK includes
#include "ctkDoubleSlider.h"

// STD includes
#include <limits>

//-----------------------------------------------------------------------------
class ctkDoubleSliderPrivate: public ctkPrivate<ctkDoubleSlider>
{
  public:
  ctkDoubleSliderPrivate();
  int toInt(double _value)const;
  double fromInt(int _value)const;
  void init();
  void updateOffset(double value);

  QSlider*    Slider;
  double      Minimum;
  double      Maximum;
  // we should have a Offset and SliderPositionOffset (and MinimumOffset?)
  double      Offset;
  double      SingleStep;
  double      Value;
};

// --------------------------------------------------------------------------
ctkDoubleSliderPrivate::ctkDoubleSliderPrivate()
{
  this->Slider = 0;
  this->Minimum = 0.;
  this->Maximum = 100.;
  this->Offset = 0.;
  this->SingleStep = 1.;
  this->Value = 0.;
}

// --------------------------------------------------------------------------
void ctkDoubleSliderPrivate::init()
{
  CTK_P(ctkDoubleSlider);
  this->Slider = new QSlider(p);
  QHBoxLayout* l = new QHBoxLayout(p);
  l->addWidget(this->Slider);
  l->setContentsMargins(0,0,0,0);

  this->Minimum = this->Slider->minimum();
  this->Maximum = this->Slider->maximum();
  this->SingleStep = this->Slider->singleStep();
  this->Value = this->Slider->value();

  p->connect(this->Slider, SIGNAL(valueChanged(int)), p, SLOT(onValueChanged(int)));
  p->connect(this->Slider, SIGNAL(sliderMoved(int)), p, SLOT(onSliderMoved(int)));
  p->connect(this->Slider, SIGNAL(sliderPressed()), p, SIGNAL(sliderPressed()));
  p->connect(this->Slider, SIGNAL(sliderReleased()), p, SIGNAL(sliderReleased()));

  p->setSizePolicy(QSizePolicy(QSizePolicy::Expanding, QSizePolicy::Fixed,
                               QSizePolicy::Slider));
}

// --------------------------------------------------------------------------
int ctkDoubleSliderPrivate::toInt(double doubleValue)const
{
  double tmp = doubleValue / this->SingleStep;
  static const double minInt = std::numeric_limits<int>::min();
  static const double maxInt = std::numeric_limits<int>::max();
#ifndef QT_NO_DEBUG
  if (tmp < minInt || tmp > maxInt)
    {
    qWarning("ctkDoubleSliderPrivate::toInt value out of bounds !");
    }
#endif
  tmp = qBound(minInt, tmp, maxInt);
  int intValue = qRound(tmp);
  //qDebug() << __FUNCTION__ << doubleValue << tmp << intValue;
  return intValue;
}

// --------------------------------------------------------------------------
double ctkDoubleSliderPrivate::fromInt(int intValue)const
{
  double doubleValue = this->SingleStep * (this->Offset + intValue) ;
  //qDebug() << __FUNCTION__ << intValue << doubleValue;
  return doubleValue;
}

// --------------------------------------------------------------------------
void ctkDoubleSliderPrivate::updateOffset(double value)
{
  this->Offset = (value / this->SingleStep) - this->toInt(value);
}

// --------------------------------------------------------------------------
ctkDoubleSlider::ctkDoubleSlider(QWidget* _parent) : Superclass(_parent)
{
  CTK_INIT_PRIVATE(ctkDoubleSlider);
  ctk_d()->init();
}

// --------------------------------------------------------------------------
ctkDoubleSlider::ctkDoubleSlider(Qt::Orientation _orientation, QWidget* _parent)
  : Superclass(_parent)
{
  CTK_INIT_PRIVATE(ctkDoubleSlider);
  ctk_d()->init();
  this->setOrientation(_orientation);
}

// --------------------------------------------------------------------------
ctkDoubleSlider::~ctkDoubleSlider()
{
}

// --------------------------------------------------------------------------
void ctkDoubleSlider::setMinimum(double min)
{
  CTK_D(ctkDoubleSlider);
  d->Minimum = min;
  if (d->Minimum >= d->Value)
    {
    d->updateOffset(d->Minimum);
    }
  d->Slider->setMinimum(d->toInt(min));
}

// --------------------------------------------------------------------------
void ctkDoubleSlider::setMaximum(double max)
{
  CTK_D(ctkDoubleSlider);
  d->Maximum = max;
  if (d->Maximum <= d->Value)
    {
    d->updateOffset(d->Maximum);
    }
  d->Slider->setMaximum(d->toInt(max));
}

// --------------------------------------------------------------------------
void ctkDoubleSlider::setRange(double min, double max)
{
  CTK_D(ctkDoubleSlider);
  d->Minimum = min;
  d->Maximum = max;

  if (d->Minimum >= d->Value)
    {
    d->updateOffset(d->Minimum);
    }
  if (d->Maximum <= d->Value)
    {
    d->updateOffset(d->Maximum);
    }

  d->Slider->setRange(d->toInt(min), d->toInt(max));
}

// --------------------------------------------------------------------------
double ctkDoubleSlider::minimum()const
{
  CTK_D(const ctkDoubleSlider);
  return d->Minimum;
}

// --------------------------------------------------------------------------
double ctkDoubleSlider::maximum()const
{
  CTK_D(const ctkDoubleSlider);
  return d->Maximum;
}

// --------------------------------------------------------------------------
double ctkDoubleSlider::sliderPosition()const
{
  CTK_D(const ctkDoubleSlider);
  return d->fromInt(d->Slider->sliderPosition());
}

// --------------------------------------------------------------------------
void ctkDoubleSlider::setSliderPosition(double newSliderPosition)
{
  CTK_D(ctkDoubleSlider);
  d->Slider->setSliderPosition(d->toInt(newSliderPosition));
}

// --------------------------------------------------------------------------
double ctkDoubleSlider::value()const
{
  CTK_D(const ctkDoubleSlider);
  return d->Value;
}

// --------------------------------------------------------------------------
void ctkDoubleSlider::setValue(double newValue)
{
  CTK_D(ctkDoubleSlider);
  newValue = qBound(d->Minimum, newValue, d->Maximum);
  d->updateOffset(newValue);
  int newIntValue = d->toInt(newValue);
  if (newIntValue != d->Slider->value())
    {
    // d->Slider will emit a valueChanged signal that is connected to
    // ctkDoubleSlider::onValueChanged
    d->Slider->setValue(newIntValue);
    }
  else
    {
    double oldValue = d->Value;
    d->Value = newValue;
    // don't emit a valuechanged signal if the new value is quite
    // similar to the old value.
    if (qAbs(newValue - oldValue) > (d->SingleStep * 0.000000001))
      {
      emit this->valueChanged(newValue);
      }
    }
}

// --------------------------------------------------------------------------
double ctkDoubleSlider::singleStep()const
{
  CTK_D(const ctkDoubleSlider);
  return d->SingleStep;
}

// --------------------------------------------------------------------------
void ctkDoubleSlider::setSingleStep(double newStep)
{
  CTK_D(ctkDoubleSlider);
  d->SingleStep = newStep;
  // update the new values of the QSlider
  double _value = d->Value;
  d->updateOffset(_value);
  bool oldBlockSignals = this->blockSignals(true);
  this->setRange(d->Minimum, d->Maximum);
  d->Slider->setValue(d->toInt(_value));
  d->Value = _value;
  this->blockSignals(oldBlockSignals);
}

// --------------------------------------------------------------------------
double ctkDoubleSlider::tickInterval()const
{
  CTK_D(const ctkDoubleSlider);
  return d->fromInt(d->Slider->tickInterval());
}

// --------------------------------------------------------------------------
void ctkDoubleSlider::setTickInterval(double newTickInterval)
{
  CTK_D(ctkDoubleSlider);
  d->Slider->setTickInterval(d->toInt(newTickInterval));
}

// --------------------------------------------------------------------------
bool ctkDoubleSlider::hasTracking()const
{
  CTK_D(const ctkDoubleSlider);
  return d->Slider->hasTracking();
}

// --------------------------------------------------------------------------
void ctkDoubleSlider::setTracking(bool enable)
{
  CTK_D(ctkDoubleSlider);
  d->Slider->setTracking(enable);
}

// --------------------------------------------------------------------------
void ctkDoubleSlider::triggerAction( QAbstractSlider::SliderAction action)
{
  CTK_D(ctkDoubleSlider);
  d->Slider->triggerAction(action);
}

// --------------------------------------------------------------------------
Qt::Orientation ctkDoubleSlider::orientation()const
{
  CTK_D(const ctkDoubleSlider);
  return d->Slider->orientation();
}

// --------------------------------------------------------------------------
void ctkDoubleSlider::setOrientation(Qt::Orientation newOrientation)
{
  CTK_D(ctkDoubleSlider);
  d->Slider->setOrientation(newOrientation);
}

// --------------------------------------------------------------------------
void ctkDoubleSlider::onValueChanged(int newValue)
{
  CTK_D(ctkDoubleSlider);
  double doubleNewValue = d->fromInt(newValue);
/*
  qDebug() << "onValueChanged: " << newValue << "->"<< d->fromInt(newValue+d->Offset)
           << " old: " << d->Value << "->" << d->toInt(d->Value)
           << "offset:" << d->Offset << doubleNewValue;
*/
  if (d->Value == doubleNewValue)
    {
    return;
    }
  d->Value = doubleNewValue;
  emit this->valueChanged(d->Value);
}

// --------------------------------------------------------------------------
void ctkDoubleSlider::onSliderMoved(int newPosition)
{
  CTK_D(const ctkDoubleSlider);
  emit this->sliderMoved(d->fromInt(newPosition));
}

<|endoftext|>


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

#include "views/touchui/touch_factory.h"

#include <gtk/gtk.h>
#include <gdk/gdkx.h>
#include <X11/cursorfont.h>
#include <X11/extensions/XInput.h>
#include <X11/extensions/XInput2.h>
#include <X11/extensions/XIproto.h>

#include "base/basictypes.h"
#include "base/compiler_specific.h"
#include "base/logging.h"
#include "ui/base/x/x11_util.h"

namespace {

// The X cursor is hidden if it is idle for kCursorIdleSeconds seconds.
int kCursorIdleSeconds = 5;

// Given the TouchParam, return the correspoding valuator index using
// the X device information through Atom name matching.
char FindTPValuator(Display* display,
                    XIDeviceInfo* info,
                    views::TouchFactory::TouchParam touch_param) {
  // Lookup table for mapping TouchParam to Atom string used in X.
  // A full set of Atom strings can be found at xserver-properties.h.
  static struct {
    views::TouchFactory::TouchParam tp;
    const char* atom;
  } kTouchParamAtom[] = {
    { views::TouchFactory::TP_TOUCH_MAJOR, "Abs MT Touch Major" },
    { views::TouchFactory::TP_TOUCH_MINOR, "Abs MT Touch Minor" },
    { views::TouchFactory::TP_ORIENTATION, "Abs MT Orientation" },
    { views::TouchFactory::TP_LAST_ENTRY, NULL },
  };

  const char* atom_tp = NULL;

  for (size_t i = 0; i < ARRAYSIZE_UNSAFE(kTouchParamAtom); i++) {
    if (touch_param == kTouchParamAtom[i].tp) {
      atom_tp = kTouchParamAtom[i].atom;
      break;
    }
  }

  if (!atom_tp)
    return -1;

  for (int i = 0; i < info->num_classes; i++) {
    if (info->classes[i]->type != XIValuatorClass)
      continue;
    XIValuatorClassInfo* v =
        reinterpret_cast<XIValuatorClassInfo*>(info->classes[i]);

    const char* atom = XGetAtomName(display, v->label);

    if (atom && strcmp(atom, atom_tp) == 0)
      return v->number;
  }

  return -1;
}

// Setup XInput2 select for the GtkWidget.
gboolean GtkWidgetRealizeCallback(GSignalInvocationHint* hint, guint nparams,
                                  const GValue* pvalues, gpointer data) {
  GtkWidget* widget = GTK_WIDGET(g_value_get_object(pvalues));
  GdkWindow* window = widget->window;
  views::TouchFactory* factory = static_cast<views::TouchFactory*>(data);

  if (GDK_WINDOW_TYPE(window) != GDK_WINDOW_TOPLEVEL &&
      GDK_WINDOW_TYPE(window) != GDK_WINDOW_CHILD &&
      GDK_WINDOW_TYPE(window) != GDK_WINDOW_DIALOG)
    return true;

  factory->SetupXI2ForXWindow(GDK_WINDOW_XID(window));
  return true;
}

// We need to capture all the GDK windows that get created, and start
// listening for XInput2 events. So we setup a callback to the 'realize'
// signal for GTK+ widgets, so that whenever the signal triggers for any
// GtkWidget, which means the GtkWidget should now have a GdkWindow, we can
// setup XInput2 events for the GdkWindow.
guint realize_signal_id = 0;
guint realize_hook_id = 0;

void SetupGtkWidgetRealizeNotifier(views::TouchFactory* factory) {
  gpointer klass = g_type_class_ref(GTK_TYPE_WIDGET);

  g_signal_parse_name("realize", GTK_TYPE_WIDGET,
                      &realize_signal_id, NULL, FALSE);
  realize_hook_id = g_signal_add_emission_hook(realize_signal_id, 0,
      GtkWidgetRealizeCallback, static_cast<gpointer>(factory), NULL);

  g_type_class_unref(klass);
}

void RemoveGtkWidgetRealizeNotifier() {
  if (realize_signal_id != 0)
    g_signal_remove_emission_hook(realize_signal_id, realize_hook_id);
  realize_signal_id = 0;
  realize_hook_id = 0;
}

}  // namespace

namespace views {

// static
TouchFactory* TouchFactory::GetInstance() {
  return Singleton<TouchFactory>::get();
}

TouchFactory::TouchFactory()
    : is_cursor_visible_(true),
      cursor_timer_(),
      pointer_device_lookup_(),
      touch_device_list_() {
  char nodata[] = { 0, 0, 0, 0, 0, 0, 0, 0 };
  XColor black;
  black.red = black.green = black.blue = 0;
  Display* display = ui::GetXDisplay();
  Pixmap blank = XCreateBitmapFromData(display, ui::GetX11RootWindow(),
                                       nodata, 8, 8);
  invisible_cursor_ = XCreatePixmapCursor(display, blank, blank,
                                          &black, &black, 0, 0);
  arrow_cursor_ = XCreateFontCursor(display, XC_arrow);

  SetCursorVisible(false, false);
  UpdateDeviceList(display);

  // TODO(sad): Here, we only setup so that the X windows created by GTK+ are
  // setup for XInput2 events. We need a way to listen for XInput2 events for X
  // windows created by other means (e.g. for context menus).
  SetupGtkWidgetRealizeNotifier(this);

  // Make sure the list of devices is kept up-to-date by listening for
  // XI_HierarchyChanged event on the root window.
  unsigned char mask[XIMaskLen(XI_LASTEVENT)];
  memset(mask, 0, sizeof(mask));

  XISetMask(mask, XI_HierarchyChanged);

  XIEventMask evmask;
  evmask.deviceid = XIAllDevices;
  evmask.mask_len = sizeof(mask);
  evmask.mask = mask;
  XISelectEvents(display, ui::GetX11RootWindow(), &evmask, 1);
}

TouchFactory::~TouchFactory() {
  SetCursorVisible(true, false);
  Display* display = ui::GetXDisplay();
  XFreeCursor(display, invisible_cursor_);
  XFreeCursor(display, arrow_cursor_);

  RemoveGtkWidgetRealizeNotifier();
}

void TouchFactory::UpdateDeviceList(Display* display) {
  // Detect touch devices.
  // NOTE: The new API for retrieving the list of devices (XIQueryDevice) does
  // not provide enough information to detect a touch device. As a result, the
  // old version of query function (XListInputDevices) is used instead.
  // If XInput2 is not supported, this will return null (with count of -1) so
  // we assume there cannot be any touch devices.
  int count = 0;
  touch_device_lookup_.reset();
  touch_device_list_.clear();
  XDeviceInfo* devlist = XListInputDevices(display, &count);
  for (int i = 0; i < count; i++) {
    const char* devtype = XGetAtomName(display, devlist[i].type);
    if (devtype && !strcmp(devtype, XI_TOUCHSCREEN)) {
      touch_device_lookup_[devlist[i].id] = true;
      touch_device_list_.push_back(devlist[i].id);
    }
  }
  if (devlist)
    XFreeDeviceList(devlist);

  // Instead of asking X for the list of devices all the time, let's maintain a
  // list of pointer devices we care about.
  // It is not necessary to select for slave devices. XInput2 provides enough
  // information to the event callback to decide which slave device triggered
  // the event, thus decide whether the 'pointer event' is a 'mouse event' or a
  // 'touch event'.
  // If the touch device has 'GrabDevice' set and 'SendCoreEvents' unset (which
  // is possible), then the device is detected as a floating device, and a
  // floating device is not connected to a master device. So it is necessary to
  // also select on the floating devices.
  pointer_device_lookup_.reset();
  XIDeviceInfo* devices = XIQueryDevice(display, XIAllDevices, &count);
  for (int i = 0; i < count; i++) {
    XIDeviceInfo* devinfo = devices + i;
    if (devinfo->use == XIFloatingSlave || devinfo->use == XIMasterPointer) {
      pointer_device_lookup_[devinfo->deviceid] = true;
    }
  }
  XIFreeDeviceInfo(devices);

  SetupValuator();
}

bool TouchFactory::ShouldProcessXI2Event(XEvent* xev) {
  DCHECK_EQ(GenericEvent, xev->type);

  XGenericEventCookie* cookie = &xev->xcookie;
  if (cookie->evtype != XI_ButtonPress &&
      cookie->evtype != XI_ButtonRelease &&
      cookie->evtype != XI_Motion)
    return true;

  XIDeviceEvent* xiev = static_cast<XIDeviceEvent*>(cookie->data);
  return pointer_device_lookup_[xiev->sourceid];
}

void TouchFactory::SetupXI2ForXWindow(Window window) {
  // Setup mask for mouse events. It is possible that a device is loaded/plugged
  // in after we have setup XInput2 on a window. In such cases, we need to
  // either resetup XInput2 for the window, so that we get events from the new
  // device, or we need to listen to events from all devices, and then filter
  // the events from uninteresting devices. We do the latter because that's
  // simpler.

  Display* display = ui::GetXDisplay();

  unsigned char mask[XIMaskLen(XI_LASTEVENT)];
  memset(mask, 0, sizeof(mask));

  XISetMask(mask, XI_ButtonPress);
  XISetMask(mask, XI_ButtonRelease);
  XISetMask(mask, XI_Motion);

  XIEventMask evmask;
  evmask.deviceid = XIAllDevices;
  evmask.mask_len = sizeof(mask);
  evmask.mask = mask;
  XISelectEvents(display, window, &evmask, 1);
  XFlush(display);
}

void TouchFactory::SetTouchDeviceList(
    const std::vector<unsigned int>& devices) {
  touch_device_lookup_.reset();
  touch_device_list_.clear();
  for (std::vector<unsigned int>::const_iterator iter = devices.begin();
       iter != devices.end(); ++iter) {
    DCHECK(*iter < touch_device_lookup_.size());
    touch_device_lookup_[*iter] = true;
    touch_device_list_.push_back(*iter);
  }

  SetupValuator();
}

bool TouchFactory::IsTouchDevice(unsigned deviceid) const {
  return deviceid < touch_device_lookup_.size() ?
      touch_device_lookup_[deviceid] : false;
}

bool TouchFactory::GrabTouchDevices(Display* display, ::Window window) {
  if (touch_device_list_.empty())
    return true;

  unsigned char mask[XIMaskLen(XI_LASTEVENT)];
  bool success = true;

  memset(mask, 0, sizeof(mask));
  XISetMask(mask, XI_ButtonPress);
  XISetMask(mask, XI_ButtonRelease);
  XISetMask(mask, XI_Motion);

  XIEventMask evmask;
  evmask.mask_len = sizeof(mask);
  evmask.mask = mask;
  for (std::vector<int>::const_iterator iter =
       touch_device_list_.begin();
       iter != touch_device_list_.end(); ++iter) {
    evmask.deviceid = *iter;
    Status status = XIGrabDevice(display, *iter, window, CurrentTime, None,
                                 GrabModeAsync, GrabModeAsync, False, &evmask);
    success = success && status == GrabSuccess;
  }

  return success;
}

bool TouchFactory::UngrabTouchDevices(Display* display) {
  bool success = true;
  for (std::vector<int>::const_iterator iter =
       touch_device_list_.begin();
       iter != touch_device_list_.end(); ++iter) {
    Status status = XIUngrabDevice(display, *iter, CurrentTime);
    success = success && status == GrabSuccess;
  }
  return success;
}

void TouchFactory::SetCursorVisible(bool show, bool start_timer) {
  // The cursor is going to be shown. Reset the timer for hiding it.
  if (show && start_timer) {
    cursor_timer_.Stop();
    cursor_timer_.Start(base::TimeDelta::FromSeconds(kCursorIdleSeconds),
        this, &TouchFactory::HideCursorForInactivity);
  } else {
    cursor_timer_.Stop();
  }

  if (show == is_cursor_visible_)
    return;

  is_cursor_visible_ = show;

  Display* display = ui::GetXDisplay();
  Window window = DefaultRootWindow(display);

  if (is_cursor_visible_) {
    XDefineCursor(display, window, arrow_cursor_);
  } else {
    XDefineCursor(display, window, invisible_cursor_);
  }
}

void TouchFactory::SetupValuator() {
  memset(valuator_lookup_, -1, sizeof(valuator_lookup_));

  Display* display = ui::GetXDisplay();
  int ndevice;
  XIDeviceInfo* info_list = XIQueryDevice(display, XIAllDevices, &ndevice);

  for (int i = 0; i < ndevice; i++) {
    XIDeviceInfo* info = info_list + i;

    if (!IsTouchDevice(info->deviceid))
      continue;

    for (int i = 0; i < TP_LAST_ENTRY; i++) {
      TouchParam tp = static_cast<TouchParam>(i);
      valuator_lookup_[info->deviceid][i] = FindTPValuator(display, info, tp);
    }
  }

  if (info_list)
    XIFreeDeviceInfo(info_list);
}

bool TouchFactory::ExtractTouchParam(const XEvent& xev,
                                     TouchParam tp,
                                     float* value) {
  XIDeviceEvent* xiev = static_cast<XIDeviceEvent*>(xev.xcookie.data);
  if (xiev->sourceid >= kMaxDeviceNum)
    return false;
  int v = valuator_lookup_[xiev->sourceid][tp];
  if (v >= 0 && XIMaskIsSet(xiev->valuators.mask, v)) {
    *value = xiev->valuators.values[v];
    return true;
  }

  return false;
}

}  // namespace views
<commit_msg>Select on slave pointers instead of master pointers.<commit_after>// Copyright (c) 2011 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include "views/touchui/touch_factory.h"

#include <gtk/gtk.h>
#include <gdk/gdkx.h>
#include <X11/cursorfont.h>
#include <X11/extensions/XInput.h>
#include <X11/extensions/XInput2.h>
#include <X11/extensions/XIproto.h>

#include "base/basictypes.h"
#include "base/compiler_specific.h"
#include "base/logging.h"
#include "ui/base/x/x11_util.h"

namespace {

// The X cursor is hidden if it is idle for kCursorIdleSeconds seconds.
int kCursorIdleSeconds = 5;

// Given the TouchParam, return the correspoding valuator index using
// the X device information through Atom name matching.
char FindTPValuator(Display* display,
                    XIDeviceInfo* info,
                    views::TouchFactory::TouchParam touch_param) {
  // Lookup table for mapping TouchParam to Atom string used in X.
  // A full set of Atom strings can be found at xserver-properties.h.
  static struct {
    views::TouchFactory::TouchParam tp;
    const char* atom;
  } kTouchParamAtom[] = {
    { views::TouchFactory::TP_TOUCH_MAJOR, "Abs MT Touch Major" },
    { views::TouchFactory::TP_TOUCH_MINOR, "Abs MT Touch Minor" },
    { views::TouchFactory::TP_ORIENTATION, "Abs MT Orientation" },
    { views::TouchFactory::TP_LAST_ENTRY, NULL },
  };

  const char* atom_tp = NULL;

  for (size_t i = 0; i < ARRAYSIZE_UNSAFE(kTouchParamAtom); i++) {
    if (touch_param == kTouchParamAtom[i].tp) {
      atom_tp = kTouchParamAtom[i].atom;
      break;
    }
  }

  if (!atom_tp)
    return -1;

  for (int i = 0; i < info->num_classes; i++) {
    if (info->classes[i]->type != XIValuatorClass)
      continue;
    XIValuatorClassInfo* v =
        reinterpret_cast<XIValuatorClassInfo*>(info->classes[i]);

    const char* atom = XGetAtomName(display, v->label);

    if (atom && strcmp(atom, atom_tp) == 0)
      return v->number;
  }

  return -1;
}

// Setup XInput2 select for the GtkWidget.
gboolean GtkWidgetRealizeCallback(GSignalInvocationHint* hint, guint nparams,
                                  const GValue* pvalues, gpointer data) {
  GtkWidget* widget = GTK_WIDGET(g_value_get_object(pvalues));
  GdkWindow* window = widget->window;
  views::TouchFactory* factory = static_cast<views::TouchFactory*>(data);

  if (GDK_WINDOW_TYPE(window) != GDK_WINDOW_TOPLEVEL &&
      GDK_WINDOW_TYPE(window) != GDK_WINDOW_CHILD &&
      GDK_WINDOW_TYPE(window) != GDK_WINDOW_DIALOG)
    return true;

  factory->SetupXI2ForXWindow(GDK_WINDOW_XID(window));
  return true;
}

// We need to capture all the GDK windows that get created, and start
// listening for XInput2 events. So we setup a callback to the 'realize'
// signal for GTK+ widgets, so that whenever the signal triggers for any
// GtkWidget, which means the GtkWidget should now have a GdkWindow, we can
// setup XInput2 events for the GdkWindow.
guint realize_signal_id = 0;
guint realize_hook_id = 0;

void SetupGtkWidgetRealizeNotifier(views::TouchFactory* factory) {
  gpointer klass = g_type_class_ref(GTK_TYPE_WIDGET);

  g_signal_parse_name("realize", GTK_TYPE_WIDGET,
                      &realize_signal_id, NULL, FALSE);
  realize_hook_id = g_signal_add_emission_hook(realize_signal_id, 0,
      GtkWidgetRealizeCallback, static_cast<gpointer>(factory), NULL);

  g_type_class_unref(klass);
}

void RemoveGtkWidgetRealizeNotifier() {
  if (realize_signal_id != 0)
    g_signal_remove_emission_hook(realize_signal_id, realize_hook_id);
  realize_signal_id = 0;
  realize_hook_id = 0;
}

}  // namespace

namespace views {

// static
TouchFactory* TouchFactory::GetInstance() {
  return Singleton<TouchFactory>::get();
}

TouchFactory::TouchFactory()
    : is_cursor_visible_(true),
      cursor_timer_(),
      pointer_device_lookup_(),
      touch_device_list_() {
  char nodata[] = { 0, 0, 0, 0, 0, 0, 0, 0 };
  XColor black;
  black.red = black.green = black.blue = 0;
  Display* display = ui::GetXDisplay();
  Pixmap blank = XCreateBitmapFromData(display, ui::GetX11RootWindow(),
                                       nodata, 8, 8);
  invisible_cursor_ = XCreatePixmapCursor(display, blank, blank,
                                          &black, &black, 0, 0);
  arrow_cursor_ = XCreateFontCursor(display, XC_arrow);

  SetCursorVisible(false, false);
  UpdateDeviceList(display);

  // TODO(sad): Here, we only setup so that the X windows created by GTK+ are
  // setup for XInput2 events. We need a way to listen for XInput2 events for X
  // windows created by other means (e.g. for context menus).
  SetupGtkWidgetRealizeNotifier(this);

  // Make sure the list of devices is kept up-to-date by listening for
  // XI_HierarchyChanged event on the root window.
  unsigned char mask[XIMaskLen(XI_LASTEVENT)];
  memset(mask, 0, sizeof(mask));

  XISetMask(mask, XI_HierarchyChanged);

  XIEventMask evmask;
  evmask.deviceid = XIAllDevices;
  evmask.mask_len = sizeof(mask);
  evmask.mask = mask;
  XISelectEvents(display, ui::GetX11RootWindow(), &evmask, 1);
}

TouchFactory::~TouchFactory() {
  SetCursorVisible(true, false);
  Display* display = ui::GetXDisplay();
  XFreeCursor(display, invisible_cursor_);
  XFreeCursor(display, arrow_cursor_);

  RemoveGtkWidgetRealizeNotifier();
}

void TouchFactory::UpdateDeviceList(Display* display) {
  // Detect touch devices.
  // NOTE: The new API for retrieving the list of devices (XIQueryDevice) does
  // not provide enough information to detect a touch device. As a result, the
  // old version of query function (XListInputDevices) is used instead.
  // If XInput2 is not supported, this will return null (with count of -1) so
  // we assume there cannot be any touch devices.
  int count = 0;
  touch_device_lookup_.reset();
  touch_device_list_.clear();
  XDeviceInfo* devlist = XListInputDevices(display, &count);
  for (int i = 0; i < count; i++) {
    const char* devtype = XGetAtomName(display, devlist[i].type);
    if (devtype && !strcmp(devtype, XI_TOUCHSCREEN)) {
      touch_device_lookup_[devlist[i].id] = true;
      touch_device_list_.push_back(devlist[i].id);
    }
  }
  if (devlist)
    XFreeDeviceList(devlist);

  // Instead of asking X for the list of devices all the time, let's maintain a
  // list of pointer devices we care about.
  // It should not be necessary to select for slave devices. XInput2 provides
  // enough information to the event callback to decide which slave device
  // triggered the event, thus decide whether the 'pointer event' is a
  // 'mouse event' or a 'touch event'.
  // However, on some desktops, some events from a master pointer are
  // not delivered to the client. So we select for slave devices instead.
  // If the touch device has 'GrabDevice' set and 'SendCoreEvents' unset (which
  // is possible), then the device is detected as a floating device, and a
  // floating device is not connected to a master device. So it is necessary to
  // also select on the floating devices.
  pointer_device_lookup_.reset();
  XIDeviceInfo* devices = XIQueryDevice(display, XIAllDevices, &count);
  for (int i = 0; i < count; i++) {
    XIDeviceInfo* devinfo = devices + i;
    if (devinfo->use == XIFloatingSlave || devinfo->use == XISlavePointer) {
      pointer_device_lookup_[devinfo->deviceid] = true;
    }
  }
  XIFreeDeviceInfo(devices);

  SetupValuator();
}

bool TouchFactory::ShouldProcessXI2Event(XEvent* xev) {
  DCHECK_EQ(GenericEvent, xev->type);

  XGenericEventCookie* cookie = &xev->xcookie;
  if (cookie->evtype != XI_ButtonPress &&
      cookie->evtype != XI_ButtonRelease &&
      cookie->evtype != XI_Motion)
    return true;

  XIDeviceEvent* xiev = static_cast<XIDeviceEvent*>(cookie->data);
  return pointer_device_lookup_[xiev->sourceid];
}

void TouchFactory::SetupXI2ForXWindow(Window window) {
  // Setup mask for mouse events. It is possible that a device is loaded/plugged
  // in after we have setup XInput2 on a window. In such cases, we need to
  // either resetup XInput2 for the window, so that we get events from the new
  // device, or we need to listen to events from all devices, and then filter
  // the events from uninteresting devices. We do the latter because that's
  // simpler.

  Display* display = ui::GetXDisplay();

  unsigned char mask[XIMaskLen(XI_LASTEVENT)];
  memset(mask, 0, sizeof(mask));

  XISetMask(mask, XI_ButtonPress);
  XISetMask(mask, XI_ButtonRelease);
  XISetMask(mask, XI_Motion);

  XIEventMask evmask;
  evmask.deviceid = XIAllDevices;
  evmask.mask_len = sizeof(mask);
  evmask.mask = mask;
  XISelectEvents(display, window, &evmask, 1);
  XFlush(display);
}

void TouchFactory::SetTouchDeviceList(
    const std::vector<unsigned int>& devices) {
  touch_device_lookup_.reset();
  touch_device_list_.clear();
  for (std::vector<unsigned int>::const_iterator iter = devices.begin();
       iter != devices.end(); ++iter) {
    DCHECK(*iter < touch_device_lookup_.size());
    touch_device_lookup_[*iter] = true;
    touch_device_list_.push_back(*iter);
  }

  SetupValuator();
}

bool TouchFactory::IsTouchDevice(unsigned deviceid) const {
  return deviceid < touch_device_lookup_.size() ?
      touch_device_lookup_[deviceid] : false;
}

bool TouchFactory::GrabTouchDevices(Display* display, ::Window window) {
  if (touch_device_list_.empty())
    return true;

  unsigned char mask[XIMaskLen(XI_LASTEVENT)];
  bool success = true;

  memset(mask, 0, sizeof(mask));
  XISetMask(mask, XI_ButtonPress);
  XISetMask(mask, XI_ButtonRelease);
  XISetMask(mask, XI_Motion);

  XIEventMask evmask;
  evmask.mask_len = sizeof(mask);
  evmask.mask = mask;
  for (std::vector<int>::const_iterator iter =
       touch_device_list_.begin();
       iter != touch_device_list_.end(); ++iter) {
    evmask.deviceid = *iter;
    Status status = XIGrabDevice(display, *iter, window, CurrentTime, None,
                                 GrabModeAsync, GrabModeAsync, False, &evmask);
    success = success && status == GrabSuccess;
  }

  return success;
}

bool TouchFactory::UngrabTouchDevices(Display* display) {
  bool success = true;
  for (std::vector<int>::const_iterator iter =
       touch_device_list_.begin();
       iter != touch_device_list_.end(); ++iter) {
    Status status = XIUngrabDevice(display, *iter, CurrentTime);
    success = success && status == GrabSuccess;
  }
  return success;
}

void TouchFactory::SetCursorVisible(bool show, bool start_timer) {
  // The cursor is going to be shown. Reset the timer for hiding it.
  if (show && start_timer) {
    cursor_timer_.Stop();
    cursor_timer_.Start(base::TimeDelta::FromSeconds(kCursorIdleSeconds),
        this, &TouchFactory::HideCursorForInactivity);
  } else {
    cursor_timer_.Stop();
  }

  if (show == is_cursor_visible_)
    return;

  is_cursor_visible_ = show;

  Display* display = ui::GetXDisplay();
  Window window = DefaultRootWindow(display);

  if (is_cursor_visible_) {
    XDefineCursor(display, window, arrow_cursor_);
  } else {
    XDefineCursor(display, window, invisible_cursor_);
  }
}

void TouchFactory::SetupValuator() {
  memset(valuator_lookup_, -1, sizeof(valuator_lookup_));

  Display* display = ui::GetXDisplay();
  int ndevice;
  XIDeviceInfo* info_list = XIQueryDevice(display, XIAllDevices, &ndevice);

  for (int i = 0; i < ndevice; i++) {
    XIDeviceInfo* info = info_list + i;

    if (!IsTouchDevice(info->deviceid))
      continue;

    for (int i = 0; i < TP_LAST_ENTRY; i++) {
      TouchParam tp = static_cast<TouchParam>(i);
      valuator_lookup_[info->deviceid][i] = FindTPValuator(display, info, tp);
    }
  }

  if (info_list)
    XIFreeDeviceInfo(info_list);
}

bool TouchFactory::ExtractTouchParam(const XEvent& xev,
                                     TouchParam tp,
                                     float* value) {
  XIDeviceEvent* xiev = static_cast<XIDeviceEvent*>(xev.xcookie.data);
  if (xiev->sourceid >= kMaxDeviceNum)
    return false;
  int v = valuator_lookup_[xiev->sourceid][tp];
  if (v >= 0 && XIMaskIsSet(xiev->valuators.mask, v)) {
    *value = xiev->valuators.values[v];
    return true;
  }

  return false;
}

}  // namespace views
<|endoftext|>


<commit_before><commit_msg>[booking] android build fix<commit_after><|endoftext|>


<commit_before>/*
 * Software License Agreement (BSD License)
 *
 *  Copyright (c) 2009, Willow Garage, Inc.
 *  All rights reserved.
 *
 *  Redistribution and use in source and binary forms, with or without
 *  modification, are permitted provided that the following conditions
 *  are met:
 *
 *   * Redistributions of source code must retain the above copyright
 *     notice, this list of conditions and the following disclaimer.
 *   * Redistributions in binary form must reproduce the above
 *     copyright notice, this list of conditions and the following
 *     disclaimer in the documentation and/or other materials provided
 *     with the distribution.
 *   * Neither the name of the copyright holder(s) nor the names of its
 *     contributors may be used to endorse or promote products derived
 *     from this software without specific prior written permission.
 *
 *  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 *  "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 *  LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
 *  FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
 *  COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
 *  INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
 *  BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
 *  LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
 *  CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
 *  LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
 *  ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
 *  POSSIBILITY OF SUCH DAMAGE.
 *
 * $Id: extract_indices.hpp 1897 2011-07-26 20:35:49Z rusu $
 *
 */

#ifndef PCL_FILTERS_IMPL_RANDOM_SAMPLE_H_
#define PCL_FILTERS_IMPL_RANDOM_SAMPLE_H_

#include <pcl/filters/random_sample.h>
#include <pcl/common/io.h>
#include <pcl/point_traits.h>

///////////////////////////////////////////////////////////////////////////////
template<typename PointT> void
pcl::RandomSample<PointT>::applyFilter (PointCloud &output)
{
  std::vector<int> indices;
  if (keep_organized_)
  {
    bool temp = extract_removed_indices_;
    extract_removed_indices_ = true;
    applyFilter (indices);
    extract_removed_indices_ = temp;
    copyPointCloud (*input_, output);
    // Get X, Y, Z fields
    std::vector<pcl::PCLPointField> fields;
    pcl::getFields (*input_, fields);
    std::vector<size_t> offsets;
    for (size_t i = 0; i < fields.size (); ++i)
    {
      if (fields[i].name == "x" ||
          fields[i].name == "y" ||
          fields[i].name == "z")
        offsets.push_back (fields[i].offset);
    }
    // For every "removed" point, set the x,y,z fields to user_filter_value_
    const static float user_filter_value = user_filter_value_;
    for (size_t rii = 0; rii < removed_indices_->size (); ++rii)
    {
      uint8_t* pt_data = reinterpret_cast<uint8_t*> (&output[(*removed_indices_)[rii]]);
      for (size_t i = 0; i < offsets.size (); ++i)
      {
        memcpy (pt_data + offsets[i], &user_filter_value, sizeof (float));
      }
      if (!pcl_isfinite (user_filter_value_))
        output.is_dense = false;
    }
  }
  else
  {
    output.is_dense = true;
    applyFilter (indices);
    copyPointCloud (*input_, indices, output);
  }
}

///////////////////////////////////////////////////////////////////////////////
template<typename PointT>
void
pcl::RandomSample<PointT>::applyFilter (std::vector<int> &indices)
{
  unsigned N = static_cast<unsigned> (indices_->size ());

  unsigned int sample_size = negative_ ? N - sample_ : sample_;
  // If sample size is 0 or if the sample size is greater then input cloud size
  //   then return all indices
  if (sample_size >= N)
  {
    indices = *indices_;
    removed_indices_->clear ();
  }
  else
  {
    // Resize output indices to sample size
    indices.resize (static_cast<size_t> (sample_size));
    if (extract_removed_indices_)
      removed_indices_->resize (static_cast<size_t> (N - sample_size));

    // Set random seed so derived indices are the same each time the filter runs
    std::srand (seed_);

    // Algorithm A
    unsigned top = N - sample_size;
    unsigned i = 0;
    unsigned index = 0;
    std::vector<bool> added;
    if (extract_removed_indices_)
      added.resize (indices_->size (), false);
    for (size_t n = sample_size; n >= 2; n--)
    {
      float V = unifRand ();
      unsigned S = 0;
      float quot = static_cast<float> (top) / static_cast<float> (N);
      while (quot > V)
      {
        S++;
        top--;
        N--;
        quot = quot * static_cast<float> (top) / static_cast<float> (N);
      }
      index += S;
      if (extract_removed_indices_)
        added[index] = true;
      indices[i++] = (*indices_)[index++];
      N--;
    }

    index += N * static_cast<unsigned> (unifRand ());
    if (extract_removed_indices_)
      added[index] = true;
    indices[i++] = (*indices_)[index++];

    // Now populate removed_indices_ appropriately
    if (extract_removed_indices_)
    {
      unsigned ri = 0;
      for (size_t i = 0; i < added.size (); i++)
      {
        if (!added[i])
        {
          (*removed_indices_)[ri++] = (*indices_)[i];
        }
      }
    }
  }
}

#define PCL_INSTANTIATE_RandomSample(T) template class PCL_EXPORTS pcl::RandomSample<T>;

#endif    // PCL_FILTERS_IMPL_RANDOM_SAMPLE_H_
<commit_msg>Avoid huge index jumps in pcl::RandomSample<commit_after>/*
 * Software License Agreement (BSD License)
 *
 *  Copyright (c) 2009, Willow Garage, Inc.
 *  All rights reserved.
 *
 *  Redistribution and use in source and binary forms, with or without
 *  modification, are permitted provided that the following conditions
 *  are met:
 *
 *   * Redistributions of source code must retain the above copyright
 *     notice, this list of conditions and the following disclaimer.
 *   * Redistributions in binary form must reproduce the above
 *     copyright notice, this list of conditions and the following
 *     disclaimer in the documentation and/or other materials provided
 *     with the distribution.
 *   * Neither the name of the copyright holder(s) nor the names of its
 *     contributors may be used to endorse or promote products derived
 *     from this software without specific prior written permission.
 *
 *  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 *  "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 *  LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
 *  FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
 *  COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
 *  INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
 *  BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
 *  LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
 *  CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
 *  LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
 *  ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
 *  POSSIBILITY OF SUCH DAMAGE.
 *
 * $Id: extract_indices.hpp 1897 2011-07-26 20:35:49Z rusu $
 *
 */

#ifndef PCL_FILTERS_IMPL_RANDOM_SAMPLE_H_
#define PCL_FILTERS_IMPL_RANDOM_SAMPLE_H_

#include <pcl/filters/random_sample.h>
#include <pcl/common/io.h>
#include <pcl/point_traits.h>

///////////////////////////////////////////////////////////////////////////////
template<typename PointT> void
pcl::RandomSample<PointT>::applyFilter (PointCloud &output)
{
  std::vector<int> indices;
  if (keep_organized_)
  {
    bool temp = extract_removed_indices_;
    extract_removed_indices_ = true;
    applyFilter (indices);
    extract_removed_indices_ = temp;
    copyPointCloud (*input_, output);
    // Get X, Y, Z fields
    std::vector<pcl::PCLPointField> fields;
    pcl::getFields (*input_, fields);
    std::vector<size_t> offsets;
    for (size_t i = 0; i < fields.size (); ++i)
    {
      if (fields[i].name == "x" ||
          fields[i].name == "y" ||
          fields[i].name == "z")
        offsets.push_back (fields[i].offset);
    }
    // For every "removed" point, set the x,y,z fields to user_filter_value_
    const static float user_filter_value = user_filter_value_;
    for (size_t rii = 0; rii < removed_indices_->size (); ++rii)
    {
      uint8_t* pt_data = reinterpret_cast<uint8_t*> (&output[(*removed_indices_)[rii]]);
      for (size_t i = 0; i < offsets.size (); ++i)
      {
        memcpy (pt_data + offsets[i], &user_filter_value, sizeof (float));
      }
      if (!pcl_isfinite (user_filter_value_))
        output.is_dense = false;
    }
  }
  else
  {
    output.is_dense = true;
    applyFilter (indices);
    copyPointCloud (*input_, indices, output);
  }
}

///////////////////////////////////////////////////////////////////////////////
template<typename PointT>
void
pcl::RandomSample<PointT>::applyFilter (std::vector<int> &indices)
{
  size_t N = indices_->size ();
  size_t sample_size = negative_ ? N - sample_ : sample_;
  // If sample size is 0 or if the sample size is greater then input cloud size
  //   then return all indices
  if (sample_size >= N)
  {
    indices = *indices_;
    removed_indices_->clear ();
  }
  else
  {
    // Resize output indices to sample size
    indices.resize (sample_size);
    if (extract_removed_indices_)
      removed_indices_->resize (N - sample_size);

    // Set random seed so derived indices are the same each time the filter runs
    std::srand (seed_);

    // Algorithm S
    size_t i = 0;
    size_t index = 0;
    std::vector<bool> added;
    if (extract_removed_indices_)
      added.resize (indices_->size (), false);
    size_t n = sample_size;
    while (n > 0)
    {
      // Step 1: [Generate U.] Generate a random variate U that is uniformly distributed between 0 and 1.
      const float U = unifRand ();
      // Step 2: [Test.] If N * U > n, go to Step 4.
      if ((N * U) <= n)
      {
        // Step 3: [Select.] Select the next record in the file for the sample, and set n : = n - 1.
        if (extract_removed_indices_)
          added[index] = true;
        indices[i++] = (*indices_)[index];
        --n;
      }
      // Step 4: [Don't select.] Skip over the next record (do not include it in the sample).
      // Set N : = N - 1.
      --N;
      ++index;
      // If n > 0, then return to Step 1; otherwise, the sample is complete and the algorithm terminates.
    }

    // Now populate removed_indices_ appropriately
    if (extract_removed_indices_)
    {
      size_t ri = 0;
      for (size_t i = 0; i < added.size (); i++)
      {
        if (!added[i])
        {
          (*removed_indices_)[ri++] = (*indices_)[i];
        }
      }
    }
  }
}

#define PCL_INSTANTIATE_RandomSample(T) template class PCL_EXPORTS pcl::RandomSample<T>;

#endif    // PCL_FILTERS_IMPL_RANDOM_SAMPLE_H_
<|endoftext|>


<commit_before>
// todo:
//   -Ir
//   -Iz
//   -zr
//   -zt
//   -f

#include "i2_options.h"
#include "posix_fe.h"
#include <netdb.h>

class i2_program
{
    i2_options  opts;
    bool _ok;
    pxfe_tcp_stream_socket * net_fd;
    uint64_t bytes_sent;
    uint64_t bytes_received;
    pxfe_timeval  start_time;
    pxfe_string  buffer;
    pxfe_ticker  ticker;
public:
    i2_program(int argc, char ** argv)
        : opts(argc, argv), _ok(false)
    {
        _ok = opts.ok;
        net_fd = NULL;
        bytes_received = 0;
        bytes_sent = 0;
    }
    ~i2_program(void)
    {
        // opts destructor will close the input & output fds.
        if (net_fd)
            delete net_fd;
    }
    bool ok(void) const { return _ok; }
    int main(void)
    {
        uint32_t addr;
        pxfe_errno e;
        if (opts.outbound &&
            pxfe_iputils::hostname_to_ipaddr(opts.hostname.c_str(),
                                             &addr) == false)
            return 1;
        if (opts.outbound)
        {
            net_fd = new pxfe_tcp_stream_socket;
            if (net_fd->init(&e) == false)
            {
                std::cerr << e.Format() << std::endl;
                return 1;
            }
            if (opts.verbose)
                fprintf(stderr, "connecting...");
            if (net_fd->connect(addr, opts.port_number, &e) == false)
            {
                std::cerr << e.Format() << std::endl;
                return 1;
            }
            if (opts.verbose)
                fprintf(stderr, "success\n");
        }
        else
        {
            pxfe_tcp_stream_socket listen;
            if (listen.init(opts.port_number,true,&e) == false)
            {
                std::cerr << e.Format() << std::endl;
                return 1;
            }
            listen.listen();
            if (opts.verbose)
                fprintf(stderr, "listening...");
            do {
                net_fd = listen.accept(&e);
                if (e.e != 0)
                    std::cerr << e.Format() << std::endl;
            } while (net_fd == NULL);
            if (opts.verbose)
            {
                uint32_t addr = net_fd->get_peer_addr();
                std::cerr << "accepted from "
                          << (int) ((addr >> 24) & 0xFF) << "."
                          << (int) ((addr >> 16) & 0xFF) << "."
                          << (int) ((addr >>  8) & 0xFF) << "."
                          << (int) ((addr >>  0) & 0xFF)
                          << std::endl;
            }
            // listen socket closed here, because we
            // no longer need it.
        }
        start_time.getNow();
        ticker.start(0, 500000);
        pxfe_poll p;

        p.set(net_fd->getFd(), POLLIN);
        p.set(ticker.fd(), POLLIN);

        while (1)
        {
            if (opts.input_set)
                p.set(opts.input_fd, POLLIN);
            else
                p.set(opts.input_fd, 0);
            p.poll(1000);
            if (p.rget(net_fd->getFd()) & POLLIN)
                if (!handle_net_fd())
                    break;
            if (opts.input_set && p.rget(opts.input_fd) & POLLIN)
                if (!handle_input_fd())
                    break;
            if (p.rget(ticker.fd()) & POLLIN)
                handle_tick();
        }
        ticker.pause();
        if (opts.verbose || opts.stats_at_end)
            print_stats(true);
        return 0;
    }
private:
    bool handle_net_fd(void)
    {
        pxfe_errno e;
        if (net_fd->recv(buffer, &e) == false)
        {
            std::cerr << e.Format() << std::endl;
            return false;
        }
        if (buffer.length() == 0)
            return false;
        bytes_received += buffer.length();
        if (opts.output_set)
        {
            int cc = -1;
            do  {
                cc = buffer.write(opts.output_fd);
                if (cc == buffer.length())
                    break;
                if (cc < 0)
                {
                    int e = errno;
                    char * err = strerror(e);
                    fprintf(stderr, "write failed: %d: %s\n", e, err);
                    return false;
                }
                else if (cc == 0)
                {
                    fprintf(stderr, "write returned zero\n");
                    return false;
                }
                else
                    // remove the bytes already written
                    // and go around again to get the rest.
                    buffer.erase(0,cc);
            } while (true);
        }
        return true;
    }
    bool handle_input_fd(void)
    {
        pxfe_errno e;
        int cc = buffer.read(opts.input_fd,
                             pxfe_tcp_stream_socket::MAX_MSG_LEN, &e);
        if (cc < 0)
        {
            std::cerr << e.Format() << std::endl;
            return false;
        }
        else if (cc == 0)
            return false;
        else
        {
            if (net_fd->send(buffer, &e) == false)
            {
                std::cerr << e.Format() << std::endl;
                return false;
            }
            bytes_sent += buffer.length();
        }
        return true;
    }
    void handle_tick(void)
    {
        ticker.doread();
        if (opts.verbose)
            print_stats(/*final*/false);
    }
    void print_stats(bool final)
    {
        pxfe_timeval now, diff;
        uint64_t total = bytes_sent + bytes_received;
        now.getNow();
        diff = now - start_time;
        float t = diff.usecs() / 1000000.0;
        if (t == 0.0)
            t = 99999.0;
        float bytes_per_sec = (float) total / t;
        float bits_per_sec = bytes_per_sec * 8.0;
        fprintf(stderr, "\r%" PRIu64 " in %u.%06u s "
               "(%.0f Bps %.0f bps)",
               total,
               (unsigned int) diff.tv_sec,
               (unsigned int) diff.tv_usec,
               bytes_per_sec, bits_per_sec);
        if (final)
            fprintf(stderr, "\n");
    }
};

extern "C" int
i2_main(int argc, char ** argv)
{
    i2_program  i2(argc, argv);
    if (i2.ok() == false)
        return 1;
    return i2.main();
}
<commit_msg>FINALLY figured out that i2 lockup -- poll's got some quirks!<commit_after>
// todo:
//   -Ir
//   -Iz
//   -zr
//   -zt
//   -f

#include "i2_options.h"
#include "posix_fe.h"
#include <netdb.h>

class i2_program
{
    i2_options  opts;
    bool _ok;
    pxfe_tcp_stream_socket * net_fd;
    uint64_t bytes_sent;
    uint64_t bytes_received;
    pxfe_timeval  start_time;
    pxfe_string  buffer;
    pxfe_ticker  ticker;
public:
    i2_program(int argc, char ** argv)
        : opts(argc, argv), _ok(false)
    {
        _ok = opts.ok;
        net_fd = NULL;
        bytes_received = 0;
        bytes_sent = 0;
    }
    ~i2_program(void)
    {
        // opts destructor will close the input & output fds.
        if (net_fd)
            delete net_fd;
    }
    bool ok(void) const { return _ok; }
    int main(void)
    {
        uint32_t addr;
        pxfe_errno e;
        if (opts.outbound &&
            pxfe_iputils::hostname_to_ipaddr(opts.hostname.c_str(),
                                             &addr) == false)
            return 1;
        if (opts.outbound)
        {
            net_fd = new pxfe_tcp_stream_socket;
            if (net_fd->init(&e) == false)
            {
                std::cerr << e.Format() << std::endl;
                return 1;
            }
            if (opts.verbose)
                fprintf(stderr, "connecting...");
            if (net_fd->connect(addr, opts.port_number, &e) == false)
            {
                std::cerr << e.Format() << std::endl;
                return 1;
            }
            if (opts.verbose)
                fprintf(stderr, "success\n");
        }
        else
        {
            pxfe_tcp_stream_socket listen;
            if (listen.init(opts.port_number,true,&e) == false)
            {
                std::cerr << e.Format() << std::endl;
                return 1;
            }
            listen.listen();
            if (opts.verbose)
                fprintf(stderr, "listening...");
            do {
                net_fd = listen.accept(&e);
                if (e.e != 0)
                    std::cerr << e.Format() << std::endl;
            } while (net_fd == NULL);
            if (opts.verbose)
            {
                uint32_t addr = net_fd->get_peer_addr();
                std::cerr << "accepted from "
                          << (int) ((addr >> 24) & 0xFF) << "."
                          << (int) ((addr >> 16) & 0xFF) << "."
                          << (int) ((addr >>  8) & 0xFF) << "."
                          << (int) ((addr >>  0) & 0xFF)
                          << std::endl;
            }
            // listen socket closed here, because we
            // no longer need it.
        }
        start_time.getNow();
        ticker.start(0, 500000);
        pxfe_poll p;

        p.set(net_fd->getFd(), POLLIN);
        p.set(ticker.fd(), POLLIN);

#define POLLERRS (POLLERR | POLLHUP | POLLNVAL)

        while (1)
        {
            int evt;

            if (opts.input_set)
                p.set(opts.input_fd, POLLIN);
            else
                p.set(opts.input_fd, 0);

            p.poll(1000);
            evt = p.rget(net_fd->getFd());

            if (evt & POLLIN)
            {
                if (!handle_net_fd())
                    break;
            }
            else if (evt & POLLERRS)
                break;

            if (opts.input_set)
            {
                evt = p.rget(opts.input_fd);
                if (evt & POLLIN)
                {
                    if (!handle_input_fd())
                        break;
                }
                else if (evt & POLLERRS)
                    break;
            }

            if (p.rget(ticker.fd()) & POLLIN)
                handle_tick();
        }
        ticker.pause();
        if (opts.verbose || opts.stats_at_end)
            print_stats(true);
        return 0;
    }
private:
    bool handle_net_fd(void)
    {
        pxfe_errno e;
        if (net_fd->recv(buffer, &e) == false)
        {
            std::cerr << e.Format() << std::endl;
            return false;
        }
        if (buffer.length() == 0)
            return false;
        bytes_received += buffer.length();
        if (opts.output_set)
        {
            int cc = -1;
            do  {
                cc = buffer.write(opts.output_fd);
                if (cc == buffer.length())
                    break;
                if (cc < 0)
                {
                    int e = errno;
                    char * err = strerror(e);
                    fprintf(stderr, "write failed: %d: %s\n", e, err);
                    return false;
                }
                else if (cc == 0)
                {
                    fprintf(stderr, "write returned zero\n");
                    return false;
                }
                else
                    // remove the bytes already written
                    // and go around again to get the rest.
                    buffer.erase(0,cc);
            } while (true);
        }
        return true;
    }
    bool handle_input_fd(void)
    {
        pxfe_errno e;
        int cc = buffer.read(opts.input_fd,
                             pxfe_tcp_stream_socket::MAX_MSG_LEN, &e);
        if (cc < 0)
        {
            std::cerr << e.Format() << std::endl;
            return false;
        }
        else if (cc == 0)
            return false;
        else
        {
            if (net_fd->send(buffer, &e) == false)
            {
                std::cerr << e.Format() << std::endl;
                return false;
            }
            bytes_sent += buffer.length();
        }
        return true;
    }
    void handle_tick(void)
    {
        ticker.doread();
        if (opts.verbose)
            print_stats(/*final*/false);
    }
    void print_stats(bool final)
    {
        pxfe_timeval now, diff;
        uint64_t total = bytes_sent + bytes_received;
        now.getNow();
        diff = now - start_time;
        float t = diff.usecs() / 1000000.0;
        if (t == 0.0)
            t = 99999.0;
        float bytes_per_sec = (float) total / t;
        float bits_per_sec = bytes_per_sec * 8.0;
        fprintf(stderr, "\r%" PRIu64 " in %u.%06u s "
               "(%.0f Bps %.0f bps)",
               total,
               (unsigned int) diff.tv_sec,
               (unsigned int) diff.tv_usec,
               bytes_per_sec, bits_per_sec);
        if (final)
            fprintf(stderr, "\n");
    }
};

extern "C" int
i2_main(int argc, char ** argv)
{
    i2_program  i2(argc, argv);
    if (i2.ok() == false)
        return 1;
    return i2.main();
}
<|endoftext|>


<commit_before>/*-----------------------------------------------------------------------------
 This source file is a part of Hopsan

 Copyright (c) 2009 to present year, Hopsan Group

    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, see <http://www.gnu.org/licenses/>.

 For license details and information about the Hopsan Group see the files
 GPLv3 and HOPSANGROUP in the Hopsan source code root directory

 For author and contributor information see the AUTHORS file
-----------------------------------------------------------------------------*/

//!
//! @file   FirstOrderTransferFunction.cc
//! @author Björn Eriksson <bjorn.eriksson@liu.se>
//! @date   2010-01-23
//!
//! @brief Contains the Core First Order Transfer Function class
//!
//$Id$

//#include <iostream>
#include <algorithm>
#include "ComponentUtilities/FirstOrderTransferFunction.h"

using namespace hopsan;

//! @class hopsan::FirstOrderTransferFunction
//! @ingroup ComponentUtilityClasses
//! @brief The FirstOrderTransferFunction class implements a first order time discrete transfer function using bilinear transform
//!
//! To declare a filter like \f[G=\frac{a_1 s + a_0}{b_1 s + b_0}\f]
//! the syntax is filter.setNumDen(num, den)
//! where \f$num[0]=a_0\f$, \f$num[1]=a_1\f$
//! and \f$den[0]=b_0\f$, \f$den[1]=b_1\f$
//!

void FirstOrderTransferFunction::initialize(double timestep, double num[2], double den[2], double u0, double y0, double min, double max)
{
    mIsSaturated = false;
    mMin = min;
    mMax = max;
    mValue = y0;
    mDelayedU = u0;
    mDelayedY = std::max(std::min(y0, mMax), mMin);
    mTimeStep = timestep;
    setNumDen(num, den);
    setBackupLength(1);
}

void FirstOrderTransferFunction::setMinMax(double min, double max)
{
    mMin = min;
    mMax = max;
}

void FirstOrderTransferFunction::setNum(double num[2])
{
    mCoeffU[0] = num[0]*mTimeStep-2.0*num[1];
    mCoeffU[1] = num[0]*mTimeStep+2.0*num[1];
    //std::cout << "DiscNum: " << mCoeffU[1] << " " << mCoeffU[0] << std::endl;

}

void FirstOrderTransferFunction::setDen(double den[2])
{
    mCoeffY[0] = den[0]*mTimeStep-2.0*den[1];
    mCoeffY[1] = den[0]*mTimeStep+2.0*den[1];
    //std::cout << "DiscDen: " << mCoeffY[1] << " " << mCoeffY[0] << std::endl;
}

void FirstOrderTransferFunction::setNumDen(double num[2], double den[2])
{
    mCoeffU[0] = num[0]*mTimeStep-2.0*num[1];
    mCoeffU[1] = num[0]*mTimeStep+2.0*num[1];

    mCoeffY[0] = den[0]*mTimeStep-2.0*den[1];
    mCoeffY[1] = den[0]*mTimeStep+2.0*den[1];
}

//! @brief Restore the backup at teh given step
//! @param[in] nSteps The number of steps backwards in time to restore (1=last step) must be >=1
//! @note The function assumes that the backup buffer has been allocated
//! @see setBackupLength
void FirstOrderTransferFunction::restoreBackup(size_t nSteps)
{
    if (nSteps > 0)
    {
        nSteps -= 1;
    }
    mDelayedU = mBackupU.getIdx(nSteps);
    mDelayedY = mBackupY.getIdx(nSteps);
}

//! @brief Pushes a backup of transfere function states into the backup buffer
//! @note Only the delayed states are backed up, not the current value or the coefficients
//! @todo Maybe we should backup more things like coefficients, saturated flag, current value, but that will take time at every timestep
void FirstOrderTransferFunction::backup()
{
    mBackupU.update(mDelayedU);
    mBackupY.update(mDelayedY);
}

void FirstOrderTransferFunction::initializeValues(double u0, double y0)
{
    mDelayedU = u0;
    mDelayedY = y0;
    mValue = y0;
}

//! @brief Setup the number of backup steps to remember (size of the backup buffer)
//! @param[in] nSteps The number of steps to remember
void FirstOrderTransferFunction::setBackupLength(size_t nStep)
{
    mBackupU.initialize(nStep, mDelayedU);
    mBackupY.initialize(nStep, mDelayedY);
}

//! @brief Updates the transfere function
//! @param[in] u The new input value
//! @returns The current transfere function ouput value after update
double FirstOrderTransferFunction::update(double u)
{
    //Filter equation
    //Bilinear transform is used

    mValue = 1.0/mCoeffY[1]*(mCoeffU[1]*u + mCoeffU[0]*mDelayedU - mCoeffY[0]*mDelayedY);

//    if (mValue >= mMax)
//    {
//        mDelayY = mMax;
//        mDelayU = mMax;
//        mValue = mMax;
//        mIsSaturated = true;
//    }
//    else if (mValue <= mMin)
//    {
//        mDelayY = mMin;
//        mDelayU = mMin;
//        mValue = mMin;
//        mIsSaturated = true;
//    }
//    else
//    {
//        mDelayY = mValue;
//        mDelayU = u;
//        mIsSaturated = false;
//    }

    if (mValue >= mMax)
    {
        mValue = mMax;
        mIsSaturated = true;
    }
    else if (mValue <= mMin)
    {
        mValue = mMin;
        mIsSaturated = true;
    }
    else
    {
        mIsSaturated = false;
    }

    mDelayedY = mValue;
    mDelayedU = u;

    return mValue;
}

//! @brief Make a backup of states and then calls update
//! @param[in] u The new input value
//! @returns The current transfere function ouput value after update
double FirstOrderTransferFunction::updateWithBackup(double u)
{
    backup();
    return update(u);
}

//! @brief Read current transfere function output value
//! @return The filtered actual value.
double FirstOrderTransferFunction::value() const
{
    return mValue;
}

double FirstOrderTransferFunction::delayedU() const
{
    return mDelayedU;
}

double FirstOrderTransferFunction::delayedY() const
{
    return mDelayedY;
}

//! @brief Check if the transfer function is saturated (has reached the set limits)
//! @returns true or false
bool FirstOrderTransferFunction::isSaturated() const
{
    return mIsSaturated;
}

void FirstOrderTransferFunctionVariable::initialize(double *pTimestep, double num[2], double den[2], double u0, double y0, double min, double max)
{
    mMin = min;
    mMax = max;
    mValue = y0;
    mDelayU = u0;
    mDelayY = std::max(std::min(y0, mMax), mMin);
    mpTimeStep = pTimestep;
    mPrevTimeStep = *pTimestep;
    mNum[0] = num[0];
    mNum[1] = num[1];
    mDen[0] = den[0];
    mDen[1] = den[1];

    recalculateCoefficients();
}

void FirstOrderTransferFunctionVariable::setMinMax(double min, double max)
{
    mMin = min;
    mMax = max;
}

void FirstOrderTransferFunctionVariable::setNum(double num[2])
{
    mNum[0] = num[0];
    mNum[1] = num[1];
    recalculateCoefficients();
}

void FirstOrderTransferFunctionVariable::setDen(double den[2])
{
    mDen[0] = den[0];
    mDen[1] = den[1];
    recalculateCoefficients();
}

void FirstOrderTransferFunctionVariable::setNumDen(double num[2], double den[2])
{
    mNum[0] = num[0];
    mNum[1] = num[1];
    mDen[0] = den[0];
    mDen[1] = den[1];
    recalculateCoefficients();
}

void FirstOrderTransferFunctionVariable::recalculateCoefficients()
{
    mCoeffU[0] = mNum[0]*(*mpTimeStep)-2.0*mNum[1];
    mCoeffU[1] = mNum[0]*(*mpTimeStep)+2.0*mNum[1];

    mCoeffY[0] = mDen[0]*(*mpTimeStep)-2.0*mDen[1];
    mCoeffY[1] = mDen[0]*(*mpTimeStep)+2.0*mDen[1];
}

void FirstOrderTransferFunctionVariable::initializeValues(double u0, double y0)
{
    mDelayU = u0;
    mDelayY = y0;
    mValue = y0;
}

double FirstOrderTransferFunctionVariable::update(double u)
{
    //Filter equation
    //Bilinear transform is used

    if((*mpTimeStep) != mPrevTimeStep)
    {
        mPrevTimeStep = (*mpTimeStep);
        recalculateCoefficients();
    }

    mValue = 1.0/mCoeffY[1]*(mCoeffU[1]*u + mCoeffU[0]*mDelayU - mCoeffY[0]*mDelayY);

    if (mValue > mMax)
    {
        mDelayY = mMax;
        mDelayU = mMax;
        mValue = mMax;
    }
    else if (mValue < mMin)
    {
        mDelayY = mMin;
        mDelayU = mMin;
        mValue = mMin;
    }
    else
    {
        mDelayY = mValue;
        mDelayU = u;
    }

    return mValue;
}

//! Read current filter output value
//! @return The filtered actual value.
double FirstOrderTransferFunctionVariable::value()
{
    return mValue;
}
<commit_msg>Fixed spelling mistakes<commit_after>/*-----------------------------------------------------------------------------
 This source file is a part of Hopsan

 Copyright (c) 2009 to present year, Hopsan Group

    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, see <http://www.gnu.org/licenses/>.

 For license details and information about the Hopsan Group see the files
 GPLv3 and HOPSANGROUP in the Hopsan source code root directory

 For author and contributor information see the AUTHORS file
-----------------------------------------------------------------------------*/

//!
//! @file   FirstOrderTransferFunction.cc
//! @author Björn Eriksson <bjorn.eriksson@liu.se>
//! @date   2010-01-23
//!
//! @brief Contains the Core First Order Transfer Function class
//!
//$Id$

//#include <iostream>
#include <algorithm>
#include "ComponentUtilities/FirstOrderTransferFunction.h"

using namespace hopsan;

//! @class hopsan::FirstOrderTransferFunction
//! @ingroup ComponentUtilityClasses
//! @brief The FirstOrderTransferFunction class implements a first order time discrete transfer function using bilinear transform
//!
//! To declare a filter like \f[G=\frac{a_1 s + a_0}{b_1 s + b_0}\f]
//! the syntax is filter.setNumDen(num, den)
//! where \f$num[0]=a_0\f$, \f$num[1]=a_1\f$
//! and \f$den[0]=b_0\f$, \f$den[1]=b_1\f$
//!

void FirstOrderTransferFunction::initialize(double timestep, double num[2], double den[2], double u0, double y0, double min, double max)
{
    mIsSaturated = false;
    mMin = min;
    mMax = max;
    mValue = y0;
    mDelayedU = u0;
    mDelayedY = std::max(std::min(y0, mMax), mMin);
    mTimeStep = timestep;
    setNumDen(num, den);
    setBackupLength(1);
}

void FirstOrderTransferFunction::setMinMax(double min, double max)
{
    mMin = min;
    mMax = max;
}

void FirstOrderTransferFunction::setNum(double num[2])
{
    mCoeffU[0] = num[0]*mTimeStep-2.0*num[1];
    mCoeffU[1] = num[0]*mTimeStep+2.0*num[1];
    //std::cout << "DiscNum: " << mCoeffU[1] << " " << mCoeffU[0] << std::endl;

}

void FirstOrderTransferFunction::setDen(double den[2])
{
    mCoeffY[0] = den[0]*mTimeStep-2.0*den[1];
    mCoeffY[1] = den[0]*mTimeStep+2.0*den[1];
    //std::cout << "DiscDen: " << mCoeffY[1] << " " << mCoeffY[0] << std::endl;
}

void FirstOrderTransferFunction::setNumDen(double num[2], double den[2])
{
    mCoeffU[0] = num[0]*mTimeStep-2.0*num[1];
    mCoeffU[1] = num[0]*mTimeStep+2.0*num[1];

    mCoeffY[0] = den[0]*mTimeStep-2.0*den[1];
    mCoeffY[1] = den[0]*mTimeStep+2.0*den[1];
}

//! @brief Restore the backup at the given step
//! @param[in] nSteps The number of steps backwards in time to restore (1=last step) must be >=1
//! @note The function assumes that the backup buffer has been allocated
//! @see setBackupLength
void FirstOrderTransferFunction::restoreBackup(size_t nSteps)
{
    if (nSteps > 0)
    {
        nSteps -= 1;
    }
    mDelayedU = mBackupU.getIdx(nSteps);
    mDelayedY = mBackupY.getIdx(nSteps);
}

//! @brief Pushes a backup of transfer function states into the backup buffer
//! @note Only the delayed states are backed up, not the current value or the coefficients
//! @todo Maybe we should backup more things like coefficients, saturated flag, current value, but that will take time at every timestep
void FirstOrderTransferFunction::backup()
{
    mBackupU.update(mDelayedU);
    mBackupY.update(mDelayedY);
}

void FirstOrderTransferFunction::initializeValues(double u0, double y0)
{
    mDelayedU = u0;
    mDelayedY = y0;
    mValue = y0;
}

//! @brief Setup the number of backup steps to remember (size of the backup buffer)
//! @param[in] nSteps The number of steps to remember
void FirstOrderTransferFunction::setBackupLength(size_t nStep)
{
    mBackupU.initialize(nStep, mDelayedU);
    mBackupY.initialize(nStep, mDelayedY);
}

//! @brief Updates the transfer function
//! @param[in] u The new input value
//! @returns The current transfer function output value after update
double FirstOrderTransferFunction::update(double u)
{
    //Filter equation
    //Bilinear transform is used

    mValue = 1.0/mCoeffY[1]*(mCoeffU[1]*u + mCoeffU[0]*mDelayedU - mCoeffY[0]*mDelayedY);

//    if (mValue >= mMax)
//    {
//        mDelayY = mMax;
//        mDelayU = mMax;
//        mValue = mMax;
//        mIsSaturated = true;
//    }
//    else if (mValue <= mMin)
//    {
//        mDelayY = mMin;
//        mDelayU = mMin;
//        mValue = mMin;
//        mIsSaturated = true;
//    }
//    else
//    {
//        mDelayY = mValue;
//        mDelayU = u;
//        mIsSaturated = false;
//    }

    if (mValue >= mMax)
    {
        mValue = mMax;
        mIsSaturated = true;
    }
    else if (mValue <= mMin)
    {
        mValue = mMin;
        mIsSaturated = true;
    }
    else
    {
        mIsSaturated = false;
    }

    mDelayedY = mValue;
    mDelayedU = u;

    return mValue;
}

//! @brief Make a backup of states and then calls update
//! @param[in] u The new input value
//! @returns The current transfer function output value after update
double FirstOrderTransferFunction::updateWithBackup(double u)
{
    backup();
    return update(u);
}

//! @brief Read current transfer function output value
//! @return The filtered actual value.
double FirstOrderTransferFunction::value() const
{
    return mValue;
}

double FirstOrderTransferFunction::delayedU() const
{
    return mDelayedU;
}

double FirstOrderTransferFunction::delayedY() const
{
    return mDelayedY;
}

//! @brief Check if the transfer function is saturated (has reached the set limits)
//! @returns true or false
bool FirstOrderTransferFunction::isSaturated() const
{
    return mIsSaturated;
}

void FirstOrderTransferFunctionVariable::initialize(double *pTimestep, double num[2], double den[2], double u0, double y0, double min, double max)
{
    mMin = min;
    mMax = max;
    mValue = y0;
    mDelayU = u0;
    mDelayY = std::max(std::min(y0, mMax), mMin);
    mpTimeStep = pTimestep;
    mPrevTimeStep = *pTimestep;
    mNum[0] = num[0];
    mNum[1] = num[1];
    mDen[0] = den[0];
    mDen[1] = den[1];

    recalculateCoefficients();
}

void FirstOrderTransferFunctionVariable::setMinMax(double min, double max)
{
    mMin = min;
    mMax = max;
}

void FirstOrderTransferFunctionVariable::setNum(double num[2])
{
    mNum[0] = num[0];
    mNum[1] = num[1];
    recalculateCoefficients();
}

void FirstOrderTransferFunctionVariable::setDen(double den[2])
{
    mDen[0] = den[0];
    mDen[1] = den[1];
    recalculateCoefficients();
}

void FirstOrderTransferFunctionVariable::setNumDen(double num[2], double den[2])
{
    mNum[0] = num[0];
    mNum[1] = num[1];
    mDen[0] = den[0];
    mDen[1] = den[1];
    recalculateCoefficients();
}

void FirstOrderTransferFunctionVariable::recalculateCoefficients()
{
    mCoeffU[0] = mNum[0]*(*mpTimeStep)-2.0*mNum[1];
    mCoeffU[1] = mNum[0]*(*mpTimeStep)+2.0*mNum[1];

    mCoeffY[0] = mDen[0]*(*mpTimeStep)-2.0*mDen[1];
    mCoeffY[1] = mDen[0]*(*mpTimeStep)+2.0*mDen[1];
}

void FirstOrderTransferFunctionVariable::initializeValues(double u0, double y0)
{
    mDelayU = u0;
    mDelayY = y0;
    mValue = y0;
}

double FirstOrderTransferFunctionVariable::update(double u)
{
    //Filter equation
    //Bilinear transform is used

    if((*mpTimeStep) != mPrevTimeStep)
    {
        mPrevTimeStep = (*mpTimeStep);
        recalculateCoefficients();
    }

    mValue = 1.0/mCoeffY[1]*(mCoeffU[1]*u + mCoeffU[0]*mDelayU - mCoeffY[0]*mDelayY);

    if (mValue > mMax)
    {
        mDelayY = mMax;
        mDelayU = mMax;
        mValue = mMax;
    }
    else if (mValue < mMin)
    {
        mDelayY = mMin;
        mDelayU = mMin;
        mValue = mMin;
    }
    else
    {
        mDelayY = mValue;
        mDelayU = u;
    }

    return mValue;
}

//! Read current filter output value
//! @return The filtered actual value.
double FirstOrderTransferFunctionVariable::value()
{
    return mValue;
}
<|endoftext|>