<commit_before>// Hossein Moein
// September 12, 2017
// Copyright (C) 2018-2019 Hossein Moein
// Distributed under the BSD Software License (see file License)

#include "DataFrame.h"
#include <tuple>

// ----------------------------------------------------------------------------

namespace hmdf
{

// ----------------------------------------------------------------------------

template<typename TS, typename HETERO>
template<typename RHS_T, typename ... types>
StdDataFrame<TS> DataFrame<TS, HETERO>::
join_by_index (const RHS_T &rhs, join_policy mp) const  {

    static_assert(std::is_base_of<StdDataFrame<TS>, RHS_T>::value or
                      std::is_base_of<DataFrameView<TS>, RHS_T>::value,
                  "The rhs argument to join_by_index() can only be "
                  "StdDataFrame<TS> or DataFrameView<TS>");

    switch(mp)  {
        case join_policy::inner_join:
            return (index_inner_join_
                        <decltype(*this), decltype(rhs), types ...>
                            (*this, rhs));
            break;
        case join_policy::left_join:
            return (index_left_join_
                        <decltype(*this), decltype(rhs), types ...>
                            (*this, rhs));
            break;
        case join_policy::right_join:
            return (index_right_join_
                        <decltype(*this), decltype(rhs), types ...>
                            (*this, rhs));
            break;
        case join_policy::left_right_join:
        default:
            return (index_left_right_join_
                        <decltype(*this), decltype(rhs), types ...>
                            (*this, rhs));
            break;
    }
}

// ----------------------------------------------------------------------------

template<typename TS, typename HETERO>
template<typename RHS_T, typename T, typename ... types>
StdDataFrame<TS> DataFrame<TS, HETERO>::
join_by_column (const RHS_T &rhs,
                 const char *col_name,
                 join_policy mp) const {

    static_assert(std::is_base_of<StdDataFrame<TS>, RHS_T>::value or
                      std::is_base_of<DataFrameView<TS>, RHS_T>::value,
                  "The rhs argument to join_by_column() can only be "
                  "StdDataFrame<TS> or DataFrameView<TS>");

    switch(mp)  {
        case join_policy::inner_join:
            return (column_inner_join_
                        <decltype(*this), decltype(rhs), T, types ...>
                            (col_name, *this, rhs));
            break;
        case join_policy::left_join:
            return (column_left_join_
                        <decltype(*this), decltype(rhs), T, types ...>
                            (col_name, *this, rhs));
            break;
        case join_policy::right_join:
            return (column_right_join_
                        <decltype(*this), decltype(rhs), T, types ...>
                            (col_name, *this, rhs));
            break;
        case join_policy::left_right_join:
        default:
            return (column_left_right_join_
                        <decltype(*this), decltype(rhs), T, types ...>
                            (col_name, *this, rhs));
            break;
    }
}

// ----------------------------------------------------------------------------

template<typename TS, typename HETERO>
template<typename LHS_T, typename RHS_T, typename ... types>
StdDataFrame<TS> DataFrame<TS, HETERO>::
join_helper_(const LHS_T &lhs,
             const RHS_T &rhs,
             const IndexIdxVector &joined_index_idx)  {

    StdDataFrame<TS>    result;
    std::vector<TS>     result_index;

    // Load the index
    result_index.reserve(joined_index_idx.size());
    for (const auto &citer : joined_index_idx)  {
        const size_type left_i = std::get<0>(citer);

        result_index.push_back(
            left_i != std::numeric_limits<size_type>::max()
                ? lhs.indices_[left_i] : rhs.indices_[std::get<1>(citer)]);
    }
    result.load_index(std::move(result_index));

    // Load the common and lhs columns
    for (auto &iter : lhs.data_tb_)  {
        auto    rhs_citer = rhs.data_tb_.find(iter.first);

        // Common column between two frames
        if (rhs_citer != rhs.data_tb_.end())  {
            index_join_functor_common_<types ...> functor (iter.first.c_str(),
                                                           rhs,
                                                           joined_index_idx,
                                                           result);

            lhs.data_[iter.second].change(functor);

        }
        else  {  // lhs only column
            // 0 = Left
            index_join_functor_oneside_<0, types ...> functor (
                iter.first.c_str(),
                joined_index_idx,
                result);

            lhs.data_[iter.second].change(functor);
        }
    }

    // Load the rhs columns
    for (auto &iter : rhs.data_tb_)  {
        auto    lhs_citer = lhs.data_tb_.find(iter.first);

        if (lhs_citer == lhs.data_tb_.end())  {  // rhs only column
            // 1 = Right
            index_join_functor_oneside_<1, types ...> functor (
                iter.first.c_str(),
                joined_index_idx,
                result);

            rhs.data_[iter.second].change(functor);
        }
    }

    return(result);
}

// ----------------------------------------------------------------------------

template<typename TS, typename HETERO>
template<typename LHS_T, typename RHS_T, typename ... types>
StdDataFrame<TS> DataFrame<TS, HETERO>::
index_inner_join_(const LHS_T &lhs, const RHS_T &rhs)  {

    size_type       lhs_current = 0;
    const size_type lhs_end = lhs.indices_.size();
    size_type       rhs_current = 0;
    const size_type rhs_end = rhs.indices_.size();

    IndexIdxVector  joined_index_idx;

    joined_index_idx.reserve(std::min(lhs_end, rhs_end));
    while (lhs_current != lhs_end && rhs_current != rhs_end) {
        if (lhs.indices_[lhs_current] < rhs.indices_[rhs_current])
            lhs_current += 1;
        else  {
            if (lhs.indices_[lhs_current] == rhs.indices_[rhs_current])
                joined_index_idx.emplace_back(lhs_current++, rhs_current);
            rhs_current += 1;
        }
    }

    return (join_helper_<LHS_T, RHS_T, types ...>(lhs, rhs, joined_index_idx));
}

// ----------------------------------------------------------------------------

template<typename TS, typename HETERO>
template<typename LHS_T, typename RHS_T, typename ... types>
StdDataFrame<TS> DataFrame<TS, HETERO>::
index_left_join_(const LHS_T &lhs, const RHS_T &rhs)  {

    size_type       lhs_current = 0;
    const size_type lhs_end = lhs.indices_.size();
    size_type       rhs_current = 0;
    const size_type rhs_end = rhs.indices_.size();

    IndexIdxVector  joined_index_idx;

    joined_index_idx.reserve(lhs_end);
    while (lhs_current != lhs_end && rhs_current != rhs_end) {
        if (lhs.indices_[lhs_current] < rhs.indices_[rhs_current])
            joined_index_idx.emplace_back(
                lhs_current++,
                std::numeric_limits<size_type>::max());
        else  {
            if (lhs.indices_[lhs_current] == rhs.indices_[rhs_current])
                joined_index_idx.emplace_back(lhs_current++, rhs_current);
            rhs_current += 1;
        }
    }

    return (join_helper_<LHS_T, RHS_T, types ...>(lhs, rhs, joined_index_idx));
}

// ----------------------------------------------------------------------------

template<typename TS, typename HETERO>
template<typename LHS_T, typename RHS_T, typename ... types>
StdDataFrame<TS> DataFrame<TS, HETERO>::
index_right_join_(const LHS_T &lhs, const RHS_T &rhs)  {

    size_type       lhs_current = 0;
    const size_type lhs_end = lhs.indices_.size();
    size_type       rhs_current = 0;
    const size_type rhs_end = rhs.indices_.size();

    IndexIdxVector  joined_index_idx;

    joined_index_idx.reserve(rhs_end);
    while (lhs_current != lhs_end && rhs_current != rhs_end) {
        if (lhs.indices_[lhs_current] < rhs.indices_[rhs_current])  {
            joined_index_idx.emplace_back(
                std::numeric_limits<size_type>::max(),
                rhs_current++);
            lhs_current += 1;
        }
        else  {
            if (lhs.indices_[lhs_current] == rhs.indices_[rhs_current])
                joined_index_idx.emplace_back(lhs_current++, rhs_current++);
            else
                joined_index_idx.emplace_back(
                    std::numeric_limits<size_type>::max(),
                    rhs_current++);
        }
    }

    return (join_helper_<LHS_T, RHS_T, types ...>(lhs, rhs, joined_index_idx));
}

// ----------------------------------------------------------------------------

template<typename TS, typename HETERO>
template<typename LHS_T, typename RHS_T, typename ... types>
StdDataFrame<TS> DataFrame<TS, HETERO>::
index_left_right_join_(const LHS_T &lhs, const RHS_T &rhs)  {

    StdDataFrame<TS>    result;

    return(result);
}

// ----------------------------------------------------------------------------

template<typename TS, typename HETERO>
template<typename LHS_T, typename RHS_T, typename COL_T, typename ... types>
StdDataFrame<TS> DataFrame<TS, HETERO>::
column_inner_join_(const char *col_name, const LHS_T &lhs, const RHS_T &rhs)  {

    StdDataFrame<TS>    result;

    return(result);
}

// ----------------------------------------------------------------------------

template<typename TS, typename HETERO>
template<typename LHS_T, typename RHS_T, typename COL_T, typename ... types>
StdDataFrame<TS> DataFrame<TS, HETERO>::
column_left_join_(const char *col_name, const LHS_T &lhs, const RHS_T &rhs)  {

    StdDataFrame<TS>    result;

    return(result);
}

// ----------------------------------------------------------------------------

template<typename TS, typename HETERO>
template<typename LHS_T, typename RHS_T, typename COL_T, typename ... types>
StdDataFrame<TS> DataFrame<TS, HETERO>::
column_right_join_(const char *col_name, const LHS_T &lhs, const RHS_T &rhs)  {

    StdDataFrame<TS>    result;

    return(result);
}

// ----------------------------------------------------------------------------

template<typename TS, typename HETERO>
template<typename LHS_T, typename RHS_T, typename COL_T, typename ... types>
StdDataFrame<TS> DataFrame<TS, HETERO>::
column_left_right_join_(const char *col_name,
                        const LHS_T &lhs,
                        const RHS_T &rhs)  {

    StdDataFrame<TS>    result;

    return(result);
}

} // namespace hmdf

// ----------------------------------------------------------------------------

// Local Variables:
// mode:C++
// tab-width:4
// c-basic-offset:4
// End:
<commit_msg>Small correction<commit_after>// Hossein Moein
// September 12, 2017
// Copyright (C) 2018-2019 Hossein Moein
// Distributed under the BSD Software License (see file License)

#include "DataFrame.h"
#include <tuple>

// ----------------------------------------------------------------------------

namespace hmdf
{

// ----------------------------------------------------------------------------

template<typename TS, typename HETERO>
template<typename RHS_T, typename ... types>
StdDataFrame<TS> DataFrame<TS, HETERO>::
join_by_index (const RHS_T &rhs, join_policy mp) const  {

    static_assert(std::is_base_of<StdDataFrame<TS>, RHS_T>::value or
                      std::is_base_of<DataFrameView<TS>, RHS_T>::value,
                  "The rhs argument to join_by_index() can only be "
                  "StdDataFrame<TS> or DataFrameView<TS>");

    switch(mp)  {
        case join_policy::inner_join:
            return (index_inner_join_
                        <decltype(*this), decltype(rhs), types ...>
                            (*this, rhs));
            break;
        case join_policy::left_join:
            return (index_left_join_
                        <decltype(*this), decltype(rhs), types ...>
                            (*this, rhs));
            break;
        case join_policy::right_join:
            return (index_right_join_
                        <decltype(*this), decltype(rhs), types ...>
                            (*this, rhs));
            break;
        case join_policy::left_right_join:
        default:
            return (index_left_right_join_
                        <decltype(*this), decltype(rhs), types ...>
                            (*this, rhs));
            break;
    }
}

// ----------------------------------------------------------------------------

template<typename TS, typename HETERO>
template<typename RHS_T, typename T, typename ... types>
StdDataFrame<TS> DataFrame<TS, HETERO>::
join_by_column (const RHS_T &rhs,
                 const char *col_name,
                 join_policy mp) const {

    static_assert(std::is_base_of<StdDataFrame<TS>, RHS_T>::value or
                      std::is_base_of<DataFrameView<TS>, RHS_T>::value,
                  "The rhs argument to join_by_column() can only be "
                  "StdDataFrame<TS> or DataFrameView<TS>");

    switch(mp)  {
        case join_policy::inner_join:
            return (column_inner_join_
                        <decltype(*this), decltype(rhs), T, types ...>
                            (col_name, *this, rhs));
            break;
        case join_policy::left_join:
            return (column_left_join_
                        <decltype(*this), decltype(rhs), T, types ...>
                            (col_name, *this, rhs));
            break;
        case join_policy::right_join:
            return (column_right_join_
                        <decltype(*this), decltype(rhs), T, types ...>
                            (col_name, *this, rhs));
            break;
        case join_policy::left_right_join:
        default:
            return (column_left_right_join_
                        <decltype(*this), decltype(rhs), T, types ...>
                            (col_name, *this, rhs));
            break;
    }
}

// ----------------------------------------------------------------------------

template<typename TS, typename HETERO>
template<typename LHS_T, typename RHS_T, typename ... types>
StdDataFrame<TS> DataFrame<TS, HETERO>::
join_helper_(const LHS_T &lhs,
             const RHS_T &rhs,
             const IndexIdxVector &joined_index_idx)  {

    StdDataFrame<TS>    result;
    std::vector<TS>     result_index;

    // Load the index
    result_index.reserve(joined_index_idx.size());
    for (const auto &citer : joined_index_idx)  {
        const size_type left_i = std::get<0>(citer);

        result_index.push_back(
            left_i != std::numeric_limits<size_type>::max()
                ? lhs.indices_[left_i] : rhs.indices_[std::get<1>(citer)]);
    }
    result.load_index(std::move(result_index));

    // Load the common and lhs columns
    for (auto &iter : lhs.data_tb_)  {
        auto    rhs_citer = rhs.data_tb_.find(iter.first);

        // Common column between two frames
        if (rhs_citer != rhs.data_tb_.end())  {
            index_join_functor_common_<types ...> functor (iter.first.c_str(),
                                                           rhs,
                                                           joined_index_idx,
                                                           result);

            lhs.data_[iter.second].change(functor);

        }
        else  {  // lhs only column
            // 0 = Left
            index_join_functor_oneside_<0, types ...> functor (
                iter.first.c_str(),
                joined_index_idx,
                result);

            lhs.data_[iter.second].change(functor);
        }
    }

    // Load the rhs columns
    for (auto &iter : rhs.data_tb_)  {
        auto    lhs_citer = lhs.data_tb_.find(iter.first);

        if (lhs_citer == lhs.data_tb_.end())  {  // rhs only column
            // 1 = Right
            index_join_functor_oneside_<1, types ...> functor (
                iter.first.c_str(),
                joined_index_idx,
                result);

            rhs.data_[iter.second].change(functor);
        }
    }

    return(result);
}

// ----------------------------------------------------------------------------

template<typename TS, typename HETERO>
template<typename LHS_T, typename RHS_T, typename ... types>
StdDataFrame<TS> DataFrame<TS, HETERO>::
index_inner_join_(const LHS_T &lhs, const RHS_T &rhs)  {

    size_type       lhs_current = 0;
    const size_type lhs_end = lhs.indices_.size();
    size_type       rhs_current = 0;
    const size_type rhs_end = rhs.indices_.size();

    IndexIdxVector  joined_index_idx;

    joined_index_idx.reserve(std::min(lhs_end, rhs_end));
    while (lhs_current != lhs_end && rhs_current != rhs_end) {
        if (lhs.indices_[lhs_current] < rhs.indices_[rhs_current])
            lhs_current += 1;
        else  {
            if (lhs.indices_[lhs_current] == rhs.indices_[rhs_current])
                joined_index_idx.emplace_back(lhs_current++, rhs_current);
            rhs_current += 1;
        }
    }

    return (join_helper_<LHS_T, RHS_T, types ...>(lhs, rhs, joined_index_idx));
}

// ----------------------------------------------------------------------------

template<typename TS, typename HETERO>
template<typename LHS_T, typename RHS_T, typename ... types>
StdDataFrame<TS> DataFrame<TS, HETERO>::
index_left_join_(const LHS_T &lhs, const RHS_T &rhs)  {

    size_type       lhs_current = 0;
    const size_type lhs_end = lhs.indices_.size();
    size_type       rhs_current = 0;
    const size_type rhs_end = rhs.indices_.size();

    IndexIdxVector  joined_index_idx;

    joined_index_idx.reserve(lhs_end);
    while (lhs_current != lhs_end && rhs_current != rhs_end) {
        if (lhs.indices_[lhs_current] < rhs.indices_[rhs_current])
            joined_index_idx.emplace_back(
                lhs_current++,
                std::numeric_limits<size_type>::max());
        else  {
            if (lhs.indices_[lhs_current] == rhs.indices_[rhs_current])
                joined_index_idx.emplace_back(lhs_current++, rhs_current++);
            else   {
                joined_index_idx.emplace_back(
                    lhs_current++,
                    std::numeric_limits<size_type>::max());
                rhs_current += 1;
            }
        }
    }

    return (join_helper_<LHS_T, RHS_T, types ...>(lhs, rhs, joined_index_idx));
}

// ----------------------------------------------------------------------------

template<typename TS, typename HETERO>
template<typename LHS_T, typename RHS_T, typename ... types>
StdDataFrame<TS> DataFrame<TS, HETERO>::
index_right_join_(const LHS_T &lhs, const RHS_T &rhs)  {

    size_type       lhs_current = 0;
    const size_type lhs_end = lhs.indices_.size();
    size_type       rhs_current = 0;
    const size_type rhs_end = rhs.indices_.size();

    IndexIdxVector  joined_index_idx;

    joined_index_idx.reserve(rhs_end);
    while (lhs_current != lhs_end && rhs_current != rhs_end) {
        if (lhs.indices_[lhs_current] < rhs.indices_[rhs_current])  {
            joined_index_idx.emplace_back(
                std::numeric_limits<size_type>::max(),
                rhs_current++);
            lhs_current += 1;
        }
        else  {
            if (lhs.indices_[lhs_current] == rhs.indices_[rhs_current])
                joined_index_idx.emplace_back(lhs_current++, rhs_current++);
            else
                joined_index_idx.emplace_back(
                    std::numeric_limits<size_type>::max(),
                    rhs_current++);
        }
    }

    return (join_helper_<LHS_T, RHS_T, types ...>(lhs, rhs, joined_index_idx));
}

// ----------------------------------------------------------------------------

template<typename TS, typename HETERO>
template<typename LHS_T, typename RHS_T, typename ... types>
StdDataFrame<TS> DataFrame<TS, HETERO>::
index_left_right_join_(const LHS_T &lhs, const RHS_T &rhs)  {

    StdDataFrame<TS>    result;

    return(result);
}

// ----------------------------------------------------------------------------

template<typename TS, typename HETERO>
template<typename LHS_T, typename RHS_T, typename COL_T, typename ... types>
StdDataFrame<TS> DataFrame<TS, HETERO>::
column_inner_join_(const char *col_name, const LHS_T &lhs, const RHS_T &rhs)  {

    StdDataFrame<TS>    result;

    return(result);
}

// ----------------------------------------------------------------------------

template<typename TS, typename HETERO>
template<typename LHS_T, typename RHS_T, typename COL_T, typename ... types>
StdDataFrame<TS> DataFrame<TS, HETERO>::
column_left_join_(const char *col_name, const LHS_T &lhs, const RHS_T &rhs)  {

    StdDataFrame<TS>    result;

    return(result);
}

// ----------------------------------------------------------------------------

template<typename TS, typename HETERO>
template<typename LHS_T, typename RHS_T, typename COL_T, typename ... types>
StdDataFrame<TS> DataFrame<TS, HETERO>::
column_right_join_(const char *col_name, const LHS_T &lhs, const RHS_T &rhs)  {

    StdDataFrame<TS>    result;

    return(result);
}

// ----------------------------------------------------------------------------

template<typename TS, typename HETERO>
template<typename LHS_T, typename RHS_T, typename COL_T, typename ... types>
StdDataFrame<TS> DataFrame<TS, HETERO>::
column_left_right_join_(const char *col_name,
                        const LHS_T &lhs,
                        const RHS_T &rhs)  {

    StdDataFrame<TS>    result;

    return(result);
}

} // namespace hmdf

// ----------------------------------------------------------------------------

// Local Variables:
// mode:C++
// tab-width:4
// c-basic-offset:4
// End:
<|endoftext|>


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

// Interface header.
#include "orennayarbrdf.h"

// appleseed.renderer headers.
#include "renderer/kernel/lighting/scatteringmode.h"
#include "renderer/kernel/shading/directshadingcomponents.h"
#include "renderer/modeling/bsdf/bsdf.h"
#include "renderer/modeling/bsdf/bsdfsample.h"
#include "renderer/modeling/bsdf/bsdfwrapper.h"

// appleseed.foundation headers.
#include "foundation/math/basis.h"
#include "foundation/math/sampling/mappings.h"
#include "foundation/math/scalar.h"
#include "foundation/math/vector.h"
#include "foundation/utility/api/specializedapiarrays.h"
#include "foundation/utility/containers/dictionary.h"

// Standard headers.
#include <algorithm>
#include <cmath>

// Forward declarations.
namespace foundation    { class IAbortSwitch; }
namespace renderer      { class Assembly; }
namespace renderer      { class Project; }

using namespace foundation;
using namespace std;

namespace renderer
{

namespace
{
    //
    // Oren-Nayar BRDF.
    //
    // Reference:
    //
    //   Generalization of Lambert's Reflectance Model
    //   http://www1.cs.columbia.edu/CAVE/publications/pdfs/Oren_SIGGRAPH94.pdf
    //

    const char* Model = "orennayar_brdf";

    class OrenNayarBRDFImpl
      : public BSDF
    {
      public:
        OrenNayarBRDFImpl(
            const char*                 name,
            const ParamArray&           params)
          : BSDF(name, Reflective, ScatteringMode::Diffuse, params)
        {
            m_inputs.declare("reflectance", InputFormatSpectralReflectance);
            m_inputs.declare("reflectance_multiplier", InputFormatFloat, "1.0");
            m_inputs.declare("roughness" , InputFormatFloat, "0.1");
        }

        void release() override
        {
            delete this;
        }

        const char* get_model() const override
        {
            return Model;
        }

        void sample(
            SamplingContext&            sampling_context,
            const void*                 data,
            const bool                  adjoint,
            const bool                  cosine_mult,
            const int                   modes,
            BSDFSample&                 sample) const override
        {
            if (!ScatteringMode::has_diffuse(modes))
                return;

            // Compute the incoming direction.
            sampling_context.split_in_place(2, 1);
            const Vector2f s = sampling_context.next2<Vector2f>();
            const Vector3f wi = sample_hemisphere_cosine(s);
            const Vector3f incoming = sample.m_shading_basis.transform_to_parent(wi);
            sample.m_incoming = Dual3f(incoming);

            // Compute the probability density of the sampled direction.
            const float probability = wi.y * RcpPi<float>();
            assert(probability > 0.0f);

            if (probability > 1.0e-6f)
            {
                // Set the scattering mode.
                sample.set_to_scattering(ScatteringMode::Diffuse, probability);

                // Compute the BRDF value.
                const InputValues* values = static_cast<const InputValues*>(data);
                if (values->m_roughness != 0.0f)
                {
                    const Vector3f& n = sample.m_shading_basis.get_normal();

                    // No reflection below the shading surface.
                    const float cos_in = dot(incoming, n);
                    if (cos_in < 0.0f)
                        return;

                    const Vector3f& outgoing = sample.m_outgoing.get_value();
                    const float cos_on = abs(dot(outgoing, n));
                    oren_nayar_qualitative(
                        cos_on,
                        cos_in,
                        values->m_roughness,
                        values->m_reflectance,
                        values->m_reflectance_multiplier,
                        outgoing,
                        incoming,
                        n,
                        sample.m_value.m_diffuse);
                }
                else
                {
                    // Revert to Lambertian when roughness is zero.
                    sample.m_value.m_diffuse = values->m_reflectance;
                    sample.m_value.m_diffuse *= values->m_reflectance_multiplier * RcpPi<float>();
                }

                sample.m_aov_components.m_albedo = values->m_reflectance;
                sample.m_aov_components.m_albedo *= values->m_reflectance_multiplier;

                sample.m_value.m_beauty = sample.m_value.m_diffuse;
                sample.m_min_roughness = 1.0f;

                sample.compute_reflected_differentials();
            }
        }

        float evaluate(
            const void*                 data,
            const bool                  adjoint,
            const bool                  cosine_mult,
            const Vector3f&             geometric_normal,
            const Basis3f&              shading_basis,
            const Vector3f&             outgoing,
            const Vector3f&             incoming,
            const int                   modes,
            DirectShadingComponents&    value) const override
        {
            if (!ScatteringMode::has_diffuse(modes))
                return 0.0f;

            const Vector3f& n = shading_basis.get_normal();
            const float cos_in = abs(dot(incoming, n));

            // Compute the BRDF value.
            const InputValues* values = static_cast<const InputValues*>(data);
            if (values->m_roughness != 0.0f)
            {
                const float cos_on = abs(dot(outgoing, n));
                oren_nayar_qualitative(
                    cos_on,
                    cos_in,
                    values->m_roughness,
                    values->m_reflectance,
                    values->m_reflectance_multiplier,
                    outgoing,
                    incoming,
                    n,
                    value.m_diffuse);
            }
            else
            {
                // Revert to Lambertian when roughness is zero.
                value.m_diffuse = values->m_reflectance;
                value.m_diffuse *= values->m_reflectance_multiplier * RcpPi<float>();
            }
            value.m_beauty = value.m_diffuse;

            // Compute the probability density of the sampled direction.
            const float pdf = cos_in * RcpPi<float>();
            assert(pdf >= 0.0f);

            return pdf;
        }

        float evaluate_pdf(
            const void*                 data,
            const bool                  adjoint,
            const Vector3f&             geometric_normal,
            const Basis3f&              shading_basis,
            const Vector3f&             outgoing,
            const Vector3f&             incoming,
            const int                   modes) const override
        {
            if (!ScatteringMode::has_diffuse(modes))
                return 0.0f;

            const Vector3f& n = shading_basis.get_normal();
            const float cos_in = abs(dot(incoming, n));

            // Compute the probability density of the sampled direction.
            const float pdf = cos_in * RcpPi<float>();
            assert(pdf >= 0.f);

            return pdf;
        }

      private:
        typedef OrenNayarBRDFInputValues InputValues;

        static void oren_nayar_qualitative(
            const float                 cos_on,
            const float                 cos_in,
            const float                 roughness,
            const Spectrum&             reflectance,
            const float                 reflectance_multiplier,
            const Vector3f&             outgoing,
            const Vector3f&             incoming,
            const Vector3f&             n,
            Spectrum&                   value)
        {
            const float sigma2 = square(roughness);
            const float theta_r = min(acos(cos_on), HalfPi<float>());
            const float theta_i = acos(cos_in);
            const float alpha = max(theta_r, theta_i);
            const float beta = min(theta_r, theta_i);

            // Project outgoing and incoming vectors onto the tangent plane
            // and compute the cosine of the angle between them.
            const Vector3f V_perp_N = normalize(project(outgoing, n));
            const Vector3f I_perp_N = normalize(incoming - n * cos_in);
            const float delta_cos_phi = dot(V_perp_N, I_perp_N);

            // Compute C1 coefficient.
            const float C1 = 1.0f - 0.5f * (sigma2 / (sigma2 + 0.33f));

            // Compute C2 coefficient.
            const float sigma2_009 = sigma2 / (sigma2 + 0.09f);
            const float C2 =
                  0.45f
                * sigma2_009
                * (delta_cos_phi >= 0.0f
                      ? sin(alpha)
                      : sin(alpha) - pow_int<3>(2.0f * beta * RcpPi<float>()));
            assert(C2 >= 0.0f);

            // Compute C3 coefficient.
            const float C3 =
                  0.125f
                * sigma2_009
                * square(4.0f * alpha * beta * RcpPiSquare<float>());
            assert(C3 >= 0.0f);

            // Direct illumination component.
            value = reflectance;
            value *=
                reflectance_multiplier *
                RcpPi<float>() * (
                      C1
                    + delta_cos_phi * C2 * tan(beta)
                    + (1.0f - abs(delta_cos_phi)) * C3 * tan(0.5f * (alpha + beta)));

            // Add interreflection component.
            Spectrum r2 = reflectance;
            r2 *= r2;
            r2 *=
                  0.17f
                * square(reflectance_multiplier) * RcpPi<float>()
                * sigma2 / (sigma2 + 0.13f)
                * (1.0f - delta_cos_phi * square(2.0f * beta * RcpPi<float>()));
            value += r2;
            clamp_low_in_place(value, 0.0f);
        }
    };

    typedef BSDFWrapper<OrenNayarBRDFImpl> OrenNayarBRDF;
}

//
// OrenNayarBRDFFactory class implementation.
//

void OrenNayarBRDFFactory::release()
{
    delete this;
}

const char* OrenNayarBRDFFactory::get_model() const
{
    return Model;
}

Dictionary OrenNayarBRDFFactory::get_model_metadata() const
{
    return
        Dictionary()
            .insert("name", Model)
            .insert("label", "Oren-Nayar BRDF");
}

DictionaryArray OrenNayarBRDFFactory::get_input_metadata() const
{
    DictionaryArray metadata;

    metadata.push_back(
        Dictionary()
            .insert("name", "reflectance")
            .insert("label", "Reflectance")
            .insert("type", "colormap")
            .insert("entity_types",
                Dictionary()
                    .insert("color", "Colors")
                    .insert("texture_instance", "Texture Instances"))
            .insert("use", "required")
            .insert("default", "0.5"));

    metadata.push_back(
        Dictionary()
            .insert("name", "reflectance_multiplier")
            .insert("label", "Reflectance Multiplier")
            .insert("type", "colormap")
            .insert("entity_types",
                Dictionary().insert("texture_instance", "Texture Instances"))
            .insert("use", "optional")
            .insert("default", "1.0"));

    metadata.push_back(
        Dictionary()
            .insert("name", "roughness")
            .insert("label", "Roughness")
            .insert("type", "colormap")
            .insert("entity_types",
                Dictionary().insert("texture_instance", "Texture Instances"))
            .insert("use", "required")
            .insert("default", "0.1"));

    return metadata;
}

auto_release_ptr<BSDF> OrenNayarBRDFFactory::create(
    const char*         name,
    const ParamArray&   params) const
{
    return auto_release_ptr<BSDF>(new OrenNayarBRDF(name, params));
}

}   // namespace renderer
<commit_msg>Renamings<commit_after>
//
// This source file is part of appleseed.
// Visit https://appleseedhq.net/ for additional information and resources.
//
// This software is released under the MIT license.
//
// Copyright (c) 2014-2018 Luis B. Barrancos, The appleseedhq Organization
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
//

// Interface header.
#include "orennayarbrdf.h"

// appleseed.renderer headers.
#include "renderer/kernel/lighting/scatteringmode.h"
#include "renderer/kernel/shading/directshadingcomponents.h"
#include "renderer/modeling/bsdf/bsdf.h"
#include "renderer/modeling/bsdf/bsdfsample.h"
#include "renderer/modeling/bsdf/bsdfwrapper.h"

// appleseed.foundation headers.
#include "foundation/math/basis.h"
#include "foundation/math/sampling/mappings.h"
#include "foundation/math/scalar.h"
#include "foundation/math/vector.h"
#include "foundation/utility/api/specializedapiarrays.h"
#include "foundation/utility/containers/dictionary.h"

// Standard headers.
#include <algorithm>
#include <cmath>

// Forward declarations.
namespace foundation    { class IAbortSwitch; }
namespace renderer      { class Assembly; }
namespace renderer      { class Project; }

using namespace foundation;
using namespace std;

namespace renderer
{

namespace
{
    //
    // Oren-Nayar BRDF.
    //
    // Reference:
    //
    //   Generalization of Lambert's Reflectance Model
    //   http://www1.cs.columbia.edu/CAVE/publications/pdfs/Oren_SIGGRAPH94.pdf
    //

    const char* Model = "orennayar_brdf";

    class OrenNayarBRDFImpl
      : public BSDF
    {
      public:
        OrenNayarBRDFImpl(
            const char*                 name,
            const ParamArray&           params)
          : BSDF(name, Reflective, ScatteringMode::Diffuse, params)
        {
            m_inputs.declare("reflectance", InputFormatSpectralReflectance);
            m_inputs.declare("reflectance_multiplier", InputFormatFloat, "1.0");
            m_inputs.declare("roughness" , InputFormatFloat, "0.1");
        }

        void release() override
        {
            delete this;
        }

        const char* get_model() const override
        {
            return Model;
        }

        void sample(
            SamplingContext&            sampling_context,
            const void*                 data,
            const bool                  adjoint,
            const bool                  cosine_mult,
            const int                   modes,
            BSDFSample&                 sample) const override
        {
            if (!ScatteringMode::has_diffuse(modes))
                return;

            // Compute the incoming direction.
            sampling_context.split_in_place(2, 1);
            const Vector2f s = sampling_context.next2<Vector2f>();
            const Vector3f wi = sample_hemisphere_cosine(s);
            const Vector3f incoming = sample.m_shading_basis.transform_to_parent(wi);
            sample.m_incoming = Dual3f(incoming);

            // Compute the probability density of the sampled direction.
            const float probability = wi.y * RcpPi<float>();
            assert(probability > 0.0f);

            if (probability > 1.0e-6f)
            {
                // Set the scattering mode.
                sample.set_to_scattering(ScatteringMode::Diffuse, probability);

                // Compute the BRDF value.
                const InputValues* values = static_cast<const InputValues*>(data);
                if (values->m_roughness != 0.0f)
                {
                    const Vector3f& n = sample.m_shading_basis.get_normal();

                    // No reflection below the shading surface.
                    const float cos_in = dot(incoming, n);
                    if (cos_in < 0.0f)
                        return;

                    const Vector3f& outgoing = sample.m_outgoing.get_value();
                    const float cos_on = abs(dot(outgoing, n));
                    oren_nayar(
                        cos_on,
                        cos_in,
                        values->m_roughness,
                        values->m_reflectance,
                        values->m_reflectance_multiplier,
                        outgoing,
                        incoming,
                        n,
                        sample.m_value.m_diffuse);
                }
                else
                {
                    // Revert to Lambertian when roughness is zero.
                    sample.m_value.m_diffuse = values->m_reflectance;
                    sample.m_value.m_diffuse *= values->m_reflectance_multiplier * RcpPi<float>();
                }

                sample.m_aov_components.m_albedo = values->m_reflectance;
                sample.m_aov_components.m_albedo *= values->m_reflectance_multiplier;

                sample.m_value.m_beauty = sample.m_value.m_diffuse;
                sample.m_min_roughness = 1.0f;

                sample.compute_reflected_differentials();
            }
        }

        float evaluate(
            const void*                 data,
            const bool                  adjoint,
            const bool                  cosine_mult,
            const Vector3f&             geometric_normal,
            const Basis3f&              shading_basis,
            const Vector3f&             outgoing,
            const Vector3f&             incoming,
            const int                   modes,
            DirectShadingComponents&    value) const override
        {
            if (!ScatteringMode::has_diffuse(modes))
                return 0.0f;

            const Vector3f& n = shading_basis.get_normal();
            const float cos_in = abs(dot(incoming, n));

            // Compute the BRDF value.
            const InputValues* values = static_cast<const InputValues*>(data);
            if (values->m_roughness != 0.0f)
            {
                const float cos_on = abs(dot(outgoing, n));
                oren_nayar(
                    cos_on,
                    cos_in,
                    values->m_roughness,
                    values->m_reflectance,
                    values->m_reflectance_multiplier,
                    outgoing,
                    incoming,
                    n,
                    value.m_diffuse);
            }
            else
            {
                // Revert to Lambertian when roughness is zero.
                value.m_diffuse = values->m_reflectance;
                value.m_diffuse *= values->m_reflectance_multiplier * RcpPi<float>();
            }
            value.m_beauty = value.m_diffuse;

            // Compute the probability density of the sampled direction.
            const float pdf = cos_in * RcpPi<float>();
            assert(pdf >= 0.0f);

            return pdf;
        }

        float evaluate_pdf(
            const void*                 data,
            const bool                  adjoint,
            const Vector3f&             geometric_normal,
            const Basis3f&              shading_basis,
            const Vector3f&             outgoing,
            const Vector3f&             incoming,
            const int                   modes) const override
        {
            if (!ScatteringMode::has_diffuse(modes))
                return 0.0f;

            const Vector3f& n = shading_basis.get_normal();
            const float cos_in = abs(dot(incoming, n));

            // Compute the probability density of the sampled direction.
            const float pdf = cos_in * RcpPi<float>();
            assert(pdf >= 0.f);

            return pdf;
        }

      private:
        typedef OrenNayarBRDFInputValues InputValues;

        static void oren_nayar(
            const float                 cos_on,
            const float                 cos_in,
            const float                 roughness,
            const Spectrum&             reflectance,
            const float                 reflectance_multiplier,
            const Vector3f&             outgoing,
            const Vector3f&             incoming,
            const Vector3f&             n,
            Spectrum&                   value)
        {
            const float sigma2 = square(roughness);
            const float theta_r = min(acos(cos_on), HalfPi<float>());
            const float theta_i = acos(cos_in);
            const float alpha = max(theta_r, theta_i);
            const float beta = min(theta_r, theta_i);

            // Project outgoing and incoming vectors onto the tangent plane
            // and compute the cosine of the angle between them.
            const Vector3f V_perp_N = normalize(project(outgoing, n));
            const Vector3f I_perp_N = normalize(incoming - n * cos_in);
            const float delta_cos_phi = dot(V_perp_N, I_perp_N);

            // Compute C1 coefficient.
            const float C1 = 1.0f - 0.5f * (sigma2 / (sigma2 + 0.33f));

            // Compute C2 coefficient.
            const float sigma2_009 = sigma2 / (sigma2 + 0.09f);
            const float C2 =
                  0.45f
                * sigma2_009
                * (delta_cos_phi >= 0.0f
                      ? sin(alpha)
                      : sin(alpha) - pow_int<3>(2.0f * beta * RcpPi<float>()));
            assert(C2 >= 0.0f);

            // Compute C3 coefficient.
            const float C3 =
                  0.125f
                * sigma2_009
                * square(4.0f * alpha * beta * RcpPiSquare<float>());
            assert(C3 >= 0.0f);

            // Direct illumination component.
            value = reflectance;
            value *=
                reflectance_multiplier *
                RcpPi<float>() * (
                      C1
                    + delta_cos_phi * C2 * tan(beta)
                    + (1.0f - abs(delta_cos_phi)) * C3 * tan(0.5f * (alpha + beta)));

            // Add interreflection component.
            Spectrum ir = reflectance;
            ir *= ir;
            ir *=
                  0.17f
                * square(reflectance_multiplier)
                * RcpPi<float>()
                * sigma2 / (sigma2 + 0.13f)
                * (1.0f - delta_cos_phi * square(2.0f * beta * RcpPi<float>()));
            value += ir;

            clamp_low_in_place(value, 0.0f);
        }
    };

    typedef BSDFWrapper<OrenNayarBRDFImpl> OrenNayarBRDF;
}

//
// OrenNayarBRDFFactory class implementation.
//

void OrenNayarBRDFFactory::release()
{
    delete this;
}

const char* OrenNayarBRDFFactory::get_model() const
{
    return Model;
}

Dictionary OrenNayarBRDFFactory::get_model_metadata() const
{
    return
        Dictionary()
            .insert("name", Model)
            .insert("label", "Oren-Nayar BRDF");
}

DictionaryArray OrenNayarBRDFFactory::get_input_metadata() const
{
    DictionaryArray metadata;

    metadata.push_back(
        Dictionary()
            .insert("name", "reflectance")
            .insert("label", "Reflectance")
            .insert("type", "colormap")
            .insert("entity_types",
                Dictionary()
                    .insert("color", "Colors")
                    .insert("texture_instance", "Texture Instances"))
            .insert("use", "required")
            .insert("default", "0.5"));

    metadata.push_back(
        Dictionary()
            .insert("name", "reflectance_multiplier")
            .insert("label", "Reflectance Multiplier")
            .insert("type", "colormap")
            .insert("entity_types",
                Dictionary().insert("texture_instance", "Texture Instances"))
            .insert("use", "optional")
            .insert("default", "1.0"));

    metadata.push_back(
        Dictionary()
            .insert("name", "roughness")
            .insert("label", "Roughness")
            .insert("type", "colormap")
            .insert("entity_types",
                Dictionary().insert("texture_instance", "Texture Instances"))
            .insert("use", "required")
            .insert("default", "0.1"));

    return metadata;
}

auto_release_ptr<BSDF> OrenNayarBRDFFactory::create(
    const char*         name,
    const ParamArray&   params) const
{
    return auto_release_ptr<BSDF>(new OrenNayarBRDF(name, params));
}

}   // namespace renderer
<|endoftext|>


<commit_before>/* bzflag
 * Copyright (c) 1993 - 2004 Tim Riker
 *
 * This package is free software;  you can redistribute it and/or
 * modify it under the terms of the license found in the file
 * named COPYING that should have accompanied this file.
 *
 * THIS PACKAGE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
 * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
 * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
 */

/* common headers */
#include "common.h"

/* interface header */
#include "WinJoystick.h"

/* implementation headers */
#include <mmsystem.h>
#include "ErrorHandler.h"
#include "TextUtils.h"

WinJoystick::WinJoystick()
{
}

WinJoystick::~WinJoystick()
{
}

void	      WinJoystick::initJoystick(const char* joystickName)
{
  if (strlen(joystickName) > 11)
    return;

  if (joystickName[10] == '1')
    JoystickID = JOYSTICKID1;
  else if (joystickName[10] == '2')
    JoystickID = JOYSTICKID2;

  JOYINFO joyInfo;
  JOYCAPS joyCaps;
  if ((joyGetPos(JoystickID, &joyInfo) != JOYERR_NOERROR) ||
      (joyGetDevCaps(JoystickID, &joyCaps, sizeof(joyCaps)) != JOYERR_NOERROR)) {
    printError("Unable to initialize joystick.  Perhaps it is not plugged in?");
    inited = false;
    return;
  }

  xMin = (float)joyCaps.wXmin;
  xMax = (float)joyCaps.wXmax;
  yMin = (float)joyCaps.wYmin;
  yMax = (float)joyCaps.wYmax;

  inited = true;
}

bool	      WinJoystick::joystick() const
{
  return inited;
}

void	      WinJoystick::getJoy(int& x, int& y) const
{
  if (!inited)
    return;

  JOYINFOEX joyInfo;
  // we're only interested in position
  joyInfo.dwFlags = JOY_RETURNX | JOY_RETURNY | JOY_USEDEADZONE;

  // check for errors
  if (joyGetPosEx(JoystickID, &joyInfo) != JOYERR_NOERROR) {
    printError("Could not get extended info from joystick");
    return;
  }

  // adjust X and Y to scale
  x = (joyInfo.dwXpos / xMax) * 2000 - 1000;
  y = (joyInfo.dwYpos / yMax) * 2000 - 1000;

  // ballistic
  x = (x * abs(x)) / 1000;
  y = (y * abs(y)) / 1000;

}

unsigned long WinJoystick::getJoyButtons() const
{
  if (!inited)
    return 0;

  // FIXME - Should use joyGetPosEx and JOYINFOEX, to get the rest of the buttons,
  // but wasn't working for me.
  JOYINFO joyInfo;

  // check for errors
  if (joyGetPos(JoystickID, &joyInfo) != JOYERR_NOERROR) {
    printError("Could not get extended info from joystick");
    return 0;
  }

  unsigned long retbuts = joyInfo.wButtons;
  unsigned long buttons = 0;
  if (retbuts & JOY_BUTTON1)  buttons = buttons | 0x00001;
  if (retbuts & JOY_BUTTON2)  buttons = buttons | 0x00002;
  if (retbuts & JOY_BUTTON3)  buttons = buttons | 0x00004;
  if (retbuts & JOY_BUTTON4)  buttons = buttons | 0x00008;
/*  if (retbuts & JOY_BUTTON5)  buttons = buttons | 0x00010;
  if (retbuts & JOY_BUTTON6)  buttons = buttons | 0x00020;
  if (retbuts & JOY_BUTTON7)  buttons = buttons | 0x00040;
  if (retbuts & JOY_BUTTON8)  buttons = buttons | 0x00080;
  if (retbuts & JOY_BUTTON9)  buttons = buttons | 0x00100;
  if (retbuts & JOY_BUTTON10) buttons = buttons | 0x00200; */

  return buttons;
}

void	      WinJoystick::getJoyDevices(std::vector<std::string> &list) const
{
  list.clear();
  if (joyGetNumDevs() != 0) {
    // we have at least one joystick driver, get the name of both joystick IDs if they exist.
    JOYCAPS joyCaps;
    if (joyGetDevCaps(JOYSTICKID1, &joyCaps, sizeof(joyCaps)) == JOYERR_NOERROR) {
      list.push_back(string_util::format("Joystick 1 (%s)", joyCaps.szOEMVxD));
    }
    if (joyGetDevCaps(JOYSTICKID2, &joyCaps, sizeof(joyCaps)) == JOYERR_NOERROR) {
      list.push_back(string_util::format("Joystick 2 (%s)", joyCaps.szOEMVxD));
    }
  }
}

// Local Variables: ***
// mode:C++ ***
// tab-width: 8 ***
// c-basic-offset: 2 ***
// indent-tabs-mode: t ***
// End: ***
// ex: shiftwidth=2 tabstop=8
<commit_msg>Make native win joystick recognize "off" and turn off, and make it initialize the first time initJoystick is called instead of the second.<commit_after>/* bzflag
 * Copyright (c) 1993 - 2004 Tim Riker
 *
 * This package is free software;  you can redistribute it and/or
 * modify it under the terms of the license found in the file
 * named COPYING that should have accompanied this file.
 *
 * THIS PACKAGE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
 * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
 * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
 */

/* common headers */
#include "common.h"

/* interface header */
#include "WinJoystick.h"

/* implementation headers */
#include <mmsystem.h>
#include "ErrorHandler.h"
#include "TextUtils.h"

WinJoystick::WinJoystick()
{
}

WinJoystick::~WinJoystick()
{
}

void	      WinJoystick::initJoystick(const char* joystickName)
{
  inited = false;

  if (!strcmp(joystickName, "off") || !strcmp(joystickName, "")) {
    return;
  }

  if (strlen(joystickName) < 11) {
    printError("Invalid joystick name.");
    return;
  }

  if (joystickName[10] == '1')
    JoystickID = JOYSTICKID1;
  else if (joystickName[10] == '2')
    JoystickID = JOYSTICKID2;

  JOYINFO joyInfo;
  JOYCAPS joyCaps;
  if ((joyGetPos(JoystickID, &joyInfo) != JOYERR_NOERROR) ||
      (joyGetDevCaps(JoystickID, &joyCaps, sizeof(joyCaps)) != JOYERR_NOERROR)) {
    printError("Unable to initialize joystick.  Perhaps it is not plugged in?");
    return;
  }

  xMin = (float)joyCaps.wXmin;
  xMax = (float)joyCaps.wXmax;
  yMin = (float)joyCaps.wYmin;
  yMax = (float)joyCaps.wYmax;

  inited = true;
}

bool	      WinJoystick::joystick() const
{
  return inited;
}

void	      WinJoystick::getJoy(int& x, int& y) const
{
  if (!inited)
    return;

  JOYINFOEX joyInfo;
  // we're only interested in position
  joyInfo.dwFlags = JOY_RETURNX | JOY_RETURNY | JOY_USEDEADZONE;

  // check for errors
  if (joyGetPosEx(JoystickID, &joyInfo) != JOYERR_NOERROR) {
    printError("Could not get extended info from joystick");
    return;
  }

  // adjust X and Y to scale
  x = (joyInfo.dwXpos / xMax) * 2000 - 1000;
  y = (joyInfo.dwYpos / yMax) * 2000 - 1000;

  // ballistic
  x = (x * abs(x)) / 1000;
  y = (y * abs(y)) / 1000;

}

unsigned long WinJoystick::getJoyButtons() const
{
  if (!inited)
    return 0;

  // FIXME - Should use joyGetPosEx and JOYINFOEX, to get the rest of the buttons,
  // but wasn't working for me.
  JOYINFO joyInfo;

  // check for errors
  if (joyGetPos(JoystickID, &joyInfo) != JOYERR_NOERROR) {
    printError("Could not get extended info from joystick");
    return 0;
  }

  unsigned long retbuts = joyInfo.wButtons;
  unsigned long buttons = 0;
  if (retbuts & JOY_BUTTON1)  buttons = buttons | 0x00001;
  if (retbuts & JOY_BUTTON2)  buttons = buttons | 0x00002;
  if (retbuts & JOY_BUTTON3)  buttons = buttons | 0x00004;
  if (retbuts & JOY_BUTTON4)  buttons = buttons | 0x00008;
/*  if (retbuts & JOY_BUTTON5)  buttons = buttons | 0x00010;
  if (retbuts & JOY_BUTTON6)  buttons = buttons | 0x00020;
  if (retbuts & JOY_BUTTON7)  buttons = buttons | 0x00040;
  if (retbuts & JOY_BUTTON8)  buttons = buttons | 0x00080;
  if (retbuts & JOY_BUTTON9)  buttons = buttons | 0x00100;
  if (retbuts & JOY_BUTTON10) buttons = buttons | 0x00200; */

  return buttons;
}

void	      WinJoystick::getJoyDevices(std::vector<std::string> &list) const
{
  list.clear();
  if (joyGetNumDevs() != 0) {
    // we have at least one joystick driver, get the name of both joystick IDs if they exist.
    JOYCAPS joyCaps;
    if (joyGetDevCaps(JOYSTICKID1, &joyCaps, sizeof(joyCaps)) == JOYERR_NOERROR) {
      list.push_back(string_util::format("Joystick 1 (%s)", joyCaps.szOEMVxD));
    }
    if (joyGetDevCaps(JOYSTICKID2, &joyCaps, sizeof(joyCaps)) == JOYERR_NOERROR) {
      list.push_back(string_util::format("Joystick 2 (%s)", joyCaps.szOEMVxD));
    }
  }
}

// Local Variables: ***
// mode:C++ ***
// tab-width: 8 ***
// c-basic-offset: 2 ***
// indent-tabs-mode: t ***
// End: ***
// ex: shiftwidth=2 tabstop=8
<|endoftext|>


<commit_before>#include "interface/usb.h"
#include <stdio.h>
#include "util/log.h"
#include "util/bytebuffer.h"
#include "gpio.h"
#include "usb_config.h"
#include "emqueue.h"

#include "LPC17xx.h"
#include "lpc17xx_pinsel.h"

extern "C" {
#include "bsp.h"
}

#define VBUS_PORT 1
#define VBUS_PIN 30
#define VBUS_FUNCNUM 2

#define USB_DM_PORT 0
#define USB_DM_PIN 30
#define USB_DM_FUNCNUM 1

#define USB_HOST_DETECT_INACTIVE_VALUE 400
#define USB_HOST_DETECT_ACTIVE_VALUE 50

#define USB_CONNECT_PORT 2
#define USB_CONNECT_PIN 9

namespace gpio = openxc::gpio;

using openxc::util::log::debug;
using openxc::interface::usb::UsbDevice;
using openxc::interface::usb::UsbEndpoint;
using openxc::interface::usb::UsbEndpointDirection;
using openxc::util::bytebuffer::processQueue;
using openxc::gpio::GPIO_VALUE_HIGH;
using openxc::gpio::GPIO_VALUE_LOW;

extern UsbDevice USB_DEVICE;
extern bool handleControlRequest(uint8_t, uint8_t[], int);

void configureEndpoints() {
    for(int i = 0; i < ENDPOINT_COUNT; i++) {
        UsbEndpoint* endpoint = &USB_DEVICE.endpoints[i];
        Endpoint_ConfigureEndpoint(endpoint->address,
                EP_TYPE_BULK,
                endpoint->direction == UsbEndpointDirection::USB_ENDPOINT_DIRECTION_OUT ?
                        ENDPOINT_DIR_OUT : ENDPOINT_DIR_IN,
                endpoint->size, ENDPOINT_BANK_DOUBLE);
    }
}

extern "C" {

void EVENT_USB_Device_Disconnect() {
    debug("USB no longer detected - marking unconfigured");
    USB_DEVICE.configured = false;
}

void EVENT_USB_Device_ControlRequest() {
    if(!(Endpoint_IsSETUPReceived())) {
        return;
    }

    QUEUE_TYPE(uint8_t) payloadQueue;
    QUEUE_INIT(uint8_t, &payloadQueue);
    debug("Rq: 0x%x, length: 0x%x", USB_ControlRequest.bRequest,
            USB_ControlRequest.wLength);

    // Don't try and read payload of system-level control requests
    if((USB_ControlRequest.bmRequestType >> 7 == 0) &&
            USB_ControlRequest.bRequest >= 0x80) {
        Endpoint_ClearSETUP();

        for(int i = 0; i < USB_ControlRequest.wLength; i++) {
            while (!(Endpoint_IsOUTReceived()));
            if(!QUEUE_PUSH(uint8_t, &payloadQueue, Endpoint_Read_8())) {
                debug("Dropped control command write from host -- queue is full");
                break;
            }
        }

        Endpoint_ClearOUT();
        Endpoint_ClearStatusStage();
    }

    int length = QUEUE_LENGTH(uint8_t, &payloadQueue);
    uint8_t snapshot[length];
    if(length > 0) {
        QUEUE_SNAPSHOT(uint8_t, &payloadQueue, snapshot, length);
    }

    handleControlRequest(USB_ControlRequest.bRequest, snapshot, length);
}

void EVENT_USB_Device_ConfigurationChanged(void) {
    USB_DEVICE.configured = false;
    configureEndpoints();
    debug("USB configured");
    USB_DEVICE.configured = true;
}

}

/* Private: Flush any queued data out to the USB host. */
static void flushQueueToHost(UsbDevice* usbDevice, UsbEndpoint* endpoint) {
    if(!usbDevice->configured || QUEUE_EMPTY(uint8_t, &endpoint->queue)) {
        return;
    }

    uint8_t previousEndpoint = Endpoint_GetCurrentEndpoint();
    Endpoint_SelectEndpoint(endpoint->address);
    if(Endpoint_IsINReady()) {
        // get bytes from transmit FIFO into intermediate buffer
        int byteCount = 0;
        while(!QUEUE_EMPTY(uint8_t, &endpoint->queue)
                && byteCount < USB_SEND_BUFFER_SIZE) {
            endpoint->sendBuffer[byteCount++] = QUEUE_POP(uint8_t,
                    &endpoint->queue);
        }

        if(byteCount > 0) {
            Endpoint_Write_Stream_LE(endpoint->sendBuffer, byteCount, NULL);
        }
        Endpoint_ClearIN();
    }
    Endpoint_SelectEndpoint(previousEndpoint);
}

/* Private: Detect if USB VBUS is active.
 *
 * This isn't useful if there's no diode between an external 12v/9v power supply
 * (e.g. vehicle power from OBD-II) and the 5v rail, because then VBUS high when
 * the power is powered on regardless of the status of USB. In that situation,
 * you can fall back to the usbHostDetected() function instead.
 *
 * Returns true if VBUS is high.
 */
bool vbusDetected() {
    return gpio::getValue(VBUS_PORT, VBUS_PIN) != GPIO_VALUE_LOW;
}

/* Private: Detect if a USB host is actually attached, regardless of VBUS.
 *
 * This is a bit hacky, as normally you should rely on VBUS to detect if USB is
 * connected. See vbusDetected() for reasons why we need this workaround on the
 * current prototype.
 *
 * Returns true of there is measurable activity on the D- USB line.
 */
bool usbHostDetected(UsbDevice* usbDevice) {
    static int debounce = 0;
    static float average = USB_HOST_DETECT_INACTIVE_VALUE / 2;

    if(gpio::getValue(USB_DM_PORT, USB_DM_PIN) == GPIO_VALUE_LOW) {
        ++debounce;
    } else {
        average = average * .9 + debounce * .1;
        debounce = 0;
    }

    if(!usbDevice->configured && average < USB_HOST_DETECT_ACTIVE_VALUE) {
        EVENT_USB_Device_ConfigurationChanged();
    }

    if(average > USB_HOST_DETECT_INACTIVE_VALUE) {
        debounce = 0;
        average = USB_HOST_DETECT_INACTIVE_VALUE / 2;
        return false;
    }
    return true;
}

/* Private: Configure I/O pins used to detect if USB is connected to a host. */
void configureUsbDetection() {
    PINSEL_CFG_Type vbusPinConfig;
    vbusPinConfig.Funcnum = VBUS_FUNCNUM;
    vbusPinConfig.Portnum = VBUS_PORT;
    vbusPinConfig.Pinnum = VBUS_PIN;
    vbusPinConfig.Pinmode = PINSEL_PINMODE_TRISTATE;
    PINSEL_ConfigPin(&vbusPinConfig);

    PINSEL_CFG_Type hostDetectPinConfig;
    hostDetectPinConfig.Funcnum = USB_DM_FUNCNUM;
    hostDetectPinConfig.Portnum = USB_DM_PORT;
    hostDetectPinConfig.Pinnum = USB_DM_PIN;
    hostDetectPinConfig.Pinmode = PINSEL_PINMODE_TRISTATE;
    PINSEL_ConfigPin(&hostDetectPinConfig);
}

void openxc::interface::usb::sendControlMessage(uint8_t* data, uint8_t length) {
    uint8_t previousEndpoint = Endpoint_GetCurrentEndpoint();
    Endpoint_SelectEndpoint(ENDPOINT_CONTROLEP);

    Endpoint_ClearSETUP();
    Endpoint_Write_Control_Stream_LE(data, length);
    // TODO I think it needs to be ClearIN since this is a device -> host
    // request, but a simple switch breaks it
    Endpoint_ClearOUT();

    Endpoint_SelectEndpoint(previousEndpoint);
}

void openxc::interface::usb::processSendQueue(UsbDevice* usbDevice) {
    USB_USBTask();

    if(!usbDevice->configured) {
        usbHostDetected(usbDevice);
    }

    if(usbDevice->configured) {
        if((USB_DeviceState != DEVICE_STATE_Configured || !vbusDetected())
                || !usbHostDetected(usbDevice)) {
            // On Windows the USB device will be configured when plugged in for
            // the first time, regardless of if you are actively using it in an
            // application. Windows will *not* send the USB configured event
            // when an application connects.
            //
            // On Linux and Mac, the USB configured event triggers each time a
            // new connection is made to the device.
            //
            // This means that if vbus is high (i.e. USB *might* be connected),
            // that's the only time we should check the usbHostDetected()
            // workaround. If we call that on Windows when USB is attached, it
            // will *unconfigure* the USB device from the VI side but not
            // reconfigure it until you disconnect and reconnect the device to
            // the PC! If the debounce value is small (which is ideal...) that
            // could happen even before your app has a chance to load the
            // device.
            EVENT_USB_Device_Disconnect();
        } else {
            for(int i = 0; i < ENDPOINT_COUNT; i++) {
                UsbEndpoint* endpoint = &usbDevice->endpoints[i];
                if(endpoint->direction == UsbEndpointDirection::USB_ENDPOINT_DIRECTION_IN) {
                    flushQueueToHost(usbDevice, endpoint);
                }
            }
        }
    }
}

void openxc::interface::usb::initialize(UsbDevice* usbDevice) {
    usb::initializeCommon(usbDevice);
    USB_Init();
    ::USB_Connect();
    configureUsbDetection();
}

void openxc::interface::usb::read(UsbDevice* device, UsbEndpoint* endpoint,
        bool (*callback)(uint8_t*)) {
    uint8_t previousEndpoint = Endpoint_GetCurrentEndpoint();
    Endpoint_SelectEndpoint(endpoint->address);

    while(Endpoint_IsOUTReceived()) {
        while(Endpoint_BytesInEndpoint()) {
            if(!QUEUE_PUSH(uint8_t, &endpoint->queue, Endpoint_Read_8())) {
                debug("Dropped write from host -- queue is full");
            }
        }
        processQueue(&endpoint->queue, callback);
        Endpoint_ClearOUT();
    }
    Endpoint_SelectEndpoint(previousEndpoint);
}

void openxc::interface::usb::deinitialize(UsbDevice* usbDevice) {
    usb::initializeCommon(usbDevice);
    // Turn off USB connection status LED
    gpio::setValue(USB_CONNECT_PORT, USB_CONNECT_PIN, GPIO_VALUE_HIGH);
}
<commit_msg>Fix reading multiple USB frames for a single control transfer.<commit_after>#include "interface/usb.h"
#include <stdio.h>
#include "util/log.h"
#include "util/bytebuffer.h"
#include "gpio.h"
#include "usb_config.h"
#include "emqueue.h"

#include "LPC17xx.h"
#include "lpc17xx_pinsel.h"

extern "C" {
#include "bsp.h"
}

#define VBUS_PORT 1
#define VBUS_PIN 30
#define VBUS_FUNCNUM 2

#define USB_DM_PORT 0
#define USB_DM_PIN 30
#define USB_DM_FUNCNUM 1

#define USB_HOST_DETECT_INACTIVE_VALUE 400
#define USB_HOST_DETECT_ACTIVE_VALUE 50

#define USB_CONNECT_PORT 2
#define USB_CONNECT_PIN 9

namespace gpio = openxc::gpio;

using openxc::util::log::debug;
using openxc::interface::usb::UsbDevice;
using openxc::interface::usb::UsbEndpoint;
using openxc::interface::usb::UsbEndpointDirection;
using openxc::util::bytebuffer::processQueue;
using openxc::gpio::GPIO_VALUE_HIGH;
using openxc::gpio::GPIO_VALUE_LOW;

extern UsbDevice USB_DEVICE;
extern bool handleControlRequest(uint8_t, uint8_t[], int);

void configureEndpoints() {
    for(int i = 0; i < ENDPOINT_COUNT; i++) {
        UsbEndpoint* endpoint = &USB_DEVICE.endpoints[i];
        Endpoint_ConfigureEndpoint(endpoint->address,
                EP_TYPE_BULK,
                endpoint->direction == UsbEndpointDirection::USB_ENDPOINT_DIRECTION_OUT ?
                        ENDPOINT_DIR_OUT : ENDPOINT_DIR_IN,
                endpoint->size, ENDPOINT_BANK_DOUBLE);
    }
}

extern "C" {

void EVENT_USB_Device_Disconnect() {
    debug("USB no longer detected - marking unconfigured");
    USB_DEVICE.configured = false;
}

void EVENT_USB_Device_ControlRequest() {
    if(!(Endpoint_IsSETUPReceived())) {
        return;
    }

    QUEUE_TYPE(uint8_t) payloadQueue;
    QUEUE_INIT(uint8_t, &payloadQueue);

    // Don't try and read payload of system-level control requests
    if((USB_ControlRequest.bmRequestType >> 7 == 0) &&
            USB_ControlRequest.bRequest >= 0x80) {
        Endpoint_ClearSETUP();

        int bytesReceived = 0;
        while(bytesReceived < USB_ControlRequest.wLength) {
            // TODO could this get stuck?
            while(!Endpoint_IsOUTReceived());
            while(Endpoint_BytesInEndpoint()) {
                uint8_t byte = Endpoint_Read_8();
                if(!QUEUE_PUSH(uint8_t, &payloadQueue, byte)) {
                    debug("Dropped control command write from host -- queue is full");
                    break;
                }
                ++bytesReceived;
            }
            Endpoint_ClearOUT();
        }

        Endpoint_ClearStatusStage();
    }

    int length = QUEUE_LENGTH(uint8_t, &payloadQueue);
    uint8_t snapshot[length];
    if(length > 0) {
        QUEUE_SNAPSHOT(uint8_t, &payloadQueue, snapshot, length);
    }

    handleControlRequest(USB_ControlRequest.bRequest, snapshot, length);
}

void EVENT_USB_Device_ConfigurationChanged(void) {
    USB_DEVICE.configured = false;
    configureEndpoints();
    debug("USB configured");
    USB_DEVICE.configured = true;
}

}

/* Private: Flush any queued data out to the USB host. */
static void flushQueueToHost(UsbDevice* usbDevice, UsbEndpoint* endpoint) {
    if(!usbDevice->configured || QUEUE_EMPTY(uint8_t, &endpoint->queue)) {
        return;
    }

    uint8_t previousEndpoint = Endpoint_GetCurrentEndpoint();
    Endpoint_SelectEndpoint(endpoint->address);
    if(Endpoint_IsINReady()) {
        // get bytes from transmit FIFO into intermediate buffer
        int byteCount = 0;
        while(!QUEUE_EMPTY(uint8_t, &endpoint->queue)
                && byteCount < USB_SEND_BUFFER_SIZE) {
            endpoint->sendBuffer[byteCount++] = QUEUE_POP(uint8_t,
                    &endpoint->queue);
        }

        if(byteCount > 0) {
            Endpoint_Write_Stream_LE(endpoint->sendBuffer, byteCount, NULL);
        }
        Endpoint_ClearIN();
    }
    Endpoint_SelectEndpoint(previousEndpoint);
}

/* Private: Detect if USB VBUS is active.
 *
 * This isn't useful if there's no diode between an external 12v/9v power supply
 * (e.g. vehicle power from OBD-II) and the 5v rail, because then VBUS high when
 * the power is powered on regardless of the status of USB. In that situation,
 * you can fall back to the usbHostDetected() function instead.
 *
 * Returns true if VBUS is high.
 */
bool vbusDetected() {
    return gpio::getValue(VBUS_PORT, VBUS_PIN) != GPIO_VALUE_LOW;
}

/* Private: Detect if a USB host is actually attached, regardless of VBUS.
 *
 * This is a bit hacky, as normally you should rely on VBUS to detect if USB is
 * connected. See vbusDetected() for reasons why we need this workaround on the
 * current prototype.
 *
 * Returns true of there is measurable activity on the D- USB line.
 */
bool usbHostDetected(UsbDevice* usbDevice) {
    static int debounce = 0;
    static float average = USB_HOST_DETECT_INACTIVE_VALUE / 2;

    if(gpio::getValue(USB_DM_PORT, USB_DM_PIN) == GPIO_VALUE_LOW) {
        ++debounce;
    } else {
        average = average * .9 + debounce * .1;
        debounce = 0;
    }

    if(!usbDevice->configured && average < USB_HOST_DETECT_ACTIVE_VALUE) {
        EVENT_USB_Device_ConfigurationChanged();
    }

    if(average > USB_HOST_DETECT_INACTIVE_VALUE) {
        debounce = 0;
        average = USB_HOST_DETECT_INACTIVE_VALUE / 2;
        return false;
    }
    return true;
}

/* Private: Configure I/O pins used to detect if USB is connected to a host. */
void configureUsbDetection() {
    PINSEL_CFG_Type vbusPinConfig;
    vbusPinConfig.Funcnum = VBUS_FUNCNUM;
    vbusPinConfig.Portnum = VBUS_PORT;
    vbusPinConfig.Pinnum = VBUS_PIN;
    vbusPinConfig.Pinmode = PINSEL_PINMODE_TRISTATE;
    PINSEL_ConfigPin(&vbusPinConfig);

    PINSEL_CFG_Type hostDetectPinConfig;
    hostDetectPinConfig.Funcnum = USB_DM_FUNCNUM;
    hostDetectPinConfig.Portnum = USB_DM_PORT;
    hostDetectPinConfig.Pinnum = USB_DM_PIN;
    hostDetectPinConfig.Pinmode = PINSEL_PINMODE_TRISTATE;
    PINSEL_ConfigPin(&hostDetectPinConfig);
}

void openxc::interface::usb::sendControlMessage(uint8_t* data, uint8_t length) {
    uint8_t previousEndpoint = Endpoint_GetCurrentEndpoint();
    Endpoint_SelectEndpoint(ENDPOINT_CONTROLEP);

    Endpoint_ClearSETUP();
    Endpoint_Write_Control_Stream_LE(data, length);
    // TODO I think it needs to be ClearIN since this is a device -> host
    // request, but a simple switch breaks it
    Endpoint_ClearOUT();

    Endpoint_SelectEndpoint(previousEndpoint);
}

void openxc::interface::usb::processSendQueue(UsbDevice* usbDevice) {
    USB_USBTask();

    if(!usbDevice->configured) {
        usbHostDetected(usbDevice);
    }

    if(usbDevice->configured) {
        if((USB_DeviceState != DEVICE_STATE_Configured || !vbusDetected())
                || !usbHostDetected(usbDevice)) {
            // On Windows the USB device will be configured when plugged in for
            // the first time, regardless of if you are actively using it in an
            // application. Windows will *not* send the USB configured event
            // when an application connects.
            //
            // On Linux and Mac, the USB configured event triggers each time a
            // new connection is made to the device.
            //
            // This means that if vbus is high (i.e. USB *might* be connected),
            // that's the only time we should check the usbHostDetected()
            // workaround. If we call that on Windows when USB is attached, it
            // will *unconfigure* the USB device from the VI side but not
            // reconfigure it until you disconnect and reconnect the device to
            // the PC! If the debounce value is small (which is ideal...) that
            // could happen even before your app has a chance to load the
            // device.
            EVENT_USB_Device_Disconnect();
        } else {
            for(int i = 0; i < ENDPOINT_COUNT; i++) {
                UsbEndpoint* endpoint = &usbDevice->endpoints[i];
                if(endpoint->direction == UsbEndpointDirection::USB_ENDPOINT_DIRECTION_IN) {
                    flushQueueToHost(usbDevice, endpoint);
                }
            }
        }
    }
}

void openxc::interface::usb::initialize(UsbDevice* usbDevice) {
    usb::initializeCommon(usbDevice);
    USB_Init();
    ::USB_Connect();
    configureUsbDetection();
}

void openxc::interface::usb::read(UsbDevice* device, UsbEndpoint* endpoint,
        bool (*callback)(uint8_t*)) {
    uint8_t previousEndpoint = Endpoint_GetCurrentEndpoint();
    Endpoint_SelectEndpoint(endpoint->address);

    while(Endpoint_IsOUTReceived()) {
        while(Endpoint_BytesInEndpoint()) {
            if(!QUEUE_PUSH(uint8_t, &endpoint->queue, Endpoint_Read_8())) {
                debug("Dropped write from host -- queue is full");
            }
        }
        processQueue(&endpoint->queue, callback);
        Endpoint_ClearOUT();
    }
    Endpoint_SelectEndpoint(previousEndpoint);
}

void openxc::interface::usb::deinitialize(UsbDevice* usbDevice) {
    usb::initializeCommon(usbDevice);
    // Turn off USB connection status LED
    gpio::setValue(USB_CONNECT_PORT, USB_CONNECT_PIN, GPIO_VALUE_HIGH);
}
<|endoftext|>


<commit_before>/* Copyright 2010 Thomas McGuire <mcguire@kde.org>
   Copyright 2011 Alexander Potashev <aspotashev@gmail.com>

   This library is free software; you can redistribute it and/or modify
   it under the terms of the GNU Library General Public License as published
   by the Free Software Foundation; either version 2 of the License or
   ( at your option ) version 3 or, at the discretion of KDE e.V.
   ( which shall act as a proxy as in section 14 of the GPLv3 ), any later version.

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

   You should have received a copy of the GNU Library General Public License
   along with this library; see the file COPYING.LIB.  If not, write to
   the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
   Boston, MA 02110-1301, USA.
*/
#include "userinfojob.moc"

#include <qjson/qobjecthelper.h>
#include <KIO/Job>
#include <KDebug>
#include <KLocale>

namespace Vkontakte
{

class UserInfoJob::Private
{
public:
    QList<UserInfoPtr> userInfo;
    QStringList fields;
};

// http://vk.com/dev/users.get
UserInfoJob::UserInfoJob(const QString &accessToken)
    : VkontakteJob(accessToken, "users.get")
    , d(new Private)
{
    // The complete list of fields
    setFields(UserInfo::allQueryFields());

    // TODO: support "counters" request (probably in another KJob)
}

UserInfoJob::UserInfoJob(const QString &accessToken, int uid)
    : VkontakteJob(accessToken, "users.get")
    , d(new Private)
{
    setFields(UserInfo::allQueryFields());
    addQueryItem("user_ids", QString::number(uid));
}

UserInfoJob::UserInfoJob(const QString &accessToken, const QIntList &uids)
    : VkontakteJob(accessToken, "users.get")
    , d(new Private)
{
    setFields(UserInfo::allQueryFields());
    addQueryItem("user_ids", uids.join());

    // TODO: make this working for more than 1000 uids
    // ("users.get" allows requesting only 1000 users at once)
}

UserInfoJob::~UserInfoJob()
{
    delete d;
}

QList<UserInfoPtr> UserInfoJob::userInfo() const
{
    return d->userInfo;
}

void UserInfoJob::setFields(const QStringList &fields)
{
    d->fields = fields;
}

void UserInfoJob::prepareQueryItems()
{
    if (!d->fields.isEmpty())
        addQueryItem("fields", d->fields.join(","));
}

UserInfoPtr UserInfoJob::handleSingleData(const QVariant &data)
{
     UserInfoPtr userInfo = UserInfoPtr(new UserInfo());
     QJson::QObjectHelper::qvariant2qobject(data.toMap(), userInfo.data());
     return userInfo;
}

void UserInfoJob::handleData(const QVariant &data)
{
    foreach(const QVariant &item, data.toList())
        d->userInfo.append(handleSingleData(item));
}

} /* namespace Vkontakte */
<commit_msg>UserInfoJob: Add comments<commit_after>/* Copyright 2010 Thomas McGuire <mcguire@kde.org>
   Copyright 2011 Alexander Potashev <aspotashev@gmail.com>

   This library is free software; you can redistribute it and/or modify
   it under the terms of the GNU Library General Public License as published
   by the Free Software Foundation; either version 2 of the License or
   ( at your option ) version 3 or, at the discretion of KDE e.V.
   ( which shall act as a proxy as in section 14 of the GPLv3 ), any later version.

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

   You should have received a copy of the GNU Library General Public License
   along with this library; see the file COPYING.LIB.  If not, write to
   the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
   Boston, MA 02110-1301, USA.
*/
#include "userinfojob.moc"

#include <qjson/qobjecthelper.h>
#include <KIO/Job>
#include <KDebug>
#include <KLocale>

namespace Vkontakte
{

class UserInfoJob::Private
{
public:
    QList<UserInfoPtr> userInfo;
    QStringList fields;
};

// http://vk.com/dev/users.get
UserInfoJob::UserInfoJob(const QString &accessToken)
    : VkontakteJob(accessToken, "users.get")
    , d(new Private)
{
    // The complete list of fields
    setFields(UserInfo::allQueryFields()); // TODO: do not pull extra fields by default

    // TODO: support "counters" request (probably in another KJob)
}

UserInfoJob::UserInfoJob(const QString &accessToken, int uid)
    : VkontakteJob(accessToken, "users.get")
    , d(new Private)
{
    setFields(UserInfo::allQueryFields()); // TODO: do not pull extra fields by default
    addQueryItem("user_ids", QString::number(uid));
}

UserInfoJob::UserInfoJob(const QString &accessToken, const QIntList &uids)
    : VkontakteJob(accessToken, "users.get")
    , d(new Private)
{
    setFields(UserInfo::allQueryFields()); // TODO: do not pull extra fields by default
    addQueryItem("user_ids", uids.join());

    // TODO: make this working for more than 1000 uids
    // ("users.get" allows requesting only 1000 users at once)
}

UserInfoJob::~UserInfoJob()
{
    delete d;
}

QList<UserInfoPtr> UserInfoJob::userInfo() const
{
    return d->userInfo;
}

void UserInfoJob::setFields(const QStringList &fields)
{
    d->fields = fields;
}

void UserInfoJob::prepareQueryItems()
{
    if (!d->fields.isEmpty())
        addQueryItem("fields", d->fields.join(","));
}

UserInfoPtr UserInfoJob::handleSingleData(const QVariant &data)
{
     UserInfoPtr userInfo = UserInfoPtr(new UserInfo());
     QJson::QObjectHelper::qvariant2qobject(data.toMap(), userInfo.data());
     return userInfo;
}

void UserInfoJob::handleData(const QVariant &data)
{
    foreach(const QVariant &item, data.toList())
        d->userInfo.append(handleSingleData(item));
}

} /* namespace Vkontakte */
<|endoftext|>


<commit_before>/*
 *      Copyright 2017 Pavel Bludov <pbludov@gmail.com>
 *
 *      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 2 of the License, or
 *      (at your option) any later version.
 *
 *      This program is distributed in the hope that it will be useful,
 *      but WITHOUT ANY WARRANTY; without even the implied warranty of
 *      MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *      GNU General Public License for more details.
 *
 *      You should have received a copy of the GNU General Public License along
 *      with this program; if not, write to the Free Software Foundation, Inc.,
 *      51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
 */

#include "qhiddevice.h"
#include "qhiddevice_hidapi.h"

#include <QDebug>

#include <errno.h>

#ifdef WITH_LIBUSB_1_0
#include <libusb.h>

static bool findUsagePage(int usagePage, const uint8_t *desc, size_t size)
{
    unsigned int i = 0;
    int dataLen, keySize;

    while (i < size)
    {
        int key = desc[i];

        if ((key & 0xf0) == 0xf0)
        {
            /* This is a Long Item. The next byte contains the
               length of the data section (value) for this key.
               See the HID specification, version 1.11, section
               6.2.2.3, titled "Long Items." */
            dataLen = i + 1 < size ? desc[i + 1] : 0;
            keySize = 3;
        }
        else
        {
            /* This is a Short Item. The bottom two bits of the
               key contain the size code for the data section
               (value) for this key.  Refer to the HID
               specification, version 1.11, section 6.2.2.2,
               titled "Short Items." */
            dataLen = key & 0x3;
            if (dataLen == 3)
                ++dataLen; // 0,1,2,4
            keySize = 1;
        }

        if ((key & 0xfc) == 0x04)
        {
            if (i + dataLen >= size)
            {
                // Truncated report?
                return false;
            }

            int page = 0;
            for (int offset = dataLen; offset > 0; --offset)
            {
                page <<= 8;
                page |= desc[i + offset];
            }

            if (page == usagePage)
                return true;
        }

        // Skip over this key and it's associated data.
        i += dataLen + keySize;
    }

    return false;
}

static void hidapiMissingFeatures(
    int vendorId, int deviceId, int usagePage, int *interfaceNumber, int *inBufferLength, int *outBufferLength)
{
    libusb_context *ctx = nullptr;
    int rc = libusb_init(&ctx);
    if (LIBUSB_SUCCESS != rc)
    {
        qWarning() << "libusb_init failed" << rc << libusb_error_name(rc);
        return;
    }

    libusb_device **devs;
    auto count = libusb_get_device_list(ctx, &devs);

    for (ssize_t i = 0; i < count; ++i)
    {
        auto dev = devs[i];
        libusb_device_descriptor desc;

        if (libusb_get_device_descriptor(dev, &desc) < 0)
            continue;

        if (desc.idVendor != vendorId || desc.idProduct != deviceId)
            continue;

        libusb_config_descriptor *confDesc = nullptr;

        if (libusb_get_active_config_descriptor(dev, &confDesc) < 0)
            continue;

        libusb_device_handle *handle;
        if (libusb_open(dev, &handle) < 0)
            continue;

        unsigned char buffer[256];
        for (int iface = 0; iface < confDesc->bNumInterfaces; ++iface)
        {
            bool detached = false;
            bool match = false;

            rc = libusb_kernel_driver_active(handle, iface);
            if (rc == 1)
            {
                rc = libusb_detach_kernel_driver(handle, iface);
                if (rc < 0)
                {
                    qWarning() << "Failed to detach kernel driver" << rc << libusb_error_name(rc);
                    continue;
                }
                detached = true;
            }

            if (libusb_claim_interface(handle, iface) >= 0)
            {
                // Get the HID Report Descriptor.
                rc = libusb_control_transfer(handle, LIBUSB_ENDPOINT_IN | LIBUSB_RECIPIENT_INTERFACE,
                    LIBUSB_REQUEST_GET_DESCRIPTOR, LIBUSB_DT_REPORT << 8, iface, buffer, sizeof(buffer), 1000);

                if (rc < 0)
                {
                    qWarning() << "Failed to read HID report descriptor" << rc << libusb_error_name(rc);
                }
                else
                {
                    match = findUsagePage(usagePage, buffer, rc);
                }

                libusb_release_interface(handle, iface);
            }

            if (detached)
            {
                // Re-attach kernel driver if necessary.
                rc = libusb_attach_kernel_driver(handle, iface);
                if (rc < 0)
                {
                    qWarning() << "Failed to re-attach kernel driver" << rc << libusb_error_name(rc);
                }
            }

            if (!match)
                continue;

            *interfaceNumber = iface;
            auto interface = confDesc->interface[iface];

            for (int ifIdx = 0; ifIdx < interface.num_altsetting; ++ifIdx)
            {
                auto intfDesc = interface.altsetting[ifIdx];

                for (int epIdx = 0; epIdx < intfDesc.bNumEndpoints; ++epIdx)
                {
                    auto ep = intfDesc.endpoint[epIdx];

                    if ((ep.bEndpointAddress & LIBUSB_ENDPOINT_DIR_MASK) == LIBUSB_ENDPOINT_IN)
                        *inBufferLength = ep.wMaxPacketSize;
                    else if ((ep.bEndpointAddress & LIBUSB_ENDPOINT_DIR_MASK) == LIBUSB_ENDPOINT_OUT)
                        *outBufferLength = ep.wMaxPacketSize;
                }
            }

            break;
        }
        libusb_close(handle);

        libusb_free_config_descriptor(confDesc);
        break;
    }

    libusb_free_device_list(devs, 1);
    libusb_exit(ctx);
}
#endif

QHIDDevicePrivate::QHIDDevicePrivate(QHIDDevice *q_ptr, int vendorId, int deviceId, int usagePage)
    : device(nullptr)
    , q_ptr(q_ptr)
{
    if (hid_init() != 0)
    {
        qWarning() << "hid_init failed, error" << errno;
        return;
    }

    int interfaceNumber = -1;
#ifdef WITH_LIBUSB_1_0
    hidapiMissingFeatures(
        vendorId, deviceId, usagePage, &interfaceNumber, &q_ptr->inputBufferLength, &q_ptr->outputBufferLength);
#endif
    auto devices = hid_enumerate(vendorId, deviceId);

    for (auto dev = devices; dev != nullptr; dev = dev->next)
    {
        if ((dev->usage_page > 0 && dev->usage_page == usagePage)
            || (dev->interface_number >= 0 && dev->interface_number == interfaceNumber))
        {
            device = hid_open_path(dev->path);

            if (device != nullptr)
            {
                break;
            }

            qWarning() << "Failed to open" << dev->path << "error" << errno;
        }
    }

    hid_free_enumeration(devices);

    if (device == nullptr)
    {
        qWarning() << "No such device" << vendorId << deviceId << usagePage;
    }
}

QHIDDevicePrivate::~QHIDDevicePrivate()
{
    if (device)
    {
        hid_close(device);
        device = nullptr;
    }

    hid_exit();
}

bool QHIDDevicePrivate::isValid() const
{
    return !!device;
}

int QHIDDevicePrivate::sendFeatureReport(const char *buffer, int length)
{
    return device == nullptr ? -1 : hid_send_feature_report(device, (const unsigned char *)buffer, length);
}

int QHIDDevicePrivate::getFeatureReport(char *buffer, int length)
{
    return device == nullptr ? -1 : hid_get_feature_report(device, (unsigned char *)buffer, length);
}

int QHIDDevicePrivate::write(const char *buffer, int length)
{
    return hid_write(device, (const unsigned char *)buffer, length);
}

int QHIDDevicePrivate::read(char *buffer, int length)
{
    return hid_read_timeout(device, (unsigned char *)buffer, length, 30000);
}
<commit_msg>Do not reattach the kernel driver for requested interface<commit_after>/*
 *      Copyright 2017 Pavel Bludov <pbludov@gmail.com>
 *
 *      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 2 of the License, or
 *      (at your option) any later version.
 *
 *      This program is distributed in the hope that it will be useful,
 *      but WITHOUT ANY WARRANTY; without even the implied warranty of
 *      MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *      GNU General Public License for more details.
 *
 *      You should have received a copy of the GNU General Public License along
 *      with this program; if not, write to the Free Software Foundation, Inc.,
 *      51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
 */

#include "qhiddevice.h"
#include "qhiddevice_hidapi.h"

#include <QDebug>

#include <errno.h>

#ifdef WITH_LIBUSB_1_0
#include <libusb.h>

static bool findUsagePage(int usagePage, const uint8_t *desc, size_t size)
{
    unsigned int i = 0;
    int dataLen, keySize;

    while (i < size)
    {
        int key = desc[i];

        if ((key & 0xf0) == 0xf0)
        {
            /* This is a Long Item. The next byte contains the
               length of the data section (value) for this key.
               See the HID specification, version 1.11, section
               6.2.2.3, titled "Long Items." */
            dataLen = i + 1 < size ? desc[i + 1] : 0;
            keySize = 3;
        }
        else
        {
            /* This is a Short Item. The bottom two bits of the
               key contain the size code for the data section
               (value) for this key.  Refer to the HID
               specification, version 1.11, section 6.2.2.2,
               titled "Short Items." */
            dataLen = key & 0x3;
            if (dataLen == 3)
                ++dataLen; // 0,1,2,4
            keySize = 1;
        }

        if ((key & 0xfc) == 0x04)
        {
            if (i + dataLen >= size)
            {
                // Truncated report?
                return false;
            }

            int page = 0;
            for (int offset = dataLen; offset > 0; --offset)
            {
                page <<= 8;
                page |= desc[i + offset];
            }

            if (page == usagePage)
                return true;
        }

        // Skip over this key and it's associated data.
        i += dataLen + keySize;
    }

    return false;
}

static void hidapiMissingFeatures(
    int vendorId, int deviceId, int usagePage, int *interfaceNumber, int *inBufferLength, int *outBufferLength)
{
    libusb_context *ctx = nullptr;
    int rc = libusb_init(&ctx);
    if (LIBUSB_SUCCESS != rc)
    {
        qWarning() << "libusb_init failed" << rc << libusb_error_name(rc);
        return;
    }

    libusb_device **devs;
    auto count = libusb_get_device_list(ctx, &devs);

    for (ssize_t i = 0; i < count; ++i)
    {
        auto dev = devs[i];
        libusb_device_descriptor desc;

        if (libusb_get_device_descriptor(dev, &desc) < 0)
            continue;

        if (desc.idVendor != vendorId || desc.idProduct != deviceId)
            continue;

        libusb_config_descriptor *confDesc = nullptr;

        if (libusb_get_active_config_descriptor(dev, &confDesc) < 0)
            continue;

        libusb_device_handle *handle;
        if (libusb_open(dev, &handle) < 0)
            continue;

        unsigned char buffer[256];
        for (int iface = 0; iface < confDesc->bNumInterfaces; ++iface)
        {
            bool detached = false;
            bool match = false;

            rc = libusb_kernel_driver_active(handle, iface);
            if (rc == 1)
            {
                rc = libusb_detach_kernel_driver(handle, iface);
                if (rc < 0)
                {
                    qWarning() << "Failed to detach kernel driver" << rc << libusb_error_name(rc);
                    continue;
                }
                detached = true;
            }

            if (libusb_claim_interface(handle, iface) >= 0)
            {
                // Get the HID Report Descriptor.
                rc = libusb_control_transfer(handle, LIBUSB_ENDPOINT_IN | LIBUSB_RECIPIENT_INTERFACE,
                    LIBUSB_REQUEST_GET_DESCRIPTOR, LIBUSB_DT_REPORT << 8, iface, buffer, sizeof(buffer), 1000);

                if (rc < 0)
                {
                    qWarning() << "Failed to read HID report descriptor" << rc << libusb_error_name(rc);
                }
                else
                {
                    match = findUsagePage(usagePage, buffer, rc);
                }

                libusb_release_interface(handle, iface);
            }

            if (match)
            {
                *interfaceNumber = iface;
                auto interface = confDesc->interface[iface];

                for (int ifIdx = 0; ifIdx < interface.num_altsetting; ++ifIdx)
                {
                    auto intfDesc = interface.altsetting[ifIdx];

                    for (int epIdx = 0; epIdx < intfDesc.bNumEndpoints; ++epIdx)
                    {
                        auto ep = intfDesc.endpoint[epIdx];

                        if ((ep.bEndpointAddress & LIBUSB_ENDPOINT_DIR_MASK) == LIBUSB_ENDPOINT_IN)
                            *inBufferLength = ep.wMaxPacketSize;
                        else if ((ep.bEndpointAddress & LIBUSB_ENDPOINT_DIR_MASK) == LIBUSB_ENDPOINT_OUT)
                            *outBufferLength = ep.wMaxPacketSize;
                    }
                }

                // Note that the kernel driver is still detached.
                break;
            }
            else if (detached)
            {
                // Re-attach kernel driver for non-mached devices.
                rc = libusb_attach_kernel_driver(handle, iface);
                if (rc < 0)
                {
                    qWarning() << "Failed to re-attach kernel driver" << rc << libusb_error_name(rc);
                }
            }
        }
        libusb_close(handle);

        libusb_free_config_descriptor(confDesc);
        break;
    }

    libusb_free_device_list(devs, 1);
    libusb_exit(ctx);
}
#endif

QHIDDevicePrivate::QHIDDevicePrivate(QHIDDevice *q_ptr, int vendorId, int deviceId, int usagePage)
    : device(nullptr)
    , q_ptr(q_ptr)
{
    if (hid_init() != 0)
    {
        qWarning() << "hid_init failed, error" << errno;
        return;
    }

    int interfaceNumber = -1;
#ifdef WITH_LIBUSB_1_0
    hidapiMissingFeatures(
        vendorId, deviceId, usagePage, &interfaceNumber, &q_ptr->inputBufferLength, &q_ptr->outputBufferLength);
#endif
    auto devices = hid_enumerate(vendorId, deviceId);

    for (auto dev = devices; dev != nullptr; dev = dev->next)
    {
        if ((dev->usage_page > 0 && dev->usage_page == usagePage)
            || (dev->interface_number >= 0 && dev->interface_number == interfaceNumber))
        {
            device = hid_open_path(dev->path);

            if (device != nullptr)
            {
                break;
            }

            qWarning() << "Failed to open" << dev->path << "error" << errno;
        }
    }

    hid_free_enumeration(devices);

    if (device == nullptr)
    {
        qWarning() << "No such device" << vendorId << deviceId << usagePage;
    }
}

QHIDDevicePrivate::~QHIDDevicePrivate()
{
    if (device)
    {
        hid_close(device);
        device = nullptr;
    }

    hid_exit();
}

bool QHIDDevicePrivate::isValid() const
{
    return !!device;
}

int QHIDDevicePrivate::sendFeatureReport(const char *buffer, int length)
{
    return device == nullptr ? -1 : hid_send_feature_report(device, (const unsigned char *)buffer, length);
}

int QHIDDevicePrivate::getFeatureReport(char *buffer, int length)
{
    return device == nullptr ? -1 : hid_get_feature_report(device, (unsigned char *)buffer, length);
}

int QHIDDevicePrivate::write(const char *buffer, int length)
{
    return hid_write(device, (const unsigned char *)buffer, length);
}

int QHIDDevicePrivate::read(char *buffer, int length)
{
    return hid_read_timeout(device, (unsigned char *)buffer, length, 30000);
}
<|endoftext|>